Hackathon fixes

Merge upstream pull requests:
- Decompress local files when running tests
- Support url tests and tags param splitting

Change-Id: Ibdf392d7c33b951e2f622c2cbfb875ac1777488f
Reviewed-on: https://chromium-review.googlesource.com/c/external/github.com/WebKit/JetStream/+/7035653
Reviewed-by: Marja Hölttä <[email protected]>
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 5d3f703..e3014f8 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -39,9 +39,6 @@
             - name: Install Node Packages
               run: npm ci
 
-            - name: Decompress compressed files
-              run: npm run decompress
-
             - name: Cache jsvu Binaries 
               uses: actions/cache@v4
               with:
diff --git a/package.json b/package.json
index 689f6ce..7b34819 100644
--- a/package.json
+++ b/package.json
@@ -17,19 +17,20 @@
     "scripts": {
         "server": "node tests/server.mjs",
         "compress": "node utils/compress.mjs",
-        "decompress": "node utils/compress.mjs -d -k",
+        "decompress": "node utils/compress.mjs --decompress --keep ",
         "lint:check": "eslint **/*.{js,mjs,jsx,ts,tsx}",
         "pretty:check": "prettier --check ./",
         "pretty:format": "prettier --write ./",
         "format:check": "npm run pretty:check && npm run lint:check",
-        "test:chrome": "node tests/run-browser.mjs --browser chrome",
-        "test:firefox": "node tests/run-browser.mjs --browser firefox",
-        "test:safari": "node tests/run-browser.mjs --browser safari",
-        "test:edge": "node tests/run-browser.mjs --browser edge",
-        "test:shell": "npm run test:v8 && npm run test:jsc && npm run test:spidermonkey",
-        "test:v8": "node tests/run-shell.mjs --shell v8",
-        "test:jsc": "node tests/run-shell.mjs --shell jsc",
-        "test:spidermonkey": "node tests/run-shell.mjs --shell spidermonkey"
+        "test:prepare": "npm run decompress -- --quiet",
+        "test:chrome": "npm run test:prepare && node tests/run-browser.mjs --browser chrome",
+        "test:firefox": "npm run test:prepare && node tests/run-browser.mjs --browser firefox",
+        "test:safari": "npm run test:prepare && node tests/run-browser.mjs --browser safari",
+        "test:edge": "npm run test:prepare && node tests/run-browser.mjs --browser edge",
+        "test:shell": "npm run test:prepare && npm run test:v8 && npm run test:jsc && npm run test:spidermonkey",
+        "test:v8": "npm run test:prepare && node tests/run-shell.mjs --shell v8",
+        "test:jsc": "npm run test:prepare && node tests/run-shell.mjs --shell jsc",
+        "test:spidermonkey": "npm run test:prepare && node tests/run-shell.mjs --shell spidermonkey"
     },
     "devDependencies": {
         "@actions/core": "^1.11.1",
diff --git a/params.js b/params.js
index 0411f0c..acea9db 100644
--- a/params.js
+++ b/params.js
@@ -78,8 +78,9 @@
         if (this.report && !this.startDelay)
             this.startDelay = 4000;
 
-        for (const paramKey of ["tag", "tags", "test", "tests"])
+        for (const paramKey of ["tag", "tags", "test", "tests"]) {
             this.testList = this._parseTestListParam(sourceParams, paramKey);
+        }
 
         this.testIterationCount = this._parseIntParam(sourceParams, "iterationCount", 1);
         this.testWorstCaseCount = this._parseIntParam(sourceParams, "worstCaseCount", 1);
@@ -94,14 +95,18 @@
             return this.testList;
         let testList = [];
         if (sourceParams?.getAll) {
-            testList = sourceParams?.getAll(key);
+            for (const param of sourceParams?.getAll(key)) {
+                testList.push(...param.split(","));
+            }
         } else {
             // fallback for cli sourceParams which is just a Map;
             testList = sourceParams.get(key).split(",");
         }
+        testList = testList.map(each => each.trim());
         sourceParams.delete(key);
-        if (this.testList.length > 0 && testList.length > 0)
+        if (this.testList.length > 0 && testList.length > 0) {
             throw new Error(`Overriding previous testList='${this.testList.join()}' with ${key} url-parameter.`);
+        }
         return testList;
     }
 
