Java edition
Compatibility
mutastic’s version says what a release may change. From 1.0.0, a release that breaks one of the promises below is a new major version. Until then, a release that has to break one says so in its release notes. Each release’s notes are its section of the changelog: Java, Rust.
Mutant ids
Section titled “Mutant ids”An ignore, a baseline, a script’s mutastic verify m855c8e3c and an agent’s notes all name a
mutant by its id. An id names the same mutant in every later version, as long as its method’s code
hasn’t changed.
An id is made from the mutant’s class, its method, its position among that method’s places of its kind, and its change, with the operator for all but mutastic’s first operators. The line isn’t part of it, so code moving around a method doesn’t change it, and nor does choosing more operators: they add mutants, and the others keep their ids.
What keeps an id:
- Code moving. Lines added or removed above it, the method moved within its class, comments and blank lines: the line isn’t part of an id.
- Other operators. Choosing more operators, or fewer, adds or takes away mutants; the others keep their ids.
- A new version of mutastic. mutastic’s own operators keep their ids from release to release; a new operator only adds ids.
What changes an id:
- The class or the method. Renaming the class or moving it to another package, renaming the method, or changing its parameter or return types.
- The same kind of change added or removed earlier in the method, which moves the count of its places, as the limit below explains.
- What the change makes of the code. A mutant that changes the code to something else is a new mutant, with a new id.
The limit: a position is counted in the method’s code as it is, so when the method’s code changes, an id can come to mean a different mutant in the same method. Add a condition before another, and the later condition’s mutants are counted one place further on: the earlier position, and its id, now belong to the new condition. An id can’t be both stable across edits and free of the code it’s in.
Identical lines show it plainest. Four i = i + 1; lines in a row compile to the same code, so
what tells their mutants apart is their place in the method, not their text or their line: each
has its own ids and its own card, on its own line, and comments or blank lines added above them
move the cards but no id. Delete the second line, though, and the third line’s mutants take the
ids the second’s had, the fourth’s take the third’s, and the fourth’s ids are gone.
So an ignore keeps the source line its mutant was on as well as the id, and lapses when that line changes. An id written down while the code changes, in a note or a script, should be checked again once the code settles.
An operator from a plugin makes its ids from its own id and its changes’ labels. They’re as stable as those: a plugin that renames a label moves its mutants’ ids.
mutastic’s build holds this: it keeps the id of every mutant its own operators make in a fixed set of compiled classes, and fails when a change moves one.
Scores
Section titled “Scores”A score is the share of the mutants caught. The same code and tests score the same, unless a release says otherwise:
- A patch release (
1.2.3to1.2.4) never moves a score. It fixes what mutastic gets wrong without changing which mutants count. - A minor release (
1.2to1.3) may change a score: an operator added to the default set, or a filter that skips more or less. Its release notes say which, and how scores move, under Scoring changes. - A score compares only with one under the same operators. A run compares itself with the last run that used the same set, so choosing more operators never shows as a drop.
A build’s threshold may need a look after a minor release’s scoring change, never after a patch.
Plugin API
Section titled “Plugin API”A plugin built against one release builds and runs against every later release with the same major version. That covers:
Mutator, an operator on an instruction, andClassMutator, one on a class as a whole, andFilter, which skips a method’s mutants with a reason: no method a plugin’s class implements is added without a body, and nothing a plugin calls on the types they use is taken away or changed.- Rules files: a rules file that reads today reads in later releases, with the same operators, changes and words. A new key may be added; none is taken away or given another meaning.
- Labels and ids: the labels mutastic’s own operators give their changes don’t change, as they’re part of ids.
What’s marked @Internal in the API reference is public only for
mutastic’s own modules, and may change in any release.
mutastic’s build holds this too, from 1.0.0: it compares the API and the rules files the last 1.0+ release shipped with what this one reads, and fails on a change that would break a plugin.
The events format
Section titled “The events format”--format=jsonl writes mutastic.events/v1 events, which CI scripts, editors and agents read.
Within v1 an event only gains: a new optional field, a new event, a new value a field can take.
No field is taken away, renamed, given another type, or made required or optional. A change that
would break a reader comes with a major release, as mutastic.events/v2.
mutastic’s build holds this as well, from 1.0.0: it compares the events schema with the last 1.0+
release’s, and fails on a change that would break a reader. Before 1.0.0 the schema may change;
from 1.0.0 it only gains. Schemas and stability has the schemas,
for the events, mutastic.yaml and rules files, and exactly what counts as breaking.
The two editions
Section titled “The two editions”From 1.0.0, the Java and Rust editions release each minor version together, at the same version,
and keep the same promises; either may ship a patch alone. In a Gradle or Maven project, the native
mutastic runs the newest Java edition of its own major and minor version, so mutastic 1.2.0
runs Java 1.2.3 once that’s out, unless MUTASTIC_VERSION names another.
Builds
Section titled “Builds”mutastic runs a Gradle or Maven build as its own developers do: with the build’s wrapper
(gradlew, mvnw) whenever it has one, including from a module’s directory, where the wrapper is
at the top of the build. A build without a wrapper runs with the gradle or mvn on the PATH.
What’s experimental
Section titled “What’s experimental”Some things ship before they’re settled, and may change in a minor release: Kotlin support,
proposed rewrites (proposed/rewrite and the files
in .mutastic/proposed/), and any other operator or option the docs call experimental. Their
mutants’ ids may move as they settle.