Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Testing

Layers

LayerWhereWhat it proves
Unit tests#[cfg(test)] modules beside the codeParsing, planning, rendering, config validation, range logic
Registry and mapper testssrc/registry/tests.rs with src/testutil.rsResolution, caching, single flight, revalidation, stale-if-error, mapper failures, and remote media, against in-process mapper and origin servers
Router testssrc/http/tests.rsReal routing, middleware, headers, and streaming, through tower::ServiceExt::oneshot (no socket)
Decode testhttp::tests::ffmpeg_decodes_hls_and_dash_presentationsFFmpeg plays the HLS and DASH output over a real TCP server
Process teststests/cli.rs, tests/package.rsThe compiled binary runs; package output is well-formed and deterministic
Conformancetests/conformance.rsPlaylist grammar, timelines, fMP4 box arithmetic, HLS/DASH byte identity, and decoding, against the running binary
Performancebenches/budgets.rsThe TDD 0001 latency, memory, and concurrency budgets (make bench)
Fuzzingfuzz/fuzz_targets/media_pipeline.rsParsing, planning, and fragment preparation survive arbitrary bytes

Run everything CI runs with make ci (format check, cargo check, Clippy with warnings denied, all tests, fuzz-target compile, rustdoc with warnings denied). make test runs only the tests.

Fixtures

Committed under tests/fixtures/, generated by tests/fixtures/generate.sh and tests/fixtures/generate-variants.sh with FFmpeg (make fixtures) from synthetic sources, so there are no licensing concerns:

FilePurpose
h264-aac.mp4Main fixture: 320x180, 30 fps, 3 seconds, keyframe every second, two B-frames, 48 kHz AAC
h264-aac.ffprobe.jsonFFprobe packet dump: the ground truth the index is compared against
h264-aac-moov-last.mp4moov after mdat
h264-aac-edit-list.mp4Edit lists written by -c copy remuxing
h264-aac-default-edits.mp4FFmpeg’s default output: an edit list per track for B-frame delay and AAC priming
h264-aac-audio-delay.mp4Audio starts half a second late, so its track has a leading empty edit
h264-aac-two-audio.mp4Two audio tracks, tagged eng and spa
h264-aac-timecode.mp4An extra tmcd track that must be skipped
h264-aac-anamorphic.mp4Non-square pixels and tagged colour: the sample entry carries pasp and colr
h264-aac-quicktime.movQuickTime: versioned mp4a entries with a wave box, and a qt brand
hevc-aac.mp4HEVC video (hvc1)
vp9-opus.mp4, av1-aac.mp4VP9 with Opus, and AV1 with AAC
h264-ac3.mp4, h264-eac3.mp4, h264-flac.mp4AC-3, E-AC-3, and FLAC audio
aac-only.m4a, aac-two-tracks-only.m4aAudio only, with FFmpeg’s default edit list, and two tagged tracks
h264-mp3.mp4MP3 in MP4: an mp4a entry that must be rejected by its audio object type
h264-aac-fragmented*.mp4The progressive fixture remuxed with -c copy into fragmented layouts: two tracks per moof, explicit base offsets (legacy), one track per moof (CMAF), a sidx, version 1 trun with negative composition offsets, and (patched by generate-variants.sh) a timeline starting at 100 seconds
h264-video-only.mp4No audio track
h264-aac-44100-stereo.mp4Different audio parameters
h264-variable-timing.mp4Two frame-duration classes

FFmpeg is needed only to regenerate fixtures and to run the decode test, which skips itself with a message when ffmpeg is not installed. CI installs it.

Techniques used here

  • Real servers, not fakes. MockMapper and MockOrigin in src/testutil.rs are Axum servers on ephemeral ports. The mapper can be told to answer a given status, delay, a raw body, or require a token; the origin can ignore ranges, advertise a chosen validator, change it mid-test, and counts requests and bytes served. Tests then assert on observable behavior: mapper call counts, origin range requests, response statuses, and metrics.

  • Mutation checks. Removing single flight or the DNS address filter makes the concurrency and SSRF tests fail, which is how those tests were validated.

  • Oracle comparison: sample_index_matches_ffprobe_packets checks every sample’s offset, size, timestamps, and keyframe flag against FFprobe.

  • Equivalence with the progressive file: every fragmented fixture holds the same packets as h264-aac.mp4, and a test compares each sample’s size, duration, sync flag, and payload bytes, and its timing.

  • Second implementation: agrees_with_an_independent_implementation_on_every_sample compares the in-tree parser with the mp4 crate (a dev-dependency only) on seven fixtures: timing, sync flags, and the payload bytes at each sample’s offset and size.

  • Verbatim init segments: the init tests assert that the stsd bytes equal the source’s, so pasp, colr, and avcC survive; the conformance suite then compares aspect ratio and colour reported by FFprobe for the source and the reassembled track.

  • Deterministic mutation: corrupted_metadata_never_panics_the_pipeline corrupts a few random bytes of each fixture’s moov thousands of times and runs parse, plan, init writing, and fragment preparation over each. Any result is fine except a panic or hang. It runs in every cargo test; the fuzz target below is the deeper version.

  • Byte mutation instead of new fixtures: tests copy moov from the fixture and patch a few bytes to make an invalid input (find_type locates a box by name). See rejects_multiple_sample_descriptions and rejects_run_length_entries_that_claim_more_samples_than_stsz.

  • State injection for limits: HTTP tests build an AppState with small limits, then hold semaphores (segment_jobs, request_slots) to force 503 paths, or shrink stream_chunk_bytes and response_idle_timeout_ms to exercise the idle-client path.

  • Versioned URLs in tests: the helper versioned(path) appends the real ?v= value that media routes require.

  • Determinism: tests/package.rs runs the packager twice and compares every output byte.

Fuzzing

The fuzz crate (fuzz/) is separate and depends on the library. Its target writes the fuzz input to a temporary file and calls segmentor::fuzzing::exercise_media_pipeline, which runs parse, plan, init-segment writing, and fragment preparation and ignores expected failures; the fuzzer looks for panics, hangs, and runaway allocation. make fuzz-check compiles the target on stable; make fuzz runs a campaign on nightly with the fixtures as seeds.

Adding a test

  1. Put unit tests in the module’s tests block using the fixtures above.
  2. For a new rejection rule, mutate fixture bytes rather than adding a binary.
  3. For a new route or header behavior, add a router test with oneshot in http/tests.rs.
  4. Anything that changes fragment or playlist bytes should also keep the FFmpeg decode test and tests/package.rs green.

Not covered yet

The vendor HLS and DASH validators and browser playback tests; see Protocol conformance. Performance results and their limits are in Performance budgets.

cargo test builds the benchmark binary but skips its run unless invoked with --bench, so a minute-long load test never runs as part of the test suite.