Read the instrumented suite, and find the test that proves nothing #231

Merged
JMR-dev merged 1 commits from docs/e2e-read-findings into main 2026-09-06 03:13:07 +00:00
JMR-dev commented 2026-09-06 02:55:27 +00:00 (Migrated from github.com)

Four coverage waves have been steered by JaCoCo, which measures testDebugUnitTest only and cannot see app/src/androidTest at all. Nothing had ever asked what the 60 device tests pin, only that they were green.

This is that read — a triage, not a test push, in the shape of docs/coverage-read-findings.md. It adds docs/e2e-read-findings.md (E1-E6) and a CLAUDE.md pointer. No production or test code changes.

The result

HardwareFallbackTest passes on every emulator leg without ever attempting the hardware path (#223).

It is the only automated check of the hardware→software fallback against a real codec failure. Measured on run 34004304566 — the API 33, 34, 35 and 37 legs each log:

I/AndroidDeviceCodecs: Hardware video encoders: []
I/ConversionWorker: Routing sample_h264_444.mp4 -> OutputSpec(container=MP4, videoCodec=H265,
                    audioCodec=AAC) via FFMPEG (NO_HARDWARE_ENCODER)

Emulators expose no hardware encoder, so the router sends the job straight to FFmpeg and runMedia3OrFallBack's catch is never entered. Its two assertions — succeeded, output non-empty — are true anyway. It finishes in 448 ms, which is not long enough to fail a hardware export and then software-encode a 3 s clip.

Deleting that catch reddens nothing, anywhere, on any leg.

The committed sample_h264_444.mp4 fixture — generated with x264 because Fedora's ffmpeg cannot produce High 4:4:4 — does nothing on any CI leg today.

Two things that generalise

  • A test can assert and still not reach. Neither a coverage number nor a "does it assert something" review catches this; it has two passing assertions. The filter that works is does this test's premise hold on the machine that runs it?
  • The codebase already knew. ForcedFailureTest pins DeviceCodecs.PERMISSIVE against this exact hazard and writes out why; ConversionWorkerTest records it a third time. Their assertions are about the path, so without the pin they fail loudly. HardwareFallbackTest's are about the output, so it passes quietly. That asymmetry is why nobody noticed.

Findings (no ticket — a test would not fix these)

ID Finding Action
E1 RemuxTest's KDoc argues for engine assertions three of its tests omit — and they are right to: those three produce MKV/AVI, which MEDIA3_CONTAINERS can never route to Media3 one line of KDoc
E2 Three of the 60 instrumented tests assert nothing; two never run no action — deliberate, but 60 ≠ 60
E3 …AndReportsProgress does not assert progress fired no action — the name overstates
E4 The API 37 marker's KDoc says "two"; three tests carry it fix the sentence
E5 coverage-read-findings.md F7's "uncovered" half is stale — RemuxTest drives it on a device every leg amend F7
E6 The one device-capability assertion asks the class under test what to expect no action — read with #223

E1 is the candidate that looked strongest and dissolved on tracing, recorded so the next read does not re-file it. E5 is the structural argument for this document existing: a JaCoCo-derived document cannot see androidTest, so it will keep re-deriving "uncovered" for code the instrumented suite covers.

Tickets filed

Each names the mutation that must go red, not a coverage delta.

# Gap
#223 HardwareFallbackTest never attempts the hardware path
#224 Cancelling a running native session, in any of the three engines
#225 No content:// input has reached a successful conversion — the ffkitsaf bridge
#226 OutputPublisher.publish against a real DocumentsProvider, and the SAF premise it rests on
#227 The notification's Cancel action has never been fired
#228 encodesFlacLosslessAudio and encodesOpus pass on any non-empty file
#229 FFmpeg's progress percentage is computed everywhere, asserted nowhere
#230 (spike) whether a running conversion's process can be killed under instrumentation

Checked and deliberately not filed

Real HEVC encode is not Pixel-only (Media3EngineTest runs on the API 33-36 gating legs; the marker excludes API 37 only). CLAUDE.md's "AndroidDeviceCodecs 20 / MediaProbe 14" is not stale — it dates the 2026-09-02 read and names #194/#195. Already tracked: #178, #190, #102, #108, F5.

Verification

./gradlew :app:ktlintCheck :app:detekt :app:lintDebug --continue — BUILD SUCCESSFUL. No .sh or workflow changes, so shellcheck/actionlint are unaffected.

🤖 Generated with Claude Code

Four coverage waves have been steered by JaCoCo, which measures `testDebugUnitTest` only and **cannot see `app/src/androidTest` at all**. Nothing had ever asked what the 60 device tests pin, only that they were green. This is that read — a **triage, not a test push**, in the shape of `docs/coverage-read-findings.md`. It adds `docs/e2e-read-findings.md` (`E1`-`E6`) and a `CLAUDE.md` pointer. No production or test code changes. ## The result **`HardwareFallbackTest` passes on every emulator leg without ever attempting the hardware path** (#223). It is the only automated check of the hardware→software fallback against a *real* codec failure. Measured on run [`34004304566`](https://github.com/JMR-dev/LibreMediaConverter/actions/runs/34004304566) — the API 33, 34, 35 and 37 legs each log: ``` I/AndroidDeviceCodecs: Hardware video encoders: [] I/ConversionWorker: Routing sample_h264_444.mp4 -> OutputSpec(container=MP4, videoCodec=H265, audioCodec=AAC) via FFMPEG (NO_HARDWARE_ENCODER) ``` Emulators expose no hardware encoder, so the router sends the job straight to FFmpeg and `runMedia3OrFallBack`'s `catch` is never entered. Its two assertions — succeeded, output non-empty — are true anyway. It finishes in **448 ms**, which is not long enough to fail a hardware export and then software-encode a 3 s clip. **Deleting that `catch` reddens nothing, anywhere, on any leg.** The committed `sample_h264_444.mp4` fixture — generated with x264 because Fedora's ffmpeg cannot produce High 4:4:4 — does nothing on any CI leg today. ## Two things that generalise - **A test can assert and still not reach.** Neither a coverage number nor a "does it assert something" review catches this; it has two passing assertions. The filter that works is *does this test's premise hold on the machine that runs it?* - **The codebase already knew.** `ForcedFailureTest` pins `DeviceCodecs.PERMISSIVE` against this exact hazard and writes out why; `ConversionWorkerTest` records it a third time. Their assertions are about the **path**, so without the pin they fail loudly. `HardwareFallbackTest`'s are about the **output**, so it passes quietly. That asymmetry is why nobody noticed. ## Findings (no ticket — a test would not fix these) | ID | Finding | Action | |---|---|---| | E1 | `RemuxTest`'s KDoc argues for engine assertions three of its tests omit — and they are **right** to: those three produce MKV/AVI, which `MEDIA3_CONTAINERS` can never route to Media3 | one line of KDoc | | E2 | Three of the 60 instrumented tests assert nothing; two never run | no action — deliberate, but 60 ≠ 60 | | E3 | `…AndReportsProgress` does not assert progress fired | no action — the name overstates | | E4 | The API 37 marker's KDoc says "two"; three tests carry it | fix the sentence | | E5 | `coverage-read-findings.md` F7's "uncovered" half is stale — `RemuxTest` drives it on a device every leg | amend F7 | | E6 | The one device-capability assertion asks the class under test what to expect | no action — read with #223 | **E1 is the candidate that looked strongest and dissolved on tracing**, recorded so the next read does not re-file it. **E5 is the structural argument for this document existing**: a JaCoCo-derived document cannot see `androidTest`, so it will keep re-deriving "uncovered" for code the instrumented suite covers. ## Tickets filed Each names the mutation that must go red, not a coverage delta. | # | Gap | |---|---| | #223 | `HardwareFallbackTest` never attempts the hardware path | | #224 | Cancelling a *running* native session, in any of the three engines | | #225 | No `content://` input has reached a *successful* conversion — the ffkitsaf bridge | | #226 | `OutputPublisher.publish` against a real `DocumentsProvider`, and the SAF premise it rests on | | #227 | The notification's Cancel action has never been fired | | #228 | `encodesFlacLosslessAudio` and `encodesOpus` pass on any non-empty file | | #229 | FFmpeg's progress percentage is computed everywhere, asserted nowhere | | #230 | *(spike)* whether a running conversion's process can be killed under instrumentation | ## Checked and deliberately not filed Real HEVC encode is **not** Pixel-only (`Media3EngineTest` runs on the API 33-36 gating legs; the marker excludes API 37 only). `CLAUDE.md`'s "AndroidDeviceCodecs 20 / MediaProbe 14" is **not** stale — it dates the 2026-09-02 read and names #194/#195. Already tracked: #178, #190, #102, #108, F5. ## Verification `./gradlew :app:ktlintCheck :app:detekt :app:lintDebug --continue` — BUILD SUCCESSFUL. No `.sh` or workflow changes, so shellcheck/actionlint are unaffected. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.