Files
completion-viz/engine262/test/test262/test262.js
T

325 lines
8.6 KiB
JavaScript

'use strict';
try {
require('@snek/source-map-support/register');
} catch {}
const path = require('path');
const fs = require('fs');
const util = require('util');
const glob = require('glob');
const readList = (name) => {
const source = fs.readFileSync(path.resolve(__dirname, name), 'utf8');
return source.split('\n').filter((l) => l && !l.startsWith('#'));
};
const readListPaths = (name) => readList(name)
.flatMap((t) => glob.sync(path.resolve(__dirname, 'test262', 'test', t)))
.map((f) => path.relative(path.resolve(__dirname, 'test262'), f));
const disabledFeatures = [];
const featureMap = {};
readList('features')
.forEach((f) => {
if (f.startsWith('-')) {
disabledFeatures.push(f.slice(1));
}
if (f.includes('=')) {
const [k, v] = f.split('=');
featureMap[k.trim()] = v.trim();
}
});
if (!process.send) {
// supervisor
const childProcess = require('child_process');
const TestStream = require('test262-stream');
const {
pass,
fail,
skip,
total,
CPU_COUNT,
} = require('../base');
const override = process.argv.find((e, i) => i > 1 && !e.startsWith('-'));
const NUM_WORKERS = process.env.NUM_WORKERS
? Number.parseInt(process.env.NUM_WORKERS, 10)
: Math.round(CPU_COUNT * 0.75);
const RUN_SLOW_TESTS = process.argv.includes('--run-slow-tests');
const createWorker = () => {
const c = childProcess.fork(__filename);
c.on('message', (message) => {
const { description, status, error } = message;
switch (status) {
case 'PASS':
pass();
break;
case 'FAIL':
fail(description, error);
break;
case 'SKIP':
skip();
break;
default:
throw new RangeError(JSON.stringify(message));
}
});
c.on('exit', (code) => {
if (code !== 0) {
process.exit(1);
}
});
return c;
};
const workers = Array.from({ length: NUM_WORKERS }, () => createWorker());
let longRunningWorker;
if (RUN_SLOW_TESTS) {
longRunningWorker = createWorker();
}
const slowlist = readListPaths('slowlist');
const skiplist = readListPaths('skiplist');
const stream = new TestStream(path.resolve(__dirname, 'test262'), {
paths: [override || 'test'],
omitRuntime: true,
});
let workerIndex = 0;
stream.on('data', (test) => {
if (test.attrs.flags.module && test.scenario !== 'default') {
// test262-stream duplicates module tests, deduplicate here
return;
}
if (/annexB|intl402/.test(test.file)) {
return;
}
total();
if ((test.attrs.features && test.attrs.features.some((feature) => disabledFeatures.includes(feature)))
|| skiplist.includes(test.file)) {
skip();
return;
}
if (slowlist.includes(test.file)) {
if (RUN_SLOW_TESTS) {
longRunningWorker.send(test);
} else {
skip();
}
} else {
workers[workerIndex].send(test);
workerIndex += 1;
if (workerIndex >= workers.length) {
workerIndex = 0;
}
}
});
stream.on('end', () => {
workers.forEach((w) => {
w.send('DONE');
});
if (RUN_SLOW_TESTS) {
longRunningWorker.send('DONE');
}
});
} else {
// worker
const {
Agent,
setSurroundingAgent,
inspect,
Value,
IsCallable,
IsDataDescriptor,
Type,
AbruptCompletion,
Throw,
} = require('../..');
const { createRealm } = require('../../bin/test262_realm');
const isError = (type, value) => {
if (Type(value) !== 'Object') {
return false;
}
const proto = value.Prototype;
if (!proto || Type(proto) !== 'Object') {
return false;
}
const ctorDesc = proto.properties.get(new Value('constructor'));
if (!ctorDesc || !IsDataDescriptor(ctorDesc)) {
return false;
}
const ctor = ctorDesc.Value;
if (Type(ctor) !== 'Object' || IsCallable(ctor) !== Value.true) {
return false;
}
const namePropDesc = ctor.properties.get(new Value('name'));
if (!namePropDesc || !IsDataDescriptor(namePropDesc)) {
return false;
}
const nameProp = namePropDesc.Value;
return Type(nameProp) === 'String' && nameProp.stringValue() === type;
};
const includeCache = {};
const run = (test) => {
const features = [];
if (test.attrs.features) {
test.attrs.features.forEach((f) => {
if (featureMap[f]) {
features.push(featureMap[f]);
}
});
}
const agent = new Agent({
features,
});
setSurroundingAgent(agent);
const {
realm, trackedPromises,
resolverCache, setPrintHandle,
} = createRealm({ file: test.file });
const r = realm.scope(() => {
test.attrs.includes.unshift('assert.js', 'sta.js');
if (test.attrs.flags.async) {
test.attrs.includes.unshift('doneprintHandle.js');
}
for (const include of test.attrs.includes) {
if (includeCache[include] === undefined) {
const p = path.resolve(__dirname, `./test262/harness/${include}`);
includeCache[include] = {
source: fs.readFileSync(p, 'utf8'),
specifier: p,
};
}
const entry = includeCache[include];
const completion = realm.evaluateScript(entry.source, { specifier: entry.specifier });
if (completion instanceof AbruptCompletion) {
return { status: 'FAIL', error: inspect(completion) };
}
}
{
const completion = realm.evaluateScript(`\
var Test262Error = class Test262Error extends Error {};
function $DONE(error) {
if (error) {
if (typeof error === 'object' && error !== null && 'stack' in error) {
__consolePrintHandle__('Test262:AsyncTestFailure:' + error.stack);
} else {
__consolePrintHandle__('Test262:AsyncTestFailure:Test262Error: ' + error);
}
} else {
__consolePrintHandle__('Test262:AsyncTestComplete');
}
}`);
if (completion instanceof AbruptCompletion) {
return { status: 'FAIL', error: inspect(completion) };
}
}
let asyncResult;
if (test.attrs.flags.async) {
setPrintHandle((m) => {
if (m.stringValue && m.stringValue() === 'Test262:AsyncTestComplete') {
asyncResult = { status: 'PASS' };
} else {
asyncResult = { status: 'FAIL', error: m.stringValue ? m.stringValue() : inspect(m) };
}
setPrintHandle(undefined);
});
}
const specifier = path.resolve(__dirname, 'test262', test.file);
let completion;
if (test.attrs.flags.module) {
completion = realm.createSourceTextModule(specifier, test.contents);
if (!(completion instanceof AbruptCompletion)) {
const module = completion;
resolverCache.set(specifier, module);
completion = module.Link();
if (!(completion instanceof AbruptCompletion)) {
completion = module.Evaluate();
}
if (!(completion instanceof AbruptCompletion)) {
if (completion.PromiseState === 'rejected') {
completion = Throw(completion.PromiseResult);
}
}
}
} else {
completion = realm.evaluateScript(test.contents, { specifier });
}
if (completion instanceof AbruptCompletion) {
if (test.attrs.negative && isError(test.attrs.negative.type, completion.Value)) {
return { status: 'PASS' };
} else {
return { status: 'FAIL', error: inspect(completion) };
}
}
if (test.attrs.flags.async) {
if (!asyncResult) {
throw new Error('missing async result');
}
return asyncResult;
}
if (trackedPromises.length > 0) {
return { status: 'FAIL', error: inspect(trackedPromises[0]) };
}
if (test.attrs.negative) {
return { status: 'FAIL', error: `Expected ${test.attrs.negative.type} during ${test.attrs.negative.phase}` };
} else {
return { status: 'PASS' };
}
});
return r;
};
let p = Promise.resolve();
const handleSendError = (e) => {
if (e) {
process.exit(1);
}
};
process.on('message', (test) => {
if (test === 'DONE') {
p.then(() => process.exit(0));
p = undefined;
} else {
const description = `${test.file}\n${test.attrs.description}`;
p = p
.then(() => run(test))
.then((r) => {
process.send({ description, ...r }, handleSendError);
})
.catch((e) => {
process.send({ description, status: 'FAIL', error: util.inspect(e) }, handleSendError);
});
}
});
}