App-Test-Generator
view release on metacpan or search on metacpan
t/extended_tests.t view on Meta::CPAN
#!/usr/bin/env perl
use strict;
use warnings;
use Test::Most;
use Capture::Tiny qw(capture);
use File::Path qw(make_path);
use File::Spec;
use File::Temp qw(tempdir tempfile);
use Scalar::Util qw(looks_like_number);
# Extended tests targeting:
# 1. Known surviving mutants in _dedup_mutants, _is_redundant_mutation,
# BooleanNegation, ReturnUndef, Emitter::Perl, Planner
# 2. Branch coverage gaps in Generator render helpers
# 3. LCSAJ/TER3 improvement via additional branch-path coverage
# 4. Stateful behaviour across multiple calls
BEGIN {
use_ok('App::Test::Generator');
use_ok('App::Test::Generator::Mutant');
use_ok('App::Test::Generator::Mutator');
use_ok('App::Test::Generator::Mutation::BooleanNegation');
use_ok('App::Test::Generator::Mutation::ReturnUndef');
use_ok('App::Test::Generator::Planner');
use_ok('App::Test::Generator::Emitter::Perl');
use_ok('App::Test::Generator::LCSAJ');
}
# ==================================================================
# Mutator._dedup_mutants â surviving mutant killers
#
# The boolean returns in _dedup_mutants use $seen{$key}++ (truthy
# after first occurrence) and !$seen{$key}++ (falsy on first, truthy
# after). Mutations flip these. The tests below assert exact counts
# to kill negation and undef-return survivors on lines 435-450.
# ==================================================================
{
no warnings 'once';
*_dedup_mutants = \&App::Test::Generator::Mutator::_dedup_mutants;
*_is_redundant_mutation = \&App::Test::Generator::Mutator::_is_redundant_mutation;
}
my $next_id = 0;
sub _make_mutant {
my %args = @_;
return App::Test::Generator::Mutant->new(
id => $args{id} // 'TEST_' . ++$next_id,
line => $args{line} // 1,
original => $args{original} // 'x',
description => $args{description} // 'test',
context => $args{context} // '',
line_content => $args{line_content} // '',
transform => $args{transform} // sub {},
);
}
subtest '_dedup_mutants: single unique mutant returns exactly 1' => sub {
my $mutants = [
_make_mutant(line => 5, original => '>', description => 'flip')
];
my $result = _dedup_mutants($mutants);
is(scalar @{$result}, 1, 'single unique mutant: count is exactly 1');
};
subtest '_dedup_mutants: two identical mutants return exactly 1' => sub {
my $mutants = [
_make_mutant(line => 5, original => '>', description => 'flip'),
_make_mutant(line => 5, original => '>', description => 'flip'),
t/extended_tests.t view on Meta::CPAN
}
};
subtest 'CoverageGuidedFuzzer: mutate handles arrayref' => sub {
my $f = App::Test::Generator::CoverageGuidedFuzzer->new(
schema => { input => { type => 'arrayref' } },
target_sub => sub { 1 },
iterations => 0,
seed => 42,
);
my $result;
lives_ok(
sub {
$result = App::Test::Generator::CoverageGuidedFuzzer::_mutate($f, [1, 2, 3])
},
'mutate([1,2,3]) lives',
);
is(ref($result), 'ARRAY', 'mutated arrayref is still an arrayref');
};
subtest 'CoverageGuidedFuzzer: mutate handles hashref' => sub {
my $f = App::Test::Generator::CoverageGuidedFuzzer->new(
schema => { input => { type => 'hashref' } },
target_sub => sub { 1 },
iterations => 0,
seed => 42,
);
my $result;
lives_ok(
sub {
$result = App::Test::Generator::CoverageGuidedFuzzer::_mutate($f, { a => 1, b => 2 })
},
'mutate({a=>1}) lives',
);
is(ref($result), 'HASH', 'mutated hashref is still a hashref');
};
subtest 'CoverageGuidedFuzzer: mutate passes blessed ref through unchanged' => sub {
my $f = App::Test::Generator::CoverageGuidedFuzzer->new(
schema => { input => { type => 'string' } },
target_sub => sub { 1 },
iterations => 0,
seed => 42,
);
my $obj = bless {}, 'FakeClass';
my $result = App::Test::Generator::CoverageGuidedFuzzer::_mutate($f, $obj);
is($result, $obj, 'blessed ref passed through unchanged');
};
subtest 'CoverageGuidedFuzzer: _rand_int returns a numeric value' => sub {
my $f = App::Test::Generator::CoverageGuidedFuzzer->new(
schema => { input => { type => 'integer', min => 5, max => 10 } },
target_sub => sub { 1 },
iterations => 0,
seed => 42,
);
for (1..20) {
my $val = App::Test::Generator::CoverageGuidedFuzzer::_rand_int(
$f, { min => 5, max => 10 }
);
ok(looks_like_number($val), "_rand_int returns numeric value (got $val)");
}
};
subtest 'CoverageGuidedFuzzer: _rand_num returns value within bounds' => sub {
my $f = App::Test::Generator::CoverageGuidedFuzzer->new(
schema => { input => { type => 'number' } },
target_sub => sub { 1 },
iterations => 0,
seed => 42,
);
for (1..10) {
my $val = App::Test::Generator::CoverageGuidedFuzzer::_rand_num(
$f, { min => 0, max => 1 }
);
ok($val >= 0 && $val <= 1,
"_rand_num($val) within [0, 1]");
}
};
subtest 'CoverageGuidedFuzzer: _validate_value correctly validates types' => sub {
my $f = App::Test::Generator::CoverageGuidedFuzzer->new(
schema => { input => { type => 'string' } },
target_sub => sub { 1 },
iterations => 0,
seed => 42,
);
# integer
is(App::Test::Generator::CoverageGuidedFuzzer::_validate_value(
$f, 42, { type => 'integer' }), 1, 'integer 42: valid');
is(App::Test::Generator::CoverageGuidedFuzzer::_validate_value(
$f, 3.14, { type => 'integer' }), 0, 'float 3.14: invalid integer');
# number
is(App::Test::Generator::CoverageGuidedFuzzer::_validate_value(
$f, '3.14', { type => 'number' }), 1, 'string 3.14: valid number');
# boolean
is(App::Test::Generator::CoverageGuidedFuzzer::_validate_value(
$f, '1', { type => 'boolean' }), 1, '"1": valid boolean');
is(App::Test::Generator::CoverageGuidedFuzzer::_validate_value(
$f, '2', { type => 'boolean' }), 0, '"2": invalid boolean');
# string with min/max
is(App::Test::Generator::CoverageGuidedFuzzer::_validate_value(
$f, 'hi', { type => 'string', min => 1, max => 10 }), 1, 'string in range: valid');
is(App::Test::Generator::CoverageGuidedFuzzer::_validate_value(
$f, '', { type => 'string', min => 1 }), 0, 'empty string below min: invalid');
# arrayref
is(App::Test::Generator::CoverageGuidedFuzzer::_validate_value(
$f, [1,2], { type => 'arrayref' }), 1, 'arrayref: valid');
is(App::Test::Generator::CoverageGuidedFuzzer::_validate_value(
$f, 'str', { type => 'arrayref' }), 0, 'string: invalid arrayref');
# hashref
is(App::Test::Generator::CoverageGuidedFuzzer::_validate_value(
$f, {a=>1}, { type => 'hashref' }), 1, 'hashref: valid');
};
( run in 0.700 second using v1.01-cache-2.11-cpan-6aa56a78535 )