Repository navigation
test: stop watch tests patching the global time.sleep - #1183
Merged
1 commit merged intoNov 9, 2026
Merged
1 commit merged into
1 commit merged into
Conversation
Watch tests stopped the loop with patch("time.sleep",
side_effect=KeyboardInterrupt). That patches sleep for the whole
process, and subprocess.run(timeout=...) sleeps while it waits for a
child. On a busy runner a git call in watch startup hit the patched
sleep, the KeyboardInterrupt escaped, and pytest stopped the whole run
(seen at about 65% on Python 3.11 and 3.12).
The loop already has a seam: watch(stop_event=...). Tests now pass an
event that is already set, or, where they script ticks or need a real
Ctrl+C path, a small driver whose wait() is one tick. Only the watch
loop calls it. The tick driver had the same leak: a git sleep could run
a scripted tick early.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
code-review-graph reviewOverall risk: 0.30 (LOW) — 24 changed function(s)/class(es), 0 affected flow(s), 0 test gap(s) Risk-scored changes
Token savings: this graph-backed report used ~56,437 fewer tokens (~94%) than reading every changed file in full (estimated, chars/4 approximation). Powered by code-review-graph — local-first analysis; no code leaves the CI runner. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What went wrong
CI on Python 3.11 and 3.12 once stopped at about 65% with a
KeyboardInterruptraised fromunittest/mock.py.Several watch tests ended the watch loop with
patch("time.sleep", side_effect=KeyboardInterrupt). That patchessleepfor the whole process.subprocess.run(timeout=...)also callstime.sleepwhile it waits for a child to exit. On a busy runner the child has not exited on the first poll, so a call in watch startup hit the patched sleep, the interrupt escapedwatch(), and pytest took it as Ctrl+C and stopped the run.Note:
patch("code_review_graph.<module>.time.sleep")would not help. That path names the same sharedtimemodule, so it is still the global sleep.Fix (tests only)
watch()already has a seam:stop_event. The loop waits on it once per tick and nothing else calls it, so no code under test can reach it.test_cli_watch_with_the_pair_keeps_running_on_a_never_embedded_graphdrivescli.main(), which passes no event, so it wrapsincremental.watchto add one.test_watch_robustness.py's tick driver is now a stop event whosewait()is one tick: it runs one scripted step, then raisesKeyboardInterruptlike a real Ctrl+C. It had the same leak: a sleep inside a subprocess call could run a scripted step early.Tests changed:
test_postprocessing.py(2),test_incremental.py(2),test_watch_refresh_without_index.py(1),test_embedding_refresh_skip_notice.py(1),test_watch_robustness.py(tick driver plus 1). No production change. No raising patch of the globaltime.sleepis left intests/. No CHANGELOG entry, as only tests change.Proof
A small pytest plugin forces the subprocess wait path: it makes
Popen._try_wait(WNOHANG)report "still running" once per process, which is what a loaded runner does by chance.Before (on
staging), with the plugin:test_postprocessing.pyandtest_embedding_refresh_skip_notice.py:!!! KeyboardInterrupt !!!atunittest/mock.py:1228, run stopped after 20 tests. Each module triggers it on its own.test_watch_robustness.py:test_normal_file_change_still_updates_the_graphfails 3 runs out of 3 (a subprocess sleep runs a tick early).After, with the plugin:
test_watch_robustness.pyandtest_postprocessing.py: 97 passed, 3 runs out of 3.Gates:
uv run pytest tests/ --tb=short -q: 6390 passed, 900 skipped, 3 xfailed, 4 xpassed.ruff check code_review_graph/clean (changed test files clean too). mypy: no issues.🤖 Generated with Claude Code