/** * @license * Copyright 2023 Google Inc. * SPDX-License-Identifier: Apache-2.0 */ // TODO: this could be an eslint rule probably. import fs from 'fs'; import path from 'path'; import url from 'url'; import prettier from 'prettier'; const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); const source = 'test/TestExpectations.json'; let testExpectations = JSON.parse(fs.readFileSync(source, 'utf-8')); const committedExpectations = structuredClone(testExpectations); function testIdMatchesExpectationPattern(title, pattern) { const patternRegExString = pattern // Replace `*` with non special character .replace(/\*/g, '--STAR--') // Escape special characters https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping .replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // Replace placeholder with greedy match .replace(/--STAR--/g, '(.*)?'); // Match beginning and end explicitly const patternRegEx = new RegExp(`^${patternRegExString}$`); return patternRegEx.test(title); } const prettierConfig = await import( path.join(__dirname, '..', '.prettierrc.cjs') ); function getSpecificity(item) { return ( item.parameters.length + (item.testIdPattern.includes('*') ? item.testIdPattern === '*' ? 0 : 1 : 2) ); } testExpectations.sort((a, b) => { const result = getSpecificity(a) - getSpecificity(b); if (result === 0) { return a.testIdPattern.localeCompare(b.testIdPattern); } return result; }); testExpectations.forEach(item => { item.parameters.sort(); item.expectations.sort(); item.platforms.sort(); // Delete comments for PASS expectations. They are likely outdated. if (item.expectations.length === 1 && item.expectations[0] === 'PASS') { delete item.comment; } }); function isSubset(superset, subset) { let isSubset = true; for (const p of subset) { if (!superset.has(p)) { isSubset = false; } } return isSubset; } const toBeRemoved = new Set(); for (let i = testExpectations.length - 1; i >= 0; i--) { const expectation = testExpectations[i]; const params = new Set(expectation.parameters); const expectations = new Set(expectation.expectations); const platforms = new Set(expectation.platforms); let foundMatch = false; for (let j = i - 1; j >= 0; j--) { const candidate = testExpectations[j]; const candidateParams = new Set(candidate.parameters); const candidateExpectations = new Set(candidate.expectations); const candidatePlatforms = new Set(candidate.platforms); if ( testIdMatchesExpectationPattern( expectation.testIdPattern, candidate.testIdPattern ) && isSubset(candidatePlatforms, platforms) && (isSubset(params, candidateParams) || isSubset(candidateParams, params)) ) { foundMatch = true; if (isSubset(candidateExpectations, expectations)) { console.log('removing', expectation, 'already covered by', candidate); toBeRemoved.add(expectation); } break; } } if (!foundMatch && isSubset(new Set(['PASS']), expectations)) { console.log( 'removing', expectation, 'because the default expectation is to pass' ); toBeRemoved.add(expectation); } } testExpectations = testExpectations.filter(item => { return !toBeRemoved.has(item); }); if (process.argv.includes('--lint')) { const missingComments = []; testExpectations.forEach(item => { if (item.expectations.length === 1 && item.expectations[0] === 'PASS') { return; } if (!item.comment) { missingComments.push(item); } }); if ( JSON.stringify(committedExpectations) !== JSON.stringify(testExpectations) ) { console.error( `${source} is not formatted properly. Run 'npm run format:expectations'.` ); process.exit(1); } if (missingComments.length > 0) { console.error( `${source}: missing comments for the following expectations:`, missingComments ); process.exit(1); } } else { fs.writeFileSync( source, await prettier.format(JSON.stringify(testExpectations), { ...prettierConfig, parser: 'json', }) ); }