# GIF→MP4 reproducible fixtures

Test date: 2026-10-12 (Beijing). Original synthetic artwork, generated with Pillow 11.3.0. No downloaded third-party photographs or animations.

1. Run `python generate-fixtures.py` in a directory with Pillow 11.3.0. It creates seven GIF inputs. Compare SHA-256 with input-manifest.json. The three error fixtures are intentional.
2. Open https://filepetal.com/gif-to-mp4/ in a desktop browser. Choose each of the four valid GIFs, convert, play the MP4 preview, and download. Conversion is local; no account is required.
3. Compare file bytes and SHA-256 with SHA256SUMS.txt. Hash differences across a future encoder/build are possible; do not assume a match without checking.
4. Inspect each actual saved MP4 with native FFmpeg 7.1: `ffmpeg -hide_banner -i unequal-loop0.mp4`. Fully decode with `ffmpeg -i unequal-loop0.mp4 -fps_mode passthrough decoded-%03d.png`. Expected size: 242×162, H.264/yuv420p, 20 fps, 20 frames, no audio stream. Inspect the central color sequence: 1 green / 3 orange / 5 blue / 11 pink frames, equivalent to 50/150/250/550 ms.
5. Compare unequal-loop0.mp4 and unequal-loop2.mp4: the tested files have identical hashes and each contains one cycle, despite different GIF loop metadata. Single-frame.mp4 contains 10 frames over 0.50 seconds. Transparent-disposal.mp4 contains 20 frames over 1.00 second; white background and cleared earlier patch positions are present in the decoded raster.
6. Read output-verification.json for measured dimensions, bytes, hashes and selected decoded RGB observations. Frame extracts are from actual converted MP4 files, not drawn stand-ins. Internal decoded-frame analysis is experimental evidence, not a runtime pixel-check feature.

Scope: four small valid fixtures and three rejection cases, in the desktop in-app browser (engine version not recorded). Layout checks at 390 and 320 pixels are not physical-phone conversion tests. Not tested: mixed disposal methods, zero/10 ms delays, long animations, byte and total-pixel thresholds, every GIF encoder, color-managed workflows or competitor conversion quality. The conversion algorithm keeps its existing fixed 20 fps setting; exact individual frame holds are not promised.
