MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1import { test, testDeep, summary } from './helpers.js';
2import {
3 Worker,
4 MessageChannel,
5 isMainThread,
6 parentPort,
7 workerData,
8 receiveMessageOnPort,
9 setEnvironmentData,
10 getEnvironmentData,
11 moveMessagePortToContext
12} from 'node:worker_threads';
13
14console.log('Worker Threads Tests\n');
15
16async function run() {
17 test('isMainThread in main context', isMainThread, true);
18 test('parentPort is null in main context', parentPort, null);
19 test('workerData is undefined in main context', workerData, undefined);
20
21 const ch1 = new MessageChannel();
22 test('MessageChannel has port1', typeof ch1.port1, 'object');
23 test('MessageChannel has port2', typeof ch1.port2, 'object');
24
25 ch1.port1.postMessage({ hello: 'queue' });
26 const queued = receiveMessageOnPort(ch1.port2);
27 testDeep('receiveMessageOnPort returns message envelope', queued, { message: { hello: 'queue' } });
28 test('receiveMessageOnPort returns undefined when empty', receiveMessageOnPort(ch1.port2), undefined);
29
30 const ch2 = new MessageChannel();
31 const seen = [];
32 ch2.port1.postMessage('queued-before-listener');
33 ch2.port2.on('message', msg => {
34 seen.push(`on:${msg}`);
35 });
36 ch2.port2.once('message', msg => {
37 seen.push(`once:${msg}`);
38 });
39 ch2.port1.postMessage('next');
40 ch2.port1.postMessage('final');
41 testDeep('MessagePort queue drains in FIFO order', seen, ['on:queued-before-listener', 'on:next', 'once:next', 'on:final']);
42
43 let onmessageData = undefined;
44 ch2.port2.onmessage = event => {
45 onmessageData = event.data;
46 };
47 ch2.port1.postMessage('event-data');
48 test('MessagePort onmessage receives event.data', onmessageData, 'event-data');
49
50 const moved = moveMessagePortToContext(ch2.port1, globalThis);
51 test('moveMessagePortToContext returns same port', moved, ch2.port1);
52
53 const shared = { answer: 42, ok: true };
54 setEnvironmentData('shared-key', shared);
55 testDeep('getEnvironmentData in main context', getEnvironmentData('shared-key'), shared);
56
57 const workerUrl = new URL('./worker_threads.worker.mjs', import.meta.url);
58 const payload = { hello: 'world', n: 42, arr: [1, 2, 3] };
59
60 const worker = new Worker(workerUrl, { workerData: payload });
61 test('Worker constructor returns object', typeof worker, 'object');
62 test('Worker has once method', typeof worker.once, 'function');
63 test('Worker has terminate method', typeof worker.terminate, 'function');
64
65 worker.unref();
66 worker.ref();
67
68 const message = await new Promise((resolve, reject) => {
69 worker.once('message', resolve);
70 worker.once('exit', code => {
71 if (code !== 0) reject(new Error(`worker exited early with code ${code}`));
72 });
73 });
74
75 test('worker replied success flag', message.ok, true);
76 test('worker isMainThread false in worker context', message.isMainThread, false);
77 test('worker has parentPort in worker context', message.hasParentPort, true);
78 testDeep('workerData round-trip', message.workerData, payload);
79 testDeep('worker getEnvironmentData snapshot', message.environmentData, shared);
80
81 const exitCode = await worker.terminate();
82 test('terminate resolves to numeric exit code', typeof exitCode, 'number');
83
84 summary();
85}
86
87void run();