Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

View File

@ -0,0 +1,122 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Test helpers.
*/
'use strict';
const assert = require('assert');
const dircompare = require('dir-compare');
const path = require('path');
const fs = require('fs');
const corpus = require('../corpus.js');
const sourceHelpers = require('../source_helpers.js');
const BASE_DIR = path.join(path.dirname(__dirname), 'test_data');
const DB_DIR = path.join(BASE_DIR, 'fake_db');
const TEST_CORPUS = new sourceHelpers.BaseCorpus(BASE_DIR);
const V8_TEST_CORPUS = corpus.create(BASE_DIR, 'v8');
const FUZZILLI_TEST_CORPUS = corpus.create(
BASE_DIR, 'fuzzilli', false, V8_TEST_CORPUS);
const HEADER = `// Copyright 2025 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
`;
/**
* Create a function that returns one of `probs` when called. It rotates
* through the values. Useful to replace `random.random()` in tests using
* the probabilities that trigger different interesting cases.
*/
function cycleProbabilitiesFun(probs) {
let index = 0;
return () => {
index = index % probs.length;
return probs[index++];
};
}
/**
* Replace Math.random with a deterministic pseudo-random function.
*/
function deterministicRandom(sandbox) {
let seed = 1;
function random() {
const x = Math.sin(seed++) * 10000;
return x - Math.floor(x);
}
sandbox.stub(Math, 'random').callsFake(() => { return random(); });
}
function loadTestData(relPath) {
return sourceHelpers.loadSource(TEST_CORPUS, relPath);
}
function loadFuzzilliTestData(relPath) {
return sourceHelpers.loadSource(FUZZILLI_TEST_CORPUS, relPath);
}
function loadV8TestData(relPath) {
return sourceHelpers.loadSource(V8_TEST_CORPUS, relPath);
}
function assertExpectedPath(expectedPath, actualPath) {
const absPath = path.join(BASE_DIR, expectedPath);
if (process.env.GENERATE) {
if (fs.existsSync(absPath)) {
fs.rmSync(absPath, { recursive: true, force: true });
}
fs.cpSync(actualPath, absPath, {recursive: true});
}
const options = { compareSize: true };
const res = dircompare.compareSync(absPath, actualPath, options);
const message = (
`Equal: ${res.equal}, distinct: ${res.distinct}, ` +
`expected only: ${res.left}, actual only: ${res.right}, ` +
`differences: ${res.differences}`
);
assert.ok(res.same, message);
}
function assertExpectedResult(expectedPath, result) {
const absPath = path.join(BASE_DIR, expectedPath);
if (process.env.GENERATE) {
let header = HEADER;
if (fs.existsSync(absPath)) {
// Keep the old copyright header if the file already exists.
const previous = fs.readFileSync(absPath, 'utf-8').trim().split('\n');
previous.splice(3);
header = previous.join('\n') + '\n\n';
}
fs.writeFileSync(absPath, header + result.trim() + '\n');
return;
}
// Omit copyright header when comparing files.
const expected = fs.readFileSync(absPath, 'utf-8').trim().split('\n');
expected.splice(0, 4);
assert.strictEqual(expected.join('\n'), result.trim());
}
module.exports = {
BASE_DIR: BASE_DIR,
DB_DIR: DB_DIR,
FUZZILLI_TEST_CORPUS: FUZZILLI_TEST_CORPUS,
TEST_CORPUS: TEST_CORPUS,
assertExpectedPath: assertExpectedPath,
assertExpectedResult: assertExpectedResult,
cycleProbabilitiesFun: cycleProbabilitiesFun,
deterministicRandom: deterministicRandom,
loadFuzzilliTestData: loadFuzzilliTestData,
loadTestData: loadTestData,
loadV8TestData: loadV8TestData,
}

View File

@ -0,0 +1,67 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Tests for mutating variables
*/
'use strict';
const babelTraverse = require('@babel/traverse').default;
const sinon = require('sinon');
const common = require('../mutators/common.js');
const helpers = require('./helpers.js');
const sandbox = sinon.createSandbox();
describe('Available variables and functions', () => {
afterEach(() => {
sandbox.restore();
});
function testAvailableIdentifiers(input, expected) {
const source = helpers.loadTestData(input);
const result = new Array();
babelTraverse(source.ast, {
CallExpression(path) {
result.push({
variables: common.availableVariables(path),
functions: common.availableFunctions(path),
});
}
});
helpers.assertExpectedResult(
expected, JSON.stringify(result, null, 2));
}
it('test with functions and strict execution order', () => {
sandbox.stub(common, 'STRICT_EXECUTION_ORDER_PROB').value(1.0);
testAvailableIdentifiers(
'available_variables.js', 'available_variables_expected.js');
});
it('test with functions and sloppy execution order', () => {
sandbox.stub(common, 'STRICT_EXECUTION_ORDER_PROB').value(0.0);
testAvailableIdentifiers(
'available_variables.js',
'available_variables_sloppy_expected.js');
});
it('test with function expressions', () => {
sandbox.stub(common, 'STRICT_EXECUTION_ORDER_PROB').value(0.0);
testAvailableIdentifiers(
'available_variables_fun_exp.js',
'available_variables_fun_exp_expected.js');
});
it('test with arrow function expressions', () => {
sandbox.stub(common, 'STRICT_EXECUTION_ORDER_PROB').value(0.0);
testAvailableIdentifiers(
'available_variables_arrow_fun_exp.js',
'available_variables_arrow_fun_exp_expected.js');
});
});

View File

@ -0,0 +1,64 @@
// Copyright 2025 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Tests for analyzing the mutation context.
*/
'use strict';
const babelTraverse = require('@babel/traverse').default;
const babelTypes = require('@babel/types');
const common = require('../mutators/common.js');
const helpers = require('./helpers.js');
const sourceHelpers = require('../source_helpers.js');
const { analyzeContext } = require('../script_mutator.js');
const { IdentifierNormalizer } = require('../mutators/normalizer.js');
describe('Mutation context', () => {
it('determines loop variables', () => {
const source = helpers.loadTestData('loop_variables.js');
const normalizer = new IdentifierNormalizer();
normalizer.mutate(source);
const context = analyzeContext(source);
const code = sourceHelpers.generateCode(source, []);
helpers.assertExpectedResult(
'loop_variables_source_expected.js', code);
helpers.assertExpectedResult(
'loop_variables_expected.js',
JSON.stringify(Array.from(context.loopVariables), null, 2));
});
it('determines large for-loops', () => {
const source = helpers.loadTestData('large_loops.js');
const normalizer = new IdentifierNormalizer();
normalizer.mutate(source);
analyzeContext(source);
const large = [];
const small = [];
babelTraverse(source.ast, {
CallExpression(path) {
// Only list our console.log calls.
if (!babelTypes.isMemberExpression(path.node.callee)) {
return;
}
const text = path.node.arguments[0].value;
const forNode = path.findParent((p) => p.isForStatement()).node;
if(common.isLargeLoop(forNode)) {
large.push(text);
} else {
small.push(text);
}
}
});
helpers.assertExpectedResult(
'large_loops_expected.js',
JSON.stringify({large: large, small: small}, null, 2));
});
});

View File

