worker-testbed
Version:
The AI-ready testbed which use Worker Threads to create isolated vm context
102 lines (98 loc) • 3.22 kB
JavaScript
;
var worker_threads = require('worker_threads');
var url = require('url');
var functoolsKit = require('functools-kit');
var tape = require('tape');
const workerFileSubject = new functoolsKit.BehaviorSubject();
const finishSubject = new functoolsKit.Subject();
let testRegistry = new functoolsKit.ToolRegistry("workertest");
let testCounter = 0;
const waitForFile = async () => {
if (workerFileSubject.data) {
return workerFileSubject.data;
}
return await workerFileSubject.toPromise();
};
class TestWrapper {
testName;
cb;
constructor(testName, cb) {
this.testName = testName;
this.cb = cb;
}
}
const test = async (testName, cb) => {
if (!worker_threads.isMainThread) {
testRegistry = testRegistry.register(testName, new TestWrapper(testName, cb));
return;
}
const workerFile = await waitForFile();
testCounter += 1;
tape(testName, async (test) => {
const [awaiter, { resolve }] = functoolsKit.createAwaiter();
let isFinished = false;
const worker = new worker_threads.Worker(workerFile, {
workerData: { testName },
});
worker.once("message", async ({ status, msg }) => {
if (status === "pass") {
test.pass(msg);
}
else if (status === "fail") {
test.fail(msg);
await functoolsKit.sleep(100);
}
isFinished = true;
worker.terminate();
resolve();
});
worker.on("error", async (err) => {
test.fail(`Worker error: ${functoolsKit.getErrorMessage(err)}`);
await functoolsKit.sleep(100);
resolve();
});
worker.on("exit", async (code) => {
if (isFinished) {
return;
}
if (code !== 0) {
test.fail(`Worker stopped with exit code ${code}`);
await functoolsKit.sleep(100);
resolve();
}
});
{
await awaiter;
testCounter -= 1;
await finishSubject.next();
}
});
};
const run = functoolsKit.singleshot(async (__filename, cb = () => { }) => {
if (worker_threads.isMainThread) {
await workerFileSubject.next(url.fileURLToPath(__filename));
await finishSubject.filter(() => testCounter === 0).toPromise();
cb();
return;
}
if (!worker_threads.parentPort) {
throw new Error("workertest parentPort is null");
}
const finishTest = functoolsKit.singleshot((status, msg) => {
worker_threads.parentPort.postMessage({ status, msg });
process.exit(0);
});
process.on('uncaughtException', function (err) {
// fail current test in worker
finishTest("fail", `Uncaught exception: ${functoolsKit.getErrorMessage(err)}`);
});
process.on('unhandledRejection', (error) => {
throw error;
});
testRegistry.get(worker_threads.workerData.testName).cb({
pass: (msg) => finishTest("pass", msg),
fail: (msg) => finishTest("fail", msg),
});
});
exports.run = run;
exports.test = test;