,
  ArrayPrototypePush,
  ArrayPrototypePushApply,
  ArrayPrototypeShift,
  ArrayPrototypeSlice,
  ArrayPrototypeSome,
  ArrayPrototypeSort,
  ObjectAssign,
  PromisePrototypeThen,
  PromiseWithResolvers,
  SafeMap,
  SafePromiseAll,
  SafePromiseAllReturnVoid,
  SafePromiseAllSettledReturnVoid,
  SafeSet,
  StringPrototypeIndexOf,
  StringPrototypeSlice,
  StringPrototypeStartsWith,
  Symbol,
  TypedArrayPrototypeGetLength,
  TypedArrayPrototypeSubarray,
} = primordials;

const { spawn } = require('child_process');
const { finished } = require('internal/streams/end-of-stream');
const { resolve, sep, isAbsolute } = require('path');
const { DefaultDeserializer, DefaultSerializer } = require('v8');
const { getOptionValue } = require('internal/options');
const { Interface } = require('internal/readline/interface');
const { deserializeError } = require('internal/error_serdes');
const { Buffer } = require('buffer');
const { FilesWatcher } = require('internal/watch_mode/files_watcher');
const console = require('internal/console/global');
const {
  codes: {
    ERR_INVALID_ARG_TYPE,
    ERR_INVALID_ARG_VALUE,
    ERR_TEST_FAILURE,
  },
} = require('internal/errors');
const esmLoader = require('internal/modules/esm/loader');
const {
  validateArray,
  validateBoolean,
  validateFunction,
  validateObject,
  validateOneOf,
  validateInteger,
  validateString,
  validateStringArray,
} = require('internal/validators');
const { getInspectPort, isUsingInspector, isInspectorMessage } = require('internal/util/inspector');
const { isRegExp } = require('internal/util/types');
const { pathToFileURL } = require('internal/url');
const {
  kEmptyObject,
} = require('internal/util');
const { kEmitMessage } = require('internal/test_runner/tests_stream');
const {
  createTestTree,
  startSubtestAfterBootstrap,
} = require('internal/test_runner/harness');
const {
  kAborted,
  kCancelledByParent,
  kSubtestsFailed,
  kTestCodeFailure,
  kTestTimeoutFailure,
  Test,
} = require('internal/test_runner/test');

const {
  convertStringToRegExp,
  countCompletedTest,
  kDefaultPattern,
  parseCommandLine,
} = require('internal/test_runner/utils');
const { Glob } = require('internal/fs/glob');
const { once } = require('events');
const {
  triggerUncaughtException,
  exitCodes: { kGenericUserError },
} = internalBinding('errors');
let debug = require('internal/util/debuglog').debuglog('test_runner', (fn) => {
  debug = fn;
});

const kIsolatedProcessName = Symbol('kIsolatedProcessName');
const kFilterArgs = ['--test', '--experimental-test-coverage', '--watch'];
const kFilterArgValues = ['--test-reporter', '--test-reporter-destination'];
const kDiagnosticsFilterArgs = ['tests', 'suites', 'pass', 'fail', 'cancelled', 'skipped', 'todo', 'duration_ms'];

const kCanceledTests = new SafeSet()
  .add(kCancelledByParent).add(kAborted).add(kTestTimeoutFailure);

let kResistStopPropagation;

function createTestFileList(patterns, cwd) {
  const hasUserSuppliedPattern = patterns != null;
  if (!patterns || patterns.length === 0) {
    patterns = [kDefaultPattern];
  }
  const glob = new Glob(patterns, {
    __proto__: null,
    cwd,
    exclude: (name) => name === 'node_modules',
  });
  const results = glob.globSync();

  if (hasUserSuppliedPattern && results.length === 0 && ArrayPrototypeEvery(glob.matchers, (m) => !m.hasMagic())) {
    console.error(`Could not find '${ArrayPrototypeJoin(patterns, ', ')}'`);
    process.exit(kGenericUserError);
  }

  return ArrayPrototypeSort(results);
}

