Skip to content

Commit abd9f8d

Browse files
vpeterssonclaude
andcommitted
test(viewer): poll-and-decode load_browser stdout via PropertyMock
Copilot flagged that the existing tests pinned process.stdout to a single bytes value, which doesn't match production where sh.RunningCommand.process.stdout is a @Property returning the latest accumulated buffer on each access — so the polling loop was effectively exercising one read instead of N. Switch to mock.PropertyMock with a chunks list so each poll inside load_browser() sees a different buffer; the success-path test now genuinely verifies that the loop re-reads stdout across iterations and finds the handshake on the second poll. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent a637df0 commit abd9f8d

1 file changed

Lines changed: 33 additions & 5 deletions

File tree

tests/test_viewer.py

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,20 +73,46 @@ def test_setup(self) -> None:
7373
self.u.setup()
7474
self.p_loadb.stop()
7575

76+
def _stub_browser_stdout(
77+
self,
78+
browser_proc: mock.Mock,
79+
chunks: list[bytes],
80+
) -> mock.PropertyMock:
81+
"""
82+
sh.RunningCommand.process.stdout is a @property that returns the
83+
latest accumulated buffer on each access. Use PropertyMock so
84+
the test exercises the same poll-and-decode pattern the
85+
production loop relies on, instead of pinning a single bytes
86+
value that would stay identical across iterations.
87+
"""
88+
prop = mock.PropertyMock(side_effect=chunks)
89+
type(browser_proc.process).stdout = prop
90+
return prop
91+
7692
def test_load_browser(self) -> None:
7793
browser_proc = self.m_cmd.return_value.return_value
78-
browser_proc.process.stdout = b'Anthias service start'
94+
# Two stdout reads: an empty buffer on the first poll, then the
95+
# handshake line appended on the second. Verifies that the
96+
# polling loop actually re-reads stdout each iteration.
97+
self._stub_browser_stdout(
98+
browser_proc,
99+
[b'starting up\n', b'starting up\nAnthias service start\n'],
100+
)
79101
browser_proc.is_alive.return_value = True
80102
self.p_cmd.start()
81-
self.u.load_browser()
82-
self.p_cmd.stop()
103+
self.p_sleep.start()
104+
try:
105+
self.u.load_browser()
106+
finally:
107+
self.p_sleep.stop()
108+
self.p_cmd.stop()
83109
self.m_cmd.assert_called_once_with('AnthiasWebview')
84110

85111
def test_load_browser_raises_when_process_exits_before_handshake(
86112
self,
87113
) -> None:
88114
browser_proc = self.m_cmd.return_value.return_value
89-
browser_proc.process.stdout = b''
115+
self._stub_browser_stdout(browser_proc, [b''])
90116
browser_proc.is_alive.return_value = False
91117
self.p_cmd.start()
92118
try:
@@ -99,7 +125,9 @@ def test_load_browser_times_out_when_handshake_never_arrives(
99125
self,
100126
) -> None:
101127
browser_proc = self.m_cmd.return_value.return_value
102-
browser_proc.process.stdout = b'irrelevant noise'
128+
# Two polled reads of the same non-handshake buffer before the
129+
# deadline elapses.
130+
self._stub_browser_stdout(browser_proc, [b'noise', b'still noise'])
103131
browser_proc.is_alive.return_value = True
104132
# Three monotonic() reads: deadline init, one loop iteration
105133
# below the deadline, one above it.

0 commit comments

Comments
 (0)