Repository navigation
test(conformance): judge the Network tab in three BiDi modes, and fix the Python race it found - #440
vishnuv688 wants to merge 2 commits into
Conversation
The suite ran each scenario with and without BiDi and served no requests in either, so an adapter that withheld the page's network rows whenever BiDi was attached passed. The mode Appium's XCUITest shim presents, a subscription that delivers nothing, was not modelled, and Safari's was modelled with nothing to list. The fake browser now loads two subresources with every document and records them as ground truth, drains them as Resource Timing rows the way the page collector reports them, and runs every scenario in three modes: live (events delivered, as Chrome), silent (subscribed, nothing delivered, as the Appium shim) and off (no socket, as Safari). Three rules hold each adapter to listing every load, listing it once, and stating a row's source where nothing but the page could have served it; the first two are separate so the declared no-BiDi gaps can cover a withheld load without excusing a doubled one. The cross-adapter agreement compares the requests listed as well, and a gap explains a difference there only where the holder's own run shows the load withheld and the other adapter's run is clean. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Selenium delivers every BiDi event callback on a thread of its own, so a request's beforeRequestSent and responseCompleted handlers ran concurrently. When the response handler won, the completed row reached the dashboard first and the late pending row replaced it by id, leaving a served request with no status or headers. The conformance rule request-row-carries-what-the-browser-served caught it about one run in five. One lock now covers the three network handlers. A response or fetch error that arrives before its request is held, keyed by request id and redirect count so a held hop cannot close the next one, and replayed right after the request's own frame instead of being dropped. At most BIDI_EARLY_EVENT_LIMIT are held, and the teardown summary counts the ones whose request never arrived. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
[Medium impact] Adds network request tracking to the conformance test suite. The PR appears safe to merge, with a non-blocking correction needed in the teardown summary. Findings
SummaryThe conformance suite now checks network rows in three BiDi modes.
One non-blocking issue remains: the teardown summary labels evicted responses as requests never seen. Diagram%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[BiDi network callback] --> B[Acquire shared lock]
B --> C{Request or closing event?}
C -->|Request| D[Send pending row]
D --> E{Matching held event?}
E -->|Yes| F[Send completed or failed row]
C -->|Closing event| G{Pending request exists?}
G -->|Yes| F
G -->|No| H[Hold by request id and redirect count]
H --> I[Evict oldest entry above cap]
Reviews (1) · Last reviewed commit: "fix(selenium-devtools-py): send a reques..." · Reviewed by Greptile |
| captured = stats.get("captured", 0) | ||
| unanswered = len(stats.get("pending") or ()) | ||
| if not captured and not unanswered: | ||
| orphaned = len(stats.get("early") or ()) + stats.get("early_dropped", 0) |
There was a problem hiding this comment.
Dropped responses get mislabeled
network_summary counts every evicted early response as one whose request was never seen. If the held set exceeds BIDI_EARLY_EVENT_LIMIT and those requests arrive afterward, their rows are listed, but early_dropped never decreases. The summary then blames missing request events instead of dropped responses. Report evicted responses separately from responses still waiting for their request.
Summary
The conformance suite now checks the Network tab, and doing so exposed a race in the Python adapter's BiDi network capture. This PR carries both.
Conformance: three BiDi modes
Before this change, every scenario ran with and without BiDi but served no requests in either. An adapter that withheld the page's network rows whenever BiDi was attached therefore passed.
The fake browser now loads two subresources with every document and records them as ground truth. It reports them as Resource Timing rows, the way the page collector does. Every scenario runs in three modes:
Three new rules hold each adapter to the following:
The first two are separate rules. That way a declared no-BiDi gap can excuse a withheld load without excusing a doubled one. The cross-adapter agreement now compares the requests listed too.
Fix: Python BiDi events out of order
Selenium runs each BiDi event callback on a new thread. A request's
beforeRequestSentandresponseCompletedhandlers could therefore run concurrently. When the response won, the completed row reached the dashboard first. The late pending row then replaced it by id, and the request was left with no status or headers.With real Chrome this shows up as a served request stuck as pending in the Network tab. The new rule
request-row-carries-what-the-browser-servedcaught it in about one run in five.The fix:
A
changes/fragment records it as a patch.Testing
pnpm test: green in 6 of 6 runs.pnpm lintandpnpm buildboth pass.Not verified against a real browser. The race was reproduced and fixed against the conformance suite's fake BiDi transport, which delivers events on separate threads the same way selenium does.
🤖 Generated with Claude Code