@ -0,0 +1,169 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Corpus loading.
*/
'use strict';
const assert = require('assert');
const sinon = require('sinon');
const corpus = require('../corpus.js');
const exceptions = require('../exceptions.js');
const helpers = require('./helpers.js');
const sourceHelpers = require('../source_helpers.js');
const sandbox = sinon.createSandbox();
function testSoftSkipped(count, softSkipped, paths) {
sandbox.stub(exceptions, 'getSoftSkipped').callsFake(() => {
return softSkipped;
});
const mjsunit = corpus.create('test_data', 'mjsunit_softskipped');
const cases = mjsunit.getRandomTestcasePaths(count);
assert.deepEqual(paths, cases);
}
describe('Loading corpus', () => {
afterEach(() => {
sandbox.restore();
});
it('keeps all tests with no soft-skipped tests', () => {
sandbox.stub(Math, 'random').callsFake(() => 0.9);
testSoftSkipped(
3,
[],
['mjsunit_softskipped/permitted.js',
'mjsunit_softskipped/object-literal.js',
'mjsunit_softskipped/regress/binaryen-123.js']);
});
it('choose one test with no soft-skipped tests', () => {
sandbox.stub(Math, 'random').callsFake(() => 0.9);
testSoftSkipped(
1,
[],
['mjsunit_softskipped/permitted.js']);
});
it('keeps soft-skipped tests', () => {
sandbox.stub(Math, 'random').callsFake(() => 0.9);
testSoftSkipped(
1,
[/^binaryen.*\.js/, 'object-literal.js'],
['mjsunit_softskipped/permitted.js']);
});
it('keeps no generated soft-skipped tests', () => {
sandbox.stub(Math, 'random').callsFake(() => 0.9);
const softSkipped = [
// Correctly listed full relative path of test case.
'mjsunit_softskipped/regress/binaryen-123.js',
// Only basename doesn't match.
'object-literal.js',
// Only pieces of the path don't match.
'mjsunit_softskipped',
];
sandbox.stub(exceptions, 'getGeneratedSoftSkipped').callsFake(
() => { return new Set(softSkipped); });
testSoftSkipped(
2,
// None soft-skipped for basenames and regexps.
[],
// Only binaryen-123.js gets filtered out.
['mjsunit_softskipped/object-literal.js',
'mjsunit_softskipped/permitted.js']);
});
it('keeps soft-skipped tests by chance', () => {
sandbox.stub(Math, 'random').callsFake(() => 0);
testSoftSkipped(
3,
[/^binaryen.*\.js/, 'object-literal.js'],
['mjsunit_softskipped/object-literal.js',
'mjsunit_softskipped/regress/binaryen-123.js',
'mjsunit_softskipped/permitted.js']);
});
it('keeps soft-skipped paths', () => {
sandbox.stub(Math, 'random').callsFake(() => 0.9);
sandbox.stub(exceptions, 'getSoftSkippedPaths').callsFake(() => {
return [/not_/];
});
const crashtests = corpus.create('test_data', 'crashtests_softskipped');
const cases = crashtests.getRandomTestcasePaths(2);
assert.deepEqual(
['crashtests_softskipped/test.js',
'crashtests_softskipped/not_great.js'],
cases);
});
it('caches relative paths', () => {
sandbox.stub(Math, 'random').callsFake(() => 0);
sandbox.stub(exceptions, 'getSoftSkipped').callsFake(
() => { return ['object-literal.js']; });
const generatedSoftSkipped = [
'mjsunit_softskipped/regress/binaryen-123.js',
];
sandbox.stub(exceptions, 'getGeneratedSoftSkipped').callsFake(
() => { return new Set(generatedSoftSkipped); });
const mjsunit = corpus.create('test_data' , 'mjsunit_softskipped');
assert.deepEqual(
['mjsunit_softskipped/object-literal.js',
'mjsunit_softskipped/regress/binaryen-123.js'],
mjsunit.softSkippedFiles);
assert.deepEqual(
['mjsunit_softskipped/permitted.js'],
mjsunit.permittedFiles);
assert.deepEqual(
['mjsunit_softskipped/permitted.js',
'mjsunit_softskipped/object-literal.js',
'mjsunit_softskipped/regress/binaryen-123.js'],
Array.from(mjsunit.relFiles()));
});
it('drops discouraged files', () => {
// Test that files with dropped flags are also dropped. This
// test sets this probability to 1.
sandbox.stub(Math, 'random').callsFake(() => 0.9);
sandbox.stub(corpus, 'DROP_DISCOURAGED_FILES_PROB').value(1);
const v8 = corpus.create('test_data/flags/corpus', 'v8');
const cases = v8.getRandomTestcases(3).map(x => x.relPath);
assert.deepEqual(['v8/input3.js'], cases);
})
it('keeps discouraged files', () => {
// Test that files with dropped flags are sometimes kept. This
// test sets this probability to 0.
sandbox.stub(Math, 'random').callsFake(() => 0.9);
sandbox.stub(corpus, 'DROP_DISCOURAGED_FILES_PROB').value(0);
const v8 = corpus.create('test_data/flags/corpus', 'v8');
const cases = v8.getRandomTestcases(3).map(x => x.relPath);
assert.deepEqual(['v8/input2.js', 'v8/input1.js', 'v8/input3.js'], cases);
})
});
describe('Fuzzilli corpus', () => {
afterEach(() => {
sandbox.restore();
});
it('loads sources with all flags', () => {
const source = sourceHelpers.loadSource(
helpers.FUZZILLI_TEST_CORPUS,
'fuzzilli/fuzzdir-1/corpus/program_1.js');
const expectedFlags = [
'--expose-gc',
'--fuzzing',
'--fuzzilli-flag1',
'--random-seed=123',
'--fuzzilli-flag2'
];
assert.deepEqual(expectedFlags, source.flags);
});
});

View File

@ -0,0 +1,42 @@
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Test the script building the DB.
*/
'use strict';
const assert = require('assert');
const { execSync } = require("child_process");
const fs = require('fs');
const path = require('path');
const tempy = require('tempy');
const helpers = require('./helpers.js');
function buildDb(inputDir, corpusName, outputDir) {
execSync(
`node build_db.js -i ${inputDir} -o ${outputDir} ${corpusName}`,
{stdio: ['pipe']});
}
describe('DB tests', () => {
// Test feeds an expression that does not apply.
it('omits erroneous expressions', () => {
const outPath = tempy.directory();
buildDb('test_data/db', 'this', outPath);
const indexFile = path.join(outPath, 'index.json');
const indexJSON = JSON.parse(fs.readFileSync(indexFile), 'utf-8');
assert.deepEqual(
indexJSON, {"statements": [], "superStatements": [], "all": []});
});
// End to end test with various extracted and not extracted examples.
it('build db e2e', () => {
const outPath = tempy.directory();
buildDb('test_data/db', 'e2e', outPath);
helpers.assertExpectedPath('db/e2e_expected', outPath);
});
});

View File

@ -0,0 +1,256 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Tests for differential fuzzing.
*/
'use strict';
const assert = require('assert');
const path = require('path');
const program = require('commander');
const sinon = require('sinon');
const helpers = require('./helpers.js');
const scriptMutator = require('../script_mutator.js');
const sourceHelpers = require('../source_helpers.js');
const random = require('../random.js');
const runner = require('../runner.js');
const { DifferentialFuzzMutator, DifferentialFuzzSuppressions } = require(
'../mutators/differential_fuzz_mutator.js');
const { DifferentialScriptMutator, FuzzilliDifferentialScriptMutator } = require(
'../differential_script_mutator.js');
const sandbox = sinon.createSandbox();
function testMutators(settings, mutatorClass, inputFile, expectedFile) {
const source = helpers.loadTestData('differential_fuzz/' + inputFile);
const mutator = new mutatorClass(settings);
mutator.mutate(source);
const mutated = sourceHelpers.generateCode(source);
helpers.assertExpectedResult(
'differential_fuzz/' + expectedFile, mutated);
}
describe('Differential fuzzing', () => {
beforeEach(() => {
// Zero settings for all mutators.
this.settings = scriptMutator.defaultSettings();
for (const key of Object.keys(this.settings)) {
this.settings[key] = 0.0;
}
// By default, deterministically use all mutations of differential
// fuzzing.
this.settings['DIFF_FUZZ_EXTRA_PRINT'] = 1.0;
this.settings['DIFF_FUZZ_TRACK_CAUGHT'] = 1.0;
// Fake fuzzer being called with --input_dir flag.
this.oldInputDir = program.input_dir;
program.input_dir = helpers.BASE_DIR;
// Make remaining places deterministic that don't use global settings, e.g.
// try-catch skip behavior.
helpers.deterministicRandom(sandbox);
});
afterEach(() => {
sandbox.restore();
program.input_dir = this.oldInputDir;
});
it('applies suppressions', () => {
// This selects the first random variable when replacing .arguments.
sandbox.stub(random, 'single').callsFake(a => a[0]);
testMutators(
this.settings,
DifferentialFuzzSuppressions,
'suppressions.js',
'suppressions_expected.js');
});
it('adds extra printing', () => {
testMutators(
this.settings,
DifferentialFuzzMutator,
'mutations.js',
'mutations_expected.js');
});
it('does no extra printing', () => {
this.settings['DIFF_FUZZ_EXTRA_PRINT'] = 0.0;
testMutators(
this.settings,
DifferentialFuzzMutator,
'exceptions.js',
'exceptions_expected.js');
});
it('runs end to end', () => {
// Don't choose any zeroed settings or IGNORE_DEFAULT_PROB in try-catch
// mutator. Choose using original flags with >= 2%.
const chooseOrigFlagsProb = 0.2;
sandbox.stub(random, 'choose').callsFake((p) => p >= chooseOrigFlagsProb);
// Fake build directory from which two json configurations for flags are
// loaded.
const env = {
APP_DIR: 'test_data/differential_fuzz',
GENERATE: process.env.GENERATE,
};
sandbox.stub(process, 'env').value(env);
// Fake loading resources and instead load one fixed fake file for each.
sandbox.stub(sourceHelpers, 'loadResource').callsFake(() => {
return helpers.loadTestData('differential_fuzz/fake_resource.js');
});
// Load input files.
const files = [
'v8/differential_fuzz/input1.js',
'v8/differential_fuzz/input2.js',
];
const sources = files.map(helpers.loadV8TestData);
// Apply top-level fuzzing, with all probabilistic configs switched off.
this.settings['DIFF_FUZZ_EXTRA_PRINT'] = 0.0;
this.settings['DIFF_FUZZ_TRACK_CAUGHT'] = 0.0;
const mutator = new DifferentialScriptMutator(
this.settings, helpers.DB_DIR);
const mutated = mutator.mutateMultiple(sources);
helpers.assertExpectedResult(
'differential_fuzz/combined_expected.js', mutated.code);
// Flags for v8_foozzie.py are calculated from v8_fuzz_experiments.json and
// v8_fuzz_flags.json in test_data/differential_fuzz.
const expectedFlags = [
'--first-config=ignition',
'--second-config=ignition_turbo',
'--second-d8=d8',
'--second-config-extra-flags=--foo1',
'--second-config-extra-flags=--foo2',
'--first-config-extra-flags=--flag1',
'--second-config-extra-flags=--flag1',
'--first-config-extra-flags=--flag2',
'--second-config-extra-flags=--flag2',
'--first-config-extra-flags=--flag3',
'--second-config-extra-flags=--flag3',
'--first-config-extra-flags=--flag4',
'--second-config-extra-flags=--flag4'
];
assert.deepEqual(expectedFlags, mutated.flags);
});
it('runs end to end with fuzzilli inputs', () => {
// Similar to the test above but using a Fuzzilli input as well.
const chooseOrigFlagsProb = 0.2;
sandbox.stub(random, 'choose').callsFake((p) => p >= chooseOrigFlagsProb);
const env = {
APP_DIR: 'test_data/differential_fuzz',
GENERATE: process.env.GENERATE,
};
sandbox.stub(process, 'env').value(env);
sandbox.stub(sourceHelpers, 'loadResource').callsFake(() => {
return helpers.loadTestData('differential_fuzz/fake_resource.js');
});
const sources = [
helpers.loadV8TestData('v8/differential_fuzz/input2.js'),
helpers.loadFuzzilliTestData('fuzzilli/fuzzdir-1/corpus/program_1.js'),
];
this.settings['DIFF_FUZZ_EXTRA_PRINT'] = 0.0;
this.settings['DIFF_FUZZ_TRACK_CAUGHT'] = 0.0;
const mutator = new DifferentialScriptMutator(
this.settings, helpers.DB_DIR);
const mutated = mutator.mutateMultiple(sources);
helpers.assertExpectedResult(
'differential_fuzz/fuzzilli_combined_expected.js', mutated.code);
const expectedFlags = [
// Flags from v8_fuzz_experiments.json.
'--first-config=ignition',
'--second-config=ignition_turbo',
'--second-d8=d8',
// Flags from v8_fuzz_flags.json.
'--second-config-extra-flags=--foo1',
'--second-config-extra-flags=--foo2',
// Flags from the mjsunit input.
'--first-config-extra-flags=--flag4',
'--second-config-extra-flags=--flag4',
// Flags from the fuzzilli input.
'--first-config-extra-flags=--fuzzilli-flag1',
'--second-config-extra-flags=--fuzzilli-flag1',
'--first-config-extra-flags=--fuzzilli-flag2',
'--second-config-extra-flags=--fuzzilli-flag2',
];
assert.deepEqual(expectedFlags, mutated.flags);
});
it('chooses fuzzilli runner', () => {
sandbox.stub(random, 'randInt').callsFake(() => 2);
const env = {
APP_DIR: 'test_data/differential_fuzz',
GENERATE: process.env.GENERATE,
};
sandbox.stub(process, 'env').value(env);
const mutator = new DifferentialScriptMutator(
this.settings, helpers.DB_DIR);
assert.deepEqual(
runner.RandomCorpusRunnerWithFuzzilli, mutator.runnerClass);
});
});
// As above, but we don't zero the settings as we use zero settings in
// production here.
describe('Differential fuzzing with fuzzilli', () => {
beforeEach(() => {
// Fake fuzzer being called with --input_dir flag.
this.oldInputDir = program.input_dir;
program.input_dir = path.join(
helpers.BASE_DIR, 'differential_fuzz_fuzzilli');
helpers.deterministicRandom(sandbox);
});
afterEach(() => {
sandbox.restore();
program.input_dir = this.oldInputDir;
});
it('runs end to end', () => {
const env = {
APP_DIR: 'test_data/differential_fuzz',
GENERATE: process.env.GENERATE,
};
sandbox.stub(process, 'env').value(env);
sandbox.stub(sourceHelpers, 'loadResource').callsFake(() => {
return helpers.loadTestData('differential_fuzz/fake_resource.js');
});
const mutator = new FuzzilliDifferentialScriptMutator(
scriptMutator.defaultSettings(), helpers.DB_DIR);
// Configure 3 output tests and V8 engine.
const testRunner = new mutator.runnerClass(
program.input_dir, 'v8', 3);
for (const [i, inputs] of testRunner.enumerateInputs()) {
const mutated = mutator.mutateMultiple(inputs);
helpers.assertExpectedResult(
`differential_fuzz_fuzzilli/expected_code_${i}.js`, mutated.code);
helpers.assertExpectedResult(
`differential_fuzz_fuzzilli/expected_flags_${i}.js`,
JSON.stringify(mutated.flags, null, 2));
}
});
});

View File

@ -0,0 +1,113 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Tests for the differential-fuzzing library files.
*/
'use strict';
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const libPath = path.resolve(
path.join(__dirname, '..', 'resources', 'differential_fuzz_library.js'));
const code = fs.readFileSync(libPath, 'utf-8');
// We wire the print function to write to this result variable.
const resultDummy = 'let result; const print = text => { result = text; };';
// The prettyPrinted function from mjsunit is reused in the library.
const prettyPrint = 'let prettyPrinted = value => value;';
const hookedUpCode = resultDummy + prettyPrint + code;
// Runs the library, adds test code and verifies the result.
function testLibrary(testCode, expected) {
// The code isn't structured as a module. The test code is expected to
// evaluate to a result which we store in actual.
const actual = eval(hookedUpCode + testCode);
assert.deepEqual(expected, actual);
}
describe('Differential fuzzing library', () => {
it('prints objects', () => {
testLibrary(
'__prettyPrint([0, 1, 2, 3]); result;',
'[0, 1, 2, 3]');
testLibrary(
'__prettyPrint({0: 1, 2: 3}); result;',
'Object{0: 1, 2: 3}');
testLibrary(
'const o = {}; o.k = 42;__prettyPrint(o); result;',
'Object{k: 42}');
});
it('cuts off deep nesting', () => {
// We print only until a nesting depth of 4.
testLibrary(
'__prettyPrint({0: [1, 2, [3, {4: []}]]}); result;',
'Object{0: [1, 2, [3, Object{4: ...}]]}');
});
it('cuts off long strings', () => {
const long = new Array(66).join('a');
const head = new Array(55).join('a');
const tail = new Array(10).join('a');
testLibrary(
`__prettyPrint("${long}"); result;`,
`${head}[...]${tail}`);
// If the string gets longer, the cut-off version is still the same.
const veryLong = new Array(100).join('a');
testLibrary(
`__prettyPrint("${veryLong}"); result;`,
`${head}[...]${tail}`);
});
it('tracks hash difference', () => {
// Test that we track a hash value for each string we print.
const long = new Array(66).join('a');
testLibrary(
`__prettyPrint("${long}"); __hash;`,
2097980794);
// Test that the hash value differs, also when the cut-off result doesn't.
const veryLong = new Array(100).join('a');
testLibrary(
`__prettyPrint("${veryLong}"); __hash;`,
-428472866);
// Test that repeated calls update the hash.
testLibrary(
`__prettyPrint("${long}");__prettyPrint("${long}"); __hash;`,
-909224493);
});
it('limits extra printing', () => {
// Test that after exceeding the limit for calling extra printing, there
// is no new string printed (in the test case no new result added).
testLibrary(
'for (let i = 0; i < 20; i++) __prettyPrintExtra(i); result;',
'19');
testLibrary(
'for (let i = 0; i < 101; i++) __prettyPrintExtra(i); result;',
'99');
testLibrary(
'for (let i = 0; i < 102; i++) __prettyPrintExtra(i); result;',
'99');
});
it('tracks hash after limit', () => {
// Test that after exceeding the limit for calling extra printing, the
// hash is still updated.
testLibrary(
'for (let i = 0; i < 20; i++) __prettyPrintExtra(i); __hash;',
-945753644);
testLibrary(
'for (let i = 0; i < 101; i++) __prettyPrintExtra(i); __hash;',
1907055979);
testLibrary(
'for (let i = 0; i < 102; i++) __prettyPrintExtra(i); __hash;',
-590842070);
});
});

View File

@ -0,0 +1,43 @@
// Copyright 2025 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Test flag loading and merging.
*/
'use strict';
const assert = require('assert');
const sinon = require('sinon');
const helpers = require('./helpers.js');
const scriptMutator = require('../script_mutator.js');
const sandbox = sinon.createSandbox();
describe('Flag loading', () => {
beforeEach(() => {
helpers.deterministicRandom(sandbox);
// Zero settings for all mutators.
this.settings = scriptMutator.defaultSettings();
for (const key of Object.keys(this.settings)) {
this.settings[key] = 0.0;
}
});
afterEach(() => {
sandbox.restore();
});
it('drops blocked flags', () => {
const inputs = [
'flags/blocked_flags/input1.js',
'flags/blocked_flags/input2.js'];
const sources = inputs.map(input => helpers.loadV8TestData(input));
const mutator = new scriptMutator.ScriptMutator(this.settings, helpers.DB_DIR);
const mutated = mutator.mutateMultiple(sources);
assert.deepEqual(['--future', '--js-staging'], mutated.flags);
});
});

View File

@ -0,0 +1,101 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Test normalization.
*/
'use strict';
const path = require('path');
const sinon = require('sinon');
const corpus = require('../corpus.js');
const helpers = require('./helpers.js');
const sourceHelpers = require('../source_helpers.js');
const { ScriptMutator } = require('../script_mutator.js');
const sandbox = sinon.createSandbox();
function testLoad(testPath, expectedPath, loadFun=helpers.loadTestData) {
const mutator = new ScriptMutator({}, helpers.DB_DIR);
const source = loadFun(testPath);
const dependencies = mutator.resolveInputDependencies([source]);
const code = sourceHelpers.generateCode(source, dependencies);
helpers.assertExpectedResult(expectedPath, code);
}
describe('V8 dependencies', () => {
it('test', () => {
testLoad(
'mjsunit/test_load.js',
'mjsunit/test_load_expected.js');
});
it('does not loop indefinitely', () => {
testLoad(
'mjsunit/test_load_self.js',
'mjsunit/test_load_self_expected.js');
});
it('loads mjsunit dependencies also from another corpus', () => {
const corpusDir = path.join(helpers.BASE_DIR, 'load' ,'fuzzilli_scenario');
const v8Corpus = corpus.create(corpusDir, 'v8');
const fuzzilliCorpus = corpus.create(
corpusDir, 'fuzzilli', false, v8Corpus);
const load = (relPath) => {
return sourceHelpers.loadSource(fuzzilliCorpus, relPath);
};
testLoad(
'fuzzilli/fuzzdir-1/corpus/program_x.js',
'load/fuzzilli_scenario/test_load_expected.js',
load);
});
});
describe('Chakra dependencies', () => {
it('test', () => {
testLoad(
'chakra/load.js',
'chakra/load_expected.js');
});
});
describe('JSTest dependencies', () => {
afterEach(() => {
sandbox.restore();
});
it('test', () => {
const fakeStubs = sourceHelpers.loadSource(
helpers.TEST_CORPUS, 'JSTests/fake_stub.js');
sandbox.stub(sourceHelpers, 'loadResource').callsFake(() => fakeStubs);
testLoad('JSTests/load.js', 'JSTests/load_expected.js');
});
});
describe('SpiderMonkey dependencies', () => {
it('test', () => {
testLoad(
'spidermonkey/test/load.js',
'spidermonkey/test/load_expected.js');
});
});
describe('Sandbox dependencies', () => {
it('test', () => {
testLoad(
'sandbox/load.js',
'sandbox/load_expected.js');
});
});
describe('Language features', () => {
it('test', () => {
testLoad(
'language/features.js',
'language/features_expected.js');
});
});

