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