Check the MIME types Media3Engine hands Transformer, and the claim above them #91

Merged
JMR-dev merged 2 commits from test/media3engine-mime-tables into main 2026-08-25 05:10:52 +00:00
JMR-dev commented 2026-08-25 03:46:10 +00:00 (Migrated from github.com)

Closes #85.

Media3Engine's two enum-to-MIME tables decide which codec ends up in the user's file, and neither
was exercised by anything. One of them also carried a claim about its callers, which nothing checked.

The verdict on the "never reached" claim: split

Video — proved. // Never reached: only an Encode plan consults this, and COPY/NONE are not Encode is true, and it is now a checked claim rather than an asserted one. The proof is a property
of CopyPlanner, which is where every plan the engine sees comes from: both codecs are answered
before the Encode branch (NONE -> Drop, COPY -> Copy or fallback), and the fallback draws from
ContainerCapabilities.encodableVideo, which contains neither. The test sweeps every spec the
planner can be handed — 15 containers x 7 video codecs x 8 audio codecs x 7 probes — and asserts no
Encode plan carries COPY or NONE.

Audio — incomplete, and rewritten. // MP3 and FLAC have no Android encoder; the router routes them to FFmpeg is true as far as it goes, and it reads as though those two were the exceptions.
They are not. One rule — audioEncode !in MEDIA3_AUDIO, where MEDIA3_AUDIO = {AAC, OPUS, PCM} —
diverts Vorbis by identical logic, so three of the six encodable codecs never reach the
table, not two. AudioCodec.VORBIS -> MimeTypes.AUDIO_VORBIS names a MIME type Transformer is never
actually asked for.

The arm is kept: the mapping is correct, and deleting a right answer out of unreachable code buys
nothing while changing behaviour in code no test can reach. The comment now says what is actually
guaranteed, and Vorbis is the one MIME type the router never asks for pins the surplus, so a
routing change that makes Vorbis live fails rather than surprises.

What the tests check

Three kinds, because arm-by-arm equality alone would only pin today's answers:

  1. Every arm of both tables, nulls included, against an expected map that is itself asserted to
    name every enum constant — so a new codec cannot arrive untested.
  2. The "never reached" claim, as the sweep above. Two counters guard it: (plan.video as? VideoPlan.Encode)?.let { ... } asserts nothing at all for a Drop or Copy plan, so a sweep
    that stopped producing Encode plans would stay green while checking nothing.
  3. The tables against ConversionRouter's decisions, not against its codec sets. The comments
    claim behaviour ("the router routes them to FFmpeg") and a set can be right while the rule
    reading it is wrong, so the tests call route(). Each helper first asserts that its request
    really does plan an Encode of the codec in question, so "routed to FFmpeg" is about the codec
    rather than about some other plan shape.

Mutations, verbatim

The one #85 names — VideoCodec.H265 -> MimeTypes.VIDEO_H264:

org.junit.ComparisonFailure: videoMimeTypeFor(H.265) expected:<video/[he]vc> but was:<video/[a]vc>

The sweep carrying the ticket's point — delete if (requested == VideoCodec.NONE) return VideoPlan.Drop from CopyPlanner.planVideo:

java.lang.AssertionError: CopyPlanner produced VideoPlan.Encode(NONE) for OutputSpec(container=MP4,
videoCodec=NONE, audioCodec=COPY) against InputProbe(videoCodec=null, audioCodec=null,
hasVideo=true, durationMs=0, kind=VIDEO, container=null, width=0, height=0)

The Vorbis finding — add AudioCodec.VORBIS to ConversionRouter.MEDIA3_AUDIO:

java.lang.AssertionError: expected:<[AAC, OPUS, PCM]> but was:<[AAC, OPUS, VORBIS, PCM]>
java.lang.AssertionError: expected:<[VORBIS]> but was:<[]>

Each mutation was reverted and the suite confirmed green again.

The visibility change

