test: align paired browser settle timing
This commit is contained in:
1 parent
b03c663952
commit
9f33db610e
3 files changed
+39
-6
No files matched your search
@@ -1242,11 +1242,24 @@ impl Page {
|
|||||||
}
|
}
|
||||||
let hydration_snapshot = self.js.as_ref().and_then(capture_hydration_snapshot);
|
let hydration_snapshot = self.js.as_ref().and_then(capture_hydration_snapshot);
|
||||||
if let Some(js) = &mut self.js {
|
if let Some(js) = &mut self.js {
|
||||||
// Bounded against both async idle and synchronous microtask storms:
|
if std::env::var_os("OBSCURA_STRICT_SETTLE").is_some() {
|
||||||
// a plain tokio timeout cannot preempt a page that pins the thread
|
// Paired browser measurements need the same wall-clock interval
|
||||||
// inside V8 (the real-world SPA hang), so settle drives the loop
|
// after load in both engines. The ordinary product path returns
|
||||||
// through the watchdog-guarded path.
|
// as soon as the loop is idle for speed; strict mode pumps all
|
||||||
|
// pending work, then retains the remaining wall-clock delay.
|
||||||
|
let started = tokio::time::Instant::now();
|
||||||
let _ = js.run_event_loop_bounded(max_ms).await;
|
let _ = js.run_event_loop_bounded(max_ms).await;
|
||||||
|
let requested = tokio::time::Duration::from_millis(max_ms);
|
||||||
|
let elapsed = started.elapsed();
|
||||||
|
if elapsed < requested {
|
||||||
|
tokio::time::sleep(requested - elapsed).await;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Bounded against both async idle and synchronous microtask
|
||||||
|
// storms: a plain tokio timeout cannot preempt a page that pins
|
||||||
|
// the thread inside V8, so settle uses the watchdog path.
|
||||||
|
let _ = js.run_event_loop_bounded(max_ms).await;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(before) = hydration_snapshot {
|
if let Some(before) = hydration_snapshot {
|
||||||
let _ = restore_hydration_snapshot_if_needed(js, before);
|
let _ = restore_hydration_snapshot_if_needed(js, before);
|
||||||
|
|||||||
@@ -970,7 +970,18 @@ async fn wait_for_selector(page: &mut Page, selector: &str, timeout_secs: u64) -
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
|
// The selector may be created by a timer, dynamic import, or fetch
|
||||||
|
// completion. Sleeping without pumping V8 makes those callbacks unable
|
||||||
|
// to run, so a valid selector wait always times out. Drive one bounded
|
||||||
|
// event-loop slice, then retain a 100ms polling cadence if it returned
|
||||||
|
// idle immediately.
|
||||||
|
let slice_started = tokio::time::Instant::now();
|
||||||
|
page.settle(100).await;
|
||||||
|
let spent = slice_started.elapsed();
|
||||||
|
let cadence = tokio::time::Duration::from_millis(100);
|
||||||
|
if spent < cadence {
|
||||||
|
tokio::time::sleep(cadence - spent).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,13 @@ def capture_obscura(binary, url, screenshot, log, width, height, settle_ms):
|
|||||||
OBSCURA_SHOT_W=str(width),
|
OBSCURA_SHOT_W=str(width),
|
||||||
OBSCURA_SHOT_H=str(height),
|
OBSCURA_SHOT_H=str(height),
|
||||||
OBSCURA_ALLOW_PRIVATE_NETWORK="1",
|
OBSCURA_ALLOW_PRIVATE_NETWORK="1",
|
||||||
|
# Product fetches return early when the event loop becomes idle. Paired
|
||||||
|
# captures instead need the same complete post-load wall interval that
|
||||||
|
# Playwright's wait_for_timeout below uses.
|
||||||
|
OBSCURA_STRICT_SETTLE="1",
|
||||||
|
# Match Playwright's 50-second goto allowance. This is the browser
|
||||||
|
# engine's millisecond ceiling, distinct from the CLI's seconds unit.
|
||||||
|
OBSCURA_NAV_TIMEOUT_MS="50000",
|
||||||
)
|
)
|
||||||
command = [
|
command = [
|
||||||
binary,
|
binary,
|
||||||
@@ -62,7 +69,7 @@ def capture_obscura(binary, url, screenshot, log, width, height, settle_ms):
|
|||||||
"--screenshot",
|
"--screenshot",
|
||||||
str(screenshot),
|
str(screenshot),
|
||||||
"--timeout",
|
"--timeout",
|
||||||
"50000",
|
"50",
|
||||||
"--wait",
|
"--wait",
|
||||||
f"{settle_ms / 1000:g}",
|
f"{settle_ms / 1000:g}",
|
||||||
]
|
]
|
||||||
@@ -119,6 +126,8 @@ def main():
|
|||||||
"started_utc": datetime.now(timezone.utc).isoformat(),
|
"started_utc": datetime.now(timezone.utc).isoformat(),
|
||||||
"viewport": {"width": args.width, "height": args.height, "dpr": 1},
|
"viewport": {"width": args.width, "height": args.height, "dpr": 1},
|
||||||
"settle_ms_after_load": args.settle_ms,
|
"settle_ms_after_load": args.settle_ms,
|
||||||
|
"settle_semantics": "full wall-clock interval while pumping each engine",
|
||||||
|
"navigation_timeout_ms": 50000,
|
||||||
"obscura": binary_version(args.obscura_bin),
|
"obscura": binary_version(args.obscura_bin),
|
||||||
"baseline": binary_version(args.baseline_bin) if args.baseline_bin else None,
|
"baseline": binary_version(args.baseline_bin) if args.baseline_bin else None,
|
||||||
"pages": [],
|
"pages": [],
|
||||||
|
|||||||
Reference in new issue
Block a user