def test_timeout1(self): a1, a2, a3 = [SLJ(x) for x in (0.5, 0.6, 0.7)] a2.requires(a1) a3.requires(a2) sched = PureScheduler(a1, a2, a3, timeout=1) # should timeout in the middle of stage 2 self.assertFalse(sched.run()) sched.list()
def test_sequence6(self): "adding a sequence" sched = PureScheduler() a1 = J(sl(0.1), label=1) a2 = J(sl(0.1), label=2) a3 = J(sl(0.1), label=3) sched.add(Seq(a1, a2, a3)) self.assertTrue(sched.run())
def test_creation_scheduler(self): sched = PureScheduler() s = Seq(J(sl(1)), J(sl(2)), scheduler=sched) J(sl(3), required=s, scheduler=sched) # make sure that jobs appended in the sequence # even later on are also added to the scheduler s.append(J(sl(.5))) self.assertEqual(len(sched.jobs), 4) self.assertTrue(sched.check_cycles()) self.assertTrue(sched.run())
def _test_exc_critical(self, verbose): print("verbose = {}".format(verbose)) a1, a2 = SLJ(1), J(co_exception(0.5), label='critical boom', critical=True) sched = PureScheduler(a1, a2, verbose=verbose) self.assertFalse(sched.run()) print(common_sep + 'debrief()') sched.debrief()
def test_cycle(self): """a simple loop with 3 jobs - cannot handle that""" a1, a2, a3 = J(sl(1.1)), J(sl(1.2)), J(sl(1.3)) a1.requires(a2) a2.requires(a3) a3.requires(a1) sched = PureScheduler(a1, a2, a3) # these lines seem to trigger a nasty message about a coro not being # waited self.assertFalse(sched.check_cycles())
def test_sequence2(self): "a job and a sequence" a1 = J(sl(0.1), label=1) a2 = J(sl(0.1), label=2) a3 = J(sl(0.1), label=3) s = Seq(a2, a3, required=a1) sched = PureScheduler(a1, s) list_sep(sched, common_sep + "sequence2") self.assertEqual(len(a1.required), 0) self.assertEqual(len(a2.required), 1) self.assertEqual(len(a3.required), 1) self.assertTrue(check_required_types(sched, "test_sequence2")) self.assertTrue(sched.run())
def test_forever2(self): async def tick(n): while True: print('tick {}'.format(n)) await asyncio.sleep(n) a1 = J(sl(0.5), label="finite") a2 = J(tick(0.1), forever=True, label="forever") sched = PureScheduler(a1, a2) result = sched.run() self.assertEqual(result, True) self.assertEqual(a1.is_done(), True) self.assertEqual(a2.is_done(), False)
def test_sequence3(self): "a sequence and a job" a1 = J(sl(0.1), label=1) a2 = J(sl(0.1), label=2) s = Seq(a1, a2) a3 = J(sl(0.1), label=3, required=s) sched = PureScheduler() sched.update((s, a3)) list_sep(sched, common_sep + "sequence3") self.assertEqual(len(a1.required), 0) self.assertEqual(len(a2.required), 1) self.assertEqual(len(a3.required), 1) self.assertTrue(check_required_types(sched, "test_sequence3")) self.assertTrue(sched.run())
def test_nesting1(self): """ one main scheduler in sequence one job one subscheduler that runs 2 jobs in parallel one job """ watch = Watch('test_nesting1') # sub-scheduler - total approx 1 s sub_sched = PureScheduler(watch=watch) Job(co_print_sleep(watch, 0.5, "sub short"), scheduler=sub_sched) Job(co_print_sleep(watch, 1, "sub longs"), scheduler=sub_sched) # main scheduler - total approx 2 s main_sched = PureScheduler(watch=watch) Sequence( Job(co_print_sleep(watch, 0.5, "main begin")), # this is where the subscheduler is merged Job(sub_sched.co_run(), label='subscheduler'), Job(co_print_sleep(watch, 0.5, "main end")), scheduler=main_sched) print("===== test_nesting1", "LIST with details") main_sched.list(details=True) ok = main_sched.run() self.assertTrue(ok) # allow for a small variation around 2s of course duration = watch.seconds() self.assertAlmostEqual(duration, 2, delta=0.05)
def test_sequence4(self): "a sequence of 2 sequences" a1 = J(sl(0.1), label=1) a2 = J(sl(0.1), label=2) a3 = J(sl(0.1), label=3) a4 = J(sl(0.1), label=4) s1 = Seq(a1, a2) s2 = Seq(a3, a4) sched = PureScheduler(Seq(s1, s2)) list_sep(sched, common_sep + "sequence4") self.assertEqual(len(a1.required), 0) self.assertEqual(len(a2.required), 1) self.assertEqual(len(a3.required), 1) self.assertEqual(len(a4.required), 1) self.assertTrue(check_required_types(sched, "test_sequence4")) self.assertTrue(sched.run())
def __init__(self, *jobs_or_sequences, jobs_window=None, timeout=None, shutdown_timeout=1, watch=None, verbose=False, **kwds): PureScheduler.__init__(self, *jobs_or_sequences, jobs_window=jobs_window, timeout=timeout, shutdown_timeout=shutdown_timeout, watch=watch, verbose=verbose) AbstractJob.__init__(self, **kwds)
def test_forever1(self): a1, a2, t1 = SLJ(1), SLJ(1.5), TJ(.6) a2.requires(a1) sched = PureScheduler(a1, a2, t1) sched.list() self.assertTrue(sched.run()) sched.list()
def test_nesting2(self): """ 2 sub schedulers run in parallel while the third main one controls them both """ watch = Watch('test_nesting2') # sub-scheduler - total approx 0.5 s sub2 = diamond_scheduler(watch, 0.5, "SUB2", scheduler_class=PureScheduler) sub2.watch = watch # sub-scheduler - total approx 1 s sub3 = diamond_scheduler(watch, 1, "SUB3", scheduler_class=PureScheduler) sub3.watch = watch # main scheduler - total approx # 0.5 + max(0.5, 1) + 0.5 = 2 s expected_duration = 2 main_sched = PureScheduler(watch=watch) mainj1 = Job(co_print_sleep(watch, 0.5, "mainj1"), label="mainj1", scheduler=main_sched) mainj2 = Job(sub2.co_run(), label="mainj2", required=mainj1, scheduler=main_sched) mainj3 = Job(sub3.co_run(), label="mainj3", required=mainj1, scheduler=main_sched) Job(co_print_sleep(watch, 0.5, "mainj4"), label="mainj4", required=(mainj2, mainj3), scheduler=main_sched) ok = main_sched.run() self.assertTrue(ok) # allow for a small variation around 2s of course duration = watch.seconds() self.assertAlmostEqual(duration, expected_duration, delta=0.05)
def test_graph1(self): watch = Watch() s = PureScheduler() s.add( Sequence( Job(co_print_sleep(watch, .25, 'begin')), Job(co_print_sleep(watch, 1., 'middle'), label='middle'), Job(co_print_sleep(watch, .25, 'end')), )) print("test_graph1 NO DETAILS") s.list() print("test_graph1 WITH DETAILS") s.list(details=True) print("GRAPH") self.assertEqual(len(s), 3) s.run() self.assertAlmostEqual(watch.seconds(), 1.5, delta=0.05) produce_png(s, "test_graph1")
def test_timeout2(self): a1 = J(sl(1), label="a1") a2 = J(sl(2), label="a2") a3 = J(sl(10), label="a3") result = PureScheduler(a1, a2, a3, timeout=3).run() self.assertEqual(result, False) self.assertEqual(a1.is_done(), True) self.assertEqual(a1.result(), 1) self.assertEqual(a2.is_done(), True) self.assertEqual(a2.result(), 2) self.assertEqual(a3.is_done(), False)
def test_sequence5(self): "sequences with required" a1 = J(sl(0.1), label=1) a2 = J(sl(0.1), label=2) a3 = J(sl(0.1), label=3) a4 = J(sl(0.1), label=4) a5 = J(sl(0.1), label=5) a6 = J(sl(0.1), label=6) s1 = Seq(a1, a2) s2 = Seq(a3, a4, required=s1) s3 = Seq(a5, a6, required=s2) sched = PureScheduler(s1, s2, s3) list_sep(sched, common_sep + "sequence5") self.assertEqual(len(a1.required), 0) self.assertEqual(len(a2.required), 1) self.assertEqual(len(a3.required), 1) self.assertEqual(len(a4.required), 1) self.assertEqual(len(a5.required), 1) self.assertEqual(len(a6.required), 1) self.assertTrue(check_required_types(sched, "test_sequence5")) self.assertTrue(sched.run())
def _test_window(self, total, window): atom = .1 tolerance = 8 # more or less % in terms of overall time s = PureScheduler(jobs_window=window) for i in range(1, total + 1): s.add(PrintJob("{}-th {}s job".format(i, atom), sleep=atom)) beg = time.time() ok = s.run() ok or s.debrief(details=True) end = time.time() duration = end - beg # estimate global time # unwindowed: overall duration is atom # otherwise a multiple of it (assuming total = k*window) expected = atom if not window else (total / window) * atom print('overall expected {} - measured {}'.format(expected, duration)) distortion = duration / expected time_ok = 1 - tolerance / 100 <= distortion <= 1 + tolerance / 100 if not time_ok: print("_test_window - window = {} :" "wrong execution time {} - not within {}% of {}".format( window, end - beg, tolerance, expected)) self.assertTrue(time_ok) self.assertTrue(ok)
def test_graph2(self): class TextJob(Job): def __init__(self, text, *args, **kwds): self.text = text super().__init__(*args, **kwds) def text_label(self): return "[[TextJob {}]]".format(self.text[::-1]) def details(self): return f"TextJob details say\n" \ f"that initial text was {self.text}" class GraphJob(Job): def __init__(self, graph, *args, **kwds): self.graph = graph super().__init__(*args, **kwds) def graph_label(self): return "[[GraphJob\n{}]]".format(self.graph[::-1]) def details(self): return f"GraphJob details\nare even more verbose and say\n" \ f"that initial graph message\nwas {self.graph}" watch = Watch() s = PureScheduler() s.add( Sequence( TextJob('textjob-with', co_print_sleep(watch, 0.1, 'textjob, no label ')), TextJob('textjob-without', co_print_sleep(watch, 0.1, 'textjob, with label '), label='TextLabel'), GraphJob('graphjob-with', co_print_sleep(watch, 0.1, 'graphjob, no label ')), GraphJob('graphjob-without', co_print_sleep(watch, 0.1, 'graphjob, with label '), label='GraphLabel'), )) print("test_graph2 NO DETAILS") s.list() print("test_graph2 WITH DETAILS") s.list(details=True) print("GRAPH") self.assertEqual(len(s), 4) produce_png(s, "test_graph2")
def _set_sched_id(self, start, id_format): """ Works as a complicit to PureScheduler._set_sched_ids. It sets local the ``_sched_id`` attribute and returns the index for the next job. """ # first set index on the current (kind of fake) node i = AbstractJob._set_sched_id( self, # pylint: disable=w0212 start, id_format) # go on with the jobs in sub scheduler return PureScheduler._set_sched_ids( self, # pylint: disable=w0212 i, id_format)
def test_display(self): class FakeTask: def __init__(self): self._result = 0 self._exception = None def annotate_job_with_fake_task(job, state, boom): task = FakeTask() if state == "done": task._state = asyncio.futures._FINISHED job._task = task job._running = True elif state == "running": task._state = "NONE" job._task = task job._running = True elif state == "scheduled": task._state = "NONE" job._task = task job._running = False else: pass # here we assume that a job that has raised an exception is # necessarily done if boom: if state in ("idle", "scheduled", "running"): print("incompatible combination boom x idle - ignored") return else: job._task._exception = True return job class AJ(AbstractJob): pass sched = PureScheduler() previous = None for state in "idle", "scheduled", "running", "done": for boom in True, False: for critical in True, False: for forever in True, False: j = AJ(critical=critical, forever=forever, label="forever={} crit.={} status={} boom={}". format(forever, critical, state, boom), required=previous) if annotate_job_with_fake_task(j, state, boom): sched.add(j) previous = j sched.list()
def test_simple(self): """a simple topology, that should work""" jobs = SLJ(0.1), SLJ(0.2), SLJ(0.3), SLJ(0.4), SLJ(0.5), J(sl(0.6)), J( sl(0.7)) a1, a2, a3, a4, a5, a6, a7 = jobs a4.requires(a1, a2, a3) a5.requires(a4) a6.requires(a4) a7.requires(a5) a7.requires(a6) sched = PureScheduler(*jobs) list_sep(sched, common_sep + "LIST BEFORE") self.assertTrue(sched.check_cycles()) self.assertTrue(sched.run()) for j in jobs: self.assertFalse(j.raised_exception()) list_sep(sched, common_sep + "LIST AFTER") print(common_sep + "DEBRIEF") sched.debrief()
def test_empty(self): # pylint: disable=r0201 s = PureScheduler() s.list() s.list(details=True) self.assertTrue(s.run())
def test_nesting3(self): """ same as test_nesting2 but using a Scheduler instance 2 sub schedulers run in parallel while the third main one controls them both """ # main scheduler - total approx # 0.5 + max(0.5, 1) + 0.5 = 2 s expected_duration = 2 watch = Watch('test_nesting3') main_sched = PureScheduler(verbose=True, watch=watch) main_sched.label = "main3" mainj1 = Job(co_print_sleep(watch, 0.5, "mainj1"), label="mainj1", scheduler=main_sched) # sub-scheduler 2 - total approx 0.5 s sub_sched2 = diamond_scheduler(watch, 0.5, "SUB2") main_sched.add(sub_sched2) sub_sched2.requires(mainj1) sub_sched2.label = "sub_sched2" sub_sched2.verbose = True # sub-scheduler 3 - total approx 1 s sub_sched3 = diamond_scheduler(watch, 1, "SUB3") main_sched.add(sub_sched3) sub_sched3.requires(mainj1) sub_sched3.label = "sub_sched3" sub_sched3.verbose = True # last job in main scheduler Job(co_print_sleep(watch, 0.5, "mainj4"), label="mainj4", required=(sub_sched2, sub_sched3), scheduler=main_sched) for s in main_sched, sub_sched2, sub_sched3: if not s.sanitize(): print(f"OOPS, had to sanitize sched {s.label}") print("===== test_nesting3", "LIST without details") main_sched.list(details=False) produce_png(main_sched, "test_nesting3") watch.reset() print("---run") ok = main_sched.run() if not ok: main_sched.debrief() self.assertTrue(ok) # allow for a small variation around 2s of course duration = watch.seconds() self.assertAlmostEqual(duration, expected_duration, delta=0.05)
def test_seq(self): s = PureScheduler() Seq(J(sl(.1)), J(sl(.2)), scheduler=s) self.assertTrue(s.run())