v0.1.0
Initial release.
Added
- Compile-once metamutant. Source under
lib/is rewritten into a single
program that embeds every mutant behind a:persistent_termruntime switch,
compiled once; the suite then runs once per mutant by flipping
MUTARE_ACTIVE_MUTANT— no per-mutant recompilation. - A broad built-in mutator set (all on by default): arithmetic/operator and
operand swaps, relational and logical swaps, strict-equality relaxation,
literals of every kind (integer/float/string/charlist/atom/sigil/regex/
bitstring/date-time), collection/string/map/keyword call rewrites, pattern and
clause restructurings, guard/default/call drops, and more. SeeMutare.Mutators. - Unit-return classification — a function (or anonymous function) whose every
return path is literally:okornilreturns no data, so its tails draw no
return-value constant and no:ok → :errorswap. Syntactic, and one-sided: it
can miss a unit function, never silence a data-returning one. - Coverage-guided test selection — coverage is self-recorded by the metamutant
at runtime and keyed by mutant id; each mutant runs only the test cases that
cover it (--per-filewidens that to the covering test files, for stateful
async: falsesuites;--fullruns the whole suite every time). Uncovered
mutants are skipped and excluded from the score. - Parallel workers with per-mutant timeouts — mutants run
:workersat a
time, each capped by a wall-clock deadline; a mutation that hangs halts itself
and counts as a kill (no process-tree killing). - Compile-poison recovery — a mutant that won't compile is identified from the
compile error, dropped (reported as poisoned, excluded from the score), and
the build retried, for a bounded number of rounds; only a compile error that
can't be attributed to any mutant (or that outlasts the bound) aborts the run,
with a copy-pasteable:skipsnippet. # mutare:ignoredirective — suppress a known-equivalent mutant per line,
per span (-start/-end), or per file (-file), with an optional free-text
reason and an optional[family]/[family:label]filter; ineffective
directives are surfaced (--strict-ignoresescalates).- Reporters — human (default, survivor diffs + score), plus machine-readable
json(Stryker / mutation-testing-elements schema),html(interactive
viewer), andsarif(GitHub code scanning) via--report FORMAT[:PATH]
(repeatable) or:reporters. - Live progress on stderr; the detailed report and score print to stdout.
- CI integration —
--since <ref>to scope to the lines changed against a
git ref,--min-scoreto gate,--lineto target lines, and a kept sandbox
(the default) so the
compiled build carries across runs;--sandbox <path>pins it at a CI cache,
--no-keep-sandboxopts back into a throwaway copy. - Umbrella-aware — target one app, several, or the whole workspace.
- Extension surface —
Mutare.Mutator(custom mutators), independent
Mutare.CallRoutingandMutare.UseExpansioncapabilities,:extensionsfor
non-mutating integrations, declarative:call_routesconfiguration
(user-tier treatments only; the adapter-grade treatments must come from a
module implementingMutare.CallRouting), theMutare.ASTnode
constructors that discharge Sourceror's emission invariants for plugins, and
theMutare.Callscall-resolution readers (resolved_call_to/3,
module_key/1) so plugins match calls without building core's key
representation, and a declarative environment guard (required_modules/0,
on mutators and extensions): the modules a DSL plugin routes are checked
loadable once at startup, aborting withMutare.EnvironmentErrorinstead of
silently registering routes against nothing on an external-source run. mix igniter.install mutareinstaller that detects frameworks and wires up
the matching companion packages and.mutare.exs.- Call routes (
call_routes:/--skip-call Module.fun/arity) — leave a
call alone::skipmakes a whole call an inert leaf (functions, macros, and
the construct special forms alike —Kernel.if/2orcaseincluded; a piped receiver and
the enclosing function's return-value mutants are unaffected),:rawleaves
an argument as written,:interiormutates an argument's contents but never
its own node, and a keyed refinement ([:expression, timeout: :raw]) reaches
one option of a literal keyword argument. The forms Mutare analyzes
structurally (if,case, the boolean operators, …) take:skiponly, and
definitions (def,defmodule, …) and literal syntax ({},%{},=, …)
take no route. Routes match qualified,
aliased, imported, and piped forms, and an entry that matches no call in a
full scan is warned about. - Argument marks (
argument_marks:) — extend the built-in timeout table (or
any label a mutator declares) to your own functions, with the mutators'
value-aware reaction:{MyApp.Http, :get, 2, [{:keyword, :recv_timeout}], :timeout}.