11cb0ef41Sopenharmony_ci'use strict'; 21cb0ef41Sopenharmony_ciconst common = require('../common'); 31cb0ef41Sopenharmony_ci 41cb0ef41Sopenharmony_cicommon.skipIfInspectorDisabled(); 51cb0ef41Sopenharmony_ci 61cb0ef41Sopenharmony_ciconst assert = require('assert'); 71cb0ef41Sopenharmony_ciconst EventEmitter = require('events'); 81cb0ef41Sopenharmony_ciconst { Session } = require('inspector'); 91cb0ef41Sopenharmony_ciconst { 101cb0ef41Sopenharmony_ci Worker, isMainThread, parentPort, workerData 111cb0ef41Sopenharmony_ci} = require('worker_threads'); 121cb0ef41Sopenharmony_ci 131cb0ef41Sopenharmony_ci 141cb0ef41Sopenharmony_ciconst workerMessage = 'This is a message from a worker'; 151cb0ef41Sopenharmony_ci 161cb0ef41Sopenharmony_cifunction waitForMessage() { 171cb0ef41Sopenharmony_ci return new Promise((resolve) => { 181cb0ef41Sopenharmony_ci parentPort.once('message', resolve); 191cb0ef41Sopenharmony_ci }); 201cb0ef41Sopenharmony_ci} 211cb0ef41Sopenharmony_ci 221cb0ef41Sopenharmony_ci// This is at the top so line numbers change less often 231cb0ef41Sopenharmony_ciif (!isMainThread) { 241cb0ef41Sopenharmony_ci if (workerData === 1) { 251cb0ef41Sopenharmony_ci console.log(workerMessage); 261cb0ef41Sopenharmony_ci debugger; // eslint-disable-line no-debugger 271cb0ef41Sopenharmony_ci } else if (workerData === 2) { 281cb0ef41Sopenharmony_ci parentPort.postMessage('running'); 291cb0ef41Sopenharmony_ci waitForMessage(); 301cb0ef41Sopenharmony_ci } 311cb0ef41Sopenharmony_ci return; 321cb0ef41Sopenharmony_ci} 331cb0ef41Sopenharmony_ci 341cb0ef41Sopenharmony_cifunction doPost(session, method, params) { 351cb0ef41Sopenharmony_ci return new Promise((resolve, reject) => { 361cb0ef41Sopenharmony_ci session.post(method, params, (error, result) => { 371cb0ef41Sopenharmony_ci if (error) 381cb0ef41Sopenharmony_ci reject(JSON.stringify(error)); 391cb0ef41Sopenharmony_ci else 401cb0ef41Sopenharmony_ci resolve(result); 411cb0ef41Sopenharmony_ci }); 421cb0ef41Sopenharmony_ci }); 431cb0ef41Sopenharmony_ci} 441cb0ef41Sopenharmony_ci 451cb0ef41Sopenharmony_cifunction waitForEvent(emitter, event) { 461cb0ef41Sopenharmony_ci return new Promise((resolve) => emitter.once(event, resolve)); 471cb0ef41Sopenharmony_ci} 481cb0ef41Sopenharmony_ci 491cb0ef41Sopenharmony_cifunction waitForWorkerAttach(session) { 501cb0ef41Sopenharmony_ci return waitForEvent(session, 'NodeWorker.attachedToWorker') 511cb0ef41Sopenharmony_ci .then(({ params }) => params); 521cb0ef41Sopenharmony_ci} 531cb0ef41Sopenharmony_ci 541cb0ef41Sopenharmony_ciasync function waitForWorkerDetach(session, id) { 551cb0ef41Sopenharmony_ci let sessionId; 561cb0ef41Sopenharmony_ci do { 571cb0ef41Sopenharmony_ci const { params } = 581cb0ef41Sopenharmony_ci await waitForEvent(session, 'NodeWorker.detachedFromWorker'); 591cb0ef41Sopenharmony_ci sessionId = params.sessionId; 601cb0ef41Sopenharmony_ci } while (sessionId !== id); 611cb0ef41Sopenharmony_ci} 621cb0ef41Sopenharmony_ci 631cb0ef41Sopenharmony_cifunction runWorker(id, workerCallback = () => {}) { 641cb0ef41Sopenharmony_ci return new Promise((resolve, reject) => { 651cb0ef41Sopenharmony_ci const worker = new Worker(__filename, { workerData: id }); 661cb0ef41Sopenharmony_ci workerCallback(worker); 671cb0ef41Sopenharmony_ci worker.on('error', reject); 681cb0ef41Sopenharmony_ci worker.on('exit', resolve); 691cb0ef41Sopenharmony_ci }); 701cb0ef41Sopenharmony_ci} 711cb0ef41Sopenharmony_ci 721cb0ef41Sopenharmony_ciclass WorkerSession extends EventEmitter { 731cb0ef41Sopenharmony_ci constructor(parentSession, id) { 741cb0ef41Sopenharmony_ci super(); 751cb0ef41Sopenharmony_ci this._parentSession = parentSession; 761cb0ef41Sopenharmony_ci this._id = id; 771cb0ef41Sopenharmony_ci this._requestCallbacks = new Map(); 781cb0ef41Sopenharmony_ci this._nextCommandId = 1; 791cb0ef41Sopenharmony_ci this._parentSession.on('NodeWorker.receivedMessageFromWorker', 801cb0ef41Sopenharmony_ci ({ params }) => { 811cb0ef41Sopenharmony_ci if (params.sessionId === this._id) 821cb0ef41Sopenharmony_ci this._processMessage(JSON.parse(params.message)); 831cb0ef41Sopenharmony_ci }); 841cb0ef41Sopenharmony_ci } 851cb0ef41Sopenharmony_ci 861cb0ef41Sopenharmony_ci _processMessage(message) { 871cb0ef41Sopenharmony_ci if (message.id === undefined) { 881cb0ef41Sopenharmony_ci // console.log(JSON.stringify(message)); 891cb0ef41Sopenharmony_ci this.emit('inspectorNotification', message); 901cb0ef41Sopenharmony_ci this.emit(message.method, message); 911cb0ef41Sopenharmony_ci return; 921cb0ef41Sopenharmony_ci } 931cb0ef41Sopenharmony_ci if (!this._requestCallbacks.has(message.id)) 941cb0ef41Sopenharmony_ci return; 951cb0ef41Sopenharmony_ci const [ resolve, reject ] = this._requestCallbacks.get(message.id); 961cb0ef41Sopenharmony_ci this._requestCallbacks.delete(message.id); 971cb0ef41Sopenharmony_ci if (message.error) 981cb0ef41Sopenharmony_ci reject(new Error(message.error.message)); 991cb0ef41Sopenharmony_ci else 1001cb0ef41Sopenharmony_ci resolve(message.result); 1011cb0ef41Sopenharmony_ci } 1021cb0ef41Sopenharmony_ci 1031cb0ef41Sopenharmony_ci async waitForBreakAfterCommand(command, script, line) { 1041cb0ef41Sopenharmony_ci const notificationPromise = waitForEvent(this, 'Debugger.paused'); 1051cb0ef41Sopenharmony_ci this.post(command); 1061cb0ef41Sopenharmony_ci const notification = await notificationPromise; 1071cb0ef41Sopenharmony_ci const callFrame = notification.params.callFrames[0]; 1081cb0ef41Sopenharmony_ci assert.strictEqual(callFrame.location.lineNumber, line); 1091cb0ef41Sopenharmony_ci } 1101cb0ef41Sopenharmony_ci 1111cb0ef41Sopenharmony_ci post(method, parameters) { 1121cb0ef41Sopenharmony_ci const msg = { 1131cb0ef41Sopenharmony_ci id: this._nextCommandId++, 1141cb0ef41Sopenharmony_ci method 1151cb0ef41Sopenharmony_ci }; 1161cb0ef41Sopenharmony_ci if (parameters) 1171cb0ef41Sopenharmony_ci msg.params = parameters; 1181cb0ef41Sopenharmony_ci 1191cb0ef41Sopenharmony_ci return new Promise((resolve, reject) => { 1201cb0ef41Sopenharmony_ci this._requestCallbacks.set(msg.id, [resolve, reject]); 1211cb0ef41Sopenharmony_ci this._parentSession.post('NodeWorker.sendMessageToWorker', { 1221cb0ef41Sopenharmony_ci sessionId: this._id, message: JSON.stringify(msg) 1231cb0ef41Sopenharmony_ci }); 1241cb0ef41Sopenharmony_ci }); 1251cb0ef41Sopenharmony_ci } 1261cb0ef41Sopenharmony_ci} 1271cb0ef41Sopenharmony_ci 1281cb0ef41Sopenharmony_ciasync function testBasicWorkerDebug(session, post) { 1291cb0ef41Sopenharmony_ci // 1. Do 'enable' with waitForDebuggerOnStart = true 1301cb0ef41Sopenharmony_ci // 2. Run worker. It should break on start. 1311cb0ef41Sopenharmony_ci // 3. Enable Runtime (to get console message) and Debugger. Resume. 1321cb0ef41Sopenharmony_ci // 4. Breaks on the 'debugger' statement. Resume. 1331cb0ef41Sopenharmony_ci // 5. Console message received, worker runs to a completion. 1341cb0ef41Sopenharmony_ci // 6. contextCreated/contextDestroyed had been properly dispatched 1351cb0ef41Sopenharmony_ci console.log('Test basic debug scenario'); 1361cb0ef41Sopenharmony_ci await post('NodeWorker.enable', { waitForDebuggerOnStart: true }); 1371cb0ef41Sopenharmony_ci const attached = waitForWorkerAttach(session); 1381cb0ef41Sopenharmony_ci const worker = runWorker(1); 1391cb0ef41Sopenharmony_ci const { sessionId, waitingForDebugger } = await attached; 1401cb0ef41Sopenharmony_ci assert.strictEqual(waitingForDebugger, true); 1411cb0ef41Sopenharmony_ci const detached = waitForWorkerDetach(session, sessionId); 1421cb0ef41Sopenharmony_ci const workerSession = new WorkerSession(session, sessionId); 1431cb0ef41Sopenharmony_ci const contextEventPromises = Promise.all([ 1441cb0ef41Sopenharmony_ci waitForEvent(workerSession, 'Runtime.executionContextCreated'), 1451cb0ef41Sopenharmony_ci waitForEvent(workerSession, 'Runtime.executionContextDestroyed'), 1461cb0ef41Sopenharmony_ci ]); 1471cb0ef41Sopenharmony_ci const consolePromise = waitForEvent(workerSession, 'Runtime.consoleAPICalled') 1481cb0ef41Sopenharmony_ci .then((notification) => notification.params.args[0].value); 1491cb0ef41Sopenharmony_ci await workerSession.post('Debugger.enable'); 1501cb0ef41Sopenharmony_ci await workerSession.post('Runtime.enable'); 1511cb0ef41Sopenharmony_ci await workerSession.waitForBreakAfterCommand( 1521cb0ef41Sopenharmony_ci 'Runtime.runIfWaitingForDebugger', __filename, 1); 1531cb0ef41Sopenharmony_ci await workerSession.waitForBreakAfterCommand( 1541cb0ef41Sopenharmony_ci 'Debugger.resume', __filename, 25); // V8 line number is zero-based 1551cb0ef41Sopenharmony_ci const msg = await consolePromise; 1561cb0ef41Sopenharmony_ci assert.strictEqual(msg, workerMessage); 1571cb0ef41Sopenharmony_ci workerSession.post('Debugger.resume'); 1581cb0ef41Sopenharmony_ci await Promise.all([worker, detached, contextEventPromises]); 1591cb0ef41Sopenharmony_ci} 1601cb0ef41Sopenharmony_ci 1611cb0ef41Sopenharmony_ciasync function testNoWaitOnStart(session, post) { 1621cb0ef41Sopenharmony_ci console.log('Test disabled waitForDebuggerOnStart'); 1631cb0ef41Sopenharmony_ci await post('NodeWorker.enable', { waitForDebuggerOnStart: false }); 1641cb0ef41Sopenharmony_ci let worker; 1651cb0ef41Sopenharmony_ci const promise = waitForWorkerAttach(session); 1661cb0ef41Sopenharmony_ci const exitPromise = runWorker(2, (w) => { worker = w; }); 1671cb0ef41Sopenharmony_ci const { waitingForDebugger } = await promise; 1681cb0ef41Sopenharmony_ci assert.strictEqual(waitingForDebugger, false); 1691cb0ef41Sopenharmony_ci worker.postMessage('resume'); 1701cb0ef41Sopenharmony_ci await exitPromise; 1711cb0ef41Sopenharmony_ci} 1721cb0ef41Sopenharmony_ci 1731cb0ef41Sopenharmony_ciasync function testTwoWorkers(session, post) { 1741cb0ef41Sopenharmony_ci console.log('Test attach to a running worker and then start a new one'); 1751cb0ef41Sopenharmony_ci await post('NodeWorker.disable'); 1761cb0ef41Sopenharmony_ci let okToAttach = false; 1771cb0ef41Sopenharmony_ci const worker1attached = waitForWorkerAttach(session).then((notification) => { 1781cb0ef41Sopenharmony_ci assert.strictEqual(okToAttach, true); 1791cb0ef41Sopenharmony_ci return notification; 1801cb0ef41Sopenharmony_ci }); 1811cb0ef41Sopenharmony_ci 1821cb0ef41Sopenharmony_ci let worker1Exited; 1831cb0ef41Sopenharmony_ci const worker = await new Promise((resolve, reject) => { 1841cb0ef41Sopenharmony_ci worker1Exited = runWorker(2, resolve); 1851cb0ef41Sopenharmony_ci }).then((worker) => new Promise( 1861cb0ef41Sopenharmony_ci (resolve) => worker.once('message', () => resolve(worker)))); 1871cb0ef41Sopenharmony_ci okToAttach = true; 1881cb0ef41Sopenharmony_ci await post('NodeWorker.enable', { waitForDebuggerOnStart: true }); 1891cb0ef41Sopenharmony_ci const { waitingForDebugger: worker1Waiting } = await worker1attached; 1901cb0ef41Sopenharmony_ci assert.strictEqual(worker1Waiting, false); 1911cb0ef41Sopenharmony_ci 1921cb0ef41Sopenharmony_ci const worker2Attached = waitForWorkerAttach(session); 1931cb0ef41Sopenharmony_ci let worker2Done = false; 1941cb0ef41Sopenharmony_ci const worker2Exited = runWorker(1) 1951cb0ef41Sopenharmony_ci .then(() => assert.strictEqual(worker2Done, true)); 1961cb0ef41Sopenharmony_ci const worker2AttachInfo = await worker2Attached; 1971cb0ef41Sopenharmony_ci assert.strictEqual(worker2AttachInfo.waitingForDebugger, true); 1981cb0ef41Sopenharmony_ci worker2Done = true; 1991cb0ef41Sopenharmony_ci 2001cb0ef41Sopenharmony_ci const workerSession = new WorkerSession(session, worker2AttachInfo.sessionId); 2011cb0ef41Sopenharmony_ci workerSession.post('Runtime.runIfWaitingForDebugger'); 2021cb0ef41Sopenharmony_ci worker.postMessage('resume'); 2031cb0ef41Sopenharmony_ci await Promise.all([worker1Exited, worker2Exited]); 2041cb0ef41Sopenharmony_ci} 2051cb0ef41Sopenharmony_ci 2061cb0ef41Sopenharmony_ciasync function testWaitForDisconnectInWorker(session, post) { 2071cb0ef41Sopenharmony_ci console.log('Test NodeRuntime.waitForDisconnect in worker'); 2081cb0ef41Sopenharmony_ci 2091cb0ef41Sopenharmony_ci const sessionWithoutWaiting = new Session(); 2101cb0ef41Sopenharmony_ci sessionWithoutWaiting.connect(); 2111cb0ef41Sopenharmony_ci const sessionWithoutWaitingPost = doPost.bind(null, sessionWithoutWaiting); 2121cb0ef41Sopenharmony_ci 2131cb0ef41Sopenharmony_ci await sessionWithoutWaitingPost('NodeWorker.enable', { 2141cb0ef41Sopenharmony_ci waitForDebuggerOnStart: true 2151cb0ef41Sopenharmony_ci }); 2161cb0ef41Sopenharmony_ci await post('NodeWorker.enable', { waitForDebuggerOnStart: true }); 2171cb0ef41Sopenharmony_ci 2181cb0ef41Sopenharmony_ci const attached = [ 2191cb0ef41Sopenharmony_ci waitForWorkerAttach(session), 2201cb0ef41Sopenharmony_ci waitForWorkerAttach(sessionWithoutWaiting), 2211cb0ef41Sopenharmony_ci ]; 2221cb0ef41Sopenharmony_ci 2231cb0ef41Sopenharmony_ci let worker = null; 2241cb0ef41Sopenharmony_ci const exitPromise = runWorker(2, (w) => worker = w); 2251cb0ef41Sopenharmony_ci 2261cb0ef41Sopenharmony_ci const [{ sessionId: sessionId1 }, { sessionId: sessionId2 }] = 2271cb0ef41Sopenharmony_ci await Promise.all(attached); 2281cb0ef41Sopenharmony_ci 2291cb0ef41Sopenharmony_ci const workerSession1 = new WorkerSession(session, sessionId1); 2301cb0ef41Sopenharmony_ci const workerSession2 = new WorkerSession(sessionWithoutWaiting, sessionId2); 2311cb0ef41Sopenharmony_ci 2321cb0ef41Sopenharmony_ci await workerSession2.post('Runtime.enable'); 2331cb0ef41Sopenharmony_ci await workerSession1.post('Runtime.enable'); 2341cb0ef41Sopenharmony_ci await workerSession1.post('NodeRuntime.notifyWhenWaitingForDisconnect', { 2351cb0ef41Sopenharmony_ci enabled: true 2361cb0ef41Sopenharmony_ci }); 2371cb0ef41Sopenharmony_ci await workerSession1.post('Runtime.runIfWaitingForDebugger'); 2381cb0ef41Sopenharmony_ci 2391cb0ef41Sopenharmony_ci // Create the promises before sending the exit message to the Worker in order 2401cb0ef41Sopenharmony_ci // to avoid race conditions. 2411cb0ef41Sopenharmony_ci const disconnectPromise = 2421cb0ef41Sopenharmony_ci waitForEvent(workerSession1, 'NodeRuntime.waitingForDisconnect'); 2431cb0ef41Sopenharmony_ci const executionContextDestroyedPromise = 2441cb0ef41Sopenharmony_ci waitForEvent(workerSession2, 'Runtime.executionContextDestroyed'); 2451cb0ef41Sopenharmony_ci worker.postMessage('resume'); 2461cb0ef41Sopenharmony_ci 2471cb0ef41Sopenharmony_ci await disconnectPromise; 2481cb0ef41Sopenharmony_ci post('NodeWorker.detach', { sessionId: sessionId1 }); 2491cb0ef41Sopenharmony_ci await executionContextDestroyedPromise; 2501cb0ef41Sopenharmony_ci 2511cb0ef41Sopenharmony_ci await exitPromise; 2521cb0ef41Sopenharmony_ci 2531cb0ef41Sopenharmony_ci await post('NodeWorker.disable'); 2541cb0ef41Sopenharmony_ci await sessionWithoutWaitingPost('NodeWorker.disable'); 2551cb0ef41Sopenharmony_ci sessionWithoutWaiting.disconnect(); 2561cb0ef41Sopenharmony_ci} 2571cb0ef41Sopenharmony_ci 2581cb0ef41Sopenharmony_ci(async function test() { 2591cb0ef41Sopenharmony_ci const session = new Session(); 2601cb0ef41Sopenharmony_ci session.connect(); 2611cb0ef41Sopenharmony_ci const post = doPost.bind(null, session); 2621cb0ef41Sopenharmony_ci 2631cb0ef41Sopenharmony_ci await testBasicWorkerDebug(session, post); 2641cb0ef41Sopenharmony_ci 2651cb0ef41Sopenharmony_ci console.log('Test disabling attach to workers'); 2661cb0ef41Sopenharmony_ci await post('NodeWorker.disable'); 2671cb0ef41Sopenharmony_ci await runWorker(1); 2681cb0ef41Sopenharmony_ci 2691cb0ef41Sopenharmony_ci await testNoWaitOnStart(session, post); 2701cb0ef41Sopenharmony_ci 2711cb0ef41Sopenharmony_ci await testTwoWorkers(session, post); 2721cb0ef41Sopenharmony_ci 2731cb0ef41Sopenharmony_ci await testWaitForDisconnectInWorker(session, post); 2741cb0ef41Sopenharmony_ci 2751cb0ef41Sopenharmony_ci session.disconnect(); 2761cb0ef41Sopenharmony_ci console.log('Test done'); 2771cb0ef41Sopenharmony_ci})().then(common.mustCall()).catch((err) => { 2781cb0ef41Sopenharmony_ci console.error(err); 2791cb0ef41Sopenharmony_ci process.exitCode = 1; 2801cb0ef41Sopenharmony_ci}); 281