From ac8449163895c20d7c3a147638a1f215a3a2659d Mon Sep 17 00:00:00 2001 From: bherbruck Date: Wed, 14 Oct 2026 02:54:45 -0400 Subject: [PATCH] test: web-offline-test reads the worker's caches without opening them (#982) The state probe opened every cache it listed; when a worker deleted one in between, the open made it again, empty, and the next wait timed out. The probe now reads each cache with caches.match over the builds' files and marks, which never makes a cache (3 failures in 20 runs before, 0 in 30 after). The worker only ever starts a complete build. Co-Authored-By: Claude Opus 5.5 --- xtask/src/web_offline_test.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/xtask/src/web_offline_test.rs b/xtask/src/web_offline_test.rs index 93a1c242..8b77b5fa 100644 --- a/xtask/src/web_offline_test.rs +++ b/xtask/src/web_offline_test.rs @@ -113,7 +113,15 @@ pub fn run(root: &Path, site: Option<&str>, required: bool) -> Result<(), String let server = Server::start(v2.clone())?; let browser = Chrome::launch(&chrome, &work.0.join("profile"))?; let mut cdp = browser.connect()?; - let state_js = STATE_JS.replace("STARTED", started); + // Every file any build keeps, by name (the state counts a cache's entries among them). + let mut files = std::collections::BTreeSet::new(); + for dir in [&v1, &v2, &v3, &v4, &v5, &v6] { + let path = dir.join("build.json"); + let build: Value = + std::fs::read(&path).ok().and_then(|b| serde_json::from_slice(&b).ok()).ok_or_else(|| format!("{}: not readable", path.display()))?; + files.extend(build["files"].as_array().into_iter().flatten().filter_map(|f| f["file"].as_str().map(str::to_string))); + } + let state_js = STATE_JS.replace("STARTED", started).replace("BUILD_FILES", &json!(files.into_iter().collect::>()).to_string()); let count = std::cell::Cell::new(0); let launch_at = |cdp: &mut Cdp, at: &str, build: &str| -> Result { let n = count.get() + 1; @@ -287,13 +295,18 @@ const STATE_JS: &str = "(async () => { const scope = new URL('.', location.href).href; const prefix = 'solidcraft-app ' + new URL(scope).pathname + ' '; const caches_ = {}, kept = {}, retired = [], complete = []; + // Each cache is read with `caches.match`, never opened: opening a cache that a worker has just + // deleted makes it again (empty), and nothing would delete it after that. + const marks = ['build-complete', 'build-retired']; for (const k of await caches.keys()) { if (!k.startsWith(prefix)) continue; - const c = await caches.open(k); - caches_[k] = (await c.keys()).length; - if (await c.match(new URL('build-complete', location.href).href)) complete.push(k); - if (await c.match(new URL('build-retired', location.href).href)) retired.push(k); - else kept[k] = caches_[k]; + const here = async (file) => Boolean(await caches.match(new URL(file, scope).href, { cacheName: k })); + let n = 0; + for (const file of BUILD_FILES.concat(marks)) if (await here(file)) n += 1; + caches_[k] = n; + if (await here('build-complete')) complete.push(k); + if (await here('build-retired')) retired.push(k); + else kept[k] = n; } const filling = (await navigator.locks.query()).held.map((l) => l.name).filter((n) => n.startsWith(prefix)); return { search: location.search, ready: document.readyState, build: document.documentElement.dataset.build ?? null,