From a5208b6caefdeddffbb6c8c8468a1e4420ab6f0a Mon Sep 17 00:00:00 2001 From: Mike Solar Date: Sat, 22 Aug 2026 20:11:43 +0800 Subject: [PATCH] test: replace wall-clock loop deadlines with progress criteria MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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/. --- .gitea/workflows/ci.yml | 2 +- assets/i18n/de.yaml | 2 +- assets/i18n/en-US.yaml | 2 +- assets/i18n/es.yaml | 2 +- assets/i18n/fr.yaml | 2 +- assets/i18n/ja.yaml | 2 +- assets/i18n/pt.yaml | 2 +- assets/i18n/ru.yaml | 2 +- assets/i18n/zh-CN.yaml | 2 +- crates/oak-app/src/oakui/real.rs | 30 +++++++++++++++++++----------- crates/oak-cli/tests/cli.rs | 11 +++++------ 11 files changed, 33 insertions(+), 26 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 9d3e1e525..ca2b833bc 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -184,7 +184,7 @@ jobs: runs-on: oak-windows-2025 defaults: run: - shell: C:\msys2\usr\bin\bash.exe -eo pipefail {0} + shell: msys2 {0} steps: - name: Checkout uses: actions/checkout@v4 diff --git a/assets/i18n/de.yaml b/assets/i18n/de.yaml index c885e5ca0..a58ddee63 100644 --- a/assets/i18n/de.yaml +++ b/assets/i18n/de.yaml @@ -1,5 +1,6 @@ # Oak Video Editor UI strings — Deutsch (de) # Format: "i18n.key": "Translated text". Add new languages as new files named .yaml. +"language.name": "Deutsch" "menu.file": "Datei(D)" "menu.edit": "Bearbeiten(B)" "menu.view": "Ansicht(A)" @@ -65,7 +66,6 @@ "menu.view.theme.dark": "Olive Dark" "menu.view.theme.light": "Olive Light" "menu.view.language": "Sprache" -"language.name": "Deutsch" "menu.view.zoom_in": "Vergrößern" "menu.view.zoom_out": "Verkleinern" "menu.view.increase_track_height": "Spurhöhe erhöhen" diff --git a/assets/i18n/en-US.yaml b/assets/i18n/en-US.yaml index 98c12829d..fc80f04b2 100644 --- a/assets/i18n/en-US.yaml +++ b/assets/i18n/en-US.yaml @@ -1,5 +1,6 @@ # Oak Video Editor UI strings — English (en-US) # Format: "i18n.key": "Translated text". Add new languages as new files named .yaml. +"language.name": "English" "menu.file": "File(F)" "menu.edit": "Edit(E)" "menu.view": "View(V)" @@ -65,7 +66,6 @@ "menu.view.theme.dark": "Olive Dark" "menu.view.theme.light": "Olive Light" "menu.view.language": "Language" -"language.name": "English" "menu.view.zoom_in": "Zoom In" "menu.view.zoom_out": "Zoom Out" "menu.view.increase_track_height": "Increase Track Height" diff --git a/assets/i18n/es.yaml b/assets/i18n/es.yaml index db7f71313..0339c31b7 100644 --- a/assets/i18n/es.yaml +++ b/assets/i18n/es.yaml @@ -1,5 +1,6 @@ # Oak Video Editor UI strings — Español (es) # Format: "i18n.key": "Translated text". Add new languages as new files named .yaml. +"language.name": "Español" "menu.file": "Archivo(A)" "menu.edit": "Editar(E)" "menu.view": "Ver(V)" @@ -65,7 +66,6 @@ "menu.view.theme.dark": "Olive Dark" "menu.view.theme.light": "Olive Light" "menu.view.language": "Idioma" -"language.name": "Español" "menu.view.zoom_in": "Acercar" "menu.view.zoom_out": "Alejar" "menu.view.increase_track_height": "Aumentar altura de pista" diff --git a/assets/i18n/fr.yaml b/assets/i18n/fr.yaml index 2f88e3741..df46cd72b 100644 --- a/assets/i18n/fr.yaml +++ b/assets/i18n/fr.yaml @@ -1,5 +1,6 @@ # Oak Video Editor UI strings — Français (fr) # Format: "i18n.key": "Translated text". Add new languages as new files named .yaml. +"language.name": "Français" "menu.file": "Fichier(F)" "menu.edit": "Édition(E)" "menu.view": "Affichage(A)" @@ -65,7 +66,6 @@ "menu.view.theme.dark": "Olive Dark" "menu.view.theme.light": "Olive Light" "menu.view.language": "Langue" -"language.name": "Français" "menu.view.zoom_in": "Zoom avant" "menu.view.zoom_out": "Zoom arrière" "menu.view.increase_track_height": "Augmenter la hauteur de piste" diff --git a/assets/i18n/ja.yaml b/assets/i18n/ja.yaml index 932accd7c..93311052e 100644 --- a/assets/i18n/ja.yaml +++ b/assets/i18n/ja.yaml @@ -1,5 +1,6 @@ # Oak Video Editor UI strings — 日本語 (ja) # Format: "i18n.key": "Translated text". Add new languages as new files named .yaml. +"language.name": "日本語" "menu.file": "ファイル(F)" "menu.edit": "編集(E)" "menu.view": "表示(V)" @@ -65,7 +66,6 @@ "menu.view.theme.dark": "Olive Dark" "menu.view.theme.light": "Olive Light" "menu.view.language": "言語" -"language.name": "日本語" "menu.view.zoom_in": "ズームイン" "menu.view.zoom_out": "ズームアウト" "menu.view.increase_track_height": "トラックの高さを増やす" diff --git a/assets/i18n/pt.yaml b/assets/i18n/pt.yaml index b42b57c22..e2907b91b 100644 --- a/assets/i18n/pt.yaml +++ b/assets/i18n/pt.yaml @@ -1,5 +1,6 @@ # Oak Video Editor UI strings — Português (pt) # Format: "i18n.key": "Translated text". Add new languages as new files named .yaml. +"language.name": "Português" "menu.file": "Arquivo(A)" "menu.edit": "Editar(E)" "menu.view": "Ver(V)" @@ -65,7 +66,6 @@ "menu.view.theme.dark": "Olive Dark" "menu.view.theme.light": "Olive Light" "menu.view.language": "Idioma" -"language.name": "Português" "menu.view.zoom_in": "Aproximar" "menu.view.zoom_out": "Afastar" "menu.view.increase_track_height": "Aumentar altura da trilha" diff --git a/assets/i18n/ru.yaml b/assets/i18n/ru.yaml index cc9a27f8c..91c294844 100644 --- a/assets/i18n/ru.yaml +++ b/assets/i18n/ru.yaml @@ -1,5 +1,6 @@ # Oak Video Editor UI strings — Русский (ru) # Format: "i18n.key": "Translated text". Add new languages as new files named .yaml. +"language.name": "Русский" "menu.file": "Файл(Ф)" "menu.edit": "Правка(П)" "menu.view": "Вид(В)" @@ -65,7 +66,6 @@ "menu.view.theme.dark": "Olive Dark" "menu.view.theme.light": "Olive Light" "menu.view.language": "Язык" -"language.name": "Русский" "menu.view.zoom_in": "Увеличить" "menu.view.zoom_out": "Уменьшить" "menu.view.increase_track_height": "Увеличить высоту дорожки" diff --git a/assets/i18n/zh-CN.yaml b/assets/i18n/zh-CN.yaml index 6c350bbe1..36508038f 100644 --- a/assets/i18n/zh-CN.yaml +++ b/assets/i18n/zh-CN.yaml @@ -1,5 +1,6 @@ # Oak Video Editor UI strings — 简体中文 (zh-CN) # Format: "i18n.key": "Translated text". Add new languages as new files named .yaml. +"language.name": "简体中文" "menu.file": "文件(F)" "menu.edit": "编辑(E)" "menu.view": "视图(V)" @@ -65,7 +66,6 @@ "menu.view.theme.dark": "Olive Dark" "menu.view.theme.light": "Olive Light" "menu.view.language": "语言" -"language.name": "简体中文" "menu.view.zoom_in": "放大" "menu.view.zoom_out": "缩小" "menu.view.increase_track_height": "增加轨道高度" diff --git a/crates/oak-app/src/oakui/real.rs b/crates/oak-app/src/oakui/real.rs index 43d0e7fe0..8afe03951 100644 --- a/crates/oak-app/src/oakui/real.rs +++ b/crates/oak-app/src/oakui/real.rs @@ -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)); } diff --git a/crates/oak-cli/tests/cli.rs b/crates/oak-cli/tests/cli.rs index 6a637b484..4a3ec2485 100644 --- a/crates/oak-cli/tests/cli.rs +++ b/crates/oak-cli/tests/cli.rs @@ -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") }