Wave 2 test gaps: the ViewModel state machines nothing chooses a branch of #153
Closed
opened 2026-08-27 11:58:43 +00:00 by JMR-dev
·
2 comments
No Branch/Tag Specified
main
fix/102-picker-back-press-overshoot
fix/268-saf-picker-determinism
feat/ogg-vorbis-libvorbis
feat/expedited-conversion-work
fix/gate-cache-in-worktrees
chore/gate-runs-shellcheck
test/publish-delete-arm-real-provider
docs/e8-instrumented-coverage
docs/api37-carrier-count-drift
docs/e7-second-constraint
test/publish-to-a-real-saf-destination
fix/api37-report-match-line
fix/api37-task-snapshot-crash
test/join-failure-message-on-device
docs/e2e-read-findings-e7
test/cancelling-a-running-export
test/reattach-to-a-running-job
test/content-uri-reaches-ffmpeg
fix/launcher-wiring-waits-for-the-pick
fix/cancel-tests-need-a-slower-encode
test/cancelling-a-running-join
test/cancelling-a-running-session
test/notification-cancel-action
test/ffmpeg-progress-is-observed
test/fallback-asserts-the-path
test/flac-and-opus-assert-their-format
docs/e2e-read-findings
docs/wave4-coverage-numbers
fix/injectable-startup-sweep-scope
test/session-outcome-seam
test/launcher-callback-identity
test/theme-follows-system-dark
test/audio-drop-arm
fix/rotation-waits-for-recreation
fix/convert-guards-on-ready
test/retry-save-mime
test/hardware-progress-reaches-workmanager
test/ffprobe-mapping-seam
test/device-codec-enumeration-seam
test/unknown-container-row
test/null-message-fallbacks
test/cancel-reaches-workmanager
docs/coverage-wave3-recovery
test/concat-engine-seam
docs/coverage-wave3
test/mediaprobe-merge-seam
test/adaptive-shell-wiring
test/aac-audio-args
test/notification-progress-text
test/media3-muxer-guard
test/hardware-fallback-and-cancellation
test/unprobeable-join-clip
test/one-branch-outcomes
test/foreground-type-regimes
fix/bound-wedge-diagnostics
docs/coverage-wave2
test/screen-wiring
test/viewmodel-setters
test/join-state-mapping
test/conversion-state-mapping
test/dedupe-user-messages
fix/restore-stack-merges
test/refused-jobs
test/concatworker-failure-arms
test/container-capabilities-audio
test/readspec-enum-fallbacks
test/outputpublisher-seams
test/mediaprobe-track-seam
test/outputpublisher-partial-branches
test/fake-provider-scaffolding
docs/coverage-read-findings
chore/gitignore-kotlin
test/bound-the-hangs
docs/coverage-remeasure
ci/baseline-counter-precision
fix/invalid-suggestion-chip
ci/wedged-leg-report
fix/reattachment-overwrites-pick
test/theme-live-branches
fix/failed-save-retry
fix/empty-composition-crash
ci/advisory-failure-report
docs/seven-run-counts
test/release-permission-guard
ci/build-workflow-permissions
docs/api37-point-release
docs/benchmark-populate-path
fix/dead-assertion-probe-test
ci/actionlint
test/device-codecs-encode-consequence
fix/sdkmanager-pipefail
docs/readme-restart-claim
fix/saf-picker-root-discovery
fix/probe-dispatcher-seam
test/media3engine-mime-tables
test/mediaprobe-pure-helpers
fix/codec-vocabulary-drift
docs/robolectric-rationale-correction
docs/api37-advisory-counts
test/r38-8-saf-e2e
test/r38-7-join-states
test/r38-6-conversion-states
test/r38-5-state-seam
fix/jacoco-robolectric-coverage
docs/instrumented-tests-correction
test/r38-2-filecard
test/r38-4-advanced-picker
test/r38-3-pickers
tools/file-issue-script
tools/api-37-emulator
fix/review-app-gaps
docs/review-corrections
No results found.
Labels
Clear labels
above-cut
accessibility
backlog
bug
confirmed
documentation
duplicate
enhancement
good first issue
help wanted
invalid
plausible
question
sev:high
sev:low
sev:medium
wontfix
Worked autonomously overnight: local, JVM-verifiable, no product decision
Barrier affecting people with disabilities
Held for manual review: product/UX call, CI/workflow, hardware, or unverifiable here
Something isn't working
Reviewer demonstrated the defect
Improvements or additions to documentation
This issue or pull request already exists
New feature or request
Good for newcomers
Extra attention is needed
This doesn't seem right
Reviewer could not fully demonstrate it; treat as unproven
Further information is requested
High severity
Low severity
Medium severity
This will not be worked on
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: JMR-dev/LibreMediaConverter#153
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Parent for the second wave of test gaps, found by a repo-wide coverage sweep rather than the file-scoped audit that closed out #132.
How this was measured, and what it is measured against
./gradlew :app:jacocoTestReportover a tree with all eight of #132/#133's branches merged together: 87.0% line (2040/2344), 69.1% branch (974/1410).At the time of filing #144–#151 are open, not merged. That does not weaken any item below: every gap here lives in
ConversionViewModel.kt,JoinViewModel.kt,ConverterScreen.ktorJoinScreen.kt, and the batch touches none of those four files. The measurement is quoted from the integrated tree because that is the state these tickets will be worked against.What separates this wave from the last one
#132's children came from a read of three files. This came from ranking every class by uncovered lines and branches, then asking of each one whether something already accounts for it. That surfaced a different kind of gap.
The last wave's items were mostly cold code. This wave's biggest item is not cold at all — it executes on every test that runs a worker. What is missing is that no test ever chooses which branch it takes. That is the argument #141 made for
MediaProbe's track walk:Here the same shape appears against
WorkManagerinstead of against a media fixture, and the answer is the same one #141 preferred: cut the branch matrix out as a pure function, leave the thin edge behind.Children
ConversionViewModel.observe's state mapping — seam + the six arms nothing choosesJoinViewModel.observe's state mapping, and the arity guard above itelsearmsW5 is listed last but should be worked first. It is the only one that is not a coverage item — it is a defect shape the sweep exposed — and W2 wants to pin a literal that W5 removes. Doing W2 first freezes the duplication in place and makes the eventual fix a three-file change with two tests to rewrite.
Suggested order: W5 → W1 → W2, with W3 and W4 independent of all of them. W1 before W2 so the two seams come out the same shape.
Accounted for, and not in scope
Recorded so the next sweep does not re-derive them:
MediaProbe35L/72BsetDataSource/releaseedge — #150's PR bodyAndroidDeviceCodecs.probe()MediaCodecInfoBuilderhas nosetIsAlias/setCanonicalNameMedia3Engine,FFmpegEngine,ConcatEngineConversionForegroundTypeFFmpegCommandBuilder:188(Vorbis)docs/coverage-read-findings.md— a test would pin dead codeConversionWorker/ConcatWorkerresidualsMainActivity.onCreateenableEdgeToEdge+setContent. There is no decision in itMainActivity.Content()whenover a two-entry enumThe Compose branch counts are not 170 missing tests.
ConverterScreenKtshows 110 missed branches andJoinScreenKt60; most are compiler-synthesized$changed/$dirtyskip checks, exactly asdocs/coverage-read-findings.mdrecords under its look-alike non-gaps. W3 is scoped by what the bindings do, not by that number.Wave 2 complete — all five children merged and verified on
main"Joining failed."JobSnapshot502 → 546 tests, 87.1% → 88.9% line, 69.1% → 75.4% branch.
CLAUDE.mdupdated in #166.What the wave produced beyond coverage
A real crash (W2).
JoinViewModelread the join strategy withConcatStrategy::valueOf, which throws inside aviewModelScopecollect with no handler — so it took the process down rather than becoming aFailedstate. Reachable on this wave's own premise: WorkManager keeps finished work a week, so a rollback hands this build a job naming a strategy it does not define. The repo had already made that exact fix one file over, inConcatWorker.kt, with a comment explaining why — and nothing connected the two reads.Three corrections to my own filings. W5's ticket named three duplicated strings and there were four; W3's ticket claimed seventeen transposable bindings and there are five, because every typed binding is already rejected by the compiler. Both are recorded on their tickets rather than only in the PRs.
The finding I would carry forward
Two of the five children found a defect rather than only a gap, and in both cases the defect had been sitting in plain sight inside a large block.
valueOfwas one line inside a 50-linewheninside acollect; the transposable bindings were nine lines in an argument list. Neither was hidden — both were unreadable in place.So the seam's value is not only that branches become testable. It is that a branch standing alone gets read. That is a better argument for the pure-seam pattern than "it raises coverage", and
CLAUDE.mdnow carries the measurement half of it too: the branch denominator fell 1410 → 1340 as this landed, because extracting awhenfrom a coroutine lambda deletes the state machine's synthesized branches around it.Still open, filed during the wave
#159 — application-scope IO racing every Robolectric test that shares
cacheDir. Found when #149'sUnit testsleg failed once on CI and passed 500-odd times locally. Worked around in that PR's fixture with a retry loop; #159's done-when is that the loop can be deleted.All five children closed, and the wave is complete. Closing this parent.
Final state on
mainatd354f64: 502 → 546 tests, 87.1% → 88.9% line, 69.1% → 75.4% branch.CLAUDE.mdre-measured in #166, including the note that the branch denominator fell 1410 → 1340 because extracting awhenfrom a coroutine lambda deletes the state machine's synthesized branches — so the percentage moved for two reasons and only one of them is new tests.What the wave produced beyond coverage, which is the part worth keeping:
JoinViewModelread the join strategy withConcatStrategy::valueOf, which throws inside aviewModelScopecollect with no handler. The repo had already made that exact fix one file over, inConcatWorker.kt, with a comment explaining why — and nothing connected the two reads."Joining failed."); #156 claimed seventeen transposable bindings and there are five, because every typed binding is already rejected by the compiler.The through-line: two of five children found a defect rather than only a gap, and both defects were in plain sight inside a large block.
valueOfwas one line inside a 50-linewhen; the transposable bindings were nine lines in an argument list. Neither was hidden — both were unreadable in place. That is a better argument for the pure-seam pattern than coverage is.Still open from this wave: #159, the application-scope IO race against every Robolectric test sharing
cacheDir. Worked around in #149's fixture; its done-when is that the retry loop can be deleted.