test(semantic): raise experts.py mutation kill rate 44.4% -> 63.2% - #492
Merged
Merged
Conversation
`semantic/experts.py` is where semantic repairs are proposed, and it killed only 111 of 250 mutants. This adds 60 tests across four themes, killing 32 of 37 targeted mutants; the other 5 are proven equivalent with executable proofs, not assertions. Expert applicability guards. `info.free_text or info.identifier_like or info.boolean_like` could become `and`, and `numeric_like and not free_text` could become `or`, with nothing failing. These are the guards that keep an expert off identifier and free-text columns, so flipping them is the "ID-protection removed" class: an expert that runs on an identifier column rewrites "007" to 7, and the damage is unrecoverable from the output alone. Date day/month disambiguation. The `a > 12 and b <= 12` family decides whether 05/12 is May 12th or 5th December. Every boundary at 12 was movable. Three of the branch-3 mutants cannot be killed: they differ only in states already claimed by the earlier branches, proven by differential over the complete input domain (180,000 inputs each, zero differences). Currency and number parsing, including the ambiguity flag for "1,000" with no currency code -- the corpus trap this parser exists to handle. Two mutants here are equivalent: one comparison is unreachable outside a branch that guarantees both operands differ, and one `return True` is dead code, confirmed by line-level trace over 610,436 calls recording zero executions. Allowed values and the category tie-break, plus the frozen-ness of _DateResolution and dropna in the value counter. Also corrects PR #491. That PR reported 98.3% for scoring.py, but its verdicts came from a harness run with a stale-bytecode defect; the true figure at that commit was 96.6%. The masked survivor was `sort_keys=True` in features_hash -- the property that makes the digest a function of feature content rather than of dict literal order. A test for it is included here, and scoring.py now genuinely measures 98.3%. Tests only; no src change.
Contributor
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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
semantic/experts.pyis the module where semantic repairs are proposed, and it killed only 111 of 250 mutants. This adds 60 tests across four themes.semantic/experts.pysemantic/scoring.pyOf the 37 targeted mutants: 32 killed, 5 proven equivalent, 0 unexplained.
All runs cover every mutation site with
not applied: 0.Why the survivors mattered
Expert applicability guards.
if info.free_text or info.identifier_like or info.boolean_like:could becomeand, andinfo.numeric_like and not info.free_textcould becomeor, with nothing failing. These guards are what keep an expert off identifier and free-text columns — the brief's "ID-protection removed" class. An expert that runs on an identifier column rewrites"007"to7, and the damage is unrecoverable from the output alone.Date day/month disambiguation. The
a > 12 and b <= 12family decides whether05/12is May 12th or 5th December. Every boundary at 12 was movable — a silent wrong date written into user data.Currency and number parsing, including the ambiguity flag for
"1,000"with no currency code — the corpus trap this parser exists to handle — and the decimal-separator selection.Allowed values and the category tie-break, plus the frozen-ness of
_DateResolutionanddropnain the value counter.The five equivalent mutants are proven, not asserted
cmp#7— reachable only insideif dots and commas:, so bothrfindcalls return ≥ 0 and, being different characters, can never be equal;>and>=agree on every reachable input. Confirmed over 727,831 parser calls.const_bool#5—_valid_groupingline 134 is dead: an early return guaranteesrestis non-empty. Confirmed bysys.settraceline coverage over 610,436 calls: zero executions.cmp#20,cmp#21,bool#16— the three branch-3 date mutants differ from the original only in states already claimed by branches 1 and 2, which return first. Confirmed by differential over the complete input domain (a, b ∈ 0..99, both separators, 3 years, 3dayfirstvalues) = 180,000 inputs per mutant, zero differences.This also corrects PR #491
#491 reported 98.3% for
scoring.py. That figure was wrong. Its verdicts came from a harness run affected by a stale-bytecode defect, and the true value at that commit was 96.6%.The masked survivor was
json.dumps(features, sort_keys=True)→Falseinfeatures_hash— the property that makes the digest a function of feature content rather than of the order the dict literal happens to be written in. Every existing test compared hashes built in a single order, so none of them noticed. A test for it is included here, andscoring.pynow genuinely measures 98.3%.Scope
Tests only — no
src/change. No behaviour changes, so no changelog entry and no compatibility impact.Verification
test_scoring_boundaries.py— all passruff check+ruff format --checkcleanTwo defects found in the library (reported, not fixed here)
Neither is fixed in this PR, because both touch
src/and warrant their own change with its own regression test.1. The currency parser's
ambiguousflag is dead on every path.experts.py:110-114documents that "a currency absent from it and not settled by structure is reported ambiguous rather than guessed". It cannot happen:_split_amount's only caller always resolves a currency code first, so the ambiguous branch is unreachable (verified: 0 of 98 public inputs), andparse_currency— the entry point all six consumers use — discards the flag anyway. No recognised currency produces a wrong value today, since all eight codes outside{EUR, CHF}are dot-decimal locales. The hazard is latent: adding SEK/BRL/DKK to_CURRENCY_CODESwithout also adding it to_COMMA_DECIMAL_CURRENCIESwould readSEK 1.200as1.2instead of1200, and the documented safety net would not catch it.2. The category tie-break is decided by row order — a metamorphic violation. On an exact tie,
count > best[1]keeps whichever spellingvalue_countssaw first, which is a property of row ordering, not of the data:semantic_auto_threshold=0.90["USA"]*3 + ["usa"]*3'usa' → 'USA'USA["usa"]*3 + ["USA"]*3'USA' → 'usa'usaAt defaults the data is unchanged (confidence 0.93 < 0.95) but the proposal direction still flips, so the report is order-dependent even at defaults.
semantic_auto_thresholdis a documented public kwarg, so the data flip needs no private API. An exact tie means there is no column-local evidence for either spelling, so the contract says route to review rather than pick one. The tie-break tests in this PR are characterization tests whose docstrings say plainly that they pin a suspected defect, so a deliberate fix has to come past them.