View File

@ -0,0 +1,50 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Tests for mutating arrays
*/
'use strict';
const sinon = require('sinon');
const babylon = require('@babel/parser');
const common = require('../mutators/common.js');
const helpers = require('./helpers.js');
const scriptMutator = require('../script_mutator.js');
const sourceHelpers = require('../source_helpers.js');
const arrayMutator = require('../mutators/array_mutator.js');
const sandbox = sinon.createSandbox();
describe('Mutate arrays', () => {
afterEach(() => {
sandbox.restore();
});
it('performs all mutations', () => {
// Make random operations deterministic.
sandbox.stub(common, 'randomValue').callsFake(
() => babylon.parseExpression('""'));
helpers.deterministicRandom(sandbox);
// Ensure large arrays are not mutated. For testing make arrays > 3 large.
sandbox.stub(arrayMutator, 'MAX_ARRAY_LENGTH').value(3);
const source = helpers.loadTestData('mutate_arrays.js');
const settings = scriptMutator.defaultSettings();
settings['MUTATE_ARRAYS'] = 1.0;
const mutator = new arrayMutator.ArrayMutator(settings);
mutator.mutate(source);
const mutated = sourceHelpers.generateCode(source);
helpers.assertExpectedResult(
'mutate_arrays_expected.js', mutated);
});
});

View File

@ -0,0 +1,79 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Tests for mutating expressions
*/
'use strict';
const assert = require('assert');
const babelTypes = require('@babel/types');
const sinon = require('sinon');
const common = require('../mutators/common.js');
const expressionMutator = require('../mutators/expression_mutator.js');
const helpers = require('./helpers.js');
const scriptMutator = require('../script_mutator.js');
const sourceHelpers = require('../source_helpers.js');
const random = require('../random.js');
const sandbox = sinon.createSandbox();
function testCloneSiblings(expected_file) {
const source = helpers.loadTestData('mutate_expressions.js');
const settings = scriptMutator.defaultSettings();
settings['MUTATE_EXPRESSIONS'] = 1.0;
const mutator = new expressionMutator.ExpressionMutator(settings);
mutator.mutate(source);
const mutated = sourceHelpers.generateCode(source);
helpers.assertExpectedResult(expected_file, mutated);
}
describe('Mutate expressions', () => {
beforeEach(() => {
// Select the previous sibling.
sandbox.stub(random, 'randInt').callsFake((a, b) => b);
// This chooses cloning siblings.
sandbox.stub(random, 'random').callsFake(() => 0.8);
});
afterEach(() => {
sandbox.restore();
});
it('clones previous to current', () => {
// Keep the order of [previous, current], select previous.
sandbox.stub(random, 'single').callsFake(a => a[0]);
// Insert after. Keep returning true for the MUTATE_EXPRESSIONS check.
sandbox.stub(random, 'choose').callsFake(a => a === 1);
testCloneSiblings('mutate_expressions_previous_expected.js');
});
it('clones current to previous', () => {
// Switch the order of [previous, current], select current.
sandbox.stub(random, 'single').callsFake(a => a[3]);
// Insert before.
sandbox.stub(random, 'choose').callsFake(() => true);
testCloneSiblings('mutate_expressions_current_expected.js');
});
});
describe('Cloning', () => {
// Ensure that the source location we add are not cloned.
it('is not copying added state', () => {
const source = helpers.loadTestData('mutate_expressions.js');
common.setSourceLoc(source, 5, 10);
const noopNode = source.ast.program.body[0];
assert.equal(0.5, common.getSourceLoc(noopNode));
const cloned = babelTypes.cloneDeep(noopNode);
assert.equal(undefined, common.getSourceLoc(cloned));
});
});

View File

@ -0,0 +1,84 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Tests for mutating function calls.
*/
'use strict';
const sinon = require('sinon');
const helpers = require('./helpers.js');
const random = require('../random.js');
const scriptMutator = require('../script_mutator.js');
const sourceHelpers = require('../source_helpers.js');
const functionCallMutator = require('../mutators/function_call_mutator.js');
const sandbox = sinon.createSandbox();
function loadAndMutate(input_file) {
const source = helpers.loadTestData(input_file);
const settings = scriptMutator.defaultSettings();
settings['engine'] = 'v8';
settings['MUTATE_FUNCTION_CALLS'] = 1.0;
const mutator = new functionCallMutator.FunctionCallMutator(settings);
mutator.mutate(source);
return source;
}
describe('Mutate functions', () => {
afterEach(() => {
sandbox.restore();
});
it('is robust without available functions', () => {
sandbox.stub(random, 'random').callsFake(() => { return 0.2; });
// We just ensure here that mutating this file doesn't throw.
loadAndMutate('mutate_function_call.js');
});
it('optimizes functions with turbofan in V8', () => {
sandbox.stub(random, 'random').callsFake(() => { return 0.5; });
sandbox.stub(random, 'choose').callsFake(p => true);
const source = loadAndMutate('mutate_function_call.js');
const mutated = sourceHelpers.generateCode(source);
helpers.assertExpectedResult(
'mutate_function_call_expected.js', mutated);
});
it('optimizes functions with maglev in V8', () => {
sandbox.stub(random, 'random').callsFake(() => { return 0.5; });
// False-path takes 'Maglev'. Other calls to choose should return
// true. It's also used to determine if a mutator should be chosen.
sandbox.stub(random, 'choose').callsFake(p => p == 0.7 ? false : true);
const source = loadAndMutate('mutate_function_call.js');
const mutated = sourceHelpers.generateCode(source);
helpers.assertExpectedResult(
'mutate_function_call_maglev_expected.js', mutated);
});
it('compiles functions in V8 to baseline', () => {
sandbox.stub(random, 'random').callsFake(() => { return 0.7; });
const source = loadAndMutate('mutate_function_call.js');
const mutated = sourceHelpers.generateCode(source);
helpers.assertExpectedResult(
'mutate_function_call_baseline_expected.js', mutated);
});
it('deoptimizes functions in V8', () => {
sandbox.stub(random, 'random').callsFake(() => { return 0.8; });
const source = loadAndMutate('mutate_function_call.js');
const mutated = sourceHelpers.generateCode(source);
helpers.assertExpectedResult(
'mutate_function_call_deopt_expected.js', mutated);
});
});

View File

@ -0,0 +1,75 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Tests for mutating variables
*/
'use strict';
const babelTypes = require('@babel/types');
const sinon = require('sinon');
const arrayMutator = require('../mutators/array_mutator.js');
const common = require('../mutators/common.js');
const helpers = require('./helpers.js');
const scriptMutator = require('../script_mutator.js');
const sourceHelpers = require('../source_helpers.js');
const numberMutator = require('../mutators/number_mutator.js');
const random = require('../random.js');
const sandbox = sinon.createSandbox();
describe('Mutate numbers', () => {
beforeEach(() => {
sandbox.stub(common, 'nearbyRandomNumber').callsFake(
() => { return babelTypes.numericLiteral(-3) });
sandbox.stub(common, 'randomInterestingNumber').callsFake(
() => { return babelTypes.numericLiteral(-4) });
sandbox.stub(random, 'randInt').callsFake(() => { return -5 });
// Interesting cases from number mutator.
const interestingProbs = [0.009, 0.05, 0.5];
sandbox.stub(random, 'random').callsFake(
helpers.cycleProbabilitiesFun(interestingProbs));
});
afterEach(() => {
sandbox.restore();
});
it('test multiple mutations', () => {
// Ensure large arrays are not mutated. For testing make arrays > 2 large.
sandbox.stub(arrayMutator, 'MAX_ARRAY_LENGTH').value(2);
sandbox.stub(numberMutator, 'SKIP_LARGE_ARRAYS_PROB').value(1);
const source = helpers.loadTestData('mutate_numbers.js');
const settings = scriptMutator.defaultSettings();
settings['MUTATE_NUMBERS'] = 1.0;
const mutator = new numberMutator.NumberMutator(settings);
mutator.mutate(source);
const mutated = sourceHelpers.generateCode(source);
helpers.assertExpectedResult(
'mutate_numbers_expected.js', mutated);
});
it('test classes', () => {
// Ensure that numbers in getters, setters and class properties
// stay positive.
const source = helpers.loadTestData('mutate_numbers_class.js');
const settings = scriptMutator.defaultSettings();
settings['MUTATE_NUMBERS'] = 1.0;
const mutator = new numberMutator.NumberMutator(settings);
mutator.mutate(source);
const mutated = sourceHelpers.generateCode(source);
helpers.assertExpectedResult(
'mutate_numbers_class_expected.js', mutated);
});
});

View File

