tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

sort-test-expectations.mjs (4447B)


      1 /**
      2 * @license
      3 * Copyright 2023 Google Inc.
      4 * SPDX-License-Identifier: Apache-2.0
      5 */
      6 
      7 // TODO: this could be an eslint rule probably.
      8 import fs from 'fs';
      9 import path from 'path';
     10 import url from 'url';
     11 
     12 import prettier from 'prettier';
     13 
     14 const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
     15 const source = 'test/TestExpectations.json';
     16 let testExpectations = JSON.parse(fs.readFileSync(source, 'utf-8'));
     17 const committedExpectations = structuredClone(testExpectations);
     18 
     19 function testIdMatchesExpectationPattern(title, pattern) {
     20  const patternRegExString = pattern
     21    // Replace `*` with non special character
     22    .replace(/\*/g, '--STAR--')
     23    // Escape special characters https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
     24    .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
     25    // Replace placeholder with greedy match
     26    .replace(/--STAR--/g, '(.*)?');
     27  // Match beginning and end explicitly
     28  const patternRegEx = new RegExp(`^${patternRegExString}$`);
     29  return patternRegEx.test(title);
     30 }
     31 
     32 const prettierConfig = await import(
     33  path.join(__dirname, '..', '.prettierrc.cjs')
     34 );
     35 
     36 function getSpecificity(item) {
     37  return (
     38    item.parameters.length +
     39    (item.testIdPattern.includes('*')
     40      ? item.testIdPattern === '*'
     41        ? 0
     42        : 1
     43      : 2)
     44  );
     45 }
     46 
     47 testExpectations.sort((a, b) => {
     48  const result = getSpecificity(a) - getSpecificity(b);
     49  if (result === 0) {
     50    return a.testIdPattern.localeCompare(b.testIdPattern);
     51  }
     52  return result;
     53 });
     54 
     55 testExpectations.forEach(item => {
     56  item.parameters.sort();
     57  item.expectations.sort();
     58  item.platforms.sort();
     59  // Delete comments for PASS expectations. They are likely outdated.
     60  if (item.expectations.length === 1 && item.expectations[0] === 'PASS') {
     61    delete item.comment;
     62  }
     63 });
     64 
     65 function isSubset(superset, subset) {
     66  let isSubset = true;
     67 
     68  for (const p of subset) {
     69    if (!superset.has(p)) {
     70      isSubset = false;
     71    }
     72  }
     73 
     74  return isSubset;
     75 }
     76 
     77 const toBeRemoved = new Set();
     78 for (let i = testExpectations.length - 1; i >= 0; i--) {
     79  const expectation = testExpectations[i];
     80  const params = new Set(expectation.parameters);
     81  const expectations = new Set(expectation.expectations);
     82  const platforms = new Set(expectation.platforms);
     83 
     84  if (params.has('cdp') && params.has('firefox')) {
     85    console.log(
     86      'removing',
     87      expectation,
     88      'because firefox-cdp is no longer tested',
     89    );
     90    toBeRemoved.add(expectation);
     91    continue;
     92  }
     93 
     94  let foundMatch = false;
     95  for (let j = i - 1; j >= 0; j--) {
     96    const candidate = testExpectations[j];
     97    const candidateParams = new Set(candidate.parameters);
     98    const candidateExpectations = new Set(candidate.expectations);
     99    const candidatePlatforms = new Set(candidate.platforms);
    100 
    101    if (
    102      testIdMatchesExpectationPattern(
    103        expectation.testIdPattern,
    104        candidate.testIdPattern,
    105      ) &&
    106      isSubset(candidatePlatforms, platforms) &&
    107      (isSubset(params, candidateParams) || isSubset(candidateParams, params))
    108    ) {
    109      foundMatch = true;
    110      if (isSubset(candidateExpectations, expectations)) {
    111        console.log('removing', expectation, 'already covered by', candidate);
    112        toBeRemoved.add(expectation);
    113      }
    114      break;
    115    }
    116  }
    117 
    118  if (!foundMatch && isSubset(new Set(['PASS']), expectations)) {
    119    console.log(
    120      'removing',
    121      expectation,
    122      'because the default expectation is to pass',
    123    );
    124    toBeRemoved.add(expectation);
    125  }
    126 }
    127 
    128 testExpectations = testExpectations.filter(item => {
    129  return !toBeRemoved.has(item);
    130 });
    131 
    132 if (process.argv.includes('--lint')) {
    133  const missingComments = [];
    134  testExpectations.forEach(item => {
    135    if (item.expectations.length === 1 && item.expectations[0] === 'PASS') {
    136      return;
    137    }
    138    if (!item.comment) {
    139      missingComments.push(item);
    140    }
    141  });
    142 
    143  if (
    144    JSON.stringify(committedExpectations) !== JSON.stringify(testExpectations)
    145  ) {
    146    console.error(
    147      `${source} is not formatted properly. Run 'npm run format:expectations'.`,
    148    );
    149    process.exit(1);
    150  }
    151 
    152  if (missingComments.length > 0) {
    153    console.error(
    154      `${source}: missing comments for the following expectations:`,
    155      missingComments,
    156    );
    157    process.exit(1);
    158  }
    159 } else {
    160  fs.writeFileSync(
    161    source,
    162    await prettier.format(JSON.stringify(testExpectations), {
    163      ...prettierConfig,
    164      parser: 'json',
    165    }),
    166  );
    167 }