Commands
Ten scripts, all installed by the distribution. Four carry the daily workflow — generate, extract, mutate, report — and the rest are helpers around them.
| Command | Purpose |
|---|---|
fuzz-harness-generator | Schema → runnable test file. |
extract-schemas | Perl module → schemas, optionally fuzzed and minimised. |
test-generator-mutate | Mutation testing against your suite. |
test-generator-index | Dashboard HTML from coverage, LCSAJ and mutation data. |
generate-test-dashboard | The whole CI pipeline as one shell script. |
benchmark-generator | Schema → Benchmark::cmpthese script. |
pod-example-tester | POD examples → round-trip test file. |
deploy-workflows | Two GitHub Actions workflows → another repository. |
instrument-modules | Writes the .lcsaj.json path definitions. |
demo_extractor | Self-contained demonstration of schema extraction. |
fuzz-harness-generator
generatefuzz-harness-generator [-r] [-o FILE] schema.yml
fuzz-harness-generator --dry-run schema.yml
fuzz-harness-generator --replay-corpus DIR -o t/fuzz_replay.t
Renders the harness. Without -o it prints the test file to
standard output, which is how many projects keep generated tests out of
version control. With -r it writes the file to a temporary
path, runs it through prove and prints the result, which is
how it is used from t/fuzz.t.
| Option | Effect |
|---|---|
-r, --run | Run the generated harness with prove and report the outcome. |
-o FILE | Write the harness to FILE instead of standard output. |
--dry-run | Parse the schema and report what would be generated, without writing a test file. |
--replay-corpus DIR|FILE | Emit a harness that replays a saved corpus — every recorded input, in a deterministic order — rather than fuzzing afresh. |
-v, --verbose | More diagnostics on standard error. |
extract-schemas
extractextract-schemas [options] lib/My/Module.pm
| Option | Effect |
|---|---|
-o DIR, --output-dir DIR | Where the YAML files go. Default schemas/; created if absent. |
-F NAME, --function NAME | Extract everything, then keep only NAME.yml and test only that routine. Prints the available names and exits 1 if NAME does not exist. |
-s MODE, --strict-pod MODE | off, warn or fatal: what to do when POD and code disagree about a parameter's type. |
-f, --fuzz | Fuzz the extracted schemas as they are produced. |
--fuzz-iters N | Iterations per method while fuzzing. Default 100. |
--fuzz-all | Fuzz routines that have no input schema too. |
-c DIR, --corpus-dir DIR | Where corpora persist between runs. Default schemas/corpus/. |
--minimize-corpus | Trim each corpus to the smallest subset that still covers every discovered branch. |
-v, --verbose | Show the analysis as it runs. |
How extraction works, and what the confidence levels mean.
test-generator-mutate
mutationtest-generator-mutate --lib lib --tests t
test-generator-mutate --file lib/My/Module.pm
test-generator-mutate --min-score 85 --json mutation.json
| Option | Effect |
|---|---|
--lib DIR | The library directory to mutate. Default lib. |
--tests DIR | The test directory to run per mutant. Default t. |
--file PATH | Mutate one file only. |
--json FILE | Write the machine-readable report. mutation.json is what the dashboard reads. |
--min-score N | Exit non-zero if the kill percentage falls below N. |
--changed_only, --base_sha SHA | Mutate only files changed against a git base — the fast mode for a pull request. |
--exclude REGEX | Skip files matching the pattern. Repeatable. |
--cover_json FILE | Use a Devel::Cover report to prioritise mutants in covered code. |
--fail-fast | Stop at the first surviving mutant. |
-v, --verbose | Show each mutant as it is applied and tested. |
Operators, skip markers and the survivor pipeline.
test-generator-index
reporttest-generator-index
test-generator-index --generate_mutant_tests=t --generate_test=mutant --generate_fuzz
Reads the artefacts other steps produced and writes the dashboard. It takes no general options beyond the three that drive the mutant-test pipeline; where its inputs live is fixed, so that the CI workflow and a local run cannot disagree.
| Option | Effect |
|---|---|
--generate_mutant_tests=DIR | Write a TODO-stub test file for every survivor into DIR. |
--generate_test=mutant | For NUM_BOUNDARY survivors, extract the enclosing routine's schema and write a runnable boundary schema into t/conf/. |
--generate_fuzz | Merge survivor boundary values into copies of the existing schemas, as t/conf/mutant_fuzz_*.yml. |
The feedback loop explains what each of those flags produces.
generate-test-dashboard
pipelinebin/generate-test-dashboard
A bash script, not a Perl program. It clears last run's artefacts, builds
blib, runs the suite under Devel::Cover, exports JSON
coverage, derives LCSAJ targets, runs the suite again under the LCSAJ
runtime debugger, merges the per-process hit files, runs mutation testing,
writes the mutant schemas and finally calls
test-generator-index.
It runs the suite twice on purpose: once for statement and branch
coverage, once for path coverage. That is why the workflow sets
NO_NETWORK_TESTING and why t/fuzz.t keeps its
extended-test guard — without them, a CI run takes hours rather than
minutes.
benchmark-generator
benchmarkbenchmark-generator -i schemas/abs.yml
benchmark-generator -i schemas/abs.yml -o benchmarks/abs.pl
benchmark-generator -i schemas/abs.yml -r
Each transform in the schema becomes one named variant in a
Benchmark::cmpthese call, with representative values derived
from that transform's types and bounds. A schema with no transforms
produces a single 'default' variant. -r writes
the script to a temporary file and runs it immediately.
pod-example-tester
generatepod-example-tester lib/My/Module.pm
pod-example-tester --output t/synopsis.t lib/My/Module.pm
pod-example-tester --no-annotated lib/My/Module.pm
Collects runnable examples from a module's POD — SYNOPSIS
verbatim blocks, =for example begin/end blocks, and indented
lines annotated # returns value or # => value —
and emits one subtest per example. Annotated examples get an
is() assertion on the documented result; unannotated blocks
are wrapped in eval and checked for "did not die".
Two safety filters matter: blocks that contain only shell commands are
dropped, and any line that would run system,
exec, a backtick or qx is replaced with a note,
so a SYNOPSIS that demonstrates invoking a command does not actually run
it inside your test suite.
deploy-workflows
cideploy-workflows
deploy-workflows --target /path/to/my-module
deploy-workflows --dry-run --target /path/to/my-module
Copies dashboard.yml and mutate.yml into
TARGET/.github/workflows/. Both files are embedded in the
script, so it works after a CPAN install with no source tree present. It
refuses to overwrite an existing file unless --force is
given, and --dry-run prints what it would do without touching
the filesystem.
The two workflows are portable by design: no shared script under
.github/scripts/, because a dependent repository only ever
receives the two YAML files.
instrument-modules
metricsinstrument-modules
instrument-modules lib/My/Module.pm
Writes a .lcsaj.json control-flow path definition for each
module into cover_html/mutation_html/lib/. With no arguments
it walks lib/ recursively. The dashboard reads these files to
compute the TER3 column, and the LCSAJ runtime debugger uses them to
record which paths were hit.
demo_extractor
demodemo_extractor
Creates a temporary sample module, extracts its schemas, prints them, and compares the result with the expected output. Useful as a tour of what extraction can infer, and as a quick check that the extractor still behaves after an upgrade.
Exit codes
The generating and reporting commands follow the usual Perl convention:
0 on success, 2 for a usage error, non-zero for a failure.
test-generator-mutate additionally exits non-zero when
--min-score is not met, which is what makes it usable as a CI
gate.