Skip to content

Commit

Permalink
support async test cases properly
Browse files Browse the repository at this point in the history
  • Loading branch information
alexlamsl committed Jan 11, 2021
1 parent 52e94a0 commit b48bc06
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 96 deletions.
11 changes: 3 additions & 8 deletions test/compress.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ function reminify(orig_options, input_code, input_formatted, stdout) {
} else {
var toplevel = sandbox.has_toplevel(options);
var expected = stdout[toplevel ? 1 : 0];
var actual = run_code(result.code, toplevel);
var actual = sandbox.run_code(result.code, toplevel);
if (typeof expected != "string" && typeof actual != "string" && expected.name == actual.name) {
actual = expected;
}
Expand Down Expand Up @@ -244,11 +244,6 @@ function reminify(orig_options, input_code, input_formatted, stdout) {
return true;
}

function run_code(code, toplevel) {
var result = sandbox.run_code(code, toplevel);
return typeof result == "string" ? result.replace(/\u001b\[\d+m/g, "") : result;
}

function test_case(test) {
log(" Running test [{name}]", { name: test.name });
U.AST_Node.enable_validation();
Expand Down Expand Up @@ -380,7 +375,7 @@ function test_case(test) {
}
}
if (test.expect_stdout && (!test.node_version || semver.satisfies(process.version, test.node_version))) {
var stdout = [ run_code(input_code), run_code(input_code, true) ];
var stdout = [ sandbox.run_code(input_code), sandbox.run_code(input_code, true) ];
var toplevel = sandbox.has_toplevel({
compress: test.options,
mangle: test.mangle
Expand Down Expand Up @@ -409,7 +404,7 @@ function test_case(test) {
});
return false;
}
actual = run_code(output_code, toplevel);
actual = sandbox.run_code(output_code, toplevel);
if (!sandbox.same_stdout(test.expect_stdout, actual)) {
log([
"!!! failed",
Expand Down
30 changes: 19 additions & 11 deletions test/compress/awaits.js
Original file line number Diff line number Diff line change
Expand Up @@ -612,22 +612,32 @@ issue_4340: {
call_expression: {
input: {
console.log(typeof async function(log) {
(await log)("FAIL");
(await log)("foo");
}(console.log).then);
console.log("bar");
}
expect_exact: 'console.log(typeof async function(log){(await log)("FAIL")}(console.log).then);'
expect_stdout: "function"
expect_exact: 'console.log(typeof async function(log){(await log)("foo")}(console.log).then);console.log("bar");'
expect_stdout: [
"function",
"bar",
"foo",
]
node_version: ">=8"
}

property_access_expression: {
input: {
console.log(typeof async function(con) {
(await con).log("FAIL");
(await con).log("foo");
}(console).then);
console.log("bar");
}
expect_exact: 'console.log(typeof async function(con){(await con).log("FAIL")}(console).then);'
expect_stdout: "function"
expect_exact: 'console.log(typeof async function(con){(await con).log("foo")}(console).then);console.log("bar");'
expect_stdout: [
"function",
"bar",
"foo",
]
node_version: ">=8"
}

Expand Down Expand Up @@ -685,20 +695,18 @@ reduce_iife_3: {
input: {
var a = "foo";
(async function() {
console.log(a);
console.log(await a);
console.log(a, await a, a, await a);
})();
a = "bar";
}
expect: {
var a = "foo";
(async function() {
console.log(a);
console.log(await a);
console.log(a, await a, a, await a);
})();
a = "bar";
}
expect_stdout: "foo"
expect_stdout: "foo foo bar bar"
node_version: ">=8"
}

Expand Down
10 changes: 3 additions & 7 deletions test/reduce.js
Original file line number Diff line number Diff line change
Expand Up @@ -592,10 +592,10 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
}
var lines = [ "" ];
if (isNaN(max_timeout)) {
lines.push("// minify error: " + to_comment(strip_color_codes(differs.minified_result.stack)));
lines.push("// minify error: " + to_comment(differs.minified_result.stack));
} else {
var unminified_result = strip_color_codes(differs.unminified_result);
var minified_result = strip_color_codes(differs.minified_result);
var unminified_result = differs.unminified_result;
var minified_result = differs.minified_result;
if (trim_trailing_whitespace(unminified_result) == trim_trailing_whitespace(minified_result)) {
lines.push(
"// (stringified)",
Expand All @@ -616,10 +616,6 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
}
};

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

function to_comment(value) {
return ("" + value).replace(/\n/g, "\n// ");
}
Expand Down
197 changes: 128 additions & 69 deletions test/sandbox.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,103 @@
var execSync = require("child_process").execSync;
var semver = require("semver");
var vm = require("vm");

var setupContext = new vm.Script([
"[ Array, Boolean, Error, Function, Number, Object, RegExp, String ].forEach(function(f) {",
" f.toString = Function.prototype.toString;",
"});",
"Function.prototype.toString = function() {",
" var id = 100000;",
" return function() {",
" var n = this.name;",
" if (!/^F[0-9]{6}N$/.test(n)) {",
' n = "F" + ++id + "N";',
].concat(Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable ? [
' Object.defineProperty(this, "name", {',
" get: function() {",
" return n;",
" }",
" });",
] : [], [
" }",
' return "function(){}";',
" };",
"}();",
"this;",
]).join("\n"));
var setup_code = "(" + setup + ")(this);";
exports.run_code = semver.satisfies(process.version, "0.8") ? function(code, toplevel, timeout) {
var stdout = run_code_vm(code, toplevel, timeout);
if (typeof stdout != "string" || !/arguments/.test(code)) return stdout;
do {
var prev = stdout;
stdout = run_code_vm(code, toplevel, timeout);
} while (prev !== stdout);
return stdout;
} : semver.satisfies(process.version, "<8") ? run_code_vm : function(code, toplevel, timeout) {
return (/\b(async|setInterval|setTimeout)\b/.test(code) ? run_code_exec : run_code_vm)(code, toplevel, timeout);
};
exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) {
if (typeof expected != typeof actual) return false;
if (typeof expected == "object" && typeof expected.name == "string" && typeof expected.message == "string") {
if (expected.name !== actual.name) return false;
if (typeof actual.message != "string") return false;
expected = expected.message.slice(expected.message.lastIndexOf("\n") + 1);
actual = actual.message.slice(actual.message.lastIndexOf("\n") + 1);
}
return strip_func_ids(expected) == strip_func_ids(actual);
} : function(expected, actual) {
return typeof expected == typeof actual && strip_func_ids(expected) == strip_func_ids(actual);
};
exports.has_toplevel = function(options) {
return options.toplevel
|| options.mangle && options.mangle.toplevel
|| options.compress && options.compress.toplevel;
};

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

function createContext() {
var ctx = vm.createContext(Object.defineProperties({}, {
console: { value: { log: log } },
function strip_func_ids(text) {
return ("" + text).replace(/F[0-9]{6}N/g, "<F<>N>");
}

function setup(global) {
[ Array, Boolean, Error, Function, Number, Object, RegExp, String ].forEach(function(f) {
f.toString = Function.prototype.toString;
});
Function.prototype.toString = function() {
var configurable = Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable;
var id = 100000;
return function() {
var n = this.name;
if (!/^F[0-9]{6}N$/.test(n)) {
n = "F" + ++id + "N";
if (configurable) Object.defineProperty(this, "name", {
get: function() {
return n;
}
});
}
return "function(){}";
};
}();
var process = global.process;
if (process) process.on("uncaughtException", function(ex) {
if (!(ex instanceof Error)) process.stderr.write(JSON.stringify(ex) + "\n");
throw ex;
}).on("unhandledRejection", function() {});
var log = console.log;
var safe_console = {
log: function(msg) {
if (arguments.length == 1 && typeof msg == "string") return log("%s", msg);
return log.apply(null, [].map.call(arguments, function(arg) {
return safe_log(arg, 3);
}));
},
};
// for Node.js v0.12
global.console = safe_console;
for (var name in global) delete global[name];
Object.defineProperties(global, {
// for Node.js v8
console: {
get: function() {
return safe_console;
},
},
global: { get: self },
// for Node.js v8
process: {
get: function() {
return process;
},
},
self: { get: self },
window: { get: self },
}));
var global = setupContext.runInContext(ctx);
return ctx;
});
// for Node.js v10+
global.toString = function() {
return "[object global]";
};

function self() {
return this;
Expand All @@ -55,60 +119,55 @@ function createContext() {
}
return arg;
}

function log(msg) {
if (arguments.length == 1 && typeof msg == "string") return console.log("%s", msg);
return console.log.apply(console, [].map.call(arguments, function(arg) {
return safe_log(arg, 3);
}));
}
}

function run_code(code, toplevel, timeout) {
function run_code_vm(code, toplevel, timeout) {
timeout = timeout || 5000;
var stdout = "";
var original_write = process.stdout.write;
process.stdout.write = function(chunk) {
stdout += chunk;
};
try {
vm.runInContext(toplevel ? "(function(){" + code + "})()" : code, createContext(), { timeout: timeout });
return stdout;
var ctx = vm.createContext({ console: console });
// for Node.js v6
vm.runInContext(setup_code, ctx);
vm.runInContext(toplevel ? "(function(){" + code + "})();" : code, ctx, { timeout: timeout });
return strip_color_codes(stdout);
} catch (ex) {
if (ex && typeof ex.stack == "string") ex.stack = strip_color_codes(ex.stack);
return ex;
} finally {
process.stdout.write = original_write;
}
}

exports.run_code = semver.satisfies(process.version, "0.8") ? function(code, toplevel, timeout) {
var stdout = run_code(code, toplevel, timeout);
if (typeof stdout != "string" || !/arguments/.test(code)) return stdout;
do {
var prev = stdout;
stdout = run_code(code, toplevel, timeout);
} while (prev !== stdout);
return stdout;
} : run_code;

function strip_func_ids(text) {
return ("" + text).replace(/F[0-9]{6}N/g, "<F<>N>");
}

exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) {
if (typeof expected != typeof actual) return false;
if (typeof expected == "object" && typeof expected.name == "string" && typeof expected.message == "string") {
if (expected.name !== actual.name) return false;
if (typeof actual.message != "string") return false;
expected = expected.message.slice(expected.message.lastIndexOf("\n") + 1);
actual = actual.message.slice(actual.message.lastIndexOf("\n") + 1);
function run_code_exec(code, toplevel, timeout) {
if (toplevel) {
code = setup_code + "(function(){" + code + "})();";
} else {
code = code.replace(/^((["'])[^"']*\1(;|$))?/, function(directive) {
return directive + setup_code;
});
}
return strip_func_ids(expected) == strip_func_ids(actual);
} : function(expected, actual) {
return typeof expected == typeof actual && strip_func_ids(expected) == strip_func_ids(actual);
};
exports.has_toplevel = function(options) {
return options.toplevel
|| options.mangle && options.mangle.toplevel
|| options.compress && options.compress.toplevel;
};
try {
return execSync('"' + process.argv[0] + '"', {
encoding: "utf8",
input: code,
stdio: "pipe",
timeout: timeout || 5000,
});
} catch (ex) {
var msg = ex.message.replace(/\r\n/g, "\n");
var match = /\n([^:\s]*Error)(?:: ([\s\S]+?))?\n( at [\s\S]+)\n$/.exec(msg);
if (match) {
ex = new global[match[1]](match[2]);
ex.stack = ex.stack.slice(0, ex.stack.indexOf(" at ")) + match[3];
} else if (/ETIMEDOUT/.test(msg)) {
ex = new Error("Script execution timed out.");
} else try {
ex = JSON.parse(msg.split(/\n/, 3)[1]);
} catch (e) {}
return ex;
}
}
8 changes: 7 additions & 1 deletion test/ufuzz/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1735,7 +1735,13 @@ function log(options) {
}

function sort_globals(code) {
var globals = sandbox.run_code("throw Object.keys(this).sort();" + code);
var globals = sandbox.run_code("throw Object.keys(this).sort(" + function(global) {
return function(m, n) {
var fm = typeof global[m] == "function";
var fn = typeof global[n] == "function";
return fn - fm || (m < n ? -1 : m > n ? 1 : 0);
};
} + "(this));" + code);
return globals.length ? "var " + globals.map(function(name) {
return name + "=" + name;
}).join(",") + ";" + code : code;
Expand Down

0 comments on commit b48bc06

Please sign in to comment.