VAST

VAST vs mutate

vary vast tests the compiler. vary mutate tests your tests. Both involve running programs multiple ways and comparing results, but they answer different questions.

Two commands, two questions

Vary has two verification tools that both involve running programs multiple ways and comparing results. They sound similar but solve different problems.

vary vast validates the compiler. vary mutate validates your tests.

vary vast: compiler semantic validation

VAST asks one question: does the compiler preserve the meaning of programs?

It generates random valid Vary programs and runs each one through multiple independent execution paths. If the paths disagree, the compiler has a bug.

TechniqueWhat it does
Random program generationProduces valid Vary programs from a seeded, deterministic generator
Metamorphic transformsRewrites a program in a semantically equivalent way and checks that the result stays the same
Mutation expansionInjects known-wrong changes into the compiler pipeline to verify that the comparison infrastructure detects them
Three-path comparisonRuns each program through AST interpreter, IR interpreter, and JVM bytecode, which should all agree
Shrinking and corpus replayMinimizes a failing program to isolate the bug, then replays known-interesting programs from a saved corpus

The target is the compiler pipeline itself: the optimizer, codegen, type checker, and every stage between parsing and execution.

vary vast --count 1000 --seed 42
vary vast --profile control --count 100

vary mutate: user-facing mutation testing

Mutation testing asks a different question: would your tests notice if something in the code changed?

It makes small changes to compiled bytecode (and optionally the AST), runs your tests against each changed version, and reports which changes your tests missed.

TechniqueWhat it does
Bytecode mutationSingle-instruction swaps in compiled bytecode, no recompilation
AST-level mutationHigher-level structural changes (optional)
Test executionRuns your tests against each mutant
Survivor analysisReports which mutations your tests failed to catch
Observability diagnosticsDetermines whether the mutation's effect reached a test boundary at all

The target is your test suite, the tests you wrote for your own code.

vary mutate math.vary
vary mutate src/ --why "add:LIT_CHANGE:abc123"

Side by side

vary vastvary mutate
QuestionDoes the compiler preserve semantics?Would your tests notice a code change?
TargetThe Vary compilerYour test suite
InputGenerated programs (nobody wrote them)Your source files and tests
Mutation roleValidation tool, confirms VAST's comparison infra catches injected faultsPrimary technique, each mutant probes a test gap
Execution pathsAST interpreter, IR interpreter, JVM bytecodeOriginal bytecode vs. mutated bytecode
OutputAgreement/mismatch verdicts per seedKill rate, survivor analysis, fix suggestions
FindsMiscompilations, optimizer bugs, codegen errorsWeak tests, missing assertions, untested branches
Who benefitsCompiler developersAnyone writing Vary code

Why mutation appears in both

The word "mutation" shows up in both tools, but it means different things.

In vary mutate, mutation is the point. You mutate compiled code to probe whether tests detect the change. A surviving mutant means your tests have a gap.

In vary vast, mutation is a self-test. VAST mutates generated programs or compiler internals to verify that its own comparison infrastructure works. If VAST's three-path comparison fails to detect an injected fault, that is a bug in VAST, not in your code. This is sometimes called mutation expansion: using mutation as a confidence check on the testing tool rather than on user code.

Where PIT fits

There is a third mutation-testing tool in the repository: PIT.

PIT is not a Vary command. It mutation-tests the Kotlin compiler implementation itself.

ToolWhat it tests
./gradlew :compiler:pitest / make kotlin-mutationKotlin compiler code and Kotlin unit tests
vary mutateVary user code and the tests written for that code
vary vastCompiler semantic correctness through generated Vary programs

This means the three tools answer different questions:

ToolCore question
PITDo our Kotlin tests catch mistakes in the compiler code?
vary vastDoes the compiler preserve the meaning of Vary programs?
vary mutateDo your tests catch mistakes in your Vary code?

PIT tests the implementation from the inside.

VAST tests the compiler from the outside.

vary mutate tests user test suites.

They work together

vary mutate validates your tests. vary vast validates the compiler that runs your tests. Neither replaces the other.

A high mutation score does not help if the compiler itself is wrong. A clean VAST run does not help if nobody tested the user code. Both layers need to hold.

← CI integration
Comparison with other systems →