App-Test-Generator
view release on metacpan or search on metacpan
lib/App/Test/Generator/Template.pm view on Meta::CPAN
return undef unless $arrayref && ref $arrayref eq 'ARRAY' && @{$arrayref};
return $arrayref->[ int(rand(scalar @$arrayref)) ];
}
sub rand_ascii_str {
my $len = shift // int(rand(10)) + 1;
# join '', map { chr(97 + int(rand(26))) } 1..$len;
return Data::Random::String->create_random_string(length => $len, contains => 'alphanumeric');
}
my @unicode_codepoints = (
0x00A9, # ©
0x00AE, # ®
0x03A9, # Ω
0x20AC, # â¬
0x2013, # â (en-dash)
0x0301, # combining acute accent
0x0308, # combining diaeresis
0x1F600, # ð (emoji)
0x1F62E, # ð®
0x1F4A9, # ð© (yes)
lib/App/Test/Generator/Template.pm view on Meta::CPAN
$len = int(rand(10)) + 1; # length random number between 1 and 10
}
return '' if($len == 0);
if(!($config{'test_non_ascii'} // 0)) {
return rand_ascii_str($len);
}
# my $rc = _rand_str_basic($len);
# $rc = _rand_unicode_fuzzer($len);
# my $l = Unicode::GCString->new($rc)->length();
# if($len > $l) {
# $rc .= 'a' x ($len - $l); # Why is this needed?
# }
# return $rc;
# TODO: length issues at the moment
my $mode = int(rand(5)); # 0..4
my $rc = _rand_str_basic($len);
$rc = _rand_str_basic($len) if $mode == 0;
$rc = _rand_codepoint_exact($len) if $mode == 1;
$rc = _rand_grapheme_exact($len) if $mode == 2;
$rc = _rand_unicode_fuzzer($len) if $mode == 3;
$rc = rand_ascii_str($len) if($mode == 4);
my $rc_len = Unicode::GCString->new($rc)->length();
if($rc_len > $len) {
my $gcstr = Unicode::GCString->new($rc);
$rc = $gcstr->substr(0, $len)->as_string();
$rc_len = Unicode::GCString->new($rc)->length();
}
if($len > $rc_len) {
$rc .= 'a' x ($len - $rc_len);
$rc_len = Unicode::GCString->new($rc)->length();
}
fail("BUG $rc_len != $len (mode == $mode)") if($rc_len != $len);
return $rc;
}
#####################################################
# 1. EXACT-LENGTH CODEPOINT MODE
# Generate a random string: mostly ASCII, sometimes unicode, sometimes nul bytes or combining marks
#####################################################
sub _rand_str_basic
{
my $len = $_[0];
my @chars;
for (1..$len) {
my $r = rand();
if ($r < PROB_LOWERCASE) {
push @chars, chr(97 + int(rand(26))); # a-z
} elsif ($r < 0.88) {
push @chars, chr(65 + int(rand(26))); # A-Z
} elsif ($r < 0.95) {
push @chars, chr(48 + int(rand(10))); # 0-9
} elsif($r < 0.975) {
push @chars, _rand_unicode_char(); # occasional emoji/marks
} elsif($config{'test_nuls'}) {
push @chars, chr(0); # nul byte injection
} else {
push @chars, chr(97 + int(rand(26))); # a-z
}
}
if (rand() < 0.08) {
# 8% chance to prepend combining acute accent (0301)
$chars[-1] = chr(0x0301);
lib/App/Test/Generator/Template.pm view on Meta::CPAN
return join('', @chars);
}
# helper for codepoint mode
sub _rand_base_char {
my $r = rand();
if ($r < 0.70) { return chr(97 + int(rand(26))); }
if ($r < 0.88) { return chr(65 + int(rand(26))); }
if ($r < 0.95) { return chr(48 + int(rand(10))); }
return _rand_unicode_char();
}
#####################################################
# 3. EXACT-LENGTH GRAPHEME-CLUSTER MODE
# each "character" is a whole grapheme cluster:
# - emoji with ZWJ sequences
# - flags
# - skin-tone variants
# - accented characters
#####################################################
lib/App/Test/Generator/Template.pm view on Meta::CPAN
}
return join('', @clusters);
}
####################################################
# 4. UNICODE FUZZER MODE
# Extremely aggressive: invalid sequences, NULs, bidirectional markers, Zalgo
# Exclude unpaired surrogates for now, since TAP::Harness complains about that
####################################################
sub _rand_unicode_fuzzer {
my $len = $_[0];
my @out;
my @zalgo_up = map { chr($_) } (0x030D..0x036F);
my @bidi = ("\x{202A}", "\x{202B}", "\x{202D}", "\x{202E}", "\x{2066}", "\x{2067}");
my @weird = $config{'test_nuls'} ? ("\x{0000}", "\x{FFFD}", "\x{FEFF}") : ("\x{FFFD}", "\x{FEFF}");
for (1..$len) {
my $r = rand();
lib/App/Test/Generator/Template.pm view on Meta::CPAN
$cp = int(rand(0x10FFFF));
} while ($cp >= 0xD800 && $cp <= 0xDFFF);
push @out, chr($cp);
} elsif ($r < 0.40) {
push @out, chr(65 + int(rand(26))) . $zalgo_up[rand @zalgo_up]; # Zalgo
} elsif ($r < 0.55) {
push @out, $bidi[rand @bidi];
} elsif ($r < 0.70) {
push @out, $weird[rand @weird];
} else {
push @out, _rand_unicode_char();
}
}
return join('', @out);
}
###################################################
# Random Unicode character helper
###################################################
sub _rand_unicode_char
{
if(rand() < 0.5) {
my $cp = $unicode_codepoints[ int(rand(@unicode_codepoints)) ];
return chr($cp);
}
my @pool = (
0x00A9, 0x00AE, 0x2600, 0x2601,
0x1F600 + int(rand(200)), # emoji block
0x0300 + int(rand(80)), # combining marks
);
return chr( $pool[int rand @pool] );
}
t/edge_cases.t view on Meta::CPAN
print $fh "input:\n x:\n type: integer\n min: 100\n max: 1\n";
print $fh "output:\n type: integer\n";
close $fh;
# Should not crash even with inverted constraints
lives_ok(
sub { capture(sub { App::Test::Generator->generate($path) }) },
'inverted min/max does not crash',
);
};
subtest 'Generator: schema with unicode function name' => sub {
my ($fh, $path) = tempfile(SUFFIX => '.yml', UNLINK => 1);
binmode $fh, ':utf8';
print $fh "module: builtin\nfunction: my_func\n";
print $fh "input:\n type: string\noutput:\n type: string\n";
close $fh;
lives_ok(
sub { capture(sub { App::Test::Generator->generate($path) }) },
'unicode in schema file does not crash',
);
};
# ==================================================================
# Generator â render helpers with pathological inputs
# ==================================================================
subtest 'perl_quote: handles undef' => sub {
is(App::Test::Generator::perl_quote(undef), 'undef',
'undef -> literal undef string');
t/edge_cases.t view on Meta::CPAN
binmode $fh, ':utf8';
print $fh "package Unicode;\nuse utf8;\nsub greet { return 'héllo'; }\n1;\n";
close $fh;
lives_ok(
sub {
my $e = App::Test::Generator::SchemaExtractor->new(
input_file => $pm
);
$e->extract_all(no_write => 1);
},
'unicode source does not crash',
);
};
subtest 'SchemaExtractor: confidence_threshold at extremes' => sub {
my ($fh, $pm) = tempfile(SUFFIX => '.pm', UNLINK => 1);
print $fh "package Foo;\nsub bar { return 1; }\n1;\n";
close $fh;
for my $thresh (0.0, 0.5, 1.0) {
lives_ok(
( run in 1.470 second using v1.01-cache-2.11-cpan-d7f47b0818f )