-
Notifications
You must be signed in to change notification settings - Fork 7
/
fuzz.ts
94 lines (80 loc) · 2.74 KB
/
fuzz.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import 'source-map-support/register';
import {execFile} from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import {Generations} from '@pkmn/data';
import {Dex} from '@pkmn/sim';
import {LE} from '../pkg/data';
import * as debug from '../tools/debug';
const ROOT = path.resolve(__dirname, '..', '..');
const usage = (msg?: string): void => {
if (msg) console.error(msg);
console.error('Usage: fuzz <pkmn|showdown> <GEN> <DURATION> <SEED?>');
process.exit(1);
};
export async function run(
gens: Generations,
gen: number | string,
showdown: boolean,
testing?: boolean,
duration?: string,
seed?: bigint
) {
const args = ['build', '--summary', 'failures', 'fuzz', '-Dlog', '-Dchance', '-Dcalc'];
if (showdown) args.push('-Dshowdown');
let input = undefined;
if (typeof duration !== 'undefined') {
args.push('--', gen.toString(), duration);
if (seed) args.push(seed.toString());
} else {
input = fs.readFileSync(gen);
}
try {
await new Promise<void>((resolve, reject) => {
const child = execFile('zig', args, {encoding: 'buffer'}, (error, stdout, stderr) => {
if (error) return reject({error, stdout, stderr});
resolve();
});
if (child.stdin && input) {
child.stdin.write(input);
child.stdin.end();
}
});
return true;
} catch (err: any) {
const {stdout, stderr} = err as {stdout: Buffer; stderr: Buffer};
const raw = stderr.toString('utf8');
const panic = raw.indexOf('panic: ');
if (testing || !stdout.length) throw new Error(raw);
console.error(raw);
const dir = path.join(ROOT, 'logs');
try {
fs.mkdirSync(dir, {recursive: true});
} catch (e: any) {
if (e.code !== 'EEXIST') throw e;
}
seed = LE ? stdout.readBigUInt64LE(0) : stdout.readBigUInt64BE(0);
const hex = `0x${seed.toString(16).toUpperCase()}`;
const file = path.join(dir, `${hex}.fuzz.html`);
const link = path.join(dir, 'fuzz.html');
fs.writeFileSync(file, debug.render(gens, stdout.subarray(8), raw.slice(panic), seed));
fs.rmSync(link, {force: true});
fs.symlinkSync(file, link);
return false;
}
}
if (require.main === module) {
(async () => {
if (process.argv.length < 4 || process.argv.length > 6) usage(process.argv.length.toString());
const mode = process.argv[2];
if (mode !== 'pkmn' && mode !== 'showdown') {
usage(`Mode must be either 'pkmn' or 'showdown', received '${mode}'`);
}
const gens = new Generations(Dex as any);
const seed = process.argv.length > 5 ? BigInt(process.argv[5]) : undefined;
await run(gens, process.argv[3], mode === 'showdown', false, process.argv[4], seed);
})().catch(err => {
console.error(err);
process.exit(1);
});
}