11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ciconst {
31cb0ef41Sopenharmony_ci  ArrayFrom,
41cb0ef41Sopenharmony_ci  ArrayIsArray,
51cb0ef41Sopenharmony_ci  ArrayPrototypeFilter,
61cb0ef41Sopenharmony_ci  ArrayPrototypeForEach,
71cb0ef41Sopenharmony_ci  ArrayPrototypeIncludes,
81cb0ef41Sopenharmony_ci  ArrayPrototypeMap,
91cb0ef41Sopenharmony_ci  ArrayPrototypePush,
101cb0ef41Sopenharmony_ci  ArrayPrototypeShift,
111cb0ef41Sopenharmony_ci  ArrayPrototypeSlice,
121cb0ef41Sopenharmony_ci  ArrayPrototypeSome,
131cb0ef41Sopenharmony_ci  ArrayPrototypeSort,
141cb0ef41Sopenharmony_ci  ObjectAssign,
151cb0ef41Sopenharmony_ci  PromisePrototypeThen,
161cb0ef41Sopenharmony_ci  SafePromiseAll,
171cb0ef41Sopenharmony_ci  SafePromiseAllReturnVoid,
181cb0ef41Sopenharmony_ci  SafePromiseAllSettledReturnVoid,
191cb0ef41Sopenharmony_ci  PromiseResolve,
201cb0ef41Sopenharmony_ci  SafeMap,
211cb0ef41Sopenharmony_ci  SafeSet,
221cb0ef41Sopenharmony_ci  StringPrototypeIndexOf,
231cb0ef41Sopenharmony_ci  StringPrototypeSlice,
241cb0ef41Sopenharmony_ci  StringPrototypeStartsWith,
251cb0ef41Sopenharmony_ci  TypedArrayPrototypeGetLength,
261cb0ef41Sopenharmony_ci  TypedArrayPrototypeSubarray,
271cb0ef41Sopenharmony_ci} = primordials;
281cb0ef41Sopenharmony_ci
291cb0ef41Sopenharmony_ciconst { spawn } = require('child_process');
301cb0ef41Sopenharmony_ciconst { readdirSync, statSync } = require('fs');
311cb0ef41Sopenharmony_ciconst { finished } = require('internal/streams/end-of-stream');
321cb0ef41Sopenharmony_ciconst { DefaultDeserializer, DefaultSerializer } = require('v8');
331cb0ef41Sopenharmony_ci// TODO(aduh95): switch to internal/readline/interface when backporting to Node.js 16.x is no longer a concern.
341cb0ef41Sopenharmony_ciconst { createInterface } = require('readline');
351cb0ef41Sopenharmony_ciconst { deserializeError } = require('internal/error_serdes');
361cb0ef41Sopenharmony_ciconst { Buffer } = require('buffer');
371cb0ef41Sopenharmony_ciconst { FilesWatcher } = require('internal/watch_mode/files_watcher');
381cb0ef41Sopenharmony_ciconst console = require('internal/console/global');
391cb0ef41Sopenharmony_ciconst {
401cb0ef41Sopenharmony_ci  codes: {
411cb0ef41Sopenharmony_ci    ERR_INVALID_ARG_TYPE,
421cb0ef41Sopenharmony_ci    ERR_INVALID_ARG_VALUE,
431cb0ef41Sopenharmony_ci    ERR_TEST_FAILURE,
441cb0ef41Sopenharmony_ci    ERR_OUT_OF_RANGE,
451cb0ef41Sopenharmony_ci  },
461cb0ef41Sopenharmony_ci} = require('internal/errors');
471cb0ef41Sopenharmony_ciconst {
481cb0ef41Sopenharmony_ci  validateArray,
491cb0ef41Sopenharmony_ci  validateBoolean,
501cb0ef41Sopenharmony_ci  validateFunction,
511cb0ef41Sopenharmony_ci  validateObject,
521cb0ef41Sopenharmony_ci  validateInteger,
531cb0ef41Sopenharmony_ci} = require('internal/validators');
541cb0ef41Sopenharmony_ciconst { getInspectPort, isUsingInspector, isInspectorMessage } = require('internal/util/inspector');
551cb0ef41Sopenharmony_ciconst { isRegExp } = require('internal/util/types');
561cb0ef41Sopenharmony_ciconst { kEmptyObject } = require('internal/util');
571cb0ef41Sopenharmony_ciconst { kEmitMessage } = require('internal/test_runner/tests_stream');
581cb0ef41Sopenharmony_ciconst { createTestTree } = require('internal/test_runner/harness');
591cb0ef41Sopenharmony_ciconst {
601cb0ef41Sopenharmony_ci  kAborted,
611cb0ef41Sopenharmony_ci  kCancelledByParent,
621cb0ef41Sopenharmony_ci  kSubtestsFailed,
631cb0ef41Sopenharmony_ci  kTestCodeFailure,
641cb0ef41Sopenharmony_ci  kTestTimeoutFailure,
651cb0ef41Sopenharmony_ci  Test,
661cb0ef41Sopenharmony_ci} = require('internal/test_runner/test');
671cb0ef41Sopenharmony_ci
681cb0ef41Sopenharmony_ciconst {
691cb0ef41Sopenharmony_ci  convertStringToRegExp,
701cb0ef41Sopenharmony_ci  countCompletedTest,
711cb0ef41Sopenharmony_ci  doesPathMatchFilter,
721cb0ef41Sopenharmony_ci  isSupportedFileType,
731cb0ef41Sopenharmony_ci} = require('internal/test_runner/utils');
741cb0ef41Sopenharmony_ciconst { basename, join, resolve } = require('path');
751cb0ef41Sopenharmony_ciconst { once } = require('events');
761cb0ef41Sopenharmony_ciconst {
771cb0ef41Sopenharmony_ci  triggerUncaughtException,
781cb0ef41Sopenharmony_ci} = internalBinding('errors');
791cb0ef41Sopenharmony_ci
801cb0ef41Sopenharmony_ciconst kFilterArgs = ['--test', '--experimental-test-coverage', '--watch'];
811cb0ef41Sopenharmony_ciconst kFilterArgValues = ['--test-reporter', '--test-reporter-destination'];
821cb0ef41Sopenharmony_ciconst kDiagnosticsFilterArgs = ['tests', 'suites', 'pass', 'fail', 'cancelled', 'skipped', 'todo', 'duration_ms'];
831cb0ef41Sopenharmony_ci
841cb0ef41Sopenharmony_ciconst kCanceledTests = new SafeSet()
851cb0ef41Sopenharmony_ci  .add(kCancelledByParent).add(kAborted).add(kTestTimeoutFailure);
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_cilet kResistStopPropagation;
881cb0ef41Sopenharmony_ci
891cb0ef41Sopenharmony_ci// TODO(cjihrig): Replace this with recursive readdir once it lands.
901cb0ef41Sopenharmony_cifunction processPath(path, testFiles, options) {
911cb0ef41Sopenharmony_ci  const stats = statSync(path);
921cb0ef41Sopenharmony_ci
931cb0ef41Sopenharmony_ci  if (stats.isFile()) {
941cb0ef41Sopenharmony_ci    if (options.userSupplied ||
951cb0ef41Sopenharmony_ci        (options.underTestDir && isSupportedFileType(path)) ||
961cb0ef41Sopenharmony_ci        doesPathMatchFilter(path)) {
971cb0ef41Sopenharmony_ci      testFiles.add(path);
981cb0ef41Sopenharmony_ci    }
991cb0ef41Sopenharmony_ci  } else if (stats.isDirectory()) {
1001cb0ef41Sopenharmony_ci    const name = basename(path);
1011cb0ef41Sopenharmony_ci
1021cb0ef41Sopenharmony_ci    if (!options.userSupplied && name === 'node_modules') {
1031cb0ef41Sopenharmony_ci      return;
1041cb0ef41Sopenharmony_ci    }
1051cb0ef41Sopenharmony_ci
1061cb0ef41Sopenharmony_ci    // 'test' directories get special treatment. Recursively add all .js,
1071cb0ef41Sopenharmony_ci    // .cjs, and .mjs files in the 'test' directory.
1081cb0ef41Sopenharmony_ci    const isTestDir = name === 'test';
1091cb0ef41Sopenharmony_ci    const { underTestDir } = options;
1101cb0ef41Sopenharmony_ci    const entries = readdirSync(path);
1111cb0ef41Sopenharmony_ci
1121cb0ef41Sopenharmony_ci    if (isTestDir) {
1131cb0ef41Sopenharmony_ci      options.underTestDir = true;
1141cb0ef41Sopenharmony_ci    }
1151cb0ef41Sopenharmony_ci
1161cb0ef41Sopenharmony_ci    options.userSupplied = false;
1171cb0ef41Sopenharmony_ci
1181cb0ef41Sopenharmony_ci    for (let i = 0; i < entries.length; i++) {
1191cb0ef41Sopenharmony_ci      processPath(join(path, entries[i]), testFiles, options);
1201cb0ef41Sopenharmony_ci    }
1211cb0ef41Sopenharmony_ci
1221cb0ef41Sopenharmony_ci    options.underTestDir = underTestDir;
1231cb0ef41Sopenharmony_ci  }
1241cb0ef41Sopenharmony_ci}
1251cb0ef41Sopenharmony_ci
1261cb0ef41Sopenharmony_cifunction createTestFileList() {
1271cb0ef41Sopenharmony_ci  const cwd = process.cwd();
1281cb0ef41Sopenharmony_ci  const hasUserSuppliedPaths = process.argv.length > 1;
1291cb0ef41Sopenharmony_ci  const testPaths = hasUserSuppliedPaths ?
1301cb0ef41Sopenharmony_ci    ArrayPrototypeSlice(process.argv, 1) : [cwd];
1311cb0ef41Sopenharmony_ci  const testFiles = new SafeSet();
1321cb0ef41Sopenharmony_ci
1331cb0ef41Sopenharmony_ci  try {
1341cb0ef41Sopenharmony_ci    for (let i = 0; i < testPaths.length; i++) {
1351cb0ef41Sopenharmony_ci      const absolutePath = resolve(testPaths[i]);
1361cb0ef41Sopenharmony_ci
1371cb0ef41Sopenharmony_ci      processPath(absolutePath, testFiles, {
1381cb0ef41Sopenharmony_ci        __proto__: null,
1391cb0ef41Sopenharmony_ci        userSupplied: true,
1401cb0ef41Sopenharmony_ci      });
1411cb0ef41Sopenharmony_ci    }
1421cb0ef41Sopenharmony_ci  } catch (err) {
1431cb0ef41Sopenharmony_ci    if (err?.code === 'ENOENT') {
1441cb0ef41Sopenharmony_ci      console.error(`Could not find '${err.path}'`);
1451cb0ef41Sopenharmony_ci      process.exit(1);
1461cb0ef41Sopenharmony_ci    }
1471cb0ef41Sopenharmony_ci
1481cb0ef41Sopenharmony_ci    throw err;
1491cb0ef41Sopenharmony_ci  }
1501cb0ef41Sopenharmony_ci
1511cb0ef41Sopenharmony_ci  return ArrayPrototypeSort(ArrayFrom(testFiles));
1521cb0ef41Sopenharmony_ci}
1531cb0ef41Sopenharmony_ci
1541cb0ef41Sopenharmony_cifunction filterExecArgv(arg, i, arr) {
1551cb0ef41Sopenharmony_ci  return !ArrayPrototypeIncludes(kFilterArgs, arg) &&
1561cb0ef41Sopenharmony_ci  !ArrayPrototypeSome(kFilterArgValues, (p) => arg === p || (i > 0 && arr[i - 1] === p) || StringPrototypeStartsWith(arg, `${p}=`));
1571cb0ef41Sopenharmony_ci}
1581cb0ef41Sopenharmony_ci
1591cb0ef41Sopenharmony_cifunction getRunArgs(path, { inspectPort, testNamePatterns, only }) {
1601cb0ef41Sopenharmony_ci  const argv = ArrayPrototypeFilter(process.execArgv, filterExecArgv);
1611cb0ef41Sopenharmony_ci  if (isUsingInspector()) {
1621cb0ef41Sopenharmony_ci    ArrayPrototypePush(argv, `--inspect-port=${getInspectPort(inspectPort)}`);
1631cb0ef41Sopenharmony_ci  }
1641cb0ef41Sopenharmony_ci  if (testNamePatterns != null) {
1651cb0ef41Sopenharmony_ci    ArrayPrototypeForEach(testNamePatterns, (pattern) => ArrayPrototypePush(argv, `--test-name-pattern=${pattern}`));
1661cb0ef41Sopenharmony_ci  }
1671cb0ef41Sopenharmony_ci  if (only === true) {
1681cb0ef41Sopenharmony_ci    ArrayPrototypePush(argv, '--test-only');
1691cb0ef41Sopenharmony_ci  }
1701cb0ef41Sopenharmony_ci  ArrayPrototypePush(argv, path);
1711cb0ef41Sopenharmony_ci
1721cb0ef41Sopenharmony_ci  return argv;
1731cb0ef41Sopenharmony_ci}
1741cb0ef41Sopenharmony_ci
1751cb0ef41Sopenharmony_ciconst serializer = new DefaultSerializer();
1761cb0ef41Sopenharmony_ciserializer.writeHeader();
1771cb0ef41Sopenharmony_ciconst v8Header = serializer.releaseBuffer();
1781cb0ef41Sopenharmony_ciconst kV8HeaderLength = TypedArrayPrototypeGetLength(v8Header);
1791cb0ef41Sopenharmony_ciconst kSerializedSizeHeader = 4 + kV8HeaderLength;
1801cb0ef41Sopenharmony_ci
1811cb0ef41Sopenharmony_ciclass FileTest extends Test {
1821cb0ef41Sopenharmony_ci  // This class maintains two buffers:
1831cb0ef41Sopenharmony_ci  #reportBuffer = []; // Parsed items waiting for this.isClearToSend()
1841cb0ef41Sopenharmony_ci  #rawBuffer = []; // Raw data waiting to be parsed
1851cb0ef41Sopenharmony_ci  #rawBufferSize = 0;
1861cb0ef41Sopenharmony_ci  #reportedChildren = 0;
1871cb0ef41Sopenharmony_ci  failedSubtests = false;
1881cb0ef41Sopenharmony_ci
1891cb0ef41Sopenharmony_ci  constructor(options) {
1901cb0ef41Sopenharmony_ci    super(options);
1911cb0ef41Sopenharmony_ci    this.loc ??= {
1921cb0ef41Sopenharmony_ci      __proto__: null,
1931cb0ef41Sopenharmony_ci      line: 1,
1941cb0ef41Sopenharmony_ci      column: 1,
1951cb0ef41Sopenharmony_ci      file: resolve(this.name),
1961cb0ef41Sopenharmony_ci    };
1971cb0ef41Sopenharmony_ci  }
1981cb0ef41Sopenharmony_ci
1991cb0ef41Sopenharmony_ci  #skipReporting() {
2001cb0ef41Sopenharmony_ci    return this.#reportedChildren > 0 && (!this.error || this.error.failureType === kSubtestsFailed);
2011cb0ef41Sopenharmony_ci  }
2021cb0ef41Sopenharmony_ci  #checkNestedComment(comment) {
2031cb0ef41Sopenharmony_ci    const firstSpaceIndex = StringPrototypeIndexOf(comment, ' ');
2041cb0ef41Sopenharmony_ci    if (firstSpaceIndex === -1) return false;
2051cb0ef41Sopenharmony_ci    const secondSpaceIndex = StringPrototypeIndexOf(comment, ' ', firstSpaceIndex + 1);
2061cb0ef41Sopenharmony_ci    return secondSpaceIndex === -1 &&
2071cb0ef41Sopenharmony_ci          ArrayPrototypeIncludes(kDiagnosticsFilterArgs, StringPrototypeSlice(comment, 0, firstSpaceIndex));
2081cb0ef41Sopenharmony_ci  }
2091cb0ef41Sopenharmony_ci  #handleReportItem(item) {
2101cb0ef41Sopenharmony_ci    const isTopLevel = item.data.nesting === 0;
2111cb0ef41Sopenharmony_ci    if (isTopLevel) {
2121cb0ef41Sopenharmony_ci      if (item.type === 'test:plan' && this.#skipReporting()) {
2131cb0ef41Sopenharmony_ci        return;
2141cb0ef41Sopenharmony_ci      }
2151cb0ef41Sopenharmony_ci      if (item.type === 'test:diagnostic' && this.#checkNestedComment(item.data.message)) {
2161cb0ef41Sopenharmony_ci        return;
2171cb0ef41Sopenharmony_ci      }
2181cb0ef41Sopenharmony_ci    }
2191cb0ef41Sopenharmony_ci    if (item.data.details?.error) {
2201cb0ef41Sopenharmony_ci      item.data.details.error = deserializeError(item.data.details.error);
2211cb0ef41Sopenharmony_ci    }
2221cb0ef41Sopenharmony_ci    if (item.type === 'test:pass' || item.type === 'test:fail') {
2231cb0ef41Sopenharmony_ci      item.data.testNumber = isTopLevel ? (this.root.harness.counters.topLevel + 1) : item.data.testNumber;
2241cb0ef41Sopenharmony_ci      countCompletedTest({
2251cb0ef41Sopenharmony_ci        __proto__: null,
2261cb0ef41Sopenharmony_ci        name: item.data.name,
2271cb0ef41Sopenharmony_ci        finished: true,
2281cb0ef41Sopenharmony_ci        skipped: item.data.skip !== undefined,
2291cb0ef41Sopenharmony_ci        isTodo: item.data.todo !== undefined,
2301cb0ef41Sopenharmony_ci        passed: item.type === 'test:pass',
2311cb0ef41Sopenharmony_ci        cancelled: kCanceledTests.has(item.data.details?.error?.failureType),
2321cb0ef41Sopenharmony_ci        nesting: item.data.nesting,
2331cb0ef41Sopenharmony_ci        reportedType: item.data.details?.type,
2341cb0ef41Sopenharmony_ci      }, this.root.harness);
2351cb0ef41Sopenharmony_ci    }
2361cb0ef41Sopenharmony_ci    this.reporter[kEmitMessage](item.type, item.data);
2371cb0ef41Sopenharmony_ci  }
2381cb0ef41Sopenharmony_ci  #accumulateReportItem(item) {
2391cb0ef41Sopenharmony_ci    if (item.type !== 'test:pass' && item.type !== 'test:fail') {
2401cb0ef41Sopenharmony_ci      return;
2411cb0ef41Sopenharmony_ci    }
2421cb0ef41Sopenharmony_ci    this.#reportedChildren++;
2431cb0ef41Sopenharmony_ci    if (item.data.nesting === 0 && item.type === 'test:fail') {
2441cb0ef41Sopenharmony_ci      this.failedSubtests = true;
2451cb0ef41Sopenharmony_ci    }
2461cb0ef41Sopenharmony_ci  }
2471cb0ef41Sopenharmony_ci  #drainReportBuffer() {
2481cb0ef41Sopenharmony_ci    if (this.#reportBuffer.length > 0) {
2491cb0ef41Sopenharmony_ci      ArrayPrototypeForEach(this.#reportBuffer, (ast) => this.#handleReportItem(ast));
2501cb0ef41Sopenharmony_ci      this.#reportBuffer = [];
2511cb0ef41Sopenharmony_ci    }
2521cb0ef41Sopenharmony_ci  }
2531cb0ef41Sopenharmony_ci  addToReport(item) {
2541cb0ef41Sopenharmony_ci    this.#accumulateReportItem(item);
2551cb0ef41Sopenharmony_ci    if (!this.isClearToSend()) {
2561cb0ef41Sopenharmony_ci      ArrayPrototypePush(this.#reportBuffer, item);
2571cb0ef41Sopenharmony_ci      return;
2581cb0ef41Sopenharmony_ci    }
2591cb0ef41Sopenharmony_ci    this.#drainReportBuffer();
2601cb0ef41Sopenharmony_ci    this.#handleReportItem(item);
2611cb0ef41Sopenharmony_ci  }
2621cb0ef41Sopenharmony_ci  reportStarted() {}
2631cb0ef41Sopenharmony_ci  drain() {
2641cb0ef41Sopenharmony_ci    this.#drainRawBuffer();
2651cb0ef41Sopenharmony_ci    this.#drainReportBuffer();
2661cb0ef41Sopenharmony_ci  }
2671cb0ef41Sopenharmony_ci  report() {
2681cb0ef41Sopenharmony_ci    this.drain();
2691cb0ef41Sopenharmony_ci    const skipReporting = this.#skipReporting();
2701cb0ef41Sopenharmony_ci    if (!skipReporting) {
2711cb0ef41Sopenharmony_ci      super.reportStarted();
2721cb0ef41Sopenharmony_ci      super.report();
2731cb0ef41Sopenharmony_ci    }
2741cb0ef41Sopenharmony_ci  }
2751cb0ef41Sopenharmony_ci  parseMessage(readData) {
2761cb0ef41Sopenharmony_ci    let dataLength = TypedArrayPrototypeGetLength(readData);
2771cb0ef41Sopenharmony_ci    if (dataLength === 0) return;
2781cb0ef41Sopenharmony_ci    const partialV8Header = readData[dataLength - 1] === v8Header[0];
2791cb0ef41Sopenharmony_ci
2801cb0ef41Sopenharmony_ci    if (partialV8Header) {
2811cb0ef41Sopenharmony_ci      // This will break if v8Header length (2 bytes) is changed.
2821cb0ef41Sopenharmony_ci      // However it is covered by tests.
2831cb0ef41Sopenharmony_ci      readData = TypedArrayPrototypeSubarray(readData, 0, dataLength - 1);
2841cb0ef41Sopenharmony_ci      dataLength--;
2851cb0ef41Sopenharmony_ci    }
2861cb0ef41Sopenharmony_ci
2871cb0ef41Sopenharmony_ci    if (this.#rawBuffer[0] && TypedArrayPrototypeGetLength(this.#rawBuffer[0]) < kSerializedSizeHeader) {
2881cb0ef41Sopenharmony_ci      this.#rawBuffer[0] = Buffer.concat([this.#rawBuffer[0], readData]);
2891cb0ef41Sopenharmony_ci    } else {
2901cb0ef41Sopenharmony_ci      ArrayPrototypePush(this.#rawBuffer, readData);
2911cb0ef41Sopenharmony_ci    }
2921cb0ef41Sopenharmony_ci    this.#rawBufferSize += dataLength;
2931cb0ef41Sopenharmony_ci    this.#proccessRawBuffer();
2941cb0ef41Sopenharmony_ci
2951cb0ef41Sopenharmony_ci    if (partialV8Header) {
2961cb0ef41Sopenharmony_ci      ArrayPrototypePush(this.#rawBuffer, TypedArrayPrototypeSubarray(v8Header, 0, 1));
2971cb0ef41Sopenharmony_ci      this.#rawBufferSize++;
2981cb0ef41Sopenharmony_ci    }
2991cb0ef41Sopenharmony_ci  }
3001cb0ef41Sopenharmony_ci  #drainRawBuffer() {
3011cb0ef41Sopenharmony_ci    while (this.#rawBuffer.length > 0) {
3021cb0ef41Sopenharmony_ci      this.#proccessRawBuffer();
3031cb0ef41Sopenharmony_ci    }
3041cb0ef41Sopenharmony_ci  }
3051cb0ef41Sopenharmony_ci  #proccessRawBuffer() {
3061cb0ef41Sopenharmony_ci    // This method is called when it is known that there is at least one message
3071cb0ef41Sopenharmony_ci    let bufferHead = this.#rawBuffer[0];
3081cb0ef41Sopenharmony_ci    let headerIndex = bufferHead.indexOf(v8Header);
3091cb0ef41Sopenharmony_ci    let nonSerialized = Buffer.alloc(0);
3101cb0ef41Sopenharmony_ci
3111cb0ef41Sopenharmony_ci    while (bufferHead && headerIndex !== 0) {
3121cb0ef41Sopenharmony_ci      const nonSerializedData = headerIndex === -1 ?
3131cb0ef41Sopenharmony_ci        bufferHead :
3141cb0ef41Sopenharmony_ci        bufferHead.slice(0, headerIndex);
3151cb0ef41Sopenharmony_ci      nonSerialized = Buffer.concat([nonSerialized, nonSerializedData]);
3161cb0ef41Sopenharmony_ci      this.#rawBufferSize -= TypedArrayPrototypeGetLength(nonSerializedData);
3171cb0ef41Sopenharmony_ci      if (headerIndex === -1) {
3181cb0ef41Sopenharmony_ci        ArrayPrototypeShift(this.#rawBuffer);
3191cb0ef41Sopenharmony_ci      } else {
3201cb0ef41Sopenharmony_ci        this.#rawBuffer[0] = TypedArrayPrototypeSubarray(bufferHead, headerIndex);
3211cb0ef41Sopenharmony_ci      }
3221cb0ef41Sopenharmony_ci      bufferHead = this.#rawBuffer[0];
3231cb0ef41Sopenharmony_ci      headerIndex = bufferHead?.indexOf(v8Header);
3241cb0ef41Sopenharmony_ci    }
3251cb0ef41Sopenharmony_ci
3261cb0ef41Sopenharmony_ci    if (TypedArrayPrototypeGetLength(nonSerialized) > 0) {
3271cb0ef41Sopenharmony_ci      this.addToReport({
3281cb0ef41Sopenharmony_ci        __proto__: null,
3291cb0ef41Sopenharmony_ci        type: 'test:stdout',
3301cb0ef41Sopenharmony_ci        data: { __proto__: null, file: this.name, message: nonSerialized.toString('utf-8') },
3311cb0ef41Sopenharmony_ci      });
3321cb0ef41Sopenharmony_ci    }
3331cb0ef41Sopenharmony_ci
3341cb0ef41Sopenharmony_ci    while (bufferHead?.length >= kSerializedSizeHeader) {
3351cb0ef41Sopenharmony_ci      // We call `readUInt32BE` manually here, because this is faster than first converting
3361cb0ef41Sopenharmony_ci      // it to a buffer and using `readUInt32BE` on that.
3371cb0ef41Sopenharmony_ci      const fullMessageSize = (
3381cb0ef41Sopenharmony_ci        bufferHead[kV8HeaderLength] << 24 |
3391cb0ef41Sopenharmony_ci        bufferHead[kV8HeaderLength + 1] << 16 |
3401cb0ef41Sopenharmony_ci        bufferHead[kV8HeaderLength + 2] << 8 |
3411cb0ef41Sopenharmony_ci        bufferHead[kV8HeaderLength + 3]
3421cb0ef41Sopenharmony_ci      ) + kSerializedSizeHeader;
3431cb0ef41Sopenharmony_ci
3441cb0ef41Sopenharmony_ci      if (this.#rawBufferSize < fullMessageSize) break;
3451cb0ef41Sopenharmony_ci
3461cb0ef41Sopenharmony_ci      const concatenatedBuffer = this.#rawBuffer.length === 1 ?
3471cb0ef41Sopenharmony_ci        this.#rawBuffer[0] : Buffer.concat(this.#rawBuffer, this.#rawBufferSize);
3481cb0ef41Sopenharmony_ci
3491cb0ef41Sopenharmony_ci      const deserializer = new DefaultDeserializer(
3501cb0ef41Sopenharmony_ci        TypedArrayPrototypeSubarray(concatenatedBuffer, kSerializedSizeHeader, fullMessageSize),
3511cb0ef41Sopenharmony_ci      );
3521cb0ef41Sopenharmony_ci
3531cb0ef41Sopenharmony_ci      bufferHead = TypedArrayPrototypeSubarray(concatenatedBuffer, fullMessageSize);
3541cb0ef41Sopenharmony_ci      this.#rawBufferSize = TypedArrayPrototypeGetLength(bufferHead);
3551cb0ef41Sopenharmony_ci      this.#rawBuffer = this.#rawBufferSize !== 0 ? [bufferHead] : [];
3561cb0ef41Sopenharmony_ci
3571cb0ef41Sopenharmony_ci      deserializer.readHeader();
3581cb0ef41Sopenharmony_ci      const item = deserializer.readValue();
3591cb0ef41Sopenharmony_ci      this.addToReport(item);
3601cb0ef41Sopenharmony_ci    }
3611cb0ef41Sopenharmony_ci  }
3621cb0ef41Sopenharmony_ci}
3631cb0ef41Sopenharmony_ci
3641cb0ef41Sopenharmony_cifunction runTestFile(path, filesWatcher, opts) {
3651cb0ef41Sopenharmony_ci  const watchMode = filesWatcher != null;
3661cb0ef41Sopenharmony_ci  const subtest = opts.root.createSubtest(FileTest, path, async (t) => {
3671cb0ef41Sopenharmony_ci    const args = getRunArgs(path, opts);
3681cb0ef41Sopenharmony_ci    const stdio = ['pipe', 'pipe', 'pipe'];
3691cb0ef41Sopenharmony_ci    const env = { __proto__: null, ...process.env, NODE_TEST_CONTEXT: 'child-v8' };
3701cb0ef41Sopenharmony_ci    if (watchMode) {
3711cb0ef41Sopenharmony_ci      stdio.push('ipc');
3721cb0ef41Sopenharmony_ci      env.WATCH_REPORT_DEPENDENCIES = '1';
3731cb0ef41Sopenharmony_ci    }
3741cb0ef41Sopenharmony_ci    if (opts.root.harness.shouldColorizeTestFiles) {
3751cb0ef41Sopenharmony_ci      env.FORCE_COLOR = '1';
3761cb0ef41Sopenharmony_ci    }
3771cb0ef41Sopenharmony_ci
3781cb0ef41Sopenharmony_ci    const child = spawn(process.execPath, args, { __proto__: null, signal: t.signal, encoding: 'utf8', env, stdio });
3791cb0ef41Sopenharmony_ci    if (watchMode) {
3801cb0ef41Sopenharmony_ci      filesWatcher.runningProcesses.set(path, child);
3811cb0ef41Sopenharmony_ci      filesWatcher.watcher.watchChildProcessModules(child, path);
3821cb0ef41Sopenharmony_ci    }
3831cb0ef41Sopenharmony_ci
3841cb0ef41Sopenharmony_ci    let err;
3851cb0ef41Sopenharmony_ci
3861cb0ef41Sopenharmony_ci
3871cb0ef41Sopenharmony_ci    child.on('error', (error) => {
3881cb0ef41Sopenharmony_ci      err = error;
3891cb0ef41Sopenharmony_ci    });
3901cb0ef41Sopenharmony_ci
3911cb0ef41Sopenharmony_ci    child.stdout.on('data', (data) => {
3921cb0ef41Sopenharmony_ci      subtest.parseMessage(data);
3931cb0ef41Sopenharmony_ci    });
3941cb0ef41Sopenharmony_ci
3951cb0ef41Sopenharmony_ci    const rl = createInterface({ __proto__: null, input: child.stderr });
3961cb0ef41Sopenharmony_ci    rl.on('line', (line) => {
3971cb0ef41Sopenharmony_ci      if (isInspectorMessage(line)) {
3981cb0ef41Sopenharmony_ci        process.stderr.write(line + '\n');
3991cb0ef41Sopenharmony_ci        return;
4001cb0ef41Sopenharmony_ci      }
4011cb0ef41Sopenharmony_ci
4021cb0ef41Sopenharmony_ci      // stderr cannot be treated as TAP, per the spec. However, we want to
4031cb0ef41Sopenharmony_ci      // surface stderr lines to improve the DX. Inject each line into the
4041cb0ef41Sopenharmony_ci      // test output as an unknown token as if it came from the TAP parser.
4051cb0ef41Sopenharmony_ci      subtest.addToReport({
4061cb0ef41Sopenharmony_ci        __proto__: null,
4071cb0ef41Sopenharmony_ci        type: 'test:stderr',
4081cb0ef41Sopenharmony_ci        data: { __proto__: null, file: path, message: line + '\n' },
4091cb0ef41Sopenharmony_ci      });
4101cb0ef41Sopenharmony_ci    });
4111cb0ef41Sopenharmony_ci
4121cb0ef41Sopenharmony_ci    const { 0: { 0: code, 1: signal } } = await SafePromiseAll([
4131cb0ef41Sopenharmony_ci      once(child, 'exit', { __proto__: null, signal: t.signal }),
4141cb0ef41Sopenharmony_ci      finished(child.stdout, { __proto__: null, signal: t.signal }),
4151cb0ef41Sopenharmony_ci    ]);
4161cb0ef41Sopenharmony_ci
4171cb0ef41Sopenharmony_ci    if (watchMode) {
4181cb0ef41Sopenharmony_ci      filesWatcher.runningProcesses.delete(path);
4191cb0ef41Sopenharmony_ci      filesWatcher.runningSubtests.delete(path);
4201cb0ef41Sopenharmony_ci      if (filesWatcher.runningSubtests.size === 0) {
4211cb0ef41Sopenharmony_ci        opts.root.reporter[kEmitMessage]('test:watch:drained');
4221cb0ef41Sopenharmony_ci      }
4231cb0ef41Sopenharmony_ci    }
4241cb0ef41Sopenharmony_ci
4251cb0ef41Sopenharmony_ci    if (code !== 0 || signal !== null) {
4261cb0ef41Sopenharmony_ci      if (!err) {
4271cb0ef41Sopenharmony_ci        const failureType = subtest.failedSubtests ? kSubtestsFailed : kTestCodeFailure;
4281cb0ef41Sopenharmony_ci        err = ObjectAssign(new ERR_TEST_FAILURE('test failed', failureType), {
4291cb0ef41Sopenharmony_ci          __proto__: null,
4301cb0ef41Sopenharmony_ci          exitCode: code,
4311cb0ef41Sopenharmony_ci          signal: signal,
4321cb0ef41Sopenharmony_ci          // The stack will not be useful since the failures came from tests
4331cb0ef41Sopenharmony_ci          // in a child process.
4341cb0ef41Sopenharmony_ci          stack: undefined,
4351cb0ef41Sopenharmony_ci        });
4361cb0ef41Sopenharmony_ci      }
4371cb0ef41Sopenharmony_ci
4381cb0ef41Sopenharmony_ci      throw err;
4391cb0ef41Sopenharmony_ci    }
4401cb0ef41Sopenharmony_ci  });
4411cb0ef41Sopenharmony_ci  return subtest.start();
4421cb0ef41Sopenharmony_ci}
4431cb0ef41Sopenharmony_ci
4441cb0ef41Sopenharmony_cifunction watchFiles(testFiles, opts) {
4451cb0ef41Sopenharmony_ci  const runningProcesses = new SafeMap();
4461cb0ef41Sopenharmony_ci  const runningSubtests = new SafeMap();
4471cb0ef41Sopenharmony_ci  const watcher = new FilesWatcher({ __proto__: null, debounce: 200, mode: 'filter', signal: opts.signal });
4481cb0ef41Sopenharmony_ci  const filesWatcher = { __proto__: null, watcher, runningProcesses, runningSubtests };
4491cb0ef41Sopenharmony_ci
4501cb0ef41Sopenharmony_ci  watcher.on('changed', ({ owners }) => {
4511cb0ef41Sopenharmony_ci    watcher.unfilterFilesOwnedBy(owners);
4521cb0ef41Sopenharmony_ci    PromisePrototypeThen(SafePromiseAllReturnVoid(testFiles, async (file) => {
4531cb0ef41Sopenharmony_ci      if (!owners.has(file)) {
4541cb0ef41Sopenharmony_ci        return;
4551cb0ef41Sopenharmony_ci      }
4561cb0ef41Sopenharmony_ci      const runningProcess = runningProcesses.get(file);
4571cb0ef41Sopenharmony_ci      if (runningProcess) {
4581cb0ef41Sopenharmony_ci        runningProcess.kill();
4591cb0ef41Sopenharmony_ci        await once(runningProcess, 'exit');
4601cb0ef41Sopenharmony_ci      }
4611cb0ef41Sopenharmony_ci      if (!runningSubtests.size) {
4621cb0ef41Sopenharmony_ci        // Reset the topLevel counter
4631cb0ef41Sopenharmony_ci        opts.root.harness.counters.topLevel = 0;
4641cb0ef41Sopenharmony_ci      }
4651cb0ef41Sopenharmony_ci      await runningSubtests.get(file);
4661cb0ef41Sopenharmony_ci      runningSubtests.set(file, runTestFile(file, filesWatcher, opts));
4671cb0ef41Sopenharmony_ci    }, undefined, (error) => {
4681cb0ef41Sopenharmony_ci      triggerUncaughtException(error, true /* fromPromise */);
4691cb0ef41Sopenharmony_ci    }));
4701cb0ef41Sopenharmony_ci  });
4711cb0ef41Sopenharmony_ci  if (opts.signal) {
4721cb0ef41Sopenharmony_ci    kResistStopPropagation ??= require('internal/event_target').kResistStopPropagation;
4731cb0ef41Sopenharmony_ci    opts.signal.addEventListener(
4741cb0ef41Sopenharmony_ci      'abort',
4751cb0ef41Sopenharmony_ci      () => opts.root.postRun(),
4761cb0ef41Sopenharmony_ci      { __proto__: null, once: true, [kResistStopPropagation]: true },
4771cb0ef41Sopenharmony_ci    );
4781cb0ef41Sopenharmony_ci  }
4791cb0ef41Sopenharmony_ci
4801cb0ef41Sopenharmony_ci  return filesWatcher;
4811cb0ef41Sopenharmony_ci}
4821cb0ef41Sopenharmony_ci
4831cb0ef41Sopenharmony_cifunction run(options) {
4841cb0ef41Sopenharmony_ci  if (options === null || typeof options !== 'object') {
4851cb0ef41Sopenharmony_ci    options = kEmptyObject;
4861cb0ef41Sopenharmony_ci  }
4871cb0ef41Sopenharmony_ci  let { testNamePatterns, shard } = options;
4881cb0ef41Sopenharmony_ci  const { concurrency, timeout, signal, files, inspectPort, watch, setup, only } = options;
4891cb0ef41Sopenharmony_ci
4901cb0ef41Sopenharmony_ci  if (files != null) {
4911cb0ef41Sopenharmony_ci    validateArray(files, 'options.files');
4921cb0ef41Sopenharmony_ci  }
4931cb0ef41Sopenharmony_ci  if (watch != null) {
4941cb0ef41Sopenharmony_ci    validateBoolean(watch, 'options.watch');
4951cb0ef41Sopenharmony_ci  }
4961cb0ef41Sopenharmony_ci  if (only != null) {
4971cb0ef41Sopenharmony_ci    validateBoolean(only, 'options.only');
4981cb0ef41Sopenharmony_ci  }
4991cb0ef41Sopenharmony_ci  if (shard != null) {
5001cb0ef41Sopenharmony_ci    validateObject(shard, 'options.shard');
5011cb0ef41Sopenharmony_ci    // Avoid re-evaluating the shard object in case it's a getter
5021cb0ef41Sopenharmony_ci    shard = { __proto__: null, index: shard.index, total: shard.total };
5031cb0ef41Sopenharmony_ci
5041cb0ef41Sopenharmony_ci    validateInteger(shard.total, 'options.shard.total', 1);
5051cb0ef41Sopenharmony_ci    validateInteger(shard.index, 'options.shard.index');
5061cb0ef41Sopenharmony_ci
5071cb0ef41Sopenharmony_ci    if (shard.index <= 0 || shard.total < shard.index) {
5081cb0ef41Sopenharmony_ci      throw new ERR_OUT_OF_RANGE('options.shard.index', `>= 1 && <= ${shard.total} ("options.shard.total")`, shard.index);
5091cb0ef41Sopenharmony_ci    }
5101cb0ef41Sopenharmony_ci
5111cb0ef41Sopenharmony_ci    if (watch) {
5121cb0ef41Sopenharmony_ci      throw new ERR_INVALID_ARG_VALUE('options.shard', watch, 'shards not supported with watch mode');
5131cb0ef41Sopenharmony_ci    }
5141cb0ef41Sopenharmony_ci  }
5151cb0ef41Sopenharmony_ci  if (setup != null) {
5161cb0ef41Sopenharmony_ci    validateFunction(setup, 'options.setup');
5171cb0ef41Sopenharmony_ci  }
5181cb0ef41Sopenharmony_ci  if (testNamePatterns != null) {
5191cb0ef41Sopenharmony_ci    if (!ArrayIsArray(testNamePatterns)) {
5201cb0ef41Sopenharmony_ci      testNamePatterns = [testNamePatterns];
5211cb0ef41Sopenharmony_ci    }
5221cb0ef41Sopenharmony_ci
5231cb0ef41Sopenharmony_ci    testNamePatterns = ArrayPrototypeMap(testNamePatterns, (value, i) => {
5241cb0ef41Sopenharmony_ci      if (isRegExp(value)) {
5251cb0ef41Sopenharmony_ci        return value;
5261cb0ef41Sopenharmony_ci      }
5271cb0ef41Sopenharmony_ci      const name = `options.testNamePatterns[${i}]`;
5281cb0ef41Sopenharmony_ci      if (typeof value === 'string') {
5291cb0ef41Sopenharmony_ci        return convertStringToRegExp(value, name);
5301cb0ef41Sopenharmony_ci      }
5311cb0ef41Sopenharmony_ci      throw new ERR_INVALID_ARG_TYPE(name, ['string', 'RegExp'], value);
5321cb0ef41Sopenharmony_ci    });
5331cb0ef41Sopenharmony_ci  }
5341cb0ef41Sopenharmony_ci
5351cb0ef41Sopenharmony_ci  const root = createTestTree({ __proto__: null, concurrency, timeout, signal });
5361cb0ef41Sopenharmony_ci  let testFiles = files ?? createTestFileList();
5371cb0ef41Sopenharmony_ci
5381cb0ef41Sopenharmony_ci  if (shard) {
5391cb0ef41Sopenharmony_ci    testFiles = ArrayPrototypeFilter(testFiles, (_, index) => index % shard.total === shard.index - 1);
5401cb0ef41Sopenharmony_ci  }
5411cb0ef41Sopenharmony_ci
5421cb0ef41Sopenharmony_ci  let postRun = () => root.postRun();
5431cb0ef41Sopenharmony_ci  let filesWatcher;
5441cb0ef41Sopenharmony_ci  const opts = { __proto__: null, root, signal, inspectPort, testNamePatterns, only };
5451cb0ef41Sopenharmony_ci  if (watch) {
5461cb0ef41Sopenharmony_ci    filesWatcher = watchFiles(testFiles, opts);
5471cb0ef41Sopenharmony_ci    postRun = undefined;
5481cb0ef41Sopenharmony_ci  }
5491cb0ef41Sopenharmony_ci  const runFiles = () => {
5501cb0ef41Sopenharmony_ci    root.harness.bootstrapComplete = true;
5511cb0ef41Sopenharmony_ci    return SafePromiseAllSettledReturnVoid(testFiles, (path) => {
5521cb0ef41Sopenharmony_ci      const subtest = runTestFile(path, filesWatcher, opts);
5531cb0ef41Sopenharmony_ci      filesWatcher?.runningSubtests.set(path, subtest);
5541cb0ef41Sopenharmony_ci      return subtest;
5551cb0ef41Sopenharmony_ci    });
5561cb0ef41Sopenharmony_ci  };
5571cb0ef41Sopenharmony_ci
5581cb0ef41Sopenharmony_ci  PromisePrototypeThen(PromisePrototypeThen(PromiseResolve(setup?.(root)), runFiles), postRun);
5591cb0ef41Sopenharmony_ci
5601cb0ef41Sopenharmony_ci  return root.reporter;
5611cb0ef41Sopenharmony_ci}
5621cb0ef41Sopenharmony_ci
5631cb0ef41Sopenharmony_cimodule.exports = {
5641cb0ef41Sopenharmony_ci  FileTest, // Exported for tests only
5651cb0ef41Sopenharmony_ci  run,
5661cb0ef41Sopenharmony_ci};
567