test update
This commit is contained in:
1 parent
4d502c5911
commit
8cd2a414cd
4 files changed
+380
-4
No files matched your search
+109
-3
@@ -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}
|
||||
|
||||
Reference in new issue
Block a user