tor-browser

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

cmdline.ts (9744B)


      1 /* eslint-disable no-console, n/no-restricted-import */
      2 
      3 import { dataCache } from '../framework/data_cache.js';
      4 import { getResourcePath, setBaseResourcePath } from '../framework/resources.js';
      5 import { globalTestConfig } from '../framework/test_config.js';
      6 import { DefaultTestFileLoader } from '../internal/file_loader.js';
      7 import { prettyPrintLog } from '../internal/logging/log_message.js';
      8 import { Logger } from '../internal/logging/logger.js';
      9 import { LiveTestCaseResult } from '../internal/logging/result.js';
     10 import { parseQuery } from '../internal/query/parseQuery.js';
     11 import { parseExpectationsForTestQuery } from '../internal/query/query.js';
     12 import { Colors } from '../util/colors.js';
     13 import { setDefaultRequestAdapterOptions, setGPUProvider } from '../util/navigator_gpu.js';
     14 import { assert, unreachable } from '../util/util.js';
     15 
     16 import sys from './helper/sys.js';
     17 
     18 function usage(rc: number): never {
     19  console.log(`Usage:
     20  tools/run_${sys.type} [OPTIONS...] QUERIES...
     21  tools/run_${sys.type} 'unittests:*' 'webgpu:buffers,*'
     22 Options:
     23  --colors                  Enable ANSI colors in output.
     24  --compat                  Runs tests in compatibility mode.
     25  --coverage                Emit coverage data.
     26  --verbose                 Print result/log of every test as it runs.
     27  --list                    Print all testcase names that match the given query and exit.
     28  --list-unimplemented      Print all unimplemented tests
     29  --debug                   Include debug messages in logging.
     30  --print-json              Print the complete result JSON in the output.
     31  --expectations            Path to expectations file.
     32  --gpu-provider            Path to node module that provides the GPU implementation.
     33  --gpu-provider-flag       Flag to set on the gpu-provider as <flag>=<value>
     34  --unroll-const-eval-loops Unrolls loops in constant-evaluation shader execution tests
     35  --enforce-default-limits  Enforce the default limits (note: powerPreference tests may fail)
     36  --force-fallback-adapter  Force a fallback adapter
     37  --log-to-websocket        Log to a websocket
     38  --quiet                   Suppress summary information in output
     39 `);
     40  return sys.exit(rc);
     41 }
     42 
     43 if (!sys.existsSync('src/common/runtime/cmdline.ts')) {
     44  console.log('Must be run from repository root');
     45  usage(1);
     46 }
     47 setBaseResourcePath('out-node/resources');
     48 
     49 // The interface that exposes creation of the GPU, and optional interface to code coverage.
     50 interface GPUProviderModule {
     51  // @returns a GPU with the given flags
     52  create(flags: string[]): GPU;
     53  // An optional interface to a CodeCoverageProvider
     54  coverage?: CodeCoverageProvider;
     55 }
     56 
     57 interface CodeCoverageProvider {
     58  // Starts collecting code coverage
     59  begin(): void;
     60  // Ends collecting of code coverage, returning the coverage data.
     61  // This data is opaque (implementation defined).
     62  end(): string;
     63 }
     64 
     65 type listModes = 'none' | 'cases' | 'unimplemented';
     66 
     67 Colors.enabled = false;
     68 
     69 let verbose = false;
     70 let emitCoverage = false;
     71 let listMode: listModes = 'none';
     72 let printJSON = false;
     73 let quiet = false;
     74 let loadWebGPUExpectations: Promise<unknown> | undefined = undefined;
     75 let gpuProviderModule: GPUProviderModule | undefined = undefined;
     76 
     77 const queries: string[] = [];
     78 const gpuProviderFlags: string[] = [];
     79 for (let i = 0; i < sys.args.length; ++i) {
     80  const a = sys.args[i];
     81  if (a.startsWith('-')) {
     82    if (a === '--colors') {
     83      Colors.enabled = true;
     84    } else if (a === '--coverage') {
     85      emitCoverage = true;
     86    } else if (a === '--verbose') {
     87      verbose = true;
     88    } else if (a === '--list') {
     89      listMode = 'cases';
     90    } else if (a === '--list-unimplemented') {
     91      listMode = 'unimplemented';
     92    } else if (a === '--debug') {
     93      globalTestConfig.enableDebugLogs = true;
     94    } else if (a === '--print-json') {
     95      printJSON = true;
     96    } else if (a === '--expectations') {
     97      const expectationsFile = new URL(sys.args[++i], `file://${sys.cwd()}`).pathname;
     98      loadWebGPUExpectations = import(expectationsFile).then(m => m.expectations);
     99    } else if (a === '--gpu-provider') {
    100      const modulePath = sys.args[++i];
    101      gpuProviderModule = require(modulePath);
    102    } else if (a === '--gpu-provider-flag') {
    103      gpuProviderFlags.push(sys.args[++i]);
    104    } else if (a === '--quiet') {
    105      quiet = true;
    106    } else if (a === '--unroll-const-eval-loops') {
    107      globalTestConfig.unrollConstEvalLoops = true;
    108    } else if (a === '--compat') {
    109      globalTestConfig.compatibility = true;
    110    } else if (a === '--force-fallback-adapter') {
    111      globalTestConfig.forceFallbackAdapter = true;
    112    } else if (a === '--enforce-default-limits') {
    113      globalTestConfig.enforceDefaultLimits = true;
    114    } else if (a === '--block-all-features') {
    115      globalTestConfig.blockAllFeatures = true;
    116    } else if (a === '--subcases-between-attempting-gc') {
    117      globalTestConfig.subcasesBetweenAttemptingGC = Number(sys.args[++i]);
    118    } else if (a === '--cases-between-replacing-device') {
    119      globalTestConfig.casesBetweenReplacingDevice = Number(sys.args[++i]);
    120    } else if (a === '--log-to-websocket') {
    121      globalTestConfig.logToWebSocket = true;
    122    } else {
    123      console.log('unrecognized flag: ', a);
    124      usage(1);
    125    }
    126  } else {
    127    queries.push(a);
    128  }
    129 }
    130 
    131 let codeCoverage: CodeCoverageProvider | undefined = undefined;
    132 
    133 if (globalTestConfig.compatibility || globalTestConfig.forceFallbackAdapter) {
    134  setDefaultRequestAdapterOptions({
    135    featureLevel: globalTestConfig.compatibility ? 'compatibility' : 'core',
    136    forceFallbackAdapter: globalTestConfig.forceFallbackAdapter,
    137  });
    138 }
    139 
    140 if (gpuProviderModule) {
    141  setGPUProvider(() => gpuProviderModule!.create(gpuProviderFlags));
    142  if (emitCoverage) {
    143    codeCoverage = gpuProviderModule.coverage;
    144    if (codeCoverage === undefined) {
    145      console.error(
    146        `--coverage specified, but the GPUProviderModule does not support code coverage.
    147 Did you remember to build with code coverage instrumentation enabled?`
    148      );
    149      sys.exit(1);
    150    }
    151  }
    152 }
    153 
    154 dataCache.setStore({
    155  load: (path: string) => {
    156    return new Promise<Uint8Array>((resolve, reject) => {
    157      sys.readFile(
    158        getResourcePath(`cache/${path}`),
    159        (err: { message: string }, data: Uint8Array) => {
    160          if (err !== null) {
    161            reject(err.message);
    162          } else {
    163            resolve(data);
    164          }
    165        }
    166      );
    167    });
    168  },
    169 });
    170 
    171 if (verbose) {
    172  dataCache.setDebugLogger(console.log);
    173 }
    174 
    175 if (queries.length === 0) {
    176  console.log('no queries specified');
    177  usage(0);
    178 }
    179 
    180 (async () => {
    181  const loader = new DefaultTestFileLoader();
    182  assert(queries.length === 1, 'currently, there must be exactly one query on the cmd line');
    183  const filterQuery = parseQuery(queries[0]);
    184  const testcases = await loader.loadCases(filterQuery);
    185  const expectations = parseExpectationsForTestQuery(
    186    await (loadWebGPUExpectations ?? []),
    187    filterQuery
    188  );
    189 
    190  const log = new Logger();
    191 
    192  const failed: Array<[string, LiveTestCaseResult]> = [];
    193  const warned: Array<[string, LiveTestCaseResult]> = [];
    194  const skipped: Array<[string, LiveTestCaseResult]> = [];
    195 
    196  let total = 0;
    197 
    198  if (codeCoverage !== undefined) {
    199    codeCoverage.begin();
    200  }
    201 
    202  for (const testcase of testcases) {
    203    const name = testcase.query.toString();
    204    switch (listMode) {
    205      case 'cases':
    206        console.log(name);
    207        continue;
    208      case 'unimplemented':
    209        if (testcase.isUnimplemented) {
    210          console.log(name);
    211        }
    212        continue;
    213      default:
    214        break;
    215    }
    216 
    217    const [rec, res] = log.record(name);
    218    await testcase.run(rec, expectations);
    219 
    220    if (verbose) {
    221      printResults([[name, res]]);
    222    }
    223 
    224    total++;
    225    switch (res.status) {
    226      case 'pass':
    227        break;
    228      case 'fail':
    229        failed.push([name, res]);
    230        break;
    231      case 'warn':
    232        warned.push([name, res]);
    233        break;
    234      case 'skip':
    235        skipped.push([name, res]);
    236        break;
    237      default:
    238        unreachable('unrecognized status');
    239    }
    240  }
    241 
    242  if (codeCoverage !== undefined) {
    243    const coverage = codeCoverage.end();
    244    console.log(`Code-coverage: [[${coverage}]]`);
    245  }
    246 
    247  if (listMode !== 'none') {
    248    return;
    249  }
    250 
    251  assert(total > 0, 'found no tests!');
    252 
    253  // MAINTENANCE_TODO: write results out somewhere (a file?)
    254  if (printJSON) {
    255    console.log(log.asJSON(2));
    256  }
    257 
    258  if (!quiet) {
    259    if (skipped.length) {
    260      console.log('');
    261      console.log('** Skipped **');
    262      printResults(skipped);
    263    }
    264    if (warned.length) {
    265      console.log('');
    266      console.log('** Warnings **');
    267      printResults(warned);
    268    }
    269    if (failed.length) {
    270      console.log('');
    271      console.log('** Failures **');
    272      printResults(failed);
    273    }
    274 
    275    const passed = total - warned.length - failed.length - skipped.length;
    276    const pct = (x: number) => ((100 * x) / total).toFixed(2);
    277    const rpt = (x: number) => {
    278      const xs = x.toString().padStart(1 + Math.log10(total), ' ');
    279      return `${xs} / ${total} = ${pct(x).padStart(6, ' ')}%`;
    280    };
    281    console.log('');
    282    console.log(`** Summary **
    283 Passed  w/o warnings = ${rpt(passed)}
    284 Passed with warnings = ${rpt(warned.length)}
    285 Skipped              = ${rpt(skipped.length)}
    286 Failed               = ${rpt(failed.length)}`);
    287  }
    288 
    289  if (failed.length || warned.length) {
    290    sys.exit(1);
    291  }
    292  sys.exit(0);
    293 })().catch(ex => {
    294  console.log(ex.stack ?? ex.toString());
    295  sys.exit(1);
    296 });
    297 
    298 function printResults(results: Array<[string, LiveTestCaseResult]>): void {
    299  for (const [name, r] of results) {
    300    console.log(`[${r.status}] ${name} (${r.timems}ms). Log:`);
    301    if (r.logs) {
    302      for (const l of r.logs) {
    303        console.log(prettyPrintLog(l));
    304      }
    305    }
    306  }
    307 }