C2 — The audio half of ContainerCapabilities.validate has no test, while the video half has one per case #136

Closed
opened 2026-08-27 03:06:38 +00:00 by JMR-dev · 1 comment
JMR-dev commented 2026-08-27 03:06:38 +00:00 (Migrated from github.com)

Child 2 of 7 decomposing #132 — item 2 there. Independent: pure model code, no Android, no scaffolding.

Why this exists

app/src/main/java/org/libremediaconverter/model/ContainerCapabilities.kt:221-254, :100-105

validate has a video half and an audio half. The video half is thoroughly tested and the audio
half is not tested at all.
Six outcomes, every one of them a string the user reads:

line outcome video twin, already tested
:227-229 "The source audio codec could not be identified, so it cannot be copied." an unidentifiable source codec cannot be copied
:232-234 " cannot hold audio." (COPY path) a codec the container cannot hold is refused in both modes
:241-243 " cannot hold audio." (encode path) H265 in AVI is refused — AVI predates it
:247-250 "This app cannot encode audio. It can still be copied…" copying is offered as the fix when the codec is right but unencodable
:101 accepts(container, AudioCodec.NONE, mode) → true covered on the video axis
:102 accepts(container, AudioCodec.COPY, mode) → error(...) resolving COPY before asking the matrix is required

:102 is the one worth pointing at: its exact video twin is already a test
(ContainerCapabilitiesTest:110), asserting that asking the matrix about an unresolved COPY throws
rather than guessing. Nothing asks the same question on the audio axis.

Scope

Six tests in ContainerCapabilitiesTest, beside the video cases they mirror. No new fixtures, no
new helpers — the file already builds OutputSpec and InputProbe inline.

Done means

Each refusal named by its message, and each Validation.Invalid's suggestions asserted to
themselves be valid — the property every suggestion is itself valid already checks globally, but
these paths reach suggestions(...) through validateAudio, which nothing currently exercises.

Note while writing these: :196-199 (COPY video whose source the container cannot hold) is cold too,
and is the one video-side refusal with no test. Fold it in — it is the same shape and the same file.

Mutation: swap CARRIES_AUDIO for CARRIES_VIDEO in validateAudio, and the container-cannot-hold
tests must go red. Separately, delete the AudioCodec.COPY -> error(...) arm at :102 and the
resolve-first test must go red rather than silently returning a wrong boolean.

_Child 2 of 7 decomposing #132 — item 2 there. Independent: pure `model` code, no Android, no scaffolding._ ### Why this exists ``` app/src/main/java/org/libremediaconverter/model/ContainerCapabilities.kt:221-254, :100-105 ``` `validate` has a video half and an audio half. **The video half is thoroughly tested and the audio half is not tested at all.** Six outcomes, every one of them a string the user reads: | line | outcome | video twin, already tested | |---|---|---| | `:227-229` | "The source audio codec could not be identified, so it cannot be copied." | `an unidentifiable source codec cannot be copied` | | `:232-234` | "<container> cannot hold <codec> audio." (COPY path) | `a codec the container cannot hold is refused in both modes` | | `:241-243` | "<container> cannot hold <codec> audio." (encode path) | `H265 in AVI is refused — AVI predates it` | | `:247-250` | "This app cannot encode <codec> audio. It can still be copied…" | `copying is offered as the fix when the codec is right but unencodable` | | `:101` | `accepts(container, AudioCodec.NONE, mode)` → true | covered on the video axis | | `:102` | `accepts(container, AudioCodec.COPY, mode)` → `error(...)` | **`resolving COPY before asking the matrix is required`** | `:102` is the one worth pointing at: its **exact video twin is already a test** (`ContainerCapabilitiesTest:110`), asserting that asking the matrix about an unresolved `COPY` throws rather than guessing. Nothing asks the same question on the audio axis. ### Scope Six tests in `ContainerCapabilitiesTest`, beside the video cases they mirror. No new fixtures, no new helpers — the file already builds `OutputSpec` and `InputProbe` inline. ### Done means Each refusal named **by its message**, and each `Validation.Invalid`'s suggestions asserted to themselves be valid — the property `every suggestion is itself valid` already checks globally, but these paths reach `suggestions(...)` through `validateAudio`, which nothing currently exercises. Note while writing these: `:196-199` (COPY video whose source the container cannot hold) is cold too, and is the one video-side refusal with no test. Fold it in — it is the same shape and the same file. **Mutation:** swap `CARRIES_AUDIO` for `CARRIES_VIDEO` in `validateAudio`, and the container-cannot-hold tests must go red. Separately, delete the `AudioCodec.COPY -> error(...)` arm at `:102` and the resolve-first test must go red rather than silently returning a wrong boolean.
JMR-dev commented 2026-09-02 01:40:32 +00:00 (Migrated from github.com)

Already done, and verified on main today — closing.

This stayed open through a bookkeeping failure, not an unfinished one. PR #146 carried Closes #136, but GitHub only fires a closing keyword when the PR merges into the default branch. #146 merged into its stack base instead (the async-retarget race written up on #160 and now in CLAUDE.md), so the keyword never ran. The content reached main later via #160, which did not carry the keywords.

Verified against main at d354f64 just now, by re-running this ticket's own named mutation rather than by checking the files exist:

The ticket named two mutations. The first cannot compile as written — codec in validateAudio is an AudioCodec, so codec !in CARRIES_VIDEO.getValue(...) is a type error rather than a behaviour change. Used the compiling equivalent, which tests the same thing:

validateAudio container guard never fires  (if (false))
  red: an audio codec the container cannot hold is refused on the encode path too
AudioCodec.COPY -> error(...) arm removed
  red: resolving audio COPY before asking the matrix is required

The second is verbatim from the ticket and behaves as predicted: the arm is load-bearing, not defensive.

Gate green on main: 546 tests in 76 classes, 0 failures.

**Already done, and verified on `main` today — closing.** This stayed open through a bookkeeping failure, not an unfinished one. PR #146 carried `Closes #136`, but **GitHub only fires a closing keyword when the PR merges into the default branch.** #146 merged into its stack base instead (the async-retarget race written up on #160 and now in `CLAUDE.md`), so the keyword never ran. The content reached `main` later via #160, which did not carry the keywords. Verified against `main` at `d354f64` just now, by re-running **this ticket's own named mutation** rather than by checking the files exist: The ticket named two mutations. The first cannot compile as written — `codec` in `validateAudio` is an `AudioCodec`, so `codec !in CARRIES_VIDEO.getValue(...)` is a type error rather than a behaviour change. Used the compiling equivalent, which tests the same thing: ``` validateAudio container guard never fires (if (false)) red: an audio codec the container cannot hold is refused on the encode path too AudioCodec.COPY -> error(...) arm removed red: resolving audio COPY before asking the matrix is required ``` The second is verbatim from the ticket and behaves as predicted: the arm is load-bearing, not defensive. Gate green on `main`: 546 tests in 76 classes, 0 failures.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: JMR-dev/LibreMediaConverter#136