Testing
September 8, 2026 · View on GitHub
tests/
├── conftest.py -- fails the whole session loudly if no usable ffmpeg-skill checkout is found
├── fixtures/
│ └── generate.py -- synthesizes test media with real ffmpeg (testsrc video incl. a dual-audio-track
│ variant, a short PiP clip, a PNG logo, sample text)
├── test_unit.py -- model validation, timeline rules, canonical JSON / identity determinism, error table, PathPolicy, fonts, dropped_non_av_streams plumbing (fake FfmpegSkill, no real ffmpeg)
├── test_security.py -- shell non-use, tool allowlist, forbidden-field rejection (incl. nested), path traversal / symlink escape, env allowlist, injection payloads as *content*
├── test_contract.py -- contract <-> implementation consistency, contract/doctor are deterministic and valid JSON, skill == contract alias
└── test_integration.py -- real ffmpeg-skill + real media: title, lower_third, text_overlay, image_overlay, video_overlay (PiP + chroma-key), bug, chapter, progress, countdown, fade, audio_stream track selection, multi-element pipelines, reuse + tamper detection, Unicode text, cancellation, CLI exit codes
Run everything:
pip install -e .
python -m pytest -q
Integration and contract tests need a real ffmpeg-skill checkout with a working ffmpeg/ffprobe on PATH.
Point at one with MOTION_GRAPHICS_FFMPEG_SKILL_DIR=/path/to/ffmpeg-skill, or clone it to vendor/ffmpeg-skill
next to this repository. conftest.py fails the session (not skips) if none is found, mirroring
audio-production-skill's policy: a missing dependency should be loud, not a quietly-green suite.
What "real media E2E" means here (STEP 19-20)
test_integration.py renders onto an actual synthesized video (ffmpeg -f lavfi -i testsrc=...) through the real
ffmpeg-skill scripts and a real ffmpeg/ffprobe, then asserts, never just an exit code:
- the output file exists and is non-empty
- its sha256 is stable across two identical runs when nothing changed (determinism)
ffprobe(viaffmpeg-skill/probe) reports a video stream, the source resolution, and the expected duration- for
image_overlay/video_overlay: comparing pixel statistics (luma) between a region sampled with the overlay and the same region on an unmodified render (frame sampling / pixel-statistics check, not a subjective "looks right" judgement) —video_overlay's chroma-key test compares an opaque PiP layer against a keyed one the same way, proving the keyed-out region reveals the base video's own content rather than just asserting no exception was raised - for
document.options.audio_stream: rendering against a fixture with two distinguishable audio tracks (one silent, one an audible tone) and comparingmean_volume(ffmpeg's ownvolumedetectfilter) on the rendered output between the two selections — the only way to tell which source track actually survived, sinceffprobeon the output alone only ever shows the one audio stream the tool mapped - reuse: a second run with an unchanged specification hits the intermediate cache (
status: "reused") for every non-final stage; corrupting a cached intermediate's bytes forces a re-render (tamper detection)
This is not a subjective visual-quality check (STEP 20 explicitly rules that out) — it is objective, comparable evidence that the pixels actually changed where and when the specification said they should.
Security tests
test_security.py sends shell/command/argv/filter-injection payloads as element text content (e.g.
"; rm -rf / #", "$(reboot)", "<script>alert(1)</script>") and asserts the render still succeeds and produces
a video — because these are just characters ffmpeg-skill's drawtext will escape and draw, not something that
should be rejected as unsafe. It separately asserts that the field names that could carry an actual command
(FORBIDDEN_KEYS) are rejected regardless of where they appear in the document.