test update

This commit is contained in:
Pierre committed 2026-11-13 13:10:37 +01:00
1 parent 4d502c5911
commit 8cd2a414cd
4 files changed
+380 -4

No files matched your search

+109 -3
View File
@@ -20,6 +20,7 @@ class Harness(Vertex2):
self._status_requested = threading.Event()
self._last_logged_status = None
self._input_5v_states = {}
self._cancel_input_5v_refresh()
self._schedule_input_5v_refresh = Mock()
self._connection_lock = threading.Lock()
self._interrupt_connection = None
@@ -197,6 +198,54 @@ class PowerTests(unittest.TestCase):
app.log_warning.assert_not_called()
class StatusTimeoutDiagnosticsTests(unittest.TestCase):
def test_timeout_reports_missing_fields_and_partial_response(self):
for silent in (True, False):
with self.subTest(silent=silent):
app = Harness()
app.video_tx_signal_sensor = Mock()
app.video_tx_signal_sensors = {}
app.allm_sensor = Mock()
app.allm_sensors = {}
app._reset_no_signal_spam = Mock()
app.log_warning.side_effect = lambda message: app._interrupt_stop.set()
connection = Mock()
fields = ('rx0', 'rx1', 'tx0', 'tx1', 'tx0sink', 'tx1sink',
'aud0', 'aud1', 'audout', 'spd0')
response = ('\n'.join(f'{field.upper()}: no signal' for field in fields)
+ '\ninsel 0 0\nint\nSPD1: incom').encode('ascii')
clock = [100.0]
calls = [0]
def recv(size):
calls[0] += 1
if calls[0] == 1:
return response
clock[0] = 106.0
if silent:
raise socket.timeout()
return b'plete'
connection.recv.side_effect = recv
context = Mock(__enter__=Mock(return_value=connection),
__exit__=Mock(return_value=False))
with patch('vertex2.socket.create_connection', return_value=context), \
patch('vertex2.time.monotonic', side_effect=lambda: clock[0]):
app._interrupt_listener(app._interrupt_stop)
app.log_warning.assert_called_once()
message = app.log_warning.call_args.args[0]
self.assertIn("missing=['spd1']", message)
self.assertIn('elapsed=6.00s, limit=5s', message)
self.assertIn('interrupts=1', message)
self.assertIn(f'bytes={len(response) + (0 if silent else 5)}', message)
self.assertIn("'insel 0 0'", message)
partial = 'incom' if silent else 'incomplete'
self.assertIn(f"partial_line='SPD1: {partial}'", message)
self.assertIn('reconfigure=True', message)
app.run_in.assert_not_called()
app.unreachable_sensor.set_state.assert_called_with(state='on')
class ReconfigurationTests(unittest.TestCase):
def test_reconnect_reapplies_current_plus5_and_selections(self):
app = Harness()
@@ -360,10 +409,11 @@ class StatusChangeTests(unittest.TestCase):
app._refresh_input_5v_sensors.reset_mock()
app._apply_vertex2_status(dict(self.status, **metadata))
app._refresh_input_5v_sensors.assert_not_called()
app.run_in.assert_called_once_with(app._refresh_input_5v_sensors, 0.1)
app.run_in.assert_called_once_with(app._on_input_5v_refresh, 1,
generation=app._input_5v_refresh_generation)
callback = app.run_in.call_args.args[0]
callback({})
app._refresh_input_5v_sensors.assert_called_once_with({})
callback(app.run_in.call_args.kwargs)
app._refresh_input_5v_sensors.assert_called_once_with()
def test_identical_status_is_silent_but_still_applied(self):
app = self.app
@@ -668,6 +718,62 @@ class InputSelectTests(unittest.TestCase):
class Input5VTests(unittest.TestCase):
def test_bursts_share_one_read_and_retry_then_recover(self):
from requests.exceptions import ConnectTimeout
app = self.app
del app._schedule_input_5v_refresh
for _ in range(10):
app._schedule_input_5v_refresh()
self.assertEqual(app.run_in.call_count, 1)
first = app.run_in.call_args
with patch('vertex2.get', side_effect=ConnectTimeout('test')):
first.args[0](first.kwargs)
app.log_warning.assert_not_called()
self.assertEqual(app.run_in.call_count, 2)
retry = app.run_in.call_args
self.assertEqual(retry.args[1], 5)
for _ in range(10):
app._schedule_input_5v_refresh()
self.assertEqual(app.run_in.call_count, 2)
with patch('vertex2.get') as get:
get.return_value.json.return_value = {'rx0in5v': '2'}
retry.args[0](retry.kwargs)
get.assert_called_once()
self.assertEqual(app._input_5v_states[0], 'on')
self.assertEqual(app._input_5v_failures, 0)
self.assertIsNone(app._input_5v_refresh_handle)
app.log_warning.assert_not_called()
self.assertTrue(any('restored after' in c.args[0] for c in app.log_info.call_args_list))
def test_failed_retry_warns_once_without_an_endless_retry_loop(self):
from requests.exceptions import ConnectTimeout
app = self.app
del app._schedule_input_5v_refresh
with patch('vertex2.get', side_effect=ConnectTimeout('test')):
app._refresh_input_5v_sensors()
retry = app.run_in.call_args
retry.args[0](retry.kwargs)
app._refresh_input_5v_sensors()
app.log_warning.assert_called_once()
self.assertIn('attempt=2', app.log_warning.call_args.args[0])
self.assertEqual(app.run_in.call_count, 1)
def test_power_off_cancels_retry_and_discards_stale_callback(self):
from requests.exceptions import ConnectTimeout
app = self.app
del app._schedule_input_5v_refresh
with patch('vertex2.get', side_effect=ConnectTimeout('test')):
app._refresh_input_5v_sensors()
retry = app.run_in.call_args
handle = app._input_5v_refresh_handle
app._suspend_vertex2()
app.cancel_timer.assert_any_call(handle, silent=True)
# Even if power returns before a cancelled callback arrives, ignore it.
with patch('vertex2.get') as get:
retry.args[0](retry.kwargs)
get.assert_not_called()
self.assertEqual(app._input_5v_failures, 0)
def setUp(self):
self.app = app = SelectHarness()
app.input_names = {'AppleTV': 0, 'Xbox': 1}