"""Launches the real MainWindow, loads the out-of-sync fixture, drives the actual marker -> offset -> export flow through the same code the UI buttons call (not a copy of the logic), and grabs screenshots so it can be visually checked instead of just trusting the app "should" work. """ import sys import time from pathlib import Path from PyQt6.QtWidgets import QApplication sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) import main as app_main ROOT = Path(__file__).resolve().parent.parent CLIP = str(ROOT / "test_media" / "sync_test_OUTOFSYNC_audio+350ms.mp4") OUT_DIR = ROOT / "test_media" / "seek_validation" EXPORT_OUT = str(OUT_DIR / "_ui_smoke_export.mp4") def main(): OUT_DIR.mkdir(exist_ok=True) qapp = QApplication(sys.argv) win = app_main.MainWindow() win.show() qapp.processEvents() win._load(CLIP) qapp.processEvents() time.sleep(0.3) qapp.processEvents() print(f"loaded: duration slider range = {win.source_panel.seek_slider.maximum()}ms") win.source_panel.player.seek(5.000) qapp.processEvents() win._mark_video() print(f"video mark: {win.video_mark:.3f}s -> label: {win.video_mark_label.text()}") win.source_panel.player.seek(5.350) qapp.processEvents() win._mark_audio() print(f"audio mark: {win.audio_mark:.3f}s -> label: {win.audio_mark_label.text()}") print(f"offset label: {win.offset_label.text()}") print(f"export button enabled: {win.export_button.isEnabled()}") assert win.export_button.isEnabled() # hr-seek lands on a nearby frame boundary (33.3ms steps @ 30fps), not # necessarily the single nearest one (observed some run-to-run variance # of a couple of frames, likely decode-order/B-frame timing rather than # anything this smoke test is meant to pin down -- exact precision is # covered separately by scripts/test_export_roundtrip.py's independent # ffmpeg-based check). Just confirm we're in the right neighborhood and # branch. assert abs(win.offset.delay_ms - (-350.0)) < 100 assert win.offset.target == "video" win.grab().save(str(OUT_DIR / "ui_smoke_after_marking.png")) # Drive export directly through the worker (skip the file-save dialog, # which needs a real user) but through the same ExportWorker class the # button uses. done = {"ok": False, "err": None} def on_ok(result): done["ok"] = True print("export finished OK, ffmpeg log:", result.stderr[:200]) def on_err(msg, stderr): done["err"] = (msg, stderr) worker = app_main.ExportWorker(win.input_path, win.offset.delay_ms, EXPORT_OUT) worker.finished_ok.connect(on_ok) worker.finished_err.connect(on_err) worker.start() deadline = time.time() + 15 while not done["ok"] and done["err"] is None and time.time() < deadline: qapp.processEvents() time.sleep(0.05) if done["err"]: raise RuntimeError(f"export failed: {done['err']}") assert done["ok"], "export did not finish within timeout" assert Path(EXPORT_OUT).exists() print(f"exported file exists: {EXPORT_OUT}, size={Path(EXPORT_OUT).stat().st_size} bytes") win.source_panel.shutdown() win.preview_panel.shutdown() print("\nUI SMOKE TEST PASSED") if __name__ == "__main__": main()