1470 lines
53 KiB
JavaScript
1470 lines
53 KiB
JavaScript
"use strict";
|
|
|
|
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
|
|
|
|
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
|
|
|
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /**
|
|
* Copyright (c) 2017-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
|
|
/* eslint-disable no-extend-native */
|
|
|
|
// need to use graceful-fs for single-process code because it opens too many
|
|
// files
|
|
|
|
|
|
var _completions = require("../lib/completions.js");
|
|
|
|
var _index = require("../lib/values/index.js");
|
|
|
|
var _realm = require("../lib/realm.js");
|
|
|
|
var _construct_realm = require("../lib/construct_realm.js");
|
|
|
|
var _construct_realm2 = _interopRequireDefault(_construct_realm);
|
|
|
|
var _globals = require("../lib/globals.js");
|
|
|
|
var _globals2 = _interopRequireDefault(_globals);
|
|
|
|
var _arraybuffer = require("../lib/methods/arraybuffer.js");
|
|
|
|
var _singletons = require("../lib/singletons.js");
|
|
|
|
var _get = require("../lib/methods/get.js");
|
|
|
|
var _invariant = require("../lib/invariant.js");
|
|
|
|
var _invariant2 = _interopRequireDefault(_invariant);
|
|
|
|
var _jsYaml = require("js-yaml");
|
|
|
|
var _jsYaml2 = _interopRequireDefault(_jsYaml);
|
|
|
|
var _chalk = require("chalk");
|
|
|
|
var _chalk2 = _interopRequireDefault(_chalk);
|
|
|
|
var _path = require("path");
|
|
|
|
var _path2 = _interopRequireDefault(_path);
|
|
|
|
var _gracefulFs = require("graceful-fs");
|
|
|
|
var _gracefulFs2 = _interopRequireDefault(_gracefulFs);
|
|
|
|
var _cluster = require("cluster");
|
|
|
|
var _cluster2 = _interopRequireDefault(_cluster);
|
|
|
|
var _os = require("os");
|
|
|
|
var _os2 = _interopRequireDefault(_os);
|
|
|
|
var _tty = require("tty");
|
|
|
|
var _tty2 = _interopRequireDefault(_tty);
|
|
|
|
var _minimist = require("minimist");
|
|
|
|
var _minimist2 = _interopRequireDefault(_minimist);
|
|
|
|
var _process = require("process");
|
|
|
|
var _process2 = _interopRequireDefault(_process);
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
var EOL = _os2.default.EOL;
|
|
var numCPUs = _os2.default.cpus().length;
|
|
require("source-map-support").install();
|
|
|
|
// A TestTask is a task for a worker process to execute, which contains a
|
|
// single test to run
|
|
var TestTask = function () {
|
|
function TestTask(file) {
|
|
_classCallCheck(this, TestTask);
|
|
|
|
this.type = TestTask.sentinel;
|
|
this.file = file;
|
|
}
|
|
|
|
// eslint-disable-next-line flowtype/no-weak-types
|
|
|
|
|
|
_createClass(TestTask, null, [{
|
|
key: "fromObject",
|
|
value: function fromObject(obj) {
|
|
// attempt to coerce the object into a test task
|
|
if ("file" in obj && _typeof(obj.file) === "object") {
|
|
return new TestTask(TestFileInfo.fromObject(obj.file));
|
|
} else {
|
|
throw new Error("Cannot be converted to a TestTask: " + JSON.stringify(obj));
|
|
}
|
|
}
|
|
}]);
|
|
|
|
return TestTask;
|
|
}();
|
|
|
|
/**
|
|
* Information about a test file to be run.
|
|
*
|
|
*/
|
|
|
|
|
|
TestTask.sentinel = "TestTask";
|
|
|
|
var TestFileInfo = function () {
|
|
function TestFileInfo(location, isES6) {
|
|
_classCallCheck(this, TestFileInfo);
|
|
|
|
this.location = location;
|
|
this.isES6 = isES6;
|
|
this.groupName = _path2.default.dirname(location);
|
|
}
|
|
|
|
// eslint-disable-next-line flowtype/no-weak-types
|
|
|
|
// Location of the test on the filesystem, call fs.readFile on this
|
|
|
|
|
|
_createClass(TestFileInfo, null, [{
|
|
key: "fromObject",
|
|
value: function fromObject(obj) {
|
|
// attempt to coerce the object into a TestFileInfo
|
|
if ("location" in obj && typeof obj.location === "string" && "isES6" in obj && typeof obj.isES6 === "boolean") {
|
|
return new TestFileInfo(obj.location, obj.isES6);
|
|
} else {
|
|
throw new Error("Cannot be converted to a TestFileInfo: " + JSON.stringify(obj));
|
|
}
|
|
}
|
|
}]);
|
|
|
|
return TestFileInfo;
|
|
}();
|
|
|
|
// A Message sent by a worker to the master to say that it has finished its
|
|
// current task successfully
|
|
|
|
|
|
var DoneMessage = function () {
|
|
function DoneMessage(test) {
|
|
var testResult = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
|
|
|
_classCallCheck(this, DoneMessage);
|
|
|
|
this.type = DoneMessage.sentinel;
|
|
this.test = test;
|
|
this.testResults = testResult;
|
|
}
|
|
|
|
// eslint-disable-next-line flowtype/no-weak-types
|
|
|
|
|
|
_createClass(DoneMessage, null, [{
|
|
key: "fromObject",
|
|
value: function fromObject(obj) {
|
|
if (!("type" in obj && typeof obj.type === "string" && obj.type === DoneMessage.sentinel)) {
|
|
throw new Error("Cannot be converted to a DoneMessage: " + JSON.stringify(obj));
|
|
}
|
|
if (!("test" in obj && _typeof(obj.test) === "object")) {
|
|
throw new Error("A DoneMessage must have a test");
|
|
}
|
|
var msg = new DoneMessage(obj.test);
|
|
if ("testResults" in obj && _typeof(obj.testResults) === "object" && Array.isArray(obj.testResults)) {
|
|
msg.testResults = obj.testResults;
|
|
}
|
|
return msg;
|
|
}
|
|
}]);
|
|
|
|
return DoneMessage;
|
|
}();
|
|
|
|
DoneMessage.sentinel = "DoneMessage";
|
|
|
|
var ErrorMessage = function () {
|
|
function ErrorMessage(err) {
|
|
_classCallCheck(this, ErrorMessage);
|
|
|
|
this.type = ErrorMessage.sentinel;
|
|
this.err = err;
|
|
}
|
|
|
|
// eslint-disable-next-line flowtype/no-weak-types
|
|
|
|
|
|
_createClass(ErrorMessage, null, [{
|
|
key: "fromObject",
|
|
value: function fromObject(obj) {
|
|
if (!("type" in obj && typeof obj.type === "string" && obj.type === ErrorMessage.sentinel)) {
|
|
throw new Error("Cannot be converted to an ErrorMessage: " + JSON.stringify(obj));
|
|
}
|
|
if (!("err" in obj && _typeof(obj.err) === "object")) {
|
|
throw new Error("Cannot be converted to an ErrorMessage: " + JSON.stringify(obj));
|
|
}
|
|
return new ErrorMessage(obj.err);
|
|
}
|
|
}]);
|
|
|
|
return ErrorMessage;
|
|
}();
|
|
|
|
/**
|
|
* TestResult contains information about a test that ran.
|
|
*/
|
|
|
|
|
|
ErrorMessage.sentinel = "ErrorMessage";
|
|
|
|
var TestResult = function TestResult(passed, strict) {
|
|
var err = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
|
|
|
|
_classCallCheck(this, TestResult);
|
|
|
|
this.passed = passed;
|
|
this.strict = strict;
|
|
this.err = err;
|
|
};
|
|
|
|
// A Message sent by the master to workers to say that there is nothing more
|
|
// to do
|
|
|
|
|
|
var QuitMessage = function () {
|
|
function QuitMessage() {
|
|
_classCallCheck(this, QuitMessage);
|
|
|
|
this.type = QuitMessage.sentinel;
|
|
}
|
|
|
|
_createClass(QuitMessage, null, [{
|
|
key: "fromObject",
|
|
value: function fromObject(obj) {
|
|
return new QuitMessage();
|
|
}
|
|
}]);
|
|
|
|
return QuitMessage;
|
|
}();
|
|
|
|
QuitMessage.sentinel = "QuitMessage";
|
|
|
|
var BannerData = function () {
|
|
function BannerData() {
|
|
_classCallCheck(this, BannerData);
|
|
|
|
this.info = "";
|
|
this.es5id = "";
|
|
this.es6id = "";
|
|
this.description = "";
|
|
this.flags = [];
|
|
this.features = [];
|
|
this.includes = [];
|
|
this.negative = {};
|
|
}
|
|
|
|
// eslint-disable-next-line flowtype/no-weak-types
|
|
|
|
// eslint-disable-next-line flowtype/no-weak-types
|
|
|
|
|
|
_createClass(BannerData, null, [{
|
|
key: "fromObject",
|
|
value: function fromObject(obj) {
|
|
var bd = new BannerData();
|
|
if ("info" in obj && typeof obj.info === "string") {
|
|
bd.info = obj.info;
|
|
}
|
|
if ("es5id" in obj && typeof obj.es5id === "string") {
|
|
bd.es5id = obj.es5id;
|
|
}
|
|
if ("es6id" in obj && typeof obj.es6id === "string") {
|
|
bd.es6id = obj.es6id;
|
|
}
|
|
if ("description" in obj && typeof obj.description === "string") {
|
|
bd.description = obj.description;
|
|
}
|
|
if ("flags" in obj && _typeof(obj.flags) === "object" && Array.isArray(obj.flags)) {
|
|
bd.flags = obj.flags;
|
|
}
|
|
if ("features" in obj && _typeof(obj.features) === "object" && Array.isArray(obj.features)) {
|
|
bd.features = obj.features;
|
|
}
|
|
if ("includes" in obj && _typeof(obj.includes) === "object" && Array.isArray(obj.includes)) {
|
|
bd.includes = obj.includes;
|
|
}
|
|
if ("negative" in obj && _typeof(obj.negative) === "object") {
|
|
bd.negative = obj.negative;
|
|
}
|
|
return bd;
|
|
}
|
|
}]);
|
|
|
|
return BannerData;
|
|
}();
|
|
|
|
var MasterProgramArgs = function MasterProgramArgs(verbose, timeout, bailAfter, cpuScale, statusFile, filterString, singleThreaded, relativeTestPath) {
|
|
_classCallCheck(this, MasterProgramArgs);
|
|
|
|
this.verbose = verbose;
|
|
this.timeout = timeout;
|
|
this.bailAfter = bailAfter;
|
|
this.cpuScale = cpuScale;
|
|
this.statusFile = statusFile;
|
|
this.filterString = filterString;
|
|
this.singleThreaded = singleThreaded;
|
|
this.relativeTestPath = relativeTestPath;
|
|
};
|
|
|
|
var WorkerProgramArgs = function WorkerProgramArgs(timeout, relativeTestPath) {
|
|
_classCallCheck(this, WorkerProgramArgs);
|
|
|
|
this.timeout = timeout;
|
|
this.relativeTestPath = relativeTestPath;
|
|
};
|
|
|
|
// NOTE: inheriting from Error does not seem to pass through an instanceof
|
|
// check
|
|
|
|
|
|
var ArgsParseError = function ArgsParseError(message) {
|
|
_classCallCheck(this, ArgsParseError);
|
|
|
|
this.message = message;
|
|
};
|
|
|
|
if (!("toJSON" in Error.prototype)) {
|
|
// $FlowFixMe this needs to become defined for Error to be serialized
|
|
Object.defineProperty(Error.prototype, "toJSON", {
|
|
// eslint-disable-line
|
|
value: function value() {
|
|
var alt = {};
|
|
Object.getOwnPropertyNames(this).forEach(function (key) {
|
|
alt[key] = this[key];
|
|
}, this);
|
|
return alt;
|
|
},
|
|
configurable: true,
|
|
writable: true
|
|
});
|
|
}
|
|
|
|
main();
|
|
|
|
function main() {
|
|
try {
|
|
if (_cluster2.default.isMaster) {
|
|
var args = masterArgsParse();
|
|
masterRun(args);
|
|
} else if (_cluster2.default.isWorker) {
|
|
var _args = workerArgsParse();
|
|
workerRun(_args);
|
|
} else {
|
|
throw new Error("Not a master or a worker");
|
|
}
|
|
} catch (e) {
|
|
if (e instanceof ArgsParseError) {
|
|
console.error("Illegal argument: %s.\n%s", e.message, usage());
|
|
} else {
|
|
console.error(e);
|
|
}
|
|
_process2.default.exit(1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
function usage() {
|
|
return "Usage: " + _process2.default.argv[0] + " " + _process2.default.argv[1] + " " + EOL + "[--verbose] [--timeout <number>] [--bailAfter <number>] " + EOL + "[--cpuScale <number>] [--statusFile <string>] [--singleThreaded] [--relativeTestPath <string>]";
|
|
}
|
|
|
|
function masterArgsParse() {
|
|
var parsedArgs = (0, _minimist2.default)(_process2.default.argv.slice(2), {
|
|
string: ["statusFile", "relativeTestPath"],
|
|
boolean: ["verbose", "singleThreaded"],
|
|
default: {
|
|
verbose: _process2.default.stdout instanceof _tty2.default.WriteStream ? false : true,
|
|
statusFile: "",
|
|
timeout: 10,
|
|
cpuScale: 1,
|
|
bailAfter: Infinity,
|
|
singleThreaded: false,
|
|
relativeTestPath: "/../test/test262"
|
|
}
|
|
});
|
|
var filterString = parsedArgs._[0];
|
|
if (typeof parsedArgs.verbose !== "boolean") {
|
|
throw new ArgsParseError("verbose must be a boolean (either --verbose or not)");
|
|
}
|
|
if (typeof parsedArgs.timeout !== "number") {
|
|
throw new ArgsParseError("timeout must be a number (in seconds) (--timeout 10)");
|
|
}
|
|
if (typeof parsedArgs.bailAfter !== "number") {
|
|
throw new ArgsParseError("bailAfter must be a number (--bailAfter 10)");
|
|
}
|
|
if (typeof parsedArgs.cpuScale !== "number") {
|
|
throw new ArgsParseError("cpuScale must be a number (--cpuScale 0.5)");
|
|
}
|
|
if (typeof parsedArgs.statusFile !== "string") {
|
|
throw new ArgsParseError("statusFile must be a string (--statusFile file.txt)");
|
|
}
|
|
if (typeof parsedArgs.singleThreaded !== "boolean") {
|
|
throw new ArgsParseError("singleThreaded must be a boolean (either --singleThreaded or not)");
|
|
}
|
|
if (typeof parsedArgs.relativeTestPath !== "string") {
|
|
throw new ArgsParseError("relativeTestPath must be a string (--relativeTestPath /../test/test262)");
|
|
}
|
|
var programArgs = new MasterProgramArgs(parsedArgs.verbose, parsedArgs.timeout, parsedArgs.bailAfter, parsedArgs.cpuScale, parsedArgs.statusFile, filterString, parsedArgs.singleThreaded, parsedArgs.relativeTestPath);
|
|
if (programArgs.filterString) {
|
|
// if filterstring is provided, assume that verbosity is desired
|
|
programArgs.verbose = true;
|
|
}
|
|
return programArgs;
|
|
}
|
|
|
|
function workerArgsParse() {
|
|
var parsedArgs = (0, _minimist2.default)(_process2.default.argv.slice(2), {
|
|
default: {
|
|
timeout: 10,
|
|
relativeTestPath: "/../test/test262"
|
|
}
|
|
});
|
|
if (typeof parsedArgs.timeout !== "number") {
|
|
throw new ArgsParseError("timeout must be a number (in seconds) (--timeout 10)");
|
|
}
|
|
if (typeof parsedArgs.relativeTestPath !== "string") {
|
|
throw new ArgsParseError("relativeTestPath must be a string (--relativeTestPath /../test/test262)");
|
|
}
|
|
return new WorkerProgramArgs(parsedArgs.timeout, parsedArgs.relativeTestPath);
|
|
}
|
|
|
|
function masterRun(args) {
|
|
var testPath = "" + __dirname + args.relativeTestPath + "/test";
|
|
var tests = getFilesSync(testPath);
|
|
// remove tests that don't need to be run
|
|
if (args.filterString) tests = tests.filter(function (test) {
|
|
return test.location.includes(args.filterString);
|
|
});
|
|
var originalTestLength = tests.length;
|
|
tests = tests.filter(function (test) {
|
|
return testFilterByMetadata(test);
|
|
});
|
|
var groups = {};
|
|
|
|
// Now that all the tasks are ready, start up workers to begin processing
|
|
// if single threaded, use that route instead
|
|
if (args.singleThreaded) {
|
|
masterRunSingleProcess(args, groups, tests, originalTestLength - tests.length);
|
|
} else {
|
|
masterRunMultiProcess(args, groups, tests, originalTestLength - tests.length);
|
|
}
|
|
}
|
|
|
|
function masterRunSingleProcess(args, groups, tests, numFiltered) {
|
|
console.log("Running " + tests.length + " tests as a single process");
|
|
// print out every 5 percent (more granularity than multi-process because multi-process
|
|
// runs a lot faster)
|
|
var granularity = Math.floor(tests.length / 20);
|
|
var harnesses = getHarnesses(args.relativeTestPath);
|
|
var numLeft = tests.length;
|
|
|
|
var _loop = function _loop(t) {
|
|
handleTest(t, harnesses, args.timeout, function (err, results) {
|
|
if (err) {
|
|
if (args.verbose) {
|
|
console.error(err);
|
|
}
|
|
} else {
|
|
var ok = handleTestResults(groups, t, results);
|
|
if (!ok) {
|
|
// handleTestResults returns false if a failure threshold was exceeded
|
|
throw new Error("Too many test failures");
|
|
}
|
|
var progress = getProgressBar(numLeft, tests.length, granularity);
|
|
if (progress) {
|
|
console.log(progress);
|
|
}
|
|
}
|
|
numLeft--;
|
|
if (numLeft === 0) {
|
|
// all done
|
|
_process2.default.exit(handleFinished(args, groups, numFiltered));
|
|
}
|
|
});
|
|
};
|
|
|
|
var _iteratorNormalCompletion = true;
|
|
var _didIteratorError = false;
|
|
var _iteratorError = undefined;
|
|
|
|
try {
|
|
for (var _iterator = tests[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
var t = _step.value;
|
|
|
|
_loop(t);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError = true;
|
|
_iteratorError = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
_iterator.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError) {
|
|
throw _iteratorError;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function masterRunMultiProcess(args, groups, tests, numFiltered) {
|
|
if (!_cluster2.default.on) {
|
|
// stop flow errors on "cluster.on"
|
|
throw new Error("cluster is malformed");
|
|
}
|
|
var granularity = Math.floor(tests.length / 10);
|
|
var originalTestLength = tests.length;
|
|
// Fork workers.
|
|
var numWorkers = Math.floor(numCPUs * args.cpuScale);
|
|
console.log("Master starting up, forking " + numWorkers + " workers");
|
|
for (var i = 0; i < numWorkers; i++) {
|
|
_cluster2.default.fork();
|
|
}
|
|
|
|
var exitCount = 0;
|
|
_cluster2.default.on("exit", function (worker, code, signal) {
|
|
exitCount++;
|
|
if (exitCount === numWorkers) {
|
|
_process2.default.exit(handleFinished(args, groups, numFiltered));
|
|
}
|
|
});
|
|
|
|
var giveTask = function giveTask(worker) {
|
|
// grab another test to run and give it to the child process
|
|
if (tests.length === 0) {
|
|
worker.send(new QuitMessage());
|
|
} else {
|
|
worker.send(new TestTask(tests.pop()));
|
|
}
|
|
};
|
|
|
|
_cluster2.default.on("message", function (worker, message, handle) {
|
|
switch (message.type) {
|
|
case ErrorMessage.sentinel:
|
|
var errMsg = ErrorMessage.fromObject(message);
|
|
// just skip the error, thus skipping that test
|
|
if (args.verbose) {
|
|
console.error("An error occurred in worker #" + worker.process.pid + ":");
|
|
console.error(errMsg.err);
|
|
}
|
|
giveTask(worker);
|
|
break;
|
|
case DoneMessage.sentinel:
|
|
var done = DoneMessage.fromObject(message);
|
|
var ok = handleTestResults(groups, done.test, done.testResults);
|
|
if (!ok) {
|
|
// bail
|
|
killWorkers(_cluster2.default.workers);
|
|
handleFinished(args, groups, numFiltered);
|
|
_process2.default.exit(1);
|
|
}
|
|
giveTask(worker);
|
|
var progress = getProgressBar(tests.length, originalTestLength, granularity);
|
|
if (progress) {
|
|
console.log(progress);
|
|
}
|
|
break;
|
|
default:
|
|
throw new Error("Master got an unexpected message: " + JSON.stringify(message));
|
|
}
|
|
});
|
|
|
|
_cluster2.default.on("online", function (worker) {
|
|
giveTask(worker);
|
|
});
|
|
}
|
|
|
|
function handleFinished(args, groups, earlierNumSkipped) {
|
|
var numPassed = 0;
|
|
var numPassedES5 = 0;
|
|
var numPassedES6 = 0;
|
|
var numFailed = 0;
|
|
var numFailedES5 = 0;
|
|
var numFailedES6 = 0;
|
|
var numSkipped = earlierNumSkipped;
|
|
var numTimeouts = 0;
|
|
var failed_groups = [];
|
|
for (var group in groups) {
|
|
// count some totals
|
|
var group_passed = 0;
|
|
var group_failed = 0;
|
|
var group_es5_passed = 0;
|
|
var group_es5_failed = 0;
|
|
var group_es6_passed = 0;
|
|
var group_es6_failed = 0;
|
|
var groupName = _path2.default.relative(_path2.default.join(__dirname, "..", "..", "test"), group);
|
|
var msg = "";
|
|
var errmsg = "";
|
|
msg += groupName + ": ";
|
|
var _iteratorNormalCompletion2 = true;
|
|
var _didIteratorError2 = false;
|
|
var _iteratorError2 = undefined;
|
|
|
|
try {
|
|
for (var _iterator2 = groups[group][Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
|
var t = _step2.value;
|
|
|
|
var testName = _path2.default.relative(group, t.test.location);
|
|
var all_passed = true;
|
|
var was_skipped = true;
|
|
var _iteratorNormalCompletion3 = true;
|
|
var _didIteratorError3 = false;
|
|
var _iteratorError3 = undefined;
|
|
|
|
try {
|
|
for (var _iterator3 = t.result[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
|
var testResult = _step3.value;
|
|
|
|
was_skipped = false;
|
|
if (!testResult.passed) {
|
|
all_passed = false;
|
|
if (args.verbose) {
|
|
errmsg += create_test_message(testName, testResult.passed, testResult.err, t.test.isES6, testResult.strict) + EOL;
|
|
}
|
|
if (testResult.err && testResult.err.message === "Timed out") {
|
|
numTimeouts++;
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError3 = true;
|
|
_iteratorError3 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion3 && _iterator3.return) {
|
|
_iterator3.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError3) {
|
|
throw _iteratorError3;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (was_skipped) {
|
|
numSkipped++;
|
|
} else if (all_passed) {
|
|
group_passed++;
|
|
if (t.test.isES6) {
|
|
group_es6_passed++;
|
|
} else {
|
|
group_es5_passed++;
|
|
}
|
|
} else {
|
|
group_failed++;
|
|
if (t.test.isES6) {
|
|
group_es6_failed++;
|
|
} else {
|
|
group_es5_failed++;
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError2 = true;
|
|
_iteratorError2 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
|
_iterator2.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError2) {
|
|
throw _iteratorError2;
|
|
}
|
|
}
|
|
}
|
|
|
|
msg += "Passed: " + group_passed + " / " + (group_passed + group_failed) + " " + ("(" + toPercentage(group_passed, group_passed + group_failed) + "%) ") + _chalk2.default.yellow("(es5)") + (": " + group_es5_passed + " / ") + (group_es5_passed + group_es5_failed + " ") + ("(" + toPercentage(group_es5_passed, group_es5_passed + group_es5_failed) + "%) ") + _chalk2.default.yellow("(es6)") + (": " + group_es6_passed + " / ") + (group_es6_passed + group_es6_failed + " ") + ("(" + toPercentage(group_es6_passed, group_es6_passed + group_es6_failed) + "%)");
|
|
if (args.verbose) {
|
|
console.log(msg);
|
|
if (errmsg) {
|
|
console.error(errmsg);
|
|
}
|
|
}
|
|
if (group_es5_failed + group_es6_failed > 0) {
|
|
failed_groups.push(msg);
|
|
}
|
|
|
|
numPassed += group_passed;
|
|
numPassedES5 += group_es5_passed;
|
|
numPassedES6 += group_es6_passed;
|
|
numFailed += group_failed;
|
|
numFailedES5 += group_es5_failed;
|
|
numFailedES6 += group_es6_failed;
|
|
}
|
|
var status = "=== RESULTS ===" + EOL + ("Passes: " + numPassed + " / " + (numPassed + numFailed) + " ") + ("(" + toPercentage(numPassed, numPassed + numFailed) + "%)") + EOL + ("ES5 passes: " + numPassedES5 + " / " + (numPassedES5 + numFailedES5) + " ") + ("(" + toPercentage(numPassedES5, numPassedES5 + numFailedES5) + "%) ") + EOL + ("ES6 passes: " + numPassedES6 + " / " + (numPassedES6 + numFailedES6) + " ") + ("(" + toPercentage(numPassedES6, numPassedES6 + numFailedES6) + "%)") + EOL + ("Skipped: " + numSkipped) + EOL + ("Timeouts: " + numTimeouts) + EOL;
|
|
console.log(status);
|
|
if (failed_groups.length !== 0) {
|
|
console.log("Groups with failures:");
|
|
var _iteratorNormalCompletion4 = true;
|
|
var _didIteratorError4 = false;
|
|
var _iteratorError4 = undefined;
|
|
|
|
try {
|
|
for (var _iterator4 = failed_groups[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
|
|
var groupMessage = _step4.value;
|
|
|
|
console.log(groupMessage);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError4 = true;
|
|
_iteratorError4 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion4 && _iterator4.return) {
|
|
_iterator4.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError4) {
|
|
throw _iteratorError4;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (args.statusFile) {
|
|
_gracefulFs2.default.writeFileSync(args.statusFile, status);
|
|
}
|
|
|
|
// exit status
|
|
if (!args.filterString && (numPassedES5 < 11738 || numPassedES6 < 3981 || numTimeouts > 0)) {
|
|
console.error(_chalk2.default.red("Overall failure. Expected more tests to pass!"));
|
|
return 1;
|
|
} else {
|
|
// use 0 to avoid the npm error messages
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
function getProgressBar(currentTestLength, originalTestLength, granularity) {
|
|
if (currentTestLength % granularity === 0 && currentTestLength !== 0) {
|
|
// print out a percent of tests completed to keep the user informed
|
|
return "Running... " + toPercentage(originalTestLength - currentTestLength, originalTestLength) + "%";
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
// Returns false if test processing should stop.
|
|
function handleTestResults(groups, test, testResults) {
|
|
// test results are in, add it to its corresponding group
|
|
if (!(test.groupName in groups)) {
|
|
groups[test.groupName] = [];
|
|
}
|
|
groups[test.groupName].push({ test: test, result: testResults });
|
|
return true;
|
|
}
|
|
|
|
// $FlowFixMe cluster.Worker is marked as not exported by the node API by flow.
|
|
function killWorkers(workers) {
|
|
for (var workerID in workers) {
|
|
workers[workerID].kill();
|
|
}
|
|
}
|
|
|
|
function toPercentage(x, total) {
|
|
if (total === 0) {
|
|
return 100;
|
|
}
|
|
return Math.floor(x / total * 100);
|
|
}
|
|
|
|
function create_test_message(name, success, err, isES6, isStrict) {
|
|
var checkmark = _chalk2.default.green("\u2713");
|
|
var xmark = _chalk2.default.red("\u2717");
|
|
var msg = "\t";
|
|
msg += (success ? checkmark : xmark) + " ";
|
|
msg += "" + (isES6 ? _chalk2.default.yellow("(es6) ") : "") + (isStrict ? "(strict)" : "(nostrict)") + ": " + name;
|
|
if (!success) {
|
|
(0, _invariant2.default)(err, "Error must be non null if success is false");
|
|
if (err.message) {
|
|
// split the message by newline, add tabs, and join
|
|
var parts = err.message.split(EOL);
|
|
var _iteratorNormalCompletion5 = true;
|
|
var _didIteratorError5 = false;
|
|
var _iteratorError5 = undefined;
|
|
|
|
try {
|
|
for (var _iterator5 = parts[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
|
|
var line = _step5.value;
|
|
|
|
msg += EOL + ("\t\t" + line);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError5 = true;
|
|
_iteratorError5 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion5 && _iterator5.return) {
|
|
_iterator5.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError5) {
|
|
throw _iteratorError5;
|
|
}
|
|
}
|
|
}
|
|
|
|
msg += EOL;
|
|
} else if (err.stack) {
|
|
msg += JSON.stringify(err.stack);
|
|
}
|
|
}
|
|
return msg;
|
|
}
|
|
|
|
function getHarnesses(relativeTestPath) {
|
|
var harnessPath = "" + __dirname + relativeTestPath + "/harness";
|
|
var harnessesList = getFilesSync(harnessPath);
|
|
// convert to a mapping from harness name to file contents
|
|
var harnesses = {};
|
|
var _iteratorNormalCompletion6 = true;
|
|
var _didIteratorError6 = false;
|
|
var _iteratorError6 = undefined;
|
|
|
|
try {
|
|
for (var _iterator6 = harnessesList[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
|
|
var harness = _step6.value;
|
|
|
|
// sync is fine, it's an initialization stage and there's not that many
|
|
// harnesses
|
|
harnesses[_path2.default.basename(harness.location)] = _gracefulFs2.default.readFileSync(harness.location).toString();
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError6 = true;
|
|
_iteratorError6 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion6 && _iterator6.return) {
|
|
_iterator6.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError6) {
|
|
throw _iteratorError6;
|
|
}
|
|
}
|
|
}
|
|
|
|
return harnesses;
|
|
}
|
|
|
|
function workerRun(args) {
|
|
// NOTE: all harnesses (including contents of harness files) need to be
|
|
// used on workers. It needs to either be read from the fs once and
|
|
// distributed via IPC or once from each process. This is the
|
|
// "once from each process" approach.
|
|
// get all the harnesses
|
|
var harnesses = getHarnesses(args.relativeTestPath);
|
|
// we're a worker, run a portion of the tests
|
|
_process2.default.on("message", function (message) {
|
|
switch (message.type) {
|
|
case TestTask.sentinel:
|
|
// begin executing this TestTask
|
|
var task = TestTask.fromObject(message);
|
|
handleTest(task.file, harnesses, args.timeout, function (err, results) {
|
|
handleTestResultsMultiProcess(err, task.file, results);
|
|
});
|
|
break;
|
|
case QuitMessage.sentinel:
|
|
_process2.default.exit(0);
|
|
break;
|
|
default:
|
|
throw new Error("Worker #" + _process2.default.pid + " got an unexpected message:\n " + JSON.stringify(message));
|
|
}
|
|
});
|
|
}
|
|
|
|
function handleTestResultsMultiProcess(err, test, testResults) {
|
|
if (err) {
|
|
_process2.default.send(new ErrorMessage(err));
|
|
} else {
|
|
var msg = new DoneMessage(test);
|
|
var _iteratorNormalCompletion7 = true;
|
|
var _didIteratorError7 = false;
|
|
var _iteratorError7 = undefined;
|
|
|
|
try {
|
|
for (var _iterator7 = testResults[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
|
|
var t = _step7.value;
|
|
|
|
msg.testResults.push(t);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError7 = true;
|
|
_iteratorError7 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion7 && _iterator7.return) {
|
|
_iterator7.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError7) {
|
|
throw _iteratorError7;
|
|
}
|
|
}
|
|
}
|
|
|
|
try {
|
|
_process2.default.send(msg);
|
|
} catch (jsonCircularSerializationErr) {
|
|
// JSON circular serialization, ThrowCompletion is too deep to be
|
|
// serialized!
|
|
// Solution, truncate the "err" field if this happens
|
|
var _iteratorNormalCompletion8 = true;
|
|
var _didIteratorError8 = false;
|
|
var _iteratorError8 = undefined;
|
|
|
|
try {
|
|
for (var _iterator8 = msg.testResults[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) {
|
|
var _t = _step8.value;
|
|
|
|
if (_t.err) {
|
|
_t.err = new Error(_t.err.message);
|
|
}
|
|
}
|
|
// now try again
|
|
} catch (err) {
|
|
_didIteratorError8 = true;
|
|
_iteratorError8 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion8 && _iterator8.return) {
|
|
_iterator8.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError8) {
|
|
throw _iteratorError8;
|
|
}
|
|
}
|
|
}
|
|
|
|
_process2.default.send(msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
function handleTest(test, harnesses, timeout, cb) {
|
|
prepareTest(test, testFilterByContents, function (err, banners, testFileContents) {
|
|
if (err != null) {
|
|
cb(err, []);
|
|
return;
|
|
}
|
|
if (!banners) {
|
|
// skip this test
|
|
cb(null, []);
|
|
} else {
|
|
(0, _invariant2.default)(testFileContents, "testFileContents should not be null if banners are not None");
|
|
// filter out by flags, features, and includes
|
|
var keepThisTest = filterFeatures(banners) && filterFlags(banners) && filterIncludes(banners) && filterDescription(banners) && filterCircleCI(banners) && filterSneakyGenerators(banners, testFileContents);
|
|
var _testResults = [];
|
|
if (keepThisTest) {
|
|
// now run the test
|
|
_testResults = runTestWithStrictness(test, testFileContents, banners, harnesses, timeout);
|
|
}
|
|
cb(null, _testResults);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* FIXME: this code is unsound in the presence of ENOENT (file not found)
|
|
* This function returns nested arrays of all the file names. It can be
|
|
* flattened at the call site, but the type hint is incorrect.
|
|
* DON'T USE THIS FUNCTION until it is fixed to behave exactly like getFilesSync
|
|
*/
|
|
/*
|
|
function getFiles(
|
|
filepath: string,
|
|
): Promise<TestFileInfo[]> {
|
|
return new Promise((resolve, reject) => {
|
|
fs.stat(filepath, (err, stat) => {
|
|
if (err !== null) {
|
|
reject(err);
|
|
} else {
|
|
if (stat.isFile()) {
|
|
// return an array of size 1
|
|
resolve([new TestFileInfo(filepath)]);
|
|
} else if (stat.isDirectory()) {
|
|
// recurse on its children
|
|
fs.readdir(filepath, (err, files) => {
|
|
if (err !== null) {
|
|
reject(err);
|
|
} else {
|
|
// FIXME flattening bug
|
|
// tmp is Promise<TestFileInfo[]>[] (array of promises of arrays)
|
|
// want to flatten that into Promise<TestFileInfo[]> where each
|
|
// promise is added to a single array
|
|
let tmp = files.map(f => getFiles(path.join(filepath, f)));
|
|
resolve(Promise.all(tmp));
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
*/
|
|
|
|
/**
|
|
* getFilesSync returns a TestFileInfo for each file that is underneath the
|
|
* directory ${filepath}. If ${filepath} is just a file, then it returns an
|
|
* array of size 1.
|
|
* This function synchronously fetches from the filesystem, as such it should
|
|
* only be used in initialization code that only runs once.
|
|
*/
|
|
function getFilesSync(filepath) {
|
|
var stat = _gracefulFs2.default.statSync(filepath);
|
|
if (stat.isFile()) {
|
|
return [new TestFileInfo(filepath, false)];
|
|
} else if (stat.isDirectory()) {
|
|
var subFiles = _gracefulFs2.default.readdirSync(filepath);
|
|
return flatten(subFiles.map(function (f) {
|
|
return getFilesSync(_path2.default.join(filepath, f));
|
|
}));
|
|
} else {
|
|
throw new Error("That type of file is not supported");
|
|
}
|
|
}
|
|
|
|
function flatten(arr) {
|
|
return arr.reduce(function (a, b) {
|
|
return a.concat(b);
|
|
}, []);
|
|
}
|
|
|
|
/**
|
|
* prepareTest opens the file corresponding to ${test} and calls ${cb} on the
|
|
* results, expect the ones for which ${filterFn} returns false.
|
|
* The value passed to ${cb} will be an error if the file could not be read,
|
|
* or the banner data for the test if successful.
|
|
* NOTE: if the test file contents match the filter function given, ${cb} will
|
|
* not be called for that test.
|
|
*/
|
|
function prepareTest(test, filterFn, cb) {
|
|
_gracefulFs2.default.readFile(test.location, function (err, contents) {
|
|
if (err != null) {
|
|
cb(err, null, null);
|
|
} else {
|
|
var contentsStr = contents.toString();
|
|
// check if this test should be filtered
|
|
if (!filterFn(test, contentsStr)) {
|
|
// skip this test
|
|
cb(null, null, null);
|
|
} else {
|
|
try {
|
|
var banners = getBanners(test, contentsStr);
|
|
cb(null, banners, contentsStr);
|
|
} catch (bannerParseErr) {
|
|
cb(bannerParseErr, null, null);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function createRealm(timeout) {
|
|
// Create a new realm.
|
|
var realm = (0, _construct_realm2.default)({
|
|
strictlyMonotonicDateNow: true,
|
|
timeout: timeout * 1000
|
|
});
|
|
(0, _globals2.default)(realm);
|
|
var executionContext = new _realm.ExecutionContext();
|
|
executionContext.realm = realm;
|
|
realm.pushContext(executionContext);
|
|
|
|
// Create the Host-Defined functions.
|
|
var $ = new _index.ObjectValue(realm);
|
|
|
|
$.defineNativeMethod("createRealm", 0, function (context) {
|
|
return createRealm(timeout).$;
|
|
});
|
|
|
|
$.defineNativeMethod("detachArrayBuffer", 1, function (context, _ref) {
|
|
var _ref2 = _slicedToArray(_ref, 1),
|
|
buffer = _ref2[0];
|
|
|
|
return (0, _arraybuffer.DetachArrayBuffer)(realm, buffer);
|
|
});
|
|
|
|
$.defineNativeMethod("evalScript", 1, function (context, _ref3) {
|
|
var _ref4 = _slicedToArray(_ref3, 1),
|
|
sourceText = _ref4[0];
|
|
|
|
// TODO: eval
|
|
return realm.intrinsics.undefined;
|
|
});
|
|
|
|
$.defineNativeProperty("global", realm.$GlobalObject);
|
|
|
|
var glob = realm.$GlobalObject;
|
|
glob.defineNativeProperty("$262", $);
|
|
glob.defineNativeMethod("print", 1, function (context, _ref5) {
|
|
var _ref6 = _slicedToArray(_ref5, 1),
|
|
arg = _ref6[0];
|
|
|
|
return realm.intrinsics.undefined;
|
|
});
|
|
|
|
return { realm: realm, $: $ };
|
|
}
|
|
|
|
/**
|
|
* runTest executes the test given by ${test} whose contents are
|
|
* ${testFileContents}.
|
|
* It returns None if the test should is skipped, otherwise it returns a
|
|
* TestResult.
|
|
*/
|
|
function runTest(test, testFileContents, data,
|
|
// eslint-disable-next-line flowtype/no-weak-types
|
|
harnesses, strict, timeout) {
|
|
var _createRealm = createRealm(timeout),
|
|
realm = _createRealm.realm;
|
|
|
|
// Run the test.
|
|
|
|
|
|
try {
|
|
try {
|
|
// execute the harnesss first
|
|
var _iteratorNormalCompletion9 = true;
|
|
var _didIteratorError9 = false;
|
|
var _iteratorError9 = undefined;
|
|
|
|
try {
|
|
for (var _iterator9 = ["sta.js", "assert.js"].concat(data.includes || [])[Symbol.iterator](), _step9; !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) {
|
|
var name = _step9.value;
|
|
|
|
var harness = harnesses[name];
|
|
var _completion = realm.$GlobalEnv.execute(harness, name);
|
|
if (_completion instanceof _completions.ThrowCompletion) throw _completion;
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError9 = true;
|
|
_iteratorError9 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion9 && _iterator9.return) {
|
|
_iterator9.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError9) {
|
|
throw _iteratorError9;
|
|
}
|
|
}
|
|
}
|
|
|
|
var completion = realm.$GlobalEnv.execute((strict ? '"use strict";' + EOL : "") + testFileContents, test.location);
|
|
if (completion instanceof _completions.ThrowCompletion) throw completion;
|
|
if (completion instanceof _completions.AbruptCompletion) return new TestResult(false, strict, new Error("Unexpected abrupt completion"));
|
|
} catch (err) {
|
|
if (err.message === "Timed out") return new TestResult(false, strict, err);
|
|
if (!data.negative || data.negative !== err.name) {
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
if (data.negative.type) {
|
|
throw new Error("Was supposed to error with type " + data.negative.type + " but passed");
|
|
}
|
|
|
|
// succeeded
|
|
return new TestResult(true, strict);
|
|
} catch (err) {
|
|
if (err.value && err.value.$Prototype && err.value.$Prototype.intrinsicName === "SyntaxError.prototype") {
|
|
return null;
|
|
}
|
|
|
|
var stack = err.stack;
|
|
if (data.negative.type) {
|
|
var type = data.negative.type;
|
|
if (err && err instanceof _completions.ThrowCompletion && (0, _get.Get)(realm, err.value, "name").value === type) {
|
|
// Expected an error and got one.
|
|
return new TestResult(true, strict);
|
|
} else {
|
|
// Expected an error, but got something else.
|
|
if (err && err instanceof _completions.ThrowCompletion) {
|
|
return new TestResult(false, strict, err);
|
|
} else {
|
|
return new TestResult(false, strict, new Error("Expected an error, but got something else: " + err.message));
|
|
}
|
|
}
|
|
} else {
|
|
// Not expecting an error, but got one.
|
|
try {
|
|
if (err && err instanceof _completions.ThrowCompletion) {
|
|
var interpreterStack = void 0;
|
|
|
|
if (err.value instanceof _index.ObjectValue) {
|
|
if (err.value.$HasProperty("stack")) {
|
|
interpreterStack = _singletons.To.ToStringPartial(realm, (0, _get.Get)(realm, err.value, "stack"));
|
|
} else {
|
|
interpreterStack = _singletons.To.ToStringPartial(realm, (0, _get.Get)(realm, err.value, "message"));
|
|
}
|
|
// filter out if the error stack is due to async
|
|
if (interpreterStack.includes("async ")) {
|
|
return null;
|
|
}
|
|
} else if (err.value instanceof _index.StringValue) {
|
|
interpreterStack = err.value.value;
|
|
if (interpreterStack === "only plain identifiers are supported in parameter lists") {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Many strict-only tests involving eval check if certain SyntaxErrors are thrown.
|
|
// Some of those would require changes to Babel to support properly, and some we should handle ourselves in Prepack some day.
|
|
// But for now, ignore.
|
|
if (testFileContents.includes("eval(") && strict) {
|
|
return null;
|
|
}
|
|
|
|
if (interpreterStack) {
|
|
stack = "Interpreter: " + interpreterStack + EOL + "Native: " + err.nativeStack;
|
|
}
|
|
}
|
|
} catch (_err) {
|
|
stack = _err.stack;
|
|
}
|
|
|
|
return new TestResult(false, strict, new Error("Got an error, but was not expecting one:" + EOL + stack));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns true if ${test} should be run, false otherwise
|
|
*/
|
|
function testFilterByMetadata(test) {
|
|
// filter hidden files
|
|
if (_path2.default.basename(test.location)[0] === ".") return false;
|
|
|
|
// emacs!
|
|
if (test.location.includes("~")) return false;
|
|
|
|
// SIMD isn't in JS yet
|
|
if (test.location.includes("Simd")) return false;
|
|
|
|
// temporarily disable intl402 tests (ES5)
|
|
if (test.location.includes("intl402") && !test.location.includes("/Date/prototype/to")) {
|
|
return false;
|
|
}
|
|
|
|
// temporarily disable tests which use realm.
|
|
if (test.location.includes("realm")) return false;
|
|
|
|
// temporarily disable tests which use with. (??)
|
|
if (test.location.includes("/with/")) return false;
|
|
|
|
// disable tests which use Atomics
|
|
if (test.location.includes("/Atomics/")) return false;
|
|
|
|
// disable tests which use generators
|
|
if (test.location.includes("/generators/")) return false;
|
|
if (test.location.includes("/yield/")) return false;
|
|
|
|
// disable tests which use modules
|
|
if (test.location.includes("/module-code/")) return false;
|
|
|
|
// disable browser specific tests
|
|
if (test.location.includes("/annexB/")) return false;
|
|
|
|
// disable tail-call optimization tests
|
|
if (test.location.includes("tco")) return false;
|
|
|
|
// disable nasty unicode tests.
|
|
if (test.location.includes("U180") || test.location.includes("u180") || test.location.includes("mongolian")) return false;
|
|
|
|
// disable function toString tests.
|
|
if (test.location.includes("Function/prototype/toString")) return false;
|
|
|
|
// disable tests that check for detached-buffer-after-toindex
|
|
if (test.location.includes("detached-buffer-after-toindex")) return false;
|
|
|
|
// disable tests to check for detatched-buffer during iteration
|
|
if (test.location.includes("detach-typedarray-in-progress.js")) return false;
|
|
|
|
// disable broken RegExp tests
|
|
if (test.location.includes("RegExp/S15.10.2.12_A1_T1.js")) return false;
|
|
if (test.location.includes("RegExp/S15.10.2.12_A2_T1.js")) return false;
|
|
if (test.location.includes("RegExp/prototype/Symbol.search/lastindex-no-restore")) return false;
|
|
if (test.location.includes("RegExp/prototype/exec/failure-lastindex-no-access.js")) return false;
|
|
if (test.location.includes("RegExp/prototype/exec/success-lastindex-no-access.js")) return false;
|
|
|
|
// disable RegExp tests that use extended unicode
|
|
if (test.location.includes("Symbol.match/builtin-success-u-return-val-groups")) return false;
|
|
|
|
// disable SharedArrayBuffer tests
|
|
if (test.location.includes("sharedarraybuffer") || test.location.includes("SharedArrayBuffer")) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
function testFilterByContents(test, testFileContents) {
|
|
// ES6 tests (can only be verified by contents, not by metadata)
|
|
var is_es6 = testFileContents.includes(EOL + "es6id: ");
|
|
test.isES6 = is_es6;
|
|
|
|
// Ignore phase: early tests because those are errors that babel should catch
|
|
// not issues related to Prepack
|
|
var phase_early = testFileContents.indexOf(" phase: early");
|
|
var end_of_comment = testFileContents.indexOf("---*/");
|
|
if (phase_early > 0 && phase_early < end_of_comment) return false;
|
|
|
|
var esid_pending = testFileContents.indexOf("esid: pending");
|
|
if (esid_pending > 0 && esid_pending < end_of_comment) return false;
|
|
|
|
// disable tests that require parser to throw SyntaxError in strict Mode
|
|
if (test.location.includes("/directive-prologue/") && testFileContents.includes("assert.throws(SyntaxError,")) return false;
|
|
|
|
// disable SharedArrayBuffer tests
|
|
if (testFileContents.includes("SharedArrayBuffer")) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
function filterFlags(data) {
|
|
return !data.flags.includes("async");
|
|
}
|
|
|
|
function filterFeatures(data) {
|
|
var features = data.features;
|
|
if (features.includes("default-parameters")) return false;
|
|
if (features.includes("generators")) return false;
|
|
if (features.includes("generator")) return false;
|
|
if (features.includes("BigInt")) return false;
|
|
if (features.includes("class-fields")) return false;
|
|
if (features.includes("async-iteration")) return false;
|
|
if (features.includes("Function.prototype.toString")) return false;
|
|
if (features.includes("SharedArrayBuffer")) return false;
|
|
if (features.includes("cross-realm")) return false;
|
|
if (features.includes("atomics")) return false;
|
|
if (features.includes("u180e")) return false;
|
|
if (features.includes("Symbol.isConcatSpreadable")) return false;
|
|
if (features.includes("destructuring-binding")) return false;
|
|
if (features.includes("IsHTMLDDA")) return false;
|
|
if (features.includes("regexp-unicode-property-escapes")) return false;
|
|
if (features.includes("regexp-named-groups")) return false;
|
|
if (features.includes("regexp-lookbehind")) return false;
|
|
if (features.includes("regexp-dotall")) return false;
|
|
if (features.includes("optional-catch-binding")) return false;
|
|
if (features.includes("Symbol.asyncIterator")) return false;
|
|
if (features.includes("Promise.prototype.finally")) return false;
|
|
return true;
|
|
}
|
|
|
|
function filterIncludes(data) {
|
|
// disable tail call optimization tests.
|
|
return !data.includes.includes("tco-helper.js");
|
|
}
|
|
|
|
function filterDescription(data) {
|
|
// For now, "Complex tests" is used in the description of some
|
|
// encode/decodeURI tests to indicate that they are long running.
|
|
// Filter these
|
|
return !data.description.includes("Complex tests") && !data.description.includes("iterating") && !data.description.includes("iterable");
|
|
}
|
|
|
|
function filterCircleCI(data) {
|
|
var skipTests = ["7.8.5_A1.4_T2", "7.8.5_A2.4_T2", "7.8.5_A2.1_T2", "7.8.5_A1.1_T2", "15.1.2.2_A8", "15.1.2.3_A6", "7.4_A5", "7.4_A6", "15.10.2.12_A3_T1", "15.10.2.12_A4_T1", "15.10.2.12_A5_T1", "15.10.2.12_A6_T1"];
|
|
var skipTests6 = ["22.1.3.1_3"];
|
|
|
|
return !!_process2.default.env.NIGHTLY_BUILD || skipTests.indexOf(data.es5id) < 0 && skipTests6.indexOf(data.es6id) < 0;
|
|
}
|
|
|
|
function filterSneakyGenerators(data, testFileContents) {
|
|
// There are some sneaky tests that use generators but are not labeled with
|
|
// the "generators" or "generator" feature tag. Here we use a simple heuristic
|
|
// to filter out tests with sneaky generators.
|
|
if (data.features.includes("destructuring-binding")) {
|
|
return !testFileContents.includes("function*") && !testFileContents.includes("*method");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Run a given ${test} whose file contents are ${testFileContents} and return
|
|
* a list of results, one for each strictness level (strict or not).
|
|
* If the list's length is less than 2, than the missing tests were skipped.
|
|
*/
|
|
function runTestWithStrictness(test, testFileContents, data,
|
|
// eslint-disable-next-line flowtype/no-weak-types
|
|
harnesses, timeout) {
|
|
var fn = function fn(strict) {
|
|
return runTest(test, testFileContents, data, harnesses, strict, timeout);
|
|
};
|
|
if (data.flags.includes("onlyStrict")) {
|
|
if (testFileContents.includes("assert.throws(SyntaxError")) return [];
|
|
var _result = fn(true);
|
|
return _result ? [_result] : [];
|
|
} else if (data.flags.includes("noStrict") || test.location.includes("global/global-object.js")) {
|
|
if (testFileContents.includes('"use strict";') && testFileContents.includes("assert.throws(SyntaxError")) return [];
|
|
var _result2 = fn(false);
|
|
return _result2 ? [_result2] : [];
|
|
} else {
|
|
// run both strict and non-strict
|
|
var strictResult = fn(true);
|
|
var unStrictResult = fn(false);
|
|
var finalResult = [];
|
|
if (strictResult) {
|
|
finalResult.push(strictResult);
|
|
}
|
|
if (unStrictResult) {
|
|
finalResult.push(unStrictResult);
|
|
}
|
|
return finalResult;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parses the banners, and returns the banners as arbitrary object data if they
|
|
* were found, or returns an error if the banner it couldn't be parsed.
|
|
*/
|
|
function getBanners(test, fileContents) {
|
|
var banners = fileContents.match(/---[\s\S]+---/);
|
|
var data = {};
|
|
if (banners) {
|
|
var bannerText = banners[0] || "";
|
|
if (bannerText.includes("StrictMode")) {
|
|
if (bannerText.includes("'arguments'")) return null;
|
|
if (bannerText.includes("'caller'")) return null;
|
|
} else if (bannerText.includes('properties "caller" or "arguments"')) {
|
|
return null;
|
|
} else if (bannerText.includes("function caller")) {
|
|
return null;
|
|
} else if (bannerText.includes("attribute of 'caller' property")) {
|
|
return null;
|
|
} else if (bannerText.includes("attribute of 'arguments'")) {
|
|
return null;
|
|
} else if (bannerText.includes("poisoned")) return null;
|
|
try {
|
|
data = _jsYaml2.default.safeLoad(banners[0].slice(3, -3));
|
|
} catch (e) {
|
|
// Some versions of test262 have comments inside of yaml banners.
|
|
// parsing these will usually fail.
|
|
return null;
|
|
}
|
|
}
|
|
return BannerData.fromObject(data);
|
|
}
|
|
//# sourceMappingURL=test262-runner.js.map
|