artiq/artiq/test/scheduler.py

202 lines
6.6 KiB
Python
Raw Normal View History

2015-05-27 19:25:50 +08:00
import unittest
2015-10-20 18:11:50 +08:00
import logging
2015-05-27 19:25:50 +08:00
import asyncio
import sys
import os
from time import time, sleep
2015-05-27 19:25:50 +08:00
from artiq.experiment import *
2015-05-27 19:25:50 +08:00
from artiq.master.scheduler import Scheduler
2015-07-14 04:08:20 +08:00
class EmptyExperiment(EnvExperiment):
def build(self):
pass
2015-05-27 19:25:50 +08:00
def run(self):
pass
2015-07-14 04:08:20 +08:00
class BackgroundExperiment(EnvExperiment):
def build(self):
2015-10-04 00:18:21 +08:00
self.setattr_device("scheduler")
2015-07-14 04:08:20 +08:00
2015-05-27 19:25:50 +08:00
def run(self):
try:
while True:
self.scheduler.pause()
sleep(0.2)
except TerminationRequested:
2015-10-12 19:20:04 +08:00
self.set_dataset("termination_ok", True,
broadcast=True, save=False)
2015-05-27 19:25:50 +08:00
def _get_expid(name):
return {
2015-10-20 18:11:50 +08:00
"log_level": logging.WARNING,
2015-05-27 19:25:50 +08:00
"file": sys.modules[__name__].__file__,
2015-07-15 17:08:12 +08:00
"class_name": name,
2015-05-27 19:25:50 +08:00
"arguments": dict()
}
2015-05-29 20:16:47 +08:00
def _get_basic_steps(rid, expid, priority=0, flush=False):
2015-05-27 19:25:50 +08:00
return [
{"action": "setitem", "key": rid, "value":
2015-05-29 20:16:47 +08:00
{"pipeline": "main", "status": "pending", "priority": priority,
2015-08-10 20:07:24 +08:00
"expid": expid, "due_date": None, "flush": flush,
"repo_msg": None},
2015-05-28 17:20:58 +08:00
"path": []},
2015-05-27 19:25:50 +08:00
{"action": "setitem", "key": "status", "value": "preparing",
"path": [rid]},
{"action": "setitem", "key": "status", "value": "prepare_done",
"path": [rid]},
{"action": "setitem", "key": "status", "value": "running",
"path": [rid]},
{"action": "setitem", "key": "status", "value": "run_done",
"path": [rid]},
{"action": "setitem", "key": "status", "value": "analyzing",
"path": [rid]},
{"action": "setitem", "key": "status", "value": "deleting",
2015-05-27 19:25:50 +08:00
"path": [rid]},
{"action": "delitem", "key": rid, "path": []}
]
class SchedulerCase(unittest.TestCase):
def setUp(self):
if os.name == "nt":
self.loop = asyncio.ProactorEventLoop()
else:
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
2015-05-27 19:25:50 +08:00
def test_steps(self):
loop = self.loop
2015-10-06 13:35:30 +08:00
scheduler = Scheduler(0, dict(), None)
2015-05-27 19:25:50 +08:00
expid = _get_expid("EmptyExperiment")
expect = _get_basic_steps(1, expid)
2015-05-27 19:25:50 +08:00
done = asyncio.Event()
expect_idx = 0
def notify(mod):
2015-05-27 19:25:50 +08:00
nonlocal expect_idx
self.assertEqual(mod, expect[expect_idx])
expect_idx += 1
if expect_idx >= len(expect):
done.set()
scheduler.notifier.publish = notify
scheduler.start()
# Verify that a timed experiment far in the future does not
# get run, even if it has high priority.
late = time() + 100000
expect.insert(0,
{"action": "setitem", "key": 0, "value":
{"pipeline": "main", "status": "pending", "priority": 99,
2015-08-10 20:07:24 +08:00
"expid": expid, "due_date": late, "flush": False,
"repo_msg": None},
"path": []})
scheduler.submit("main", expid, 99, late, False)
# This one (RID 1) gets run instead.
2015-05-28 17:20:58 +08:00
scheduler.submit("main", expid, 0, None, False)
2015-05-27 19:25:50 +08:00
loop.run_until_complete(done.wait())
scheduler.notifier.publish = None
2015-05-27 19:25:50 +08:00
loop.run_until_complete(scheduler.stop())
def test_pause(self):
loop = self.loop
termination_ok = False
2015-10-12 19:20:04 +08:00
def check_termination(mod):
nonlocal termination_ok
2015-10-12 19:20:04 +08:00
self.assertEqual(
mod,
{"action": "setitem", "key": "termination_ok",
"value": (False, True), "path": []})
termination_ok = True
handlers = {
2015-10-12 19:20:04 +08:00
"update_dataset": check_termination
}
scheduler = Scheduler(0, handlers, None)
2015-05-27 19:25:50 +08:00
expid_bg = _get_expid("BackgroundExperiment")
expid = _get_expid("EmptyExperiment")
expect = _get_basic_steps(1, expid)
background_running = asyncio.Event()
empty_completed = asyncio.Event()
background_completed = asyncio.Event()
2015-05-27 19:25:50 +08:00
expect_idx = 0
def notify(mod):
2015-05-27 19:25:50 +08:00
nonlocal expect_idx
if mod == {"path": [0],
"value": "running",
"key": "status",
"action": "setitem"}:
background_running.set()
if mod == {"path": [0],
"value": "deleting",
"key": "status",
"action": "setitem"}:
background_completed.set()
2015-05-27 19:25:50 +08:00
if mod["path"] == [1] or (mod["path"] == [] and mod["key"] == 1):
self.assertEqual(mod, expect[expect_idx])
expect_idx += 1
if expect_idx >= len(expect):
empty_completed.set()
2015-05-27 19:25:50 +08:00
scheduler.notifier.publish = notify
scheduler.start()
2015-05-28 17:20:58 +08:00
scheduler.submit("main", expid_bg, -99, None, False)
2015-05-27 19:25:50 +08:00
loop.run_until_complete(background_running.wait())
2015-05-28 17:20:58 +08:00
scheduler.submit("main", expid, 0, None, False)
loop.run_until_complete(empty_completed.wait())
self.assertFalse(termination_ok)
scheduler.request_termination(0)
loop.run_until_complete(background_completed.wait())
self.assertTrue(termination_ok)
2015-05-27 19:25:50 +08:00
loop.run_until_complete(scheduler.stop())
2015-05-29 20:16:47 +08:00
def test_flush(self):
loop = self.loop
2015-10-06 13:35:30 +08:00
scheduler = Scheduler(0, dict(), None)
2015-05-29 20:16:47 +08:00
expid = _get_expid("EmptyExperiment")
expect = _get_basic_steps(1, expid, 1, True)
expect.insert(1, {"key": "status",
"path": [1],
"value": "flushing",
"action": "setitem"})
first_preparing = asyncio.Event()
done = asyncio.Event()
expect_idx = 0
def notify(mod):
2015-05-29 20:16:47 +08:00
nonlocal expect_idx
if mod == {"path": [0],
"value": "preparing",
"key": "status",
"action": "setitem"}:
first_preparing.set()
if mod["path"] == [1] or (mod["path"] == [] and mod["key"] == 1):
self.assertEqual(mod, expect[expect_idx])
expect_idx += 1
if expect_idx >= len(expect):
done.set()
scheduler.notifier.publish = notify
scheduler.start()
scheduler.submit("main", expid, 0, None, False)
loop.run_until_complete(first_preparing.wait())
scheduler.submit("main", expid, 1, None, True)
loop.run_until_complete(done.wait())
loop.run_until_complete(scheduler.stop())
def tearDown(self):
self.loop.close()