Kill mutation survivors in the post-turn quality stop hook (#36, #37, #38, #40) - #50
Kill mutation survivors in the post-turn quality stop hook (#36, #37, #38, #40)#50leynos wants to merge 3 commits into
Conversation
Add targeted unit tests for the highest-value mutation-testing survivors reported against the stop hook: exact rendering tests for format_reason, boundary and split tests for truncate, representative parsing tests for parse_make_targets and is_missing_makefile, extension-mapping tests for detect_categories, full-tuple parse_env and parse_max_output tests, equality tests for default_categories, real-subprocess and error-branch tests for run(), and argv, wiring, and exact-message assertions for the git plumbing helpers. Annotate the four equivalent mutants (subprocess check=False and the falsy env-var defaults) with pragma no mutate comments, splitting the affected arguments onto their own lines so the suppression does not shield killable mutants on the same line.
mutmut 3.6 only honours pragma comments at statement level, so the argument-level annotations added earlier were ignored. Hoist the falsy env-var defaults into their own pragma-suppressed statements, drop the redundant check=False argument from run() (False is the subprocess default, so both of its mutants were equivalent by construction), and suppress the max-output default whose mutation falls back to the same 12000 value. Also convert the remaining substring assertions on git error messages to exact equality, cover truncate at max_chars=1, extend the make -qp fixture with a mid-stream blank line and a mixed pattern/plain rule, and exercise format_reason with a command entry lacking cmd/stdout/stderr keys and with a falsy base ref.
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 20 seconds Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Reviewer's GuideThis PR adds high-precision tests around the post-turn quality stop hook to kill/suppress surviving mutation-testing mutants, and performs small refactorings in the hook implementation to structurally suppress known equivalent mutants without changing behaviour. Flow diagram for updated parse_env default handlingflowchart TD
parse_env[parse_env]
flag_default["flag_default = '' (no mutate)"]
max_output_default["max_output_default = '12000' (no mutate)"]
parse_env --> flag_default
parse_env --> max_output_default
base_ref_env["os.environ.get POST_TURN_BASE_REF"]
always_fetch_env["os.environ.get POST_TURN_ALWAYS_FETCH"]
max_output_env["os.environ.get POST_TURN_MAX_OUTPUT_CHARS"]
compush_env["os.environ.get POST_TURN_COMPUSH"]
parse_env --> base_ref_env
parse_env --> always_fetch_env
parse_env --> max_output_env
parse_env --> compush_env
always_fetch_env --> parse_bool_env
compush_env --> parse_bool_env
max_output_env --> parse_max_output
flag_default --> always_fetch_env
flag_default --> compush_env
max_output_default --> max_output_env
parse_bool_env[parse_bool_env]
parse_max_output[parse_max_output]
parse_bool_env --> always_fetch
parse_bool_env --> compush
parse_max_output --> max_out
base_ref_env --> base_ref
base_ref[base_ref]
always_fetch[always_fetch]
max_out[max_out]
compush[compush]
base_ref --> result_tuple
always_fetch --> result_tuple
max_out --> result_tuple
compush --> result_tuple
result_tuple[("(base_ref, always_fetch, max_out, compush)")]
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Closes #37 and #38. Addresses #36 and #40. Issues #39 and #41 are deferred in full.
Survivor-triage PR for the mutation-testing run recorded against
main(b850e2f). It adds targeted unit tests for the highest-value surviving mutants inhooks/post-turn-quality-stop-hook.py, and eliminates or suppresses the equivalent mutants identified at triage.Before/after (full
mutmut run, mutmut 3.6.0, CPython 3.14)Six equivalent mutants are no longer generated (pragma suppression and the removal of a redundant argument — see below). The 129 remaining survivors break down as 117 deferred real gaps (71
prepare_run_stop_checks+ 10run_stop_checks→ #39; 18repo_root+ 18get_make_targets→ #36) and 12 newly identified equivalents (7format_reason, 3parse_make_targets, 2truncate— justified below).Review walkthrough
hooks/test_post_turn_quality_stop_hook.py— new test classes fortruncate(boundary and exact head/tail split),parse_make_targetsandis_missing_makefile(representativemake -qpfixture),detect_categoriesanddefault_categories(full-dict equality across every extension group),format_reason(exact multi-line rendering, the 60-file elision boundary, missing-key command entries), the fullparse_envtuple andparse_max_outputfallback, andrun()(real subprocess capturing text output, theNotADirectoryErrorbranch, the filename-lessFileNotFoundErrorguard). Existing plumbing tests gain exact argv (call_args_list) assertions, exact error-message equality in place of substring checks, wiring assertions incompush_check, and the ahead-by-exactly-one boundary case.hooks/post-turn-quality-stop-hook.py— equivalence handling only; no behavioural change (the full suite and a fresh mutation run confirm).Equivalent mutants (category 2)
mutmut 3.6 honours
# pragma: no mutateonly at statement level (its pragma visitor ignores trailing comments inside call argument lists), so the worklist's four equivalents were handled structurally:parse_envflag defaults (""→"XXXX"onPOST_TURN_ALWAYS_FETCH/POST_TURN_COMPUSH): hoisted into a singleflag_default = "" # pragma: no mutatestatement with a justification comment — any mutated default is still non-truthy toparse_bool_env.run()check=False→check=None/dropped: the argument was the subprocess default, so both mutants were equivalent by construction; the redundant argument is removed and a comment records that non-zero return codes are deliberately surfaced viaCompletedProcess.returncode.POST_TURN_MAX_OUTPUT_CHARSdefault ("12000"→ non-numeric) falls back toparse_max_output's own 12000 default — hoisted and suppressed the same way.Twelve further equivalents remain in the survivor list unsuppressed, because each sits on a line that also carries killable mutants (line-level suppression would shield them):
format_reason(7):c.get("exit_code", …)defaults are unreachable — the failures filter guarantees the key is present;c.get("stdout"/"stderr", None/dropped)defaults are filtered identically to""by theif xcomprehension guard.parse_make_targets(3): corrupting one element of thestartswith(("#", "\t", " "))guard is unobservable — the rule regex rejects those lines anyway.truncate(2):<=→<at both boundaries converges on the same output through the fall-through slice paths.Red-green evidence
Each new test was verified against a hand-applied mutant diff (fail) and the reverted source (pass); cycles were run for
format_reason,run,default_categories,has_unpushed_commits, andparse_env. Representative transcript:(Two same-length mutations initially produced misleading transcripts because Python's mtime-plus-size
.pycinvalidation served stale bytecode; the cycles were re-run withPYTHONDONTWRITEBYTECODE=1and clean caches, and both then behaved correctly.)Deferred work
The 400-line test-budget tolerance for this PR is spent (392 net new test lines). Deferred explicitly:
repo_root(18) andget_make_targets(18) argv/message survivors.prepare_run_stop_checks,run_stop_checks).targets_for_categories,dedup_preserve_order,parse_hook_input,resolve_start_cwd,block_and_print, andfail_stateremain in the no-tests bucket.Validation
make ci(check-fmt, lint, typecheck, full pytest suite — 149 tests) green.mutmut runre-runs after each commit; scopedmutmut run 'hooks.post-turn-quality-stop-hook.x_format_reason*'confirmed the final residual kill.Summary by Sourcery
Strengthen post-turn quality hook behavior around git status, upstream detection, commit/push checks, environment parsing, and subprocess execution, while annotating equivalent mutation points.
New Features:
Enhancements: