App-Test-Generator

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

	  wraps a condition's content in PPI::Statement::Expression, so an
	  operator's immediate parent is never literally
	  PPI::Structure::Condition. Operators inside if/unless/while/until
	  conditions were always tagged 'expression'. Now uses the same
	  ancestor-walking _in_conditional() helper already shared by
	  BooleanNegation and ReturnUndef.
	- Fix Analyzer::SideEffect.pm's mutates_self and mutates_globals
	  detection ($self->{field} = ... assignment and %ENV/%SIG/@ARGV
	  mutation) matching against the raw method body instead of the
	  comment/string-stripped $code_only, so a field-assignment-like
	  fragment inside a string literal or comment could be mistaken for
	  an actual mutation. Both checks now match against $code_only, same
	  as the keyword/operator counts already fixed in a prior release.
	- Fix Emitter::Perl.pm's _emit_method_tests() having no dispatch
	  branch for the boundary_tests plan flag: TestStrategy.pm sets
	  $plan{boundary_tests} when a method's schema carries non-empty
	  _yamltest_hints, but the corresponding $TEST_BOUNDARY constant was
	  defined and never read, so methods planned for boundary testing
	  silently got zero generated test code for it. Added the dispatch
	  line plus a new _emit_boundary_test() that emits one smoke-test
	  block per boundary_values/invalid_inputs hint value.

README.md  view on Meta::CPAN


    { href => { type => 'any', optional => 1 } }

#### output

    { type => 'string' }

## render\_arrayref\_map

Render a hashref whose values are arrayrefs into a Perl source-code
fragment suitable for use as a hash literal in a generated test file.

    my $code = render_arrayref_map({ name => ['', 'a' x 100] });

### Arguments

- `$href`

    A hashref whose values are arrayrefs. Keys whose values are not
    arrayrefs are silently skipped.

README.md  view on Meta::CPAN

#### input

    { href => { type => 'any', optional => 1 } }

#### output

    { type => 'string' }

## perl\_quote

Convert any Perl value into a source-code fragment that reproduces that value
when evaluated in a generated test file.

### Arguments

- `$v`

    Any Perl value. May be undef, a scalar, an arrayref, a Regexp, or a blessed
    object. All types are handled — undef becomes `'undef'`, the strings
    `'true'`/`'false'` become the Perl boolean constants `!!1`/`!!0`,
    numbers are unquoted, other strings are single-quoted, arrayrefs recurse,

bin/test-generator-mutate  view on Meta::CPAN

=head2 --changed_only

Only mutate files that were changed in the most recent commit, as
determined by C<git diff --name-only HEAD~1 HEAD>. Files not changed
in the current commit retain their mutation results from the previous
dashboard run. This significantly reduces CI runtime while preserving
accuracy for the files that actually changed.

=head2 --exclude <path>

Exclude files matching the given path fragment from mutation testing.
May be specified multiple times. For example:

    --exclude lib/Devel --exclude lib/App/Test/Generator/Sample

=head2 --base_sha <sha>

The git commit SHA to use as the base when computing which files have
changed under C<--changed_only>. Defaults to C<HEAD~1>.

Use this when automated commits (such as coverage snapshots or generated

lib/App/Test/Generator.pm  view on Meta::CPAN

							croak("Boolean is expected to return $expected_str");
						}
					} else {
						$corpus_code .= "is(\$obj->$function($input_str), $expected_str, " . q_wrap($desc) . ");\n";
					}
				}
			}
		}
	}

	# Prepare seed/iterations code fragment for the generated test
	my $seed_code = '';
	if (defined $seed) {
		# ensure integer-ish
		$seed = int($seed);
		$seed_code = "srand($seed);\n";
	}

	my $determinism_code = 'my $result2;' .
		'eval { $result2 = do { ' . (defined($position_code) ? $position_code : $call_code) . " }; };\n" .
		'is_deeply($result2, $result, "deterministic result for same input");' .

lib/App/Test/Generator.pm  view on Meta::CPAN

	my @pairs = map {
		perl_quote($_) . ' => ' . perl_quote($href->{$_})
	} sort keys %{$href};

	return join(', ', @pairs);
}

=head2 render_arrayref_map

Render a hashref whose values are arrayrefs into a Perl source-code
fragment suitable for use as a hash literal in a generated test file.

    my $code = render_arrayref_map({ name => ['', 'a' x 100] });

=head3 Arguments

=over 4

=item * C<$href>

A hashref whose values are arrayrefs. Keys whose values are not

lib/App/Test/Generator.pm  view on Meta::CPAN

# --------------------------------------------------
# q_wrap
#
# Purpose:    Wrap a string in the most readable
#             q{} form that does not require escaping,
#             falling back to single-quoted form with
#             escaped apostrophes if no delimiter is
#             available.
#
# Entry:      $s - the string to wrap. May be undef.
# Exit:       Returns a Perl source-code fragment that
#             evaluates to the original string value,
#             or the string 'undef' if $s is undef.
#
# Notes:      index() returns -1 when not found and
#             any value >= 0 when found, including 0
#             for a delimiter at the start of the
#             string. We compare against $INDEX_NOT_FOUND
#             to make this boundary explicit and to
#             prevent off-by-one mutation survivors.
#             See GitHub issue #1.

lib/App/Test/Generator.pm  view on Meta::CPAN


	# Replace NUL bytes with \0 — valid only in
	# double-quoted string context in generated code
	$s =~ s/\0/\\0/g;

	return $s;
}

