A new epheleral_condition sensor type + fix issue with dangling callback

This commit is contained in:
Pierre committed 2026-10-16 08:49:57 +02:00
1 parent 3b0c24e795
commit b97445ce92
4 files changed
+369 -13

No files matched your search

+58 -5
View File
@@ -27,6 +27,7 @@ class RetainConditionTests(unittest.TestCase):
def setUp(self):
self.sensor = RetainCondition()
self.sensor.cb_handle = None
self.sensor._timer_generation = 0
self.sensor.retain_time = 15
self.sensor.reset_retain_time_on_new_success = True
self.state = 'off'
@@ -41,6 +42,54 @@ class RetainConditionTests(unittest.TestCase):
def set_state(self, *, state):
self.state = state
def queued_expiry(self):
call = self.sensor.ad_api.run_in.call_args
callback = call.args[0]
kwargs = dict(call.kwargs)
return lambda: callback(kwargs)
def test_return_at_expiry_ignores_queued_callback(self):
self.sensor.on_condition_succeed()
self.sensor.on_condition_fail()
queued = self.queued_expiry()
# The scheduler has already removed the timer and queued its callback.
self.sensor.ad_api.cancel_timer.return_value = False
self.sensor.condition.evaluate.return_value = Result.Succeeded
self.sensor.on_condition_succeed()
queued()
self.assertEqual(self.state, 'on')
self.sensor.condition.evaluate.assert_not_called()
self.sensor.ad_api.cancel_timer.assert_called_once_with('timer', silent=True)
def test_stale_expiry_cannot_shorten_new_retention(self):
self.sensor.ad_api.run_in.side_effect = ['old-timer', 'new-timer']
self.sensor.on_condition_succeed()
self.sensor.on_condition_fail()
queued = self.queued_expiry()
self.sensor.on_condition_succeed()
self.sensor.on_condition_fail()
current_expiry = self.queued_expiry()
queued()
self.assertEqual(self.state, 'on')
self.assertEqual(self.sensor.cb_handle, 'new-timer')
current_expiry()
self.assertEqual(self.state, 'off')
self.assertIsNone(self.sensor.cb_handle)
def test_stale_expiry_cannot_forget_timer_while_condition_disabled(self):
self.sensor.ad_api.run_in.side_effect = ['old-timer', 'new-timer']
self.sensor.on_condition_succeed()
self.sensor.on_condition_fail()
queued = self.queued_expiry()
self.sensor.on_condition_succeed()
self.sensor.on_condition_fail()
self.sensor.condition.evaluate.return_value = Result.Disabled
queued()
self.assertEqual(self.sensor.cb_handle, 'new-timer')
self.assertEqual(self.state, 'on')
self.sensor.on_condition_succeed()
self.assertEqual(self.sensor.ad_api.cancel_timer.call_args.args, ('new-timer',))
def test_long_lived_signal_gets_full_delay_after_loss(self):
self.sensor.on_condition_succeed()
self.assertEqual(self.state, 'on')
@@ -48,15 +97,16 @@ class RetainConditionTests(unittest.TestCase):
# No timer can expire while the signal stays valid.
self.sensor.on_condition_fail()
self.assertEqual(self.state, 'on')
self.sensor.ad_api.run_in.assert_called_once_with(self.sensor.on_time_elapse, 15)
self.sensor.on_time_elapse({})
self.sensor.ad_api.run_in.assert_called_once_with(
self.sensor.on_time_elapse, 15, generation=self.sensor._timer_generation)
self.queued_expiry()()
self.assertEqual(self.state, 'off')
def test_returning_signal_cancels_off_and_next_loss_gets_new_delay(self):
self.sensor.on_condition_succeed()
self.sensor.on_condition_fail()
self.sensor.on_condition_succeed()
self.sensor.ad_api.cancel_timer.assert_called_once_with('timer')
self.sensor.ad_api.cancel_timer.assert_called_once_with('timer', silent=True)
self.assertIsNone(self.sensor.cb_handle)
self.assertEqual(self.state, 'on')
self.sensor.on_condition_fail()
@@ -79,8 +129,11 @@ class RetainConditionTests(unittest.TestCase):
with self.subTest(result=result):
self.state = 'on'
self.sensor.condition.evaluate.return_value = result
self.sensor.on_time_elapse({})
self.sensor.register_callback()
self.queued_expiry()()
self.assertEqual(self.state, 'on')
self.assertIsNone(self.sensor.cb_handle)
self.sensor.condition.evaluate.assert_called_with()
def test_explicit_no_reset_preserves_fixed_window(self):
self.sensor.reset_retain_time_on_new_success = False
@@ -88,7 +141,7 @@ class RetainConditionTests(unittest.TestCase):
self.sensor.on_condition_fail()
self.assertEqual(self.state, 'on')
self.sensor.condition.evaluate.return_value = Result.Succeeded
self.sensor.on_time_elapse({})
self.queued_expiry()()
self.sensor.on_condition_fail()
self.assertEqual(self.state, 'off')