Skip to content

Commit

Permalink
improve --reduce-test (#3742)
Browse files Browse the repository at this point in the history
- ignore difference in error messages
- improve readability on trailing whitespace differences
- improve performance & quality via `console.log()` insertions
  • Loading branch information
alexlamsl authored Mar 20, 2020
1 parent 0a1c9b3 commit ff72eaa
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 51 deletions.
5 changes: 2 additions & 3 deletions test/input/reduce/label.reduced.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// (beautified)
var o = this;

for (var k in o) {
0;
}
for (var k in o) {}

var a;

Expand Down
1 change: 1 addition & 0 deletions test/input/reduce/setter.reduced.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// (beautified)
console.log(function f(a) {
({
set p(v) {
Expand Down
1 change: 1 addition & 0 deletions test/input/reduce/unsafe_math.reduced.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// (beautified)
var b = 0;

var expr2 = (0 - 1 - .1 - .1).toString();
Expand Down
69 changes: 50 additions & 19 deletions test/mocha/reduce.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ describe("test/reduce.js", function() {
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"// Can't reproduce test failure with minify options provided:",
"// {",
"// Can't reproduce test failure",
"// minify options: {",
'// "toplevel": true',
"// }",
].join("\n"));
Expand All @@ -70,8 +70,8 @@ describe("test/reduce.js", function() {
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"// Can't reproduce test failure with minify options provided:",
"// {",
"// Can't reproduce test failure",
"// minify options: {",
'// "compress": {',
'// "toplevel": true',
"// }",
Expand All @@ -89,8 +89,8 @@ describe("test/reduce.js", function() {
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"// Can't reproduce test failure with minify options provided:",
"// {",
"// Can't reproduce test failure",
"// minify options: {",
'// "mangle": {',
'// "toplevel": true',
"// }",
Expand All @@ -101,11 +101,8 @@ describe("test/reduce.js", 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',
"// }",
"// Can't reproduce test failure",
"// minify options: {}",
].join("\n"));
});
it("Should print correct output for irreducible test case", function() {
Expand All @@ -121,6 +118,7 @@ describe("test/reduce.js", function() {
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"// (beautified)",
"console.log(function f(a) {",
" return f.length;",
"}());",
Expand Down Expand Up @@ -169,6 +167,7 @@ describe("test/reduce.js", function() {
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
"// (beautified)",
code,
"// output: 0.8",
"// 1.6",
Expand All @@ -189,11 +188,41 @@ describe("test/reduce.js", function() {
it("Should reduce infinite loops with reasonable performance", function() {
if (semver.satisfies(process.version, "0.10")) return;
this.timeout(120000);
var result = reduce_test("while (/9/.test(1 - .8));", {
compress: {
unsafe_math: true,
},
mangle: false,
});
if (result.error) throw result.error;
assert.strictEqual(result.code.replace(/ timed out after [0-9]+ms/, " timed out."), [
"// (beautified)",
"while (/9/.test(1 - .8)) {}",
"// output: Error: Script execution timed out.",
"// minify: ",
"// options: {",
'// "compress": {',
'// "unsafe_math": true',
"// },",
'// "mangle": false',
"// }",
].join("\n"));
});
it("Should ignore difference in Error.message", function() {
var result = reduce_test("null[function() {\n}];");
if (result.error) throw result.error;
assert.strictEqual(result.code, (semver.satisfies(process.version, "0.10") ? [
"// Can't reproduce test failure",
"// minify options: {}",
] : [
"// No differences except in error message",
"// minify options: {}",
]).join("\n"));
});
it("Should report trailing whitespace difference in stringified format", function() {
var code = [
"var a = 9007199254740992, b = 1;",
"",
"while (a++ + (1 - b) < a) {",
" 0;",
"for (var a in (1 - .8).toString()) {",
" console.log();",
"}",
].join("\n");
var result = reduce_test(code, {
Expand All @@ -203,14 +232,16 @@ describe("test/reduce.js", function() {
mangle: false,
});
if (result.error) throw result.error;
assert.strictEqual(result.code.replace(/ timed out after [0-9]+ms/, " timed out."), [
assert.strictEqual(result.code, [
"// (beautified)",
code,
"// output: ",
"// minify: Error: Script execution timed out.",
"// (stringified)",
'// output: "\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n"',
'// minify: "\\n\\n\\n"',
"// options: {",
'// "compress": {',
'// "unsafe_math": true',
"// },",
'// },',
'// "mangle": false',
"// }",
].join("\n"));
Expand Down
116 changes: 87 additions & 29 deletions test/reduce.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var sandbox = require("./sandbox");

module.exports = function reduce_test(testcase, minify_options, reduce_options) {
if (testcase instanceof U.AST_Node) testcase = testcase.print_to_string();
minify_options = minify_options || { compress: {}, mangle: false };
minify_options = minify_options || {};
reduce_options = reduce_options || {};
var max_iterations = reduce_options.max_iterations || 1000;
var max_timeout = reduce_options.max_timeout || 10000;
Expand All @@ -36,16 +36,29 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
if (!differs) {
// same stdout result produced when minified
return {
code: "// Can't reproduce test failure with minify options provided:"
+ "\n// " + to_comment(minify_options_json)
code: [
"// Can't reproduce test failure",
"// minify options: " + to_comment(minify_options_json)
].join("\n")
};
} else if (differs.timed_out) {
return {
code: "// Can't reproduce test failure within " + max_timeout + "ms:"
+ "\n// " + to_comment(minify_options_json)
code: [
"// Can't reproduce test failure within " + max_timeout + "ms",
"// minify options: " + to_comment(minify_options_json)
].join("\n")
};
} else if (differs.error) {
return differs;
} else if (is_error(differs.unminified_result)
&& is_error(differs.minified_result)
&& differs.unminified_result.name == differs.minified_result.name) {
return {
code: [
"// No differences except in error message",
"// minify options: " + to_comment(minify_options_json)
].join("\n")
};
} else {
max_timeout = Math.min(100 * differs.elapsed, max_timeout);
// Replace expressions with constants that will be parsed into
Expand All @@ -71,6 +84,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
// quick ignores
if (node instanceof U.AST_Accessor) return;
if (node instanceof U.AST_Directive) return;
if (!in_list && node instanceof U.AST_EmptyStatement) return;
if (node instanceof U.AST_Label) return;
if (node instanceof U.AST_LabelRef) return;
if (!in_list && node instanceof U.AST_SymbolDeclaration) return;
Expand Down Expand Up @@ -112,10 +126,25 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
}
else if (node instanceof U.AST_Binary) {
CHANGED = true;
return [
var permute = ((node.start._permute += step) * steps | 0) % 4;
var expr = [
node.left,
node.right,
][ ((node.start._permute += step) * steps | 0) % 2 ];
][ permute & 1 ];
if (permute < 2) return expr;
// wrap with console.log()
return new U.AST_Call({
expression: new U.AST_Dot({
expression: new U.AST_SymbolRef({
name: "console",
start: {},
}),
property: "log",
start: {},
}),
args: [ expr ],
start: {},
});
}
else if (node instanceof U.AST_Catch || node instanceof U.AST_Finally) {
// drop catch or finally block
Expand Down Expand Up @@ -357,15 +386,11 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
}

// replace this node
var newNode = U.parse(REPLACEMENTS[node.start._permute % REPLACEMENTS.length | 0], {
var newNode = is_statement(node) ? new U.AST_EmptyStatement({
start: {},
}) : U.parse(REPLACEMENTS[node.start._permute % REPLACEMENTS.length | 0], {
expression: true,
});
if (is_statement(node)) {
newNode = new U.AST_SimpleStatement({
body: newNode,
start: {},
});
}
newNode.start._permute = ++node.start._permute;
CHANGED = true;
return newNode;
Expand Down Expand Up @@ -445,29 +470,62 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
console.error("// reduce test pass " + pass + ": " + testcase.length + " bytes");
}
}
testcase = U.minify(testcase, {
compress: false,
mangle: false,
output: {
beautify: true,
braces: true,
comments: true,
},
});
testcase.code += [
"",
"// output: " + to_comment(differs.unminified_result),
"// minify: " + to_comment(differs.minified_result),
"// options: " + to_comment(minify_options_json),
].join("\n").replace(/\u001b\[\d+m/g, "");
testcase = try_beautify(result_cache, testcase, minify_options, differs.unminified_result, max_timeout);
var lines = [ "" ];
var unminified_result = strip_color_codes(differs.unminified_result);
var minified_result = strip_color_codes(differs.minified_result);
if (trim_trailing_whitespace(unminified_result) == trim_trailing_whitespace(minified_result)) {
lines.push(
"// (stringified)",
"// output: " + JSON.stringify(unminified_result),
"// minify: " + JSON.stringify(minified_result)
);
} else {
lines.push(
"// output: " + to_comment(unminified_result),
"// minify: " + to_comment(minified_result)
);
}
lines.push("// options: " + to_comment(minify_options_json));
testcase.code += lines.join("\n");
return testcase;
}
};

function strip_color_codes(value) {
return ("" + value).replace(/\u001b\[\d+m/g, "");
}

function to_comment(value) {
return ("" + value).replace(/\n/g, "\n// ");
}

function trim_trailing_whitespace(value) {
return ("" + value).replace(/\s+$/, "");
}

function try_beautify(result_cache, testcase, minify_options, expected, timeout) {
var result = U.minify(testcase, {
compress: false,
mangle: false,
output: {
beautify: true,
braces: true,
comments: true,
},
});
if (result.error) return {
code: testcase,
};
var toplevel = sandbox.has_toplevel(minify_options);
var actual = run_code(result_cache, result.code, toplevel, timeout);
if (!sandbox.same_stdout(expected, actual)) return {
code: testcase,
};
result.code = "// (beautified)\n" + result.code;
return result;
}

function has_exit(fn) {
var found = false;
var tw = new U.TreeWalker(function(node) {
Expand Down

0 comments on commit ff72eaa

Please sign in to comment.