Files
WeAreSyncing/scripts/smoke_test_ui.py
T
ThePendulum 8c34a0f0c2 Give each tab its own full timeline and transport (PlayerPanel refactor)
Extracts a new PlayerPanel widget (video + its own seek slider/marker
strip + its own centered frame-step/play/mute transport row) into
player_panel.py. Both Source and Preview tabs now use one, so
switching tabs switches a complete, independent monitor -- its own
scrubber, its own play state, its own frame-stepping -- not just the
video frame with a single shared play button underneath.

main.py's global keyboard shortcuts (,/./space/left/right) now
dispatch to whichever panel is active via a small _active_panel()
helper, so they follow the same 'whichever monitor you're looking at'
rule as the on-screen controls.

MarkerStrip (clap detection) and the Mark Audio/Video/offset/export
row stay outside the tabs, at the MainWindow level -- marking and
exporting are inherently about the source file, not the preview clip,
so only the timeline+transport moved, not the whole marking workflow.

Updated every existing smoke test for the new win.source_panel.* /
win.preview_panel.* structure (previously flat win.player,
win.seek_slider, etc.), and added a new check to
smoke_test_preview.py confirming the Preview tab's slider is a
genuinely separate, functional widget -- dragging it seeks the preview
player without moving the source player, which is the actual thing
this change was for.

Verified the full suite on the virtual Xvfb display, plus a live
click-through (mark both events, build preview, confirm the Preview
tab shows its own populated timeline/transport, not the source's).
2026-07-26 03:58:23 +02:00

95 lines
3.2 KiB
Python

"""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()