NAME
App::Test::Generator::Mutator - Generate mutation tests
VERSION
Version 0.30
DESCRIPTION
App::Test::Generator::Mutator is a mutation engine that programmatically alters Perl source files to evaluate the effectiveness of a project's test suite. It analyzes modules, generates systematic code mutations (such as conditional inversions, logical operator changes, and other behavioral tweaks), and applies them within an isolated workspace so tests can be executed safely against each modified variant. By tracking which mutants are "killed" (cause tests to fail) versus those that "survive" (tests still pass), the module enables calculation of a mutation score, providing a quantitative measure of how well the test suite detects unintended behavioral changes.
prepare_workspace
Prepares an isolated temporary workspace for a single mutation test run.
The entire lib/ tree is copied into the workspace so that all module dependencies resolve correctly when the test suite runs against the mutant. Only after this copy is complete is the single target file overwritten by apply_mutant.
Arguments
Takes no arguments beyond the invocant.
Returns
A string containing the absolute path to the temporary directory that was created. The directory is automatically removed when the App::Test::Generator::Mutator object goes out of scope (via File::Temp's CLEANUP => 1 behaviour).
Side Effects
Creates a temporary directory under the system's default temp location.
Recursively copies the entire
lib_dirtree (defaultlib/) into the workspace usingFile::Copy::Recursive::dircopy.Sets
$self->{workspace}to the absolute path of the temporary directory.Sets
$self->{relative}to the path of the target file relative tolib_dir, for use byapply_mutant.
Notes
The workspace is only valid for a single file's mutation run. Call prepare_workspace once per file, then call apply_mutant once per mutant within that file. Because CLEANUP => 1 is set, the workspace is silently removed when the tempdir handle is garbage collected - do not store the path beyond the lifetime of the enclosing scope.
Example
my $mutator = App::Test::Generator::Mutator->new(
file => 'lib/My/Module.pm',
lib_dir => 'lib',
);
my @mutants = $mutator->generate_mutants();
for my $mutant (@mutants) {
my $workspace = $mutator->prepare_workspace();
$mutator->apply_mutant($mutant);
local $ENV{PERL5LIB} = "$workspace/lib";
my $survived = (system('prove', 't') == 0);
# workspace cleaned up automatically when $workspace goes out of scope
}
API Specification
Input
# Params::Validate::Strict schema
{
# No named parameters - invocant only.
# Required state (validated by new()):
# file => { type => SCALAR, callbacks => { 'file exists' => sub { -f $_[0] } } }
# lib_dir => { type => SCALAR, default => 'lib' }
}
Output
# Return::Set schema
{
type => SCALAR,
description => 'Absolute path to the temporary workspace directory',
callbacks => {
'is absolute path' => sub { File::Spec->file_name_is_absolute($_[0]) },
'directory exists' => sub { -d $_[0] },
},
}
SEE ALSO
bin/app-test-generator-mutate- Devel::Mutator
AUTHOR
Nigel Horne, <njh at nigelhorne.com>
Portions of this module's initial design and documentation were created with the assistance of AI.
LICENCE AND COPYRIGHT
Copyright 2026 Nigel Horne.
Usage is subject to licence terms.
The licence terms of this software are as follows:
Personal single user, single computer use: GPL2
All other users (including Commercial, Charity, Educational, Government) must apply in writing for a licence for use from Nigel Horne at the above e-mail.