refactor test suites to allow testing one implementation against another
This commit is contained in:
parent
4ef8d9ab5a
commit
7fc18f6fd7
|
@ -25,13 +25,10 @@ std::vector<std::string> split(const std::string &s, char delim) {
|
|||
int main() {
|
||||
const uint64_t idSize = 16;
|
||||
|
||||
uint64_t frameSizeLimit1 = 0, frameSizeLimit2 = 0;
|
||||
if (::getenv("FRAMESIZELIMIT1")) frameSizeLimit1 = std::stoull(::getenv("FRAMESIZELIMIT1"));
|
||||
if (::getenv("FRAMESIZELIMIT2")) frameSizeLimit2 = std::stoull(::getenv("FRAMESIZELIMIT2"));
|
||||
uint64_t frameSizeLimit = 0;
|
||||
if (::getenv("FRAMESIZELIMIT")) frameSizeLimit = std::stoull(::getenv("FRAMESIZELIMIT"));
|
||||
|
||||
// x1 is client, x2 is server
|
||||
Negentropy x1(idSize, frameSizeLimit1);
|
||||
Negentropy x2(idSize, frameSizeLimit2);
|
||||
Negentropy ne(idSize, frameSizeLimit);
|
||||
|
||||
std::string line;
|
||||
while (std::cin) {
|
||||
|
@ -39,65 +36,43 @@ int main() {
|
|||
if (!line.size()) continue;
|
||||
|
||||
auto items = split(line, ',');
|
||||
if (items.size() != 3) throw hoytech::error("too few items");
|
||||
|
||||
int mode = std::stoi(items[0]);
|
||||
uint64_t created = std::stoull(items[1]);
|
||||
auto id = hoytech::from_hex(items[2]);
|
||||
if (id.size() != idSize) throw hoytech::error("unexpected id size");
|
||||
if (items[0] == "item") {
|
||||
if (items.size() != 3) throw hoytech::error("wrong num of fields");
|
||||
uint64_t created = std::stoull(items[1]);
|
||||
auto id = hoytech::from_hex(items[2]);
|
||||
ne.addItem(created, id);
|
||||
} else if (items[0] == "seal") {
|
||||
ne.seal();
|
||||
} else if (items[0] == "initiate") {
|
||||
auto q = ne.initiate();
|
||||
if (frameSizeLimit && q.size() > frameSizeLimit) throw hoytech::error("frameSizeLimit exceeded");
|
||||
std::cout << "msg," << hoytech::to_hex(q) << std::endl;
|
||||
} else if (items[0] == "msg") {
|
||||
std::string q;
|
||||
if (items.size() >= 2) q = hoytech::from_hex(items[1]);
|
||||
|
||||
if (mode == 1) {
|
||||
x1.addItem(created, id);
|
||||
} else if (mode == 2) {
|
||||
x2.addItem(created, id);
|
||||
} else if (mode == 3) {
|
||||
x1.addItem(created, id);
|
||||
x2.addItem(created, id);
|
||||
if (ne.isInitiator) {
|
||||
std::vector<std::string> have, need;
|
||||
q = ne.reconcile(q, have, need);
|
||||
|
||||
for (auto &id : have) std::cout << "have," << hoytech::to_hex(id) << "\n";
|
||||
for (auto &id : need) std::cout << "need," << hoytech::to_hex(id) << "\n";
|
||||
|
||||
if (q.size() == 0) {
|
||||
std::cout << "done" << std::endl;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
q = ne.reconcile(q);
|
||||
}
|
||||
|
||||
if (frameSizeLimit && q.size() > frameSizeLimit) throw hoytech::error("frameSizeLimit exceeded");
|
||||
std::cout << "msg," << hoytech::to_hex(q) << std::endl;
|
||||
} else {
|
||||
throw hoytech::error("unexpected mode");
|
||||
throw hoytech::error("unknown cmd: ", items[0]);
|
||||
}
|
||||
}
|
||||
|
||||
x1.seal();
|
||||
x2.seal();
|
||||
|
||||
std::string q;
|
||||
uint64_t round = 0;
|
||||
uint64_t totalUp = 0;
|
||||
uint64_t totalDown = 0;
|
||||
|
||||
while (1) {
|
||||
// CLIENT -> SERVER
|
||||
|
||||
if (round == 0) {
|
||||
q = x1.initiate();
|
||||
} else {
|
||||
std::vector<std::string> have, need;
|
||||
q = x1.reconcile(q, have, need);
|
||||
|
||||
for (auto &id : have) std::cout << "xor,HAVE," << hoytech::to_hex(id) << "\n";
|
||||
for (auto &id : need) std::cout << "xor,NEED," << hoytech::to_hex(id) << "\n";
|
||||
}
|
||||
|
||||
if (q.size() == 0) break;
|
||||
|
||||
std::cerr << "[" << round << "] CLIENT -> SERVER: " << q.size() << " bytes" << std::endl;
|
||||
totalUp += q.size();
|
||||
if (frameSizeLimit1 && q.size() > frameSizeLimit1) throw hoytech::error("frameSizeLimit1 exceeded");
|
||||
|
||||
// SERVER -> CLIENT
|
||||
|
||||
q = x2.reconcile(q);
|
||||
|
||||
std::cerr << "[" << round << "] SERVER -> CLIENT: " << q.size() << " bytes" << std::endl;
|
||||
totalDown += q.size();
|
||||
if (frameSizeLimit2 && q.size() > frameSizeLimit2) throw hoytech::error("frameSizeLimit2 exceeded");
|
||||
|
||||
|
||||
round++;
|
||||
}
|
||||
|
||||
std::cerr << "UP: " << totalUp << " bytes, DOWN: " << totalDown << " bytes" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
182
test/fuzz.pl
182
test/fuzz.pl
|
@ -1,21 +1,25 @@
|
|||
#!/usr/bin/env perl
|
||||
|
||||
use strict;
|
||||
$|++;
|
||||
|
||||
use IPC::Open2;
|
||||
use Session::Token;
|
||||
|
||||
my $harnessType = shift || die "please provide harness type (cpp, js, etc)";
|
||||
die "usage: $0 <lang1> <lang2>" if @ARGV < 2;
|
||||
my $harnessCmd1 = harnessTypeToCmd(shift) || die "please provide harness type (cpp, js, etc)";
|
||||
my $harnessCmd2 = harnessTypeToCmd(shift) || die "please provide harness type (cpp, js, etc)";
|
||||
my $idSize = shift || 16;
|
||||
|
||||
sub harnessTypeToCmd {
|
||||
my $harnessType = shift;
|
||||
|
||||
my $harnessCmd;
|
||||
if ($harnessType eq 'cpp') {
|
||||
return './cpp/harness';
|
||||
} elsif ($harnessType eq 'js') {
|
||||
return 'node js/harness.js';
|
||||
}
|
||||
|
||||
if ($harnessType eq 'cpp') {
|
||||
$harnessCmd = './cpp/harness';
|
||||
} elsif ($harnessType eq 'js') {
|
||||
$harnessCmd = 'node js/harness.js';
|
||||
} else {
|
||||
die "unknown harness type: $harnessType";
|
||||
}
|
||||
|
||||
|
@ -24,8 +28,6 @@ srand($ENV{SEED} || 0);
|
|||
my $stgen = Session::Token->new(seed => "\x00" x 1024, alphabet => '0123456789abcdef', length => $idSize * 2);
|
||||
|
||||
|
||||
my $iters = $ENV{ITERS} // 1;
|
||||
|
||||
my $minRecs = $ENV{MIN_RECS} // 1;
|
||||
my $maxRecs = $ENV{MAX_RECS} // 10_000;
|
||||
die "MIN_RECS > MAX_RECS" if $minRecs > $maxRecs;
|
||||
|
@ -44,67 +46,115 @@ my $prob3 = $ENV{P3} // 98;
|
|||
}
|
||||
|
||||
|
||||
for (my $i = 0; $i < $iters; $i++) {
|
||||
my $ids1 = {};
|
||||
my $ids2 = {};
|
||||
my $ids1 = {};
|
||||
my $ids2 = {};
|
||||
|
||||
my $pid = open2(my $outfile, my $infile, $harnessCmd);
|
||||
my ($pid1, $pid2);
|
||||
my ($infile1, $infile2);
|
||||
my ($outfile1, $outfile2);
|
||||
|
||||
my $num = $minRecs + rnd($maxRecs - $minRecs);
|
||||
|
||||
for (1..$num) {
|
||||
my $mode;
|
||||
|
||||
my $modeRnd = rand();
|
||||
|
||||
if ($modeRnd < $prob1) {
|
||||
$mode = 1;
|
||||
} elsif ($modeRnd < $prob1 + $prob2) {
|
||||
$mode = 2;
|
||||
} else {
|
||||
$mode = 3;
|
||||
}
|
||||
|
||||
my $created = 1677970534 + rnd($num);
|
||||
my $id = $stgen->get;
|
||||
|
||||
$ids1->{$id} = 1 if $mode == 1 || $mode == 3;
|
||||
$ids2->{$id} = 1 if $mode == 2 || $mode == 3;
|
||||
|
||||
print $infile "$mode,$created,$id\n";
|
||||
}
|
||||
|
||||
close($infile);
|
||||
|
||||
while (<$outfile>) {
|
||||
if (/^xor,(HAVE|NEED),(\w+)/) {
|
||||
my ($action, $id) = ($1, $2);
|
||||
|
||||
if ($action eq 'NEED') {
|
||||
die "duplicate insert of $action,$id" if $ids1->{$id};
|
||||
$ids1->{$id} = 1;
|
||||
} elsif ($action eq 'HAVE') {
|
||||
die "duplicate insert of $action,$id" if $ids2->{$id};
|
||||
$ids2->{$id} = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
waitpid($pid, 0);
|
||||
my $child_exit_status = $?;
|
||||
die "failure running test harness" if $child_exit_status;
|
||||
|
||||
for my $id (keys %$ids1) {
|
||||
die "$id not in ids2" if !$ids2->{$id};
|
||||
}
|
||||
|
||||
for my $id (keys %$ids2) {
|
||||
die "$id not in ids1" if !$ids1->{$id};
|
||||
}
|
||||
|
||||
print "\n-----------OK-----------\n";
|
||||
{
|
||||
local $ENV{FRAMESIZELIMIT};
|
||||
$ENV{FRAMESIZELIMIT} = $ENV{FRAMESIZELIMIT1} if defined $ENV{FRAMESIZELIMIT1};
|
||||
$pid1 = open2($outfile1, $infile1, $harnessCmd1);
|
||||
}
|
||||
|
||||
{
|
||||
local $ENV{FRAMESIZELIMIT};
|
||||
$ENV{FRAMESIZELIMIT} = $ENV{FRAMESIZELIMIT2} if defined $ENV{FRAMESIZELIMIT2};
|
||||
$pid2 = open2($outfile2, $infile2, $harnessCmd2);
|
||||
}
|
||||
|
||||
my $num = $minRecs + rnd($maxRecs - $minRecs);
|
||||
|
||||
for (1..$num) {
|
||||
my $created = 1677970534 + rnd($num);
|
||||
my $id = $stgen->get;
|
||||
|
||||
my $modeRnd = rand();
|
||||
|
||||
if ($modeRnd < $prob1) {
|
||||
print $infile1 "item,$created,$id\n";
|
||||
$ids1->{$id} = 1;
|
||||
} elsif ($modeRnd < $prob1 + $prob2) {
|
||||
print $infile2 "item,$created,$id\n";
|
||||
$ids2->{$id} = 1;
|
||||
} else {
|
||||
print $infile1 "item,$created,$id\n";
|
||||
print $infile2 "item,$created,$id\n";
|
||||
$ids1->{$id} = 1;
|
||||
$ids2->{$id} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
print $infile1 "seal\n";
|
||||
print $infile2 "seal\n";
|
||||
|
||||
print $infile1 "initiate\n";
|
||||
|
||||
|
||||
my $round = 0;
|
||||
my $totalUp = 0;
|
||||
my $totalDown = 0;
|
||||
|
||||
while (1) {
|
||||
my $msg = <$outfile1>;
|
||||
|
||||
if ($msg =~ /^(have|need),(\w+)/) {
|
||||
my ($action, $id) = ($1, $2);
|
||||
|
||||
if ($action eq 'need') {
|
||||
die "duplicate insert of $action,$id" if $ids1->{$id};
|
||||
$ids1->{$id} = 1;
|
||||
} elsif ($action eq 'have') {
|
||||
die "duplicate insert of $action,$id" if $ids2->{$id};
|
||||
$ids2->{$id} = 1;
|
||||
}
|
||||
|
||||
next;
|
||||
} elsif ($msg =~ /^msg,(\w+)/) {
|
||||
my $data = $1;
|
||||
print $infile2 "msg,$data\n";
|
||||
|
||||
my $bytes = length($data) / 2;
|
||||
$totalUp += $bytes;
|
||||
print "[$round] CLIENT -> SERVER: $bytes bytes\n";
|
||||
} elsif ($msg =~ /^done/) {
|
||||
last;
|
||||
} else {
|
||||
die "unexpected line from 1: '$msg'";
|
||||
}
|
||||
|
||||
$msg = <$outfile2>;
|
||||
|
||||
if ($msg =~ /^msg,(\w*)/) {
|
||||
my $data = $1;
|
||||
print $infile1 "msg,$data\n";
|
||||
|
||||
my $bytes = length($data) / 2;
|
||||
$totalDown += $bytes;
|
||||
print "[$round] SERVER -> CLIENT: $bytes bytes\n";
|
||||
} else {
|
||||
die "unexpected line from 2: $msg";
|
||||
}
|
||||
|
||||
$round++;
|
||||
}
|
||||
|
||||
kill 'KILL', $pid1, $pid2;
|
||||
|
||||
for my $id (keys %$ids1) {
|
||||
die "$id not in ids2" if !$ids2->{$id};
|
||||
}
|
||||
|
||||
for my $id (keys %$ids2) {
|
||||
die "$id not in ids1" if !$ids1->{$id};
|
||||
}
|
||||
|
||||
print "UP: $totalUp bytes, DOWN: $totalDown bytes\n";
|
||||
|
||||
print "\n-----------OK-----------\n";
|
||||
|
||||
|
||||
sub rnd {
|
||||
my $n = shift;
|
||||
|
|
|
@ -3,66 +3,50 @@ const Negentropy = require('../../js/Negentropy.js');
|
|||
|
||||
const idSize = 16;
|
||||
|
||||
let frameSizeLimit = 0;
|
||||
if (process.env.FRAMESIZELIMIT) frameSizeLimit = parseInt(process.env.FRAMESIZELIMIT);
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
terminal: false
|
||||
});
|
||||
|
||||
let n = 0;
|
||||
let x1 = new Negentropy(idSize);
|
||||
let x2 = new Negentropy(idSize);
|
||||
let ne = new Negentropy(idSize, frameSizeLimit);
|
||||
|
||||
rl.on('line', (line) => {
|
||||
let items = line.split(',');
|
||||
if (items.length !== 3) throw Error("too few items");
|
||||
|
||||
let mode = parseInt(items[0]);
|
||||
let created = parseInt(items[1]);
|
||||
let id = items[2].trim();
|
||||
if (id.length !== idSize*2) throw Error(`id should be ${idSize} bytes`);
|
||||
|
||||
if (mode === 1) {
|
||||
x1.addItem(created, id);
|
||||
} else if (mode === 2) {
|
||||
x2.addItem(created, id);
|
||||
} else if (mode === 3) {
|
||||
x1.addItem(created, id);
|
||||
x2.addItem(created, id);
|
||||
} else {
|
||||
throw Error("unexpected mode");
|
||||
}
|
||||
|
||||
n++;
|
||||
});
|
||||
|
||||
rl.once('close', () => {
|
||||
x1.seal();
|
||||
x2.seal();
|
||||
|
||||
let q;
|
||||
let round = 0;
|
||||
|
||||
while (true) {
|
||||
if (round === 0) {
|
||||
q = x1.initiate();
|
||||
} else {
|
||||
let [newQ, haveIds, needIds] = x1.reconcile(q);
|
||||
q = newQ;
|
||||
|
||||
for (let id of haveIds) console.log(`xor,HAVE,${id}`);
|
||||
for (let id of needIds) console.log(`xor,NEED,${id}`);
|
||||
}
|
||||
|
||||
if (q.length === 0) break;
|
||||
|
||||
console.error(`[${round}] CLIENT -> SERVER: ${q.length / 2} bytes`);
|
||||
|
||||
let [newQ, haveIds, needIds] = x2.reconcile(q);
|
||||
if (items[0] == "item") {
|
||||
if (items.length !== 3) throw Error("too few items");
|
||||
let created = parseInt(items[1]);
|
||||
let id = items[2].trim();
|
||||
ne.addItem(created, id);
|
||||
} else if (items[0] == "seal") {
|
||||
ne.seal();
|
||||
} else if (items[0] == "initiate") {
|
||||
let q = ne.initiate();
|
||||
if (frameSizeLimit && q.length/2 > frameSizeLimit) throw Error("frameSizeLimit exceeded");
|
||||
console.log(`msg,${q}`);
|
||||
} else if (items[0] == "msg") {
|
||||
let q = items[1];
|
||||
let [newQ, haveIds, needIds] = ne.reconcile(q);
|
||||
q = newQ;
|
||||
if (frameSizeLimit && q.length/2 > frameSizeLimit) throw Error("frameSizeLimit exceeded");
|
||||
|
||||
console.error(`[${round}] SERVER -> CLIENT: ${q.length / 2} bytes`);
|
||||
for (let id of haveIds) console.log(`have,${id}`);
|
||||
for (let id of needIds) console.log(`need,${id}`);
|
||||
|
||||
round++;
|
||||
if (ne.isInitiator && q.length === 0) {
|
||||
console.log(`done`);
|
||||
} else {
|
||||
console.log(`msg,${q}`);
|
||||
}
|
||||
} else {
|
||||
throw Error(`unknown cmd: ${items[0]}`);
|
||||
}
|
||||
});
|
||||
|
||||
rl.on('close', () => {
|
||||
process.exit(0);
|
||||
});
|
||||
|
|
|
@ -12,16 +12,16 @@ foreach my $lang (split /,/, $langs) {
|
|||
note("------LANG $lang ------");
|
||||
|
||||
note("Full upload");
|
||||
run("RECS=100000 FRAMESIZELIMIT1=60000 FRAMESIZELIMIT2=500000 P1=1 P2=0 P3=0 perl fuzz.pl $lang");
|
||||
run("RECS=100000 FRAMESIZELIMIT1=60000 FRAMESIZELIMIT2=500000 P1=1 P2=0 P3=0 perl fuzz.pl $lang $lang");
|
||||
|
||||
note("Full download");
|
||||
run("RECS=100000 FRAMESIZELIMIT1=60000 FRAMESIZELIMIT2=500000 P1=0 P2=1 P3=0 perl fuzz.pl $lang");
|
||||
run("RECS=100000 FRAMESIZELIMIT1=60000 FRAMESIZELIMIT2=500000 P1=0 P2=1 P3=0 perl fuzz.pl $lang $lang");
|
||||
|
||||
note("Identical DBs");
|
||||
run("RECS=100000 FRAMESIZELIMIT1=60000 FRAMESIZELIMIT2=500000 P1=0 P2=0 P3=1 perl fuzz.pl $lang");
|
||||
run("RECS=100000 FRAMESIZELIMIT1=60000 FRAMESIZELIMIT2=500000 P1=0 P2=0 P3=1 perl fuzz.pl $lang $lang");
|
||||
|
||||
note("Mixed");
|
||||
run("RECS=100000 FRAMESIZELIMIT1=60000 FRAMESIZELIMIT2=500000 P1=1 P2=1 P3=5 perl fuzz.pl $lang");
|
||||
run("RECS=100000 FRAMESIZELIMIT1=60000 FRAMESIZELIMIT2=500000 P1=1 P2=1 P3=5 perl fuzz.pl $lang $lang");
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue