The mutation score and the CI gates computed over a run's results.
The score is killed / (total − no_coverage − ignored − poisoned − harness_error): a
timeout or an atom-table exhaustion counts as a kill, and a mutant that reached no verdict
is left out of the denominator. Mutare.Result classifies each status (kill?/1,
scored?/1, ran?/1); this module only counts.
The gates (gate_failures/2, harness_errors_exceed?/2) are policy over the same tallies:
:no_coverage, :poisoned, and :harness_error stay out of the score, but a CI run can
still fail on them. Rendering the tallies as text is Mutare.Report's job.
Summary
Functions
Human-readable failures for complete-run CI gates.
Fraction of launched mutant runs that ended in :harness_error.
Returns whether harness_error_rate/1 exceeds max_rate.
Returns whether results meet a minimum score percentage.
Format a percentage value (already on a 0..100 scale) to one decimal place,
without a trailing %.
Mutation score as a percentage:
killed / (total − no_coverage − ignored − poisoned − harness_error).
Returns 100.0 when the denominator is zero (nothing to test).
score/1 over a tally of the results' statuses, as Enum.frequencies_by(results, & &1.status)
gives it: for a caller that keeps the counts rather than the results.
Functions
@spec gate_failures([Mutare.Result.t()], keyword() | map(), [ Mutare.Run.BrokenPartition.t() ]) :: [ String.t() ]
Human-readable failures for complete-run CI gates.
These gates are separate from score semantics: :no_coverage, :poisoned,
and :harness_error stay out of the mutation-score denominator, but a caller
can still make them fatal for CI. opts may be a keyword list or an options
map carrying:
:min_score— minimum mutation score percentage, ornil:max_no_coverage— maximum allowed:no_coveragecount, ornil:fail_on_poisoned— fail if any mutant is:poisoned:fail_on_harness_error— fail if any mutant is:harness_error
broken_partitions are Mutare.Run's: partitions whose kills may be false. The score
counts them, so with any present a :min_score gate fails whatever the score: below the
minimum as usual, since false kills only raise it, and otherwise because the score may
clear the minimum only on those kills.
@spec harness_error_rate([Mutare.Result.t()]) :: float()
Fraction of launched mutant runs that ended in :harness_error.
The denominator includes :killed, :survived, :timeout,
:atom_exhausted, and :harness_error. It excludes :no_coverage,
:ignored, and :poisoned, which never launch a test run. Returns 0.0
when nothing ran.
iex> results = [
...> %Mutare.Result{status: :killed},
...> %Mutare.Result{status: :harness_error},
...> %Mutare.Result{status: :no_coverage}
...> ]
iex> Mutare.Score.harness_error_rate(results)
0.5
@spec harness_errors_exceed?([Mutare.Result.t()], number() | nil) :: boolean()
Returns whether harness_error_rate/1 exceeds max_rate.
max_rate is a fraction from 0.0 to 1.0. A nil value disables the
check and returns false.
@spec passes_gate?([Mutare.Result.t()], number() | nil) :: boolean()
Returns whether results meet a minimum score percentage.
A nil minimum always passes.
iex> results = [%Mutare.Result{status: :killed}, %Mutare.Result{status: :survived}]
iex> Mutare.Score.passes_gate?(results, 60)
false
iex> Mutare.Score.passes_gate?(results, nil)
true
Format a percentage value (already on a 0..100 scale) to one decimal place,
without a trailing %.
iex> Mutare.Score.percent(2 / 3 * 100)
"66.7"
@spec score([Mutare.Result.t()]) :: float()
Mutation score as a percentage:
killed / (total − no_coverage − ignored − poisoned − harness_error).
Returns 100.0 when the denominator is zero (nothing to test).
iex> results = [
...> %Mutare.Result{status: :killed},
...> %Mutare.Result{status: :survived},
...> %Mutare.Result{status: :no_coverage}
...> ]
iex> Mutare.Score.score(results)
50.0
@spec score_counts(%{optional(Mutare.Result.status()) => non_neg_integer()}) :: float()
score/1 over a tally of the results' statuses, as Enum.frequencies_by(results, & &1.status)
gives it: for a caller that keeps the counts rather than the results.
iex> Mutare.Score.score_counts(%{killed: 3, survived: 1, ignored: 2})
75.0