Renders mutation results for the human report.
Diffs are patched against the original source via Sourceror.patch_string at the site's recorded range, so untouched source stays byte-identical and a survivor reads as a precise source-range change.
Summary
Functions
A -/+ diff of the lines touched by the mutation.
Human-readable failures for complete-run CI gates.
One line for a harness-errored mutant, including a compact diagnostic.
Fraction of launched mutant runs that ended in :harness_error.
Returns whether harness_error_rate/1 exceeds max_rate.
Header line for a surviving mutant, e.g.
lib/x.ex:42 [relational, in-place] SURVIVED.
One line for an ignored mutant, e.g.
lib/x.ex:42 [arithmetic] IGNORED — off-by-one is intentional.
Returns whether results meet a minimum score percentage.
Apply a single mutation to the original source string.
Format a percentage value (already on a 0..100 scale) to one decimal place,
without a trailing %.
Render the whole report from results and a %{file => original_source} map.
Renders the human report with the same arity as machine reporters.
Mutation score as a percentage:
killed / (total − no_coverage − ignored − poisoned − harness_error).
Returns 100.0 when the denominator is zero (nothing to test).
One-line tally, e.g. mutation score: 66.7% (2 killed, 1 survived, 3 total).
Full diff block (header + diff) for one survivor.
Functions
@spec diff(Mutare.Site.t(), String.t()) :: String.t()
A -/+ diff of the lines touched by the mutation.
The diff is line-based within the site's source range. Changed lines are shown as deletions and insertions; unchanged lines inside a multi-line fragment are shown as context. This keeps multi-line replacements aligned when a mutation adds or removes a line in the middle of the fragment.
@spec gate_failures([Mutare.Result.t()], keyword() | map()) :: [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
@spec harness_error(Mutare.Result.t()) :: String.t()
One line for a harness-errored mutant, including a compact diagnostic.
@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.Report.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 header(Mutare.Site.t()) :: String.t()
Header line for a surviving mutant, e.g.
lib/x.ex:42 [relational, in-place] SURVIVED.
A mutation with a note appends it as a trailing — note, matching the ignored
mutant reason format.
@spec ignored(Mutare.Site.t()) :: String.t()
One line for an ignored mutant, e.g.
lib/x.ex:42 [arithmetic] IGNORED — off-by-one is intentional.
The trailing — reason is present only when the directive carried one, so a
bare # mutare:ignore reads as … IGNORED with nothing after it.
@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.Report.passes_gate?(results, 60)
false
iex> Mutare.Report.passes_gate?(results, nil)
true
@spec patch(Mutare.Site.t(), String.t()) :: String.t()
Apply a single mutation to the original source string.
Format a percentage value (already on a 0..100 scale) to one decimal place,
without a trailing %.
iex> Mutare.Report.percent(2 / 3 * 100)
"66.7"
@spec render([Mutare.Result.t()], %{optional(String.t()) => String.t()}) :: String.t()
Render the whole report from results and a %{file => original_source} map.
@spec render([Mutare.Result.t()], %{optional(String.t()) => String.t()}, keyword()) :: String.t()
Renders the human report with the same arity as machine reporters.
opts is ignored; score gating is handled by the caller.
@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.Report.score(results)
50.0
@spec summary([Mutare.Result.t()]) :: String.t()
One-line tally, e.g. mutation score: 66.7% (2 killed, 1 survived, 3 total).
iex> Mutare.Report.summary([
...> %Mutare.Result{status: :killed},
...> %Mutare.Result{status: :survived}
...> ])
"mutation score: 50.0% (1 killed, 1 survived, 2 total)"
@spec survivor(Mutare.Site.t(), String.t()) :: String.t()
Full diff block (header + diff) for one survivor.