function filterExecArgv(arg, i, arr) {
  return !ArrayPrototypeIncludes(kFilterArgs, arg) &&
  !ArrayPrototypeSome(kFilterArgValues, (p) => arg === p || (i > 0 && arr[i - 1] === p) || StringPrototypeStartsWith(arg, `${p}=`));
}

function getRunArgs(path, { forceExit,
                            inspectPort,
                            testNamePatterns,
                            testSkipPatterns,
                            only,
                            argv: suppliedArgs,
                            execArgv,
                            cwd }) {
  const argv = ArrayPrototypeFilter(process.execArgv, filterExecArgv);
  if (forceExit === true) {
    ArrayPrototypePush(argv, '--test-force-exit');
  }
  if (isUsingInspector()) {
    ArrayPrototypePush(argv, `--inspect-port=${getInspectPort(inspectPort)}`);
  }
  if (testNamePatterns != null) {
    ArrayPrototypeForEach(testNamePatterns, (pattern) => ArrayPrototypePush(argv, `--test-name-pattern=${pattern}`));
  }
  if (testSkipPatterns != null) {
    ArrayPrototypeForEach(testSkipPatterns, (pattern) => ArrayPrototypePush(argv, `--test-skip-pattern=${pattern}`));
  }
  if (only === true) {
    ArrayPrototypePush(argv, '--test-only');
  }

  ArrayPrototypePushApply(argv, execArgv);

  if (path === kIsolatedProcessName) {
    ArrayPrototypePush(argv, '--test');
    ArrayPrototypePushApply(argv, ArrayPrototypeSlice(process.argv, 1));
  } else {
    ArrayPrototypePush(argv, path);
  }

  ArrayPrototypePushApply(argv, suppliedArgs);

  return argv;
}

const serializer = new DefaultSerializer();
serializer.writeHeader();
const v8Header = serializer.releaseBuffer();
const kV8HeaderLength = TypedArrayPrototypeGetLength(v8Header);
const kSerializedSizeHeader = 4 + kV8HeaderLength;

class FileTest extends Test {
  // This class maintains two buffers:
  #reportBuffer = []; // Parsed items waiting for this.isClearToSend()
  #rawBuffer = []; // Raw data waiting to be parsed
  #rawBufferSize = 0;
  #reportedChildren = 0;
  failedSubtests = false;

  constructor(options) {
    super(options);
    this.loc ??= {
      __proto__: null,
      line: 1,
      column: 1,
      file: resolve(this.name),
    };
  }