@ -0,0 +1,47 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Tests for mutating object expressions
*/
'use strict';
const sinon = require('sinon');
const babylon = require('@babel/parser');
const common = require('../mutators/common.js');
const helpers = require('./helpers.js');
const scriptMutator = require('../script_mutator.js');
const sourceHelpers = require('../source_helpers.js');
const {ObjectMutator} = require('../mutators/object_mutator.js');
const sandbox = sinon.createSandbox();
describe('Mutate objects', () => {
afterEach(() => {
sandbox.restore();
});
it('performs all mutations', () => {
// Make random operations deterministic.
sandbox.stub(common, 'randomValue').callsFake(
() => babylon.parseExpression('""'));
helpers.deterministicRandom(sandbox);
const source = helpers.loadTestData('mutate_objects.js');
const settings = scriptMutator.defaultSettings();
settings['MUTATE_OBJECTS'] = 1.0;
const mutator = new ObjectMutator(settings);
mutator.mutate(source);
const mutated = sourceHelpers.generateCode(source);
helpers.assertExpectedResult(
'mutate_objects_expected.js', mutated);
});
});

View File

@ -0,0 +1,78 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Test variable-or-object mutator.
*/
'use strict';
const babylon = require('@babel/parser');
const sinon = require('sinon');
const common = require('../mutators/common.js');
const helpers = require('./helpers.js');
const variableOrObject = require('../mutators/variable_or_object_mutation.js');
const random = require('../random.js');
const sourceHelpers = require('../source_helpers.js');
const sandbox = sinon.createSandbox();
function testMutations(testPath, expectedPath) {
const source = helpers.loadTestData(testPath);
const mutator = new variableOrObject.VariableOrObjectMutator(
{ ADD_VAR_OR_OBJ_MUTATIONS: 1.0 });
mutator.mutate(source);
const mutated = sourceHelpers.generateCode(source);
helpers.assertExpectedResult(expectedPath, mutated);
}
describe('Variable or object mutator', () => {
beforeEach(() => {
// Make before/after insertion deterministic. This also chooses
// random objects.
sandbox.stub(random, 'choose').callsFake(() => { return true; });
// This stubs out the random seed.
sandbox.stub(random, 'randInt').callsFake(() => { return 123; });
// Random value is itself dependent on too much randomization.
sandbox.stub(common, 'randomValue').callsFake(
() => { return babylon.parseExpression('0'); });
});
afterEach(() => {
sandbox.restore();
});
it('test', () => {
let index = 0;
// Test different cases of _randomVariableOrObjectMutations in
// variable_or_object_mutation.js.
const choices = [
0.2, // Trigger recursive case.
0.3, // Recursion 1: Delete.
0.4, // Recursion 2: Property access.
0.5, // Random assignment.
// 0.6 case for randomFunction omitted as it has too much randomization.
0.7, // Variable assignment.
0.8, // Object.defineProperty.
0.9, // Object.defineProperty recursive.
0.3, // Recursion 1: Delete.
0.4, // Recursion 2: Property access.
];
sandbox.stub(random, 'random').callsFake(
() => { return choices[index++]; });
// Choose __callGC on every other occasion.
let gcIndex = 0;
sandbox.stub(variableOrObject, 'chooseCallGC').callsFake(
() => { return gcIndex++ % 2 == 0; });
testMutations(
'mutate_var_or_obj.js',
'mutate_var_or_obj_expected.js');
});
});

View File

@ -0,0 +1,47 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Tests for mutating variables
*/
'use strict';
const babelTypes = require('@babel/types');
const sinon = require('sinon');
const common = require('../mutators/common.js');
const helpers = require('./helpers.js');
const scriptMutator = require('../script_mutator.js');
const sourceHelpers = require('../source_helpers.js');
const variableMutator = require('../mutators/variable_mutator.js');
const sandbox = sinon.createSandbox();
describe('Mutate variables', () => {
beforeEach(() => {
sandbox.stub(
common, 'randomVariable').callsFake(
() => { return babelTypes.identifier('REPLACED') });
});
afterEach(() => {
sandbox.restore();
});
it('test', () => {
const source = helpers.loadTestData('mutate_variables.js');
const settings = scriptMutator.defaultSettings();
settings['MUTATE_VARIABLES'] = 1.0;
const mutator = new variableMutator.VariableMutator(settings);
mutator.mutate(source);
const mutated = sourceHelpers.generateCode(source);
helpers.assertExpectedResult(
'mutate_variables_expected.js', mutated);
});
});

View File

@ -0,0 +1,56 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Test shuffling mutators and extra mutations.
*
* Use minimal probability settings to demonstrate order changes of top-level
* mutators. Which mutations are used exactly is not relevant to the test and
* handled pseudo-randomly.
*/
'use strict';
const sinon = require('sinon');
const helpers = require('./helpers.js');
const scriptMutator = require('../script_mutator.js');
const sourceHelpers = require('../source_helpers.js');
const random = require('../random.js');
const sandbox = sinon.createSandbox();
describe('Toplevel mutations', () => {
afterEach(() => {
sandbox.restore();
});
it('shuffle their order', () => {
// Make random operations deterministic.
helpers.deterministicRandom(sandbox);
this.settings = {
ADD_VAR_OR_OBJ_MUTATIONS: 0.0,
MUTATE_CROSSOVER_INSERT: 0.0,
MUTATE_EXPRESSIONS: 0.0,
MUTATE_FUNCTION_CALLS: 1.0,
MUTATE_NUMBERS: 1.0,
MUTATE_VARIABLES: 0.0,
SCRIPT_MUTATOR_SHUFFLE: 1.0,
SCRIPT_MUTATOR_EXTRA_MUTATIONS: 1.0,
engine: 'v8',
testing: true,
};
const source = helpers.loadTestData('mutation_order/input.js');
const mutator = new scriptMutator.ScriptMutator(this.settings, helpers.DB_DIR);
const mutated = mutator.mutateInputs([source]);
const code = sourceHelpers.generateCode(mutated);
// The test data should be rich enough to produce a pattern from the
// FunctionCallMutator that afterwards gets mutated by the NumberMutator.
helpers.assertExpectedResult(
'mutation_order/output_expected.js', code);
});
});

View File

@ -0,0 +1,83 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Test normalization.
*/
'use strict';
const { ClosureRemover } = require('../mutators/closure_remover.js');
const helpers = require('./helpers.js');
const normalizer = require('../mutators/normalizer.js');
const scriptMutator = require('../script_mutator.js');
const sourceHelpers = require('../source_helpers.js');
describe('Normalize', () => {
it('test basic', () => {
const source = helpers.loadTestData('normalize.js');
const mutator = new normalizer.IdentifierNormalizer();
mutator.mutate(source);
const normalized_0 = sourceHelpers.generateCode(source);
helpers.assertExpectedResult(
'normalize_expected_0.js', normalized_0);
mutator.mutate(source);
const normalized_1 = sourceHelpers.generateCode(source);
helpers.assertExpectedResult(
'normalize_expected_1.js', normalized_1);
});
it('test simple_test.js', () => {
const source = helpers.loadTestData('simple_test.js');
const mutator = new normalizer.IdentifierNormalizer();
mutator.mutate(source);
const normalized = sourceHelpers.generateCode(source);
helpers.assertExpectedResult(
'simple_test_expected.js', normalized);
});
it('test already normalized variables', () => {
const source = helpers.loadTestData('normalize_fuzz_test.js');
const mutator = new normalizer.IdentifierNormalizer();
mutator.mutate(source);
const normalized = sourceHelpers.generateCode(source);
helpers.assertExpectedResult(
'normalize_fuzz_test_expected.js', normalized);
});
it('test already normalized functions', () => {
const source = helpers.loadTestData('normalize_fuzz_test_functions.js');
const mutator = new normalizer.IdentifierNormalizer();
mutator.mutate(source);
const normalized = sourceHelpers.generateCode(source);
helpers.assertExpectedResult(
'normalize_fuzz_test_functions_expected.js', normalized);
});
it('test closure transformations', () => {
const source = helpers.loadTestData('closures.js');
const mutator = new normalizer.IdentifierNormalizer();
mutator.mutate(source);
// Ensure we activate closure transformations.
const settings = scriptMutator.defaultSettings();
settings['TRANSFORM_CLOSURES'] = 1.0;
const closures = new ClosureRemover(settings);
closures.mutate(source);
helpers.assertExpectedResult(
'closures_expected.js', sourceHelpers.generateCode(source));
});
});

View File

