Skip to content

feat(tests): check complete video exports - #200

Open
DonIsmaelito wants to merge 2 commits into
browser-use:mainfrom
DonIsmaelito:submit/export-checks
Open

DonIsmaelito wants to merge 2 commits into
browser-use:mainfrom
DonIsmaelito:submit/export-checks

Conversation

@DonIsmaelito

@DonIsmaelito DonIsmaelito commented Sep 29, 2026 •

Copy link
Copy Markdown

Why

The editing checks should detect failures in actual exported files. This extends #198 with real FFmpeg exports instead of relying only on mocked commands and subtitle string tests.

Changes

  • Generate small temporary video, audio, overlay and transcript fixtures, then run the public render command.
  • Fully decode each output and check clip order, overlay timing, visible captions above an overlay, caption offsets, audio tone placement, frame rate, portrait/display rotation and unchanged source bytes.
  • Require the export tests in CI and install a font package for libass. Missing media capabilities remain an explicit skip locally and a failure in CI.

Validation: all 29 tests pass on Python 3.12, including the three real-export tests and their portrait/rotation subtests. Tested with an FFmpeg build containing libass. The rotation fixture checks its actual display matrix and supports both the legacy metadata option and newer FFmpeg display option. Whitespace checks pass. No provider calls or downloads are used by the tests.

Limits

Builds on #198 and includes its small workflow commit until that PR lands. Compare only this change.

Fixtures use the draft preset and skip loudness normalization. They do not certify visual taste, final-quality encoding, long-edit synchronization, or every codec. The legacy copy-concat path retains its existing AAC padding; the two-second fixture allows up to 100 ms of total duration or audio/video difference. GitHub-hosted Python 3.10/3.12 results remain to be verified; fork workflows may require maintainer approval.


Summary by cubic

Adds real FFmpeg export tests that run the public render command on generated media and fully decode each output to verify clip order, overlay timing, visible captions, audio placement, frame rate, portrait/rotation, and unchanged source bytes. Missing media capabilities skip locally but fail CI, so exports can't silently go untested.

  • New CI workflow runs the suite on Python 3.10 and 3.12 with FFmpeg, libass, and DejaVu fonts.
  • tests/README.md documents requirements, usage, and known limitations like the legacy AAC padding tolerance.

Written for commit 059a471. Summary will update on new commits.

Review in cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="tests/test_video_exports.py">

<violation number="1" location="tests/test_video_exports.py:23">
P3: A command that hangs past the 90s timeout raises `subprocess.TimeoutExpired`, which is not caught here, so the forced-failure diagnostic this helper exists to produce (command + stderr tail) never appears and the test fails with a raw traceback. Catch `TimeoutExpired` and re-raise it as the same AssertionError with the command name.</violation>

<violation number="2" location="tests/test_video_exports.py:45">
P3: The capability gate checks ffmpeg/ffprobe, the libass `subtitles` filter, and libx264, but never checks that fontconfig has any installed fonts. The caption assertions (`white(samples[0.5]) > 150`) fail silently-mysterious when libass has no font, producing an unrelated pixel-check failure instead of the skip-with-hint this gate and tests/README.md promise. Add a fontconfig check (e.g., non-empty `fc-list` output) to the `missing` list.</violation>
</file>

Heads up: you’ve reached your flex budget. Increase your flex budget or wait for usage to reset.

Fix all with cubic | Re-trigger cubic

class VideoExportTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
missing = [name for name in ("ffmpeg", "ffprobe") if shutil.which(name) is None]

@cubic-dev-ai cubic-dev-ai Bot Sep 29, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The capability gate checks ffmpeg/ffprobe, the libass subtitles filter, and libx264, but never checks that fontconfig has any installed fonts. The caption assertions (white(samples[0.5]) > 150) fail silently-mysterious when libass has no font, producing an unrelated pixel-check failure instead of the skip-with-hint this gate and tests/README.md promise. Add a fontconfig check (e.g., non-empty fc-list output) to the missing list.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/test_video_exports.py, line 45:

<comment>The capability gate checks ffmpeg/ffprobe, the libass `subtitles` filter, and libx264, but never checks that fontconfig has any installed fonts. The caption assertions (`white(samples[0.5]) > 150`) fail silently-mysterious when libass has no font, producing an unrelated pixel-check failure instead of the skip-with-hint this gate and tests/README.md promise. Add a fontconfig check (e.g., non-empty `fc-list` output) to the `missing` list.</comment>

<file context>
@@ -0,0 +1,167 @@
+class VideoExportTests(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls):
+        missing = [name for name in ("ffmpeg", "ffprobe") if shutil.which(name) is None]
+        if not missing:
+            filters = run(["ffmpeg", "-hide_banner", "-filters"]).decode()
</file context>
Fix with cubic


# Surface the failing media command and its diagnostics without an unbounded log.
def run(command):
result = subprocess.run([str(part) for part in command], capture_output=True, timeout=90)

@cubic-dev-ai cubic-dev-ai Bot Sep 29, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: A command that hangs past the 90s timeout raises subprocess.TimeoutExpired, which is not caught here, so the forced-failure diagnostic this helper exists to produce (command + stderr tail) never appears and the test fails with a raw traceback. Catch TimeoutExpired and re-raise it as the same AssertionError with the command name.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/test_video_exports.py, line 23:

<comment>A command that hangs past the 90s timeout raises `subprocess.TimeoutExpired`, which is not caught here, so the forced-failure diagnostic this helper exists to produce (command + stderr tail) never appears and the test fails with a raw traceback. Catch `TimeoutExpired` and re-raise it as the same AssertionError with the command name.</comment>

<file context>
@@ -0,0 +1,167 @@
+
+# Surface the failing media command and its diagnostics without an unbounded log.
+def run(command):
+    result = subprocess.run([str(part) for part in command], capture_output=True, timeout=90)
+    if result.returncode:
+        raise AssertionError(f"Command failed: {command}\n{result.stderr.decode(errors='replace')[-4000:]}")
</file context>
Fix with cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant