You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
_start_input_reader_thread in src/copilot_usage/cli.py (lines 184–212) is exercised only through integration-level tests that monkeypatch builtins.input and drive the full _interactive_loop. Three invariants are never directly asserted:
KeyboardInterrupt → _FALLBACK_EOF path — the thread's except (EOFError, KeyboardInterrupt) clause handles both cases, but only the EOFError branch is exercised (via test_interactive_loop_fallback_eof_exits_cleanly). There is no test — at any level — that injects KeyboardInterrupt into the input() call inside the thread and asserts _FALLBACK_EOF lands on the queue.
Normal queue behavior — the happy path (stripped user input placed on the queue) is implicitly covered by the full interactive-loop integration tests but is never isolated to verify the function's contract in isolation.
Missing Test Scenarios
Add a TestStartInputReaderThread class in tests/copilot_usage/test_cli.py with:
1. Daemon flag
deftest_thread_is_daemon(self, monkeypatch: pytest.MonkeyPatch) ->None:
"""Spawned thread must be daemon so it cannot block process exit."""importcopilot_usage.cliascli_modmonkeypatch.setattr("builtins.input", lambda: (_for_in ()).throw(EOFError()))
q=_start_input_reader_thread()
# drain sentinel so thread exitsq.get(timeout=1.0)
# The threading.Thread object is gone by now, but we can capture it.
(Alternatively, patch threading.Thread to capture the daemon kwarg before forwarding to the real constructor.)
The daemon-flag test is the highest-priority regression guard. Removing daemon=True from the threading.Thread(...) call would not be caught by any existing test — the integration tests would hang indefinitely waiting for the reader thread to exit, eventually being killed by CI timeout (the exact failure mode described in issue #1012).
Files to Modify
tests/copilot_usage/test_cli.py — add TestStartInputReaderThread class after TestReadLineNonblocking (currently at line 2145)
Import _start_input_reader_thread and _FALLBACK_EOF alongside the existing private imports at the top of the test file
Warning
⚠️ Firewall blocked 1 domain
The following domain was blocked by the firewall during workflow execution:
pypi.org
To allow these domains, add them to the network.allowed list in your workflow frontmatter:
Root Cause
_start_input_reader_threadinsrc/copilot_usage/cli.py(lines 184–212) is exercised only through integration-level tests that monkeypatchbuiltins.inputand drive the full_interactive_loop. Three invariants are never directly asserted:daemon=Trueflag — the thread is created withdaemon=Trueto ensure it cannot prevent process exit. This is the exact property documented inCODING_GUIDELINES.mdas the root cause of the CI hang fixed in issue [aw][code health] Interactive mode auto-refresh silently broken on Windows (select.selectstdin incompatibility) #1012 / PR fix: cross-platform non-blocking stdin for interactive mode auto-refresh (#1012) #1015. Ifdaemon=Truewere accidentally removed, no existing test would catch it — the integration tests would simply hang until a CI timeout killed them.KeyboardInterrupt→_FALLBACK_EOFpath — the thread'sexcept (EOFError, KeyboardInterrupt)clause handles both cases, but only theEOFErrorbranch is exercised (viatest_interactive_loop_fallback_eof_exits_cleanly). There is no test — at any level — that injectsKeyboardInterruptinto theinput()call inside the thread and asserts_FALLBACK_EOFlands on the queue.Normal queue behavior — the happy path (stripped user input placed on the queue) is implicitly covered by the full interactive-loop integration tests but is never isolated to verify the function's contract in isolation.
Missing Test Scenarios
Add a
TestStartInputReaderThreadclass intests/copilot_usage/test_cli.pywith:1. Daemon flag
(Alternatively, patch
threading.Threadto capture thedaemonkwarg before forwarding to the real constructor.)2. Normal input placed on queue (stripped)
3.
EOFError→_FALLBACK_EOF(isolated)4.
KeyboardInterrupt→_FALLBACK_EOF(untested at any level)5. Unexpected
Exception→_FALLBACK_EOF+ warning logged (isolated)Regression Scenario
The daemon-flag test is the highest-priority regression guard. Removing
daemon=Truefrom thethreading.Thread(...)call would not be caught by any existing test — the integration tests would hang indefinitely waiting for the reader thread to exit, eventually being killed by CI timeout (the exact failure mode described in issue #1012).Files to Modify
tests/copilot_usage/test_cli.py— addTestStartInputReaderThreadclass afterTestReadLineNonblocking(currently at line 2145)_start_input_reader_threadand_FALLBACK_EOFalongside the existing private imports at the top of the test fileWarning
The following domain was blocked by the firewall during workflow execution:
pypi.orgTo allow these domains, add them to the
network.allowedlist in your workflow frontmatter:See Network Configuration for more information.