11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ciconst {
31cb0ef41Sopenharmony_ci  ArrayPrototypeForEach,
41cb0ef41Sopenharmony_ci  FunctionPrototypeBind,
51cb0ef41Sopenharmony_ci  PromiseResolve,
61cb0ef41Sopenharmony_ci  SafeMap,
71cb0ef41Sopenharmony_ci} = primordials;
81cb0ef41Sopenharmony_ciconst { getCallerLocation } = internalBinding('util');
91cb0ef41Sopenharmony_ciconst {
101cb0ef41Sopenharmony_ci  createHook,
111cb0ef41Sopenharmony_ci  executionAsyncId,
121cb0ef41Sopenharmony_ci} = require('async_hooks');
131cb0ef41Sopenharmony_ciconst {
141cb0ef41Sopenharmony_ci  codes: {
151cb0ef41Sopenharmony_ci    ERR_TEST_FAILURE,
161cb0ef41Sopenharmony_ci  },
171cb0ef41Sopenharmony_ci} = require('internal/errors');
181cb0ef41Sopenharmony_ciconst { kEmptyObject } = require('internal/util');
191cb0ef41Sopenharmony_ciconst { kCancelledByParent, Test, Suite } = require('internal/test_runner/test');
201cb0ef41Sopenharmony_ciconst {
211cb0ef41Sopenharmony_ci  parseCommandLine,
221cb0ef41Sopenharmony_ci  reporterScope,
231cb0ef41Sopenharmony_ci  setupTestReporters,
241cb0ef41Sopenharmony_ci} = require('internal/test_runner/utils');
251cb0ef41Sopenharmony_ciconst { bigint: hrtime } = process.hrtime;
261cb0ef41Sopenharmony_ci
271cb0ef41Sopenharmony_ciconst testResources = new SafeMap();
281cb0ef41Sopenharmony_ci
291cb0ef41Sopenharmony_citestResources.set(reporterScope.asyncId(), reporterScope);
301cb0ef41Sopenharmony_ci
311cb0ef41Sopenharmony_cifunction createTestTree(options = kEmptyObject) {
321cb0ef41Sopenharmony_ci  return setup(new Test({ __proto__: null, ...options, name: '<root>' }));
331cb0ef41Sopenharmony_ci}
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_cifunction createProcessEventHandler(eventName, rootTest) {
361cb0ef41Sopenharmony_ci  return (err) => {
371cb0ef41Sopenharmony_ci    if (!rootTest.harness.bootstrapComplete) {
381cb0ef41Sopenharmony_ci      // Something went wrong during the asynchronous portion of bootstrapping
391cb0ef41Sopenharmony_ci      // the test runner. Since the test runner is not setup properly, we can't
401cb0ef41Sopenharmony_ci      // do anything but throw the error.
411cb0ef41Sopenharmony_ci      throw err;
421cb0ef41Sopenharmony_ci    }
431cb0ef41Sopenharmony_ci
441cb0ef41Sopenharmony_ci    const test = testResources.get(executionAsyncId());
451cb0ef41Sopenharmony_ci
461cb0ef41Sopenharmony_ci    // Check if this error is coming from a reporter. If it is, throw it.
471cb0ef41Sopenharmony_ci    if (test === reporterScope) {
481cb0ef41Sopenharmony_ci      throw err;
491cb0ef41Sopenharmony_ci    }
501cb0ef41Sopenharmony_ci
511cb0ef41Sopenharmony_ci    // Check if this error is coming from a test. If it is, fail the test.
521cb0ef41Sopenharmony_ci    if (!test || test.finished) {
531cb0ef41Sopenharmony_ci      // If the test is already finished or the resource that created the error
541cb0ef41Sopenharmony_ci      // is not mapped to a Test, report this as a top level diagnostic.
551cb0ef41Sopenharmony_ci      let msg;
561cb0ef41Sopenharmony_ci
571cb0ef41Sopenharmony_ci      if (test) {
581cb0ef41Sopenharmony_ci        msg = `Warning: Test "${test.name}" generated asynchronous ` +
591cb0ef41Sopenharmony_ci          'activity after the test ended. This activity created the error ' +
601cb0ef41Sopenharmony_ci          `"${err}" and would have caused the test to fail, but instead ` +
611cb0ef41Sopenharmony_ci          `triggered an ${eventName} event.`;
621cb0ef41Sopenharmony_ci      } else {
631cb0ef41Sopenharmony_ci        msg = 'Warning: A resource generated asynchronous activity after ' +
641cb0ef41Sopenharmony_ci          `the test ended. This activity created the error "${err}" which ` +
651cb0ef41Sopenharmony_ci          `triggered an ${eventName} event, caught by the test runner.`;
661cb0ef41Sopenharmony_ci      }
671cb0ef41Sopenharmony_ci
681cb0ef41Sopenharmony_ci      rootTest.diagnostic(msg);
691cb0ef41Sopenharmony_ci      process.exitCode = 1;
701cb0ef41Sopenharmony_ci      return;
711cb0ef41Sopenharmony_ci    }
721cb0ef41Sopenharmony_ci
731cb0ef41Sopenharmony_ci    test.fail(new ERR_TEST_FAILURE(err, eventName));
741cb0ef41Sopenharmony_ci    test.postRun();
751cb0ef41Sopenharmony_ci  };
761cb0ef41Sopenharmony_ci}
771cb0ef41Sopenharmony_ci
781cb0ef41Sopenharmony_cifunction configureCoverage(rootTest, globalOptions) {
791cb0ef41Sopenharmony_ci  if (!globalOptions.coverage) {
801cb0ef41Sopenharmony_ci    return null;
811cb0ef41Sopenharmony_ci  }
821cb0ef41Sopenharmony_ci
831cb0ef41Sopenharmony_ci  const { setupCoverage } = require('internal/test_runner/coverage');
841cb0ef41Sopenharmony_ci
851cb0ef41Sopenharmony_ci  try {
861cb0ef41Sopenharmony_ci    return setupCoverage();
871cb0ef41Sopenharmony_ci  } catch (err) {
881cb0ef41Sopenharmony_ci    const msg = `Warning: Code coverage could not be enabled. ${err}`;
891cb0ef41Sopenharmony_ci
901cb0ef41Sopenharmony_ci    rootTest.diagnostic(msg);
911cb0ef41Sopenharmony_ci    process.exitCode = 1;
921cb0ef41Sopenharmony_ci  }
931cb0ef41Sopenharmony_ci}
941cb0ef41Sopenharmony_ci
951cb0ef41Sopenharmony_cifunction collectCoverage(rootTest, coverage) {
961cb0ef41Sopenharmony_ci  if (!coverage) {
971cb0ef41Sopenharmony_ci    return null;
981cb0ef41Sopenharmony_ci  }
991cb0ef41Sopenharmony_ci
1001cb0ef41Sopenharmony_ci  let summary = null;
1011cb0ef41Sopenharmony_ci
1021cb0ef41Sopenharmony_ci  try {
1031cb0ef41Sopenharmony_ci    summary = coverage.summary();
1041cb0ef41Sopenharmony_ci    coverage.cleanup();
1051cb0ef41Sopenharmony_ci  } catch (err) {
1061cb0ef41Sopenharmony_ci    const op = summary ? 'clean up' : 'report';
1071cb0ef41Sopenharmony_ci    const msg = `Warning: Could not ${op} code coverage. ${err}`;
1081cb0ef41Sopenharmony_ci
1091cb0ef41Sopenharmony_ci    rootTest.diagnostic(msg);
1101cb0ef41Sopenharmony_ci    process.exitCode = 1;
1111cb0ef41Sopenharmony_ci  }
1121cb0ef41Sopenharmony_ci
1131cb0ef41Sopenharmony_ci  return summary;
1141cb0ef41Sopenharmony_ci}
1151cb0ef41Sopenharmony_ci
1161cb0ef41Sopenharmony_cifunction setup(root) {
1171cb0ef41Sopenharmony_ci  if (root.startTime !== null) {
1181cb0ef41Sopenharmony_ci    return root;
1191cb0ef41Sopenharmony_ci  }
1201cb0ef41Sopenharmony_ci
1211cb0ef41Sopenharmony_ci  // Parse the command line options before the hook is enabled. We don't want
1221cb0ef41Sopenharmony_ci  // global input validation errors to end up in the uncaughtException handler.
1231cb0ef41Sopenharmony_ci  const globalOptions = parseCommandLine();
1241cb0ef41Sopenharmony_ci
1251cb0ef41Sopenharmony_ci  const hook = createHook({
1261cb0ef41Sopenharmony_ci    __proto__: null,
1271cb0ef41Sopenharmony_ci    init(asyncId, type, triggerAsyncId, resource) {
1281cb0ef41Sopenharmony_ci      if (resource instanceof Test) {
1291cb0ef41Sopenharmony_ci        testResources.set(asyncId, resource);
1301cb0ef41Sopenharmony_ci        return;
1311cb0ef41Sopenharmony_ci      }
1321cb0ef41Sopenharmony_ci
1331cb0ef41Sopenharmony_ci      const parent = testResources.get(triggerAsyncId);
1341cb0ef41Sopenharmony_ci
1351cb0ef41Sopenharmony_ci      if (parent !== undefined) {
1361cb0ef41Sopenharmony_ci        testResources.set(asyncId, parent);
1371cb0ef41Sopenharmony_ci      }
1381cb0ef41Sopenharmony_ci    },
1391cb0ef41Sopenharmony_ci    destroy(asyncId) {
1401cb0ef41Sopenharmony_ci      testResources.delete(asyncId);
1411cb0ef41Sopenharmony_ci    },
1421cb0ef41Sopenharmony_ci  });
1431cb0ef41Sopenharmony_ci
1441cb0ef41Sopenharmony_ci  hook.enable();
1451cb0ef41Sopenharmony_ci
1461cb0ef41Sopenharmony_ci  const exceptionHandler =
1471cb0ef41Sopenharmony_ci    createProcessEventHandler('uncaughtException', root);
1481cb0ef41Sopenharmony_ci  const rejectionHandler =
1491cb0ef41Sopenharmony_ci    createProcessEventHandler('unhandledRejection', root);
1501cb0ef41Sopenharmony_ci  const coverage = configureCoverage(root, globalOptions);
1511cb0ef41Sopenharmony_ci  const exitHandler = () => {
1521cb0ef41Sopenharmony_ci    root.postRun(new ERR_TEST_FAILURE(
1531cb0ef41Sopenharmony_ci      'Promise resolution is still pending but the event loop has already resolved',
1541cb0ef41Sopenharmony_ci      kCancelledByParent));
1551cb0ef41Sopenharmony_ci
1561cb0ef41Sopenharmony_ci    hook.disable();
1571cb0ef41Sopenharmony_ci    process.removeListener('unhandledRejection', rejectionHandler);
1581cb0ef41Sopenharmony_ci    process.removeListener('uncaughtException', exceptionHandler);
1591cb0ef41Sopenharmony_ci  };
1601cb0ef41Sopenharmony_ci
1611cb0ef41Sopenharmony_ci  const terminationHandler = () => {
1621cb0ef41Sopenharmony_ci    exitHandler();
1631cb0ef41Sopenharmony_ci    process.exit();
1641cb0ef41Sopenharmony_ci  };
1651cb0ef41Sopenharmony_ci
1661cb0ef41Sopenharmony_ci  process.on('uncaughtException', exceptionHandler);
1671cb0ef41Sopenharmony_ci  process.on('unhandledRejection', rejectionHandler);
1681cb0ef41Sopenharmony_ci  process.on('beforeExit', exitHandler);
1691cb0ef41Sopenharmony_ci  // TODO(MoLow): Make it configurable to hook when isTestRunner === false.
1701cb0ef41Sopenharmony_ci  if (globalOptions.isTestRunner) {
1711cb0ef41Sopenharmony_ci    process.on('SIGINT', terminationHandler);
1721cb0ef41Sopenharmony_ci    process.on('SIGTERM', terminationHandler);
1731cb0ef41Sopenharmony_ci  }
1741cb0ef41Sopenharmony_ci
1751cb0ef41Sopenharmony_ci  root.harness = {
1761cb0ef41Sopenharmony_ci    __proto__: null,
1771cb0ef41Sopenharmony_ci    bootstrapComplete: false,
1781cb0ef41Sopenharmony_ci    coverage: FunctionPrototypeBind(collectCoverage, null, root, coverage),
1791cb0ef41Sopenharmony_ci    counters: {
1801cb0ef41Sopenharmony_ci      __proto__: null,
1811cb0ef41Sopenharmony_ci      all: 0,
1821cb0ef41Sopenharmony_ci      failed: 0,
1831cb0ef41Sopenharmony_ci      passed: 0,
1841cb0ef41Sopenharmony_ci      cancelled: 0,
1851cb0ef41Sopenharmony_ci      skipped: 0,
1861cb0ef41Sopenharmony_ci      todo: 0,
1871cb0ef41Sopenharmony_ci      topLevel: 0,
1881cb0ef41Sopenharmony_ci      suites: 0,
1891cb0ef41Sopenharmony_ci    },
1901cb0ef41Sopenharmony_ci    shouldColorizeTestFiles: false,
1911cb0ef41Sopenharmony_ci  };
1921cb0ef41Sopenharmony_ci  root.startTime = hrtime();
1931cb0ef41Sopenharmony_ci  return root;
1941cb0ef41Sopenharmony_ci}
1951cb0ef41Sopenharmony_ci
1961cb0ef41Sopenharmony_cilet globalRoot;
1971cb0ef41Sopenharmony_cilet reportersSetup;
1981cb0ef41Sopenharmony_cifunction getGlobalRoot() {
1991cb0ef41Sopenharmony_ci  if (!globalRoot) {
2001cb0ef41Sopenharmony_ci    globalRoot = createTestTree();
2011cb0ef41Sopenharmony_ci    globalRoot.reporter.on('test:fail', (data) => {
2021cb0ef41Sopenharmony_ci      if (data.todo === undefined || data.todo === false) {
2031cb0ef41Sopenharmony_ci        process.exitCode = 1;
2041cb0ef41Sopenharmony_ci      }
2051cb0ef41Sopenharmony_ci    });
2061cb0ef41Sopenharmony_ci    reportersSetup = setupTestReporters(globalRoot);
2071cb0ef41Sopenharmony_ci  }
2081cb0ef41Sopenharmony_ci  return globalRoot;
2091cb0ef41Sopenharmony_ci}
2101cb0ef41Sopenharmony_ci
2111cb0ef41Sopenharmony_ciasync function startSubtest(subtest) {
2121cb0ef41Sopenharmony_ci  await reportersSetup;
2131cb0ef41Sopenharmony_ci  getGlobalRoot().harness.bootstrapComplete = true;
2141cb0ef41Sopenharmony_ci  await subtest.start();
2151cb0ef41Sopenharmony_ci}
2161cb0ef41Sopenharmony_ci
2171cb0ef41Sopenharmony_cifunction runInParentContext(Factory) {
2181cb0ef41Sopenharmony_ci  function run(name, options, fn, overrides) {
2191cb0ef41Sopenharmony_ci    const parent = testResources.get(executionAsyncId()) || getGlobalRoot();
2201cb0ef41Sopenharmony_ci    const subtest = parent.createSubtest(Factory, name, options, fn, overrides);
2211cb0ef41Sopenharmony_ci    if (!(parent instanceof Suite)) {
2221cb0ef41Sopenharmony_ci      return startSubtest(subtest);
2231cb0ef41Sopenharmony_ci    }
2241cb0ef41Sopenharmony_ci    return PromiseResolve();
2251cb0ef41Sopenharmony_ci  }
2261cb0ef41Sopenharmony_ci
2271cb0ef41Sopenharmony_ci  const test = (name, options, fn) => {
2281cb0ef41Sopenharmony_ci    const overrides = {
2291cb0ef41Sopenharmony_ci      __proto__: null,
2301cb0ef41Sopenharmony_ci      loc: getCallerLocation(),
2311cb0ef41Sopenharmony_ci    };
2321cb0ef41Sopenharmony_ci
2331cb0ef41Sopenharmony_ci    return run(name, options, fn, overrides);
2341cb0ef41Sopenharmony_ci  };
2351cb0ef41Sopenharmony_ci  ArrayPrototypeForEach(['skip', 'todo', 'only'], (keyword) => {
2361cb0ef41Sopenharmony_ci    test[keyword] = (name, options, fn) => {
2371cb0ef41Sopenharmony_ci      const overrides = {
2381cb0ef41Sopenharmony_ci        __proto__: null,
2391cb0ef41Sopenharmony_ci        [keyword]: true,
2401cb0ef41Sopenharmony_ci        loc: getCallerLocation(),
2411cb0ef41Sopenharmony_ci      };
2421cb0ef41Sopenharmony_ci
2431cb0ef41Sopenharmony_ci      return run(name, options, fn, overrides);
2441cb0ef41Sopenharmony_ci    };
2451cb0ef41Sopenharmony_ci  });
2461cb0ef41Sopenharmony_ci  return test;
2471cb0ef41Sopenharmony_ci}
2481cb0ef41Sopenharmony_ci
2491cb0ef41Sopenharmony_cifunction hook(hook) {
2501cb0ef41Sopenharmony_ci  return (fn, options) => {
2511cb0ef41Sopenharmony_ci    const parent = testResources.get(executionAsyncId()) || getGlobalRoot();
2521cb0ef41Sopenharmony_ci    parent.createHook(hook, fn, {
2531cb0ef41Sopenharmony_ci      __proto__: null,
2541cb0ef41Sopenharmony_ci      ...options,
2551cb0ef41Sopenharmony_ci      parent,
2561cb0ef41Sopenharmony_ci      hookType: hook,
2571cb0ef41Sopenharmony_ci      loc: getCallerLocation(),
2581cb0ef41Sopenharmony_ci    });
2591cb0ef41Sopenharmony_ci  };
2601cb0ef41Sopenharmony_ci}
2611cb0ef41Sopenharmony_ci
2621cb0ef41Sopenharmony_cimodule.exports = {
2631cb0ef41Sopenharmony_ci  createTestTree,
2641cb0ef41Sopenharmony_ci  test: runInParentContext(Test),
2651cb0ef41Sopenharmony_ci  describe: runInParentContext(Suite),
2661cb0ef41Sopenharmony_ci  it: runInParentContext(Test),
2671cb0ef41Sopenharmony_ci  before: hook('before'),
2681cb0ef41Sopenharmony_ci  after: hook('after'),
2691cb0ef41Sopenharmony_ci  beforeEach: hook('beforeEach'),
2701cb0ef41Sopenharmony_ci  afterEach: hook('afterEach'),
2711cb0ef41Sopenharmony_ci};
272