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_dir tree (default lib/) into the workspace using File::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 to lib_dir, for use by apply_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.