Read shapes out of a mix run's captured output.
A mutant's exit code is the primary signal (Mutare.Sandbox.Command decodes
it), but three jobs need to look past the code at the human-readable output:
- Refining a verdict. Exit
1is ambiguous — a genuine harness failure or a mutation that broke the test suite's own compilation — and a BEAM abort can land on any code.suite_compile_error?/1,atom_exhausted?/1, andboot_failure?/1are the pure discriminatorsMutare.Sandbox.Command.outcome/2consults to split those cases (see that module's moduledoc for why each is the verdict it is). - Locating a failure.
Mutare.Poisonmaps a failed metamutant compile back to mutant ids (source_location_regex/0+diagnostic_severity/1), andMutare.Runner.Baselinenames the tests in a flaky run (test_location_regex/0). - Diagnosing dependencies.
dependency_issue/1distinguishes Mix's dependency-check failures from compile-poisoning so the runner can stop recovery immediately and the Mix task can recommend the correct command in the original project rather than the disposable sandbox.
Why these live together
Every pattern here recognises a shape in mix's human-readable output, so they
all break together if mix ever changes its format. They are read for different
jobs by different modules — compile_error_banner/0 to tell a kill from infra,
source_location_regex/0 to map a compile error to a mutant id, test_location_regex/0
to name flaky tests — and the consumers are deliberately not merged (they parse
for different ends). But sourcing every pattern from one module gives a
mix-output-format change a single home.
Everything here is pure, so each discriminator is unit-testable without spawning
mix.
Summary
Types
The remediation class of a Mix dependency-check failure.
Functions
Returns whether output shows the BEAM aborting because the atom table filled.
Returns whether output matches the known boot-failure harness error.
Regex for Mix's == Compilation error in file <path> == banner.
Classify a dependency-check failure in captured Mix output.
Returns the diagnostic block started by a compiler-output line.
Return the last lines lines of captured Mix output.
Regex for a <file>:<line> source reference in Mix output.
Returns whether output reports a Mix compilation error in a test script.
Regex for a <test_file>:<line> reference in Mix output.
Types
Functions
Returns whether output shows the BEAM aborting because the atom table filled.
This is the signature of a mutation that mints unbounded atoms. The runner treats that as a kill, like a timeout, because the suite cannot complete with the mutation active.
This predicate only refines an otherwise-:harness_error exit in
Mutare.Sandbox.Command.outcome/2; normal pass, fail, and timeout verdicts
take precedence.
Returns whether output matches the known boot-failure harness error.
The signature is the emulator's terminating during boot message paired with a
secondary :standard_error failure. The original diagnostic is usually gone by
then, and the common cause is resource or connection contention while concurrent
workers start.
The runner still records this as :harness_error, but it can show a more useful
message and use the dedicated boot-failure retry budget. This predicate only
refines an otherwise-:harness_error exit; normal pass, fail, and timeout
verdicts take precedence.
@spec compile_error_banner() :: Regex.t()
Regex for Mix's == Compilation error in file <path> == banner.
The regex captures <path> and is used by suite_compile_error?/1.
@spec dependency_issue(String.t()) :: dependency_issue() | nil
Classify a dependency-check failure in captured Mix output.
Returns nil for output unrelated to dependencies. The categories deliberately
follow Mix's own recommendations:
:fetch— the lock/source state requiresmix deps.get;:compile— sources exist but requiremix deps.compile;:diverged— dependency declarations conflict;:unavailable— a non-fetchable dependency (normally a local/path dep) is missing from the sandbox's view of the filesystem;:invalid— another status under Mix's unchecked-dependencies banner.
The broad banner proves this is dependency validation, while the narrower
recommendation phrases choose remediation. This keeps an arbitrary compiler
error that merely mentions mix deps.get from being reclassified.
@spec diagnostic_severity(String.t()) :: :error | :warning | nil
Returns the diagnostic block started by a compiler-output line.
The result is :error for an error: header or raised ** (…Error),
:warning for a warning: header, and nil for any other line. Body,
footer, and chatter lines inherit the previous header's severity in the caller.
Mutare.Poison uses this to scan only non-warning lines for mutant locations.
A failed metamutant compile can include warnings caused by mutations, and those
warnings carry the same file:line footer shape as real errors. Separating the
diagnostic blocks keeps warning locations from being treated as poison.
@spec output_tail(String.t(), pos_integer()) :: String.t()
Return the last lines lines of captured Mix output.
Used by baseline, coverage-probe, and Mix-task errors to show enough context without printing an entire suite run.
@spec source_location_regex() :: Regex.t()
Regex for a <file>:<line> source reference in Mix output.
It matches .ex and .exs paths such as lib/foo.ex:5 or
test/foo_test.exs:42, capturing the file and line. Mutare.Poison uses it
to map compile errors back to mutant ids.
Returns whether output reports a Mix compilation error in a test script.
This identifies a mutation that broke test-suite compilation. It matches the
compile_error_banner/0 only when the captured path is a .exs file under a
test/ directory. Lib-file errors and output without the banner return false.
@spec test_location_regex() :: Regex.t()
Regex for a <test_file>:<line> reference in Mix output.
This narrows source_location_regex/0 to _test.exs files. The baseline
runner uses it to name tests involved in a flaky run.