commit 8d56d54bd397c7533da83c40ff8e7a25542ff7a3
parent b59ff82f68bfd7993040b334b63f78b760c76c63
Author: Tooru Fujisawa <arai_a@mac.com>
Date: Wed, 5 Nov 2025 04:09:45 +0000
Bug 1853173 - Part 1: Add basic tests for modules. r=nbp
Differential Revision: https://phabricator.services.mozilla.com/D271022
Diffstat:
6 files changed, 503 insertions(+), 10 deletions(-)
diff --git a/dom/base/test/browser.toml b/dom/base/test/browser.toml
@@ -160,6 +160,10 @@ support-files = [
"file_js_cache_small.js",
"file_js_cache_large.js",
"file_js_cache_large_syntax_error.js",
+ "file_js_cache_importer.mjs",
+ "file_js_cache_imported1.mjs",
+ "file_js_cache_imported2.mjs",
+ "file_js_cache_imported3.mjs",
]
["browser_set_focus_after_reuse_bcg.js"]
diff --git a/dom/base/test/browser_script_loader_js_cache.js b/dom/base/test/browser_script_loader_js_cache.js
@@ -7,7 +7,60 @@ function ev(event, file, hasElement = !!file) {
hasElement,
};
}
-async function contentTask(item) {
+
+function unordered(list) {
+ return {
+ unordered: list,
+ };
+}
+
+async function contentTask(test, item) {
+ function match(param, event) {
+ if (event.event !== param.event) {
+ return false;
+ }
+
+ if (param.url && event.url !== param.url) {
+ return false;
+ }
+
+ if (event.hasElement) {
+ if (param.id !== "watchme") {
+ return false;
+ }
+ } else {
+ if (param.id) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ function consumeIfMatched(param, events) {
+ if ("unordered" in events[0]) {
+ const unordered = events[0].unordered;
+ for (let i = 0; i < unordered.length; i++) {
+ if (match(param, unordered[i])) {
+ unordered.splice(i, 1);
+ if (!unordered.length) {
+ events.shift();
+ }
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (match(param, events[0])) {
+ events.shift();
+ return true;
+ }
+
+ return false;
+ }
+
const { promise, resolve, reject } = Promise.withResolvers();
const observer = function (subject, topic, data) {
const param = {};
@@ -20,26 +73,23 @@ async function contentTask(item) {
return;
}
- const event = item.events[0];
- if (
- event.event === param.event &&
- (!param.url || event.url === param.url) &&
- (event.hasElement ? param.id === "watchme" : !param.id)
- ) {
+ if (consumeIfMatched(param, item.events)) {
dump("@@@ Got expected event: " + data + "\n");
- item.events.shift();
if (item.events.length === 0) {
resolve();
}
} else {
dump("@@@ Got unexpected event: " + data + "\n");
- dump("@@@ Expected: " + JSON.stringify(event) + "\n");
+ dump("@@@ Expected: " + JSON.stringify(item.events[0]) + "\n");
}
};
Services.obs.addObserver(observer, "ScriptLoaderTest");
const script = content.document.createElement("script");
script.id = "watchme";
+ if (test.module) {
+ script.type = "module";
+ }
script.src = item.file;
content.document.body.appendChild(script);
@@ -50,6 +100,8 @@ async function contentTask(item) {
async function runTests(tests) {
await BrowserTestUtils.withNewTab(BASE_URL + "empty.html", async browser => {
+ const tab = gBrowser.getTabForBrowser(browser);
+
for (const test of tests) {
ChromeUtils.clearResourceCache();
Services.cache2.clear();
@@ -57,6 +109,10 @@ async function runTests(tests) {
for (let i = 0; i < test.items.length; i++) {
const item = test.items[i];
info(`start: ${test.title} (item ${i})`);
+
+ // Make sure the test starts in clean document.
+ await BrowserTestUtils.reloadTab(tab);
+
if (item.clearMemory) {
info("clear memory cache");
ChromeUtils.clearResourceCache();
@@ -65,7 +121,7 @@ async function runTests(tests) {
info("clear disk cache");
Services.cache2.clear();
}
- await SpecialPowers.spawn(browser, [item], contentTask);
+ await SpecialPowers.spawn(browser, [test, item], contentTask);
}
ok(true, "end: " + test.title);
@@ -79,6 +135,8 @@ add_task(async function testDiskCache() {
await SpecialPowers.pushPrefEnv({
set: [
["dom.expose_test_interfaces", true],
+ ["dom.script_loader.bytecode_cache.enabled", true],
+ ["dom.script_loader.bytecode_cache.strategy", 0],
["dom.script_loader.experimental.navigation_cache", false],
],
});
@@ -228,6 +286,8 @@ add_task(async function testMemoryCache() {
await SpecialPowers.pushPrefEnv({
set: [
["dom.expose_test_interfaces", true],
+ ["dom.script_loader.bytecode_cache.enabled", true],
+ ["dom.script_loader.bytecode_cache.strategy", 0],
["dom.script_loader.experimental.navigation_cache", true],
],
});
@@ -398,3 +458,355 @@ add_task(async function testMemoryCache() {
await SpecialPowers.popPrefEnv();
});
+
+add_task(async function testDiskCache_modules() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.expose_test_interfaces", true],
+ ["dom.script_loader.bytecode_cache.enabled", true],
+ ["dom.script_loader.bytecode_cache.strategy", 0],
+ ["dom.script_loader.experimental.navigation_cache", false],
+ ],
+ });
+
+ await runTests([
+ // A small module shouldn't be saved to the disk.
+ {
+ title: "small module",
+ module: true,
+ items: [
+ {
+ file: "file_js_cache_small.js",
+ events: [
+ ev("load:source", "file_js_cache_small.js"),
+ ev("evaluate:module", "file_js_cache_small.js"),
+ ev("diskcache:disabled", "file_js_cache_small.js"),
+ ],
+ },
+ {
+ file: "file_js_cache_small.js",
+ events: [
+ ev("load:source", "file_js_cache_small.js"),
+ ev("evaluate:module", "file_js_cache_small.js"),
+ ev("diskcache:disabled", "file_js_cache_small.js"),
+ ],
+ },
+ {
+ file: "file_js_cache_small.js",
+ events: [
+ ev("load:source", "file_js_cache_small.js"),
+ ev("evaluate:module", "file_js_cache_small.js"),
+ ev("diskcache:disabled", "file_js_cache_small.js"),
+ ],
+ },
+ {
+ file: "file_js_cache_small.js",
+ events: [
+ ev("load:source", "file_js_cache_small.js"),
+ ev("evaluate:module", "file_js_cache_small.js"),
+ ev("diskcache:disabled", "file_js_cache_small.js"),
+ ],
+ },
+ ],
+ },
+
+ // A large module file should be saved to the disk.
+ {
+ title: "large module",
+ module: true,
+ items: [
+ {
+ file: "file_js_cache_large.js",
+ events: [
+ ev("load:source", "file_js_cache_large.js"),
+ ev("evaluate:module", "file_js_cache_large.js"),
+ ev("diskcache:disabled", "file_js_cache_large.js"),
+ ],
+ },
+ {
+ file: "file_js_cache_large.js",
+ events: [
+ ev("load:source", "file_js_cache_large.js"),
+ ev("evaluate:module", "file_js_cache_large.js"),
+ ev("diskcache:disabled", "file_js_cache_large.js"),
+ ],
+ },
+ {
+ file: "file_js_cache_large.js",
+ events: [
+ ev("load:source", "file_js_cache_large.js"),
+ ev("evaluate:module", "file_js_cache_large.js"),
+ ev("diskcache:disabled", "file_js_cache_large.js"),
+ ],
+ },
+ {
+ file: "file_js_cache_large.js",
+ events: [
+ ev("load:source", "file_js_cache_large.js"),
+ ev("evaluate:module", "file_js_cache_large.js"),
+ ev("diskcache:register", "file_js_cache_large.js"),
+ ev("diskcache:saved", "file_js_cache_large.js", false),
+ ],
+ },
+ ],
+ },
+
+ // All imported modules should be saved to the disk.
+ {
+ title: "imported modules",
+ module: true,
+ items: [
+ {
+ file: "file_js_cache_importer.mjs",
+ events: [
+ ev("load:source", "file_js_cache_importer.mjs"),
+ ev("load:source", "file_js_cache_imported1.mjs", false),
+ ev("load:source", "file_js_cache_imported2.mjs", false),
+ ev("load:source", "file_js_cache_imported3.mjs", false),
+ ev("evaluate:module", "file_js_cache_importer.mjs"),
+ ev("diskcache:disabled", "file_js_cache_importer.mjs"),
+ // non-top-level modules that don't pass the condition
+ // don't emit events.
+ ],
+ },
+ {
+ file: "file_js_cache_importer.mjs",
+ events: [
+ ev("load:source", "file_js_cache_importer.mjs"),
+ ev("load:source", "file_js_cache_imported1.mjs", false),
+ ev("load:source", "file_js_cache_imported2.mjs", false),
+ ev("load:source", "file_js_cache_imported3.mjs", false),
+ ev("evaluate:module", "file_js_cache_importer.mjs"),
+ ev("diskcache:disabled", "file_js_cache_importer.mjs"),
+ ],
+ },
+ {
+ file: "file_js_cache_importer.mjs",
+ events: [
+ ev("load:source", "file_js_cache_importer.mjs"),
+ ev("load:source", "file_js_cache_imported1.mjs", false),
+ ev("load:source", "file_js_cache_imported2.mjs", false),
+ ev("load:source", "file_js_cache_imported3.mjs", false),
+ ev("evaluate:module", "file_js_cache_importer.mjs"),
+ ev("diskcache:disabled", "file_js_cache_importer.mjs"),
+ ],
+ },
+ {
+ file: "file_js_cache_importer.mjs",
+ events: [
+ ev("load:source", "file_js_cache_importer.mjs"),
+ ev("load:source", "file_js_cache_imported1.mjs", false),
+ ev("load:source", "file_js_cache_imported2.mjs", false),
+ ev("load:source", "file_js_cache_imported3.mjs", false),
+ ev("evaluate:module", "file_js_cache_importer.mjs"),
+ ev("diskcache:register", "file_js_cache_importer.mjs"),
+ ev("diskcache:register", "file_js_cache_imported1.mjs", false),
+ ev("diskcache:register", "file_js_cache_imported2.mjs", false),
+ ev("diskcache:register", "file_js_cache_imported3.mjs", false),
+ ev("diskcache:saved", "file_js_cache_importer.mjs", false),
+ ev("diskcache:saved", "file_js_cache_imported1.mjs", false),
+ ev("diskcache:saved", "file_js_cache_imported2.mjs", false),
+ ev("diskcache:saved", "file_js_cache_imported3.mjs", false),
+ ],
+ },
+ {
+ file: "file_js_cache_importer.mjs",
+ events: [
+ ev("load:diskcache", "file_js_cache_importer.mjs"),
+ ev("load:diskcache", "file_js_cache_imported1.mjs", false),
+ ev("load:diskcache", "file_js_cache_imported2.mjs", false),
+ ev("load:diskcache", "file_js_cache_imported3.mjs", false),
+ ev("evaluate:module", "file_js_cache_importer.mjs"),
+ ev("diskcache:disabled", "file_js_cache_importer.mjs"),
+ ],
+ },
+ ],
+ },
+ ]);
+
+ await SpecialPowers.popPrefEnv();
+});
+
+add_task(async function testMemoryCache_modules() {
+ if (!AppConstants.NIGHTLY_BUILD) {
+ todo(false, "navigation cache is not yet enabled on non-nightly");
+ return;
+ }
+
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.expose_test_interfaces", true],
+ ["dom.script_loader.bytecode_cache.enabled", true],
+ ["dom.script_loader.bytecode_cache.strategy", 0],
+ ["dom.script_loader.experimental.navigation_cache", true],
+ ],
+ });
+
+ await runTests([
+ // A small module shouldn't be saved to the disk.
+ {
+ title: "small module",
+ module: true,
+ items: [
+ {
+ file: "file_js_cache_small.js",
+ events: [
+ ev("load:source", "file_js_cache_small.js"),
+ ev("memorycache:saved", "file_js_cache_small.js"),
+ ev("evaluate:module", "file_js_cache_small.js"),
+ ev("diskcache:noschedule"),
+ ],
+ },
+ {
+ file: "file_js_cache_small.js",
+ events: [
+ ev("load:memorycache", "file_js_cache_small.js"),
+ ev("evaluate:module", "file_js_cache_small.js"),
+ ev("diskcache:noschedule"),
+ ],
+ },
+ {
+ file: "file_js_cache_small.js",
+ events: [
+ ev("load:memorycache", "file_js_cache_small.js"),
+ ev("evaluate:module", "file_js_cache_small.js"),
+ ev("diskcache:noschedule"),
+ ],
+ },
+ {
+ file: "file_js_cache_small.js",
+ events: [
+ ev("load:memorycache", "file_js_cache_small.js"),
+ ev("evaluate:module", "file_js_cache_small.js"),
+ ev("diskcache:noschedule"),
+ ],
+ },
+ ],
+ },
+
+ // A large module file should be saved to the disk.
+ {
+ title: "large module",
+ module: true,
+ items: [
+ {
+ file: "file_js_cache_large.js",
+ events: [
+ ev("load:source", "file_js_cache_large.js"),
+ ev("memorycache:saved", "file_js_cache_large.js"),
+ ev("evaluate:module", "file_js_cache_large.js"),
+ ev("diskcache:noschedule"),
+ ],
+ },
+ {
+ file: "file_js_cache_large.js",
+ events: [
+ ev("load:memorycache", "file_js_cache_large.js"),
+ ev("evaluate:module", "file_js_cache_large.js"),
+ ev("diskcache:noschedule"),
+ ],
+ },
+ {
+ file: "file_js_cache_large.js",
+ events: [
+ ev("load:memorycache", "file_js_cache_large.js"),
+ ev("evaluate:module", "file_js_cache_large.js"),
+ ev("diskcache:noschedule"),
+ ],
+ },
+ {
+ file: "file_js_cache_large.js",
+ events: [
+ ev("load:memorycache", "file_js_cache_large.js"),
+ ev("evaluate:module", "file_js_cache_large.js"),
+ ev("diskcache:saved", "file_js_cache_large.js", false),
+ ],
+ },
+ {
+ file: "file_js_cache_large.js",
+ events: [
+ ev("load:memorycache", "file_js_cache_large.js"),
+ ev("evaluate:module", "file_js_cache_large.js"),
+ ev("diskcache:noschedule"),
+ ],
+ },
+ ],
+ },
+
+ // All imported modules should be saved to the disk.
+ {
+ title: "imported modules",
+ module: true,
+ items: [
+ {
+ file: "file_js_cache_importer.mjs",
+ events: [
+ ev("load:source", "file_js_cache_importer.mjs"),
+ ev("memorycache:saved", "file_js_cache_importer.mjs"),
+ ev("load:source", "file_js_cache_imported1.mjs", false),
+ ev("memorycache:saved", "file_js_cache_imported1.mjs", false),
+ ev("load:source", "file_js_cache_imported2.mjs", false),
+ ev("memorycache:saved", "file_js_cache_imported2.mjs", false),
+ ev("load:source", "file_js_cache_imported3.mjs", false),
+ ev("memorycache:saved", "file_js_cache_imported3.mjs", false),
+ ev("evaluate:module", "file_js_cache_importer.mjs"),
+ ev("diskcache:noschedule"),
+ ],
+ },
+ {
+ file: "file_js_cache_importer.mjs",
+ events: [
+ ev("load:memorycache", "file_js_cache_importer.mjs"),
+ ev("load:memorycache", "file_js_cache_imported1.mjs", false),
+ ev("load:memorycache", "file_js_cache_imported2.mjs", false),
+ ev("load:memorycache", "file_js_cache_imported3.mjs", false),
+ ev("evaluate:module", "file_js_cache_importer.mjs"),
+ ev("diskcache:noschedule"),
+ ],
+ },
+ {
+ file: "file_js_cache_importer.mjs",
+ events: [
+ ev("load:memorycache", "file_js_cache_importer.mjs"),
+ ev("load:memorycache", "file_js_cache_imported1.mjs", false),
+ ev("load:memorycache", "file_js_cache_imported2.mjs", false),
+ ev("load:memorycache", "file_js_cache_imported3.mjs", false),
+ ev("evaluate:module", "file_js_cache_importer.mjs"),
+ ev("diskcache:noschedule"),
+ ],
+ },
+ {
+ file: "file_js_cache_importer.mjs",
+ events: [
+ ev("load:memorycache", "file_js_cache_importer.mjs"),
+ ev("load:memorycache", "file_js_cache_imported1.mjs", false),
+ ev("load:memorycache", "file_js_cache_imported2.mjs", false),
+ ev("load:memorycache", "file_js_cache_imported3.mjs", false),
+ ev("evaluate:module", "file_js_cache_importer.mjs"),
+ // SharedScriptCache iterates over unordered hashmap while
+ // saving.
+ unordered([
+ ev("diskcache:saved", "file_js_cache_importer.mjs", false),
+ ev("diskcache:saved", "file_js_cache_imported1.mjs", false),
+ ev("diskcache:saved", "file_js_cache_imported2.mjs", false),
+ ev("diskcache:saved", "file_js_cache_imported3.mjs", false),
+ ]),
+ ],
+ },
+ {
+ file: "file_js_cache_importer.mjs",
+ events: [
+ ev("load:memorycache", "file_js_cache_importer.mjs"),
+ ev("load:memorycache", "file_js_cache_imported1.mjs", false),
+ ev("load:memorycache", "file_js_cache_imported2.mjs", false),
+ ev("load:memorycache", "file_js_cache_imported3.mjs", false),
+ ev("evaluate:module", "file_js_cache_importer.mjs"),
+ ev("diskcache:noschedule"),
+ ],
+ },
+ ],
+ },
+ ]);
+
+ await SpecialPowers.popPrefEnv();
+});
diff --git a/dom/base/test/file_js_cache_imported1.mjs b/dom/base/test/file_js_cache_imported1.mjs
@@ -0,0 +1,18 @@
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+
+export function func1() {}
diff --git a/dom/base/test/file_js_cache_imported2.mjs b/dom/base/test/file_js_cache_imported2.mjs
@@ -0,0 +1,22 @@
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+
+import { func3 } from "./file_js_cache_imported3.mjs";
+
+export function func2() {
+ func3();
+}
diff --git a/dom/base/test/file_js_cache_imported3.mjs b/dom/base/test/file_js_cache_imported3.mjs
@@ -0,0 +1,18 @@
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+
+export function func3() {}
diff --git a/dom/base/test/file_js_cache_importer.mjs b/dom/base/test/file_js_cache_importer.mjs
@@ -0,0 +1,19 @@
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+// A large comment block to make the file size pass the condition.
+
+import { func1 } from "./file_js_cache_imported1.mjs";
+import { func2 } from "./file_js_cache_imported2.mjs";