=head2 perl_quote

Convert any Perl value into a source-code fragment that reproduces that value
when evaluated in a generated test file.

=head3 Arguments

=over 4

=item * C<$v>

Any Perl value. May be undef, a scalar, an arrayref, a Regexp, or a blessed
object. All types are handled — undef becomes C<'undef'>, the strings

lib/App/Test/Generator/Analyzer/SideEffect.pm  view on Meta::CPAN

		mutates_globals => 0,
		performs_io     => 0,
		calls_external  => 0,
		mutation_fields => [],
	);

	# --------------------------------------------------
	# Detect assignment to $self->{field} — any such
	# assignment means the method mutates its own state.
	# Matched against $code_only so a field-assignment-like
	# fragment appearing inside a string literal or comment
	# is not mistaken for an actual mutation.
	# --------------------------------------------------
	my %seen_fields;
	while($code_only =~ /\$self->\{(\w+)\}\s*=/g) {
		$result{mutates_self} = 1;

		# Deduplicate field names in case the same field
		# is assigned more than once in the method body
		push @{ $result{mutation_fields} }, $1
			unless $seen_fields{$1}++;

lib/App/Test/Generator/SchemaExtractor.pm  view on Meta::CPAN

		}
	}

	return \%params;
}

# --------------------------------------------------
# _map_formal_input_type
#
# Purpose:    Extract and normalise the type string
#             from a parameter spec fragment such as
#             "type => 'scalar | scalarref'".
#             Handles union types by returning the
#             canonical ATG type for the first
#             recognised alternative.
#
# Entry:      $spec - text content of a { } block
#                     from a =head3|4 Input spec.
#
# Exit:       Canonical type string, or undef when
#             no 'type' key is present or the value

t/Generator.t  view on Meta::CPAN


subtest 'perl_sq() replaces NUL bytes' => sub {
	is(App::Test::Generator::perl_sq("a\0b"), 'a\\0b', 'NUL replaced with \\0');
};

subtest 'perl_sq() leaves plain string unchanged' => sub {
	is(App::Test::Generator::perl_sq('hello'), 'hello', 'plain string unchanged');
};

# ------------------------------------------------------------------
# perl_quote — convert a Perl value to source-code fragment
# ------------------------------------------------------------------
subtest 'perl_quote() returns undef literal for undef' => sub {
	is(App::Test::Generator::perl_quote(undef), 'undef', 'undef -> "undef"');
};

subtest 'perl_quote() converts true/false string booleans' => sub {
	is(App::Test::Generator::perl_quote('true'),  '!!1', '"true" -> "!!1"');
	is(App::Test::Generator::perl_quote('false'), '!!0', '"false" -> "!!0"');
};

t/Mutant_unit.t  view on Meta::CPAN


subtest 'description() returns the stored description' => sub {
	my $m = _mutant(description => 'Flip > to <');
	is($m->description, 'Flip > to <', 'description returned correctly');
};

# ==================================================================
# original()
#
# POD spec:
#   Returns the original source fragment string.
# ==================================================================

subtest 'original() returns the stored original value' => sub {
	my $m = _mutant(original => '>=');
	is($m->original, '>=', 'original returned correctly');
};

subtest 'original() can hold arbitrary Perl code snippets' => sub {
	my $m = _mutant(original => 'return $self->{name}');
	is($m->original, 'return $self->{name}',

t/function.t  view on Meta::CPAN

	my $e = bless {}, 'App::Test::Generator::SchemaExtractor';

	is($e->_extract_function_name("sub foo { return 1; }"), 'foo', 'a simple one-line sub declaration is matched');
	is($e->_extract_function_name("\n\tsub  bar_baz2 {\n\t\treturn;\n\t}"), 'bar_baz2',
		'leading whitespace and extra spaces before the name are tolerated');
	is($e->_extract_function_name("my \$x = 1; sub foo {}"), undef,
		'returns undef when "sub NAME" does not occur at the very start of the string');
	is($e->_extract_function_name(''), undef, 'returns undef for an empty string');
};

subtest 'SchemaExtractor::_map_formal_input_type - maps formal spec type fragments to canonical ATG types' => sub {
	my $e = bless {}, 'App::Test::Generator::SchemaExtractor';

	is($e->_map_formal_input_type(q{type=>'scalar'}), 'string', 'scalar maps to string');
	is($e->_map_formal_input_type(q{type => "Integer"}), 'integer', 'case is folded before lookup');
	is($e->_map_formal_input_type(q{type => 'scalar | scalarref'}), 'string',
		'a union type resolves to the first recognised alternative');
	is($e->_map_formal_input_type(q{type => 'bogus'}), undef, 'an unrecognised type name returns undef');
	is($e->_map_formal_input_type(q{name => 'x'}), undef, 'a spec fragment with no type key returns undef');
};

subtest 'SchemaExtractor::_analyze_output - dispatch isolation, conditional _validate_output, and empty fallback' => sub {
	my $e = bless {}, 'App::Test::Generator::SchemaExtractor';
	my @detectors = qw(_analyze_output_from_pod _analyze_output_from_code _enhance_boolean_detection _detect_list_context _detect_void_context _detect_chaining_pattern _detect_error_conventions);
	my @calls;

	for my $sub (@detectors, '_validate_output') {
		Test::Mockingbird::mock('App::Test::Generator::SchemaExtractor', $sub, sub { push @calls, $sub; return; });
	}



( run in 4.242 seconds using v1.01-cache-2.11-cpan-b16cb0d3907 )