class TestActionTimerTick(unittest.TestCase): "Test the progress_callback functionality of the ActionTimer" def setUp(self) -> None: super().setUp() self.progress_callback = Mock() self.action_duration = 2 self.action_timer = ActionTimer(self.action_duration, self.progress_callback) def assertTickCall( self, start_time: float, end_time: float, prev_progress: float, ) -> float: self.action_timer.tick(end_time) # recalculate the duration to avoid floating-point precision errors progress = (end_time - start_time) / self.action_duration self.progress_callback.assert_called_with( StationCode.BE, Claimant.ZONE_1, progress, prev_progress, ) return progress def assertCallCount(self, call_count: int, context: str) -> None: self.assertEqual( self.progress_callback.call_count, call_count, f"Incorrect number of calls of progress_callback {context}" f" ({self.progress_callback.call_args_list})", ) def test_successful_completion(self) -> None: """ Test that progress_callback is called with appropriate arguments at the start and completion of the timer. Namely the progress parameter should be 0 when the timer starts and TIMER_COMPLETE when the timer action is successfully completed. Once TIMER_COMPLETE or TIMER_EXPIRE is parsed to progress_callback, the timer item is removed from the internal dict and should not cause progress_callback to be called on subsequent calls of tick(). """ start_time = random.uniform(0, 1000) self.action_timer.begin_action(StationCode.BE, Claimant.ZONE_1, start_time) self.progress_callback.assert_called_with(StationCode.BE, Claimant.ZONE_1, 0, 0) used_duration = random.uniform(1.8, 2.2) self.action_timer.has_begun_action_in_time_window( StationCode.BE, Claimant.ZONE_1, start_time + used_duration, ) self.progress_callback.assert_called_with( StationCode.BE, Claimant.ZONE_1, ActionTimer.TIMER_COMPLETE, 0, ) self.action_timer.tick(start_time + used_duration) self.assertCallCount(2, "after action completion") def test_expired_timer(self) -> None: """ Test that progress_callback is called with appropriate arguments at the start and expiry of the timer. Namely the progress parameter should be 0 when the timer starts and TIMER_EXPIRE when the timer expires. """ start_time = random.uniform(0, 1000) self.action_timer.begin_action(StationCode.BE, Claimant.ZONE_1, start_time) self.progress_callback.assert_called_with(StationCode.BE, Claimant.ZONE_1, 0, 0) # make timer expire used_duration = random.uniform(2.3, 10) self.action_timer.tick(start_time + used_duration) self.progress_callback.assert_called_with( StationCode.BE, Claimant.ZONE_1, ActionTimer.TIMER_EXPIRE, 0, ) self.action_timer.tick(start_time + used_duration) self.assertCallCount(2, "after timer expired") def test_tick_call(self) -> None: """ Test that the progress_callback method is called will the given progress on each call to ActionTimer.tick """ start_time = random.uniform(0, 1000) self.action_timer.begin_action(StationCode.BE, Claimant.ZONE_1, start_time) prev_progress = self.assertTickCall(start_time, start_time + 0.9, 0) self.assertTickCall(start_time, start_time + 2.1, prev_progress) self.assertCallCount(3, "")
class TestActionTimer(unittest.TestCase): def setUp(self) -> None: super().setUp() self.action_timer = ActionTimer(1.9) def assertBegunInsideDuration(self, duration: float, expected_result: bool) -> None: start_time = random.uniform(0, 1000) self.action_timer.begin_action(StationCode.PN, Claimant.ZONE_1, start_time) actual_result = self.action_timer.has_begun_action_in_time_window( StationCode.PN, Claimant.ZONE_1, start_time + duration, ) self.assertEqual( actual_result, expected_result, f"Timer gave incorect result with duration {duration}", ) def test_exact_time(self) -> None: self.assertBegunInsideDuration(2, True) def test_too_short_time(self) -> None: self.assertBegunInsideDuration(1.7, False) def test_too_long_time(self) -> None: self.assertBegunInsideDuration(2.2, False) def test_marginal_short_time(self) -> None: self.assertBegunInsideDuration(1.72, True) def test_marginal_long_time(self) -> None: self.assertBegunInsideDuration(2.08, True) def test_different_stations(self) -> None: start_time = random.uniform(0, 1000) # Start action with station PN self.action_timer.begin_action(StationCode.PN, Claimant.ZONE_1, start_time) # Attempt to complete action with SZ result = self.action_timer.has_begun_action_in_time_window( StationCode.SZ, Claimant.ZONE_1, start_time + 1.9, ) self.assertFalse(result) def test_different_claimants(self) -> None: start_time = random.uniform(0, 1000) # Zone 0 starts action self.action_timer.begin_action(StationCode.PN, Claimant.ZONE_0, start_time) # Zone 1 attempts to complete action result = self.action_timer.has_begun_action_in_time_window( StationCode.PN, Claimant.ZONE_1, start_time + 1.9, ) self.assertFalse(result)