Skip to content

Commit 6f1bf52

Browse files
committed
sched: Rewrite crontest.py. Add simulate.py. Docs to follow.
1 parent 2ef4a6a commit 6f1bf52

File tree

3 files changed

+133
-59
lines changed

3 files changed

+133
-59
lines changed

v3/as_drivers/sched/cron.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# cron.py
22

3-
# Copyright (c) 2020 Peter Hinch
3+
# Copyright (c) 2020-2023 Peter Hinch
44
# Released under the MIT License (MIT) - see LICENSE file
55

66
from time import mktime, localtime
@@ -93,7 +93,7 @@ def inner(tnow):
9393
md += toff % 7 # mktime handles md > 31 but month may increment
9494
tev = mktime((yr, mo, md, h, m, s, wd, 0))
9595
cur_mo = mo
96-
_, mo = localtime(tev)[:2] # get month
96+
mo = localtime(tev)[1] # get month
9797
if mo != cur_mo:
9898
toff = do_arg(month, mo) # Get next valid month
9999
mo += toff # Offset is relative to new, incremented month

v3/as_drivers/sched/crontest.py

Lines changed: 91 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,106 @@
1-
# crontest.py
1+
# crontest.py Now works under Unix build
22

3-
# Copyright (c) 2020 Peter Hinch
3+
# Copyright (c) 2020-2023 Peter Hinch
44
# Released under the MIT License (MIT) - see LICENSE file
55

6-
from time import time, ticks_diff, ticks_us, localtime
6+
from time import time, ticks_diff, ticks_us, localtime, mktime
77
from sched.cron import cron
88
import sys
99

1010
maxruntime = 0
1111
fail = 0
12-
def result(t, msg):
13-
global fail
14-
if t != next(iexp):
15-
print('FAIL', msg, t)
16-
fail += 1
17-
return
18-
print('PASS', msg, t)
1912

