Skip to content

v0.9.0

Choose a tag to compare

@github-actions github-actions released this 23 Oct 21:00
· 23 commits to main since this release
a7c39f3

For consumers

Two opt-in operators reach the numbers and arithmetic the defaults never touched.
NumberLiteral only ever turned N into N+1, so a boundary test that catches one of N+1, N-1
and 0 and not the others looked complete, and a fractional literal such as 1.5 produced no mutant
at all. NumberBoundary turns an integer into N-1 and 0, and a fractional number into N+1,
N-1 and 0; a value equal to the original or already listed is dropped, so 1 gives only 0 and
0 only -1, and a negative result is bracketed so it still parses after a unary minus.
ArithmeticOperator turns % into *, swaps += with -= and *= with /=, turns %= into
*=, and swaps ++ with -- on either side of the operand -- except in a for loop's iterator,
where $i++ turned into $i-- is a loop that never ends. Both are opt-in, like every operator
after the first four: on by default they would lower the score of a repo that changed nothing but
its PSMutant. Add them to operators to use them.

Every mutant can be written in the format Stryker's tools read. Set mutationTestingReportPath
and a run also writes a mutation-testing-elements report: each mutated file with its source, and
each mutant with its operator, its exact start and end, its replacement and its verdict, which is what
Stryker's HTML viewer and dashboard need to draw it in place. PSMutant writes the data and ships no
viewer. A declared equivalent is Ignored with its argument as the reason, TimedOut is Timeout,
and mutants on lines no test ran are left out, so the score a viewer shows is the one the run gated
on. Like the SARIF log it is opt-in, a -ChangedFile run writes a .changed sibling, and a recheck,
a preview or an interrupted run writes none.

-Incremental now reuses a file covered by more than one test file. Such a file -- two
entries in its tests mapping, or no mapping at all, so the whole suite of two or more files covers
it -- was evaluated in full on every run. Its covering files were joined into one path with a space
in it: measuring what the suite executes failed on a path that does not exist, and the sandbox's
random name went into the file's key, so the key never matched the next run's. No verdict was ever
wrong, because a key that does not match means "evaluate again"; the cost was the time. Found by
measuring this repository's own incremental runs, where the one file mapped to two test files
re-ran 85 mutants with nothing changed.

A dependency measurement that fails no longer fails the run's CI step. Recording keys,
-Incremental runs each covering suite once more with coverage to see what it executes. When that
run went wrong, Pester's error reached the caller's error stream and Pester set the global
$LASTEXITCODE -- which a GitHub Actions pwsh step exits with -- so the step failed after a run
that had scored fine. The measurement now captures what Pester writes and restores $LASTEXITCODE.
A suite whose run returned nothing, threw or did not pass is treated as unmeasured: its files get
no key, the next run evaluates them in full, and the run says so in one line. Not passing counts
because a suite that stopped part-way ran fewer files than it does, and a key short of a file would
later reuse a verdict whose dependency had changed. An error a passing test provokes on purpose --
every Should-Throw writes one -- does not count.

What's Changed

  • ci: complexity gate on PSComplexity 0.7.0 by @TaekeK in #299
  • feat: NumberBoundary and ArithmeticOperator, two opt-in operators (#260) by @TaekeK in #301
  • feat: write every mutant in the mutation-testing-elements format (#261) by @TaekeK in #302
  • fix: -Incremental reuses a file covered by more than one test file by @TaekeK in #303
  • ci: pull requests reuse main's self-mutation verdicts by @TaekeK in #304
  • fix: a failed dependency measurement no longer fails the CI step (#305) by @TaekeK in #306

Full Changelog: v0.8.0...v0.9.0