Skip to content

v0.1.0

Choose a tag to compare

@foxbenjaminfox foxbenjaminfox released this 07 Sep 04:28
· 10 commits to master since this release

Initial release.

Added

  • Compile-once metamutant. Source under lib/ is rewritten into a single
    program that embeds every mutant behind a :persistent_term runtime 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. See Mutare.Mutators.
  • Unit-return classification — a function (or anonymous function) whose every
    return path is literally :ok or nil returns no data, so its tails draw no
    return-value constant and no :ok → :error swap. 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-file widens that to the covering test files, for stateful
    async: false suites; --full runs the whole suite every time). Uncovered
    mutants are skipped and excluded from the score.
  • Parallel workers with per-mutant timeouts — mutants run :workers at 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 :skip snippet.
  • # mutare:ignore directive — 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-ignores escalates).
  • Reporters — human (default, survivor diffs + score), plus machine-readable
    json (Stryker / mutation-testing-elements schema), html (interactive
    viewer), and sarif (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-score to gate, --line to 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-sandbox opts back into a throwaway copy.
  • Umbrella-aware — target one app, several, or the whole workspace.
  • Extension surfaceMutare.Mutator (custom mutators), independent
    Mutare.CallRouting and Mutare.UseExpansion capabilities, :extensions for
    non-mutating integrations, declarative :call_routes configuration
    (user-tier treatments only; the adapter-grade treatments must come from a
    module implementing Mutare.CallRouting), the Mutare.AST node
    constructors that discharge Sourceror's emission invariants for plugins, and
    the Mutare.Calls call-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 with Mutare.EnvironmentError instead of
    silently registering routes against nothing on an external-source run.
  • mix igniter.install mutare installer 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: :skip makes a whole call an inert leaf (functions, macros, and
    the construct special forms alike — Kernel.if/2 or case included; a piped receiver and
    the enclosing function's return-value mutants are unaffected), :raw leaves
    an argument as written, :interior mutates 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 :skip only, 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}.