Both functions move to an internal companion object. internal is #57's precedent — the JVM test
source set is a friend of main. The companion is what avoids constructing a Media3Engine to
answer an enum lookup, which would start a real HandlerThread; the call sites in buildTransformer
are unchanged.

Not covered, and why

  • transcode, buildTransformer, pollProgress, close and the constructor drive Transformer
    against real codecs. androidTest/Media3EngineTest and RemuxTest cover them on a device, and
    JaCoCo measures testDebugUnitTest only. Untouched, and not mocked.
  • No fifth codec table was added, and CodecNames.kt / AndroidDeviceCodecs.kt / MediaProbe.kt
    are untouched — #87 owns the alias-name drift. Worth noting for that ticket: AndroidDeviceCodecs
    has an enum -> MIME table too (mimeFor(VideoCodec)), on the same axis as the one here, and it
    currently agrees by value. That is a fifth pairing nothing checks; it belongs with #87's
    cross-check rather than here.

🤖 Generated with Claude Code

Closes #85. `Media3Engine`'s two enum-to-MIME tables decide which codec ends up in the user's file, and neither was exercised by anything. One of them also carried a claim about its callers, which nothing checked. ## The verdict on the "never reached" claim: split **Video — proved.** `// Never reached: only an Encode plan consults this, and COPY/NONE are not Encode` is true, and it is now a checked claim rather than an asserted one. The proof is a property of `CopyPlanner`, which is where every plan the engine sees comes from: both codecs are answered before the `Encode` branch (`NONE -> Drop`, `COPY -> Copy or fallback`), and the fallback draws from `ContainerCapabilities.encodableVideo`, which contains neither. The test sweeps every spec the planner can be handed — 15 containers x 7 video codecs x 8 audio codecs x 7 probes — and asserts no `Encode` plan carries `COPY` or `NONE`. **Audio — incomplete, and rewritten.** `// MP3 and FLAC have no Android encoder; the router routes them to FFmpeg` is true as far as it goes, and it reads as though those two were the exceptions. They are not. One rule — `audioEncode !in MEDIA3_AUDIO`, where `MEDIA3_AUDIO = {AAC, OPUS, PCM}` — diverts **Vorbis** by identical logic, so **three** of the six encodable codecs never reach the table, not two. `AudioCodec.VORBIS -> MimeTypes.AUDIO_VORBIS` names a MIME type Transformer is never actually asked for. The arm is kept: the mapping is correct, and deleting a right answer out of unreachable code buys nothing while changing behaviour in code no test can reach. The comment now says what is actually guaranteed, and `Vorbis is the one MIME type the router never asks for` pins the surplus, so a routing change that makes Vorbis live fails rather than surprises. ## What the tests check Three kinds, because arm-by-arm equality alone would only pin today's answers: 1. **Every arm of both tables**, nulls included, against an expected map that is itself asserted to name every enum constant — so a new codec cannot arrive untested. 2. **The "never reached" claim**, as the sweep above. Two counters guard it: `(plan.video as? VideoPlan.Encode)?.let { ... }` asserts nothing at all for a `Drop` or `Copy` plan, so a sweep that stopped producing `Encode` plans would stay green while checking nothing. 3. **The tables against `ConversionRouter`'s decisions**, not against its codec sets. The comments claim behaviour ("the router routes them to FFmpeg") and a set can be right while the rule reading it is wrong, so the tests call `route()`. Each helper first asserts that its request really does plan an `Encode` of the codec in question, so "routed to FFmpeg" is about the codec rather than about some other plan shape. ## Mutations, verbatim **The one #85 names** — `VideoCodec.H265 -> MimeTypes.VIDEO_H264`: ``` org.junit.ComparisonFailure: videoMimeTypeFor(H.265) expected:<video/[he]vc> but was:<video/[a]vc> ``` **The sweep carrying the ticket's point** — delete `if (requested == VideoCodec.NONE) return VideoPlan.Drop` from `CopyPlanner.planVideo`: ``` java.lang.AssertionError: CopyPlanner produced VideoPlan.Encode(NONE) for OutputSpec(container=MP4, videoCodec=NONE, audioCodec=COPY) against InputProbe(videoCodec=null, audioCodec=null, hasVideo=true, durationMs=0, kind=VIDEO, container=null, width=0, height=0) ``` **The Vorbis finding** — add `AudioCodec.VORBIS` to `ConversionRouter.MEDIA3_AUDIO`: ``` java.lang.AssertionError: expected:<[AAC, OPUS, PCM]> but was:<[AAC, OPUS, VORBIS, PCM]> java.lang.AssertionError: expected:<[VORBIS]> but was:<[]> ``` Each mutation was reverted and the suite confirmed green again. ## The visibility change Both functions move to an `internal companion object`. `internal` is #57's precedent — the JVM test source set is a friend of `main`. The companion is what avoids constructing a `Media3Engine` to answer an enum lookup, which would start a real `HandlerThread`; the call sites in `buildTransformer` are unchanged. ## Not covered, and why - `transcode`, `buildTransformer`, `pollProgress`, `close` and the constructor drive Transformer against real codecs. `androidTest/Media3EngineTest` and `RemuxTest` cover them on a device, and JaCoCo measures `testDebugUnitTest` only. Untouched, and not mocked. - No fifth codec table was added, and `CodecNames.kt` / `AndroidDeviceCodecs.kt` / `MediaProbe.kt` are untouched — #87 owns the alias-name drift. Worth noting for that ticket: `AndroidDeviceCodecs` has an `enum -> MIME` table too (`mimeFor(VideoCodec)`), on the same axis as the one here, and it currently agrees by value. That is a fifth pairing nothing checks; it belongs with #87's cross-check rather than here. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
JMR-dev commented 2026-08-25 04:04:51 +00:00 (Migrated from github.com)

