def test_simple(monkeypatch, config_yaml, expected_events): monkeypatch.setattr(yacron.cron, "RunningJob", TracingRunningJob) cron = yacron.cron.Cron(None, config_yaml=config_yaml) events = [] async def wait_and_quit(): the_job = None while True: ts, event, job = await TracingRunningJob._TRACE.get() print(ts, event) if the_job is None: job = the_job else: assert job is the_job events.append(event) if event in {'report_success', 'report_permanent_failure'}: break cron.signal_shutdown() loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.gather( wait_and_quit(), cron.run())) assert events == expected_events
def test_concurrency_policy(monkeypatch, policy, expected_numjobs, expected_max_running): monkeypatch.setattr(yacron.cron, "RunningJob", TracingRunningJob) START_TIME = datetime.datetime(year=1999, month=12, day=31, hour=12, minute=0, second=59, microsecond=750000) t0 = time.perf_counter() def get_now(): return (START_TIME + datetime.timedelta(seconds=(time.perf_counter() - t0))) monkeypatch.setattr("yacron.cron.get_now", get_now) cron = yacron.cron.Cron( None, config_yaml=CONCURRENT_JOB.format(policy=policy), ) events = [] numjobs = 0 max_running = 0 async def wait_and_quit(): nonlocal numjobs, max_running known_jobs = {} pending_jobs = set() running_jobs = set() while not (known_jobs and not pending_jobs): ts, event, job = await TracingRunningJob._TRACE.get() try: jobnum = known_jobs[job] except KeyError: if known_jobs: jobnum = max(known_jobs.values()) + 1 else: jobnum = 1 known_jobs[job] = jobnum pending_jobs.add(jobnum) running_jobs.add(jobnum) numjobs += 1 print(ts, event, jobnum) events.append((jobnum, event)) if event in {'report_success', 'report_permanent_failure'}: pending_jobs.discard(jobnum) if event in {'report_success', 'report_permanent_failure', 'cancelled'}: running_jobs.discard(jobnum) max_running = max(len(running_jobs), max_running) cron.signal_shutdown() loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.gather( wait_and_quit(), cron.run())) import pprint pprint.pprint(events) assert (numjobs, max_running) == (expected_numjobs, expected_max_running)
def test_fail_retry(monkeypatch): monkeypatch.setattr(yacron.cron, "RunningJob", TracingRunningJob) cron = yacron.cron.Cron(None, config_yaml=RETRYING_JOB_THAT_FAILS) events = [] async def wait_and_quit(): known_jobs = {} while True: ts, event, job = await TracingRunningJob._TRACE.get() try: jobnum = known_jobs[job] except KeyError: if known_jobs: jobnum = max(known_jobs.values()) + 1 else: jobnum = 1 known_jobs[job] = jobnum print(ts, event, jobnum) events.append((jobnum, event)) if jobnum == 3 and event == 'report_permanent_failure': break cron.signal_shutdown() loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.gather(wait_and_quit(), cron.run())) assert events == [ # initial attempt (1, 'create'), (1, 'start'), (1, 'started'), (1, 'wait'), (1, 'waited'), (1, 'report_failure'), # first retry (2, 'create'), (2, 'start'), (2, 'started'), (2, 'wait'), (2, 'waited'), (2, 'report_failure'), # second retry (3, 'create'), (3, 'start'), (3, 'started'), (3, 'wait'), (3, 'waited'), (3, 'report_failure'), (3, 'report_permanent_failure') ]
def test_execution_timeout(monkeypatch): monkeypatch.setattr(yacron.cron, "RunningJob", TracingRunningJob) cron = yacron.cron.Cron(None, config_yaml=JOB_THAT_HANGS) events = [] jobs_stdout = {} async def wait_and_quit(): known_jobs = {} while True: ts, event, job = await TracingRunningJob._TRACE.get() try: jobnum = known_jobs[job] except KeyError: if known_jobs: jobnum = max(known_jobs.values()) + 1 else: jobnum = 1 known_jobs[job] = jobnum print(ts, event, jobnum) events.append((jobnum, event)) if jobnum == 1 and event == 'report_permanent_failure': jobs_stdout[jobnum] = job.stdout break cron.signal_shutdown() loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.gather(wait_and_quit(), cron.run())) assert events == [ # initial attempt (1, 'create'), (1, 'start'), (1, 'started'), (1, 'wait'), (1, 'cancel'), (1, 'cancelled'), (1, 'waited'), (1, 'report_failure'), (1, 'report_permanent_failure') ] assert jobs_stdout[1] == 'starting...\n'
def test_concurrency_and_backoff(monkeypatch): monkeypatch.setattr(yacron.cron, "RunningJob", TracingRunningJob) START_TIME = datetime.datetime(year=1999, month=12, day=31, hour=12, minute=0, second=59, microsecond=750000) STOP_TIME = datetime.datetime(year=1999, month=12, day=31, hour=12, minute=1, second=00, microsecond=250000) t0 = time.perf_counter() def get_now(): return (START_TIME + datetime.timedelta(seconds=(time.perf_counter() - t0))) def get_reltime(ts): return (START_TIME + datetime.timedelta(seconds=(ts - t0))) monkeypatch.setattr("yacron.cron.get_now", get_now) cron = yacron.cron.Cron(None, config_yaml=RETRYING_JOB_THAT_FAILS2) events = [] numjobs = 0 async def wait_and_quit(): nonlocal numjobs known_jobs = {} pending_jobs = set() running_jobs = set() while get_now() < STOP_TIME: try: ts, event, job = await asyncio.wait_for( TracingRunningJob._TRACE.get(), 0.1) except asyncio.TimeoutError: continue try: jobnum = known_jobs[job] except KeyError: if known_jobs: jobnum = max(known_jobs.values()) + 1 else: jobnum = 1 known_jobs[job] = jobnum pending_jobs.add(jobnum) running_jobs.add(jobnum) numjobs += 1 print(get_reltime(ts), event, jobnum) events.append((jobnum, event)) if event in {'report_success', 'report_permanent_failure'}: pending_jobs.discard(jobnum) if event in { 'report_success', 'report_permanent_failure', 'cancelled' }: running_jobs.discard(jobnum) cron.signal_shutdown() loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.gather(wait_and_quit(), cron.run())) import pprint pprint.pprint(events) assert numjobs == 2