mirror of
https://github.com/facebookarchive/prepack.git
synced 2024-09-11 14:46:37 +03:00
Support running tests in inferior JavaScript repl.
Summary: Define two new flags for test-runner.js: --repl <path> : If present the, the prepack sources of test are evaluated using by piping to the process instead of in a new node context. --es5 : run babel on tests, used if the repl does not support es6. To further deal with es6 a new test header flag is introduced, es6. If a test is not relevant in a non es6 world, the test is skipped if the es5 flag is also set. This flag has been added to some tests. Running subprocess for each test is obviously more expensive so the default behavior is unchanged and still uses a new context. Closes https://github.com/facebook/prepack/pull/1103 Differential Revision: D6192670 Pulled By: simonhj fbshipit-source-id: 35e4b819d863634f3273a4da7984c7a71d8c4fb9
This commit is contained in:
parent
da692f0275
commit
0a3e23eeb0
@ -23,11 +23,14 @@ let vm = require("vm");
|
||||
let os = require("os");
|
||||
let minimist = require("minimist");
|
||||
let babel = require("babel-core");
|
||||
let child_process = require("child_process");
|
||||
const EOL = os.EOL;
|
||||
let execSpec;
|
||||
|
||||
function transformWithBabel(code, plugins) {
|
||||
function transformWithBabel(code, plugins, presets) {
|
||||
return babel.transform(code, {
|
||||
plugins,
|
||||
plugins: plugins,
|
||||
presets: presets,
|
||||
}).code;
|
||||
}
|
||||
|
||||
@ -53,7 +56,55 @@ function search(dir, relative) {
|
||||
|
||||
let tests = search(`${__dirname}/../test/serializer`, "test/serializer");
|
||||
|
||||
function exec(code) {
|
||||
// run JS subprocess
|
||||
// externalSpec defines how to invoke external REPL and how to print.
|
||||
// - cmd - cmd to execute, script is piped into this.
|
||||
// - printName - name of function which can be used to print to stdout.
|
||||
function execExternal(externalSpec, code) {
|
||||
// essentially the code from execInContext run through babel
|
||||
let script = `
|
||||
var global = this;
|
||||
var self = this;
|
||||
var _logOutput = "";
|
||||
function write(prefix, values) {
|
||||
_logOutput += "\\n" + prefix + values.join("");
|
||||
}
|
||||
var cachePrint = ${externalSpec.printName};
|
||||
global.console = {}
|
||||
global.console.log = function log() {
|
||||
for (var _len = arguments.length, s = Array(_len), _key = 0; _key < _len; _key++) {
|
||||
s[_key] = arguments[_key];
|
||||
}
|
||||
write("", s);
|
||||
};
|
||||
global.console.warn = function warn() {
|
||||
for (var _len2 = arguments.length, s = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
|
||||
s[_key2] = arguments[_key2];
|
||||
}
|
||||
write("WARN:", s);
|
||||
};
|
||||
global.console.error = function error() {
|
||||
for (var _len3 = arguments.length, s = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
|
||||
s[_key3] = arguments[_key3];
|
||||
}
|
||||
write("ERROR:", s);
|
||||
};
|
||||
try {
|
||||
${code}
|
||||
cachePrint(inspect() + _logOutput);
|
||||
} catch (e) {
|
||||
cachePrint(e);
|
||||
}`;
|
||||
|
||||
let child = child_process.spawnSync(externalSpec.cmd, { input: script });
|
||||
|
||||
let output = String(child.stdout);
|
||||
|
||||
return String(output.trim());
|
||||
}
|
||||
|
||||
// run code in a seperate context
|
||||
function execInContext(code) {
|
||||
let script = new vm.Script(
|
||||
`var global = this; var self = this; ${code}; // keep newline here as code may end with comment
|
||||
report(inspect());`,
|
||||
@ -87,7 +138,7 @@ report(inspect());`,
|
||||
},
|
||||
},
|
||||
});
|
||||
return result + logOutput;
|
||||
return (result + logOutput).trim();
|
||||
}
|
||||
|
||||
function runTest(name, code, options, args) {
|
||||
@ -223,10 +274,10 @@ function runTest(name, code, options, args) {
|
||||
}
|
||||
try {
|
||||
try {
|
||||
expected = exec(`${addedCode}\n(function () {${expectedCode} // keep newline here as code may end with comment
|
||||
expected = execInContext(`${addedCode}\n(function () {${expectedCode} // keep newline here as code may end with comment
|
||||
}).call(this);`);
|
||||
} catch (e) {
|
||||
expected = e;
|
||||
expected = "" + e;
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
@ -257,10 +308,16 @@ function runTest(name, code, options, args) {
|
||||
}
|
||||
}
|
||||
if (markersIssue) break;
|
||||
let codeToRun = addedCode + newCode;
|
||||
if (args.es5) {
|
||||
codeToRun = transformWithBabel(codeToRun, [], [["env", { forceAllTransforms: true, modules: false }]]);
|
||||
}
|
||||
try {
|
||||
actual = exec(addedCode + newCode);
|
||||
if (execSpec) actual = execExternal(execSpec, codeToRun);
|
||||
else actual = execInContext(codeToRun);
|
||||
} catch (e) {
|
||||
actual = e;
|
||||
// always compare strings.
|
||||
actual = "" + e;
|
||||
}
|
||||
if (expected !== actual) {
|
||||
console.log(chalk.red("Output mismatch!"));
|
||||
@ -314,16 +371,41 @@ function runTest(name, code, options, args) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function prepareReplExternalSepc(procPath) {
|
||||
if (!fs.existsSync(procPath)) {
|
||||
throw new ArgsParseError(`runtime ${procPath} does not exist`);
|
||||
}
|
||||
// find out how to print
|
||||
let script = `
|
||||
if (typeof (console) !== 'undefined' && console.log !== undefined) {
|
||||
console.log('console.log')
|
||||
}
|
||||
else if (typeof('print') !== 'undefined') {
|
||||
print('print')
|
||||
}`;
|
||||
let out = child_process.spawnSync(procPath, { input: script });
|
||||
let output = String(out.stdout);
|
||||
if (output.trim() === "") {
|
||||
throw new ArgsParseError(`could not figure out how to print in inferior repl ${procPath}`);
|
||||
}
|
||||
return { printName: output.trim(), cmd: procPath.trim() };
|
||||
}
|
||||
|
||||
function run(args) {
|
||||
let failed = 0;
|
||||
let passed = 0;
|
||||
let total = 0;
|
||||
if (args.outOfProcessRuntime !== "") {
|
||||
execSpec = prepareReplExternalSepc(args.outOfProcessRuntime);
|
||||
}
|
||||
|
||||
for (let test of tests) {
|
||||
// filter hidden files
|
||||
if (path.basename(test.name)[0] === ".") continue;
|
||||
if (test.name.endsWith("~")) continue;
|
||||
if (test.file.includes("// skip")) continue;
|
||||
if (args.es5 && test.file.includes("// es6")) continue;
|
||||
//only run specific tests if desired
|
||||
if (!test.name.includes(args.filter)) continue;
|
||||
|
||||
@ -344,10 +426,14 @@ class ProgramArgs {
|
||||
debugNames: boolean;
|
||||
verbose: boolean;
|
||||
filter: string;
|
||||
constructor(debugNames: boolean, verbose: boolean, filter: string) {
|
||||
outOfProcessRuntime: string;
|
||||
es5: boolean;
|
||||
constructor(debugNames: boolean, verbose: boolean, filter: string, outOfProcessRuntime: string, es5: boolean) {
|
||||
this.debugNames = debugNames;
|
||||
this.verbose = verbose;
|
||||
this.filter = filter; //lets user choose specific test files, runs all tests if omitted
|
||||
this.outOfProcessRuntime = outOfProcessRuntime;
|
||||
this.es5 = es5;
|
||||
}
|
||||
}
|
||||
|
||||
@ -373,7 +459,11 @@ function main(): number {
|
||||
|
||||
// Helper function to provide correct usage information to the user
|
||||
function usage(): string {
|
||||
return `Usage: ${process.argv[0]} ${process.argv[1]} ` + EOL + `[--verbose] [--filter <string>]`;
|
||||
return (
|
||||
`Usage: ${process.argv[0]} ${process.argv[1]} ` +
|
||||
EOL +
|
||||
`[--verbose] [--filter <string>] [--outOfProcessRuntime <path>] [--es5]`
|
||||
);
|
||||
}
|
||||
|
||||
// NOTE: inheriting from Error does not seem to pass through an instanceof
|
||||
@ -388,12 +478,16 @@ class ArgsParseError {
|
||||
// Parses through the command line arguments and throws errors if usage is incorrect
|
||||
function argsParse(): ProgramArgs {
|
||||
let parsedArgs = minimist(process.argv.slice(2), {
|
||||
string: ["filter"],
|
||||
boolean: ["debugNames", "verbose"],
|
||||
string: ["filter", "outOfProcessRuntime"],
|
||||
boolean: ["debugNames", "verbose", "es5"],
|
||||
default: {
|
||||
debugNames: false,
|
||||
verbose: false,
|
||||
es5: false, // if true test marked as es6 only are not run
|
||||
filter: "",
|
||||
outOfProcessRuntime: "", // if set, assumed to be a JS runtime and is used
|
||||
// to run tests. If not a seperate node contexr is
|
||||
// used.
|
||||
},
|
||||
});
|
||||
if (typeof parsedArgs.debugNames !== "boolean") {
|
||||
@ -402,12 +496,24 @@ function argsParse(): ProgramArgs {
|
||||
if (typeof parsedArgs.verbose !== "boolean") {
|
||||
throw new ArgsParseError("verbose must be a boolean (either --verbose or not)");
|
||||
}
|
||||
if (typeof parsedArgs.es5 !== "boolean") {
|
||||
throw new ArgsParseError("es5 must be a boolean (either --es5 or not)");
|
||||
}
|
||||
if (typeof parsedArgs.filter !== "string") {
|
||||
throw new ArgsParseError(
|
||||
"filter must be a string (relative path from serialize directory) (--filter abstract/Residual.js)"
|
||||
);
|
||||
}
|
||||
let programArgs = new ProgramArgs(parsedArgs.debugNames, parsedArgs.verbose, parsedArgs.filter);
|
||||
if (typeof parsedArgs.outOfProcessRuntime !== "string") {
|
||||
throw new ArgsParseError("outOfProcessRuntime must be path pointing to an javascript runtime");
|
||||
}
|
||||
let programArgs = new ProgramArgs(
|
||||
parsedArgs.debugNames,
|
||||
parsedArgs.verbose,
|
||||
parsedArgs.filter,
|
||||
parsedArgs.outOfProcessRuntime,
|
||||
parsedArgs.es5
|
||||
);
|
||||
return programArgs;
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
var someNumber = 5
|
||||
var someString = "hello"
|
||||
var abstractNumber = global.__abstract ? __abstract("number", "someNumber") : 5;
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
(function() {
|
||||
var myObj = {};
|
||||
var fooSym = Symbol('foo');
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
(function() {
|
||||
var myObj = {};
|
||||
var otherSym = Symbol('bar');
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
(function() {
|
||||
let x = global.__abstract ? __abstract("boolean", "(true)") : true;
|
||||
let obj = {};
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
(function() {
|
||||
//let x = global.__abstract ? __abstract("boolean", "(true)") : true;
|
||||
let obj = {};
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
let x = global.__abstract ? __abstract("boolean", "true") : true;
|
||||
let foo = { x: "yz" };
|
||||
let bar1 = { y: 1 };
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
let x = global.__abstract ? __abstract("boolean", "true") : true;
|
||||
let bar0 = { y: 0 };
|
||||
let bar1 = { y: 1 };
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
let nondet = global.__abstract ? __abstract("boolean", "true") : true;
|
||||
global.a = undefined;
|
||||
let descriptor;
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
// delay unsupported requires
|
||||
|
||||
var modules = Object.create(null);
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
// delay unsupported requires
|
||||
|
||||
var modules = Object.create(null);
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
// additional functions
|
||||
// does not contain:var y = 5;
|
||||
// does not contain:var y = 10;
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
// We use the name "console" because it already exists.
|
||||
// Adding a non-existing property will break our test-runner in Node 7.x.x.
|
||||
Object.defineProperty(global, "console", {
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
let IteratorPrototype = [][Symbol.iterator]().__proto__.__proto__;
|
||||
|
||||
inspect = function() {
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
let MapIteratorPrototype = new Map()[Symbol.iterator]().__proto__;
|
||||
|
||||
inspect = function() {
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
(function() {
|
||||
var a = [
|
||||
"Object", "Array", "Function", "Symbol", "String",
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
var st = global.setTimeout;
|
||||
global.setTimeout = function(x) {
|
||||
return st(x, y);
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
it = Symbol.iterator;
|
||||
|
||||
inspect = function() { return it === Symbol.iterator; }
|
||||
inspect = function() { return it === Symbol.iterator; }
|
||||
|
@ -1,7 +1,8 @@
|
||||
// es6
|
||||
(function() {
|
||||
let a = Symbol.for("test");
|
||||
let b = Symbol.for("test");
|
||||
inspect = function() {
|
||||
return "" + (a === Symbol.for("test"));
|
||||
}
|
||||
})();
|
||||
})();
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
let a = {a:1};
|
||||
let b = {b:3};
|
||||
let c = {c:5};
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
let a = {a:1};
|
||||
let b = {b:2};
|
||||
var m = new WeakSet([a, b]);
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
// does contain:keep me
|
||||
(function() {
|
||||
let f = function() { /* keep me */ };
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
// delay unsupported requires
|
||||
|
||||
var modules = Object.create(null);
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
// delay unsupported requires
|
||||
|
||||
var modules = Object.create(null);
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
var modules = Object.create(null);
|
||||
|
||||
__d = define;
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
// does not contain:(0)
|
||||
var modules = Object.create(null);
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
// delay unsupported requires
|
||||
// initialize more modules
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
// does not contain:r(0)
|
||||
// initialize more modules
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
// react
|
||||
// babel:jsx
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
// react
|
||||
// babel:jsx
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
// react
|
||||
// babel:jsx
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
// react
|
||||
// babel:jsx
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
// es6
|
||||
// react
|
||||
// babel:jsx
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user