@ -0,0 +1,84 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Test random utilities.
*/
'use strict';
const assert = require('assert');
const sinon = require('sinon');
const helpers = require('./helpers.js');
const { sample, sampleOfTwo, twoBucketSample } = require('../random.js');
const sandbox = sinon.createSandbox();
describe('Random functions', () => {
afterEach(() => {
sandbox.restore();
});
it('twoBucketSample with one empty', () => {
sandbox.stub(Math, 'random').callsFake(() => 0.5);
assert.deepEqual([1, 2], twoBucketSample([0, 1, 2], [], 1, 2));
assert.deepEqual([1, 2], twoBucketSample([], [0, 1, 2], 1, 2));
assert.deepEqual([0], twoBucketSample([0], [], 1, 1));
assert.deepEqual([0], twoBucketSample([], [0], 1, 1));
});
it('twoBucketSample chooses with 0.3', () => {
sandbox.stub(Math, 'random').callsFake(() => 0.3);
assert.deepEqual([1, 2], twoBucketSample([0, 1, 2], [3, 4, 5], 1, 2));
// Higher factor.
assert.deepEqual([3, 5], twoBucketSample([0, 1, 2], [3, 4, 5], 4, 2));
});
it('twoBucketSample chooses with 0.7', () => {
sandbox.stub(Math, 'random').callsFake(() => 0.7);
assert.deepEqual([4, 3], twoBucketSample([0, 1, 2], [3, 4, 5], 1, 2));
});
it('twoBucketSample chooses with 0.5', () => {
sandbox.stub(Math, 'random').callsFake(() => 0.5);
assert.deepEqual([3], twoBucketSample([0, 1], [2, 3, 4, 5], 1, 1));
assert.deepEqual([3], twoBucketSample([0, 1, 2, 3], [4, 5], 1, 1));
// Higher factor.
assert.deepEqual([4], twoBucketSample([0, 1, 2, 3], [4, 5], 2, 1));
});
it('sampleOfTwo - low collision', () => {
sandbox.stub(Math, 'random').callsFake(() => 0);
assert.deepEqual([0, 1], sampleOfTwo([0, 1]));
assert.deepEqual([0, 1], sampleOfTwo([0, 1, 2, 3, 4, 5]));
});
it('sampleOfTwo - high collision', () => {
sandbox.stub(Math, 'random').callsFake(() => 0.99);
assert.deepEqual([1, 0], sampleOfTwo([0, 1]));
assert.deepEqual([5, 0], sampleOfTwo([0, 1, 2, 3, 4, 5]));
});
it('sampleOfTwo - no collision', () => {
const stub = sandbox.stub(Math, 'random');
stub.onCall(0).returns(0.7);
stub.onCall(1).returns(0.2);
stub.onCall(2).returns(0.7);
stub.onCall(3).returns(0.2);
assert.deepEqual([1, 0], sampleOfTwo([0, 1]));
assert.deepEqual([4, 2], sampleOfTwo([0, 1, 2, 3, 4, 5]));
});
it('sample - pseudo random', () => {
helpers.deterministicRandom(sandbox);
assert.deepEqual([1], sample([0, 1], 1));
assert.deepEqual([1, 0], sample([0, 1], 2));
assert.deepEqual([5, 4, 0], sample([0, 1, 2, 3, 4, 5], 3));
assert.deepEqual([1, 4, 2], sample([0, 1, 2, 3, 4, 5], 3));
});
sample
});

View File

@ -0,0 +1,485 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Regression tests.
*/
'use strict';
const assert = require('assert');
const { execSync } = require("child_process");
const fs = require('fs');
const path = require('path');
const sinon = require('sinon');
const tempfile = require('tempfile');
const tempy = require('tempy');
const baseMutator = require('../mutators/mutator.js');
const crossOver = require('../mutators/crossover_mutator.js');
const db = require('../db.js');
const exceptions = require('../exceptions.js');
const functionCallMutator = require('../mutators/function_call_mutator.js');
const helpers = require('./helpers.js');
const random = require('../random.js');
const scriptMutator = require('../script_mutator.js');
const sourceHelpers = require('../source_helpers.js');
const tryCatch = require('../mutators/try_catch.js');
const variableMutator = require('../mutators/variable_mutator.js');
const sandbox = sinon.createSandbox();
const SYNTAX_ERROR_RE = /.*SyntaxError.*/
function createFuzzTest(fake_db, settings, inputFiles) {
const sources = inputFiles.map(input => helpers.loadV8TestData(input));
const mutator = new scriptMutator.ScriptMutator(settings, fake_db);
const result = mutator.mutateMultiple(sources);
const output_file = tempfile('.js');
fs.writeFileSync(output_file, result.code);
return { file:output_file, flags:result.flags };
}
function execFile(jsFile) {
execSync("node " + jsFile, {stdio: ['pipe']});
}
describe('Regression tests', () => {
beforeEach(() => {
helpers.deterministicRandom(sandbox);
this.settings = {
ADD_VAR_OR_OBJ_MUTATIONS: 0.0,
MUTATE_CROSSOVER_INSERT: 0.0,
MUTATE_EXPRESSIONS: 0.0,
MUTATE_FUNCTION_CALLS: 0.0,
MUTATE_NUMBERS: 0.0,
MUTATE_VARIABLES: 0.0,
engine: 'v8',
testing: true,
}
});
afterEach(() => {
sandbox.restore();
});
it('combine strict and with', () => {
// Test that when a file with "use strict" is used in the inputs,
// the result is only strict if no other file contains anything
// prohibited in strict mode (here a with statement).
// It is assumed that such input files are marked as sloppy in the
// auto generated exceptions.
sandbox.stub(exceptions, 'getGeneratedSloppy').callsFake(
() => { return new Set(['regress/strict/input_with.js']); });
const {file, flags} = createFuzzTest(
'test_data/regress/empty_db',
this.settings,
['regress/strict/input_strict.js', 'regress/strict/input_with.js']);
execFile(file);
});
it('combine strict and with, life analysis', () => {
// As above, but without the sloppy file being marked. "Strict"
// incompatibility will also be detected at parse time.
const {file, flags} = createFuzzTest(
'test_data/regress/empty_db',
this.settings,
['regress/strict/input_strict.js', 'regress/strict/input_with.js']);
execFile(file);
});
it('combine strict and delete', () => {
// As above with unqualified delete.
sandbox.stub(exceptions, 'getGeneratedSloppy').callsFake(
() => { return new Set(['regress/strict/input_delete.js']); });
const {file, flags} = createFuzzTest(
'test_data/regress/empty_db',
this.settings,
['regress/strict/input_strict.js', 'regress/strict/input_delete.js']);
execFile(file);
});
it('combine strict and delete, life analysis', () => {
// As above, but without the sloppy file being marked. "Strict"
// incompatibility will also be detected at parse time.
const {file, flags} = createFuzzTest(
'test_data/regress/empty_db',
this.settings,
['regress/strict/input_strict.js', 'regress/strict/input_delete.js']);
execFile(file);
});
it('mutates negative value', () => {
// This tests that the combination of number, function call and expression
// mutator does't produce an update expression.
// Previously the 1 in -1 was replaced with another negative number leading
// to e.g. -/*comment/*-2. Then cloning the expression removed the
// comment and produced --2 in the end.
this.settings['MUTATE_NUMBERS'] = 1.0;
this.settings['MUTATE_FUNCTION_CALLS'] = 1.0;
this.settings['MUTATE_EXPRESSIONS'] = 1.0;
const {file, flags} = createFuzzTest(
'test_data/regress/empty_db',
this.settings,
['regress/numbers/input_negative.js']);
execFile(file);
});
it('mutates indices', () => {
// Test that indices are not replaced with a negative number causing a
// syntax error (e.g. {-1: ""}).
this.settings['MUTATE_NUMBERS'] = 1.0;
const {file, flags} = createFuzzTest(
'test_data/regress/empty_db',
this.settings,
['regress/numbers/input_indices.js']);
execFile(file);
});
it('does not collide the module builder', () => {
const {file, flags} = createFuzzTest(
'test_data/regress/empty_db',
this.settings,
['regress/dependency_collision/v8/test/mjsunit/wasm/input.js',
'regress/dependency_collision/chakra/input.js']);
execFile(file);
});
it('resolves flag contradictions', () => {
sandbox.stub(exceptions, 'CONTRADICTORY_FLAGS').value(
[['--flag1', '--flag2']])
const {file, flags} = createFuzzTest(
'test_data/regress/empty_db',
this.settings,
['v8/regress/contradictions/input1.js',
'v8/regress/contradictions/input2.js']);
assert.deepEqual(['--flag1'], flags);
});
it('skips mjs flags', () => {
const {file, flags} = createFuzzTest(
'test_data/regress/empty_db',
this.settings,
['v8/regress/mjs_flags/input.js']);
assert.deepEqual(['--ok-flag1', '--ok-flag2'], flags);
});
function testSuper(settings, db_path, expected) {
// Enforce mutations at every possible location.
settings['MUTATE_CROSSOVER_INSERT'] = 1.0;
// Choose the only-super-statments path. This also fixed the insertion
// order to only insert before a statement.
sandbox.stub(random, 'choose').callsFake(() => { return true; });
const fakeDb = new db.MutateDb(db_path);
const mutator = new crossOver.CrossOverMutator(settings, fakeDb);
// An input with a couple of insertion spots in constructors and
// methods of two classes. One root and one a subclass.
const source = helpers.loadTestData('regress/super/input.js');
mutator.mutate(source);
const mutated = sourceHelpers.generateCode(source);
helpers.assertExpectedResult(expected, mutated);
}
it('mutates super call', () => {
// Ensure that a super() call expression isn't added to a
// non-constructor class member or to a root class.
testSuper(
this.settings,
'test_data/regress/super/super_call_db',
'regress/super/call_expected.js');
});
it('mutates super member expression', () => {
// Ensure that a super.x member expression isn't added to a
// root class.
testSuper(
this.settings,
'test_data/regress/super/super_member_db',
'regress/super/member_expected.js');
});
it('does not cross-insert duplicate variables', () => {
// Ensure we don't declare a duplicate variable when the
// declaration is part of a cross-over inserted snippet.
this.settings['MUTATE_CROSSOVER_INSERT'] = 1.0;
const fakeDb = new db.MutateDb(
'test_data/regress/duplicates/duplicates_db');
const mutator = new crossOver.CrossOverMutator(this.settings, fakeDb);
const source = helpers.loadTestData('regress/duplicates/input.js');
mutator.mutate(source);
const mutated = sourceHelpers.generateCode(source);
helpers.assertExpectedResult(
'regress/duplicates/duplicates_expected.js', mutated);
});
function testAsyncReplacements(settings, expected) {
settings['MUTATE_FUNCTION_CALLS'] = 1.0;
sandbox.stub(sourceHelpers, 'loadResource').callsFake(() => {
return helpers.loadTestData('differential_fuzz/fake_resource.js');
});
// Go only into function-call replacements.
sandbox.stub(random, 'random').callsFake(() => { return 0.2; });
// Input with several async and non-async replacement sites.
const source = helpers.loadTestData('regress/async/input.js');
const mutator = new scriptMutator.ScriptMutator(
settings, 'test_data/regress/empty_db');
const mutated = mutator.mutateMultiple([source]).code;
helpers.assertExpectedResult(expected, mutated);
}
it('makes no cross-async replacements', () => {
// Test to show that when REPLACE_CROSS_ASYNC_PROB isn't chosen, there
// are no replacements that change the async property.
sandbox.stub(functionCallMutator, 'REPLACE_CROSS_ASYNC_PROB').value(0);
testAsyncReplacements(
this.settings, 'regress/async/no_async_expected.js');
});
it('makes full cross-async replacements', () => {
// Test to show that when REPLACE_CROSS_ASYNC_PROB is chosen, the
// replacement functions are from the pool of all functions (both
// maintaining and not maintaining the async property).
sandbox.stub(functionCallMutator, 'REPLACE_CROSS_ASYNC_PROB').value(1);
testAsyncReplacements(
this.settings, 'regress/async/full_async_expected.js');
});
it('does not drop required parentheses', () => {
sandbox.stub(sourceHelpers, 'loadResource').callsFake(() => {
return helpers.loadTestData('differential_fuzz/fake_resource.js');
});
const source = helpers.loadTestData('regress/parentheses/input.js');
const mutator = new scriptMutator.ScriptMutator(
this.settings, 'test_data/regress/empty_db');
const mutated = mutator.mutateMultiple([source]).code;
helpers.assertExpectedResult('regress/parentheses/expected.js', mutated);
});
it('loads async iterator resource', () => {
// Test that inputs with the `AsyncIterator` identifier load an additional
// stub.
sandbox.stub(sourceHelpers, 'loadResource').callsFake((resource) => {
if (resource === 'async_iterator.js') {
// Stub out resource loading, except for the resource in question.
return sourceHelpers.loadResource.wrappedMethod(resource);
}
return helpers.loadTestData('differential_fuzz/fake_resource.js');
});
const source = helpers.loadTestData('regress/iterator/input.js');
const mutator = new scriptMutator.ScriptMutator(
this.settings, 'test_data/regress/empty_db');
const mutated = mutator.mutateMultiple([source]).code;
helpers.assertExpectedResult('regress/iterator/expected.js', mutated);
});
it('does not crash on spurious identifiers', () => {
// Test that we don't erroneously load identifiers like `toString` from
// the map of additional resources.
const source = helpers.loadTestData('regress/resources/input.js');
const mutator = new scriptMutator.ScriptMutator(
this.settings, 'test_data/regress/empty_db');
const mutated = mutator.mutateMultiple([source]).code;
});
it('does not mutate loop variables', () => {
sandbox.stub(sourceHelpers, 'loadResource').callsFake(() => {
return helpers.loadTestData('differential_fuzz/fake_resource.js');
});
// Choose replacement in var-or-object mutation.
sandbox.stub(random, 'random').callsFake(() => 0.75);
// We usually skip loop variables with 0.95 - set it to 1.0
// for a more predictable test.
sandbox.stub(variableMutator, 'SKIP_LOOP_VAR_PROB').value(1.0);
// Try-catch is not helpful for reading the test output.
sandbox.stub(
tryCatch.AddTryCatchMutator.prototype, "mutate").returns(undefined);
// Maximum variable mutations.
this.settings['MUTATE_VARIABLES'] = 1.0;
this.settings['ADD_VAR_OR_OBJ_MUTATIONS'] = 1.0;
// Also stress closure removal (though it has no effect in this test).
this.settings['TRANSFORM_CLOSURES'] = 1.0;
const source = helpers.loadTestData('loop_mutations.js');
const mutator = new scriptMutator.ScriptMutator(
this.settings, 'test_data/regress/empty_db');
const mutated = mutator.mutateMultiple([source]).code;
helpers.assertExpectedResult('loop_mutations_expected.js', mutated);
});
it('does not block-wrap nested functions', () => {
sandbox.stub(sourceHelpers, 'loadResource').callsFake(() => {
return helpers.loadTestData('differential_fuzz/fake_resource.js');
});
// Maximum top-level wrapping and no skipping try-catch to
// deterministically try to wrap an entire function block.
sandbox.stub(tryCatch, 'DEFAULT_SKIP_PROB').value(0.0);
sandbox.stub(tryCatch, 'DEFAULT_TOPLEVEL_PROB').value(1.0);
sandbox.stub(tryCatch, 'IGNORE_DEFAULT_PROB').value(0.0);
const source = helpers.loadTestData('regress/legacy_scope/input.js');
const mutator = new scriptMutator.ScriptMutator(
this.settings, 'test_data/regress/empty_db');
const mutated = mutator.mutateMultiple([source]).code;
helpers.assertExpectedResult('regress/legacy_scope/expected.js', mutated);
});
it('does not try-catch wrap in infinite while loops', () => {
sandbox.stub(sourceHelpers, 'loadResource').callsFake(() => {
return helpers.loadTestData('differential_fuzz/fake_resource.js');
});
// No top-level wrapping and no skipping try-catch to
// deterministically try to wrap within the loop.
sandbox.stub(tryCatch, 'DEFAULT_SKIP_PROB').value(0.0);
sandbox.stub(tryCatch, 'DEFAULT_TOPLEVEL_PROB').value(0.0);
sandbox.stub(tryCatch, 'IGNORE_DEFAULT_PROB').value(0.0);
const source = helpers.loadTestData('regress/try_catch_while_true/input.js');
const mutator = new scriptMutator.ScriptMutator(
this.settings, 'test_data/regress/empty_db');
const mutated = mutator.mutateMultiple([source]).code;
helpers.assertExpectedResult('regress/try_catch_while_true/expected.js', mutated);
});
it('does not replace with diverging functions', () => {
sandbox.stub(sourceHelpers, 'loadResource').callsFake(() => {
return helpers.loadTestData('differential_fuzz/fake_resource.js');
});
// Go only into function-call replacements.
sandbox.stub(random, 'random').callsFake(() => { return 0.2; });
this.settings['MUTATE_FUNCTION_CALLS'] = 1.0;
const source = helpers.loadTestData('regress/infinite_loop_fun/input.js');
const mutator = new scriptMutator.ScriptMutator(
this.settings, 'test_data/regress/empty_db');
const mutated = mutator.mutateMultiple([source]).code;
helpers.assertExpectedResult('regress/infinite_loop_fun/expected.js', mutated);
});
it('mutates less in large loops', () => {
sandbox.stub(sourceHelpers, 'loadResource').callsFake(() => {
return helpers.loadTestData('differential_fuzz/fake_resource.js');
});
// Try-catch is not helpful for reading the test output.
sandbox.stub(
tryCatch.AddTryCatchMutator.prototype, "mutate").returns(undefined);
// Maximum var/obj and cross-over mutations.
this.settings['ADD_VAR_OR_OBJ_MUTATIONS'] = 1.0;
this.settings['MUTATE_CROSSOVER_INSERT'] = 1.0;
const fakeDb = new db.MutateDb(
'test_data/regress/duplicates/duplicates_db');
sandbox.stub(crossOver.CrossOverMutator.prototype, "db").returns(fakeDb);
// Test case with large loops.
const source = helpers.loadTestData('regress/large_loops/input.js');
const mutator = new scriptMutator.ScriptMutator(
this.settings, 'test_data/regress/empty_db');
const mutated = mutator.mutateMultiple([source]).code;
helpers.assertExpectedResult('regress/large_loops/expected.js', mutated);
});
// TODO(https://crbug.com/389069288): This still needs fixing, though it's
// probably rare that usage of 'arguments' and assigning to 'arguments'
// happen in the same test case. Due to the assignment, all occurences
// are normalized. Such an assignment is not allowed in strict mode and
// causes a syntax error in this case. We'd need to fix this in a way that
// unnormalized argument assignment is not mixed with strict-mode cases.
//
// It's analogue with eval and undefined, though the strict-mode rules are
// asymmetric:
//
// Not allowed in strict mode:
// let eval = 0;
// let arguments = 0;
// arguments = 0;
// undefined = 0;
// eval = 0
//
// Allowed in strict mode:
// let undefined = 0;
it('does not normalize arguments', () => {
sandbox.stub(sourceHelpers, 'loadResource').callsFake(() => {
return helpers.loadTestData('differential_fuzz/fake_resource.js');
});
// Try-catch is not helpful for reading the test output.
sandbox.stub(
tryCatch.AddTryCatchMutator.prototype, "mutate").returns(undefined);
const source = helpers.loadTestData('regress/arguments/input.js');
const mutator = new scriptMutator.ScriptMutator(
this.settings, 'test_data/regress/empty_db');
const mutated = mutator.mutateMultiple([source]).code;
helpers.assertExpectedResult('regress/arguments/expected.js', mutated);
});
it('does not try-catch wrap yield expressions', () => {
sandbox.stub(sourceHelpers, 'loadResource').callsFake(() => {
return helpers.loadTestData('differential_fuzz/fake_resource.js');
});
const source = helpers.loadTestData('regress/yield/input.js');
const mutator = new scriptMutator.ScriptMutator(
this.settings, 'test_data/regress/empty_db');
const mutated = mutator.mutateMultiple([source]).code;
helpers.assertExpectedResult('regress/yield/expected.js', mutated);
});
it('iterates snippets', () => {
const mutator = new scriptMutator.CrossScriptMutator(
this.settings, 'test_data/regress/empty_db');
const testRunner = new mutator.runnerClass();
testRunner.dbPath = 'test_data/regress/super/super_call_db';
for (const [i, inputs] of testRunner.enumerateInputs()) {
const mutated = mutator.mutateMultiple(inputs);
helpers.assertExpectedResult(
`verify_db/expected_code_${i}.js`, mutated.code);
}
});
});
describe('DB tests', () => {
afterEach(() => {
sandbox.restore();
});
it('creates DB with Object.assign', () => {
// Test that an Object.assign expression is inserted into the snippet DB.
const tmpOut = tempy.directory();
const mutateDb = new db.MutateDbWriter(tmpOut);
const source = helpers.loadTestData('regress/db/input/input.js');
mutateDb.process(source);
mutateDb.writeIndex();
const expressionFile = path.join(
tmpOut,
'CallExpression/113f18444843b9cbf6778f4ac8b6f7cf585b280b.json');
const content = fs.readFileSync(expressionFile, 'utf-8');
assert.deepEqual(
'{"type":"CallExpression","source":"Object.assign(VAR_0, VAR_1)",' +
'"isStatement":true,"originalPath":"regress/db/input/input.js",' +
'"dependencies":["VAR_0","VAR_1"],"needsSuper":false}', content);
});
});

View File

@ -0,0 +1,136 @@
// Copyright 2024 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Test the runner implementation.
*/
'use strict';
const assert = require('assert');
const path = require('path');
const sinon = require('sinon');
const helpers = require('./helpers.js');
const runner = require('../runner.js');
const sandbox = sinon.createSandbox();
describe('Load tests', () => {
afterEach(() => {
sandbox.restore();
});
it('from test archive', () => {
helpers.deterministicRandom(sandbox);
const archivePath = path.join(helpers.BASE_DIR, 'input_archive');
// Create 2 test cases with a maximum of 2 inputs per test.
const testRunner = new runner.RandomCorpusRunner(archivePath, 'v8', 2, 2);
var inputs = Array.from(testRunner.enumerateInputs());
// Check the enumeration counter separately.
assert.equal(2, inputs.length);
assert.deepEqual([0, 1], inputs.map((x) => x[0]));
assert.deepEqual(
["v8/test/mjsunit/wasm/regress-123.js",
"v8/test/mjsunit/wasm/regress-456.js"],
inputs[0][1].map((x) => x.relPath));
assert.deepEqual(
["v8/test/mjsunit/v8_test.js", "chakra/chakra_test2.js"],
inputs[1][1].map((x) => x.relPath));
});
it('from test corpus', () => {
// Also need to fix random here as each corpus shuffles their test cases
// once.
sandbox.stub(Math, 'random').callsFake(() => 0.5);
const archivePath = path.join(helpers.BASE_DIR, 'input_archive');
// Select all tests from the chakra archive (has 2 in total). Also exercise
// the extra strict parsing logic, which parses all files twice.
const testRunner = new runner.SingleCorpusRunner(
archivePath, 'chakra', true);
var inputs = Array.from(testRunner.enumerateInputs());
assert.equal(2, inputs.length);
assert.deepEqual([0, 1], inputs.map((x) => x[0]));
assert.deepEqual(
["chakra/chakra_test2.js"],
inputs[0][1].map((x) => x.relPath));
assert.deepEqual(
["chakra/chakra_test1.js"],
inputs[1][1].map((x) => x.relPath));
});
it('includes fuzzilli', () => {
sandbox.stub(Math, 'random').callsFake(() => 0.2);
const archivePath = path.join(helpers.BASE_DIR, 'input_archive');
// Create 2 test cases with a maximum of 2 inputs per test.
const testRunner = new runner.RandomCorpusRunnerWithFuzzilli(
archivePath, 'v8', 2, 2);
var inputs = Array.from(testRunner.enumerateInputs());
// Check the enumeration counter separately.
assert.equal(2, inputs.length);
assert.deepEqual([0, 1], inputs.map((x) => x[0]));
assert.deepEqual(
["fuzzilli/fuzzdir-1/corpus/program_1.js",
"fuzzilli/fuzzdir-2/crashes/program_3.js"],
inputs[0][1].map((x) => x.relPath));
assert.deepEqual(
["fuzzilli/fuzzdir-2/crashes/program_2.js",
"fuzzilli/fuzzdir-1/corpus/program_1.js"],
inputs[1][1].map((x) => x.relPath));
});
it('does not pick crashes with fuzzilli runner', () => {
sandbox.stub(Math, 'random').callsFake(() => 0.2);
const archivePath = path.join(helpers.BASE_DIR, 'input_archive');
// Try to get 2 test cases, but there's only 1 viable in the test data.
const testRunner = new runner.RandomFuzzilliNoCrashCorpusRunner(
archivePath, 'v8', 2);
var inputs = Array.from(testRunner.enumerateInputs());
assert.equal(1, inputs.length);
assert.deepEqual(
["fuzzilli/fuzzdir-1/corpus/program_1.js"],
inputs[0][1].map((x) => x.relPath));
});
it('only picks Wasm cases', () => {
helpers.deterministicRandom(sandbox);
const archivePath = path.join(helpers.BASE_DIR, 'input_archive');
// Create 4 test cases with a maximum of 2 inputs per test.
const testRunner = new runner.RandomWasmCorpusRunner(
archivePath, 'v8', 4, 2);
var inputs = Array.from(testRunner.enumerateInputs());
// Check the enumeration counter separately.
assert.equal(4, inputs.length);
assert.deepEqual([0, 1, 2, 3], inputs.map((x) => x[0]));
assert.deepEqual(
["v8/test/mjsunit/wasm/regress-123.js",
"fuzzilli/fuzzdir-2/crashes/program_3.js"],
inputs[0][1].map((x) => x.relPath));
assert.deepEqual(
["v8/test/mjsunit/wasm/regress-456.js",
"fuzzilli/fuzzdir-2/crashes/program_3.js"],
inputs[1][1].map((x) => x.relPath));
assert.deepEqual(
["fuzzilli/fuzzdir-2/crashes/program_3.js"],
inputs[2][1].map((x) => x.relPath));
assert.deepEqual(
["fuzzilli/fuzzdir-2/crashes/program_3.js",
"v8/test/mjsunit/wasm/regress-456.js"],
inputs[3][1].map((x) => x.relPath));
});
});

View File

@ -0,0 +1,117 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Test normalization.
*/
'use strict';
const sinon = require('sinon');
const common = require('../mutators/common.js');
const helpers = require('./helpers.js');
const random = require('../random.js');
const scriptMutator = require('../script_mutator.js');
const sourceHelpers = require('../source_helpers.js');
const tryCatch = require('../mutators/try_catch.js');
const sandbox = sinon.createSandbox();
function loadSource() {
return helpers.loadTestData('try_catch.js');
}
function testTryCatch(source, expected) {
const mutator = new tryCatch.AddTryCatchMutator();
mutator.mutate(source);
const mutated = sourceHelpers.generateCode(source);
helpers.assertExpectedResult(expected, mutated);
}
describe('Try catch', () => {
afterEach(() => {
sandbox.restore();
});
// Wrap on exit, hence wrap everything nested.
it('wraps all', () => {
sandbox.stub(random, 'choose').callsFake(() => { return false; });
sandbox.stub(random, 'random').callsFake(() => { return 0.7; });
testTryCatch(loadSource(), 'try_catch_expected.js');
});
// Wrap on enter and skip.
it('wraps toplevel', () => {
sandbox.stub(random, 'choose').callsFake(() => { return false; });
sandbox.stub(random, 'random').callsFake(() => { return 0.04; });
const source = loadSource();
// Fake source fraction 0.1 (i.e. the second of 10 files).
// Probability for toplevel try-catch is 0.05.
common.setSourceLoc(source, 1, 10);
testTryCatch(source, 'try_catch_toplevel_expected.js');
});
// Choose the rare case of skipping try-catch.
it('wraps nothing', () => {
sandbox.stub(random, 'choose').callsFake(() => { return false; });
sandbox.stub(random, 'random').callsFake(() => { return 0.01; });
const source = loadSource();
// Fake source fraction 0.2 (i.e. the third of 10 files).
// Probability for skipping is 0.02.
common.setSourceLoc(source, 2, 10);
testTryCatch(source, 'try_catch_nothing_expected.js');
});
// Choose to alter the target probability to 0.9 resulting in skipping
// all try-catch.
it('wraps nothing with high target probability', () => {
sandbox.stub(random, 'choose').callsFake(() => { return true; });
sandbox.stub(random, 'uniform').callsFake(() => { return 0.9; });
sandbox.stub(random, 'random').callsFake(() => { return 0.8; });
const source = loadSource();
// Fake source fraction 0.9 (i.e. the last of 10 files).
// Probability for skipping is 0.81 (0.9 * 0.9).
common.setSourceLoc(source, 9, 10);
testTryCatch(source, 'try_catch_alternate_expected.js');
});
// General distribution with pseudo-random execution.
it('distributes', () => {
helpers.deterministicRandom(sandbox);
sandbox.stub(sourceHelpers, 'loadResource').callsFake(() => {
return helpers.loadTestData('differential_fuzz/fake_resource.js');
});
// Zero settings for all mutators (try-catch always runs).
const settings = scriptMutator.defaultSettings();
for (const key of Object.keys(settings)) {
settings[key] = 0.0;
}
const inputs = [
'try_catch_distribution.js',
'try_catch_distribution.js',
'try_catch_distribution.js',
'try_catch_distribution.js',
'try_catch_distribution.js',
'try_catch_distribution.js',
];
const sources = inputs.map(input => helpers.loadTestData(input));
const mutator = new scriptMutator.ScriptMutator(settings, helpers.DB_DIR);
const mutated = mutator.mutateMultiple(sources);
helpers.assertExpectedResult(
'try_catch_distribution_expected.js', mutated.code);
});
});