diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index f2502045..ba6d24c5 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -18,19 +18,15 @@ jobs: cache: 'npm' - name: Cache Node modules - uses: actions/cache@v2 - env: - cache-name: cache-node-modules + uses: actions/cache@v3 + id: npm-cache with: - # npm cache files are stored in `~/.npm` on Linux/macOS path: ~/.npm - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- + ${{ runner.os }}-node- - - name: "Install dependencies" + - name: Install dependencies run: | npm install sudo npx playwright install-deps @@ -39,3 +35,24 @@ jobs: run: npm run lint - name: Test run: npm test + + - name: Benchmark + run: npm run benchmark | tee benchmark.txt + + - name: Download previous benchmark data + uses: actions/cache@v3 + with: + path: ./cache + key: ${{ runner.os }}-benchmark + + - name: Store benchmark result + uses: benchmark-action/github-action-benchmark@v1 + with: + tool: 'benchmarkjs' + output-file-path: benchmark.txt + external-data-json-path: ./cache/benchmark-data.json + github-token: ${{ secrets.GITHUB_TOKEN }} + alert-threshold: '150%' + comment-on-alert: true + fail-on-alert: true + alert-comment-cc-users: '@Stuk' diff --git a/package-lock.json b/package-lock.json index 298c6918..e6eef778 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5292,6 +5292,24 @@ "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==", "dev": true }, + "benchmark": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-2.1.4.tgz", + "integrity": "sha1-CfPeMckWQl1JjMLuVloOvzwqVik=", + "dev": true, + "requires": { + "lodash": "^4.17.4", + "platform": "^1.3.3" + }, + "dependencies": { + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + } + } + }, "binary-extensions": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz", @@ -8142,6 +8160,12 @@ "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", "dev": true }, + "platform": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", + "dev": true + }, "playwright": { "version": "1.15.2", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.15.2.tgz", diff --git a/package.json b/package.json index 8e7a1da7..25f74b67 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,10 @@ "scripts": { "test": "npm run test-node && npm run test-browser && tsc", "test-node": "qunit --require ./test/helpers/test-utils.js --require ./test/helpers/node-test-utils.js test/asserts/", - "test-browser": "grunt build && node test/run.js", + "test-browser": "grunt build && node test/run.js --test", + "benchmark": "npm run benchmark-node && npm run benchmark-browser", + "benchmark-node": "node test/benchmark/node.js", + "benchmark-browser": "node test/run.js --benchmark", "lint": "eslint ." }, "contributors": [ @@ -39,6 +42,7 @@ "inflate" ], "devDependencies": { + "benchmark": "^2.1.4", "browserify": "~13.0.0", "eslint": "^8.18.0", "grunt": "~0.4.1", diff --git a/test/benchmark/.eslintrc.js b/test/benchmark/.eslintrc.js new file mode 100644 index 00000000..28835dd3 --- /dev/null +++ b/test/benchmark/.eslintrc.js @@ -0,0 +1,8 @@ +"use strict"; + +module.exports = { + globals: { + // Added by index.html and node.js + Benchmark: false, + }, +}; diff --git a/test/benchmark/benchmark.js b/test/benchmark/benchmark.js new file mode 100644 index 00000000..30f00637 --- /dev/null +++ b/test/benchmark/benchmark.js @@ -0,0 +1,39 @@ +"use strict"; + +(function (root, factory) { + if (typeof module === "object" && module.exports) { + module.exports = factory(); + } else { + root.benchmark = factory(); + } +}(typeof self !== "undefined" ? self : this, function () { + return function (type) { + return new Promise(resolve => { + const suite = new Benchmark.Suite(); + + suite + .add(`${type} generateAsync`, { + defer: true, + async fn(deferred) { + const zip = new JSZip(); + + for (let i = 0; i < 50; i++) { + zip.file("file_" + i, "R0lGODdhBQAFAIACAAAAAP/eACwAAAAABQAFAAACCIwPkWerClIBADs=", { base64: true, date: new Date(1234123491011) }); + } + + await zip.generateAsync({ type }); + deferred.resolve(); + } + }) + .on("cycle", event => { + // Output benchmark result by converting benchmark result to string + console.log(String(event.target)); + }) + .on("complete", () => { + console.log("Benchmark complete"); + resolve(); + }) + .run({ "async": true }); + }); + }; +})); diff --git a/test/benchmark/index.html b/test/benchmark/index.html new file mode 100644 index 00000000..0f92fe1f --- /dev/null +++ b/test/benchmark/index.html @@ -0,0 +1,16 @@ + + + + + JSZip Benchmark + + + + + + + + + diff --git a/test/benchmark/node.js b/test/benchmark/node.js new file mode 100644 index 00000000..8b359596 --- /dev/null +++ b/test/benchmark/node.js @@ -0,0 +1,7 @@ +"use strict"; + +globalThis.Benchmark = require("benchmark"); +globalThis.JSZip = require("../../lib/index"); + +const benchmark = require("./benchmark"); +benchmark("nodebuffer"); diff --git a/test/run.js b/test/run.js index fe52d6de..d2993cda 100644 --- a/test/run.js +++ b/test/run.js @@ -23,20 +23,14 @@ const createServer = require("http-server").createServer; * @param {string} browserType * @returns {Promise<[string, Results]>} */ -async function run(browserType) { +async function runBrowser(browserType, waitFor, file) { console.log("Starting", browserType); const browser = await playwright[browserType].launch(); const context = await browser.newContext(); const page = await context.newPage(); - await page.goto("http://127.0.0.1:8080/test/index.html?hidepassed"); - - let result; - do { - result = await page.evaluate(() => { - return window.global_test_results; - }); - } while (!result); + await page.goto(`http://127.0.0.1:8080/test/${file}`); + const result = await waitFor(page); console.log("Closing", browserType); await browser.close(); @@ -44,7 +38,7 @@ async function run(browserType) { return [browserType, result]; } -async function main() { +async function runBrowsers(waitFor, file) { const browsersTypes = ["chromium", "firefox", "webkit"]; const server = createServer({root: path.join(__dirname, "..")}); @@ -52,23 +46,71 @@ async function main() { console.log("Server started"); try { - const results = await Promise.all(browsersTypes.map(run)); + const results = await Promise.all(browsersTypes.map(b => runBrowser(b, waitFor, file))); + return results; + } finally { + server.close(); + } +} - let failures = false; - for (const result of results) { - console.log(...result); - failures = failures || result[1].failed > 0; - } +async function waitForTests(page) { + let result; + do { + result = await page.evaluate(() => { + return window.global_test_results; + }); + } while (!result); + return result; +} + +async function runTests() { + const results = await runBrowsers(waitForTests, "index.html?hidepassed"); + + let failures = false; + for (const result of results) { + console.log(...result); + failures = failures || result[1].failed > 0; + } + + if (failures) { + console.log("Tests failed"); + process.exit(1); + } else { + console.log("Tests passed!"); + } +} - if (failures) { - console.log("Tests failed"); - process.exit(1); - } else { - console.log("Tests passed!"); +async function waitForBenchmark(page) { + return new Promise(resolve => { + const logs = []; + + page.on("console", async message => { + if (message.text() === "Benchmark complete") { + resolve(logs); + } else { + logs.push(message.text()); + } + }); + }); +} + +async function runBenchmark() { + const results = await runBrowsers(waitForBenchmark, "benchmark/index.html"); + + for (const [browser, logs] of results) { + for (const log of logs) { + console.log(browser, log); } - } finally { - server.close(); } } -main(); +switch (process.argv[2]) { +case "--test": + runTests(); + break; +case "--benchmark": + runBenchmark(); + break; +default: + throw new Error(`Unknown argument: ${process.argv[2]}`); +}