1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
#!/usr/bin/python3
# Tests for cal-notif. Run: /usr/bin/python3 test_cal_notif.py
# Copyright (C) 2026 Danilo M. <danix@danix.xyz>
# Licensed under the GNU General Public License version 2 only.
import importlib.machinery
import importlib.util
import os
import tempfile
import time
from datetime import datetime, timedelta
from pathlib import Path
from zoneinfo import ZoneInfo
os.environ["TZ"] = "Europe/Rome" # all-day and floating times are local
time.tzset()
HERE = Path(__file__).resolve().parent
_loader = importlib.machinery.SourceFileLoader("cal_notif", str(HERE / "cal-notif"))
_spec = importlib.util.spec_from_loader("cal_notif", _loader)
cn = importlib.util.module_from_spec(_spec)
_loader.exec_module(cn)
ROME = ZoneInfo("Europe/Rome")
H, M, D = timedelta(hours=1), timedelta(minutes=1), timedelta(days=1)
def ics(*events):
body = "".join(f"BEGIN:VEVENT\n{e.strip()}\nEND:VEVENT\n" for e in events)
return f"BEGIN:VCALENDAR\nVERSION:2.0\n{body}END:VCALENDAR\n".encode()
def at(y, mo, d, h=0, mi=0):
return datetime(y, mo, d, h, mi, tzinfo=ROME)
def test_durations():
assert cn.parse_duration("1d 2h 15m") == D + 2 * H + 15 * M
assert cn.parse_duration("90m") == 90 * M
assert cn.parse_duration("0m") == timedelta()
for bad in ("", "5", "1x", "m", "2h1d"):
try:
cn.parse_duration(bad)
raise AssertionError(bad)
except ValueError:
pass
assert cn.fmt_duration(D + 2 * H) == "1d 2h"
assert cn.fmt_duration(timedelta()) == "0m"
assert cn.spoken_it(10 * M) == "dieci minuti"
assert cn.spoken_it(H) == "un'ora"
assert cn.spoken_it(D + 2 * H) == "un giorno e due ore"
assert cn.spoken_it(2 * D + H + 21 * M) == "due giorni, un'ora e ventuno minuti"
assert cn.number_it(28) == "ventotto" and cn.number_it(45) == "quarantacinque"
if __name__ == "__main__":
for name, fn in list(globals().items()):
if name.startswith("test_"):
fn()
print("ok", name)
|