diff --git a/tests/run-browser.mjs b/tests/run-browser.mjs
index f1e7eb6..52cf123 100644
--- a/tests/run-browser.mjs
+++ b/tests/run-browser.mjs
@@ -61,6 +61,7 @@
     let success = true;
     try {
         success &&= await runEnd2EndTest("Run Single Suite", { test: "proxy-mobx" });
+        success &&= await runEnd2EndTest("Run Multiple Suites", { test: "prismjs-startup-es6,postcss-wtb" });
         success &&= await runEnd2EndTest("Run Tag No Prefetch",  { tag: "proxy", prefetchResources: "false" });
         success &&= await runEnd2EndTest("Run Disabled Suite", { tag: "disabled" });
         success &&= await runEnd2EndTest("Run Default Suite");
diff --git a/utils/compress.mjs b/utils/compress.mjs
index fb91d7b..657f035 100644
--- a/utils/compress.mjs
+++ b/utils/compress.mjs
@@ -1,15 +1,19 @@
 import commandLineArgs from 'command-line-args';
 import commandLineUsage from 'command-line-usage';
-import { globSync } from 'glob';
+import { globSync, globIterate } from 'glob';
 import zlib from 'zlib';
 import fs from 'fs';
 import path from 'path';
 
+
+let log = console.log
+
 function parseCommandLineArgs() {
     const optionDefinitions = [
         { name: 'decompress', alias: 'd', type: Boolean, description: 'Decompress files (default: compress).' },
         { name: 'keep', alias: 'k', type: Boolean, description: 'Keep input files after processing (default: delete).' },
         { name: 'help', alias: 'h', type: Boolean, description: 'Print this usage guide.' },
+        { name: 'quiet', alias: 'q', type: Boolean, description: 'Quite output, only print errors.' },
         { name: 'globs', type: String, multiple: true, defaultOption: true, description: 'Glob patterns of files to process.' },
     ];
     const options = commandLineArgs(optionDefinitions);
@@ -32,10 +36,14 @@
         process.exit(0);
     }
 
+    if (options.quiet) {
+        log = () => {};
+    }
+
     if (options.globs === undefined) {
         if (options.decompress) {
             const defaultGlob = '**/*.z';
-            console.log(`No input glob pattern given, using default: ${defaultGlob}`);
+            log(`No input glob pattern given, using default: ${defaultGlob}`);
             options.globs = [defaultGlob];
         } else {
             // For compression, require the user to specify explicit input file patterns.
@@ -44,6 +52,7 @@
             process.exit(1);
         }
     }
+
     return options;
 }
 
@@ -61,9 +70,9 @@
     const originalSize = inputData.length;
     const compressedSize = compressedData.length;
     const compressionRatio = calculateCompressionRatio(originalSize, compressedSize);
-    console.log(`  Original size:   ${String(originalSize).padStart(8)} bytes`);
-    console.log(`  Compressed size: ${String(compressedSize).padStart(8)} bytes`);
-    console.log(`  Compression ratio:  ${compressionRatio.toFixed(2).padStart(8)}%`);
+    log(`  Original size:   ${String(originalSize).padStart(8)} bytes`);
+    log(`  Compressed size: ${String(compressedSize).padStart(8)} bytes`);
+    log(`  Compression ratio:  ${compressionRatio.toFixed(2).padStart(8)}%`);
 
     return compressedData;
 }
@@ -74,35 +83,39 @@
     const compressedSize = inputData.length;
     const decompressedSize = decompressedData.length;
     const expansionRatio = calculateExpansionRatio(compressedSize, decompressedSize);