20-
def test(*, secs=0, mins=0, hrs=3, mday=None, month=None, wday=None, tsource=None):
21-
global maxruntime
22-
ts = int(time() if tsource is None else tsource) # int() for Unix build
13+
# Args:
14+
# ts Time of run in secs since epoch
15+
# exp Expected absolute end time (yr, mo, md, h, m, s)
16+
# msg Message describing test
17+
# kwargs are args for cron
18+
def test(ts, exp, msg, *, secs=0, mins=0, hrs=3, mday=None, month=None, wday=None):
19+
global maxruntime, fail
20+
texp = mktime(exp + (0, 0)) # Expected absolute end time
21+
yr, mo, md, h, m, s, wd = localtime(texp)[:7]
22+
print(f"Test: {msg}")
23+
print(f"Expected endtime: {h:02d}:{m:02d}:{s:02d} on {md:02d}/{mo:02d}/{yr:02d}")
24+
2325
cg = cron(secs=secs, mins=mins, hrs=hrs, mday=mday, month=month, wday=wday)
2426
start = ticks_us()
25-
t = cg(ts) # Time relative to ts
27+
t = cg(ts) # Wait duration returned by cron (secs)
2628
delta = ticks_diff(ticks_us(), start)
2729
maxruntime = max(maxruntime, delta)
28-
print('Runtime = {}μs'.format(delta))
29-
tev = t + ts # Absolute time of 1st event
30-
yr, mo, md, h, m, s, wd = localtime(tev)[:7]
31-
print('{:02d}:{:02d}:{:02d} on {:02d}/{:02d}/{:02d}'.format(h, m, s, md, mo, yr))
32-
return t # Relative time
33-
34-
now = 1596074400 if sys.platform == 'linux' else 649393200 # 3am Thursday (day 3) 30 July 2020
35-
iexp = iter([79500, 79500, 86700, 10680, 13564800, 17712000,
36-
12781800, 11217915, 5443200, 21600, 17193600,
37-
18403200, 5353140, 13392000, 18662400])
38-
# Expect 01:05:00 on 31/07/2020
39-
result(test(wday=4, hrs=(1,2), mins=5, tsource=now), 'wday and time both cause 1 day increment.')
40-
# 01:05:00 on 31/07/2020
41-
result(test(hrs=(1,2), mins=5, tsource=now), 'time causes 1 day increment.')
42-
# 03:05:00 on 31/07/2020
43-
result(test(wday=4, mins=5, tsource=now), 'wday causes 1 day increment.')
44-
# 05:58:00 on 30/07/2020
45-
result(test(hrs=(5, 23), mins=58, tsource=now), 'time increment no day change.')
46-
# 03:00:00 on 03/01/2021
47-
result(test(month=1, wday=6, tsource=now), 'month and year rollover, 1st Sunday')
48-
# 03:00:00 on 20/02/2021
49-
result(test(month=2, mday=20, tsource=now), 'month and year rollover, mday->20 Feb')
50-
# 01:30:00 on 25/12/2020
51-
result(test(month=12, mday=25, hrs=1, mins=30, tsource=now), 'Forward to Christmas day, hrs backwards')
52-
# 23:05:15 on 06/12/2020
53-
result(test(month=12, wday=6, hrs=23, mins=5, secs=15, tsource=now), '1st Sunday in Dec 2020')
54-
# 03:00:00 on 01/10/2020
55-
result(test(month=10, tsource=now), 'Current time on 1st Oct 2020')
56-
# 09:00:00 on 30/07/2020
57-
result(test(month=7, hrs=9, tsource=now), 'Explicitly specify current month')
58-
# 03:00:00 on 14/02/2021
59-
result(test(month=2, mday=8, wday=6, tsource=now), 'Second Sunday in February 2021')
60-
# 03:00:00 on 28/02/2021
61-
result(test(month=2, mday=22, wday=6, tsource=now), 'Fourth Sunday in February 2021') # last day of month
62-
# 01:59:00 on 01/10/2020
63-
result(test(month=(7, 10), hrs=1, mins=59, tsource=now + 24*3600), 'Time causes month rollover to next legal month')
64-
# 03:00:00 on 01/01/2021
65-
result(test(month=(7, 1), mday=1, tsource=now), 'mday causes month rollover to next year')
66-
# 03:00:00 on 03/03/2021
67-
result(test(month=(7, 3), wday=(2, 6), tsource=now), 'wday causes month rollover to next year')
68-
print('Max runtime {}μs'.format(maxruntime))
30+
yr, mo, md, h, m, s, wd = localtime(t + ts)[:7] # Get absolute time from cron
31+
print(f"Endtime from cron: {h:02d}:{m:02d}:{s:02d} on {md:02d}/{mo:02d}/{yr:02d}")
32+
if t == texp - ts:
33+
print(f"PASS")
34+
else:
35+
print(f"FAIL [{t}]")
36+
fail += 1
37+
print(f"Runtime = {delta}us\n")
38+
39+
40+
now = mktime((2020, 7, 30, 3, 0, 0, 0, 0)) # 3am Thursday (day 3) 30 July 2020
41+
42+
exp = (2020, 7, 31, 1, 5, 0) # Expect 01:05:00 on 31/07/2020
43+
msg = "wday and time both cause 1 day increment."
44+
test(now, exp, msg, wday=4, hrs=(1, 2), mins=5)
45+
46+
exp = (2020, 7, 31, 1, 5, 0) # 01:05:00 on 31/07/2020
47+
msg = "time causes 1 day increment."
48+
test(now, exp, msg, hrs=(1, 2), mins=5)
49+
50+
exp = (2020, 7, 31, 3, 5, 0) # 03:05:00 on 31/07/2020
51+
msg = "wday causes 1 day increment."
52+
test(now, exp, msg, wday=4, mins=5)
53+
54+
exp = (2020, 7, 30, 5, 58, 0) # 05:58:00 on 30/07/2020
55+
msg = "time increment no day change."
56+
test(now, exp, msg, hrs=(5, 23), mins=58)
57+
58+
exp = (2021, 1, 3, 3, 0, 0) # 03:00:00 on 03/01/2021
59+
msg = "month and year rollover, 1st Sunday"
60+
test(now, exp, msg, month=1, wday=6)
61+
62+
exp = (2021, 2, 20, 3, 0, 0) # 03:00:00 on 20/02/2021
63+
msg = "month and year rollover, mday->20 Feb"
64+
test(now, exp, msg, month=2, mday=20)
65+
66+
exp = (2020, 12, 25, 1, 30, 0) # 01:30:00 on 25/12/2020
67+
msg = "Forward to Xmas day, hrs backwards"
68+
test(now, exp, msg, month=12, mday=25, hrs=1, mins=30)
69+
70+
exp = (2020, 12, 6, 23, 5, 15) # 23:05:15 on 06/12/2020
71+
msg = "1st Sunday in Dec 2020"
72+
test(now, exp, msg, month=12, wday=6, hrs=23, mins=5, secs=15)
73+
74+
exp = (2020, 10, 1, 3, 0, 0) # 03:00:00 on 01/10/2020
75+
msg = "Current time on 1st Oct 2020"
76+
test(now, exp, msg, month=10)
77+
78+
exp = (2020, 7, 30, 9, 0, 0) # 09:00:00 on 30/07/2020
79+
msg = "Explicitly specify current month"
80+
test(now, exp, msg, month=7, hrs=9)
81+
82+
exp = (2021, 2, 14, 3, 0, 0) # 03:00:00 on 14/02/2021
83+
msg = "Second Sunday in February 2021"
84+
test(now, exp, msg, month=2, mday=8, wday=6)
85+
86+
exp = (2021, 2, 28, 3, 0, 0) # 03:00:00 on 28/02/2021
87+
msg = "Fourth Sunday in February 2021"
88+
test(now, exp, msg, month=2, mday=22, wday=6) # month end
89+
90+
exp = (2020, 10, 1, 1, 59, 0) # 01:59:00 on 01/10/2020
91+
msg = "Time causes month rollover to next legal month"
92+
test(now + 24 * 3600, exp, msg, month=(7, 10), hrs=1, mins=59)
93+
94+
exp = (2021, 1, 1, 3, 0, 0) # 03:00:00 on 01/01/2021
95+
msg = "mday causes month rollover to next year"
96+
test(now, exp, msg, month=(7, 1), mday=1)
97+
98+
exp = (2021, 3, 3, 3, 0, 0) # 03:00:00 on 03/03/2021
99+
msg = "wday causes month rollover to next year"
100+
test(now, exp, msg, month=(7, 3), wday=(2, 6))
101+
102+
print(f"Max runtime {maxruntime}us")
69103
if fail:
70-
print(fail, 'FAILURES OCCURRED')
104+
print(fail, "FAILURES OCCURRED")
71105
else:
72-
print('ALL TESTS PASSED')
106+
print("ALL TESTS PASSED")

