11cb0ef41Sopenharmony_ci'use strict'; 21cb0ef41Sopenharmony_ciconst { 31cb0ef41Sopenharmony_ci ArrayPrototypePush, 41cb0ef41Sopenharmony_ci ArrayPrototypePushApply, 51cb0ef41Sopenharmony_ci ArrayPrototypeReduce, 61cb0ef41Sopenharmony_ci ArrayPrototypeShift, 71cb0ef41Sopenharmony_ci ArrayPrototypeSlice, 81cb0ef41Sopenharmony_ci ArrayPrototypeSome, 91cb0ef41Sopenharmony_ci ArrayPrototypeUnshift, 101cb0ef41Sopenharmony_ci FunctionPrototype, 111cb0ef41Sopenharmony_ci MathMax, 121cb0ef41Sopenharmony_ci Number, 131cb0ef41Sopenharmony_ci ObjectSeal, 141cb0ef41Sopenharmony_ci PromisePrototypeThen, 151cb0ef41Sopenharmony_ci PromiseResolve, 161cb0ef41Sopenharmony_ci SafePromisePrototypeFinally, 171cb0ef41Sopenharmony_ci ReflectApply, 181cb0ef41Sopenharmony_ci RegExpPrototypeExec, 191cb0ef41Sopenharmony_ci SafeMap, 201cb0ef41Sopenharmony_ci SafeSet, 211cb0ef41Sopenharmony_ci SafePromiseAll, 221cb0ef41Sopenharmony_ci SafePromiseRace, 231cb0ef41Sopenharmony_ci SymbolDispose, 241cb0ef41Sopenharmony_ci ObjectDefineProperty, 251cb0ef41Sopenharmony_ci Symbol, 261cb0ef41Sopenharmony_ci} = primordials; 271cb0ef41Sopenharmony_ciconst { getCallerLocation } = internalBinding('util'); 281cb0ef41Sopenharmony_ciconst { addAbortListener } = require('events'); 291cb0ef41Sopenharmony_ciconst { AsyncResource } = require('async_hooks'); 301cb0ef41Sopenharmony_ciconst { AbortController } = require('internal/abort_controller'); 311cb0ef41Sopenharmony_ciconst { 321cb0ef41Sopenharmony_ci codes: { 331cb0ef41Sopenharmony_ci ERR_INVALID_ARG_TYPE, 341cb0ef41Sopenharmony_ci ERR_TEST_FAILURE, 351cb0ef41Sopenharmony_ci }, 361cb0ef41Sopenharmony_ci AbortError, 371cb0ef41Sopenharmony_ci} = require('internal/errors'); 381cb0ef41Sopenharmony_ciconst { MockTracker } = require('internal/test_runner/mock/mock'); 391cb0ef41Sopenharmony_ciconst { TestsStream } = require('internal/test_runner/tests_stream'); 401cb0ef41Sopenharmony_ciconst { 411cb0ef41Sopenharmony_ci createDeferredCallback, 421cb0ef41Sopenharmony_ci countCompletedTest, 431cb0ef41Sopenharmony_ci isTestFailureError, 441cb0ef41Sopenharmony_ci parseCommandLine, 451cb0ef41Sopenharmony_ci} = require('internal/test_runner/utils'); 461cb0ef41Sopenharmony_ciconst { 471cb0ef41Sopenharmony_ci createDeferredPromise, 481cb0ef41Sopenharmony_ci kEmptyObject, 491cb0ef41Sopenharmony_ci once: runOnce, 501cb0ef41Sopenharmony_ci} = require('internal/util'); 511cb0ef41Sopenharmony_ciconst { isPromise } = require('internal/util/types'); 521cb0ef41Sopenharmony_ciconst { 531cb0ef41Sopenharmony_ci validateAbortSignal, 541cb0ef41Sopenharmony_ci validateNumber, 551cb0ef41Sopenharmony_ci validateOneOf, 561cb0ef41Sopenharmony_ci validateUint32, 571cb0ef41Sopenharmony_ci} = require('internal/validators'); 581cb0ef41Sopenharmony_ciconst { setTimeout } = require('timers'); 591cb0ef41Sopenharmony_ciconst { TIMEOUT_MAX } = require('internal/timers'); 601cb0ef41Sopenharmony_ciconst { availableParallelism } = require('os'); 611cb0ef41Sopenharmony_ciconst { bigint: hrtime } = process.hrtime; 621cb0ef41Sopenharmony_ciconst kCallbackAndPromisePresent = 'callbackAndPromisePresent'; 631cb0ef41Sopenharmony_ciconst kCancelledByParent = 'cancelledByParent'; 641cb0ef41Sopenharmony_ciconst kAborted = 'testAborted'; 651cb0ef41Sopenharmony_ciconst kParentAlreadyFinished = 'parentAlreadyFinished'; 661cb0ef41Sopenharmony_ciconst kSubtestsFailed = 'subtestsFailed'; 671cb0ef41Sopenharmony_ciconst kTestCodeFailure = 'testCodeFailure'; 681cb0ef41Sopenharmony_ciconst kTestTimeoutFailure = 'testTimeoutFailure'; 691cb0ef41Sopenharmony_ciconst kHookFailure = 'hookFailed'; 701cb0ef41Sopenharmony_ciconst kDefaultTimeout = null; 711cb0ef41Sopenharmony_ciconst noop = FunctionPrototype; 721cb0ef41Sopenharmony_ciconst kShouldAbort = Symbol('kShouldAbort'); 731cb0ef41Sopenharmony_ciconst kFilename = process.argv?.[1]; 741cb0ef41Sopenharmony_ciconst kHookNames = ObjectSeal(['before', 'after', 'beforeEach', 'afterEach']); 751cb0ef41Sopenharmony_ciconst kUnwrapErrors = new SafeSet() 761cb0ef41Sopenharmony_ci .add(kTestCodeFailure).add(kHookFailure) 771cb0ef41Sopenharmony_ci .add('uncaughtException').add('unhandledRejection'); 781cb0ef41Sopenharmony_ciconst { testNamePatterns, testOnlyFlag } = parseCommandLine(); 791cb0ef41Sopenharmony_cilet kResistStopPropagation; 801cb0ef41Sopenharmony_ci 811cb0ef41Sopenharmony_cifunction stopTest(timeout, signal) { 821cb0ef41Sopenharmony_ci const deferred = createDeferredPromise(); 831cb0ef41Sopenharmony_ci const abortListener = addAbortListener(signal, deferred.resolve); 841cb0ef41Sopenharmony_ci let timer; 851cb0ef41Sopenharmony_ci let disposeFunction; 861cb0ef41Sopenharmony_ci 871cb0ef41Sopenharmony_ci if (timeout === kDefaultTimeout) { 881cb0ef41Sopenharmony_ci disposeFunction = abortListener[SymbolDispose]; 891cb0ef41Sopenharmony_ci } else { 901cb0ef41Sopenharmony_ci timer = setTimeout(() => deferred.resolve(), timeout); 911cb0ef41Sopenharmony_ci timer.unref(); 921cb0ef41Sopenharmony_ci 931cb0ef41Sopenharmony_ci ObjectDefineProperty(deferred, 'promise', { 941cb0ef41Sopenharmony_ci __proto__: null, 951cb0ef41Sopenharmony_ci configurable: true, 961cb0ef41Sopenharmony_ci writable: true, 971cb0ef41Sopenharmony_ci value: PromisePrototypeThen(deferred.promise, () => { 981cb0ef41Sopenharmony_ci throw new ERR_TEST_FAILURE( 991cb0ef41Sopenharmony_ci `test timed out after ${timeout}ms`, 1001cb0ef41Sopenharmony_ci kTestTimeoutFailure, 1011cb0ef41Sopenharmony_ci ); 1021cb0ef41Sopenharmony_ci }), 1031cb0ef41Sopenharmony_ci }); 1041cb0ef41Sopenharmony_ci 1051cb0ef41Sopenharmony_ci disposeFunction = () => { 1061cb0ef41Sopenharmony_ci abortListener[SymbolDispose](); 1071cb0ef41Sopenharmony_ci timer[SymbolDispose](); 1081cb0ef41Sopenharmony_ci }; 1091cb0ef41Sopenharmony_ci } 1101cb0ef41Sopenharmony_ci 1111cb0ef41Sopenharmony_ci ObjectDefineProperty(deferred.promise, SymbolDispose, { 1121cb0ef41Sopenharmony_ci __proto__: null, 1131cb0ef41Sopenharmony_ci configurable: true, 1141cb0ef41Sopenharmony_ci writable: true, 1151cb0ef41Sopenharmony_ci value: disposeFunction, 1161cb0ef41Sopenharmony_ci }); 1171cb0ef41Sopenharmony_ci return deferred.promise; 1181cb0ef41Sopenharmony_ci} 1191cb0ef41Sopenharmony_ci 1201cb0ef41Sopenharmony_ciclass TestContext { 1211cb0ef41Sopenharmony_ci #test; 1221cb0ef41Sopenharmony_ci 1231cb0ef41Sopenharmony_ci constructor(test) { 1241cb0ef41Sopenharmony_ci this.#test = test; 1251cb0ef41Sopenharmony_ci } 1261cb0ef41Sopenharmony_ci 1271cb0ef41Sopenharmony_ci get signal() { 1281cb0ef41Sopenharmony_ci return this.#test.signal; 1291cb0ef41Sopenharmony_ci } 1301cb0ef41Sopenharmony_ci 1311cb0ef41Sopenharmony_ci get name() { 1321cb0ef41Sopenharmony_ci return this.#test.name; 1331cb0ef41Sopenharmony_ci } 1341cb0ef41Sopenharmony_ci 1351cb0ef41Sopenharmony_ci diagnostic(message) { 1361cb0ef41Sopenharmony_ci this.#test.diagnostic(message); 1371cb0ef41Sopenharmony_ci } 1381cb0ef41Sopenharmony_ci 1391cb0ef41Sopenharmony_ci get mock() { 1401cb0ef41Sopenharmony_ci this.#test.mock ??= new MockTracker(); 1411cb0ef41Sopenharmony_ci return this.#test.mock; 1421cb0ef41Sopenharmony_ci } 1431cb0ef41Sopenharmony_ci 1441cb0ef41Sopenharmony_ci runOnly(value) { 1451cb0ef41Sopenharmony_ci this.#test.runOnlySubtests = !!value; 1461cb0ef41Sopenharmony_ci } 1471cb0ef41Sopenharmony_ci 1481cb0ef41Sopenharmony_ci skip(message) { 1491cb0ef41Sopenharmony_ci this.#test.skip(message); 1501cb0ef41Sopenharmony_ci } 1511cb0ef41Sopenharmony_ci 1521cb0ef41Sopenharmony_ci todo(message) { 1531cb0ef41Sopenharmony_ci this.#test.todo(message); 1541cb0ef41Sopenharmony_ci } 1551cb0ef41Sopenharmony_ci 1561cb0ef41Sopenharmony_ci test(name, options, fn) { 1571cb0ef41Sopenharmony_ci const overrides = { 1581cb0ef41Sopenharmony_ci __proto__: null, 1591cb0ef41Sopenharmony_ci loc: getCallerLocation(), 1601cb0ef41Sopenharmony_ci }; 1611cb0ef41Sopenharmony_ci 1621cb0ef41Sopenharmony_ci const subtest = this.#test.createSubtest( 1631cb0ef41Sopenharmony_ci // eslint-disable-next-line no-use-before-define 1641cb0ef41Sopenharmony_ci Test, name, options, fn, overrides, 1651cb0ef41Sopenharmony_ci ); 1661cb0ef41Sopenharmony_ci 1671cb0ef41Sopenharmony_ci return subtest.start(); 1681cb0ef41Sopenharmony_ci } 1691cb0ef41Sopenharmony_ci 1701cb0ef41Sopenharmony_ci before(fn, options) { 1711cb0ef41Sopenharmony_ci this.#test.createHook('before', fn, options); 1721cb0ef41Sopenharmony_ci } 1731cb0ef41Sopenharmony_ci 1741cb0ef41Sopenharmony_ci after(fn, options) { 1751cb0ef41Sopenharmony_ci this.#test.createHook('after', fn, options); 1761cb0ef41Sopenharmony_ci } 1771cb0ef41Sopenharmony_ci 1781cb0ef41Sopenharmony_ci beforeEach(fn, options) { 1791cb0ef41Sopenharmony_ci this.#test.createHook('beforeEach', fn, options); 1801cb0ef41Sopenharmony_ci } 1811cb0ef41Sopenharmony_ci 1821cb0ef41Sopenharmony_ci afterEach(fn, options) { 1831cb0ef41Sopenharmony_ci this.#test.createHook('afterEach', fn, options); 1841cb0ef41Sopenharmony_ci } 1851cb0ef41Sopenharmony_ci} 1861cb0ef41Sopenharmony_ci 1871cb0ef41Sopenharmony_ciclass SuiteContext { 1881cb0ef41Sopenharmony_ci #suite; 1891cb0ef41Sopenharmony_ci 1901cb0ef41Sopenharmony_ci constructor(suite) { 1911cb0ef41Sopenharmony_ci this.#suite = suite; 1921cb0ef41Sopenharmony_ci } 1931cb0ef41Sopenharmony_ci 1941cb0ef41Sopenharmony_ci get signal() { 1951cb0ef41Sopenharmony_ci return this.#suite.signal; 1961cb0ef41Sopenharmony_ci } 1971cb0ef41Sopenharmony_ci 1981cb0ef41Sopenharmony_ci get name() { 1991cb0ef41Sopenharmony_ci return this.#suite.name; 2001cb0ef41Sopenharmony_ci } 2011cb0ef41Sopenharmony_ci} 2021cb0ef41Sopenharmony_ci 2031cb0ef41Sopenharmony_ciclass Test extends AsyncResource { 2041cb0ef41Sopenharmony_ci abortController; 2051cb0ef41Sopenharmony_ci outerSignal; 2061cb0ef41Sopenharmony_ci #reportedSubtest; 2071cb0ef41Sopenharmony_ci 2081cb0ef41Sopenharmony_ci constructor(options) { 2091cb0ef41Sopenharmony_ci super('Test'); 2101cb0ef41Sopenharmony_ci 2111cb0ef41Sopenharmony_ci let { fn, name, parent, skip } = options; 2121cb0ef41Sopenharmony_ci const { concurrency, loc, only, timeout, todo, signal } = options; 2131cb0ef41Sopenharmony_ci 2141cb0ef41Sopenharmony_ci if (typeof fn !== 'function') { 2151cb0ef41Sopenharmony_ci fn = noop; 2161cb0ef41Sopenharmony_ci } 2171cb0ef41Sopenharmony_ci 2181cb0ef41Sopenharmony_ci if (typeof name !== 'string' || name === '') { 2191cb0ef41Sopenharmony_ci name = fn.name || '<anonymous>'; 2201cb0ef41Sopenharmony_ci } 2211cb0ef41Sopenharmony_ci 2221cb0ef41Sopenharmony_ci if (!(parent instanceof Test)) { 2231cb0ef41Sopenharmony_ci parent = null; 2241cb0ef41Sopenharmony_ci } 2251cb0ef41Sopenharmony_ci 2261cb0ef41Sopenharmony_ci if (parent === null) { 2271cb0ef41Sopenharmony_ci this.concurrency = 1; 2281cb0ef41Sopenharmony_ci this.nesting = 0; 2291cb0ef41Sopenharmony_ci this.only = testOnlyFlag; 2301cb0ef41Sopenharmony_ci this.reporter = new TestsStream(); 2311cb0ef41Sopenharmony_ci this.runOnlySubtests = this.only; 2321cb0ef41Sopenharmony_ci this.testNumber = 0; 2331cb0ef41Sopenharmony_ci this.timeout = kDefaultTimeout; 2341cb0ef41Sopenharmony_ci this.root = this; 2351cb0ef41Sopenharmony_ci this.hooks = { 2361cb0ef41Sopenharmony_ci __proto__: null, 2371cb0ef41Sopenharmony_ci before: [], 2381cb0ef41Sopenharmony_ci after: [], 2391cb0ef41Sopenharmony_ci beforeEach: [], 2401cb0ef41Sopenharmony_ci afterEach: [], 2411cb0ef41Sopenharmony_ci }; 2421cb0ef41Sopenharmony_ci } else { 2431cb0ef41Sopenharmony_ci const nesting = parent.parent === null ? parent.nesting : 2441cb0ef41Sopenharmony_ci parent.nesting + 1; 2451cb0ef41Sopenharmony_ci 2461cb0ef41Sopenharmony_ci this.concurrency = parent.concurrency; 2471cb0ef41Sopenharmony_ci this.nesting = nesting; 2481cb0ef41Sopenharmony_ci this.only = only ?? !parent.runOnlySubtests; 2491cb0ef41Sopenharmony_ci this.reporter = parent.reporter; 2501cb0ef41Sopenharmony_ci this.runOnlySubtests = !this.only; 2511cb0ef41Sopenharmony_ci this.testNumber = parent.subtests.length + 1; 2521cb0ef41Sopenharmony_ci this.timeout = parent.timeout; 2531cb0ef41Sopenharmony_ci this.root = parent.root; 2541cb0ef41Sopenharmony_ci this.hooks = { 2551cb0ef41Sopenharmony_ci __proto__: null, 2561cb0ef41Sopenharmony_ci before: [], 2571cb0ef41Sopenharmony_ci after: [], 2581cb0ef41Sopenharmony_ci beforeEach: ArrayPrototypeSlice(parent.hooks.beforeEach), 2591cb0ef41Sopenharmony_ci afterEach: ArrayPrototypeSlice(parent.hooks.afterEach), 2601cb0ef41Sopenharmony_ci }; 2611cb0ef41Sopenharmony_ci } 2621cb0ef41Sopenharmony_ci 2631cb0ef41Sopenharmony_ci switch (typeof concurrency) { 2641cb0ef41Sopenharmony_ci case 'number': 2651cb0ef41Sopenharmony_ci validateUint32(concurrency, 'options.concurrency', 1); 2661cb0ef41Sopenharmony_ci this.concurrency = concurrency; 2671cb0ef41Sopenharmony_ci break; 2681cb0ef41Sopenharmony_ci 2691cb0ef41Sopenharmony_ci case 'boolean': 2701cb0ef41Sopenharmony_ci if (concurrency) { 2711cb0ef41Sopenharmony_ci this.concurrency = parent === null ? 2721cb0ef41Sopenharmony_ci MathMax(availableParallelism() - 1, 1) : Infinity; 2731cb0ef41Sopenharmony_ci } else { 2741cb0ef41Sopenharmony_ci this.concurrency = 1; 2751cb0ef41Sopenharmony_ci } 2761cb0ef41Sopenharmony_ci break; 2771cb0ef41Sopenharmony_ci 2781cb0ef41Sopenharmony_ci default: 2791cb0ef41Sopenharmony_ci if (concurrency != null) 2801cb0ef41Sopenharmony_ci throw new ERR_INVALID_ARG_TYPE('options.concurrency', ['boolean', 'number'], concurrency); 2811cb0ef41Sopenharmony_ci } 2821cb0ef41Sopenharmony_ci 2831cb0ef41Sopenharmony_ci if (timeout != null && timeout !== Infinity) { 2841cb0ef41Sopenharmony_ci validateNumber(timeout, 'options.timeout', 0, TIMEOUT_MAX); 2851cb0ef41Sopenharmony_ci this.timeout = timeout; 2861cb0ef41Sopenharmony_ci } 2871cb0ef41Sopenharmony_ci 2881cb0ef41Sopenharmony_ci this.name = name; 2891cb0ef41Sopenharmony_ci this.parent = parent; 2901cb0ef41Sopenharmony_ci 2911cb0ef41Sopenharmony_ci if (testNamePatterns !== null && !this.matchesTestNamePatterns()) { 2921cb0ef41Sopenharmony_ci skip = 'test name does not match pattern'; 2931cb0ef41Sopenharmony_ci } 2941cb0ef41Sopenharmony_ci 2951cb0ef41Sopenharmony_ci if (testOnlyFlag && !this.only) { 2961cb0ef41Sopenharmony_ci skip = '\'only\' option not set'; 2971cb0ef41Sopenharmony_ci } 2981cb0ef41Sopenharmony_ci 2991cb0ef41Sopenharmony_ci if (skip) { 3001cb0ef41Sopenharmony_ci fn = noop; 3011cb0ef41Sopenharmony_ci } 3021cb0ef41Sopenharmony_ci 3031cb0ef41Sopenharmony_ci this.abortController = new AbortController(); 3041cb0ef41Sopenharmony_ci this.outerSignal = signal; 3051cb0ef41Sopenharmony_ci this.signal = this.abortController.signal; 3061cb0ef41Sopenharmony_ci 3071cb0ef41Sopenharmony_ci validateAbortSignal(signal, 'options.signal'); 3081cb0ef41Sopenharmony_ci if (signal) { 3091cb0ef41Sopenharmony_ci kResistStopPropagation ??= require('internal/event_target').kResistStopPropagation; 3101cb0ef41Sopenharmony_ci } 3111cb0ef41Sopenharmony_ci 3121cb0ef41Sopenharmony_ci this.outerSignal?.addEventListener( 3131cb0ef41Sopenharmony_ci 'abort', 3141cb0ef41Sopenharmony_ci this.#abortHandler, 3151cb0ef41Sopenharmony_ci { __proto__: null, [kResistStopPropagation]: true }, 3161cb0ef41Sopenharmony_ci ); 3171cb0ef41Sopenharmony_ci 3181cb0ef41Sopenharmony_ci this.fn = fn; 3191cb0ef41Sopenharmony_ci this.harness = null; // Configured on the root test by the test harness. 3201cb0ef41Sopenharmony_ci this.mock = null; 3211cb0ef41Sopenharmony_ci this.cancelled = false; 3221cb0ef41Sopenharmony_ci this.skipped = skip !== undefined && skip !== false; 3231cb0ef41Sopenharmony_ci this.isTodo = todo !== undefined && todo !== false; 3241cb0ef41Sopenharmony_ci this.startTime = null; 3251cb0ef41Sopenharmony_ci this.endTime = null; 3261cb0ef41Sopenharmony_ci this.passed = false; 3271cb0ef41Sopenharmony_ci this.error = null; 3281cb0ef41Sopenharmony_ci this.diagnostics = []; 3291cb0ef41Sopenharmony_ci this.message = typeof skip === 'string' ? skip : 3301cb0ef41Sopenharmony_ci typeof todo === 'string' ? todo : null; 3311cb0ef41Sopenharmony_ci this.activeSubtests = 0; 3321cb0ef41Sopenharmony_ci this.pendingSubtests = []; 3331cb0ef41Sopenharmony_ci this.readySubtests = new SafeMap(); 3341cb0ef41Sopenharmony_ci this.subtests = []; 3351cb0ef41Sopenharmony_ci this.waitingOn = 0; 3361cb0ef41Sopenharmony_ci this.finished = false; 3371cb0ef41Sopenharmony_ci 3381cb0ef41Sopenharmony_ci if (!testOnlyFlag && (only || this.runOnlySubtests)) { 3391cb0ef41Sopenharmony_ci const warning = 3401cb0ef41Sopenharmony_ci "'only' and 'runOnly' require the --test-only command-line option."; 3411cb0ef41Sopenharmony_ci this.diagnostic(warning); 3421cb0ef41Sopenharmony_ci } 3431cb0ef41Sopenharmony_ci 3441cb0ef41Sopenharmony_ci if (loc === undefined || kFilename === undefined) { 3451cb0ef41Sopenharmony_ci this.loc = undefined; 3461cb0ef41Sopenharmony_ci } else { 3471cb0ef41Sopenharmony_ci this.loc = { 3481cb0ef41Sopenharmony_ci __proto__: null, 3491cb0ef41Sopenharmony_ci line: loc[0], 3501cb0ef41Sopenharmony_ci column: loc[1], 3511cb0ef41Sopenharmony_ci file: loc[2], 3521cb0ef41Sopenharmony_ci }; 3531cb0ef41Sopenharmony_ci } 3541cb0ef41Sopenharmony_ci } 3551cb0ef41Sopenharmony_ci 3561cb0ef41Sopenharmony_ci matchesTestNamePatterns() { 3571cb0ef41Sopenharmony_ci return ArrayPrototypeSome(testNamePatterns, (re) => RegExpPrototypeExec(re, this.name) !== null) || 3581cb0ef41Sopenharmony_ci this.parent?.matchesTestNamePatterns(); 3591cb0ef41Sopenharmony_ci } 3601cb0ef41Sopenharmony_ci 3611cb0ef41Sopenharmony_ci hasConcurrency() { 3621cb0ef41Sopenharmony_ci return this.concurrency > this.activeSubtests; 3631cb0ef41Sopenharmony_ci } 3641cb0ef41Sopenharmony_ci 3651cb0ef41Sopenharmony_ci addPendingSubtest(deferred) { 3661cb0ef41Sopenharmony_ci ArrayPrototypePush(this.pendingSubtests, deferred); 3671cb0ef41Sopenharmony_ci } 3681cb0ef41Sopenharmony_ci 3691cb0ef41Sopenharmony_ci async processPendingSubtests() { 3701cb0ef41Sopenharmony_ci while (this.pendingSubtests.length > 0 && this.hasConcurrency()) { 3711cb0ef41Sopenharmony_ci const deferred = ArrayPrototypeShift(this.pendingSubtests); 3721cb0ef41Sopenharmony_ci const test = deferred.test; 3731cb0ef41Sopenharmony_ci this.reporter.dequeue(test.nesting, test.loc, test.name); 3741cb0ef41Sopenharmony_ci await test.run(); 3751cb0ef41Sopenharmony_ci deferred.resolve(); 3761cb0ef41Sopenharmony_ci } 3771cb0ef41Sopenharmony_ci } 3781cb0ef41Sopenharmony_ci 3791cb0ef41Sopenharmony_ci addReadySubtest(subtest) { 3801cb0ef41Sopenharmony_ci this.readySubtests.set(subtest.testNumber, subtest); 3811cb0ef41Sopenharmony_ci } 3821cb0ef41Sopenharmony_ci 3831cb0ef41Sopenharmony_ci processReadySubtestRange(canSend) { 3841cb0ef41Sopenharmony_ci const start = this.waitingOn; 3851cb0ef41Sopenharmony_ci const end = start + this.readySubtests.size; 3861cb0ef41Sopenharmony_ci 3871cb0ef41Sopenharmony_ci for (let i = start; i < end; i++) { 3881cb0ef41Sopenharmony_ci const subtest = this.readySubtests.get(i); 3891cb0ef41Sopenharmony_ci 3901cb0ef41Sopenharmony_ci // Check if the specified subtest is in the map. If it is not, return 3911cb0ef41Sopenharmony_ci // early to avoid trying to process any more tests since they would be 3921cb0ef41Sopenharmony_ci // out of order. 3931cb0ef41Sopenharmony_ci if (subtest === undefined) { 3941cb0ef41Sopenharmony_ci return; 3951cb0ef41Sopenharmony_ci } 3961cb0ef41Sopenharmony_ci 3971cb0ef41Sopenharmony_ci // Call isClearToSend() in the loop so that it is: 3981cb0ef41Sopenharmony_ci // - Only called if there are results to report in the correct order. 3991cb0ef41Sopenharmony_ci // - Guaranteed to only be called a maximum of once per call to 4001cb0ef41Sopenharmony_ci // processReadySubtestRange(). 4011cb0ef41Sopenharmony_ci canSend = canSend || this.isClearToSend(); 4021cb0ef41Sopenharmony_ci 4031cb0ef41Sopenharmony_ci if (!canSend) { 4041cb0ef41Sopenharmony_ci return; 4051cb0ef41Sopenharmony_ci } 4061cb0ef41Sopenharmony_ci 4071cb0ef41Sopenharmony_ci if (i === 1 && this.parent !== null) { 4081cb0ef41Sopenharmony_ci this.reportStarted(); 4091cb0ef41Sopenharmony_ci } 4101cb0ef41Sopenharmony_ci 4111cb0ef41Sopenharmony_ci // Report the subtest's results and remove it from the ready map. 4121cb0ef41Sopenharmony_ci subtest.finalize(); 4131cb0ef41Sopenharmony_ci this.readySubtests.delete(i); 4141cb0ef41Sopenharmony_ci } 4151cb0ef41Sopenharmony_ci } 4161cb0ef41Sopenharmony_ci 4171cb0ef41Sopenharmony_ci createSubtest(Factory, name, options, fn, overrides) { 4181cb0ef41Sopenharmony_ci if (typeof name === 'function') { 4191cb0ef41Sopenharmony_ci fn = name; 4201cb0ef41Sopenharmony_ci } else if (name !== null && typeof name === 'object') { 4211cb0ef41Sopenharmony_ci fn = options; 4221cb0ef41Sopenharmony_ci options = name; 4231cb0ef41Sopenharmony_ci } else if (typeof options === 'function') { 4241cb0ef41Sopenharmony_ci fn = options; 4251cb0ef41Sopenharmony_ci } 4261cb0ef41Sopenharmony_ci 4271cb0ef41Sopenharmony_ci if (options === null || typeof options !== 'object') { 4281cb0ef41Sopenharmony_ci options = kEmptyObject; 4291cb0ef41Sopenharmony_ci } 4301cb0ef41Sopenharmony_ci 4311cb0ef41Sopenharmony_ci let parent = this; 4321cb0ef41Sopenharmony_ci 4331cb0ef41Sopenharmony_ci // If this test has already ended, attach this test to the root test so 4341cb0ef41Sopenharmony_ci // that the error can be properly reported. 4351cb0ef41Sopenharmony_ci const preventAddingSubtests = this.finished || this.buildPhaseFinished; 4361cb0ef41Sopenharmony_ci if (preventAddingSubtests) { 4371cb0ef41Sopenharmony_ci while (parent.parent !== null) { 4381cb0ef41Sopenharmony_ci parent = parent.parent; 4391cb0ef41Sopenharmony_ci } 4401cb0ef41Sopenharmony_ci } 4411cb0ef41Sopenharmony_ci 4421cb0ef41Sopenharmony_ci const test = new Factory({ __proto__: null, fn, name, parent, ...options, ...overrides }); 4431cb0ef41Sopenharmony_ci 4441cb0ef41Sopenharmony_ci if (parent.waitingOn === 0) { 4451cb0ef41Sopenharmony_ci parent.waitingOn = test.testNumber; 4461cb0ef41Sopenharmony_ci } 4471cb0ef41Sopenharmony_ci 4481cb0ef41Sopenharmony_ci if (preventAddingSubtests) { 4491cb0ef41Sopenharmony_ci test.startTime = test.startTime || hrtime(); 4501cb0ef41Sopenharmony_ci test.fail( 4511cb0ef41Sopenharmony_ci new ERR_TEST_FAILURE( 4521cb0ef41Sopenharmony_ci 'test could not be started because its parent finished', 4531cb0ef41Sopenharmony_ci kParentAlreadyFinished, 4541cb0ef41Sopenharmony_ci ), 4551cb0ef41Sopenharmony_ci ); 4561cb0ef41Sopenharmony_ci } 4571cb0ef41Sopenharmony_ci 4581cb0ef41Sopenharmony_ci ArrayPrototypePush(parent.subtests, test); 4591cb0ef41Sopenharmony_ci return test; 4601cb0ef41Sopenharmony_ci } 4611cb0ef41Sopenharmony_ci 4621cb0ef41Sopenharmony_ci #abortHandler = () => { 4631cb0ef41Sopenharmony_ci const error = this.outerSignal?.reason || new AbortError('The test was aborted'); 4641cb0ef41Sopenharmony_ci error.failureType = kAborted; 4651cb0ef41Sopenharmony_ci this.#cancel(error); 4661cb0ef41Sopenharmony_ci }; 4671cb0ef41Sopenharmony_ci 4681cb0ef41Sopenharmony_ci #cancel(error) { 4691cb0ef41Sopenharmony_ci if (this.endTime !== null) { 4701cb0ef41Sopenharmony_ci return; 4711cb0ef41Sopenharmony_ci } 4721cb0ef41Sopenharmony_ci 4731cb0ef41Sopenharmony_ci this.fail(error || 4741cb0ef41Sopenharmony_ci new ERR_TEST_FAILURE( 4751cb0ef41Sopenharmony_ci 'test did not finish before its parent and was cancelled', 4761cb0ef41Sopenharmony_ci kCancelledByParent, 4771cb0ef41Sopenharmony_ci ), 4781cb0ef41Sopenharmony_ci ); 4791cb0ef41Sopenharmony_ci this.startTime = this.startTime || this.endTime; // If a test was canceled before it was started, e.g inside a hook 4801cb0ef41Sopenharmony_ci this.cancelled = true; 4811cb0ef41Sopenharmony_ci this.abortController.abort(); 4821cb0ef41Sopenharmony_ci } 4831cb0ef41Sopenharmony_ci 4841cb0ef41Sopenharmony_ci createHook(name, fn, options) { 4851cb0ef41Sopenharmony_ci validateOneOf(name, 'hook name', kHookNames); 4861cb0ef41Sopenharmony_ci // eslint-disable-next-line no-use-before-define 4871cb0ef41Sopenharmony_ci const hook = new TestHook(fn, options); 4881cb0ef41Sopenharmony_ci if (name === 'before' || name === 'after') { 4891cb0ef41Sopenharmony_ci hook.run = runOnce(hook.run); 4901cb0ef41Sopenharmony_ci } 4911cb0ef41Sopenharmony_ci ArrayPrototypePush(this.hooks[name], hook); 4921cb0ef41Sopenharmony_ci return hook; 4931cb0ef41Sopenharmony_ci } 4941cb0ef41Sopenharmony_ci 4951cb0ef41Sopenharmony_ci fail(err) { 4961cb0ef41Sopenharmony_ci if (this.error !== null) { 4971cb0ef41Sopenharmony_ci return; 4981cb0ef41Sopenharmony_ci } 4991cb0ef41Sopenharmony_ci 5001cb0ef41Sopenharmony_ci this.endTime = hrtime(); 5011cb0ef41Sopenharmony_ci this.passed = false; 5021cb0ef41Sopenharmony_ci this.error = err; 5031cb0ef41Sopenharmony_ci } 5041cb0ef41Sopenharmony_ci 5051cb0ef41Sopenharmony_ci pass() { 5061cb0ef41Sopenharmony_ci if (this.endTime !== null) { 5071cb0ef41Sopenharmony_ci return; 5081cb0ef41Sopenharmony_ci } 5091cb0ef41Sopenharmony_ci 5101cb0ef41Sopenharmony_ci this.endTime = hrtime(); 5111cb0ef41Sopenharmony_ci this.passed = true; 5121cb0ef41Sopenharmony_ci } 5131cb0ef41Sopenharmony_ci 5141cb0ef41Sopenharmony_ci skip(message) { 5151cb0ef41Sopenharmony_ci this.skipped = true; 5161cb0ef41Sopenharmony_ci this.message = message; 5171cb0ef41Sopenharmony_ci } 5181cb0ef41Sopenharmony_ci 5191cb0ef41Sopenharmony_ci todo(message) { 5201cb0ef41Sopenharmony_ci this.isTodo = true; 5211cb0ef41Sopenharmony_ci this.message = message; 5221cb0ef41Sopenharmony_ci } 5231cb0ef41Sopenharmony_ci 5241cb0ef41Sopenharmony_ci diagnostic(message) { 5251cb0ef41Sopenharmony_ci ArrayPrototypePush(this.diagnostics, message); 5261cb0ef41Sopenharmony_ci } 5271cb0ef41Sopenharmony_ci 5281cb0ef41Sopenharmony_ci start() { 5291cb0ef41Sopenharmony_ci // If there is enough available concurrency to run the test now, then do 5301cb0ef41Sopenharmony_ci // it. Otherwise, return a Promise to the caller and mark the test as 5311cb0ef41Sopenharmony_ci // pending for later execution. 5321cb0ef41Sopenharmony_ci this.reporter.enqueue(this.nesting, this.loc, this.name); 5331cb0ef41Sopenharmony_ci if (!this.parent.hasConcurrency()) { 5341cb0ef41Sopenharmony_ci const deferred = createDeferredPromise(); 5351cb0ef41Sopenharmony_ci 5361cb0ef41Sopenharmony_ci deferred.test = this; 5371cb0ef41Sopenharmony_ci this.parent.addPendingSubtest(deferred); 5381cb0ef41Sopenharmony_ci return deferred.promise; 5391cb0ef41Sopenharmony_ci } 5401cb0ef41Sopenharmony_ci 5411cb0ef41Sopenharmony_ci this.reporter.dequeue(this.nesting, this.loc, this.name); 5421cb0ef41Sopenharmony_ci return this.run(); 5431cb0ef41Sopenharmony_ci } 5441cb0ef41Sopenharmony_ci 5451cb0ef41Sopenharmony_ci [kShouldAbort]() { 5461cb0ef41Sopenharmony_ci if (this.signal.aborted) { 5471cb0ef41Sopenharmony_ci return true; 5481cb0ef41Sopenharmony_ci } 5491cb0ef41Sopenharmony_ci if (this.outerSignal?.aborted) { 5501cb0ef41Sopenharmony_ci this.#abortHandler(); 5511cb0ef41Sopenharmony_ci return true; 5521cb0ef41Sopenharmony_ci } 5531cb0ef41Sopenharmony_ci } 5541cb0ef41Sopenharmony_ci 5551cb0ef41Sopenharmony_ci getRunArgs() { 5561cb0ef41Sopenharmony_ci const ctx = new TestContext(this); 5571cb0ef41Sopenharmony_ci return { __proto__: null, ctx, args: [ctx] }; 5581cb0ef41Sopenharmony_ci } 5591cb0ef41Sopenharmony_ci 5601cb0ef41Sopenharmony_ci async runHook(hook, args) { 5611cb0ef41Sopenharmony_ci validateOneOf(hook, 'hook name', kHookNames); 5621cb0ef41Sopenharmony_ci try { 5631cb0ef41Sopenharmony_ci await ArrayPrototypeReduce(this.hooks[hook], async (prev, hook) => { 5641cb0ef41Sopenharmony_ci await prev; 5651cb0ef41Sopenharmony_ci await hook.run(args); 5661cb0ef41Sopenharmony_ci if (hook.error) { 5671cb0ef41Sopenharmony_ci throw hook.error; 5681cb0ef41Sopenharmony_ci } 5691cb0ef41Sopenharmony_ci }, PromiseResolve()); 5701cb0ef41Sopenharmony_ci } catch (err) { 5711cb0ef41Sopenharmony_ci const error = new ERR_TEST_FAILURE(`failed running ${hook} hook`, kHookFailure); 5721cb0ef41Sopenharmony_ci error.cause = isTestFailureError(err) ? err.cause : err; 5731cb0ef41Sopenharmony_ci throw error; 5741cb0ef41Sopenharmony_ci } 5751cb0ef41Sopenharmony_ci } 5761cb0ef41Sopenharmony_ci 5771cb0ef41Sopenharmony_ci async run() { 5781cb0ef41Sopenharmony_ci if (this.parent !== null) { 5791cb0ef41Sopenharmony_ci this.parent.activeSubtests++; 5801cb0ef41Sopenharmony_ci } 5811cb0ef41Sopenharmony_ci this.startTime = hrtime(); 5821cb0ef41Sopenharmony_ci 5831cb0ef41Sopenharmony_ci if (this[kShouldAbort]()) { 5841cb0ef41Sopenharmony_ci this.postRun(); 5851cb0ef41Sopenharmony_ci return; 5861cb0ef41Sopenharmony_ci } 5871cb0ef41Sopenharmony_ci 5881cb0ef41Sopenharmony_ci const { args, ctx } = this.getRunArgs(); 5891cb0ef41Sopenharmony_ci const after = async () => { 5901cb0ef41Sopenharmony_ci if (this.hooks.after.length > 0) { 5911cb0ef41Sopenharmony_ci await this.runHook('after', { __proto__: null, args, ctx }); 5921cb0ef41Sopenharmony_ci } 5931cb0ef41Sopenharmony_ci }; 5941cb0ef41Sopenharmony_ci const afterEach = runOnce(async () => { 5951cb0ef41Sopenharmony_ci if (this.parent?.hooks.afterEach.length > 0) { 5961cb0ef41Sopenharmony_ci await this.parent.runHook('afterEach', { __proto__: null, args, ctx }); 5971cb0ef41Sopenharmony_ci } 5981cb0ef41Sopenharmony_ci }); 5991cb0ef41Sopenharmony_ci 6001cb0ef41Sopenharmony_ci let stopPromise; 6011cb0ef41Sopenharmony_ci 6021cb0ef41Sopenharmony_ci try { 6031cb0ef41Sopenharmony_ci if (this.parent?.hooks.before.length > 0) { 6041cb0ef41Sopenharmony_ci await this.parent.runHook('before', this.parent.getRunArgs()); 6051cb0ef41Sopenharmony_ci } 6061cb0ef41Sopenharmony_ci if (this.parent?.hooks.beforeEach.length > 0) { 6071cb0ef41Sopenharmony_ci await this.parent.runHook('beforeEach', { __proto__: null, args, ctx }); 6081cb0ef41Sopenharmony_ci } 6091cb0ef41Sopenharmony_ci stopPromise = stopTest(this.timeout, this.signal); 6101cb0ef41Sopenharmony_ci const runArgs = ArrayPrototypeSlice(args); 6111cb0ef41Sopenharmony_ci ArrayPrototypeUnshift(runArgs, this.fn, ctx); 6121cb0ef41Sopenharmony_ci 6131cb0ef41Sopenharmony_ci if (this.fn.length === runArgs.length - 1) { 6141cb0ef41Sopenharmony_ci // This test is using legacy Node.js error first callbacks. 6151cb0ef41Sopenharmony_ci const { promise, cb } = createDeferredCallback(); 6161cb0ef41Sopenharmony_ci 6171cb0ef41Sopenharmony_ci ArrayPrototypePush(runArgs, cb); 6181cb0ef41Sopenharmony_ci const ret = ReflectApply(this.runInAsyncScope, this, runArgs); 6191cb0ef41Sopenharmony_ci 6201cb0ef41Sopenharmony_ci if (isPromise(ret)) { 6211cb0ef41Sopenharmony_ci this.fail(new ERR_TEST_FAILURE( 6221cb0ef41Sopenharmony_ci 'passed a callback but also returned a Promise', 6231cb0ef41Sopenharmony_ci kCallbackAndPromisePresent, 6241cb0ef41Sopenharmony_ci )); 6251cb0ef41Sopenharmony_ci await SafePromiseRace([ret, stopPromise]); 6261cb0ef41Sopenharmony_ci } else { 6271cb0ef41Sopenharmony_ci await SafePromiseRace([PromiseResolve(promise), stopPromise]); 6281cb0ef41Sopenharmony_ci } 6291cb0ef41Sopenharmony_ci } else { 6301cb0ef41Sopenharmony_ci // This test is synchronous or using Promises. 6311cb0ef41Sopenharmony_ci const promise = ReflectApply(this.runInAsyncScope, this, runArgs); 6321cb0ef41Sopenharmony_ci await SafePromiseRace([PromiseResolve(promise), stopPromise]); 6331cb0ef41Sopenharmony_ci } 6341cb0ef41Sopenharmony_ci 6351cb0ef41Sopenharmony_ci if (this[kShouldAbort]()) { 6361cb0ef41Sopenharmony_ci this.postRun(); 6371cb0ef41Sopenharmony_ci return; 6381cb0ef41Sopenharmony_ci } 6391cb0ef41Sopenharmony_ci 6401cb0ef41Sopenharmony_ci await afterEach(); 6411cb0ef41Sopenharmony_ci await after(); 6421cb0ef41Sopenharmony_ci this.pass(); 6431cb0ef41Sopenharmony_ci } catch (err) { 6441cb0ef41Sopenharmony_ci try { await afterEach(); } catch { /* test is already failing, let's ignore the error */ } 6451cb0ef41Sopenharmony_ci try { await after(); } catch { /* Ignore error. */ } 6461cb0ef41Sopenharmony_ci if (isTestFailureError(err)) { 6471cb0ef41Sopenharmony_ci if (err.failureType === kTestTimeoutFailure) { 6481cb0ef41Sopenharmony_ci this.#cancel(err); 6491cb0ef41Sopenharmony_ci } else { 6501cb0ef41Sopenharmony_ci this.fail(err); 6511cb0ef41Sopenharmony_ci } 6521cb0ef41Sopenharmony_ci } else { 6531cb0ef41Sopenharmony_ci this.fail(new ERR_TEST_FAILURE(err, kTestCodeFailure)); 6541cb0ef41Sopenharmony_ci } 6551cb0ef41Sopenharmony_ci } finally { 6561cb0ef41Sopenharmony_ci stopPromise?.[SymbolDispose](); 6571cb0ef41Sopenharmony_ci 6581cb0ef41Sopenharmony_ci // Do not abort hooks and the root test as hooks instance are shared between tests suite so aborting them will 6591cb0ef41Sopenharmony_ci // cause them to not run for further tests. 6601cb0ef41Sopenharmony_ci if (this.parent !== null) { 6611cb0ef41Sopenharmony_ci this.abortController.abort(); 6621cb0ef41Sopenharmony_ci } 6631cb0ef41Sopenharmony_ci } 6641cb0ef41Sopenharmony_ci 6651cb0ef41Sopenharmony_ci if (this.parent !== null || typeof this.hookType === 'string') { 6661cb0ef41Sopenharmony_ci // Clean up the test. Then, try to report the results and execute any 6671cb0ef41Sopenharmony_ci // tests that were pending due to available concurrency. 6681cb0ef41Sopenharmony_ci // 6691cb0ef41Sopenharmony_ci // The root test is skipped here because it is a special case. Its 6701cb0ef41Sopenharmony_ci // postRun() method is called when the process is getting ready to exit. 6711cb0ef41Sopenharmony_ci // This helps catch any asynchronous activity that occurs after the tests 6721cb0ef41Sopenharmony_ci // have finished executing. 6731cb0ef41Sopenharmony_ci this.postRun(); 6741cb0ef41Sopenharmony_ci } 6751cb0ef41Sopenharmony_ci } 6761cb0ef41Sopenharmony_ci 6771cb0ef41Sopenharmony_ci postRun(pendingSubtestsError) { 6781cb0ef41Sopenharmony_ci // If the test was failed before it even started, then the end time will 6791cb0ef41Sopenharmony_ci // be earlier than the start time. Correct that here. 6801cb0ef41Sopenharmony_ci if (this.endTime < this.startTime) { 6811cb0ef41Sopenharmony_ci this.endTime = hrtime(); 6821cb0ef41Sopenharmony_ci } 6831cb0ef41Sopenharmony_ci this.startTime ??= this.endTime; 6841cb0ef41Sopenharmony_ci 6851cb0ef41Sopenharmony_ci // The test has run, so recursively cancel any outstanding subtests and 6861cb0ef41Sopenharmony_ci // mark this test as failed if any subtests failed. 6871cb0ef41Sopenharmony_ci this.pendingSubtests = []; 6881cb0ef41Sopenharmony_ci let failed = 0; 6891cb0ef41Sopenharmony_ci for (let i = 0; i < this.subtests.length; i++) { 6901cb0ef41Sopenharmony_ci const subtest = this.subtests[i]; 6911cb0ef41Sopenharmony_ci 6921cb0ef41Sopenharmony_ci if (!subtest.finished) { 6931cb0ef41Sopenharmony_ci subtest.#cancel(pendingSubtestsError); 6941cb0ef41Sopenharmony_ci subtest.postRun(pendingSubtestsError); 6951cb0ef41Sopenharmony_ci } 6961cb0ef41Sopenharmony_ci if (!subtest.passed && !subtest.isTodo) { 6971cb0ef41Sopenharmony_ci failed++; 6981cb0ef41Sopenharmony_ci } 6991cb0ef41Sopenharmony_ci } 7001cb0ef41Sopenharmony_ci 7011cb0ef41Sopenharmony_ci if ((this.passed || this.parent === null) && failed > 0) { 7021cb0ef41Sopenharmony_ci const subtestString = `subtest${failed > 1 ? 's' : ''}`; 7031cb0ef41Sopenharmony_ci const msg = `${failed} ${subtestString} failed`; 7041cb0ef41Sopenharmony_ci 7051cb0ef41Sopenharmony_ci this.fail(new ERR_TEST_FAILURE(msg, kSubtestsFailed)); 7061cb0ef41Sopenharmony_ci } 7071cb0ef41Sopenharmony_ci 7081cb0ef41Sopenharmony_ci this.outerSignal?.removeEventListener('abort', this.#abortHandler); 7091cb0ef41Sopenharmony_ci this.mock?.reset(); 7101cb0ef41Sopenharmony_ci 7111cb0ef41Sopenharmony_ci if (this.parent !== null) { 7121cb0ef41Sopenharmony_ci this.parent.activeSubtests--; 7131cb0ef41Sopenharmony_ci this.parent.addReadySubtest(this); 7141cb0ef41Sopenharmony_ci this.parent.processReadySubtestRange(false); 7151cb0ef41Sopenharmony_ci this.parent.processPendingSubtests(); 7161cb0ef41Sopenharmony_ci 7171cb0ef41Sopenharmony_ci if (this.parent === this.root && 7181cb0ef41Sopenharmony_ci this.root.activeSubtests === 0 && 7191cb0ef41Sopenharmony_ci this.root.pendingSubtests.length === 0 && 7201cb0ef41Sopenharmony_ci this.root.readySubtests.size === 0 && 7211cb0ef41Sopenharmony_ci this.root.hooks.after.length > 0) { 7221cb0ef41Sopenharmony_ci // This is done so that any global after() hooks are run. At this point 7231cb0ef41Sopenharmony_ci // all of the tests have finished running. However, there might be 7241cb0ef41Sopenharmony_ci // ref'ed handles keeping the event loop alive. This gives the global 7251cb0ef41Sopenharmony_ci // after() hook a chance to clean them up. 7261cb0ef41Sopenharmony_ci this.root.run(); 7271cb0ef41Sopenharmony_ci } 7281cb0ef41Sopenharmony_ci } else if (!this.reported) { 7291cb0ef41Sopenharmony_ci const { 7301cb0ef41Sopenharmony_ci diagnostics, 7311cb0ef41Sopenharmony_ci harness, 7321cb0ef41Sopenharmony_ci loc, 7331cb0ef41Sopenharmony_ci nesting, 7341cb0ef41Sopenharmony_ci reporter, 7351cb0ef41Sopenharmony_ci } = this; 7361cb0ef41Sopenharmony_ci 7371cb0ef41Sopenharmony_ci this.reported = true; 7381cb0ef41Sopenharmony_ci reporter.plan(nesting, loc, harness.counters.topLevel); 7391cb0ef41Sopenharmony_ci 7401cb0ef41Sopenharmony_ci // Call this harness.coverage() before collecting diagnostics, since failure to collect coverage is a diagnostic. 7411cb0ef41Sopenharmony_ci const coverage = harness.coverage(); 7421cb0ef41Sopenharmony_ci for (let i = 0; i < diagnostics.length; i++) { 7431cb0ef41Sopenharmony_ci reporter.diagnostic(nesting, loc, diagnostics[i]); 7441cb0ef41Sopenharmony_ci } 7451cb0ef41Sopenharmony_ci 7461cb0ef41Sopenharmony_ci reporter.diagnostic(nesting, loc, `tests ${harness.counters.all}`); 7471cb0ef41Sopenharmony_ci reporter.diagnostic(nesting, loc, `suites ${harness.counters.suites}`); 7481cb0ef41Sopenharmony_ci reporter.diagnostic(nesting, loc, `pass ${harness.counters.passed}`); 7491cb0ef41Sopenharmony_ci reporter.diagnostic(nesting, loc, `fail ${harness.counters.failed}`); 7501cb0ef41Sopenharmony_ci reporter.diagnostic(nesting, loc, `cancelled ${harness.counters.cancelled}`); 7511cb0ef41Sopenharmony_ci reporter.diagnostic(nesting, loc, `skipped ${harness.counters.skipped}`); 7521cb0ef41Sopenharmony_ci reporter.diagnostic(nesting, loc, `todo ${harness.counters.todo}`); 7531cb0ef41Sopenharmony_ci reporter.diagnostic(nesting, loc, `duration_ms ${this.duration()}`); 7541cb0ef41Sopenharmony_ci 7551cb0ef41Sopenharmony_ci if (coverage) { 7561cb0ef41Sopenharmony_ci reporter.coverage(nesting, loc, coverage); 7571cb0ef41Sopenharmony_ci } 7581cb0ef41Sopenharmony_ci 7591cb0ef41Sopenharmony_ci reporter.end(); 7601cb0ef41Sopenharmony_ci } 7611cb0ef41Sopenharmony_ci } 7621cb0ef41Sopenharmony_ci 7631cb0ef41Sopenharmony_ci isClearToSend() { 7641cb0ef41Sopenharmony_ci return this.parent === null || 7651cb0ef41Sopenharmony_ci ( 7661cb0ef41Sopenharmony_ci this.parent.waitingOn === this.testNumber && this.parent.isClearToSend() 7671cb0ef41Sopenharmony_ci ); 7681cb0ef41Sopenharmony_ci } 7691cb0ef41Sopenharmony_ci 7701cb0ef41Sopenharmony_ci finalize() { 7711cb0ef41Sopenharmony_ci // By the time this function is called, the following can be relied on: 7721cb0ef41Sopenharmony_ci // - The current test has completed or been cancelled. 7731cb0ef41Sopenharmony_ci // - All of this test's subtests have completed or been cancelled. 7741cb0ef41Sopenharmony_ci // - It is the current test's turn to report its results. 7751cb0ef41Sopenharmony_ci 7761cb0ef41Sopenharmony_ci // Report any subtests that have not been reported yet. Since all of the 7771cb0ef41Sopenharmony_ci // subtests have finished, it's safe to pass true to 7781cb0ef41Sopenharmony_ci // processReadySubtestRange(), which will finalize all remaining subtests. 7791cb0ef41Sopenharmony_ci this.processReadySubtestRange(true); 7801cb0ef41Sopenharmony_ci 7811cb0ef41Sopenharmony_ci // Output this test's results and update the parent's waiting counter. 7821cb0ef41Sopenharmony_ci this.report(); 7831cb0ef41Sopenharmony_ci this.parent.waitingOn++; 7841cb0ef41Sopenharmony_ci this.finished = true; 7851cb0ef41Sopenharmony_ci } 7861cb0ef41Sopenharmony_ci 7871cb0ef41Sopenharmony_ci duration() { 7881cb0ef41Sopenharmony_ci // Duration is recorded in BigInt nanoseconds. Convert to milliseconds. 7891cb0ef41Sopenharmony_ci return Number(this.endTime - this.startTime) / 1_000_000; 7901cb0ef41Sopenharmony_ci } 7911cb0ef41Sopenharmony_ci 7921cb0ef41Sopenharmony_ci report() { 7931cb0ef41Sopenharmony_ci countCompletedTest(this); 7941cb0ef41Sopenharmony_ci if (this.subtests.length > 0) { 7951cb0ef41Sopenharmony_ci this.reporter.plan(this.subtests[0].nesting, this.loc, this.subtests.length); 7961cb0ef41Sopenharmony_ci } else { 7971cb0ef41Sopenharmony_ci this.reportStarted(); 7981cb0ef41Sopenharmony_ci } 7991cb0ef41Sopenharmony_ci let directive; 8001cb0ef41Sopenharmony_ci const details = { __proto__: null, duration_ms: this.duration() }; 8011cb0ef41Sopenharmony_ci 8021cb0ef41Sopenharmony_ci if (this.skipped) { 8031cb0ef41Sopenharmony_ci directive = this.reporter.getSkip(this.message); 8041cb0ef41Sopenharmony_ci } else if (this.isTodo) { 8051cb0ef41Sopenharmony_ci directive = this.reporter.getTodo(this.message); 8061cb0ef41Sopenharmony_ci } 8071cb0ef41Sopenharmony_ci 8081cb0ef41Sopenharmony_ci if (this.reportedType) { 8091cb0ef41Sopenharmony_ci details.type = this.reportedType; 8101cb0ef41Sopenharmony_ci } 8111cb0ef41Sopenharmony_ci 8121cb0ef41Sopenharmony_ci if (this.passed) { 8131cb0ef41Sopenharmony_ci this.reporter.ok(this.nesting, this.loc, this.testNumber, this.name, details, directive); 8141cb0ef41Sopenharmony_ci } else { 8151cb0ef41Sopenharmony_ci details.error = this.error; 8161cb0ef41Sopenharmony_ci this.reporter.fail(this.nesting, this.loc, this.testNumber, this.name, details, directive); 8171cb0ef41Sopenharmony_ci } 8181cb0ef41Sopenharmony_ci 8191cb0ef41Sopenharmony_ci for (let i = 0; i < this.diagnostics.length; i++) { 8201cb0ef41Sopenharmony_ci this.reporter.diagnostic(this.nesting, this.loc, this.diagnostics[i]); 8211cb0ef41Sopenharmony_ci } 8221cb0ef41Sopenharmony_ci } 8231cb0ef41Sopenharmony_ci 8241cb0ef41Sopenharmony_ci reportStarted() { 8251cb0ef41Sopenharmony_ci if (this.#reportedSubtest || this.parent === null) { 8261cb0ef41Sopenharmony_ci return; 8271cb0ef41Sopenharmony_ci } 8281cb0ef41Sopenharmony_ci this.#reportedSubtest = true; 8291cb0ef41Sopenharmony_ci this.parent.reportStarted(); 8301cb0ef41Sopenharmony_ci this.reporter.start(this.nesting, this.loc, this.name); 8311cb0ef41Sopenharmony_ci } 8321cb0ef41Sopenharmony_ci} 8331cb0ef41Sopenharmony_ci 8341cb0ef41Sopenharmony_ciclass TestHook extends Test { 8351cb0ef41Sopenharmony_ci #args; 8361cb0ef41Sopenharmony_ci constructor(fn, options) { 8371cb0ef41Sopenharmony_ci if (options === null || typeof options !== 'object') { 8381cb0ef41Sopenharmony_ci options = kEmptyObject; 8391cb0ef41Sopenharmony_ci } 8401cb0ef41Sopenharmony_ci const { loc, timeout, signal } = options; 8411cb0ef41Sopenharmony_ci super({ __proto__: null, fn, loc, timeout, signal }); 8421cb0ef41Sopenharmony_ci 8431cb0ef41Sopenharmony_ci this.parentTest = options.parent ?? null; 8441cb0ef41Sopenharmony_ci this.hookType = options.hookType; 8451cb0ef41Sopenharmony_ci } 8461cb0ef41Sopenharmony_ci run(args) { 8471cb0ef41Sopenharmony_ci if (this.error && !this.outerSignal?.aborted) { 8481cb0ef41Sopenharmony_ci this.passed = false; 8491cb0ef41Sopenharmony_ci this.error = null; 8501cb0ef41Sopenharmony_ci this.abortController.abort(); 8511cb0ef41Sopenharmony_ci this.abortController = new AbortController(); 8521cb0ef41Sopenharmony_ci this.signal = this.abortController.signal; 8531cb0ef41Sopenharmony_ci } 8541cb0ef41Sopenharmony_ci 8551cb0ef41Sopenharmony_ci this.#args = args; 8561cb0ef41Sopenharmony_ci return super.run(); 8571cb0ef41Sopenharmony_ci } 8581cb0ef41Sopenharmony_ci getRunArgs() { 8591cb0ef41Sopenharmony_ci return this.#args; 8601cb0ef41Sopenharmony_ci } 8611cb0ef41Sopenharmony_ci matchesTestNamePatterns() { 8621cb0ef41Sopenharmony_ci return true; 8631cb0ef41Sopenharmony_ci } 8641cb0ef41Sopenharmony_ci postRun() { 8651cb0ef41Sopenharmony_ci const { error, loc, parentTest: parent } = this; 8661cb0ef41Sopenharmony_ci 8671cb0ef41Sopenharmony_ci // Report failures in the root test's after() hook. 8681cb0ef41Sopenharmony_ci if (error && parent !== null && 8691cb0ef41Sopenharmony_ci parent === parent.root && this.hookType === 'after') { 8701cb0ef41Sopenharmony_ci 8711cb0ef41Sopenharmony_ci if (isTestFailureError(error)) { 8721cb0ef41Sopenharmony_ci error.failureType = kHookFailure; 8731cb0ef41Sopenharmony_ci } 8741cb0ef41Sopenharmony_ci 8751cb0ef41Sopenharmony_ci parent.reporter.fail(0, loc, parent.subtests.length + 1, loc.file, { 8761cb0ef41Sopenharmony_ci __proto__: null, 8771cb0ef41Sopenharmony_ci duration_ms: this.duration(), 8781cb0ef41Sopenharmony_ci error, 8791cb0ef41Sopenharmony_ci }, undefined); 8801cb0ef41Sopenharmony_ci } 8811cb0ef41Sopenharmony_ci } 8821cb0ef41Sopenharmony_ci} 8831cb0ef41Sopenharmony_ci 8841cb0ef41Sopenharmony_ciclass Suite extends Test { 8851cb0ef41Sopenharmony_ci reportedType = 'suite'; 8861cb0ef41Sopenharmony_ci constructor(options) { 8871cb0ef41Sopenharmony_ci super(options); 8881cb0ef41Sopenharmony_ci 8891cb0ef41Sopenharmony_ci if (testNamePatterns !== null && !options.skip && !options.todo) { 8901cb0ef41Sopenharmony_ci this.fn = options.fn || this.fn; 8911cb0ef41Sopenharmony_ci this.skipped = false; 8921cb0ef41Sopenharmony_ci } 8931cb0ef41Sopenharmony_ci this.runOnlySubtests = testOnlyFlag; 8941cb0ef41Sopenharmony_ci 8951cb0ef41Sopenharmony_ci try { 8961cb0ef41Sopenharmony_ci const { ctx, args } = this.getRunArgs(); 8971cb0ef41Sopenharmony_ci const runArgs = [this.fn, ctx]; 8981cb0ef41Sopenharmony_ci ArrayPrototypePushApply(runArgs, args); 8991cb0ef41Sopenharmony_ci this.buildSuite = SafePromisePrototypeFinally( 9001cb0ef41Sopenharmony_ci PromisePrototypeThen( 9011cb0ef41Sopenharmony_ci PromiseResolve(ReflectApply(this.runInAsyncScope, this, runArgs)), 9021cb0ef41Sopenharmony_ci undefined, 9031cb0ef41Sopenharmony_ci (err) => { 9041cb0ef41Sopenharmony_ci this.fail(new ERR_TEST_FAILURE(err, kTestCodeFailure)); 9051cb0ef41Sopenharmony_ci }), 9061cb0ef41Sopenharmony_ci () => { 9071cb0ef41Sopenharmony_ci this.buildPhaseFinished = true; 9081cb0ef41Sopenharmony_ci }, 9091cb0ef41Sopenharmony_ci ); 9101cb0ef41Sopenharmony_ci } catch (err) { 9111cb0ef41Sopenharmony_ci this.fail(new ERR_TEST_FAILURE(err, kTestCodeFailure)); 9121cb0ef41Sopenharmony_ci 9131cb0ef41Sopenharmony_ci this.buildPhaseFinished = true; 9141cb0ef41Sopenharmony_ci } 9151cb0ef41Sopenharmony_ci this.fn = () => {}; 9161cb0ef41Sopenharmony_ci } 9171cb0ef41Sopenharmony_ci 9181cb0ef41Sopenharmony_ci getRunArgs() { 9191cb0ef41Sopenharmony_ci const ctx = new SuiteContext(this); 9201cb0ef41Sopenharmony_ci return { __proto__: null, ctx, args: [ctx] }; 9211cb0ef41Sopenharmony_ci } 9221cb0ef41Sopenharmony_ci 9231cb0ef41Sopenharmony_ci async run() { 9241cb0ef41Sopenharmony_ci const hookArgs = this.getRunArgs(); 9251cb0ef41Sopenharmony_ci 9261cb0ef41Sopenharmony_ci let stopPromise; 9271cb0ef41Sopenharmony_ci try { 9281cb0ef41Sopenharmony_ci this.parent.activeSubtests++; 9291cb0ef41Sopenharmony_ci await this.buildSuite; 9301cb0ef41Sopenharmony_ci this.startTime = hrtime(); 9311cb0ef41Sopenharmony_ci 9321cb0ef41Sopenharmony_ci if (this[kShouldAbort]()) { 9331cb0ef41Sopenharmony_ci this.subtests = []; 9341cb0ef41Sopenharmony_ci this.postRun(); 9351cb0ef41Sopenharmony_ci return; 9361cb0ef41Sopenharmony_ci } 9371cb0ef41Sopenharmony_ci 9381cb0ef41Sopenharmony_ci if (this.parent.hooks.before.length > 0) { 9391cb0ef41Sopenharmony_ci await this.parent.runHook('before', this.parent.getRunArgs()); 9401cb0ef41Sopenharmony_ci } 9411cb0ef41Sopenharmony_ci 9421cb0ef41Sopenharmony_ci await this.runHook('before', hookArgs); 9431cb0ef41Sopenharmony_ci 9441cb0ef41Sopenharmony_ci stopPromise = stopTest(this.timeout, this.signal); 9451cb0ef41Sopenharmony_ci const subtests = this.skipped || this.error ? [] : this.subtests; 9461cb0ef41Sopenharmony_ci const promise = SafePromiseAll(subtests, (subtests) => subtests.start()); 9471cb0ef41Sopenharmony_ci 9481cb0ef41Sopenharmony_ci await SafePromiseRace([promise, stopPromise]); 9491cb0ef41Sopenharmony_ci await this.runHook('after', hookArgs); 9501cb0ef41Sopenharmony_ci 9511cb0ef41Sopenharmony_ci this.pass(); 9521cb0ef41Sopenharmony_ci } catch (err) { 9531cb0ef41Sopenharmony_ci if (isTestFailureError(err)) { 9541cb0ef41Sopenharmony_ci this.fail(err); 9551cb0ef41Sopenharmony_ci } else { 9561cb0ef41Sopenharmony_ci this.fail(new ERR_TEST_FAILURE(err, kTestCodeFailure)); 9571cb0ef41Sopenharmony_ci } 9581cb0ef41Sopenharmony_ci } finally { 9591cb0ef41Sopenharmony_ci stopPromise?.[SymbolDispose](); 9601cb0ef41Sopenharmony_ci } 9611cb0ef41Sopenharmony_ci 9621cb0ef41Sopenharmony_ci this.postRun(); 9631cb0ef41Sopenharmony_ci } 9641cb0ef41Sopenharmony_ci} 9651cb0ef41Sopenharmony_ci 9661cb0ef41Sopenharmony_cimodule.exports = { 9671cb0ef41Sopenharmony_ci kCancelledByParent, 9681cb0ef41Sopenharmony_ci kSubtestsFailed, 9691cb0ef41Sopenharmony_ci kTestCodeFailure, 9701cb0ef41Sopenharmony_ci kTestTimeoutFailure, 9711cb0ef41Sopenharmony_ci kAborted, 9721cb0ef41Sopenharmony_ci kUnwrapErrors, 9731cb0ef41Sopenharmony_ci Suite, 9741cb0ef41Sopenharmony_ci Test, 9751cb0ef41Sopenharmony_ci}; 976