From bae29396a26966e33e4bdcee613f406b8af3273a Mon Sep 17 00:00:00 2001 From: Camillo Bruni Date: Wed, 20 Aug 2025 15:08:40 +0200 Subject: [PATCH 1/7] adding tedsts --- tests/unit-tests.js | 48 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/unit-tests.js b/tests/unit-tests.js index 9c476841..a796ae71 100644 --- a/tests/unit-tests.js +++ b/tests/unit-tests.js @@ -1,4 +1,5 @@ load("shell-config.js") +load("startup-helper/StartupBenchmark.js"); load("JetStreamDriver.js"); function assertTrue(condition, message) { @@ -102,3 +103,50 @@ function assertEquals(actual, expected, message) { assertTrue(name in allScores); } })(); + +function validateIterationSources(sources) { + for (const source of sources) { + assertTrue(typeof(source) == "string"); + assertFalse(source.includes(CACHE_BUST_COMMENT)); + } +} + +(async function testStartupBenchmark() { + const benchmark = new StartupBenchmark(12, 1); + assertEquals(benchmark.iterationCount, 12); + assertEquals(benchmark.expectedCacheCommentCount, 1); + + try { + JetStream.preload = { BUNDLE: "test-bundle.js" }; + JetStream.getString = (file) => { + assertEquals(file, "test-bundle.js"); + return `function test() { +${CACHE_BUST_COMMENT} + return 1; + }`; + } + assertEquals(benchmark.iterationSourceCodes.length, 0); + await benchmark.init(); + assertEquals(benchmark.iterationSourceCodes.length, 12); + validateIterationSources(benchmark.iterationSourceCodes); + + const reuseBenchmark = new StartupBenchmark(12, 1); + reuseBenchmark.codeReuseCount = 3; + assertEquals(reuseBenchmark.iterationSourceCodes.length, 0); + await reuseBenchmark.init(); + assertEquals(reuseBenchmark.iterationSourceCodes.length, 12); + assertEquals(new Set(reuseBenchmark.iterationSourceCodes).size, 4); + validateIterationSources(reuseBenchmark.iterationSourceCodes); + + const reuseBenchmark2 = new StartupBenchmark(12, 1); + reuseBenchmark2.codeReuseCount = 5; + assertEquals(reuseBenchmark2.iterationSourceCodes.length, 0); + await reuseBenchmark2.init(); + assertEquals(reuseBenchmark2.iterationSourceCodes.length, 12); + assertEquals(new Set(reuseBenchmark2.iterationSourceCodes).size, 3); + validateIterationSources(reuseBenchmark2.iterationSourceCodes); + } finally { + JetStream.preload = undefined; + JetStream.getString = undefined; + } +})(); \ No newline at end of file From 872bfb0f3027783f0839bb7c9286ef609ff52cd4 Mon Sep 17 00:00:00 2001 From: Camillo Bruni Date: Wed, 20 Aug 2025 15:08:51 +0200 Subject: [PATCH 2/7] adding code --- startup-helper/BabelCacheBuster.js | 27 ++++++++++ startup-helper/StartupBenchmark.js | 82 ++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 startup-helper/BabelCacheBuster.js create mode 100644 startup-helper/StartupBenchmark.js diff --git a/startup-helper/BabelCacheBuster.js b/startup-helper/BabelCacheBuster.js new file mode 100644 index 00000000..9c28bdb9 --- /dev/null +++ b/startup-helper/BabelCacheBuster.js @@ -0,0 +1,27 @@ +// Babel plugin that adds CACHE_BUST_COMMENT to every function body. +const CACHE_BUST_COMMENT = "ThouShaltNotCache"; + +module.exports = function ({ types: t }) { + return { + visitor: { + Function(path) { + const bodyPath = path.get("body"); + // Handle arrow functions: () => "value" + // Convert them to block statements: () => { return "value"; } + if (!bodyPath.isBlockStatement()) { + const newBody = t.blockStatement([t.returnStatement(bodyPath.node)]); + path.set("body", newBody); + } + + // Handle empty function bodies: function foo() {} + // Add an empty statement so we have a first node to attach the comment to. + if (path.get("body.body").length === 0) { + path.get("body").pushContainer("body", t.emptyStatement()); + } + + const firstNode = path.node.body.body[0]; + t.addComment(firstNode, "leading", CACHE_BUST_COMMENT); + }, + }, + }; +}; diff --git a/startup-helper/StartupBenchmark.js b/startup-helper/StartupBenchmark.js new file mode 100644 index 00000000..eafcdf9f --- /dev/null +++ b/startup-helper/StartupBenchmark.js @@ -0,0 +1,82 @@ +const CACHE_BUST_COMMENT = "/*ThouShaltNotCache*/"; +const CACHE_BUST_COMMENT_RE = new RegExp( + `\n${RegExp.escape(CACHE_BUST_COMMENT)}\n`, + "g" +); + +class StartupBenchmark { + // How many times (separate iterations) should we reuse the source code. + // Use 0 to skip. + codeReuseCount = 1; + iterationCount = 0; + sourceCode; + sourceHash = 0; + expectedCacheCommentCount = 0; + + iterationSourceCodes = []; + + constructor(iterationCount, expectedCacheCommentCount) { + this.iterationCount = iterationCount; + this.expectedCacheCommentCount = expectedCacheCommentCount; + console.assert(expectedCacheCommentCount > 0); + } + + async init() { + this.sourceCode = await JetStream.getString(JetStream.preload.BUNDLE); + const cacheCommentCount = this.sourceCode.match( + CACHE_BUST_COMMENT_RE + ).length; + this.sourceHash = this.quickHash(this.sourceCode); + this.validateSourceCacheComments(cacheCommentCount); + for (let i = 0; i < this.iterationCount; i++) + this.iterationSourceCodes[i] = this.createIterationSourceCode(i); + this.validateIterationSourceCodes(); + } + + validateSourceCacheComments(cacheCommentCount) { + console.assert( + cacheCommentCount === this.expectedCacheCommentCount, + `Invalid cache comment count ${cacheCommentCount} expected ${this.expectedCacheCommentCount}.` + ); + } + + validateIterationSourceCodes() { + if (this.iterationSourceCodes.some((each) => !each?.length)) + throw new Error(`Got invalid iterationSourceCodes`); + let expectedSize = this.iterationCount; + if (this.codeReuseCount !== 0) + expectedSize = Math.ceil(this.iterationCount / this.codeReuseCount); + const uniqueSources = new Set(this.iterationSourceCodes); + if (uniqueSources.size != expectedSize) + throw new Error( + `Expected ${expectedSize} unique sources, but got ${uniqueSources.size}.` + ); + } + createIterationSourceCode(iteration) { + if (!this.codeReuseCount) return this.sourceCode; + // Alter the code per iteration to prevent caching. + const cacheId = + Math.floor(iteration / this.codeReuseCount) * this.codeReuseCount; + // Reuse existing sources if this.codeReuseCount > 1: + if (cacheId < this.iterationSourceCodes.length) + return this.iterationSourceCodes[cacheId]; + + const sourceCode = this.sourceCode.replaceAll( + CACHE_BUST_COMMENT_RE, + `/*${cacheId}*/` + ); + // Warm up quickHash. + this.quickHash(sourceCode); + return sourceCode; + } + + quickHash(str) { + let hash = 5381; + let i = str.length; + while (i > 0) { + hash = (hash * 33) ^ (str.charCodeAt(i) | 0); + i -= 919; + } + return hash | 0; + } +} From 64f97bdf2f944ef26d7ae8840618ae737954fc43 Mon Sep 17 00:00:00 2001 From: Camillo Bruni Date: Wed, 20 Aug 2025 15:09:56 +0200 Subject: [PATCH 3/7] adding no-reuse test --- tests/unit-tests.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/unit-tests.js b/tests/unit-tests.js index a796ae71..03cd72d9 100644 --- a/tests/unit-tests.js +++ b/tests/unit-tests.js @@ -130,6 +130,14 @@ ${CACHE_BUST_COMMENT} assertEquals(benchmark.iterationSourceCodes.length, 12); validateIterationSources(benchmark.iterationSourceCodes); + const noReuseBenchmark = new StartupBenchmark(12, 1); + noReuseBenchmark.codeReuseCount = 0; + assertEquals(noReuseBenchmark.iterationSourceCodes.length, 0); + await noReuseBenchmark.init(); + assertEquals(noReuseBenchmark.iterationSourceCodes.length, 12); + assertEquals(new Set(noReuseBenchmark.iterationSourceCodes).size, 1); + validateIterationSources(noReuseBenchmark.iterationSourceCodes); + const reuseBenchmark = new StartupBenchmark(12, 1); reuseBenchmark.codeReuseCount = 3; assertEquals(reuseBenchmark.iterationSourceCodes.length, 0); From 74483ff1b9dad34a096a9f02f5b052bb755bb207 Mon Sep 17 00:00:00 2001 From: Camillo Bruni Date: Wed, 20 Aug 2025 15:12:00 +0200 Subject: [PATCH 4/7] adding more tests --- startup-helper/StartupBenchmark.js | 3 +-- tests/unit-tests.js | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/startup-helper/StartupBenchmark.js b/startup-helper/StartupBenchmark.js index eafcdf9f..0750481d 100644 --- a/startup-helper/StartupBenchmark.js +++ b/startup-helper/StartupBenchmark.js @@ -43,7 +43,7 @@ class StartupBenchmark { validateIterationSourceCodes() { if (this.iterationSourceCodes.some((each) => !each?.length)) throw new Error(`Got invalid iterationSourceCodes`); - let expectedSize = this.iterationCount; + let expectedSize = 1; if (this.codeReuseCount !== 0) expectedSize = Math.ceil(this.iterationCount / this.codeReuseCount); const uniqueSources = new Set(this.iterationSourceCodes); @@ -53,7 +53,6 @@ class StartupBenchmark { ); } createIterationSourceCode(iteration) { - if (!this.codeReuseCount) return this.sourceCode; // Alter the code per iteration to prevent caching. const cacheId = Math.floor(iteration / this.codeReuseCount) * this.codeReuseCount; diff --git a/tests/unit-tests.js b/tests/unit-tests.js index 03cd72d9..bac3476a 100644 --- a/tests/unit-tests.js +++ b/tests/unit-tests.js @@ -128,6 +128,7 @@ ${CACHE_BUST_COMMENT} assertEquals(benchmark.iterationSourceCodes.length, 0); await benchmark.init(); assertEquals(benchmark.iterationSourceCodes.length, 12); + assertEquals(new Set(benchmark.iterationSourceCodes).size, 12); validateIterationSources(benchmark.iterationSourceCodes); const noReuseBenchmark = new StartupBenchmark(12, 1); From bc5fa33e2d60e964e17693b58e4384b5718a917b Mon Sep 17 00:00:00 2001 From: Camillo Bruni Date: Tue, 26 Aug 2025 10:30:34 +0200 Subject: [PATCH 5/7] update code and add more tests --- startup-helper/StartupBenchmark.js | 30 +++++--- tests/unit-tests.js | 119 +++++++++++++++++++++-------- 2 files changed, 107 insertions(+), 42 deletions(-) diff --git a/startup-helper/StartupBenchmark.js b/startup-helper/StartupBenchmark.js index 0750481d..71006cbf 100644 --- a/startup-helper/StartupBenchmark.js +++ b/startup-helper/StartupBenchmark.js @@ -5,20 +5,30 @@ const CACHE_BUST_COMMENT_RE = new RegExp( ); class StartupBenchmark { - // How many times (separate iterations) should we reuse the source code. - // Use 0 to skip. - codeReuseCount = 1; + // Total iterations for this benchmark. iterationCount = 0; + // Original source code. sourceCode; + // quickHahs(this.sourceCode) for use in custom validate() methods. sourceHash = 0; + // Number of no-cache comments in the original sourceCode. expectedCacheCommentCount = 0; - + // How many times (separate iterations) should we reuse the source code. + // Use 0 to skip and only use a single sourceCode string. + sourceCodeReuseCount = 1; + // SourceCode for each iteration, number of unique sources is controlled + // by codeReuseCount; iterationSourceCodes = []; - constructor(iterationCount, expectedCacheCommentCount) { + constructor( + {iterationCount, expectedCacheCommentCount, sourceCodeReuseCount = 1 } = {} + ) { this.iterationCount = iterationCount; + console.assert(this.iterationCount > 0) this.expectedCacheCommentCount = expectedCacheCommentCount; - console.assert(expectedCacheCommentCount > 0); + console.assert(this.expectedCacheCommentCount > 0) + this.sourceCodeReuseCount = sourceCodeReuseCount; + console.assert(this.sourceCodeReuseCount >= 0); } async init() { @@ -44,18 +54,20 @@ class StartupBenchmark { if (this.iterationSourceCodes.some((each) => !each?.length)) throw new Error(`Got invalid iterationSourceCodes`); let expectedSize = 1; - if (this.codeReuseCount !== 0) - expectedSize = Math.ceil(this.iterationCount / this.codeReuseCount); + if (this.sourceCodeReuseCount !== 0) + expectedSize = Math.ceil(this.iterationCount / this.sourceCodeReuseCount); const uniqueSources = new Set(this.iterationSourceCodes); if (uniqueSources.size != expectedSize) throw new Error( `Expected ${expectedSize} unique sources, but got ${uniqueSources.size}.` ); } + createIterationSourceCode(iteration) { // Alter the code per iteration to prevent caching. const cacheId = - Math.floor(iteration / this.codeReuseCount) * this.codeReuseCount; + Math.floor(iteration / this.sourceCodeReuseCount) * + this.sourceCodeReuseCount; // Reuse existing sources if this.codeReuseCount > 1: if (cacheId < this.iterationSourceCodes.length) return this.iterationSourceCodes[cacheId]; diff --git a/tests/unit-tests.js b/tests/unit-tests.js index bac3476a..f374f152 100644 --- a/tests/unit-tests.js +++ b/tests/unit-tests.js @@ -1,4 +1,4 @@ -load("shell-config.js") +load("shell-config.js"); load("startup-helper/StartupBenchmark.js"); load("JetStreamDriver.js"); @@ -20,17 +20,25 @@ function assertEquals(actual, expected, message) { } } +function assertThrows(message, func) { + let didThrow = false; + try { + func(); + } catch (e) { + didThrow = true; + } + assertTrue(didThrow, message); +} + (function testTagsAreLowerCaseStrings() { for (const benchmark of BENCHMARKS) { - benchmark.tags.forEach(tag => { - assertTrue(typeof(tag) == "string"); - assertTrue(tag == tag.toLowerCase()); - }) + benchmark.tags.forEach((tag) => { + assertTrue(typeof tag == "string"); + assertTrue(tag == tag.toLowerCase()); + }); } })(); - - (function testTagsAll() { for (const benchmark of BENCHMARKS) { const tags = benchmark.tags; @@ -42,53 +50,56 @@ function assertEquals(actual, expected, message) { } })(); - (function testDriverBenchmarksOrder() { const benchmarks = findBenchmarksByTag("all"); const driver = new Driver(benchmarks); assertEquals(driver.benchmarks.length, BENCHMARKS.length); - const names = driver.benchmarks.map(b => b.name.toLowerCase()).sort().reverse(); + const names = driver.benchmarks + .map((b) => b.name.toLowerCase()) + .sort() + .reverse(); for (let i = 0; i < names.length; i++) { assertEquals(driver.benchmarks[i].name.toLowerCase(), names[i]); } })(); - (function testEnableByTag() { const driverA = new Driver(findBenchmarksByTag("Default")); const driverB = new Driver(findBenchmarksByTag("default")); assertTrue(driverA.benchmarks.length > 0); assertEquals(driverA.benchmarks.length, driverB.benchmarks.length); const enabledBenchmarkNames = new Set( - Array.from(driverA.benchmarks).map(b => b.name)); + Array.from(driverA.benchmarks).map((b) => b.name) + ); for (const benchmark of BENCHMARKS) { if (benchmark.tags.has("default")) assertTrue(enabledBenchmarkNames.has(benchmark.name)); } })(); - (function testDriverEnableDuplicateAndSort() { - const benchmarks = [...findBenchmarksByTag("wasm"), ...findBenchmarksByTag("wasm")]; - assertTrue(benchmarks.length > 0); - const uniqueBenchmarks = new Set(benchmarks); - assertFalse(uniqueBenchmarks.size == benchmarks.length); - const driver = new Driver(benchmarks); - assertEquals(driver.benchmarks.length, uniqueBenchmarks.size); + const benchmarks = [ + ...findBenchmarksByTag("wasm"), + ...findBenchmarksByTag("wasm"), + ]; + assertTrue(benchmarks.length > 0); + const uniqueBenchmarks = new Set(benchmarks); + assertFalse(uniqueBenchmarks.size == benchmarks.length); + const driver = new Driver(benchmarks); + assertEquals(driver.benchmarks.length, uniqueBenchmarks.size); })(); - (function testBenchmarkSubScores() { for (const benchmark of BENCHMARKS) { const subScores = benchmark.subScores(); assertTrue(subScores instanceof Object); assertTrue(Object.keys(subScores).length > 0); for (const [name, value] of Object.entries(subScores)) { - assertTrue(typeof(name) == "string"); + assertTrue(typeof name == "string"); // "Score" can only be part of allScores(). assertFalse(name == "Score"); // Without running values should be either null (or 0 for GroupedBenchmark) - assertFalse(value) + assertFalse(value); } } })(); @@ -99,20 +110,22 @@ function assertEquals(actual, expected, message) { const allScores = benchmark.allScores(); assertTrue("Score" in allScores); // All subScore items are part of allScores. - for (const name of Object.keys(subScores)) - assertTrue(name in allScores); + for (const name of Object.keys(subScores)) assertTrue(name in allScores); } })(); function validateIterationSources(sources) { for (const source of sources) { - assertTrue(typeof(source) == "string"); + assertTrue(typeof source == "string"); assertFalse(source.includes(CACHE_BUST_COMMENT)); } } (async function testStartupBenchmark() { - const benchmark = new StartupBenchmark(12, 1); + const benchmark = new StartupBenchmark({ + iterationCount: 12, + expectedCacheCommentCount: 1, + }); assertEquals(benchmark.iterationCount, 12); assertEquals(benchmark.expectedCacheCommentCount, 1); @@ -124,31 +137,40 @@ function validateIterationSources(sources) { ${CACHE_BUST_COMMENT} return 1; }`; - } + }; assertEquals(benchmark.iterationSourceCodes.length, 0); await benchmark.init(); assertEquals(benchmark.iterationSourceCodes.length, 12); assertEquals(new Set(benchmark.iterationSourceCodes).size, 12); validateIterationSources(benchmark.iterationSourceCodes); - const noReuseBenchmark = new StartupBenchmark(12, 1); - noReuseBenchmark.codeReuseCount = 0; + const noReuseBenchmark = new StartupBenchmark({ + iterationCount: 12, + expectedCacheCommentCount: 1, + }); + noReuseBenchmark.sourceCodeReuseCount = 0; assertEquals(noReuseBenchmark.iterationSourceCodes.length, 0); await noReuseBenchmark.init(); assertEquals(noReuseBenchmark.iterationSourceCodes.length, 12); assertEquals(new Set(noReuseBenchmark.iterationSourceCodes).size, 1); validateIterationSources(noReuseBenchmark.iterationSourceCodes); - const reuseBenchmark = new StartupBenchmark(12, 1); - reuseBenchmark.codeReuseCount = 3; + const reuseBenchmark = new StartupBenchmark({ + iterationCount: 12, + expectedCacheCommentCount: 1, + }); + reuseBenchmark.sourceCodeReuseCount = 3; assertEquals(reuseBenchmark.iterationSourceCodes.length, 0); await reuseBenchmark.init(); assertEquals(reuseBenchmark.iterationSourceCodes.length, 12); assertEquals(new Set(reuseBenchmark.iterationSourceCodes).size, 4); validateIterationSources(reuseBenchmark.iterationSourceCodes); - const reuseBenchmark2 = new StartupBenchmark(12, 1); - reuseBenchmark2.codeReuseCount = 5; + const reuseBenchmark2 = new StartupBenchmark({ + iterationCount: 12, + expectedCacheCommentCount: 1, + }); + reuseBenchmark2.sourceCodeReuseCount = 5; assertEquals(reuseBenchmark2.iterationSourceCodes.length, 0); await reuseBenchmark2.init(); assertEquals(reuseBenchmark2.iterationSourceCodes.length, 12); @@ -158,4 +180,35 @@ ${CACHE_BUST_COMMENT} JetStream.preload = undefined; JetStream.getString = undefined; } -})(); \ No newline at end of file +})(); + +(function testStartupBenchmarkThrow() { + assertThrows( + "StartupBenchmark constructor should throw with no arguments.", + () => new StartupBenchmark() + ); + + assertThrows( + "StartupBenchmark constructor should throw with missing expectedCacheCommentCount.", + () => new StartupBenchmark({ iterationCount: 1 }) + ); + + assertThrows( + "StartupBenchmark constructor should throw with missing iterationCount.", + () => new StartupBenchmark({ expectedCacheCommentCount: 1 }) + ); + + assertThrows( + "StartupBenchmark constructor should throw with iterationCount=0.", + () => { + new StartupBenchmark({ iterationCount: 0, expectedCacheCommentCount: 1 }); + } + ); + + assertThrows( + "StartupBenchmark constructor should throw with expectedCacheCommentCount=0.", + () => { + new StartupBenchmark({ iterationCount: 1, expectedCacheCommentCount: 0 }); + } + ); +})(); From 677c139ec9ed872b2059ca87a93b7303f8b57069 Mon Sep 17 00:00:00 2001 From: Camillo Bruni Date: Tue, 26 Aug 2025 10:43:23 +0200 Subject: [PATCH 6/7] more cleanup --- startup-helper/StartupBenchmark.js | 80 +++++++++++++++-------- tests/unit-tests.js | 100 ++++++++++++++++------------- 2 files changed, 110 insertions(+), 70 deletions(-) diff --git a/startup-helper/StartupBenchmark.js b/startup-helper/StartupBenchmark.js index 71006cbf..7a369fb3 100644 --- a/startup-helper/StartupBenchmark.js +++ b/startup-helper/StartupBenchmark.js @@ -6,56 +6,86 @@ const CACHE_BUST_COMMENT_RE = new RegExp( class StartupBenchmark { // Total iterations for this benchmark. - iterationCount = 0; + #iterationCount = 0; // Original source code. - sourceCode; - // quickHahs(this.sourceCode) for use in custom validate() methods. - sourceHash = 0; - // Number of no-cache comments in the original sourceCode. - expectedCacheCommentCount = 0; + #sourceCode; + // quickHahs(this.#sourceCode) for use in custom validate() methods. + #sourceHash = 0; + // Number of no-cache comments in the original #sourceCode. + #expectedCacheCommentCount = 0; // How many times (separate iterations) should we reuse the source code. - // Use 0 to skip and only use a single sourceCode string. - sourceCodeReuseCount = 1; - // SourceCode for each iteration, number of unique sources is controlled + // Use 0 to skip and only use a single #sourceCode string. + #sourceCodeReuseCount = 1; + // #sourceCode for each iteration, number of unique sources is controlled // by codeReuseCount; - iterationSourceCodes = []; + #iterationSourceCodes = []; - constructor( - {iterationCount, expectedCacheCommentCount, sourceCodeReuseCount = 1 } = {} - ) { - this.iterationCount = iterationCount; - console.assert(this.iterationCount > 0) - this.expectedCacheCommentCount = expectedCacheCommentCount; - console.assert(this.expectedCacheCommentCount > 0) - this.sourceCodeReuseCount = sourceCodeReuseCount; - console.assert(this.sourceCodeReuseCount >= 0); + constructor({ + iterationCount, + expectedCacheCommentCount, + sourceCodeReuseCount = 1, + } = {}) { + console.assert(iterationCount > 0); + this.#iterationCount = iterationCount; + console.assert(expectedCacheCommentCount > 0); + this.#expectedCacheCommentCount = expectedCacheCommentCount; + console.assert(sourceCodeReuseCount >= 0); + this.#sourceCodeReuseCount = sourceCodeReuseCount; + } + + get iterationCount() { + return this.#iterationCount; + } + + get sourceCode() { + return this.#sourceCode; + } + + get sourceHash() { + return this.#sourceHash; + } + + get expectedCacheCommentCount() { + return this.#expectedCacheCommentCount; + } + + get sourceCodeReuseCount() { + return this.#sourceCodeReuseCount; + } + + get iterationSourceCodes() { + return this.#iterationSourceCodes; } async init() { - this.sourceCode = await JetStream.getString(JetStream.preload.BUNDLE); + this.#sourceCode = await JetStream.getString(JetStream.preload.BUNDLE); const cacheCommentCount = this.sourceCode.match( CACHE_BUST_COMMENT_RE ).length; - this.sourceHash = this.quickHash(this.sourceCode); + this.#sourceHash = this.quickHash(this.sourceCode); this.validateSourceCacheComments(cacheCommentCount); for (let i = 0; i < this.iterationCount; i++) - this.iterationSourceCodes[i] = this.createIterationSourceCode(i); + this.#iterationSourceCodes[i] = this.createIterationSourceCode(i); this.validateIterationSourceCodes(); } validateSourceCacheComments(cacheCommentCount) { console.assert( cacheCommentCount === this.expectedCacheCommentCount, - `Invalid cache comment count ${cacheCommentCount} expected ${this.expectedCacheCommentCount}.` + `Invalid cache comment count ${cacheCommentCount} expected ${ + this.expectedCacheCommentCount + }.` ); } validateIterationSourceCodes() { - if (this.iterationSourceCodes.some((each) => !each?.length)) + if (this.#iterationSourceCodes.some((each) => !each?.length)) throw new Error(`Got invalid iterationSourceCodes`); let expectedSize = 1; if (this.sourceCodeReuseCount !== 0) - expectedSize = Math.ceil(this.iterationCount / this.sourceCodeReuseCount); + expectedSize = Math.ceil( + this.iterationCount / this.sourceCodeReuseCount + ); const uniqueSources = new Set(this.iterationSourceCodes); if (uniqueSources.size != expectedSize) throw new Error( diff --git a/tests/unit-tests.js b/tests/unit-tests.js index f374f152..adc7d294 100644 --- a/tests/unit-tests.js +++ b/tests/unit-tests.js @@ -121,14 +121,8 @@ function validateIterationSources(sources) { } } -(async function testStartupBenchmark() { - const benchmark = new StartupBenchmark({ - iterationCount: 12, - expectedCacheCommentCount: 1, - }); - assertEquals(benchmark.iterationCount, 12); - assertEquals(benchmark.expectedCacheCommentCount, 1); +(async function testStartupBenchmark() { try { JetStream.preload = { BUNDLE: "test-bundle.js" }; JetStream.getString = (file) => { @@ -138,50 +132,66 @@ ${CACHE_BUST_COMMENT} return 1; }`; }; - assertEquals(benchmark.iterationSourceCodes.length, 0); - await benchmark.init(); - assertEquals(benchmark.iterationSourceCodes.length, 12); - assertEquals(new Set(benchmark.iterationSourceCodes).size, 12); - validateIterationSources(benchmark.iterationSourceCodes); - - const noReuseBenchmark = new StartupBenchmark({ - iterationCount: 12, - expectedCacheCommentCount: 1, - }); - noReuseBenchmark.sourceCodeReuseCount = 0; - assertEquals(noReuseBenchmark.iterationSourceCodes.length, 0); - await noReuseBenchmark.init(); - assertEquals(noReuseBenchmark.iterationSourceCodes.length, 12); - assertEquals(new Set(noReuseBenchmark.iterationSourceCodes).size, 1); - validateIterationSources(noReuseBenchmark.iterationSourceCodes); - - const reuseBenchmark = new StartupBenchmark({ - iterationCount: 12, - expectedCacheCommentCount: 1, - }); - reuseBenchmark.sourceCodeReuseCount = 3; - assertEquals(reuseBenchmark.iterationSourceCodes.length, 0); - await reuseBenchmark.init(); - assertEquals(reuseBenchmark.iterationSourceCodes.length, 12); - assertEquals(new Set(reuseBenchmark.iterationSourceCodes).size, 4); - validateIterationSources(reuseBenchmark.iterationSourceCodes); - - const reuseBenchmark2 = new StartupBenchmark({ - iterationCount: 12, - expectedCacheCommentCount: 1, - }); - reuseBenchmark2.sourceCodeReuseCount = 5; - assertEquals(reuseBenchmark2.iterationSourceCodes.length, 0); - await reuseBenchmark2.init(); - assertEquals(reuseBenchmark2.iterationSourceCodes.length, 12); - assertEquals(new Set(reuseBenchmark2.iterationSourceCodes).size, 3); - validateIterationSources(reuseBenchmark2.iterationSourceCodes); + await testStartupBenchmarkInnerTests(); } finally { JetStream.preload = undefined; JetStream.getString = undefined; } })(); + +async function testStartupBenchmarkInnerTests() { + const benchmark = new StartupBenchmark({ + iterationCount: 12, + expectedCacheCommentCount: 1, + }); + assertEquals(benchmark.iterationCount, 12); + assertEquals(benchmark.expectedCacheCommentCount, 1); + assertEquals(benchmark.iterationSourceCodes.length, 0); + assertEquals(benchmark.sourceCode, undefined); + assertEquals(benchmark.sourceHash, 0); + await benchmark.init(); + assertEquals(benchmark.sourceHash, 177573); + assertEquals(benchmark.sourceCode.length, 68); + assertEquals(benchmark.iterationSourceCodes.length, 12); + assertEquals(new Set(benchmark.iterationSourceCodes).size, 12); + validateIterationSources(benchmark.iterationSourceCodes); + + const noReuseBenchmark = new StartupBenchmark({ + iterationCount: 12, + expectedCacheCommentCount: 1, + sourceCodeReuseCount: 0, + }); + assertEquals(noReuseBenchmark.iterationSourceCodes.length, 0); + await noReuseBenchmark.init(); + assertEquals(noReuseBenchmark.iterationSourceCodes.length, 12); + assertEquals(new Set(noReuseBenchmark.iterationSourceCodes).size, 1); + validateIterationSources(noReuseBenchmark.iterationSourceCodes); + + const reuseBenchmark = new StartupBenchmark({ + iterationCount: 12, + expectedCacheCommentCount: 1, + sourceCodeReuseCount: 3, + }); + assertEquals(reuseBenchmark.iterationSourceCodes.length, 0); + await reuseBenchmark.init(); + assertEquals(reuseBenchmark.iterationSourceCodes.length, 12); + assertEquals(new Set(reuseBenchmark.iterationSourceCodes).size, 4); + validateIterationSources(reuseBenchmark.iterationSourceCodes); + + const reuseBenchmark2 = new StartupBenchmark({ + iterationCount: 12, + expectedCacheCommentCount: 1, + sourceCodeReuseCount: 5, + }); + assertEquals(reuseBenchmark2.iterationSourceCodes.length, 0); + await reuseBenchmark2.init(); + assertEquals(reuseBenchmark2.iterationSourceCodes.length, 12); + assertEquals(new Set(reuseBenchmark2.iterationSourceCodes).size, 3); + validateIterationSources(reuseBenchmark2.iterationSourceCodes); +} + + (function testStartupBenchmarkThrow() { assertThrows( "StartupBenchmark constructor should throw with no arguments.", From 38011f233542d41986bceabd67227a7563598bbf Mon Sep 17 00:00:00 2001 From: Camillo Bruni Date: Tue, 26 Aug 2025 10:43:37 +0200 Subject: [PATCH 7/7] format document --- startup-helper/StartupBenchmark.js | 8 ++------ tests/unit-tests.js | 3 --- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/startup-helper/StartupBenchmark.js b/startup-helper/StartupBenchmark.js index 7a369fb3..65b44d58 100644 --- a/startup-helper/StartupBenchmark.js +++ b/startup-helper/StartupBenchmark.js @@ -72,9 +72,7 @@ class StartupBenchmark { validateSourceCacheComments(cacheCommentCount) { console.assert( cacheCommentCount === this.expectedCacheCommentCount, - `Invalid cache comment count ${cacheCommentCount} expected ${ - this.expectedCacheCommentCount - }.` + `Invalid cache comment count ${cacheCommentCount} expected ${this.expectedCacheCommentCount}.` ); } @@ -83,9 +81,7 @@ class StartupBenchmark { throw new Error(`Got invalid iterationSourceCodes`); let expectedSize = 1; if (this.sourceCodeReuseCount !== 0) - expectedSize = Math.ceil( - this.iterationCount / this.sourceCodeReuseCount - ); + expectedSize = Math.ceil(this.iterationCount / this.sourceCodeReuseCount); const uniqueSources = new Set(this.iterationSourceCodes); if (uniqueSources.size != expectedSize) throw new Error( diff --git a/tests/unit-tests.js b/tests/unit-tests.js index adc7d294..c1395454 100644 --- a/tests/unit-tests.js +++ b/tests/unit-tests.js @@ -121,7 +121,6 @@ function validateIterationSources(sources) { } } - (async function testStartupBenchmark() { try { JetStream.preload = { BUNDLE: "test-bundle.js" }; @@ -139,7 +138,6 @@ ${CACHE_BUST_COMMENT} } })(); - async function testStartupBenchmarkInnerTests() { const benchmark = new StartupBenchmark({ iterationCount: 12, @@ -191,7 +189,6 @@ async function testStartupBenchmarkInnerTests() { validateIterationSources(reuseBenchmark2.iterationSourceCodes); } - (function testStartupBenchmarkThrow() { assertThrows( "StartupBenchmark constructor should throw with no arguments.",