MediaProbe passes the video codec to containerFrom, and nothing checks that it does #195

Closed
opened 2026-09-02 12:46:15 +00:00 by JMR-dev · 0 comments
JMR-dev commented 2026-09-02 12:46:15 +00:00 (Migrated from github.com)

Wave 4, filed from a coverage read on main @ 54ca2dd, 2026-09-02. The shared filter note is on #194.

readMediaInformation is 114 missed instructions, and only one of them needs FFprobe

app/src/main/java/org/libremediaconverter/convert/MediaProbe.kt:224-243

The second-biggest block on the report, mi=114, mb=24. One line of it is native:

val info: MediaInformation = FFprobeKit.getMediaInformation(path).getMediaInformation() ?: return null

Everything after it is a pure mapping from a MediaInformation to an FFprobeInfo — first video stream, first audio stream, container, duration, dimensions, image-ness — and it is uncovered only because it sits behind that one call.

The seam

internal fun ffprobeInfoFrom(info: MediaInformation): FFprobeInfo

readMediaInformation keeps the FFprobeKit call and the ?: return null, and delegates the rest. This is work/FailureOutcome.kt's pattern: the edge stays thin and device-only, the decision becomes a function a test chooses the inputs for.

Verified JVM-safe rather than assumed. javap over bin/ffmpeg-kit-next-8.1.1.aar's transformed runtime jar:

public com.arthenica.ffmpegkit.MediaInformation(org.json.JSONObject, java.util.List<StreamInformation>, java.util.List<Chapter>);
public com.arthenica.ffmpegkit.StreamInformation(org.json.JSONObject);

Both are plain public constructors over org.json.JSONObject, which Robolectric provides. Neither class touches the native library — the static {} blocks are Companion and constant initialisation. So a test builds its own MediaInformation without loading libffmpegkit.

Behaviour the test asserts

  • The video stream's codec is passed to containerFrom as its second argument. This is the real find: containerFrom is well tested (mi=4, ci=148, 33 of 40 branches) and nothing checks the call.
  • The first video stream wins over a later one, and likewise for audio.
  • "12.345" becomes 12345 ms; an unparseable duration becomes 0 rather than throwing.
  • A stream list with no video yields null codec, 0×0, and a container decided from the format name alone.

Acceptance: the mutation that must go red

Pass null instead of video?.getCodec() to containerFrom at :236. Every existing containerFrom test stays green while a matroska,webm file of VP9 silently resolves to MKV instead of WebM.

The mutation is confirmed to be a real change, which is the check wave 3 learned to run after a classify reorder turned out to be semantically equivalent for every reachable input. matroskaOrWebm (MediaProbe.kt:295-298):

private fun matroskaOrWebm(videoCodec: String?): Container = when (videoCodec?.lowercase()) {
    "vp8", "vp9", "vp09", "av1", "av01" -> Container.WEBM
    else -> Container.MKV
}

"vp9" → WEBM, null → MKV. The two calls genuinely differ.

Scope note

This does not revisit probeWithFFprobe's two catch arms (:209-212, :219). The Error arm is already driven by MediaProbeNativeLoadTest and ConversionViewModelProbeFailureTest; the throw e rethrow at :219 is unreachable on the JVM because the first FFmpegKit touch always yields the native-load Error first. Leave both alone.

_Wave 4, filed from a coverage read on `main` @ `54ca2dd`, 2026-09-02. The shared filter note is on #194._ ## `readMediaInformation` is 114 missed instructions, and only one of them needs FFprobe ``` app/src/main/java/org/libremediaconverter/convert/MediaProbe.kt:224-243 ``` The second-biggest block on the report, `mi=114, mb=24`. One line of it is native: ```kotlin val info: MediaInformation = FFprobeKit.getMediaInformation(path).getMediaInformation() ?: return null ``` Everything after it is a pure mapping from a `MediaInformation` to an `FFprobeInfo` — first video stream, first audio stream, container, duration, dimensions, image-ness — and it is uncovered only because it sits behind that one call. ## The seam ```kotlin internal fun ffprobeInfoFrom(info: MediaInformation): FFprobeInfo ``` `readMediaInformation` keeps the `FFprobeKit` call and the `?: return null`, and delegates the rest. This is `work/FailureOutcome.kt`'s pattern: the edge stays thin and device-only, the decision becomes a function a test chooses the inputs for. **Verified JVM-safe rather than assumed.** `javap` over `bin/ffmpeg-kit-next-8.1.1.aar`'s transformed runtime jar: ``` public com.arthenica.ffmpegkit.MediaInformation(org.json.JSONObject, java.util.List<StreamInformation>, java.util.List<Chapter>); public com.arthenica.ffmpegkit.StreamInformation(org.json.JSONObject); ``` Both are plain public constructors over `org.json.JSONObject`, which Robolectric provides. Neither class touches the native library — the `static {}` blocks are Companion and constant initialisation. So a test builds its own `MediaInformation` without loading `libffmpegkit`. ## Behaviour the test asserts - **The video stream's codec is passed to `containerFrom` as its second argument.** This is the real find: `containerFrom` is well tested (`mi=4, ci=148`, 33 of 40 branches) and **nothing checks the call**. - The first video stream wins over a later one, and likewise for audio. - `"12.345"` becomes 12345 ms; an unparseable duration becomes 0 rather than throwing. - A stream list with no video yields null codec, 0×0, and a container decided from the format name alone. ## Acceptance: the mutation that must go red Pass `null` instead of `video?.getCodec()` to `containerFrom` at `:236`. Every existing `containerFrom` test stays green while a `matroska,webm` file of VP9 silently resolves to MKV instead of WebM. **The mutation is confirmed to be a real change**, which is the check wave 3 learned to run after a `classify` reorder turned out to be semantically equivalent for every reachable input. `matroskaOrWebm` (`MediaProbe.kt:295-298`): ```kotlin private fun matroskaOrWebm(videoCodec: String?): Container = when (videoCodec?.lowercase()) { "vp8", "vp9", "vp09", "av1", "av01" -> Container.WEBM else -> Container.MKV } ``` `"vp9"` → WEBM, `null` → MKV. The two calls genuinely differ. ## Scope note This does **not** revisit `probeWithFFprobe`'s two catch arms (`:209-212`, `:219`). The `Error` arm is already driven by `MediaProbeNativeLoadTest` and `ConversionViewModelProbeFailureTest`; the `throw e` rethrow at `:219` is unreachable on the JVM because the first FFmpegKit touch always yields the native-load `Error` first. Leave both alone.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: JMR-dev/LibreMediaConverter#195