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