v3/as_drivers/sched/simulate.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# simulate.py Adapt this to simulate scheduled sequences
2+
3+
from time import localtime, mktime
4+
from sched.cron import cron
5+
6+
days = ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
7+
tim = 0 # Global time in secs
8+
9+
def print_time(msg=""):
10+
yr, mo, md, h, m, s, wd = localtime(tim)[:7]
11+
print(f"{msg} {h:02d}:{m:02d}:{s:02d} on {days[wd]} {md:02d}/{mo:02d}/{yr:02d}")
12+
13+
def wait(cr): # Simulate waiting on a cron instance
14+
global tim
15+
tim += 2 # Must always wait >=2s before calling cron again
16+
dt = cr(tim)
17+
hrs, m_s = divmod(dt + 2, 3600) # For neat display add back the 2 secs
18+
mins, secs = divmod(m_s, 60)
19+
print(f"Wait {hrs}hrs {mins}mins {secs}s")
20+
tim += dt
21+
print_time("Time now:")
22+
23+
def set_time(y, month, mday, hrs, mins, secs):
24+
global tim
25+
tim = mktime((y, month, mday, hrs, mins, secs, 0, 0))
26+
print_time("Start at:")
27+
28+
# Adapt the following to emulate the proposed application. Cron args
29+
# secs=0, mins=0, hrs=3, mday=None, month=None, wday=None
30+
31+
def sim(*args):
32+
set_time(*args)
33+
cs = cron(hrs = 0, mins = 59)
34+
wait(cs)
35+
cn = cron(wday=(0, 5), hrs=(1, 10), mins = range(0, 60, 15))
36+
for _ in range(10):
37+
wait(cn)
38+
print("Run payload.\n")
39+
40+
sim(2023, 3, 29, 15, 20, 0) # Start time: year, month, mday, hrs, mins, secs

0 commit comments

Comments
 (0)