-    console.log(`  Compressed size:   ${String(compressedSize).padStart(8)} bytes`);
-    console.log(`  Decompressed size: ${String(decompressedSize).padStart(8)} bytes`);
-    console.log(`  Expansion ratio:      ${expansionRatio.toFixed(2).padStart(8)}%`);
+    log(`  Compressed size:   ${String(compressedSize).padStart(8)} bytes`);
+    log(`  Decompressed size: ${String(decompressedSize).padStart(8)} bytes`);
+    log(`  Expansion ratio:      ${expansionRatio.toFixed(2).padStart(8)}%`);
 
     return decompressedData;
 }
 
-function globsToFiles(globs) {
-    let files = [];
+async function* globsToFiles(globs) {
+    let files = new Set();
     console.assert(globs.length > 0);
+    const globtions =  { nodir: true, ignore: '**/node_modules/**' };
     for (const glob of globs) {
-        const matches = globSync(glob, { nodir: true });
-        files = files.concat(matches);
+        for await (const file of globIterate(glob, globtions)) {
+            if (files.has(file))
+                continue;
+            files.add(file)
+            yield file;
+        }
     }
-    files = Array.from(new Set(files)).sort();
-    return files;
 }
 
-function processFiles(files, isDecompress, keep) {
+async function processFiles(filesGenerator, isDecompress, keep) {
     const verb = isDecompress ? 'decompress' : 'compress';
-    console.log(`Found ${files.length} files to ${verb}` + (files.length ? ':' : '.'));
+    const files = [];
 
     // For printing overall statistics at the end.
     let totalInputSize = 0;
     let totalOutputSize = 0;
 
-    for (const inputFilename of files) {
+    for await (const inputFilename of filesGenerator) {
+        files.push(inputFilename);
         try {
-            console.log(inputFilename);
+            log(inputFilename);
             let outputFilename;
             if (isDecompress) {
                 if (path.extname(inputFilename) !== '.z') {
@@ -111,7 +124,7 @@
                 } else {
                     outputFilename = inputFilename.slice(0, -2);
                 }
-                console.log(`  Decompressing to: ${outputFilename}`);
+                log(`  Decompressing to: ${outputFilename}`);
             } else {
                 if (path.extname(inputFilename) === '.z') {
                     console.warn(`  Warning: Input file already has a .z extension.`);
@@ -130,7 +143,7 @@
 
             if (!keep) {
                 fs.unlinkSync(inputFilename);
-                console.log(`  Deleted input file.`);
+                log(`  Deleted input file.`);
             }
         } catch (err) {
             console.error(`Error ${verb}ing ${inputFilename}:`, err);
@@ -138,16 +151,17 @@
     }
 
     if (files.length > 1) {
+        log(`Found ${files.length} files to ${verb}` + (files.length ? ':' : '.'));
         if (isDecompress) {
             const totalExpansionRatio = calculateExpansionRatio(totalInputSize, totalOutputSize);
-            console.log(`Total compressed sizes:   ${String(totalInputSize).padStart(9)} bytes`);
-            console.log(`Total decompressed sizes: ${String(totalOutputSize).padStart(9)} bytes`);
-            console.log(`Average expansion ratio:     ${totalExpansionRatio.toFixed(2).padStart(9)}%`);
+            log(`Total compressed sizes:   ${String(totalInputSize).padStart(9)} bytes`);
+            log(`Total decompressed sizes: ${String(totalOutputSize).padStart(9)} bytes`);
+            log(`Average expansion ratio:     ${totalExpansionRatio.toFixed(2).padStart(9)}%`);
         } else {
             const totalCompressionRatio = calculateCompressionRatio(totalInputSize, totalOutputSize);
-            console.log(`Total original sizes:   ${String(totalInputSize).padStart(9)} bytes`);
-            console.log(`Total compressed sizes: ${String(totalOutputSize).padStart(9)} bytes`);
-            console.log(`Average compression ratio: ${totalCompressionRatio.toFixed(2).padStart(9)}%`);
+            log(`Total original sizes:   ${String(totalInputSize).padStart(9)} bytes`);
+            log(`Total compressed sizes: ${String(totalOutputSize).padStart(9)} bytes`);
+            log(`Average compression ratio: ${totalCompressionRatio.toFixed(2).padStart(9)}%`);
         }
     }
 }