Skip to content

Commit a9b1040

Browse files
committed
Add basic WallClockTimer tests
These tests only test the basic functionality, like construction, string representation, etc. The ticking behaviour test will be done in a sepate file in a follow-up commit. Signed-off-by: Leandro Lucarella <[email protected]>
1 parent 4c32631 commit a9b1040

File tree

1 file changed

+103
-0
lines changed

1 file changed

+103
-0
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# License: MIT
2+
# Copyright © 2025 Frequenz Energy-as-a-Service GmbH
3+
4+
"""Basic tests for `WallClockTimer`."""
5+
6+
import re
7+
from datetime import timedelta
8+
from unittest.mock import MagicMock
9+
10+
import pytest
11+
12+
from frequenz.sdk.timeseries._resampling._wall_clock_timer import (
13+
WallClockTimer,
14+
WallClockTimerConfig,
15+
)
16+
17+
from .util import to_seconds, wall_now
18+
19+
pytestmark = pytest.mark.usefixtures("datetime_mock")
20+
21+
22+
@pytest.mark.parametrize(
23+
"interval",
24+
[timedelta(seconds=0.0), timedelta(seconds=-0.01)],
25+
ids=["zero", "negative"],
26+
)
27+
def test_invalid_interval(interval: timedelta) -> None:
28+
"""Test WallClockTimer with invalid intervals raises ValueError."""
29+
with pytest.raises(
30+
ValueError,
31+
match=rf"^interval must be positive, not {re.escape(str(interval))}$",
32+
):
33+
_ = WallClockTimer(interval)
34+
35+
36+
def test_custom_config() -> None:
37+
"""Test WallClockTimer with a custom configuration."""
38+
interval = timedelta(seconds=5)
39+
config = MagicMock(name="config", spec=WallClockTimerConfig)
40+
41+
timer = WallClockTimer(interval, config=config, auto_start=False)
42+
assert timer.interval == interval
43+
assert timer.config is config
44+
45+
46+
def test_auto_start_default() -> None:
47+
"""Test WallClockTimer uses auto_start=True by default."""
48+
interval = timedelta(seconds=1)
49+
timer = WallClockTimer(interval)
50+
assert timer.interval == interval
51+
assert timer.config == WallClockTimerConfig.from_interval(interval)
52+
assert timer.is_running
53+
assert timer.next_tick_time == wall_now() + interval
54+
55+
56+
def test_auto_start_disabled() -> None:
57+
"""Test WallClockTimer does not start when auto_start=False."""
58+
interval = timedelta(seconds=1)
59+
timer = WallClockTimer(interval, auto_start=False)
60+
assert timer.interval == interval
61+
assert timer.config == WallClockTimerConfig.from_interval(interval)
62+
assert not timer.is_running
63+
assert timer.next_tick_time is None
64+
65+
66+
def test_reset() -> None:
67+
"""Test WallClockTimer.reset() starts the timer and sets next_tick_time relative to now."""
68+
interval = timedelta(seconds=3)
69+
timer = WallClockTimer(interval, auto_start=False)
70+
timer.reset()
71+
assert timer.is_running
72+
assert timer.next_tick_time is not None
73+
assert to_seconds(timer.next_tick_time) == pytest.approx(
74+
to_seconds(wall_now() + interval)
75+
)
76+
77+
78+
def test_close() -> None:
79+
"""Test WallClockTimer.close() stops the timer and returns no next_tick_time."""
80+
interval = timedelta(seconds=2)
81+
timer = WallClockTimer(interval, auto_start=True)
82+
timer.close()
83+
assert not timer.is_running
84+
assert timer.next_tick_time is None
85+
86+
87+
def test_str() -> None:
88+
"""Test __str__ returns only the interval."""
89+
interval = timedelta(seconds=4)
90+
timer = WallClockTimer(interval, auto_start=False)
91+
assert str(timer) == f"WallClockTimer({interval})"
92+
93+
94+
def test_repr() -> None:
95+
"""Test __repr__ includes interval and running state."""
96+
interval = timedelta(seconds=4)
97+
timer = WallClockTimer(interval, auto_start=False)
98+
assert repr(timer) == f"WallClockTimer<interval={interval!r}, is_running=False>"
99+
timer.reset()
100+
assert (
101+
repr(timer) == f"WallClockTimer<interval={interval!r}, is_running=True, "
102+
f"next_tick_time={timer.next_tick_time!r}>"
103+
)

0 commit comments

Comments
 (0)