improve --reduce-test (#3719)

- cover missing cases when eliminating unreferenced labels
- format multi-line outputs correctly
This commit is contained in:
Alex Lam S.L
2020-02-14 02:47:20 +00:00
committed by GitHub
parent c01ff76288
commit f01f580d6c
6 changed files with 112 additions and 20 deletions

View File

@@ -0,0 +1,9 @@
var o = this;
for (var k in o) L17060: {
a++;
}
var a;
console.log(k);

View File

@@ -0,0 +1,16 @@
var o = this;
for (var k in o) {
0;
}
var a;
console.log(k);
// output: a
//
// minify: k
//
// options: {
// "mangle": false
// }

View File

@@ -11,5 +11,12 @@ var a_1 = f0();
console.log(b); console.log(b);
// output: -19 // output: -19
//
// minify: -4 // minify: -4
// options: {"compress":{"unsafe_math":true},"mangle":false} //
// options: {
// "compress": {
// "unsafe_math": true
// },
// "mangle": false
// }

View File

@@ -8,9 +8,9 @@ function read(path) {
} }
describe("test/reduce.js", function() { describe("test/reduce.js", function() {
this.timeout(60000);
it("Should reduce test case", function() { it("Should reduce test case", function() {
this.timeout(60000); var result = reduce_test(read("test/input/reduce/unsafe_math.js"), {
var result = reduce_test(read("test/input/reduce/input.js"), {
compress: { compress: {
unsafe_math: true, unsafe_math: true,
}, },
@@ -19,7 +19,16 @@ describe("test/reduce.js", function() {
verbose: false, verbose: false,
}); });
if (result.error) throw result.error; if (result.error) throw result.error;
assert.strictEqual(result.code, read("test/input/reduce/output.js")); assert.strictEqual(result.code, read("test/input/reduce/unsafe_math.reduced.js"));
});
it("Should eliminate unreferenced labels", function() {
var result = reduce_test(read("test/input/reduce/label.js"), {
mangle: false,
}, {
verbose: false,
});
if (result.error) throw result.error;
assert.strictEqual(result.code, read("test/input/reduce/label.reduced.js"));
}); });
it("Should handle test cases with --toplevel", function() { it("Should handle test cases with --toplevel", function() {
var result = reduce_test([ var result = reduce_test([
@@ -31,7 +40,9 @@ describe("test/reduce.js", function() {
if (result.error) throw result.error; if (result.error) throw result.error;
assert.strictEqual(result.code, [ assert.strictEqual(result.code, [
"// Can't reproduce test failure with minify options provided:", "// Can't reproduce test failure with minify options provided:",
'// {"toplevel":true}', "// {",
'// "toplevel": true',
"// }",
"", "",
].join("\n")); ].join("\n"));
}); });
@@ -40,7 +51,10 @@ describe("test/reduce.js", function() {
if (result.error) throw result.error; if (result.error) throw result.error;
assert.strictEqual(result.code, [ assert.strictEqual(result.code, [
"// Can't reproduce test failure with minify options provided:", "// Can't reproduce test failure with minify options provided:",
'// {"compress":{},"mangle":false}', "// {",
'// "compress": {},',
'// "mangle": false',
"// }",
"", "",
].join("\n")); ].join("\n"));
}); });
@@ -61,8 +75,15 @@ describe("test/reduce.js", function() {
" return f.length;", " return f.length;",
"}());", "}());",
"// output: 1", "// output: 1",
"// ",
"// minify: 0", "// minify: 0",
'// options: {"compress":{"keep_fargs":false},"mangle":false}', "// ",
"// options: {",
'// "compress": {',
'// "keep_fargs": false',
"// },",
'// "mangle": false',
"// }",
].join("\n")); ].join("\n"));
}); });
it("Should fail when invalid option is supplied", function() { it("Should fail when invalid option is supplied", function() {
@@ -81,4 +102,38 @@ describe("test/reduce.js", function() {
assert.ok(err instanceof Error); assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Name expected"); assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Name expected");
}); });
it("Should format multi-line output correctly", function() {
var code = [
"var a = 0;",
"",
"for (var b in [ 1, 2, 3 ]) {",
" a = +a + 1 - .2;",
" console.log(a);",
"}",
].join("\n");
var result = reduce_test(code, {
compress: {
unsafe_math: true,
},
mangle: false,
});
if (result.error) throw result.error;
assert.strictEqual(result.code, [
code,
"// output: 0.8",
"// 1.6",
"// 2.4",
"// ",
"// minify: 0.8",
"// 1.6",
"// 2.4000000000000004",
"// ",
"// options: {",
'// "compress": {',
'// "unsafe_math": true',
"// },",
'// "mangle": false',
"// }",
].join("\n"));
});
}); });

View File

@@ -22,7 +22,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
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 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, null, 2);
var timeout = 1000; // start with a low timeout var timeout = 1000; // start with a low timeout
var differs; var differs;
@@ -256,6 +256,15 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
CHANGED = true; CHANGED = true;
return node.expression; return node.expression;
} }
else if (node instanceof U.AST_LabeledStatement) {
if (node.body instanceof U.AST_Statement
&& !has_loopcontrol(node.body, node.body, node)) {
// replace labelled statement with its non-labelled body
node.start._permute = REPLACEMENTS.length;
CHANGED = true;
return node.body;
}
}
if (in_list) { if (in_list) {
// special case to drop object properties and switch branches // special case to drop object properties and switch branches
@@ -268,14 +277,6 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
// replace or skip statement // replace or skip statement
if (node instanceof U.AST_Statement) { if (node instanceof U.AST_Statement) {
if (node instanceof U.AST_LabeledStatement
&& node.body instanceof U.AST_Statement
&& !has_loopcontrol(node.body, node.body, node)) {
// replace labelled statement with its non-labelled body
node.start._permute = REPLACEMENTS.length;
CHANGED = true;
return node.body;
}
node.start._permute++; node.start._permute++;
CHANGED = true; CHANGED = true;
return List.skip; return List.skip;
@@ -379,13 +380,13 @@ 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");
} }
} }
testcase += "\n// output: " + differs.unminified_result testcase += "\n// output: " + to_comment(differs.unminified_result)
+ "\n// minify: " + differs.minified_result + "\n// minify: " + to_comment(differs.minified_result)
+ "\n// options: " + minify_options_json; + "\n// options: " + to_comment(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// " + to_comment(minify_options_json);
} }
var result = U.minify(testcase.replace(/\u001b\[\d+m/g, ""), { var result = U.minify(testcase.replace(/\u001b\[\d+m/g, ""), {
compress: false, compress: false,
@@ -399,6 +400,10 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
return result; return result;
}; };
function to_comment(value) {
return ("" + value).replace(/\n/g, "\n// ");
}
function has_loopcontrol(body, loop, label) { function has_loopcontrol(body, loop, label) {
var found = false; var found = false;
var tw = new U.TreeWalker(function(node) { var tw = new U.TreeWalker(function(node) {