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