11cb0ef41Sopenharmony_ci'use strict'; 21cb0ef41Sopenharmony_ci 31cb0ef41Sopenharmony_ciconst assert = require('assert'); 41cb0ef41Sopenharmony_ciconst fixtures = require('../common/fixtures'); 51cb0ef41Sopenharmony_ciconst fs = require('fs'); 61cb0ef41Sopenharmony_ciconst fsPromises = fs.promises; 71cb0ef41Sopenharmony_ciconst path = require('path'); 81cb0ef41Sopenharmony_ciconst events = require('events'); 91cb0ef41Sopenharmony_ciconst os = require('os'); 101cb0ef41Sopenharmony_ciconst { inspect } = require('util'); 111cb0ef41Sopenharmony_ciconst { Worker } = require('worker_threads'); 121cb0ef41Sopenharmony_ci 131cb0ef41Sopenharmony_cifunction getBrowserProperties() { 141cb0ef41Sopenharmony_ci const { node: version } = process.versions; // e.g. 18.13.0, 20.0.0-nightly202302078e6e215481 151cb0ef41Sopenharmony_ci const release = /^\d+\.\d+\.\d+$/.test(version); 161cb0ef41Sopenharmony_ci const browser = { 171cb0ef41Sopenharmony_ci browser_channel: release ? 'stable' : 'experimental', 181cb0ef41Sopenharmony_ci browser_version: version, 191cb0ef41Sopenharmony_ci }; 201cb0ef41Sopenharmony_ci 211cb0ef41Sopenharmony_ci return browser; 221cb0ef41Sopenharmony_ci} 231cb0ef41Sopenharmony_ci 241cb0ef41Sopenharmony_ci/** 251cb0ef41Sopenharmony_ci * Return one of three expected values 261cb0ef41Sopenharmony_ci * https://github.com/web-platform-tests/wpt/blob/1c6ff12/tools/wptrunner/wptrunner/tests/test_update.py#L953-L958 271cb0ef41Sopenharmony_ci */ 281cb0ef41Sopenharmony_cifunction getOs() { 291cb0ef41Sopenharmony_ci switch (os.type()) { 301cb0ef41Sopenharmony_ci case 'Linux': 311cb0ef41Sopenharmony_ci return 'linux'; 321cb0ef41Sopenharmony_ci case 'Darwin': 331cb0ef41Sopenharmony_ci return 'mac'; 341cb0ef41Sopenharmony_ci case 'Windows_NT': 351cb0ef41Sopenharmony_ci return 'win'; 361cb0ef41Sopenharmony_ci default: 371cb0ef41Sopenharmony_ci throw new Error('Unsupported os.type()'); 381cb0ef41Sopenharmony_ci } 391cb0ef41Sopenharmony_ci} 401cb0ef41Sopenharmony_ci 411cb0ef41Sopenharmony_ci// https://github.com/web-platform-tests/wpt/blob/b24eedd/resources/testharness.js#L3705 421cb0ef41Sopenharmony_cifunction sanitizeUnpairedSurrogates(str) { 431cb0ef41Sopenharmony_ci return str.replace( 441cb0ef41Sopenharmony_ci /([\ud800-\udbff]+)(?![\udc00-\udfff])|(^|[^\ud800-\udbff])([\udc00-\udfff]+)/g, 451cb0ef41Sopenharmony_ci function(_, low, prefix, high) { 461cb0ef41Sopenharmony_ci let output = prefix || ''; // Prefix may be undefined 471cb0ef41Sopenharmony_ci const string = low || high; // Only one of these alternates can match 481cb0ef41Sopenharmony_ci for (let i = 0; i < string.length; i++) { 491cb0ef41Sopenharmony_ci output += codeUnitStr(string[i]); 501cb0ef41Sopenharmony_ci } 511cb0ef41Sopenharmony_ci return output; 521cb0ef41Sopenharmony_ci }); 531cb0ef41Sopenharmony_ci} 541cb0ef41Sopenharmony_ci 551cb0ef41Sopenharmony_cifunction codeUnitStr(char) { 561cb0ef41Sopenharmony_ci return 'U+' + char.charCodeAt(0).toString(16); 571cb0ef41Sopenharmony_ci} 581cb0ef41Sopenharmony_ci 591cb0ef41Sopenharmony_ciclass WPTReport { 601cb0ef41Sopenharmony_ci constructor() { 611cb0ef41Sopenharmony_ci this.results = []; 621cb0ef41Sopenharmony_ci this.time_start = Date.now(); 631cb0ef41Sopenharmony_ci } 641cb0ef41Sopenharmony_ci 651cb0ef41Sopenharmony_ci addResult(name, status) { 661cb0ef41Sopenharmony_ci const result = { 671cb0ef41Sopenharmony_ci test: name, 681cb0ef41Sopenharmony_ci status, 691cb0ef41Sopenharmony_ci subtests: [], 701cb0ef41Sopenharmony_ci addSubtest(name, status, message) { 711cb0ef41Sopenharmony_ci const subtest = { 721cb0ef41Sopenharmony_ci status, 731cb0ef41Sopenharmony_ci // https://github.com/web-platform-tests/wpt/blob/b24eedd/resources/testharness.js#L3722 741cb0ef41Sopenharmony_ci name: sanitizeUnpairedSurrogates(name), 751cb0ef41Sopenharmony_ci }; 761cb0ef41Sopenharmony_ci if (message) { 771cb0ef41Sopenharmony_ci // https://github.com/web-platform-tests/wpt/blob/b24eedd/resources/testharness.js#L4506 781cb0ef41Sopenharmony_ci subtest.message = sanitizeUnpairedSurrogates(message); 791cb0ef41Sopenharmony_ci } 801cb0ef41Sopenharmony_ci this.subtests.push(subtest); 811cb0ef41Sopenharmony_ci return subtest; 821cb0ef41Sopenharmony_ci }, 831cb0ef41Sopenharmony_ci }; 841cb0ef41Sopenharmony_ci this.results.push(result); 851cb0ef41Sopenharmony_ci return result; 861cb0ef41Sopenharmony_ci } 871cb0ef41Sopenharmony_ci 881cb0ef41Sopenharmony_ci write() { 891cb0ef41Sopenharmony_ci this.time_end = Date.now(); 901cb0ef41Sopenharmony_ci this.results = this.results.filter((result) => { 911cb0ef41Sopenharmony_ci return result.status === 'SKIP' || result.subtests.length !== 0; 921cb0ef41Sopenharmony_ci }).map((result) => { 931cb0ef41Sopenharmony_ci const url = new URL(result.test, 'http://wpt'); 941cb0ef41Sopenharmony_ci url.pathname = url.pathname.replace(/\.js$/, '.html'); 951cb0ef41Sopenharmony_ci result.test = url.href.slice(url.origin.length); 961cb0ef41Sopenharmony_ci return result; 971cb0ef41Sopenharmony_ci }); 981cb0ef41Sopenharmony_ci 991cb0ef41Sopenharmony_ci if (fs.existsSync('out/wpt/wptreport.json')) { 1001cb0ef41Sopenharmony_ci const prev = JSON.parse(fs.readFileSync('out/wpt/wptreport.json')); 1011cb0ef41Sopenharmony_ci this.results = [...prev.results, ...this.results]; 1021cb0ef41Sopenharmony_ci this.time_start = prev.time_start; 1031cb0ef41Sopenharmony_ci this.time_end = Math.max(this.time_end, prev.time_end); 1041cb0ef41Sopenharmony_ci this.run_info = prev.run_info; 1051cb0ef41Sopenharmony_ci } else { 1061cb0ef41Sopenharmony_ci /** 1071cb0ef41Sopenharmony_ci * Return required and some optional properties 1081cb0ef41Sopenharmony_ci * https://github.com/web-platform-tests/wpt.fyi/blob/60da175/api/README.md?plain=1#L331-L335 1091cb0ef41Sopenharmony_ci */ 1101cb0ef41Sopenharmony_ci this.run_info = { 1111cb0ef41Sopenharmony_ci product: 'node.js', 1121cb0ef41Sopenharmony_ci ...getBrowserProperties(), 1131cb0ef41Sopenharmony_ci revision: process.env.WPT_REVISION || 'unknown', 1141cb0ef41Sopenharmony_ci os: getOs(), 1151cb0ef41Sopenharmony_ci }; 1161cb0ef41Sopenharmony_ci } 1171cb0ef41Sopenharmony_ci 1181cb0ef41Sopenharmony_ci fs.writeFileSync('out/wpt/wptreport.json', JSON.stringify(this)); 1191cb0ef41Sopenharmony_ci } 1201cb0ef41Sopenharmony_ci} 1211cb0ef41Sopenharmony_ci 1221cb0ef41Sopenharmony_ci// https://github.com/web-platform-tests/wpt/blob/HEAD/resources/testharness.js 1231cb0ef41Sopenharmony_ci// TODO: get rid of this half-baked harness in favor of the one 1241cb0ef41Sopenharmony_ci// pulled from WPT 1251cb0ef41Sopenharmony_ciconst harnessMock = { 1261cb0ef41Sopenharmony_ci test: (fn, desc) => { 1271cb0ef41Sopenharmony_ci try { 1281cb0ef41Sopenharmony_ci fn(); 1291cb0ef41Sopenharmony_ci } catch (err) { 1301cb0ef41Sopenharmony_ci console.error(`In ${desc}:`); 1311cb0ef41Sopenharmony_ci throw err; 1321cb0ef41Sopenharmony_ci } 1331cb0ef41Sopenharmony_ci }, 1341cb0ef41Sopenharmony_ci assert_equals: assert.strictEqual, 1351cb0ef41Sopenharmony_ci assert_true: (value, message) => assert.strictEqual(value, true, message), 1361cb0ef41Sopenharmony_ci assert_false: (value, message) => assert.strictEqual(value, false, message), 1371cb0ef41Sopenharmony_ci assert_throws: (code, func, desc) => { 1381cb0ef41Sopenharmony_ci assert.throws(func, function(err) { 1391cb0ef41Sopenharmony_ci return typeof err === 'object' && 1401cb0ef41Sopenharmony_ci 'name' in err && 1411cb0ef41Sopenharmony_ci err.name.startsWith(code.name); 1421cb0ef41Sopenharmony_ci }, desc); 1431cb0ef41Sopenharmony_ci }, 1441cb0ef41Sopenharmony_ci assert_array_equals: assert.deepStrictEqual, 1451cb0ef41Sopenharmony_ci assert_unreached(desc) { 1461cb0ef41Sopenharmony_ci assert.fail(`Reached unreachable code: ${desc}`); 1471cb0ef41Sopenharmony_ci }, 1481cb0ef41Sopenharmony_ci}; 1491cb0ef41Sopenharmony_ci 1501cb0ef41Sopenharmony_ciclass ResourceLoader { 1511cb0ef41Sopenharmony_ci constructor(path) { 1521cb0ef41Sopenharmony_ci this.path = path; 1531cb0ef41Sopenharmony_ci } 1541cb0ef41Sopenharmony_ci 1551cb0ef41Sopenharmony_ci toRealFilePath(from, url) { 1561cb0ef41Sopenharmony_ci // We need to patch this to load the WebIDL parser 1571cb0ef41Sopenharmony_ci url = url.replace( 1581cb0ef41Sopenharmony_ci '/resources/WebIDLParser.js', 1591cb0ef41Sopenharmony_ci '/resources/webidl2/lib/webidl2.js', 1601cb0ef41Sopenharmony_ci ); 1611cb0ef41Sopenharmony_ci const base = path.dirname(from); 1621cb0ef41Sopenharmony_ci return url.startsWith('/') ? 1631cb0ef41Sopenharmony_ci fixtures.path('wpt', url) : 1641cb0ef41Sopenharmony_ci fixtures.path('wpt', base, url); 1651cb0ef41Sopenharmony_ci } 1661cb0ef41Sopenharmony_ci 1671cb0ef41Sopenharmony_ci /** 1681cb0ef41Sopenharmony_ci * Load a resource in test/fixtures/wpt specified with a URL 1691cb0ef41Sopenharmony_ci * @param {string} from the path of the file loading this resource, 1701cb0ef41Sopenharmony_ci * relative to the WPT folder. 1711cb0ef41Sopenharmony_ci * @param {string} url the url of the resource being loaded. 1721cb0ef41Sopenharmony_ci * @param {boolean} asFetch if true, return the resource in a 1731cb0ef41Sopenharmony_ci * pseudo-Response object. 1741cb0ef41Sopenharmony_ci */ 1751cb0ef41Sopenharmony_ci read(from, url, asFetch = true) { 1761cb0ef41Sopenharmony_ci const file = this.toRealFilePath(from, url); 1771cb0ef41Sopenharmony_ci if (asFetch) { 1781cb0ef41Sopenharmony_ci return fsPromises.readFile(file) 1791cb0ef41Sopenharmony_ci .then((data) => { 1801cb0ef41Sopenharmony_ci return { 1811cb0ef41Sopenharmony_ci ok: true, 1821cb0ef41Sopenharmony_ci json() { return JSON.parse(data.toString()); }, 1831cb0ef41Sopenharmony_ci text() { return data.toString(); }, 1841cb0ef41Sopenharmony_ci }; 1851cb0ef41Sopenharmony_ci }); 1861cb0ef41Sopenharmony_ci } 1871cb0ef41Sopenharmony_ci return fs.readFileSync(file, 'utf8'); 1881cb0ef41Sopenharmony_ci } 1891cb0ef41Sopenharmony_ci} 1901cb0ef41Sopenharmony_ci 1911cb0ef41Sopenharmony_ciclass StatusRule { 1921cb0ef41Sopenharmony_ci constructor(key, value, pattern) { 1931cb0ef41Sopenharmony_ci this.key = key; 1941cb0ef41Sopenharmony_ci this.requires = value.requires || []; 1951cb0ef41Sopenharmony_ci this.fail = value.fail; 1961cb0ef41Sopenharmony_ci this.skip = value.skip; 1971cb0ef41Sopenharmony_ci if (pattern) { 1981cb0ef41Sopenharmony_ci this.pattern = this.transformPattern(pattern); 1991cb0ef41Sopenharmony_ci } 2001cb0ef41Sopenharmony_ci // TODO(joyeecheung): implement this 2011cb0ef41Sopenharmony_ci this.scope = value.scope; 2021cb0ef41Sopenharmony_ci this.comment = value.comment; 2031cb0ef41Sopenharmony_ci } 2041cb0ef41Sopenharmony_ci 2051cb0ef41Sopenharmony_ci /** 2061cb0ef41Sopenharmony_ci * Transform a filename pattern into a RegExp 2071cb0ef41Sopenharmony_ci * @param {string} pattern 2081cb0ef41Sopenharmony_ci * @returns {RegExp} 2091cb0ef41Sopenharmony_ci */ 2101cb0ef41Sopenharmony_ci transformPattern(pattern) { 2111cb0ef41Sopenharmony_ci const result = path.normalize(pattern).replace(/[-/\\^$+?.()|[\]{}]/g, '\\$&'); 2121cb0ef41Sopenharmony_ci return new RegExp(result.replace('*', '.*')); 2131cb0ef41Sopenharmony_ci } 2141cb0ef41Sopenharmony_ci} 2151cb0ef41Sopenharmony_ci 2161cb0ef41Sopenharmony_ciclass StatusRuleSet { 2171cb0ef41Sopenharmony_ci constructor() { 2181cb0ef41Sopenharmony_ci // We use two sets of rules to speed up matching 2191cb0ef41Sopenharmony_ci this.exactMatch = {}; 2201cb0ef41Sopenharmony_ci this.patternMatch = []; 2211cb0ef41Sopenharmony_ci } 2221cb0ef41Sopenharmony_ci 2231cb0ef41Sopenharmony_ci /** 2241cb0ef41Sopenharmony_ci * @param {object} rules 2251cb0ef41Sopenharmony_ci */ 2261cb0ef41Sopenharmony_ci addRules(rules) { 2271cb0ef41Sopenharmony_ci for (const key of Object.keys(rules)) { 2281cb0ef41Sopenharmony_ci if (key.includes('*')) { 2291cb0ef41Sopenharmony_ci this.patternMatch.push(new StatusRule(key, rules[key], key)); 2301cb0ef41Sopenharmony_ci } else { 2311cb0ef41Sopenharmony_ci const normalizedPath = path.normalize(key); 2321cb0ef41Sopenharmony_ci this.exactMatch[normalizedPath] = new StatusRule(key, rules[key]); 2331cb0ef41Sopenharmony_ci } 2341cb0ef41Sopenharmony_ci } 2351cb0ef41Sopenharmony_ci } 2361cb0ef41Sopenharmony_ci 2371cb0ef41Sopenharmony_ci match(file) { 2381cb0ef41Sopenharmony_ci const result = []; 2391cb0ef41Sopenharmony_ci const exact = this.exactMatch[file]; 2401cb0ef41Sopenharmony_ci if (exact) { 2411cb0ef41Sopenharmony_ci result.push(exact); 2421cb0ef41Sopenharmony_ci } 2431cb0ef41Sopenharmony_ci for (const item of this.patternMatch) { 2441cb0ef41Sopenharmony_ci if (item.pattern.test(file)) { 2451cb0ef41Sopenharmony_ci result.push(item); 2461cb0ef41Sopenharmony_ci } 2471cb0ef41Sopenharmony_ci } 2481cb0ef41Sopenharmony_ci return result; 2491cb0ef41Sopenharmony_ci } 2501cb0ef41Sopenharmony_ci} 2511cb0ef41Sopenharmony_ci 2521cb0ef41Sopenharmony_ci// A specification of WPT test 2531cb0ef41Sopenharmony_ciclass WPTTestSpec { 2541cb0ef41Sopenharmony_ci /** 2551cb0ef41Sopenharmony_ci * @param {string} mod name of the WPT module, e.g. 2561cb0ef41Sopenharmony_ci * 'html/webappapis/microtask-queuing' 2571cb0ef41Sopenharmony_ci * @param {string} filename path of the test, relative to mod, e.g. 2581cb0ef41Sopenharmony_ci * 'test.any.js' 2591cb0ef41Sopenharmony_ci * @param {StatusRule[]} rules 2601cb0ef41Sopenharmony_ci */ 2611cb0ef41Sopenharmony_ci constructor(mod, filename, rules) { 2621cb0ef41Sopenharmony_ci this.module = mod; 2631cb0ef41Sopenharmony_ci this.filename = filename; 2641cb0ef41Sopenharmony_ci 2651cb0ef41Sopenharmony_ci this.requires = new Set(); 2661cb0ef41Sopenharmony_ci this.failedTests = []; 2671cb0ef41Sopenharmony_ci this.flakyTests = []; 2681cb0ef41Sopenharmony_ci this.skipReasons = []; 2691cb0ef41Sopenharmony_ci for (const item of rules) { 2701cb0ef41Sopenharmony_ci if (item.requires.length) { 2711cb0ef41Sopenharmony_ci for (const req of item.requires) { 2721cb0ef41Sopenharmony_ci this.requires.add(req); 2731cb0ef41Sopenharmony_ci } 2741cb0ef41Sopenharmony_ci } 2751cb0ef41Sopenharmony_ci if (Array.isArray(item.fail?.expected)) { 2761cb0ef41Sopenharmony_ci this.failedTests.push(...item.fail.expected); 2771cb0ef41Sopenharmony_ci } 2781cb0ef41Sopenharmony_ci if (Array.isArray(item.fail?.flaky)) { 2791cb0ef41Sopenharmony_ci this.failedTests.push(...item.fail.flaky); 2801cb0ef41Sopenharmony_ci this.flakyTests.push(...item.fail.flaky); 2811cb0ef41Sopenharmony_ci } 2821cb0ef41Sopenharmony_ci if (item.skip) { 2831cb0ef41Sopenharmony_ci this.skipReasons.push(item.skip); 2841cb0ef41Sopenharmony_ci } 2851cb0ef41Sopenharmony_ci } 2861cb0ef41Sopenharmony_ci 2871cb0ef41Sopenharmony_ci this.failedTests = [...new Set(this.failedTests)]; 2881cb0ef41Sopenharmony_ci this.flakyTests = [...new Set(this.flakyTests)]; 2891cb0ef41Sopenharmony_ci this.skipReasons = [...new Set(this.skipReasons)]; 2901cb0ef41Sopenharmony_ci } 2911cb0ef41Sopenharmony_ci 2921cb0ef41Sopenharmony_ci getRelativePath() { 2931cb0ef41Sopenharmony_ci return path.join(this.module, this.filename); 2941cb0ef41Sopenharmony_ci } 2951cb0ef41Sopenharmony_ci 2961cb0ef41Sopenharmony_ci getAbsolutePath() { 2971cb0ef41Sopenharmony_ci return fixtures.path('wpt', this.getRelativePath()); 2981cb0ef41Sopenharmony_ci } 2991cb0ef41Sopenharmony_ci 3001cb0ef41Sopenharmony_ci getContent() { 3011cb0ef41Sopenharmony_ci return fs.readFileSync(this.getAbsolutePath(), 'utf8'); 3021cb0ef41Sopenharmony_ci } 3031cb0ef41Sopenharmony_ci} 3041cb0ef41Sopenharmony_ci 3051cb0ef41Sopenharmony_ciconst kIntlRequirement = { 3061cb0ef41Sopenharmony_ci none: 0, 3071cb0ef41Sopenharmony_ci small: 1, 3081cb0ef41Sopenharmony_ci full: 2, 3091cb0ef41Sopenharmony_ci // TODO(joyeecheung): we may need to deal with --with-intl=system-icu 3101cb0ef41Sopenharmony_ci}; 3111cb0ef41Sopenharmony_ci 3121cb0ef41Sopenharmony_ciclass IntlRequirement { 3131cb0ef41Sopenharmony_ci constructor() { 3141cb0ef41Sopenharmony_ci this.currentIntl = kIntlRequirement.none; 3151cb0ef41Sopenharmony_ci if (process.config.variables.v8_enable_i18n_support === 0) { 3161cb0ef41Sopenharmony_ci this.currentIntl = kIntlRequirement.none; 3171cb0ef41Sopenharmony_ci return; 3181cb0ef41Sopenharmony_ci } 3191cb0ef41Sopenharmony_ci // i18n enabled 3201cb0ef41Sopenharmony_ci if (process.config.variables.icu_small) { 3211cb0ef41Sopenharmony_ci this.currentIntl = kIntlRequirement.small; 3221cb0ef41Sopenharmony_ci } else { 3231cb0ef41Sopenharmony_ci this.currentIntl = kIntlRequirement.full; 3241cb0ef41Sopenharmony_ci } 3251cb0ef41Sopenharmony_ci } 3261cb0ef41Sopenharmony_ci 3271cb0ef41Sopenharmony_ci /** 3281cb0ef41Sopenharmony_ci * @param {Set} requires 3291cb0ef41Sopenharmony_ci * @returns {string|false} The config that the build is lacking, or false 3301cb0ef41Sopenharmony_ci */ 3311cb0ef41Sopenharmony_ci isLacking(requires) { 3321cb0ef41Sopenharmony_ci const current = this.currentIntl; 3331cb0ef41Sopenharmony_ci if (requires.has('full-icu') && current !== kIntlRequirement.full) { 3341cb0ef41Sopenharmony_ci return 'full-icu'; 3351cb0ef41Sopenharmony_ci } 3361cb0ef41Sopenharmony_ci if (requires.has('small-icu') && current < kIntlRequirement.small) { 3371cb0ef41Sopenharmony_ci return 'small-icu'; 3381cb0ef41Sopenharmony_ci } 3391cb0ef41Sopenharmony_ci return false; 3401cb0ef41Sopenharmony_ci } 3411cb0ef41Sopenharmony_ci} 3421cb0ef41Sopenharmony_ci 3431cb0ef41Sopenharmony_ciconst intlRequirements = new IntlRequirement(); 3441cb0ef41Sopenharmony_ci 3451cb0ef41Sopenharmony_ciclass StatusLoader { 3461cb0ef41Sopenharmony_ci /** 3471cb0ef41Sopenharmony_ci * @param {string} path relative path of the WPT subset 3481cb0ef41Sopenharmony_ci */ 3491cb0ef41Sopenharmony_ci constructor(path) { 3501cb0ef41Sopenharmony_ci this.path = path; 3511cb0ef41Sopenharmony_ci this.loaded = false; 3521cb0ef41Sopenharmony_ci this.rules = new StatusRuleSet(); 3531cb0ef41Sopenharmony_ci /** @type {WPTTestSpec[]} */ 3541cb0ef41Sopenharmony_ci this.specs = []; 3551cb0ef41Sopenharmony_ci } 3561cb0ef41Sopenharmony_ci 3571cb0ef41Sopenharmony_ci /** 3581cb0ef41Sopenharmony_ci * Grep for all .*.js file recursively in a directory. 3591cb0ef41Sopenharmony_ci * @param {string} dir 3601cb0ef41Sopenharmony_ci */ 3611cb0ef41Sopenharmony_ci grep(dir) { 3621cb0ef41Sopenharmony_ci let result = []; 3631cb0ef41Sopenharmony_ci const list = fs.readdirSync(dir); 3641cb0ef41Sopenharmony_ci for (const file of list) { 3651cb0ef41Sopenharmony_ci const filepath = path.join(dir, file); 3661cb0ef41Sopenharmony_ci const stat = fs.statSync(filepath); 3671cb0ef41Sopenharmony_ci if (stat.isDirectory()) { 3681cb0ef41Sopenharmony_ci const list = this.grep(filepath); 3691cb0ef41Sopenharmony_ci result = result.concat(list); 3701cb0ef41Sopenharmony_ci } else { 3711cb0ef41Sopenharmony_ci if (!(/\.\w+\.js$/.test(filepath)) || filepath.endsWith('.helper.js')) { 3721cb0ef41Sopenharmony_ci continue; 3731cb0ef41Sopenharmony_ci } 3741cb0ef41Sopenharmony_ci result.push(filepath); 3751cb0ef41Sopenharmony_ci } 3761cb0ef41Sopenharmony_ci } 3771cb0ef41Sopenharmony_ci return result; 3781cb0ef41Sopenharmony_ci } 3791cb0ef41Sopenharmony_ci 3801cb0ef41Sopenharmony_ci load() { 3811cb0ef41Sopenharmony_ci const dir = path.join(__dirname, '..', 'wpt'); 3821cb0ef41Sopenharmony_ci const statusFile = path.join(dir, 'status', `${this.path}.json`); 3831cb0ef41Sopenharmony_ci const result = JSON.parse(fs.readFileSync(statusFile, 'utf8')); 3841cb0ef41Sopenharmony_ci this.rules.addRules(result); 3851cb0ef41Sopenharmony_ci 3861cb0ef41Sopenharmony_ci const subDir = fixtures.path('wpt', this.path); 3871cb0ef41Sopenharmony_ci const list = this.grep(subDir); 3881cb0ef41Sopenharmony_ci for (const file of list) { 3891cb0ef41Sopenharmony_ci const relativePath = path.relative(subDir, file); 3901cb0ef41Sopenharmony_ci const match = this.rules.match(relativePath); 3911cb0ef41Sopenharmony_ci this.specs.push(new WPTTestSpec(this.path, relativePath, match)); 3921cb0ef41Sopenharmony_ci } 3931cb0ef41Sopenharmony_ci this.loaded = true; 3941cb0ef41Sopenharmony_ci } 3951cb0ef41Sopenharmony_ci} 3961cb0ef41Sopenharmony_ci 3971cb0ef41Sopenharmony_ciconst kPass = 'pass'; 3981cb0ef41Sopenharmony_ciconst kFail = 'fail'; 3991cb0ef41Sopenharmony_ciconst kSkip = 'skip'; 4001cb0ef41Sopenharmony_ciconst kTimeout = 'timeout'; 4011cb0ef41Sopenharmony_ciconst kIncomplete = 'incomplete'; 4021cb0ef41Sopenharmony_ciconst kUncaught = 'uncaught'; 4031cb0ef41Sopenharmony_ciconst NODE_UNCAUGHT = 100; 4041cb0ef41Sopenharmony_ci 4051cb0ef41Sopenharmony_ciclass WPTRunner { 4061cb0ef41Sopenharmony_ci constructor(path) { 4071cb0ef41Sopenharmony_ci this.path = path; 4081cb0ef41Sopenharmony_ci this.resource = new ResourceLoader(path); 4091cb0ef41Sopenharmony_ci 4101cb0ef41Sopenharmony_ci this.flags = []; 4111cb0ef41Sopenharmony_ci this.globalThisInitScripts = []; 4121cb0ef41Sopenharmony_ci this.initScript = null; 4131cb0ef41Sopenharmony_ci 4141cb0ef41Sopenharmony_ci this.status = new StatusLoader(path); 4151cb0ef41Sopenharmony_ci this.status.load(); 4161cb0ef41Sopenharmony_ci this.specMap = new Map( 4171cb0ef41Sopenharmony_ci this.status.specs.map((item) => [item.filename, item]), 4181cb0ef41Sopenharmony_ci ); 4191cb0ef41Sopenharmony_ci 4201cb0ef41Sopenharmony_ci this.results = {}; 4211cb0ef41Sopenharmony_ci this.inProgress = new Set(); 4221cb0ef41Sopenharmony_ci this.workers = new Map(); 4231cb0ef41Sopenharmony_ci this.unexpectedFailures = []; 4241cb0ef41Sopenharmony_ci 4251cb0ef41Sopenharmony_ci this.scriptsModifier = null; 4261cb0ef41Sopenharmony_ci 4271cb0ef41Sopenharmony_ci if (process.env.WPT_REPORT != null) { 4281cb0ef41Sopenharmony_ci this.report = new WPTReport(); 4291cb0ef41Sopenharmony_ci } 4301cb0ef41Sopenharmony_ci } 4311cb0ef41Sopenharmony_ci 4321cb0ef41Sopenharmony_ci /** 4331cb0ef41Sopenharmony_ci * Sets the Node.js flags passed to the worker. 4341cb0ef41Sopenharmony_ci * @param {Array<string>} flags 4351cb0ef41Sopenharmony_ci */ 4361cb0ef41Sopenharmony_ci setFlags(flags) { 4371cb0ef41Sopenharmony_ci this.flags = flags; 4381cb0ef41Sopenharmony_ci } 4391cb0ef41Sopenharmony_ci 4401cb0ef41Sopenharmony_ci /** 4411cb0ef41Sopenharmony_ci * Sets a script to be run in the worker before executing the tests. 4421cb0ef41Sopenharmony_ci * @param {string} script 4431cb0ef41Sopenharmony_ci */ 4441cb0ef41Sopenharmony_ci setInitScript(script) { 4451cb0ef41Sopenharmony_ci this.initScript = script; 4461cb0ef41Sopenharmony_ci } 4471cb0ef41Sopenharmony_ci 4481cb0ef41Sopenharmony_ci /** 4491cb0ef41Sopenharmony_ci * Set the scripts modifier for each script. 4501cb0ef41Sopenharmony_ci * @param {(meta: { code: string, filename: string }) => void} modifier 4511cb0ef41Sopenharmony_ci */ 4521cb0ef41Sopenharmony_ci setScriptModifier(modifier) { 4531cb0ef41Sopenharmony_ci this.scriptsModifier = modifier; 4541cb0ef41Sopenharmony_ci } 4551cb0ef41Sopenharmony_ci 4561cb0ef41Sopenharmony_ci fullInitScript(hasSubsetScript, locationSearchString) { 4571cb0ef41Sopenharmony_ci let { initScript } = this; 4581cb0ef41Sopenharmony_ci if (hasSubsetScript || locationSearchString) { 4591cb0ef41Sopenharmony_ci initScript = `${initScript}\n\n//===\nglobalThis.location ||= {};`; 4601cb0ef41Sopenharmony_ci } 4611cb0ef41Sopenharmony_ci 4621cb0ef41Sopenharmony_ci if (locationSearchString) { 4631cb0ef41Sopenharmony_ci initScript = `${initScript}\n\n//===\nglobalThis.location.search = "${locationSearchString}";`; 4641cb0ef41Sopenharmony_ci } 4651cb0ef41Sopenharmony_ci 4661cb0ef41Sopenharmony_ci if (this.globalThisInitScripts.length === null) { 4671cb0ef41Sopenharmony_ci return initScript; 4681cb0ef41Sopenharmony_ci } 4691cb0ef41Sopenharmony_ci 4701cb0ef41Sopenharmony_ci const globalThisInitScript = this.globalThisInitScripts.join('\n\n//===\n'); 4711cb0ef41Sopenharmony_ci 4721cb0ef41Sopenharmony_ci if (initScript === null) { 4731cb0ef41Sopenharmony_ci return globalThisInitScript; 4741cb0ef41Sopenharmony_ci } 4751cb0ef41Sopenharmony_ci 4761cb0ef41Sopenharmony_ci return `${globalThisInitScript}\n\n//===\n${initScript}`; 4771cb0ef41Sopenharmony_ci } 4781cb0ef41Sopenharmony_ci 4791cb0ef41Sopenharmony_ci /** 4801cb0ef41Sopenharmony_ci * Pretend the runner is run in `name`'s environment (globalThis). 4811cb0ef41Sopenharmony_ci * @param {'Window'} name 4821cb0ef41Sopenharmony_ci * @see {@link https://github.com/nodejs/node/blob/24673ace8ae196bd1c6d4676507d6e8c94cf0b90/test/fixtures/wpt/resources/idlharness.js#L654-L671} 4831cb0ef41Sopenharmony_ci */ 4841cb0ef41Sopenharmony_ci pretendGlobalThisAs(name) { 4851cb0ef41Sopenharmony_ci switch (name) { 4861cb0ef41Sopenharmony_ci case 'Window': { 4871cb0ef41Sopenharmony_ci this.globalThisInitScripts.push( 4881cb0ef41Sopenharmony_ci `global.Window = Object.getPrototypeOf(globalThis).constructor; 4891cb0ef41Sopenharmony_ci self.GLOBAL.isWorker = () => false;`); 4901cb0ef41Sopenharmony_ci this.loadLazyGlobals(); 4911cb0ef41Sopenharmony_ci break; 4921cb0ef41Sopenharmony_ci } 4931cb0ef41Sopenharmony_ci 4941cb0ef41Sopenharmony_ci // TODO(XadillaX): implement `ServiceWorkerGlobalScope`, 4951cb0ef41Sopenharmony_ci // `DedicateWorkerGlobalScope`, etc. 4961cb0ef41Sopenharmony_ci // 4971cb0ef41Sopenharmony_ci // e.g. `ServiceWorkerGlobalScope` should implement dummy 4981cb0ef41Sopenharmony_ci // `addEventListener` and so on. 4991cb0ef41Sopenharmony_ci 5001cb0ef41Sopenharmony_ci default: throw new Error(`Invalid globalThis type ${name}.`); 5011cb0ef41Sopenharmony_ci } 5021cb0ef41Sopenharmony_ci } 5031cb0ef41Sopenharmony_ci 5041cb0ef41Sopenharmony_ci loadLazyGlobals() { 5051cb0ef41Sopenharmony_ci const lazyProperties = [ 5061cb0ef41Sopenharmony_ci 'DOMException', 5071cb0ef41Sopenharmony_ci 'Performance', 'PerformanceEntry', 'PerformanceMark', 'PerformanceMeasure', 5081cb0ef41Sopenharmony_ci 'PerformanceObserver', 'PerformanceObserverEntryList', 'PerformanceResourceTiming', 5091cb0ef41Sopenharmony_ci 'Blob', 'atob', 'btoa', 5101cb0ef41Sopenharmony_ci 'MessageChannel', 'MessagePort', 'MessageEvent', 5111cb0ef41Sopenharmony_ci 'EventTarget', 'Event', 5121cb0ef41Sopenharmony_ci 'AbortController', 'AbortSignal', 5131cb0ef41Sopenharmony_ci 'performance', 5141cb0ef41Sopenharmony_ci 'TransformStream', 'TransformStreamDefaultController', 5151cb0ef41Sopenharmony_ci 'WritableStream', 'WritableStreamDefaultController', 'WritableStreamDefaultWriter', 5161cb0ef41Sopenharmony_ci 'ReadableStream', 'ReadableStreamDefaultReader', 5171cb0ef41Sopenharmony_ci 'ReadableStreamBYOBReader', 'ReadableStreamBYOBRequest', 5181cb0ef41Sopenharmony_ci 'ReadableByteStreamController', 'ReadableStreamDefaultController', 5191cb0ef41Sopenharmony_ci 'ByteLengthQueuingStrategy', 'CountQueuingStrategy', 5201cb0ef41Sopenharmony_ci 'TextEncoderStream', 'TextDecoderStream', 5211cb0ef41Sopenharmony_ci 'CompressionStream', 'DecompressionStream', 5221cb0ef41Sopenharmony_ci ]; 5231cb0ef41Sopenharmony_ci if (Boolean(process.versions.openssl) && !process.env.NODE_SKIP_CRYPTO) { 5241cb0ef41Sopenharmony_ci lazyProperties.push('crypto'); 5251cb0ef41Sopenharmony_ci } 5261cb0ef41Sopenharmony_ci const script = lazyProperties.map((name) => `globalThis.${name};`).join('\n'); 5271cb0ef41Sopenharmony_ci this.globalThisInitScripts.push(script); 5281cb0ef41Sopenharmony_ci } 5291cb0ef41Sopenharmony_ci 5301cb0ef41Sopenharmony_ci // TODO(joyeecheung): work with the upstream to port more tests in .html 5311cb0ef41Sopenharmony_ci // to .js. 5321cb0ef41Sopenharmony_ci async runJsTests() { 5331cb0ef41Sopenharmony_ci let queue = []; 5341cb0ef41Sopenharmony_ci 5351cb0ef41Sopenharmony_ci // If the tests are run as `node test/wpt/test-something.js subset.any.js`, 5361cb0ef41Sopenharmony_ci // only `subset.any.js` will be run by the runner. 5371cb0ef41Sopenharmony_ci if (process.argv[2]) { 5381cb0ef41Sopenharmony_ci const filename = process.argv[2]; 5391cb0ef41Sopenharmony_ci if (!this.specMap.has(filename)) { 5401cb0ef41Sopenharmony_ci throw new Error(`${filename} not found!`); 5411cb0ef41Sopenharmony_ci } 5421cb0ef41Sopenharmony_ci queue.push(this.specMap.get(filename)); 5431cb0ef41Sopenharmony_ci } else { 5441cb0ef41Sopenharmony_ci queue = this.buildQueue(); 5451cb0ef41Sopenharmony_ci } 5461cb0ef41Sopenharmony_ci 5471cb0ef41Sopenharmony_ci this.inProgress = new Set(queue.map((spec) => spec.filename)); 5481cb0ef41Sopenharmony_ci 5491cb0ef41Sopenharmony_ci for (const spec of queue) { 5501cb0ef41Sopenharmony_ci const testFileName = spec.filename; 5511cb0ef41Sopenharmony_ci const content = spec.getContent(); 5521cb0ef41Sopenharmony_ci const meta = spec.meta = this.getMeta(content); 5531cb0ef41Sopenharmony_ci 5541cb0ef41Sopenharmony_ci const absolutePath = spec.getAbsolutePath(); 5551cb0ef41Sopenharmony_ci const relativePath = spec.getRelativePath(); 5561cb0ef41Sopenharmony_ci const harnessPath = fixtures.path('wpt', 'resources', 'testharness.js'); 5571cb0ef41Sopenharmony_ci const scriptsToRun = []; 5581cb0ef41Sopenharmony_ci let hasSubsetScript = false; 5591cb0ef41Sopenharmony_ci 5601cb0ef41Sopenharmony_ci // Scripts specified with the `// META: script=` header 5611cb0ef41Sopenharmony_ci if (meta.script) { 5621cb0ef41Sopenharmony_ci for (const script of meta.script) { 5631cb0ef41Sopenharmony_ci if (script === '/common/subset-tests.js' || script === '/common/subset-tests-by-key.js') { 5641cb0ef41Sopenharmony_ci hasSubsetScript = true; 5651cb0ef41Sopenharmony_ci } 5661cb0ef41Sopenharmony_ci const obj = { 5671cb0ef41Sopenharmony_ci filename: this.resource.toRealFilePath(relativePath, script), 5681cb0ef41Sopenharmony_ci code: this.resource.read(relativePath, script, false), 5691cb0ef41Sopenharmony_ci }; 5701cb0ef41Sopenharmony_ci this.scriptsModifier?.(obj); 5711cb0ef41Sopenharmony_ci scriptsToRun.push(obj); 5721cb0ef41Sopenharmony_ci } 5731cb0ef41Sopenharmony_ci } 5741cb0ef41Sopenharmony_ci // The actual test 5751cb0ef41Sopenharmony_ci const obj = { 5761cb0ef41Sopenharmony_ci code: content, 5771cb0ef41Sopenharmony_ci filename: absolutePath, 5781cb0ef41Sopenharmony_ci }; 5791cb0ef41Sopenharmony_ci this.scriptsModifier?.(obj); 5801cb0ef41Sopenharmony_ci scriptsToRun.push(obj); 5811cb0ef41Sopenharmony_ci 5821cb0ef41Sopenharmony_ci /** 5831cb0ef41Sopenharmony_ci * Example test with no META variant 5841cb0ef41Sopenharmony_ci * https://github.com/nodejs/node/blob/03854f6/test/fixtures/wpt/WebCryptoAPI/sign_verify/hmac.https.any.js#L1-L4 5851cb0ef41Sopenharmony_ci * 5861cb0ef41Sopenharmony_ci * Example test with multiple META variants 5871cb0ef41Sopenharmony_ci * https://github.com/nodejs/node/blob/03854f6/test/fixtures/wpt/WebCryptoAPI/generateKey/successes_RSASSA-PKCS1-v1_5.https.any.js#L1-L9 5881cb0ef41Sopenharmony_ci */ 5891cb0ef41Sopenharmony_ci for (const variant of meta.variant || ['']) { 5901cb0ef41Sopenharmony_ci const workerPath = path.join(__dirname, 'wpt/worker.js'); 5911cb0ef41Sopenharmony_ci const worker = new Worker(workerPath, { 5921cb0ef41Sopenharmony_ci execArgv: this.flags, 5931cb0ef41Sopenharmony_ci workerData: { 5941cb0ef41Sopenharmony_ci testRelativePath: relativePath, 5951cb0ef41Sopenharmony_ci wptRunner: __filename, 5961cb0ef41Sopenharmony_ci wptPath: this.path, 5971cb0ef41Sopenharmony_ci initScript: this.fullInitScript(hasSubsetScript, variant), 5981cb0ef41Sopenharmony_ci harness: { 5991cb0ef41Sopenharmony_ci code: fs.readFileSync(harnessPath, 'utf8'), 6001cb0ef41Sopenharmony_ci filename: harnessPath, 6011cb0ef41Sopenharmony_ci }, 6021cb0ef41Sopenharmony_ci scriptsToRun, 6031cb0ef41Sopenharmony_ci }, 6041cb0ef41Sopenharmony_ci }); 6051cb0ef41Sopenharmony_ci this.workers.set(testFileName, worker); 6061cb0ef41Sopenharmony_ci 6071cb0ef41Sopenharmony_ci let reportResult; 6081cb0ef41Sopenharmony_ci worker.on('message', (message) => { 6091cb0ef41Sopenharmony_ci switch (message.type) { 6101cb0ef41Sopenharmony_ci case 'result': 6111cb0ef41Sopenharmony_ci reportResult ||= this.report?.addResult(`/${relativePath}${variant}`, 'OK'); 6121cb0ef41Sopenharmony_ci return this.resultCallback(testFileName, message.result, reportResult); 6131cb0ef41Sopenharmony_ci case 'completion': 6141cb0ef41Sopenharmony_ci return this.completionCallback(testFileName, message.status); 6151cb0ef41Sopenharmony_ci default: 6161cb0ef41Sopenharmony_ci throw new Error(`Unexpected message from worker: ${message.type}`); 6171cb0ef41Sopenharmony_ci } 6181cb0ef41Sopenharmony_ci }); 6191cb0ef41Sopenharmony_ci 6201cb0ef41Sopenharmony_ci worker.on('error', (err) => { 6211cb0ef41Sopenharmony_ci if (!this.inProgress.has(testFileName)) { 6221cb0ef41Sopenharmony_ci // The test is already finished. Ignore errors that occur after it. 6231cb0ef41Sopenharmony_ci // This can happen normally, for example in timers tests. 6241cb0ef41Sopenharmony_ci return; 6251cb0ef41Sopenharmony_ci } 6261cb0ef41Sopenharmony_ci this.fail( 6271cb0ef41Sopenharmony_ci testFileName, 6281cb0ef41Sopenharmony_ci { 6291cb0ef41Sopenharmony_ci status: NODE_UNCAUGHT, 6301cb0ef41Sopenharmony_ci name: 'evaluation in WPTRunner.runJsTests()', 6311cb0ef41Sopenharmony_ci message: err.message, 6321cb0ef41Sopenharmony_ci stack: inspect(err), 6331cb0ef41Sopenharmony_ci }, 6341cb0ef41Sopenharmony_ci kUncaught, 6351cb0ef41Sopenharmony_ci ); 6361cb0ef41Sopenharmony_ci this.inProgress.delete(testFileName); 6371cb0ef41Sopenharmony_ci }); 6381cb0ef41Sopenharmony_ci 6391cb0ef41Sopenharmony_ci await events.once(worker, 'exit').catch(() => {}); 6401cb0ef41Sopenharmony_ci } 6411cb0ef41Sopenharmony_ci } 6421cb0ef41Sopenharmony_ci 6431cb0ef41Sopenharmony_ci process.on('exit', () => { 6441cb0ef41Sopenharmony_ci for (const spec of this.inProgress) { 6451cb0ef41Sopenharmony_ci this.fail(spec, { name: 'Incomplete' }, kIncomplete); 6461cb0ef41Sopenharmony_ci } 6471cb0ef41Sopenharmony_ci inspect.defaultOptions.depth = Infinity; 6481cb0ef41Sopenharmony_ci // Sorts the rules to have consistent output 6491cb0ef41Sopenharmony_ci console.log(JSON.stringify(Object.keys(this.results).sort().reduce( 6501cb0ef41Sopenharmony_ci (obj, key) => { 6511cb0ef41Sopenharmony_ci obj[key] = this.results[key]; 6521cb0ef41Sopenharmony_ci return obj; 6531cb0ef41Sopenharmony_ci }, 6541cb0ef41Sopenharmony_ci {}, 6551cb0ef41Sopenharmony_ci ), null, 2)); 6561cb0ef41Sopenharmony_ci 6571cb0ef41Sopenharmony_ci const failures = []; 6581cb0ef41Sopenharmony_ci let expectedFailures = 0; 6591cb0ef41Sopenharmony_ci let skipped = 0; 6601cb0ef41Sopenharmony_ci for (const [key, item] of Object.entries(this.results)) { 6611cb0ef41Sopenharmony_ci if (item.fail?.unexpected) { 6621cb0ef41Sopenharmony_ci failures.push(key); 6631cb0ef41Sopenharmony_ci } 6641cb0ef41Sopenharmony_ci if (item.fail?.expected) { 6651cb0ef41Sopenharmony_ci expectedFailures++; 6661cb0ef41Sopenharmony_ci } 6671cb0ef41Sopenharmony_ci if (item.skip) { 6681cb0ef41Sopenharmony_ci skipped++; 6691cb0ef41Sopenharmony_ci } 6701cb0ef41Sopenharmony_ci } 6711cb0ef41Sopenharmony_ci 6721cb0ef41Sopenharmony_ci const unexpectedPasses = []; 6731cb0ef41Sopenharmony_ci for (const specMap of queue) { 6741cb0ef41Sopenharmony_ci const key = specMap.filename; 6751cb0ef41Sopenharmony_ci 6761cb0ef41Sopenharmony_ci // File has no expected failures 6771cb0ef41Sopenharmony_ci if (!specMap.failedTests.length) { 6781cb0ef41Sopenharmony_ci continue; 6791cb0ef41Sopenharmony_ci } 6801cb0ef41Sopenharmony_ci 6811cb0ef41Sopenharmony_ci // File was (maybe even conditionally) skipped 6821cb0ef41Sopenharmony_ci if (this.results[key]?.skip) { 6831cb0ef41Sopenharmony_ci continue; 6841cb0ef41Sopenharmony_ci } 6851cb0ef41Sopenharmony_ci 6861cb0ef41Sopenharmony_ci // Full check: every expected to fail test is present 6871cb0ef41Sopenharmony_ci if (specMap.failedTests.some((expectedToFail) => { 6881cb0ef41Sopenharmony_ci if (specMap.flakyTests.includes(expectedToFail)) { 6891cb0ef41Sopenharmony_ci return false; 6901cb0ef41Sopenharmony_ci } 6911cb0ef41Sopenharmony_ci return this.results[key]?.fail?.expected?.includes(expectedToFail) !== true; 6921cb0ef41Sopenharmony_ci })) { 6931cb0ef41Sopenharmony_ci unexpectedPasses.push(key); 6941cb0ef41Sopenharmony_ci continue; 6951cb0ef41Sopenharmony_ci } 6961cb0ef41Sopenharmony_ci } 6971cb0ef41Sopenharmony_ci 6981cb0ef41Sopenharmony_ci this.report?.write(); 6991cb0ef41Sopenharmony_ci 7001cb0ef41Sopenharmony_ci const ran = queue.length; 7011cb0ef41Sopenharmony_ci const total = ran + skipped; 7021cb0ef41Sopenharmony_ci const passed = ran - expectedFailures - failures.length; 7031cb0ef41Sopenharmony_ci console.log(`Ran ${ran}/${total} tests, ${skipped} skipped,`, 7041cb0ef41Sopenharmony_ci `${passed} passed, ${expectedFailures} expected failures,`, 7051cb0ef41Sopenharmony_ci `${failures.length} unexpected failures,`, 7061cb0ef41Sopenharmony_ci `${unexpectedPasses.length} unexpected passes`); 7071cb0ef41Sopenharmony_ci if (failures.length > 0) { 7081cb0ef41Sopenharmony_ci const file = path.join('test', 'wpt', 'status', `${this.path}.json`); 7091cb0ef41Sopenharmony_ci throw new Error( 7101cb0ef41Sopenharmony_ci `Found ${failures.length} unexpected failures. ` + 7111cb0ef41Sopenharmony_ci `Consider updating ${file} for these files:\n${failures.join('\n')}`); 7121cb0ef41Sopenharmony_ci } 7131cb0ef41Sopenharmony_ci if (unexpectedPasses.length > 0) { 7141cb0ef41Sopenharmony_ci const file = path.join('test', 'wpt', 'status', `${this.path}.json`); 7151cb0ef41Sopenharmony_ci throw new Error( 7161cb0ef41Sopenharmony_ci `Found ${unexpectedPasses.length} unexpected passes. ` + 7171cb0ef41Sopenharmony_ci `Consider updating ${file} for these files:\n${unexpectedPasses.join('\n')}`); 7181cb0ef41Sopenharmony_ci } 7191cb0ef41Sopenharmony_ci }); 7201cb0ef41Sopenharmony_ci } 7211cb0ef41Sopenharmony_ci 7221cb0ef41Sopenharmony_ci getTestTitle(filename) { 7231cb0ef41Sopenharmony_ci const spec = this.specMap.get(filename); 7241cb0ef41Sopenharmony_ci return spec.meta?.title || filename.split('.')[0]; 7251cb0ef41Sopenharmony_ci } 7261cb0ef41Sopenharmony_ci 7271cb0ef41Sopenharmony_ci // Map WPT test status to strings 7281cb0ef41Sopenharmony_ci getTestStatus(status) { 7291cb0ef41Sopenharmony_ci switch (status) { 7301cb0ef41Sopenharmony_ci case 1: 7311cb0ef41Sopenharmony_ci return kFail; 7321cb0ef41Sopenharmony_ci case 2: 7331cb0ef41Sopenharmony_ci return kTimeout; 7341cb0ef41Sopenharmony_ci case 3: 7351cb0ef41Sopenharmony_ci return kIncomplete; 7361cb0ef41Sopenharmony_ci case NODE_UNCAUGHT: 7371cb0ef41Sopenharmony_ci return kUncaught; 7381cb0ef41Sopenharmony_ci default: 7391cb0ef41Sopenharmony_ci return kPass; 7401cb0ef41Sopenharmony_ci } 7411cb0ef41Sopenharmony_ci } 7421cb0ef41Sopenharmony_ci 7431cb0ef41Sopenharmony_ci /** 7441cb0ef41Sopenharmony_ci * Report the status of each specific test case (there could be multiple 7451cb0ef41Sopenharmony_ci * in one test file). 7461cb0ef41Sopenharmony_ci * @param {string} filename 7471cb0ef41Sopenharmony_ci * @param {Test} test The Test object returned by WPT harness 7481cb0ef41Sopenharmony_ci */ 7491cb0ef41Sopenharmony_ci resultCallback(filename, test, reportResult) { 7501cb0ef41Sopenharmony_ci const status = this.getTestStatus(test.status); 7511cb0ef41Sopenharmony_ci const title = this.getTestTitle(filename); 7521cb0ef41Sopenharmony_ci if (/^Untitled( \d+)?$/.test(test.name)) { 7531cb0ef41Sopenharmony_ci test.name = `${title}${test.name.slice(8)}`; 7541cb0ef41Sopenharmony_ci } 7551cb0ef41Sopenharmony_ci console.log(`---- ${title} ----`); 7561cb0ef41Sopenharmony_ci if (status !== kPass) { 7571cb0ef41Sopenharmony_ci this.fail(filename, test, status, reportResult); 7581cb0ef41Sopenharmony_ci } else { 7591cb0ef41Sopenharmony_ci this.succeed(filename, test, status, reportResult); 7601cb0ef41Sopenharmony_ci } 7611cb0ef41Sopenharmony_ci } 7621cb0ef41Sopenharmony_ci 7631cb0ef41Sopenharmony_ci /** 7641cb0ef41Sopenharmony_ci * Report the status of each WPT test (one per file) 7651cb0ef41Sopenharmony_ci * @param {string} filename 7661cb0ef41Sopenharmony_ci * @param {object} harnessStatus - The status object returned by WPT harness. 7671cb0ef41Sopenharmony_ci */ 7681cb0ef41Sopenharmony_ci completionCallback(filename, harnessStatus) { 7691cb0ef41Sopenharmony_ci const status = this.getTestStatus(harnessStatus.status); 7701cb0ef41Sopenharmony_ci 7711cb0ef41Sopenharmony_ci // Treat it like a test case failure 7721cb0ef41Sopenharmony_ci if (status === kTimeout) { 7731cb0ef41Sopenharmony_ci this.fail(filename, { name: 'WPT testharness timeout' }, kTimeout); 7741cb0ef41Sopenharmony_ci } 7751cb0ef41Sopenharmony_ci this.inProgress.delete(filename); 7761cb0ef41Sopenharmony_ci // Always force termination of the worker. Some tests allocate resources 7771cb0ef41Sopenharmony_ci // that would otherwise keep it alive. 7781cb0ef41Sopenharmony_ci this.workers.get(filename).terminate(); 7791cb0ef41Sopenharmony_ci } 7801cb0ef41Sopenharmony_ci 7811cb0ef41Sopenharmony_ci addTestResult(filename, item) { 7821cb0ef41Sopenharmony_ci let result = this.results[filename]; 7831cb0ef41Sopenharmony_ci if (!result) { 7841cb0ef41Sopenharmony_ci result = this.results[filename] = {}; 7851cb0ef41Sopenharmony_ci } 7861cb0ef41Sopenharmony_ci if (item.status === kSkip) { 7871cb0ef41Sopenharmony_ci // { filename: { skip: 'reason' } } 7881cb0ef41Sopenharmony_ci result[kSkip] = item.reason; 7891cb0ef41Sopenharmony_ci } else { 7901cb0ef41Sopenharmony_ci // { filename: { fail: { expected: [ ... ], 7911cb0ef41Sopenharmony_ci // unexpected: [ ... ] } }} 7921cb0ef41Sopenharmony_ci if (!result[item.status]) { 7931cb0ef41Sopenharmony_ci result[item.status] = {}; 7941cb0ef41Sopenharmony_ci } 7951cb0ef41Sopenharmony_ci const key = item.expected ? 'expected' : 'unexpected'; 7961cb0ef41Sopenharmony_ci if (!result[item.status][key]) { 7971cb0ef41Sopenharmony_ci result[item.status][key] = []; 7981cb0ef41Sopenharmony_ci } 7991cb0ef41Sopenharmony_ci const hasName = result[item.status][key].includes(item.name); 8001cb0ef41Sopenharmony_ci if (!hasName) { 8011cb0ef41Sopenharmony_ci result[item.status][key].push(item.name); 8021cb0ef41Sopenharmony_ci } 8031cb0ef41Sopenharmony_ci } 8041cb0ef41Sopenharmony_ci } 8051cb0ef41Sopenharmony_ci 8061cb0ef41Sopenharmony_ci succeed(filename, test, status, reportResult) { 8071cb0ef41Sopenharmony_ci console.log(`[${status.toUpperCase()}] ${test.name}`); 8081cb0ef41Sopenharmony_ci reportResult?.addSubtest(test.name, 'PASS'); 8091cb0ef41Sopenharmony_ci } 8101cb0ef41Sopenharmony_ci 8111cb0ef41Sopenharmony_ci fail(filename, test, status, reportResult) { 8121cb0ef41Sopenharmony_ci const spec = this.specMap.get(filename); 8131cb0ef41Sopenharmony_ci const expected = spec.failedTests.includes(test.name); 8141cb0ef41Sopenharmony_ci if (expected) { 8151cb0ef41Sopenharmony_ci console.log(`[EXPECTED_FAILURE][${status.toUpperCase()}] ${test.name}`); 8161cb0ef41Sopenharmony_ci console.log(test.message || status); 8171cb0ef41Sopenharmony_ci } else { 8181cb0ef41Sopenharmony_ci console.log(`[UNEXPECTED_FAILURE][${status.toUpperCase()}] ${test.name}`); 8191cb0ef41Sopenharmony_ci } 8201cb0ef41Sopenharmony_ci if (status === kFail || status === kUncaught) { 8211cb0ef41Sopenharmony_ci console.log(test.message); 8221cb0ef41Sopenharmony_ci console.log(test.stack); 8231cb0ef41Sopenharmony_ci } 8241cb0ef41Sopenharmony_ci const command = `${process.execPath} ${process.execArgv}` + 8251cb0ef41Sopenharmony_ci ` ${require.main.filename} ${filename}`; 8261cb0ef41Sopenharmony_ci console.log(`Command: ${command}\n`); 8271cb0ef41Sopenharmony_ci 8281cb0ef41Sopenharmony_ci reportResult?.addSubtest(test.name, 'FAIL', test.message); 8291cb0ef41Sopenharmony_ci 8301cb0ef41Sopenharmony_ci this.addTestResult(filename, { 8311cb0ef41Sopenharmony_ci name: test.name, 8321cb0ef41Sopenharmony_ci expected, 8331cb0ef41Sopenharmony_ci status: kFail, 8341cb0ef41Sopenharmony_ci reason: test.message || status, 8351cb0ef41Sopenharmony_ci }); 8361cb0ef41Sopenharmony_ci } 8371cb0ef41Sopenharmony_ci 8381cb0ef41Sopenharmony_ci skip(filename, reasons) { 8391cb0ef41Sopenharmony_ci const title = this.getTestTitle(filename); 8401cb0ef41Sopenharmony_ci console.log(`---- ${title} ----`); 8411cb0ef41Sopenharmony_ci const joinedReasons = reasons.join('; '); 8421cb0ef41Sopenharmony_ci console.log(`[SKIPPED] ${joinedReasons}`); 8431cb0ef41Sopenharmony_ci this.addTestResult(filename, { 8441cb0ef41Sopenharmony_ci status: kSkip, 8451cb0ef41Sopenharmony_ci reason: joinedReasons, 8461cb0ef41Sopenharmony_ci }); 8471cb0ef41Sopenharmony_ci } 8481cb0ef41Sopenharmony_ci 8491cb0ef41Sopenharmony_ci getMeta(code) { 8501cb0ef41Sopenharmony_ci const matches = code.match(/\/\/ META: .+/g); 8511cb0ef41Sopenharmony_ci if (!matches) { 8521cb0ef41Sopenharmony_ci return {}; 8531cb0ef41Sopenharmony_ci } 8541cb0ef41Sopenharmony_ci const result = {}; 8551cb0ef41Sopenharmony_ci for (const match of matches) { 8561cb0ef41Sopenharmony_ci const parts = match.match(/\/\/ META: ([^=]+?)=(.+)/); 8571cb0ef41Sopenharmony_ci const key = parts[1]; 8581cb0ef41Sopenharmony_ci const value = parts[2]; 8591cb0ef41Sopenharmony_ci if (key === 'script' || key === 'variant') { 8601cb0ef41Sopenharmony_ci if (result[key]) { 8611cb0ef41Sopenharmony_ci result[key].push(value); 8621cb0ef41Sopenharmony_ci } else { 8631cb0ef41Sopenharmony_ci result[key] = [value]; 8641cb0ef41Sopenharmony_ci } 8651cb0ef41Sopenharmony_ci } else { 8661cb0ef41Sopenharmony_ci result[key] = value; 8671cb0ef41Sopenharmony_ci } 8681cb0ef41Sopenharmony_ci } 8691cb0ef41Sopenharmony_ci return result; 8701cb0ef41Sopenharmony_ci } 8711cb0ef41Sopenharmony_ci 8721cb0ef41Sopenharmony_ci buildQueue() { 8731cb0ef41Sopenharmony_ci const queue = []; 8741cb0ef41Sopenharmony_ci for (const spec of this.specMap.values()) { 8751cb0ef41Sopenharmony_ci const filename = spec.filename; 8761cb0ef41Sopenharmony_ci if (spec.skipReasons.length > 0) { 8771cb0ef41Sopenharmony_ci this.skip(filename, spec.skipReasons); 8781cb0ef41Sopenharmony_ci continue; 8791cb0ef41Sopenharmony_ci } 8801cb0ef41Sopenharmony_ci 8811cb0ef41Sopenharmony_ci const lackingIntl = intlRequirements.isLacking(spec.requires); 8821cb0ef41Sopenharmony_ci if (lackingIntl) { 8831cb0ef41Sopenharmony_ci this.skip(filename, [ `requires ${lackingIntl}` ]); 8841cb0ef41Sopenharmony_ci continue; 8851cb0ef41Sopenharmony_ci } 8861cb0ef41Sopenharmony_ci 8871cb0ef41Sopenharmony_ci queue.push(spec); 8881cb0ef41Sopenharmony_ci } 8891cb0ef41Sopenharmony_ci return queue; 8901cb0ef41Sopenharmony_ci } 8911cb0ef41Sopenharmony_ci} 8921cb0ef41Sopenharmony_ci 8931cb0ef41Sopenharmony_cimodule.exports = { 8941cb0ef41Sopenharmony_ci harness: harnessMock, 8951cb0ef41Sopenharmony_ci ResourceLoader, 8961cb0ef41Sopenharmony_ci WPTRunner, 8971cb0ef41Sopenharmony_ci}; 898