tor-browser

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

try-runner.js (7367B)


      1 /* eslint-disable no-console */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
      5 
      6 /*
      7 * A small test runner/reporter for node-based tests,
      8 * which are run via taskcluster node(debugger).
      9 *
     10 * Forked from
     11 * https://searchfox.org/mozilla-central/rev/c3453c7a0427eb27d467e1582f821f402aed9850/devtools/client/debugger/bin/try-runner.js
     12 */
     13 
     14 const { execFileSync } = require("child_process");
     15 const { readFileSync } = require("fs");
     16 const path = require("path");
     17 const { pathToFileURL } = require("url");
     18 const chalk = require("chalk");
     19 
     20 function logErrors(tool, errors) {
     21  for (const error of errors) {
     22    console.log(`TEST-UNEXPECTED-FAIL | ${tool} | ${error}`);
     23  }
     24  return errors;
     25 }
     26 
     27 function execOut(...args) {
     28  let exitCode = 0;
     29  let out;
     30  let err;
     31 
     32  try {
     33    out = execFileSync(...args, {
     34      silent: false,
     35    });
     36  } catch (e) {
     37    // For debugging on (eg) try server...
     38    //
     39    // if (e) {
     40    //   logErrors("execOut", ["execFileSync returned exception: ", e]);
     41    // }
     42 
     43    out = e && e.stdout;
     44    err = e && e.stderr;
     45    exitCode = e && e.status;
     46  }
     47  return { exitCode, out: out && out.toString(), err: err && err.toString() };
     48 }
     49 
     50 function logStart(name) {
     51  console.log(`TEST-START | ${name}`);
     52 }
     53 
     54 function logSkip(name) {
     55  console.log(`TEST-SKIP | ${name}`);
     56 }
     57 
     58 const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
     59 
     60 const tests = {
     61  bundles() {
     62    logStart("bundles");
     63 
     64    const items = {
     65      "Activity Stream bundle": {
     66        path: path.join("data", "content", "activity-stream.bundle.js"),
     67      },
     68      "activity-stream.html": {
     69        path: path.join("prerendered", "activity-stream.html"),
     70      },
     71      "activity-stream-debug.html": {
     72        path: path.join("prerendered", "activity-stream-debug.html"),
     73      },
     74      "activity-stream-noscripts.html": {
     75        path: path.join("prerendered", "activity-stream-noscripts.html"),
     76      },
     77      "activity-stream.css": {
     78        path: path.join("css", "activity-stream.css"),
     79      },
     80    };
     81    const errors = [];
     82 
     83    for (const name of Object.keys(items)) {
     84      const item = items[name];
     85      item.before = readFileSync(item.path, item.encoding || "utf8");
     86    }
     87 
     88    let newtabBundleExitCode = execOut(npmCommand, ["run", "bundle"]).exitCode;
     89 
     90    for (const name of Object.keys(items)) {
     91      const item = items[name];
     92      const after = readFileSync(item.path, item.encoding || "utf8");
     93 
     94      if (item.before !== after) {
     95        errors.push(`${name} out of date`);
     96      }
     97 
     98      if (item.extraCheck) {
     99        const extraError = item.extraCheck(after);
    100        if (extraError) {
    101          errors.push(extraError);
    102        }
    103      }
    104    }
    105 
    106    if (newtabBundleExitCode !== 0) {
    107      errors.push("newtab npm:bundle did not run successfully");
    108    }
    109 
    110    logErrors("bundles", errors);
    111    return errors.length === 0;
    112  },
    113 
    114  karma() {
    115    logStart(`karma ${process.cwd()}`);
    116 
    117    const errors = [];
    118    const { exitCode, out } = execOut(npmCommand, [
    119      "run",
    120      "testmc:unit",
    121      // , "--", "--log-level", "--verbose",
    122      // to debug the karma integration, uncomment the above line
    123    ]);
    124 
    125    // karma spits everything to stdout, not stderr, so if nothing came back on
    126    // stdout, give up now.
    127    if (!out) {
    128      return false;
    129    }
    130 
    131    // Detect mocha failures
    132    let jsonContent;
    133    try {
    134      // Note that this will be overwritten at each run, but that shouldn't
    135      // matter.
    136      jsonContent = readFileSync(path.join("logs", "karma-run-results.json"));
    137    } catch (ex) {
    138      console.error("exception reading karma-run-results.json: ", ex);
    139      return false;
    140    }
    141    const results = JSON.parse(jsonContent);
    142    // eslint-disable-next-line guard-for-in
    143    for (let testArray in results.result) {
    144      let failedTests = Array.from(results.result[testArray]).filter(
    145        test => !test.success && !test.skipped
    146      );
    147 
    148      errors.push(
    149        ...failedTests.map(
    150          test => `${test.suite.join(":")} ${test.description}: ${test.log[0]}`
    151        )
    152      );
    153    }
    154 
    155    // Detect istanbul failures (coverage thresholds set in karma config)
    156    const coverage = out.match(/ERROR.+coverage-istanbul.+/g);
    157    if (coverage) {
    158      errors.push(...coverage.map(line => line.match(/Coverage.+/)[0]));
    159    }
    160 
    161    logErrors(`karma ${process.cwd()}`, errors);
    162 
    163    console.log("-----karma stdout below this line---");
    164    console.log(out);
    165    console.log("-----karma stdout above this line---");
    166 
    167    // Pass if there's no detected errors and nothing unexpected.
    168    return errors.length === 0 && !exitCode;
    169  },
    170 
    171  zipCodeCoverage() {
    172    logStart("zipCodeCoverage");
    173 
    174    const { exitCode, out } = execOut("zip", [
    175      "-j",
    176      "logs/coverage/code-coverage-grcov",
    177      "logs/coverage/lcov.info",
    178    ]);
    179 
    180    console.log("zipCodeCoverage log output: ", out);
    181 
    182    if (!exitCode) {
    183      return true;
    184    }
    185 
    186    return false;
    187  },
    188 };
    189 
    190 async function main() {
    191  const { default: meow } = await import("meow");
    192  const fileUrl = pathToFileURL(__filename);
    193  const cli = meow(
    194    `
    195  Usage
    196    $ node bin/try-runner.js <tests> [options]
    197 
    198  Options
    199    -t NAME, --test NAME   Run only the specified test. If not specified,
    200                           all tests will be run. Argument can be passed 
    201                           multiple times to run multiple tests.
    202    --help                 Show this help message.
    203 
    204  Examples
    205    $ node bin/try-runner.js bundles karma
    206    $ node bin/try-runner.js -t karma -t zip
    207 `,
    208    {
    209      description: false,
    210      // `pkg` is a tiny optimization. It prevents meow from looking for a package
    211      // that doesn't technically exist. meow searches for a package and changes
    212      // the process name to the package name. It resolves to the newtab
    213      // package.json, which would give a confusing name and be wasteful.
    214      pkg: {
    215        name: "try-runner",
    216        version: "1.0.0",
    217      },
    218      // `importMeta` is required by meow 10+. It was added to support ESM, but
    219      // meow now requires it, and no longer supports CJS style imports. But it
    220      // only uses import.meta.url, which can be polyfilled like this:
    221      importMeta: { url: fileUrl },
    222      flags: {
    223        test: {
    224          type: "string",
    225          isMultiple: true,
    226          shortFlag: "t",
    227        },
    228      },
    229    }
    230  );
    231  const aliases = {
    232    bundle: "bundles",
    233    build: "bundles",
    234    coverage: "karma",
    235    cov: "karma",
    236    zip: "zipCodeCoverage",
    237  };
    238 
    239  const inputs = [...cli.input, ...cli.flags.test].map(input =>
    240    (aliases[input] || input).toLowerCase()
    241  );
    242 
    243  function shouldRunTest(name) {
    244    if (inputs.length) {
    245      return inputs.includes(name.toLowerCase());
    246    }
    247    return true;
    248  }
    249 
    250  const results = [];
    251  for (const name of Object.keys(tests)) {
    252    if (shouldRunTest(name)) {
    253      results.push([name, tests[name]()]);
    254    } else {
    255      logSkip(name);
    256    }
    257  }
    258 
    259  for (const [name, result] of results) {
    260    // colorize output based on result
    261    console.log(result ? chalk.green(`✓ ${name}`) : chalk.red(`✗ ${name}`));
    262  }
    263 
    264  const success = results.every(([, result]) => result);
    265  process.exitCode = success ? 0 : 1;
    266  console.log("CODE", process.exitCode);
    267 }
    268 
    269 main();