mix mob.mutate (mob_dev v0.7.0)

Copy Markdown View Source

Mutation-test the lines this branch changed.

mix mob.mutate                      # lines changed vs origin/master
mix mob.mutate --file lib/foo.ex    # every mutable line in one file
mix mob.mutate --base HEAD~3        # against a different base
mix mob.mutate --json

A green suite says the tests ran, not that they guard anything. This changes the production code one line at a time and reports the changes nothing noticed.

Options

  • --file PATH — mutate this file instead of the branch diff; repeatable
  • --base REF — diff base (default: origin/master)
  • --test-command — how to run the suite (default: mix test). Narrow it
                    when the full suite is slow: every mutant runs it once.
  • --max N — stop after N mutants
  • --json — machine-readable result on stdout, progress on stderr

Running it on this task

It reports most of run/1 as surviving, and that is accurate rather than a bug in either. This repo's convention is that a Mix task stays a thin unstubbed I/O wrapper and the decisions get extracted and tested — so the parts with a testable kernel (source_file?/1, summary/4, dirty_message/2, and everything in MobDev.Mutate) are guarded, and the orchestration that only sequences them is not. The tool measures what the suite guards; it does not know what you decided not to guard.

Exit status

Non-zero when any mutation survived, so this can gate a change the way a failing test does.

What it does to your working tree

It rewrites real source files in place and restores them from memory after each mutant. An interrupted run leaves the last mutant on disk — Ctrl-C, a closed terminal and an OOM kill all skip the restore, and no amount of in-process care changes that.

So the run refuses to start unless the files it would touch are clean in git. That way git checkout -- <file> is always a complete recovery, which is the only guarantee that survives being killed outright. An earlier version of this text claimed a snapshot restored the tree "even if the run is interrupted"; there was no snapshot, the claim was false, and it talked the reader out of the one habit that would have saved them.

Summary

Functions

The refusal for a tree that is not safe to mutate, or nil.

Functions

dirty_message(output, status)

@spec dirty_message(String.t(), non_neg_integer()) :: String.t() | nil

The refusal for a tree that is not safe to mutate, or nil.

Pure, and tested, because it is the only thing standing between an interrupted run and someone's uncommitted work. A git status that fails is as disqualifying as one that reports changes: if we cannot tell whether recovery is possible, we must not proceed as though it is.