diff --git a/crates/op-host-services/src/web_static/index.html b/crates/op-host-services/src/web_static/index.html index 216e2e106..a9a00de55 100644 --- a/crates/op-host-services/src/web_static/index.html +++ b/crates/op-host-services/src/web_static/index.html @@ -48,6 +48,11 @@ const errorPane = document.getElementById('op-error'); const markSmoke = (state) => { document.documentElement.setAttribute('data-op-smoke', state); + // Mirrored to the console for the headless browser smoke: the + // editor keeps a live SSE stream open, which freezes headless + // Chrome's virtual clock, so --dump-dom never flushes — stderr + // console logging is the only channel that arrives incrementally. + console.log(`op-smoke:${state}`); }; const showError = (msg) => { markSmoke('error'); diff --git a/crates/op-host-web/smoke/step-1b.html b/crates/op-host-web/smoke/step-1b.html index f3fc8ae7b..ef45a56e5 100644 --- a/crates/op-host-web/smoke/step-1b.html +++ b/crates/op-host-web/smoke/step-1b.html @@ -59,6 +59,10 @@ const status = document.getElementById('status'); const markSmoke = (state) => { document.documentElement.setAttribute('data-op-smoke', state); + // Console mirror for the headless smoke; see the host page note — + // stderr logging arrives incrementally while --dump-dom waits for + // a quiescence the live editor never reaches. + console.log(`op-smoke:${state}`); }; const log = (cls, msg) => { const block = document.createElement('pre'); diff --git a/tools/check-web-browser-smoke.sh b/tools/check-web-browser-smoke.sh index cec911bfe..34981a151 100755 --- a/tools/check-web-browser-smoke.sh +++ b/tools/check-web-browser-smoke.sh @@ -101,10 +101,22 @@ chrome_dump() { --enable-unsafe-swiftshader \ --window-size=1280,800 \ --user-data-dir="$profile" \ + --enable-logging=stderr \ --virtual-time-budget="${OPENPENCIL_BROWSER_SMOKE_VIRTUAL_TIME_MS:-15000}" \ --dump-dom "$url" >"$out" 2>"$out.stderr" & local chrome_pid=$! for _ in $(seq 1 "$timeout"); do + # The editor holds a live SSE stream, which pauses headless Chrome's + # virtual clock and keeps --dump-dom from ever flushing. The console + # mirror on stderr arrives incrementally, so it is the readiness + # signal; a successful mount synthesizes the DOM line the assertions + # expect. + if grep -Fq 'op-smoke:ok' "$out.stderr" 2>/dev/null; then + printf '\n' >"$out" + kill -9 "$chrome_pid" >/dev/null 2>&1 || true + wait "$chrome_pid" >/dev/null 2>&1 || true + return 0 + fi if grep -Fq 'data-op-smoke="ok"' "$out" 2>/dev/null; then kill "$chrome_pid" >/dev/null 2>&1 || true wait "$chrome_pid" >/dev/null 2>&1 || true @@ -116,7 +128,9 @@ chrome_dump() { fi sleep 1 done - kill "$chrome_pid" >/dev/null 2>&1 || true + # SIGKILL, not SIGTERM: a headless Chrome wedged mid-GPU-stall has been + # observed ignoring TERM, which turns the wait below into a permanent hang. + kill -9 "$chrome_pid" >/dev/null 2>&1 || true wait "$chrome_pid" >/dev/null 2>&1 || true printf 'headless Chrome timed out for %s after %ss\n' "$url" "$timeout" >&2 return 124