  #skipReporting() {
    return this.#reportedChildren > 0 && (!this.error || this.error.failureType === kSubtestsFailed);
  }
  #checkNestedComment(comment) {
    const firstSpaceIndex = StringPrototypeIndexOf(comment, ' ');
    if (firstSpaceIndex === -1) return false;
    const secondSpaceIndex = StringPrototypeIndexOf(comment, ' ', firstSpaceIndex + 1);
    return secondSpaceIndex === -1 &&
          ArrayPrototypeIncludes(kDiagnosticsFilterArgs, StringPrototypeSlice(comment, 0, firstSpaceIndex));
  }
  #handleReportItem(item) {
    const isTopLevel = item.data.nesting === 0;
    if (isTopLevel) {
      if (item.type === 'test:plan' && this.#skipReporting()) {
        return;
      }
      if (item.type === 'test:diagnostic' && this.#checkNestedComment(item.data.message)) {
        return;
      }
    }
    if (item.data.details?.error) {
      item.data.details.error = deserializeError(item.data.details.error);
    }
    if (item.type === 'test:pass' || item.type === 'test:fail') {
      item.data.testNumber = isTopLevel ? (this.root.harness.counters.topLevel + 1) : item.data.testNumber;
      countCompletedTest({
        __proto__: null,
        name: item.data.name,
        finished: true,
        skipped: item.data.skip !== undefined,
        isTodo: item.data.todo !== undefined,
        passed: item.type === 'test:pass',
        cancelled: kCanceledTests.has(item.data.details?.error?.failureType),
        nesting: item.data.nesting,
        reportedType: item.data.details?.type,
      }, this.root.harness);
    }
    this.reporter[kEmitMessage](item.type, item.data);
  }
  #accumulateReportItem(item) {
    if (item.type !== 'test:pass' && item.type !== 'test:fail') {
      return;
    }
    this.#reportedChildren++;
    if (item.data.nesting === 0 && item.type === 'test:fail') {
      this.failedSubtests = true;
    }
  }
  #drainReportBuffer() {
    if (this.#reportBuffer.length > 0) {
      ArrayPrototypeForEach(this.#reportBuffer, (ast) => this.#handleReportItem(ast));
      this.#reportBuffer = [];
    }
  }
  addToReport(item) {
    this.#accumulateReportItem(item);
    if (!this.isClearToSend()) {
      ArrayPrototypePush(this.#reportBuffer, item);
      return;
    }
    this.#drainReportBuffer();
    this.#handleReportItem(item);
  }
  reportStarted() {}
  drain() {
    this.#drainRawBuffer();
    this.#drainReportBuffer();
  }
  report() {
    this.drain();
    const skipReporting = this.#skipReporting();
    if (!skipReporting) {
      super.reportStarted();
      super.report();
    }
  }
  parseMessage(readData) {
    let dataLength = TypedArrayPrototypeGetLength(readData);
    if (dataLength === 0) return;
    const partialV8Header = readData[dataLength - 1] === v8Header[0];

    if (partialV8Header) {
      // This will break if v8Header length (2 bytes) is changed.
      // However it is covered by tests.
      readData = TypedArrayPrototypeSubarray(readData, 0, dataLength - 1);
      dataLength--;
    }

    if (this.#rawBuffer[0] && TypedArrayPrototypeGetLength(this.#rawBuffer[0]) < kSerializedSizeHeader) {
      this.#rawBuffer[0] = Buffer.concat([this.#rawBuffer[0], readData]);
    } else {
      ArrayPrototypePush(this.#rawBuffer, readData);
    }
    this.#rawBufferSize += dataLength;
    this.#processRawBuffer();

    if (partialV8Header) {
      ArrayPrototypePush(this.#rawBuffer, TypedArrayPrototypeSubarray(v8Header, 0, 1));
      this.#rawBufferSize++;
    }
  }
  #drainRawBuffer() {
    while (this.#rawBuffer.length > 0) {
      this.#processRawBuffer();
    }
  }
  #processRawBuffer() {
    // This method is called when it is known that there is at least one message
    let bufferHead = this.#rawBuffer[0];
    let headerIndex = bufferHead.indexOf(v8Header);
    let nonSerialized = Buffer.alloc(0);

    while (bufferHead && headerIndex !== 0) {
      const nonSerializedData = headerIndex === -1 ?
        bufferHead :
        bufferHead.slice(0, headerIndex);
      nonSerialized = Buffer.concat([nonSerialized, nonSerializedData]);
      this.#rawBufferSize -= TypedArrayPrototypeGetLength(nonSerializedData);
      if (headerIndex === -1) {
        ArrayPrototypeShift(this.#rawBuffer);
      } else {
        this.#rawBuffer[0] = TypedArrayPrototypeSubarray(bufferHead, headerIndex);
      }
      bufferHead = this.#rawBuffer[0];
      headerIndex = bufferHead?.indexOf(v8Header);
    }

    if (TypedArrayPrototypeGetLength(nonSerialized) > 0) {
      this.addToReport({
        __proto__: null,
        type: 'test:stdout',
        data: { __proto__: null, file: this.name, message: nonSerialized.toString('utf-8') },
      });
    }

    while (bufferHead?.length >= kSerializedSizeHeader) {
      // We call `readUInt32BE` manually here, because this is faster than first converting
      // it to a buffer and using `readUInt32BE` on that.
      const fullMessageSize = (
        bufferHead[kV8HeaderLength] << 24 |
        bufferHead[kV8HeaderLength + 1] << 16 |
        bufferHead[kV8HeaderLength + 2] << 8 |
        bufferHead[kV8HeaderLength + 3]
      ) + kSerializedSizeHeader;

      if (this.#rawBufferSize < fullMessageSize) break;

      const concatenatedBuffer = this.#rawBuffer.length === 1 ?
        this.#rawBuffer[0] : Buffer.concat(this.#rawBuffer, this.#rawBufferSize);

      const deserializer = new DefaultDeserializer(
        TypedArrayPrototypeSubarray(concatenatedBuffer, kSerializedSizeHeader, fullMessageSize),
      );

      bufferHead = TypedArrayPrototypeSubarray(concatenatedBuffer, fullMessageSize);
      this.#rawBufferSize = TypedArrayPrototypeGetLength(bufferHead);
      this.#rawBuffer = this.#rawBufferSize !== 0 ? [bufferHead] : [];

      deserializer.readHeader();
      const item = deserializer.readValue();
      this.addToReport(item);
    }
  }
}

function runTestFile(path, filesWatcher, opts) {
  const watchMode = filesWatcher != null;
  const testPath = path === kIsolatedProcessName ? '' : path;
  const testOpts = { __proto__: null, signal: opts.signal };
  const subtest = opts.root.createSubtest(FileTest, testPath, testOpts, async (t) => {
    const args = getRunArgs(path, opts);
    const stdio = ['pipe', 'pipe', 'pipe'];
    const env = { __proto__: null, ...process.env, NODE_TEST_CONTEXT: 'child-v8' };
    if (watchMode) {
      stdio.push('ipc');
      env.WATCH_REPORT_DEPENDENCIES = '1';
    }
    if (opts.root.harness.shouldColorizeTestFiles) {
      env.FORCE_COLOR = '1';
    }

    const child = spawn(
      process.execPath, args,
      {
        __proto__: null,
        signal: t.signal,
        encoding: 'utf8',
        env,
        stdio,
        cwd: opts.cwd,
      },
    );
    if (watchMode) {
      filesWatcher.runningProcesses.set(path, child);
      filesWatcher.watcher.watchChildProcessModules(child, path);
    }

    let err;

    child.on('error', (error) => {
      err = error;
    });

    child.stdout.on('data', (data) => {
      subtest.parseMessage(data);
    });

    const rl = new Interface({ __proto__: null, input: child.stderr });
    rl.on('line', (line) => {
      if (isInspectorMessage(line)) {
        process.stderr.write(line + '\n');
        return;
      }

      // stderr cannot be treated as TAP, per the spec. However, we want to
      // surface stderr lines to improve the DX. Inject each line into the
      // test output as an unknown token as if it came from the TAP parser.
      subtest.addToReport({
        __proto__: null,
        type: 'test:stderr',
        data: { __proto__: null, file: path, message: line + '\n' },
      });
    });

    const { 0: { 0: code, 1: signal } } = await SafePromiseAll([
      once(child, 'exit', { __proto__: null, signal: t.signal }),
      finished(child.stdout, { __proto__: null, signal: t.signal }),
    ]);

    if (watchMode) {
      filesWatcher.runningProcesses.delete(path);
      filesWatcher.runningSubtests.delete(path);
      (async () => {
        try {
          await subTestEnded;
        } finally {
          if (filesWatcher.runningSubtests.size === 0) {
            opts.root.reporter[kEmitMessage]('test:watch:drained');
            opts.root.postRun();
          }
        }
      })();
    }

    if (code !== 0 || signal !== null) {
      if (!err) {
        const failureType = subtest.failedSubtests ? kSubtestsFailed : kTestCodeFailure;
        err = ObjectAssign(new ERR_TEST_FAILURE('test failed', failureType), {
          __proto__: null,
          exitCode: code,
          signal: signal,
          // The stack will not be useful since the failures came from tests
          // in a child process.
          stack: undefined,
        });
      }

      throw err;
    }
  });
  const subTestEnded = subtest.start();
  return subTestEnded;
}

function watchFiles(testFiles, opts) {
  const runningProcesses = new SafeMap();
  const runningSubtests = new SafeMap();
  const watcherMode = opts.hasFiles ? 'filter' : 'all';
  const watcher = new FilesWatcher({ __proto__: null, debounce: 200, mode: watcherMode, signal: opts.signal });
  if (!opts.hasFiles) {
    watcher.watchPath(opts.cwd);
  }
  const filesWatcher = { __proto__: null, watcher, runningProcesses, runningSubtests };
  opts.root.harness.watching = true;

  async function restartTestFile(file) {
    const runningProcess = runningProcesses.get(file);
    if (runningProcess) {
      runningProcess.kill();
      await once(runningProcess, 'exit');
    }
    if (!runningSubtests.size) {
      // Reset the topLevel counter
      opts.root.harness.counters.topLevel = 0;
    }
    await runningSubtests.get(file);
    runningSubtests.set(file, runTestFile(file, filesWatcher, opts));
  }

  // Watch for changes in current filtered files
  watcher.on('changed', ({ owners, eventType }) => {
    if (!opts.hasFiles && (eventType === 'rename' || eventType === 'change')) {
      const updatedTestFiles = createTestFileList(opts.globPatterns, opts.cwd);
      const newFileName = ArrayPrototypeFind(updatedTestFiles, (x) => !ArrayPrototypeIncludes(testFiles, x));
      const previousFileName = ArrayPrototypeFind(testFiles, (x) => !ArrayPrototypeIncludes(updatedTestFiles, x));

      testFiles = updatedTestFiles;

      // When file renamed (created / deleted) we need to update the watcher
      if (newFileName) {
        owners = new SafeSet().add(newFileName);
        const resolveFileName = isAbsolute(newFileName) ? newFileName : resolve(opts.cwd, newFileName);
        watcher.filterFile(resolveFileName, owners);
      }

      if (!newFileName && previousFileName) {
        return; // Avoid rerunning files when file deleted
      }
    }

    if (opts.isolation === 'none') {
      PromisePrototypeThen(restartTestFile(kIsolatedProcessName), undefined, (error) => {
        triggerUncaughtException(error, true /* fromPromise */);
      });
    } else {
      watcher.unfilterFilesOwnedBy(owners);
      PromisePrototypeThen(SafePromiseAllReturnVoid(testFiles, async (file) => {
        if (!owners.has(file)) {
          return;
        }

        await restartTestFile(file);
      }, undefined, (error) => {
        triggerUncaughtException(error, true /* fromPromise */);
      }));
    }
  });
  if (opts.signal) {
    kResistStopPropagation ??= require('internal/event_target').kResistStopPropagation;
    opts.signal.addEventListener(
      'abort',
      () => {
        opts.root.harness.watching = false;
        opts.root.postRun();
      },
      { __proto__: null, once: true, [kResistStopPropagation]: true },
    );
  }

  return filesWatcher;
}

function run(options = kEmptyObject) {
  validateObject(options, 'options');

  let {
    testNamePatterns,
    testSkipPatterns,
    shard,
    coverageExcludeGlobs,
    coverageIncludeGlobs,
  } = options;
  const {
    concurrency,
    timeout,
    signal,
    files,
    forceExit,
    inspectPort,
    isolation = 'process',
    watch,
    setup,
    only,
    globPatterns,
    coverage = false,
    lineCoverage = 0,
    branchCoverage = 0,
    functionCoverage = 0,
    execArgv = [],
    argv = [],
    cwd = process.cwd(),
  } = options;

  if (files != null) {
    validateArray(files, 'options.files');
  }
  if (watch != null) {
    validateBoolean(watch, 'options.watch');
  }
  if (forceExit != null) {
    validateBoolean(forceExit, 'options.forceExit');

    if (forceExit && watch) {
      throw new ERR_INVALID_ARG_VALUE(
        'options.forceExit', watch, 'is not supported with watch mode',
      );
    }
  }
  if (only != null) {
    validateBoolean(only, 'options.only');
  }
  if (globPatterns != null) {
    validateArray(globPatterns, 'options.globPatterns');
  }

  validateString(cwd, 'options.cwd');

  if (globPatterns?.length > 0 && files?.length > 0) {
    throw new ERR_INVALID_ARG_VALUE(
      'options.globPatterns', globPatterns, 'is not supported when specifying \'options.files\'',
    );
  }

  if (shard != null) {
    validateObject(shard, 'options.shard');
    // Avoid re-evaluating the shard object in case it's a getter
    shard = { __proto__: null, index: shard.index, total: shard.total };

    validateInteger(shard.total, 'options.shard.total', 1);
    validateInteger(shard.index, 'options.shard.index', 1, shard.total);

    if (watch) {
      throw new ERR_INVALID_ARG_VALUE('options.shard', watch, 'shards not supported with watch mode');
    }
  }
  if (setup != null) {
    validateFunction(setup, 'options.setup');
  }
  if (testNamePatterns != null) {
    if (!ArrayIsArray(testNamePatterns)) {
      testNamePatterns = [testNamePatterns];
    }

    testNamePatterns = ArrayPrototypeMap(testNamePatterns, (value, i) => {
      if (isRegExp(value)) {
        return value;
      }
      const name = `options.testNamePatterns[${i}]`;
      if (typeof value === 'string') {
        return convertStringToRegExp(value, name);
      }
      throw new ERR_INVALID_ARG_TYPE(name, ['string', 'RegExp'], value);
    });
  }
  if (testSkipPatterns != null) {
    if (!ArrayIsArray(testSkipPatterns)) {
      testSkipPatterns = [testSkipPatterns];
    }

    testSkipPatterns = ArrayPrototypeMap(testSkipPatterns, (value, i) => {
      if (isRegExp(value)) {
        return value;
      }
      const name = `options.testSkipPatterns[${i}]`;
      if (typeof value === 'string') {
        return convertStringToRegExp(value, name);
      }
      throw new ERR_INVALID_ARG_TYPE(name, ['string', 'RegExp'], value);
    });
  }
  validateOneOf(isolation, 'options.isolation', ['process', 'none']);
  validateBoolean(coverage, 'options.coverage');
  if (coverageExcludeGlobs != null) {
    if (!ArrayIsArray(coverageExcludeGlobs)) {
      coverageExcludeGlobs = [coverageExcludeGlobs];
    }
    validateStringArray(coverageExcludeGlobs, 'options.coverageExcludeGlobs');
  }
  if (coverageIncludeGlobs != null) {
    if (!ArrayIsArray(coverageIncludeGlobs)) {
      coverageIncludeGlobs = [coverageIncludeGlobs];
    }
    validateStringArray(coverageIncludeGlobs, 'options.coverageIncludeGlobs');
  }
  validateInteger(lineCoverage, 'options.lineCoverage', 0, 100);
  validateInteger(branchCoverage, 'options.branchCoverage', 0, 100);
  validateInteger(functionCoverage, 'options.functionCoverage', 0, 100);

  validateStringArray(argv, 'options.argv');
  validateStringArray(execArgv, 'options.execArgv');

  const rootTestOptions = { __proto__: null, concurrency, timeout, signal };
  const globalOptions = {
    __proto__: null,
    // parseCommandLine() should not be used here. However, The existing run()
    // behavior has relied on it, so removing it must be done in a semver major.
    ...parseCommandLine(),
    setup,  // This line can be removed when parseCommandLine() is removed here.
    coverage,
    coverageExcludeGlobs,
    coverageIncludeGlobs,
    lineCoverage: lineCoverage,
    branchCoverage: branchCoverage,
    functionCoverage: functionCoverage,
    cwd,
  };
  const root = createTestTree(rootTestOptions, globalOptions);
  let testFiles = files ?? createTestFileList(globPatterns, cwd);

  if (shard) {
    testFiles = ArrayPrototypeFilter(testFiles, (_, index) => index % shard.total === shard.index - 1);
  }

  let teardown;
  let postRun;
  let filesWatcher;
  let runFiles;
  const opts = {
    __proto__: null,
    root,
    signal,
    inspectPort,
    testNamePatterns,
    testSkipPatterns,
    hasFiles: files != null,
    globPatterns,
    only,
    forceExit,
    cwd,
    isolation,
    argv,
    execArgv,
  };

  if (isolation === 'process') {
    if (process.env.NODE_TEST_CONTEXT !== undefined) {
      process.emitWarning('node:test run() is being called recursively within a test file. skipping running files.');
      root.postRun();
      return root.reporter;
    }

    if (watch) {
      filesWatcher = watchFiles(testFiles, opts);
    } else {
      postRun = () => root.postRun();
      teardown = () => root.harness.teardown();
    }

    runFiles = () => {
      root.harness.bootstrapPromise = null;
      root.harness.buildPromise = null;
      return SafePromiseAllSettledReturnVoid(testFiles, (path) => {
        const subtest = runTestFile(path, filesWatcher, opts);
        filesWatcher?.runningSubtests.set(path, subtest);
        return subtest;
      });
    };
  } else if (isolation === 'none') {
    if (watch) {
      const absoluteTestFiles = ArrayPrototypeMap(testFiles, (file) => (isAbsolute(file) ? file : resolve(cwd, file)));
      filesWatcher = watchFiles(absoluteTestFiles, opts);
      runFiles = async () => {
        root.harness.bootstrapPromise = null;
        root.harness.buildPromise = null;
        const subtest = runTestFile(kIsolatedProcessName, filesWatcher, opts);
        filesWatcher?.runningSubtests.set(kIsolatedProcessName, subtest);
        return subtest;
      };
    } else {
      runFiles = async () => {
        const { promise, resolve: finishBootstrap } = PromiseWithResolvers();

        await root.runInAsyncScope(async () => {
          const parentURL = pathToFileURL(cwd + sep).href;
          const cascadedLoader = esmLoader.getOrInitializeCascadedLoader();
          let topLevelTestCount = 0;

          root.harness.bootstrapPromise = promise;

          const userImports = getOptionValue('--import');
          for (let i = 0; i < userImports.length; i++) {
            await cascadedLoader.import(userImports[i], parentURL, kEmptyObject);
          }

          for (let i = 0; i < testFiles.length; ++i) {
            const testFile = testFiles[i];
            const fileURL = pathToFileURL(resolve(cwd, testFile));
            const parent = i === 0 ? undefined : parentURL;
            let threw = false;
            let importError;

            root.entryFile = resolve(testFile);
            debug('loading test file:', fileURL.href);
            try {
              await cascadedLoader.import(fileURL, parent, { __proto__: null });
            } catch (err) {
              threw = true;
              importError = err;
            }

            debug(
              'loaded "%s": top level test count before = %d and after = %d',
              testFile,
              topLevelTestCount,
              root.subtests.length,
            );
            if (topLevelTestCount === root.subtests.length) {
              // This file had no tests in it. Add the placeholder test.
              const subtest = root.createSubtest(Test, testFile);
              if (threw) {
                subtest.fail(importError);
              }
              startSubtestAfterBootstrap(subtest);
            }

            topLevelTestCount = root.subtests.length;
          }
        });

        debug('beginning test execution');
        root.entryFile = null;
        finishBootstrap();
        root.processPendingSubtests();
      };
    }
  }

  const runChain = async () => {
    if (typeof setup === 'function') {
      await setup(root.reporter);
    }

    await runFiles();
    postRun?.();
    teardown?.();
  };

  runChain();
  return root.reporter;
}

module.exports = {
  FileTest, // Exported for tests only
  run,
};
