Skip to content

feat: perch coverage mutates method calls, literals and chains like Stryker - #350

Closed
jbrown9513 wants to merge 2 commits into
feat/coverage-report-redesignfrom
feat/stryker-operators
Closed

jbrown9513 wants to merge 2 commits into
feat/coverage-report-redesignfrom
feat/stryker-operators

Conversation

@jbrown9513

@jbrown9513 jbrown9513 commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

The mutant table stopped at operators, conditions, literals, statement calls and bodies: a method's own calls were never touched. items.filter(x => x.ok).map(…) got no mutant, so a test that never checks the filter was never found. Stryker mutates all of it.

src/mutants.js now makes the rest of Stryker's set, in every language perch reads:

Kind Edit
method A method becomes its opposite, or its call drops out of the chain, so the chain runs without it: startsWith to endsWith, toUpperCase to toLowerCase, min to max, every to some; .trim(), .filter(…), .sort(), .slice(…), .reverse() gone. Each language's own names: Python's strip and sorted(…), Ruby's upcase and select {…}, Kotlin's uppercase and sorted(), Swift's hasPrefix, Go's strings.ToUpper and strings.TrimSpace(…), Rust's to_uppercase and .rev(), C#'s Where(…) and OrderBy, Scala's forall and .sorted, PHP's strtoupper and array_filter(…), Lua's string.upper, C++'s std::min and std::any_of. Only a method that gives back its receiver's kind is dropped, so the edit compiles wherever the language checks types.
collection A list, dictionary or object literal is emptied: [1, 2] to [], { a: 1 } to {}, {1} to set(), []int{1, 2} to []int{}, new int[]{1, 2} to new int[]{}, vec![1, 2] to vec![]. An empty list is filled, [] to ["perch was here"], where the language lets it stand alone. Kotlin's listOf() and Swift's [] have no type of their own and are left alone.
chaining An optional chain is made unconditional: a?.b to a.b, a?.() to a(), Ruby's a&.b to a.b, PHP's ?-> to ->, Kotlin's ?. to !!., C#'s a?.b to a.b.
lambda An arrow function's expression body returns undefined.
regex A regular expression loses an anchor, a + becomes * and back, a ? goes, \d becomes \D, \w and \s likewise, [^a] becomes [a], each its own mutant. JavaScript and Ruby literals.
condition Now also a ternary's condition, forced each way, and a for or do loop's, forced false.
update Now every assignment operator Stryker turns: *= and /=, %= to *=, <<= and >>=, &= and |=, &&= and ||=, ??= to &&=, Python's //=.
logic ?? becomes &&, where && takes any value.
string An empty string is filled: "" to "perch was here".

The mutants for a method call are read off each grammar's own shape: a member call in JavaScript, Python, Java, Go, Rust, C++, Lua and PHP; a navigation suffix in Kotlin and Swift; the receiver on the call itself in Ruby; a field access with no parentheses in Scala. A dropped call is described as the call to \trim` removed, and the test to add names it: Add a test that fails when `filter` is not called on line 12.`

On the shop fixture the count goes from 27 mutants to 28. On perch, 20,374 mutants become 32,822: 1,387 method swaps and drops, 2,249 collections, 2,011 arrow functions, 709 pattern edits, and the rest ternaries, loops and assignment operators; the score reads 27% where it read 28%.

Closes #349

…tterns as Stryker does

The mutant table stopped at operators, conditions, literals, statement calls and bodies; a method's own calls were never touched, so a test that never checks a filter was never found. mutants.js now swaps a method for its opposite or drops it from a chain in every language's own names, empties list and object literals and fills empty ones, makes an optional chain unconditional, blanks an arrow function, edits regular expressions, forces ternary and loop conditions, turns every assignment operator, and makes ?? an &&.
@jbrown9513

Copy link
Copy Markdown
Contributor Author

Stacked on #348, which is stacked on #345; merge those first, in that order.

@perchcode-bot

perchcode-bot Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Perch Scan: no issues in 40 methods at f765a28.

See the scan

The method tables were plain objects, so a call to a name Object has a property for, toString, valueOf, constructor, hasOwnProperty, found that property and handed a function to Buffer.from. The tables have no prototype now.
@jbrown9513

Copy link
Copy Markdown
Contributor Author

Folded into #355.

@jbrown9513 jbrown9513 closed this Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant