fix corner cases in test/reduce (#3709)
This commit is contained in:
60
test/mocha/reduce.js
Normal file
60
test/mocha/reduce.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
var assert = require("assert");
|
||||||
|
var exec = require("child_process").exec;
|
||||||
|
var reduce_test = require("../reduce");
|
||||||
|
|
||||||
|
describe("test/reduce.js", function() {
|
||||||
|
it("Should handle test cases with --toplevel", function() {
|
||||||
|
var result = reduce_test([
|
||||||
|
"var Infinity = 42;",
|
||||||
|
"console.log(Infinity);",
|
||||||
|
].join("\n"), {
|
||||||
|
toplevel: true,
|
||||||
|
});
|
||||||
|
if (result.error) throw result.error;
|
||||||
|
assert.strictEqual(result.code, [
|
||||||
|
"// Can't reproduce test failure with minify options provided:",
|
||||||
|
'// {"toplevel":true}',
|
||||||
|
"",
|
||||||
|
].join("\n"));
|
||||||
|
});
|
||||||
|
it("Should handle test result of NaN", function() {
|
||||||
|
var result = reduce_test("throw 0 / 0;");
|
||||||
|
if (result.error) throw result.error;
|
||||||
|
assert.strictEqual(result.code, [
|
||||||
|
"// Can't reproduce test failure with minify options provided:",
|
||||||
|
'// {"compress":{},"mangle":false}',
|
||||||
|
"",
|
||||||
|
].join("\n"));
|
||||||
|
});
|
||||||
|
it("Should print correct output for irreducible test case", function() {
|
||||||
|
var result = reduce_test([
|
||||||
|
"console.log(function f(a) {",
|
||||||
|
" return f.length;",
|
||||||
|
"}());",
|
||||||
|
].join("\n"), {
|
||||||
|
compress: {
|
||||||
|
keep_fargs: false,
|
||||||
|
},
|
||||||
|
mangle: false,
|
||||||
|
});
|
||||||
|
if (result.error) throw result.error;
|
||||||
|
assert.strictEqual(result.code, [
|
||||||
|
"console.log(function f(a) {",
|
||||||
|
" return f.length;",
|
||||||
|
"}());",
|
||||||
|
"// output: 1",
|
||||||
|
"// minify: 0",
|
||||||
|
'// options: {"compress":{"keep_fargs":false},"mangle":false}',
|
||||||
|
].join("\n"));
|
||||||
|
});
|
||||||
|
it("Should fail when invalid option is supplied", function() {
|
||||||
|
var result = reduce_test("", {
|
||||||
|
compress: {
|
||||||
|
unsafe_regex: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
var err = result.error;
|
||||||
|
assert.ok(err instanceof Error);
|
||||||
|
assert.strictEqual(err.stack.split(/\n/)[0], "DefaultsError: `unsafe_regex` is not a supported option");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
var U = require("./node");
|
var U = require("./node");
|
||||||
var List = U.List;
|
var List = U.List;
|
||||||
var run_code = require("./sandbox").run_code;
|
var sandbox = require("./sandbox");
|
||||||
|
|
||||||
// Reduce a ufuzz-style `console.log` based test case by iteratively replacing
|
// Reduce a ufuzz-style `console.log` based test case by iteratively replacing
|
||||||
// AST nodes with various permutations. Each AST_Statement in the tree is also
|
// AST nodes with various permutations. Each AST_Statement in the tree is also
|
||||||
@@ -19,16 +19,18 @@ var run_code = require("./sandbox").run_code;
|
|||||||
module.exports = function reduce_test(testcase, minify_options, reduce_options) {
|
module.exports = function reduce_test(testcase, minify_options, reduce_options) {
|
||||||
minify_options = minify_options || { compress: {}, mangle: false };
|
minify_options = minify_options || { compress: {}, mangle: false };
|
||||||
reduce_options = reduce_options || {};
|
reduce_options = reduce_options || {};
|
||||||
var timeout = 1500; // start with a low timeout
|
|
||||||
var max_iterations = reduce_options.max_iterations || 1000;
|
var max_iterations = reduce_options.max_iterations || 1000;
|
||||||
|
var max_timeout = reduce_options.max_timeout || 15000;
|
||||||
var verbose = reduce_options.verbose;
|
var verbose = reduce_options.verbose;
|
||||||
var minify_options_json = JSON.stringify(minify_options);
|
var minify_options_json = JSON.stringify(minify_options);
|
||||||
var reduced = false;
|
var timeout = 1000; // start with a low timeout
|
||||||
|
var differs;
|
||||||
|
|
||||||
if (testcase instanceof U.AST_Node) testcase = testcase.print_to_string();
|
if (testcase instanceof U.AST_Node) testcase = testcase.print_to_string();
|
||||||
|
|
||||||
// the initial timeout to assess the viability of the test case must be large
|
// the initial timeout to assess the viability of the test case must be large
|
||||||
if (producesDifferentResultWhenMinified(testcase, minify_options, 5000)) {
|
if (differs = producesDifferentResultWhenMinified(testcase, minify_options, max_timeout)) {
|
||||||
|
if (differs.error) return differs;
|
||||||
// Replace expressions with constants that will be parsed into
|
// Replace expressions with constants that will be parsed into
|
||||||
// AST_Nodes as required. Each AST_Node has its own permutation count,
|
// AST_Nodes as required. Each AST_Node has its own permutation count,
|
||||||
// so these replacements can't be shared.
|
// so these replacements can't be shared.
|
||||||
@@ -323,18 +325,17 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
console.error("*** Discarding permutation and continuing.");
|
console.error("*** Discarding permutation and continuing.");
|
||||||
}
|
}
|
||||||
if (code) {
|
if (code) {
|
||||||
var differs = producesDifferentResultWhenMinified(code, minify_options, timeout);
|
var diff = producesDifferentResultWhenMinified(code, minify_options, timeout);
|
||||||
if (differs) {
|
if (diff) {
|
||||||
if (differs.timed_out) {
|
if (diff.timed_out) {
|
||||||
// can't trust the validity of `code_ast` and `code` when timed out.
|
// can't trust the validity of `code_ast` and `code` when timed out.
|
||||||
// no harm done - just ignore latest change and continue iterating.
|
// no harm done - just ignore latest change and continue iterating.
|
||||||
if (timeout < 5000) timeout += 100;
|
if (timeout < max_timeout) timeout += 250;
|
||||||
} else {
|
} else {
|
||||||
// latest permutation is valid, so use it as the basis of new changes
|
// latest permutation is valid, so use it as the basis of new changes
|
||||||
testcase_ast = code_ast;
|
testcase_ast = code_ast;
|
||||||
testcase = code;
|
testcase = code;
|
||||||
var testcase_unminified_result = differs.unminified_result;
|
differs = diff;
|
||||||
var testcase_minified_result = differs.minified_result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -344,16 +345,15 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
console.error("// reduce test pass " + pass + ": " + testcase.length + " bytes");
|
console.error("// reduce test pass " + pass + ": " + testcase.length + " bytes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reduced = true;
|
testcase += "\n// output: " + differs.unminified_result
|
||||||
testcase += "\n// output: " + testcase_unminified_result
|
+ "\n// minify: " + differs.minified_result
|
||||||
+ "\n// minify: " + testcase_minified_result
|
|
||||||
+ "\n// options: " + minify_options_json;
|
+ "\n// options: " + minify_options_json;
|
||||||
} else {
|
} else {
|
||||||
// same stdout result produced when minified
|
// same stdout result produced when minified
|
||||||
testcase = "// Can't reproduce test failure with minify options provided:"
|
testcase = "// Can't reproduce test failure with minify options provided:"
|
||||||
+ "\n// " + minify_options_json;
|
+ "\n// " + minify_options_json;
|
||||||
}
|
}
|
||||||
var result = U.minify(testcase, {
|
var result = U.minify(testcase.replace(/\u001b\[\d+m/g, ""), {
|
||||||
compress: false,
|
compress: false,
|
||||||
mangle: false,
|
mangle: false,
|
||||||
output: {
|
output: {
|
||||||
@@ -362,21 +362,22 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
comments: true,
|
comments: true,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
result.reduced = reduced;
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
function producesDifferentResultWhenMinified(code, minify_options, timeout) {
|
function producesDifferentResultWhenMinified(code, minify_options, timeout) {
|
||||||
var toplevel = undefined;
|
var minified = U.minify(code, minify_options);
|
||||||
var unminified_result = run_code(code, toplevel, timeout);
|
if (minified.error) return minified;
|
||||||
|
var toplevel = minify_options.toplevel;
|
||||||
|
var unminified_result = sandbox.run_code(code, toplevel, timeout);
|
||||||
if (/timed out/i.test(unminified_result)) return false;
|
if (/timed out/i.test(unminified_result)) return false;
|
||||||
if (/^\s*$|Error/.test(unminified_result)) return false;
|
if (/^\s*$|Error/.test(unminified_result)) return false;
|
||||||
|
|
||||||
var minified_result = run_code(U.minify(code, minify_options).code, toplevel, timeout);
|
var minified_result = sandbox.run_code(minified.code, toplevel, timeout);
|
||||||
if (/timed out/i.test(minified_result)) return { timed_out: true };
|
if (/timed out/i.test(minified_result)) return { timed_out: true };
|
||||||
if (/^\s*$/.test(minified_result)) return false;
|
if (/^\s*$/.test(minified_result)) return false;
|
||||||
|
|
||||||
return unminified_result !== minified_result ? {
|
return !sandbox.same_stdout(unminified_result, minified_result) ? {
|
||||||
unminified_result: unminified_result,
|
unminified_result: unminified_result,
|
||||||
minified_result: minified_result,
|
minified_result: minified_result,
|
||||||
} : false;
|
} : false;
|
||||||
|
|||||||
@@ -77,8 +77,9 @@ function strip_func_ids(text) {
|
|||||||
|
|
||||||
exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) {
|
exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) {
|
||||||
if (typeof expected != typeof actual) return false;
|
if (typeof expected != typeof actual) return false;
|
||||||
if (typeof expected != "string") {
|
if (typeof expected == "object" && typeof expected.name == "string" && typeof expected.message == "string") {
|
||||||
if (expected.name != actual.name) return false;
|
if (expected.name !== actual.name) return false;
|
||||||
|
if (typeof actual.message != "string") return false;
|
||||||
expected = expected.message.slice(expected.message.lastIndexOf("\n") + 1);
|
expected = expected.message.slice(expected.message.lastIndexOf("\n") + 1);
|
||||||
actual = actual.message.slice(actual.message.lastIndexOf("\n") + 1);
|
actual = actual.message.slice(actual.message.lastIndexOf("\n") + 1);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user