test(cdp): add Chromium-calibrated Inspector routing smoke

This commit is contained in:
ldm0
2026-08-17 00:40:39 +08:00
committed by Donough Liu
parent 9a3763405c
commit e097320650
5 changed files with 2274 additions and 1 deletions
+43
View File
@@ -107,6 +107,7 @@ Covered well:
response/event crossover, foreign flattened and legacy session rejection,
and staged peer-disconnect isolation.
- Raw CDP websocket command flow for `Runtime.evaluate(awaitPromise=true)` resolving page `fetch()`, timer-triggered `fetch()`, and WebSocket echo work without any follow-up client command; emitted `Runtime.executionContextCreated.uniqueId` round-tripping through DevTools-shaped `Runtime.evaluate` and `Runtime.callFunctionOn`; Chromium-calibrated pre-commit navigation suspension where DOM/Runtime/Debugger main-thread commands wait while `Performance.getMetrics`, `Runtime.terminateExecution`, and browser commands remain dispatchable; `Debugger.pause` responding before `Debugger.paused`, interrupting an in-flight `Runtime.evaluate`, and resuming that evaluation; commands queued behind a winning `Debugger.resume` completing through normal owner dispatch rather than synthetic cancellation; deterministic nested-function `Debugger.stepOut` response/resumed/caller-pause ordering; browser-global Tracing ownership across independent browser/page WebSocket frontends, including exactly one response for a synchronously completed start and the stop-before-start-ack `end response -> start error -> data -> complete` sequence; shared worker target discovery through `Target.getTargets`, worker-session `Runtime.executionContextCreated` / console log replay, and `Profiler.enable` / `Profiler.start` / `Profiler.stop` through `Target.setAutoAttach`; plus Chromium-calibrated DedicatedWorker target creation/update/attach ordering, exact worker-isolate Runtime/Console routing, `Inspector.workerScriptLoaded`, terminate, and owner-navigation cleanup.
- The optional raw `inspector-routing` group is the executable DevToolsSession boundary matrix. It covers per-session Main/IO FIFO and exactly-once completion, IO preemption of non-yielding JavaScript, all 13 methods in Chromium 147's `ShouldSendOnIO`, normal debugger-pause pumping of one mixed V8/Page/DOM Main receiver, instrumentation-pause IO-only behavior, navigation replacement, auxiliary-session detach, BrowserContext teardown with interrupts in flight, and `Page.crash`. Every scenario runs in an isolated target and records its Chromium-derived contract.
- The focused raw `agent-episode` group copies the recorded RL
`Runtime.evaluate(awaitPromise=true)` observe/fill/click path. It requires the
action response before destructive cross-document realm events, observes only
@@ -290,9 +291,39 @@ uv run moli-cdp-smoke --group dom-whitespace
uv run moli-cdp-smoke --group computed-style
uv run moli-cdp-smoke --group error-document
uv run moli-cdp-smoke --group url-policy
uv run moli-cdp-smoke --group inspector-routing
MOLI_SMOKE_GROUPS=protocol,websocket uv run moli-cdp-smoke
```
The Inspector routing group is intentionally outside the default selection.
Run one or more named contracts while iterating with
`MOLI_INSPECTOR_ROUTING_SCENARIOS`:
```bash
MOLI_INSPECTOR_ROUTING_SCENARIOS=raw_cdp_active_javascript_main_io_lane_matrix \
uv run moli-cdp-smoke --group inspector-routing
MOLI_INSPECTOR_ROUTING_SCENARIOS=raw_cdp_nested_v8_main_receiver_matrix,raw_cdp_nested_non_v8_main_receiver_matrix \
uv run moli-cdp-smoke --group inspector-routing
```
The Chromium 147 IO catalog exercised by the complete group is:
```text
Debugger.getPossibleBreakpoints Debugger.getScriptSource
Debugger.getStackTrace Debugger.pause
Debugger.removeBreakpoint Debugger.resume
Debugger.setBreakpoint Debugger.setBreakpointByUrl
Debugger.setBreakpointsActive Emulation.setScriptExecutionDisabled
Page.crash Performance.getMetrics
Runtime.terminateExecution
```
Main and IO are ordered independently; the smoke never requires relative
ordering between the two routes. A normal debugger pause must pump mixed V8
and non-V8 Main commands from the same session in send order. An
instrumentation pause must leave Main blocked and admit only IO work.
Run the same focused group against an already-running Chromium CDP endpoint:
```bash
@@ -301,6 +332,17 @@ uv run moli-cdp-smoke \
--group dom-snapshot
```
The complete `inspector-routing` group was calibrated on 2026-08-16 against
the local Chromium build `Chrome/147.0.7709.0`. Point the same command at a
Chromium remote-debugging endpoint to re-run the oracle before changing a
routing contract:
```bash
uv run moli-cdp-smoke \
--endpoint http://127.0.0.1:9222 \
--group inspector-routing
```
Run the Puppeteer group:
```bash
@@ -348,6 +390,7 @@ Useful environment variables:
- `MOLI_BIN`: path to the `moli` binary under test.
- `MOLI_CDP_PORT`: CDP server port. Defaults to a free local port.
- `MOLI_SMOKE_GROUPS`: comma-separated smoke group list. CLI `--group` takes precedence.
- `MOLI_INSPECTOR_ROUTING_SCENARIOS`: comma-separated scenario names within the optional `inspector-routing` group.
- `MOLI_SMOKE_TRACE=1`: print extra runner-side trace logs.
- `MOLI_SMOKE_TRACE_BG=1`: print background `moli serve` logs.
- `NODE`: Node executable used by optional Node client groups. Defaults to `node`.
+17
View File
@@ -474,6 +474,23 @@ class FixtureServer:
self.send_response(HTTPStatus.NO_CONTENT)
self.end_headers()
return
if route == "/inspector-routing-witness/reset":
outer.reset_request_count("/inspector-routing-witness/entered")
self._send_json({"enteredCount": 0})
return
if route == "/inspector-routing-witness/status":
self._send_json(
{
"enteredCount": outer.request_count(
"/inspector-routing-witness/entered"
)
}
)
return
if route == "/inspector-routing-witness/entered":
count = outer._increment_request_count(route)
self._send_text(f"entered-{count}")
return
if route == "/chromium-network-redirect-before-reset":
outer._increment_request_count(route)
self.send_response(HTTPStatus.FOUND)
File diff suppressed because it is too large Load Diff
+7 -1
View File
@@ -111,7 +111,13 @@ class RawCdpClient:
remaining = deadline - asyncio.get_running_loop().time()
if remaining <= 0:
raise RawCdpError(f"timed out waiting for CDP response id={message_id}; seen={seen[-20:]}")
message = await asyncio.wait_for(self.recv(), timeout=remaining)
try:
message = await asyncio.wait_for(self.recv(), timeout=remaining)
except TimeoutError as error:
raise RawCdpError(
f"timed out waiting for CDP response id={message_id}; "
f"seen={seen[-20:]}"
) from error
seen.append(message)
if message.get("id") == message_id:
if "error" in message:
+8
View File
@@ -34,6 +34,7 @@ from .groups.document_content import run_document_content_group
from .groups.emulation_storage import run_emulation_storage_group
from .groups.error_document import run_error_document_group
from .groups.fetch_runtime_teardown import run_fetch_runtime_teardown_group
from .groups.inspector_routing import run_inspector_routing_group
from .groups.layout_screenshot import run_layout_screenshot_group
from .groups.multi_client import run_multi_client_group
from .groups.multi_context import run_multi_context_group
@@ -77,6 +78,13 @@ class SmokeGroup:
RAW_GROUPS: tuple[SmokeGroup, ...] = (
SmokeGroup(
"inspector-routing",
"Chromium-calibrated active-JS IO interrupt, nested Main receiver, per-session FIFO, and non-V8 IO boundaries.",
"raw",
run_inspector_routing_group,
default=False,
),
SmokeGroup(
"url-policy",
"Hosted file URL rejection across raw Page.navigate and Runtime fetch/XHR without lifecycle, interception, or transport leakage.",