EV-Kafka
view release on metacpan or search on metacpan
xt/fuzz_structured.t view on Meta::CPAN
my ($off, $kind, $width) = @$site;
substr($bytes, $off, $kind eq 'uvar' ? $width : length $value) = $value;
return $bytes;
}
my ($checked, $bad) = (0, 0);
sub run_parse {
my ($desc, $api, $ver, $bytes) = @_;
$checked++;
my $r = eval { EV::Kafka::_test_parse_response($api, $ver, $bytes) };
if ($@) { $bad++; diag "DIE $desc: $@"; return; }
if (defined $r && ref $r ne 'HASH') {
$bad++; diag "NON-HASH $desc: " . ref $r;
}
}
sub run_batch {
my ($desc, $bytes) = @_;
$checked++;
my $r = eval { EV::Kafka::_test_decode_batch($bytes) };
if ($@) { $bad++; diag "DIE $desc: $@"; return; }
if (defined $r && ref $r ne 'ARRAY') {
$bad++; diag "NON-ARRAY $desc: " . ref $r;
}
}
my @TPL = (
['metadata v1', 'metadata', 1, \&tpl_metadata_v1],
['metadata v9', 'metadata', 9, \&tpl_metadata_v9],
['produce v7', 'produce', 7, \&tpl_produce_v7],
['fetch v4', 'fetch', 4, \&tpl_fetch_v4],
['fetch v7', 'fetch', 7, \&tpl_fetch_v7],
['list_offsets v1', 'list_offsets', 1, \&tpl_list_offsets_v1],
);
# --- Templates must be valid, else the whole exercise is vacuous ---------
{
my ($bytes) = tpl_metadata_v1();
my $r = EV::Kafka::_test_parse_response('metadata', 1, $bytes);
ok((ref $r eq 'HASH') && @{$r->{brokers}} == 1 && @{$r->{topics}} == 1,
'template metadata v1 parses to expected structure');
}
{
my ($bytes) = tpl_metadata_v9();
my $r = EV::Kafka::_test_parse_response('metadata', 9, $bytes);
ok((ref $r eq 'HASH') && @{$r->{brokers}} == 1
&& $r->{brokers}[0]{host} eq 'host1',
'template metadata v9 parses to expected structure');
}
{
my ($bytes) = tpl_produce_v7();
my $r = EV::Kafka::_test_parse_response('produce', 7, $bytes);
ok((ref $r eq 'HASH')
&& $r->{topics}[0]{partitions}[0]{base_offset} == 42,
'template produce v7 parses to expected structure');
}
{
my ($bytes) = tpl_fetch_v4();
my $r = EV::Kafka::_test_parse_response('fetch', 4, $bytes);
my $recs = (ref $r eq 'HASH') ? $r->{topics}[0]{partitions}[0]{records} : undef;
ok((ref $recs eq 'ARRAY') && @$recs == 1 && $recs->[0]{key} eq 'k',
'template fetch v4 parses, record batch decoded');
}
{
my ($bytes) = tpl_fetch_v7();
my $r = EV::Kafka::_test_parse_response('fetch', 7, $bytes);
my $recs = (ref $r eq 'HASH') ? $r->{topics}[0]{partitions}[0]{records} : undef;
ok((ref $recs eq 'ARRAY') && @$recs == 1 && $recs->[0]{value} eq 'v',
'template fetch v7 parses, record batch decoded');
}
{
my ($bytes) = tpl_list_offsets_v1();
my $r = EV::Kafka::_test_parse_response('list_offsets', 1, $bytes);
ok((ref $r eq 'HASH')
&& $r->{topics}[0]{partitions}[0]{offset} == 7,
'template list_offsets v1 parses to expected structure');
}
{
my ($bytes) = tpl_batch();
my $r = EV::Kafka::_test_decode_batch($bytes);
ok((ref $r eq 'ARRAY') && @$r == 1 && $r->[0]{key} eq 'k',
'template record batch decodes');
}
# --- Phase 1: deterministic hostile-field matrix, response parsers --------
($checked, $bad) = (0, 0);
for my $tpl (@TPL) {
my ($name, $api, $ver, $build) = @$tpl;
my ($bytes, $sites) = $build->();
for my $site (@$sites) {
my ($off, $kind) = @$site;
my @values = $kind eq 'i32' ? hostile_i32(length $bytes, $off)
: $kind eq 'i16' ? hostile_i16(length $bytes, $off)
: @HOSTILE_UVAR;
for my $v (@values) {
run_parse("$name site\@$off", $api, $ver,
mutate_at($bytes, $site, $v));
}
}
}
ok(!$bad, "phase 1: $checked deterministic hostile-field response parses survived");
# --- Phase 2: deterministic hostile-field matrix, record-batch decoder ----
($checked, $bad) = (0, 0);
{
my ($bytes, $sites) = tpl_batch();
for my $site (@$sites) {
my ($off, $kind, $width, $crc, $fname) = @$site;
my @values = $kind eq 'i32' ? hostile_i32(length $bytes, $off)
: @HOSTILE_UVAR;
for my $v (@values) {
my $mut = mutate_at($bytes, $site, $v);
$mut = fix_crc($mut) if $crc;
run_batch("batch $fname\@$off", $mut);
}
}
}
ok(!$bad, "phase 2: $checked deterministic hostile-field batch decodes survived");
# --- Phase 3: random structured mutations, response envelopes -------------
($checked, $bad) = (0, 0);
srand 42;
for my $i (1 .. $iters) {
my $tpl = $TPL[int(rand(scalar @TPL))];
my ($name, $api, $ver, $build) = @$tpl;
my ($bytes, $sites) = $build->();
for (1 .. 1 + int(rand(3))) {
my $site = $sites->[int(rand(scalar @$sites))];
my ($off, $kind) = @$site;
( run in 1.452 second using v1.01-cache-2.11-cpan-3fabe0161c3 )