[Test] (scripts/ci): keep the TCP lane controls' errors out of annotations

- the red controls ran spawn_lane_parity and junit_tally main() uncaptured, so every passing run showed their ::error:: line as a failure annotation on the pipe-gates job
- quiet_main captures a gate's output; each red control now also asserts the reason it printed
This commit is contained in:
2026-10-17 07:50:08 -04:00
parent 53e4c423ee
commit 4f34da0ef1
+24 -6
View File
@@ -8,7 +8,9 @@
# End of Source File Header # End of Source File Header
"""Executable negative controls for TCP lane accounting and supervisor lifecycle.""" """Executable negative controls for TCP lane accounting and supervisor lifecycle."""
import contextlib
import importlib.util import importlib.util
import io
import json import json
import os import os
from pathlib import Path from pathlib import Path
@@ -30,6 +32,16 @@ def module(name):
return loaded return loaded
def quiet_main(main):
"""A gate's main() with its output captured, as (exit status, text). A red control prints the
gate's real ::error:: line, which a runner would turn into a failure annotation on a passing
step."""
captured = io.StringIO()
with contextlib.redirect_stdout(captured), contextlib.redirect_stderr(captured):
status = main()
return status, captured.getvalue()
class Accounting(unittest.TestCase): class Accounting(unittest.TestCase):
def check_tcp_proof(self, backend): def check_tcp_proof(self, backend):
tally = module('junit_tally') tally = module('junit_tally')
@@ -134,12 +146,14 @@ class Accounting(unittest.TestCase):
patch.object(parity, 'lane_names', return_value=set()), \ patch.object(parity, 'lane_names', return_value=set()), \
patch.object(parity, 'whole_build_discovery', return_value={'tests': []}), \ patch.object(parity, 'whole_build_discovery', return_value={'tests': []}), \
patch.object(sys, 'argv', ['spawn_lane_parity.py', 'unused']): patch.object(sys, 'argv', ['spawn_lane_parity.py', 'unused']):
self.assertEqual(parity.main(), 1) status, text = quiet_main(parity.main)
self.assertEqual(status, 1)
self.assertIn(f"integration-tcp: missing=['{a}'], extra=['{b}']", text)
with patch.object(parity, 'lane_cases', return_value={a}), \ with patch.object(parity, 'lane_cases', return_value={a}), \
patch.object(parity, 'lane_names', return_value=set()), \ patch.object(parity, 'lane_names', return_value=set()), \
patch.object(parity, 'whole_build_discovery', return_value={'tests': []}), \ patch.object(parity, 'whole_build_discovery', return_value={'tests': []}), \
patch.object(sys, 'argv', ['spawn_lane_parity.py', 'unused']): patch.object(sys, 'argv', ['spawn_lane_parity.py', 'unused']):
self.assertEqual(parity.main(), 0) self.assertEqual(quiet_main(parity.main)[0], 0)
@staticmethod @staticmethod
def discovered(name, **properties): def discovered(name, **properties):
@@ -190,17 +204,21 @@ class Accounting(unittest.TestCase):
root = Path(directory) root = Path(directory)
junit, discovery = root / 'junit.xml', root / 'lane.json' junit, discovery = root / 'junit.xml', root / 'lane.json'
junit.write_text('<testsuite><testcase name="TcpLane.SupervisorProtocolControls" /></testsuite>') junit.write_text('<testsuite><testcase name="TcpLane.SupervisorProtocolControls" /></testsuite>')
for tests, rc in ((broken, 1), (sound, 0)): for tests, rc, said in ((broken, 1, tus), (sound, 0, '')):
discovery.write_text(json.dumps({'tests': tests})) discovery.write_text(json.dumps({'tests': tests}))
with patch.object(sys, 'argv', ['junit_tally.py', str(junit), '--discovery', str(discovery), with patch.object(sys, 'argv', ['junit_tally.py', str(junit), '--discovery', str(discovery),
'--require-entry-passed', 'TcpLane.SupervisorProtocolControls']): '--require-entry-passed', 'TcpLane.SupervisorProtocolControls']):
self.assertEqual(tally.main(), rc) status, text = quiet_main(tally.main)
for tests, rc in ((broken, 1), (sound, 0)): self.assertEqual(status, rc)
self.assertIn(said, text)
for tests, rc, said in ((broken, 1, 'must hold RESOURCE_LOCK mobilegl-tcp'), (sound, 0, '')):
with patch.object(parity, 'lane_cases', return_value={'TriangleScenario.Draw'}), \ with patch.object(parity, 'lane_cases', return_value={'TriangleScenario.Draw'}), \
patch.object(parity, 'lane_names', return_value=set()), \ patch.object(parity, 'lane_names', return_value=set()), \
patch.object(parity, 'whole_build_discovery', return_value={'tests': tests}), \ patch.object(parity, 'whole_build_discovery', return_value={'tests': tests}), \
patch.object(sys, 'argv', ['spawn_lane_parity.py', 'unused']): patch.object(sys, 'argv', ['spawn_lane_parity.py', 'unused']):
self.assertEqual(parity.main(), rc) status, text = quiet_main(parity.main)
self.assertEqual(status, rc)
self.assertIn(said, text)
class DeviceIdleExemption(unittest.TestCase): class DeviceIdleExemption(unittest.TestCase):