66
77import asyncio
88import enum
9- from collections .abc import Iterator
109from datetime import timedelta
1110
1211import async_solipsism
2221)
2322
2423
25- # Setting 'autouse' has no effect as this method replaces the event loop for all tests in the file.
26- @pytest .fixture ()
27- def event_loop () -> Iterator [async_solipsism .EventLoop ]:
28- """Replace the loop with one that doesn't interact with the outside world."""
29- loop = async_solipsism .EventLoop ()
30- yield loop
31- loop .close ()
24+ @pytest .fixture (autouse = True )
25+ def event_loop_policy () -> async_solipsism .EventLoopPolicy :
26+ """Return an event loop policy that uses the async solipsism event loop."""
27+ return async_solipsism .EventLoopPolicy ()
3228
3329
34- # We give some extra room (dividing by 10) to the max and min to avoid flaky errors
35- # failing when getting too close to the limits, as these are not realistic scenarios and
36- # weird things can happen.
37- _max_timedelta_microseconds = int (timedelta .max .total_seconds () * 1_000_000 / 10 )
30+ _max_timedelta_microseconds = (
31+ int (
32+ timedelta .max .total_seconds () * 1_000_000 ,
33+ )
34+ - 1
35+ )
3836
39- _min_timedelta_microseconds = int (timedelta .min .total_seconds () * 1_000_000 / 10 )
37+ _min_timedelta_microseconds = (
38+ int (
39+ timedelta .min .total_seconds () * 1_000_000 ,
40+ )
41+ + 1
42+ )
4043
4144_calculate_next_tick_time_args = {
4245 "now" : st .integers (),
@@ -136,20 +139,51 @@ def test_policy_skip_missed_and_resync_examples() -> None:
136139
137140
138141@hypothesis .given (
139- tolerance = st .integers (min_value = _min_timedelta_microseconds , max_value = - 1 )
142+ tolerance = st .floats (
143+ min_value = timedelta .min .total_seconds (),
144+ max_value = - 1 ,
145+ exclude_max = False ,
146+ allow_nan = False ,
147+ allow_infinity = False ,
148+ ),
140149)
141- def test_policy_skip_missed_and_drift_invalid_tolerance (tolerance : int ) -> None :
150+ def test_policy_skip_missed_and_drift_invalid_tolerance (tolerance : float ) -> None :
142151 """Test the SkipMissedAndDrift policy raises an error for invalid tolerances."""
143152 with pytest .raises (ValueError , match = "delay_tolerance must be positive" ):
144153 SkipMissedAndDrift (delay_tolerance = timedelta (microseconds = tolerance ))
145154
146155
147156@hypothesis .given (
148- tolerance = st .integers (min_value = 0 , max_value = _max_timedelta_microseconds ),
157+ tolerance = st .floats (
158+ min_value = 0 ,
159+ max_value = timedelta .max .total_seconds (),
160+ allow_nan = False ,
161+ allow_infinity = False ,
162+ ),
149163 ** _calculate_next_tick_time_args ,
150164)
165+ # We add some particular tests cases that were problematic in the past. See:
166+ # https://github.com/frequenz-floss/frequenz-channels-python/pull/347
167+ @hypothesis .example (
168+ tolerance = 171726190479152832.0 ,
169+ now = 171_726_190_479_152_817 ,
170+ scheduled_tick_time = - 1 ,
171+ interval = 1 ,
172+ )
173+ @hypothesis .example (
174+ tolerance = 171726190479152830.0 ,
175+ now = 171_726_190_479_152_817 ,
176+ scheduled_tick_time = - 1 ,
177+ interval = 1 ,
178+ )
179+ @hypothesis .example (
180+ tolerance = 171726190479152831.0 ,
181+ now = 171_726_190_479_152_817 ,
182+ scheduled_tick_time = - 1 ,
183+ interval = 1 ,
184+ )
151185def test_policy_skip_missed_and_drift (
152- tolerance : int , now : int , scheduled_tick_time : int , interval : int
186+ tolerance : float , now : int , scheduled_tick_time : int , interval : int
153187) -> None :
154188 """Test the SkipMissedAndDrift policy."""
155189 hypothesis .assume (now >= scheduled_tick_time )
@@ -297,10 +331,10 @@ async def test_timer_construction_wrong_args() -> None:
297331 )
298332
299333
300- async def test_timer_autostart (
301- event_loop : async_solipsism .EventLoop , # pylint: disable=redefined-outer-name
302- ) -> None :
334+ async def test_timer_autostart () -> None :
303335 """Test the autostart of a periodic timer."""
336+ event_loop = asyncio .get_running_loop ()
337+
304338 timer = Timer (timedelta (seconds = 1.0 ), TriggerAllMissed ())
305339
306340 # We sleep some time, less than the interval, and then receive from the
@@ -312,10 +346,10 @@ async def test_timer_autostart(
312346 assert event_loop .time () == pytest .approx (1.0 )
313347
314348
315- async def test_timer_autostart_with_delay (
316- event_loop : async_solipsism .EventLoop , # pylint: disable=redefined-outer-name
317- ) -> None :
349+ async def test_timer_autostart_with_delay () -> None :
318350 """Test the autostart of a periodic timer with a delay."""
351+ event_loop = asyncio .get_running_loop ()
352+
319353 timer = Timer (
320354 timedelta (seconds = 1.0 ), TriggerAllMissed (), start_delay = timedelta (seconds = 0.5 )
321355 )
@@ -344,9 +378,10 @@ class _StartMethod(enum.Enum):
344378@pytest .mark .parametrize ("start_method" , list (_StartMethod ))
345379async def test_timer_no_autostart (
346380 start_method : _StartMethod ,
347- event_loop : async_solipsism .EventLoop , # pylint: disable=redefined-outer-name
348381) -> None :
349382 """Test a periodic timer when it is not automatically started."""
383+ event_loop = asyncio .get_running_loop ()
384+
350385 timer = Timer (
351386 timedelta (seconds = 1.0 ),
352387 TriggerAllMissed (),
@@ -377,10 +412,10 @@ async def test_timer_no_autostart(
377412 assert event_loop .time () == pytest .approx (1.5 )
378413
379414
380- async def test_timer_trigger_all_missed (
381- event_loop : async_solipsism .EventLoop , # pylint: disable=redefined-outer-name
382- ) -> None :
415+ async def test_timer_trigger_all_missed () -> None :
383416 """Test a timer using the TriggerAllMissed policy."""
417+ event_loop = asyncio .get_running_loop ()
418+
384419 interval = 1.0
385420 timer = Timer (timedelta (seconds = interval ), TriggerAllMissed ())
386421
@@ -438,10 +473,10 @@ async def test_timer_trigger_all_missed(
438473 assert drift == pytest .approx (timedelta (seconds = 0.0 ))
439474
440475
441- async def test_timer_skip_missed_and_resync (
442- event_loop : async_solipsism .EventLoop , # pylint: disable=redefined-outer-name
443- ) -> None :
476+ async def test_timer_skip_missed_and_resync () -> None :
444477 """Test a timer using the SkipMissedAndResync policy."""
478+ event_loop = asyncio .get_running_loop ()
479+
445480 interval = 1.0
446481 timer = Timer (timedelta (seconds = interval ), SkipMissedAndResync ())
447482
@@ -489,10 +524,10 @@ async def test_timer_skip_missed_and_resync(
489524 assert drift == pytest .approx (timedelta (seconds = 0.0 ))
490525
491526
492- async def test_timer_skip_missed_and_drift (
493- event_loop : async_solipsism .EventLoop , # pylint: disable=redefined-outer-name
494- ) -> None :
527+ async def test_timer_skip_missed_and_drift () -> None :
495528 """Test a timer using the SkipMissedAndDrift policy."""
529+ event_loop = asyncio .get_running_loop ()
530+
496531 interval = 1.0
497532 tolerance = 0.1
498533 timer = Timer (
@@ -553,10 +588,10 @@ async def test_timer_skip_missed_and_drift(
553588 assert drift == pytest .approx (timedelta (seconds = 0.0 ))
554589
555590
556- async def test_timer_reset_with_new_interval (
557- event_loop : async_solipsism .EventLoop , # pylint: disable=redefined-outer-name
558- ) -> None :
591+ async def test_timer_reset_with_new_interval () -> None :
559592 """Test resetting the timer with a new interval."""
593+ event_loop = asyncio .get_running_loop ()
594+
560595 initial_interval = timedelta (seconds = 1.0 )
561596 new_interval = timedelta (seconds = 2.0 )
562597 timer = Timer (initial_interval , TriggerAllMissed ())
0 commit comments