test: replace wall-clock loop deadlines with progress criteria
CI / Build & test (Windows) (push) Failing after 30s
CI / Build & test (Linux) (push) Canceled after 8m31s

Three real-engine throughput loops (thumbnail pipeline, full-res fill
behind the proxy, playback window supply) failed on machine slowness:
their pass/fail was a wall-clock Instant deadline, so a loaded machine
broke them for speed, not for a broken pipeline. Each loop now counts
engine pumps — machine-speed independent — and asserts the condition
within a bounded number of pumps. The two single-frame worker channel
receives keep a generous 60 s recv_timeout (a one-shot bounded
operation, not a throughput loop).

oak-cli: the integration fixtures moved with the app crate during the
workspace restructure; point the fixture helpers at
../oak-app/tests instead of the (now empty) repo-root tests/.
This commit is contained in:
2026-08-22 20:11:43 +08:00
parent c4705cbb2c
commit a5208b6cae
11 changed files with 33 additions and 26 deletions
+19 -11
View File
@@ -5829,8 +5829,12 @@ mod tests {
})
})
.expect("re-import the footage");
let deadline = std::time::Instant::now() + Duration::from_secs(20);
// Progress criterion, not wall time: the worker's async thumbnail
// installs after a bounded number of engine pumps (a wall-clock
// cap would fail on slow machines for machine speed).
let mut pumps = 0usize;
loop {
pumps += 1;
let thumbnail = cx.read(|app| {
engine
.read(app)
@@ -5847,8 +5851,8 @@ mod tests {
break;
}
assert!(
std::time::Instant::now() < deadline,
"the entry eventually carries a thumbnail path"
pumps < 5000,
"the entry eventually carries a thumbnail path (after {pumps} pumps)"
);
cx.update(|app| engine.update(app, |engine, _cx| engine.drain_thumbnails()));
std::thread::sleep(Duration::from_millis(10));
@@ -6318,7 +6322,7 @@ mod tests {
std::thread::spawn(move || RealEngine::full_res_worker(request, tx));
let event = rx
.recv_timeout(Duration::from_secs(20))
.recv_timeout(Duration::from_secs(60))
.expect("the worker delivers the frame after the project drop");
let bytes = event.image.as_bytes(0).expect("one frame");
assert_eq!(bytes.len(), 64 * 64 * 4, "full-res geometry");
@@ -6488,7 +6492,7 @@ mod tests {
std::thread::spawn(move || RealEngine::full_res_worker(request, tx));
let event = rx
.recv_timeout(Duration::from_secs(20))
.recv_timeout(Duration::from_secs(60))
.expect("the worker delivers the full-res frame");
assert_eq!(event.monitor, Monitor::Program);
assert_eq!(event.frame, 0);
@@ -6543,9 +6547,12 @@ mod tests {
// Drive the tick loop until the background fill lands and replaces
// the proxy in the display path.
// Progress criterion, not wall time: the fill lands after a bounded
// number of engine pumps (machine-speed independent).
let full_len = (width * height * 4) as usize;
let deadline = std::time::Instant::now() + Duration::from_secs(20);
let mut pumps = 0usize;
loop {
pumps += 1;
cx.update(|app| engine.update(app, |engine, cx| engine.tick(cx)));
let len = cx.read(|app| {
engine
@@ -6559,8 +6566,8 @@ mod tests {
break;
}
assert!(
std::time::Instant::now() < deadline,
"the full-res fill lands within the deadline (got {len} bytes)"
pumps < 5000,
"the full-res fill lands after a bounded number of pumps (got {len} bytes after {pumps})"
);
std::thread::sleep(Duration::from_millis(10));
}
@@ -6858,10 +6865,11 @@ mod tests {
// Start playback and drive the tick loop: the window must fill.
cx.update(|app| engine.update(app, |engine, cx| engine.play(Monitor::Program, cx)));
let deadline = std::time::Instant::now() + Duration::from_secs(30);
let mut filled = 0usize;
let mut hit = false;
let mut pumps = 0usize;
loop {
pumps += 1;
cx.update(|app| engine.update(app, |engine, cx| engine.tick(cx)));
let (slots, submitted) = cx.read(|app| {
let engine = engine.read(app);
@@ -6882,8 +6890,8 @@ mod tests {
break;
}
assert!(
std::time::Instant::now() < deadline,
"the playback window must supply playhead frames (peak cached {filled}, submitted {submitted})"
pumps < 5000,
"the playback window must supply playhead frames (peak cached {filled}, submitted {submitted}, after {pumps} pumps)"
);
std::thread::sleep(Duration::from_millis(10));
}
+5 -6
View File
@@ -42,17 +42,17 @@ fn bin() -> &'static str {
/// The fixture `.ove` file (relative to the workspace root).
fn fixture_project() -> PathBuf {
// The fixtures live with the app crate (moved there in the workspace
// restructure); oak-cli sits one level below oak-app.
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join("tests")
.join("../oak-app/tests")
.join("project_with_footage.ove")
}
/// The real media fixture.
fn fixture_media() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join("tests")
.join("../oak-app/tests")
.join("demo.mp4")
}
@@ -60,8 +60,7 @@ fn fixture_media() -> PathBuf {
/// no audio — fast end-to-end transcode coverage.
fn fixture_image() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join("tests")
.join("../oak-app/tests")
.join("img.png")
}