CI: everything green except two things, neither of them this diff.

E2E API 37 Media3 hardware transcode (advisory) — red on every PR by design, per CLAUDE.md.

E2E API 34 — SafPickerRoundTripTest.thePickedInputSurvivesARealRotation and
.pickingAFileThroughTheSystemPickerFillsInTheFileCard, the pair landed in #80. Three pieces of
evidence that they are a flake rather than a regression:

  1. The identical two tests failed at the same moment on #89, whose entire diff is two JVM unit
    test files. A change that touches no androidTest code cannot break a SAF picker test.
  2. On a re-run of this same commit, E2E API 33 and E2E API 35 flipped from red to green
    while nothing changed. E2E API 36 and the gating E2E API 37 were green in both runs.
  3. This diff is a visibility change plus comments in Media3Engine.kt and one new JVM test file.
    There is no path from it to the SAF picker.

The first run had five agent PRs on CI simultaneously, which is the kind of load a
rotation-and-picker test is sensitive to. Not filing a ticket from here in case a sibling agent
already has; flagging it because it will keep costing re-runs.

CI: everything green except two things, neither of them this diff. **`E2E API 37 Media3 hardware transcode (advisory)`** — red on every PR by design, per CLAUDE.md. **`E2E API 34`** — `SafPickerRoundTripTest.thePickedInputSurvivesARealRotation` and `.pickingAFileThroughTheSystemPickerFillsInTheFileCard`, the pair landed in #80. Three pieces of evidence that they are a flake rather than a regression: 1. The **identical two tests** failed at the same moment on #89, whose entire diff is two JVM unit test files. A change that touches no `androidTest` code cannot break a SAF picker test. 2. On a re-run of **this same commit**, `E2E API 33` and `E2E API 35` flipped from red to green while nothing changed. `E2E API 36` and the gating `E2E API 37` were green in both runs. 3. This diff is a visibility change plus comments in `Media3Engine.kt` and one new JVM test file. There is no path from it to the SAF picker. The first run had five agent PRs on CI simultaneously, which is the kind of load a rotation-and-picker test is sensitive to. Not filing a ticket from here in case a sibling agent already has; flagging it because it will keep costing re-runs.
Sign in to join this conversation.