Repository navigation
chore: add kimera for mutation testing - #3
Merged
Merged
Conversation
Adds kimera to the test group with the config `kimera init` generates, and makes the unreadable-file spec independent of the user running it: it stubbed permissions with chmod 000, which root reads anyway. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013MJdrcPfvw5DYY31WE1XJF
…tion gaps Two real bugs surfaced by surviving mutants: - feature_envy counted an explicit `self.x` receiver twice (once for the SelfNode, once for the call), inflating the method's self references so genuine envy was hidden: `self.rate * order.net + order.tax` stayed quiet while the `@rate` spelling was flagged. - assumed_state resolved `class Thing < Thing` (inside a namespace) to the class itself, so the real parent's assignments were never seen and ivars set there were reported as assumed. A type is never its own ancestor. Also drops repeated_call's size > 1 group filter, which Branches#together? already implies (a single call has no pair), and adds specs for untested behaviour: underscore caches read without ||=, resolved superclasses, mixin cycles, else branches and nested defs in control_parameter, condition/ensure repeats in repeated_call, literal-built, rescued, guarded-elsewhere and table-indexed names in feature_envy, constant-defined error classes, non-hash constants, and `private :name` leaving later methods public. Two vacuous specs (literal builds, constructor calls) now actually exercise the rule they name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013MJdrcPfvw5DYY31WE1XJF
…tation-testing-ep6q15
Specs for behaviour the suite never pinned: - anchor: deep compact paths hoist to a top-level root; single-segment definitions nest in the current scope - boundary errors name the root, or "(unknown)" when it is missing - usage credits a nested constant or unknown member to its holding type - metrics order: stable to unstable, unconnected packages last - a closed class body leaves the lexical scope (no stale nesting) - every distinct cycle is reported; the lightest edge is found anywhere on the cycle - namespace packaging skips wrappers above a deep prefix, even ones with methods - Pipeline: verify checks boundaries without smells; structural findings only with coupling; hotspots only with complexity or duplication - Trees: unparsed works before any tree is read; unreadable paths carry the system's reason (also covers the rescue without chmod, so root runs keep 100% coverage) Dead code: Audiences#common's empty guard (an empty shared part is dropped by the MIN filter anyway) and Census#refold's empty-map check (re-tallying through an empty fold map is identical, and Roster is lazy). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013MJdrcPfvw5DYY31WE1XJF
…tation-testing-ep6q15
…p withheld
The README promises that every capped list "says how many rows it withheld",
but only the package table and the findings list did. The methods table, the
per-class rollup and the hotspot rollup cut at 10 rows (or --top N) in silence,
so a reader had no way to know the list was truncated. They now print the same
"… and N more — raise the cap with --top, or read them all with --json" line,
worded once in Phrases.withheld.
Mutation triage for cli/, ci/ and report/ (kimera: 56 survivors/uncovered -> 0
survivors), the rest being test gaps and dead code:
- Baseline: drop keyed(); Array#to_h with a two-parameter block already reads
a pre-magnitude list of keys as key => nil.
- Only.parse: drop the empty-string guard; "".split(",") is already [].
- Specs pin what the mutants erased: every smell message names its method,
complexity sites and duplication advice, the ratchet refusals name the
baseline path and packaging, a focused ratchet fails on a new finding without
calling the rest resolved, no empty traces section, the crash notice's
message and backtrace, terminal progress/churn/timing notices end to end, the
help rows, the unwritable-baseline reason, a cell exactly at the clip cap,
the leaf boundary and exact-fit metrics table, pretty/compact JSON, accepted
and per-class JSON, hotspots and fold spacing in the text report.
- The unreadable-file spec used chmod 000, which root reads anyway; a dangling
symlink fails for every user, so the suite is green (and 100% covered) in a
root container too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013MJdrcPfvw5DYY31WE1XJF
A client that references only a nested constant (Core::Walk::LIMIT) is counted under its holder (Core::Walk), but mentions? matched only evidence lines ending in the holder, so that audience printed no evidence. Match the holder at any segment boundary instead. Also restructures Refs#record: with the double-counted self branch gone, Refs#named referred only to its argument and hashira's own ratchet flagged it as feature envy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013MJdrcPfvw5DYY31WE1XJF
…tation-testing-ep6q15 # Conflicts: # spec/cli/cli_spec.rb
… LCS in linear time Mutation triage of complexity/, duplication/ and hotspots/ turned up two real bugs and a set of test gaps. Exact clones went missing. When one copy of a duplicated stretch ran even one statement longer, union-find merged the exact pair with the longer near-miss windows. Grouping kept only the largest non-overlapping site per file, so the cluster became a near-miss that the 40-node floor rejected. The fallback core was then drawn from those surviving sites, where the exact pair no longer existed, and the finding disappeared. That is the burial the 0.3 fix was meant to prevent. The fallback now regroups the whole union group by shape, so every exact class gets weighed on its own floor. It also stops two unrelated clones that sit side by side in one method from being cut down to a shorter shared window. The LCS pre-check was neither linear nor a multiset bound. overlap re-tallied the right-hand sequence for every token, so it cost O(n*m) and never decremented. A lopsided pair therefore passed the bound and paid for the quadratic comparison anyway. It now tallies once. Similarity#ratio also drops a guard that only mattered when both sides are empty; normalized returns 0.0 for a zero length instead. New specs pin the cognitive-complexity rules: nesting inside loops/case/else/rescue, an if's predicate, a ternary's operands, begin/else/ensure bodies, the threshold at exactly 10, the finding's site and dominant-label advice, and calls read before the score. They also pin the duplication boundaries: the mass floor, the near-miss mass ratio, the overlap/comparable guard, single-site and shared-statement clusters, string literals, constant-receiver macros, 3-site :structure, the digest, rare-type indexing, and the canonical shape. For hotspots they cover Cluster#masses and FileCost#to_h. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013MJdrcPfvw5DYY31WE1XJF
…tation-testing-ep6q15
UnlessNode went through the generic nester, which descended the else clause without its +1 and scored the condition one level deeper than an if's. Route it through IfChain so both read the same. Also records the fixes found by mutation testing in the CHANGELOG. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013MJdrcPfvw5DYY31WE1XJF
feature_envy already read TABLE[x.class] as dispatch through a table keyed
by foreign classes; fetch is the same lookup and now counts too. The key is
the first argument, so fetch(x.class, default) and fetch(x.class) { } work.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013MJdrcPfvw5DYY31WE1XJF
Cluster#masses charged every site the canonical copy's mass, so in a near-miss cluster a longer copy was under-charged and a shorter one over-charged in the hotspot Dup column. The README already said each file pays for the clone sites it holds; charge each site its own mass and say so explicitly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013MJdrcPfvw5DYY31WE1XJF
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.
Adds kimera to the test group with the config
kimera initgenerates,and makes the unreadable-file spec independent of the user running it:
it stubbed permissions with chmod 000, which root reads anyway.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_013MJdrcPfvw5DYY31WE1XJF