The two launcher callbacks have the same signature, so transposing them compiles and passes #200

Closed
opened 2026-09-02 12:46:32 +00:00 by JMR-dev · 0 comments
JMR-dev commented 2026-09-02 12:46:32 +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. This is the wave's one assertion-gap item — it fell out of the coverage read rather than being looked for.

The two launcher callbacks have the same signature, so transposing them compiles

app/src/main/java/org/libremediaconverter/convert/ConverterScreen.kt:70, :83
val pickInput = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) {
    uri -> uri?.let(viewModel::onInputPicked)          // :70
}
...
val chooseDestination = rememberLauncherForActivityResult(...) {
    uri -> uri?.let(viewModel::save)                   // :83
}

ConversionViewModel.onInputPicked(uri: Uri) and .save(destination: Uri) are both (Uri) -> Unit. Swap the two lambda bodies and the file compiles, the screen renders, and picking a file attempts a save to it while choosing a destination loads it as input.

This is the exact defect class ScreenWiringTest exists for, on the one pair it declines to cover. That file's own framing is that the launcher-backed actions stay parameters — which is correct as far as the actions seam goes, and leaves the launcher-to-ViewModel edge above it unpinned.

Join's equivalents (JoinScreen.kt:45, :55) are List<Uri> and Uri respectively and are not transposable. No test needed there.

Folded in: the picker filter, since the harness is the same

app/src/main/java/org/libremediaconverter/convert/ConverterScreen.kt:99
app/src/main/java/org/libremediaconverter/join/JoinScreen.kt:61

ConverterScreen.kt:65-67 records why */* with OpenDocument is load-bearing rather than lazy:

ACTION_OPEN_DOCUMENT rather than the photo picker: the picker is images and video only, offers no audio at all, and will not reliably surface .mkv/.flac/.webm.

Nothing checks it. Narrowing */* to video/* would silently make every audio conversion unreachable from the file picker, and the whole suite would stay green.

Behaviour the tests assert

  • An OpenDocument result lands in onInputPicked, so after a pick the screen is Ready with that file — not attempting a save.
  • A CreateDocument result lands in save.
  • CHOOSE_FILE starts ACTION_OPEN_DOCUMENT carrying EXTRA_MIME_TYPES of ["*/*"]; Join's CHOOSE_FILES starts OpenMultipleDocuments with ["video/*"].

Verify the mechanic before writing the assertions

Both halves need the real Activity, so use createAndroidComposeRule<ComponentActivity>() as AdaptiveShellTest does — and for the same reason it gives: the plain rule supplies no ViewModelStoreOwner, and ui-test-manifest's ComponentActivity is already in the merged manifest the unit tests build against.

  • Delivering a result: shadowOf(activity).receiveResult(...) through ComponentActivity's ActivityResultRegistry.
  • Reading the launched intent: shadowOf(activity).nextStartedActivityForResult.

Neither is used anywhere in the suite today. If either turns out not to work under Robolectric, say so on this ticket and narrow the scope rather than asserting something weaker and describing it as the stronger thing.

Follow AdaptiveShellTest's setUp in stubbing ConversionDependencies.probe — the real screens compose real ViewModels, and neither test is about probing.

Acceptance: the mutations that must go red

  1. Swap viewModel::onInputPicked and viewModel::save between :70 and :83.
  2. Narrow arrayOf("*/*") to arrayOf("video/*") at :99.

Each must fail. Restore, confirm green.

_Wave 4, filed from a coverage read on `main` @ `54ca2dd`, 2026-09-02. The shared filter note is on #194. **This is the wave's one assertion-gap item** — it fell out of the coverage read rather than being looked for._ ## The two launcher callbacks have the same signature, so transposing them compiles ``` app/src/main/java/org/libremediaconverter/convert/ConverterScreen.kt:70, :83 ``` ```kotlin val pickInput = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) { uri -> uri?.let(viewModel::onInputPicked) // :70 } ... val chooseDestination = rememberLauncherForActivityResult(...) { uri -> uri?.let(viewModel::save) // :83 } ``` `ConversionViewModel.onInputPicked(uri: Uri)` and `.save(destination: Uri)` are **both `(Uri) -> Unit`**. Swap the two lambda bodies and the file compiles, the screen renders, and picking a file attempts a save to it while choosing a destination loads it as input. **This is the exact defect class `ScreenWiringTest` exists for, on the one pair it declines to cover.** That file's own framing is that the launcher-backed actions stay parameters — which is correct as far as the `actions` seam goes, and leaves the launcher-to-ViewModel edge above it unpinned. Join's equivalents (`JoinScreen.kt:45`, `:55`) are `List<Uri>` and `Uri` respectively and are **not** transposable. No test needed there. ## Folded in: the picker filter, since the harness is the same ``` app/src/main/java/org/libremediaconverter/convert/ConverterScreen.kt:99 app/src/main/java/org/libremediaconverter/join/JoinScreen.kt:61 ``` `ConverterScreen.kt:65-67` records why `*/*` with `OpenDocument` is load-bearing rather than lazy: > ACTION_OPEN_DOCUMENT rather than the photo picker: the picker is images and video only, offers no audio at all, and will not reliably surface .mkv/.flac/.webm. Nothing checks it. Narrowing `*/*` to `video/*` would silently make every audio conversion unreachable from the file picker, and the whole suite would stay green. ## Behaviour the tests assert - An `OpenDocument` result lands in `onInputPicked`, so after a pick the screen is `Ready` with that file — not attempting a save. - A `CreateDocument` result lands in `save`. - CHOOSE_FILE starts `ACTION_OPEN_DOCUMENT` carrying `EXTRA_MIME_TYPES` of `["*/*"]`; Join's CHOOSE_FILES starts `OpenMultipleDocuments` with `["video/*"]`. ## Verify the mechanic before writing the assertions Both halves need the real Activity, so use `createAndroidComposeRule<ComponentActivity>()` as `AdaptiveShellTest` does — and for the same reason it gives: the plain rule supplies no `ViewModelStoreOwner`, and `ui-test-manifest`'s `ComponentActivity` is already in the merged manifest the unit tests build against. - Delivering a result: `shadowOf(activity).receiveResult(...)` through `ComponentActivity`'s `ActivityResultRegistry`. - Reading the launched intent: `shadowOf(activity).nextStartedActivityForResult`. Neither is used anywhere in the suite today. **If either turns out not to work under Robolectric, say so on this ticket and narrow the scope** rather than asserting something weaker and describing it as the stronger thing. Follow `AdaptiveShellTest`'s `setUp` in stubbing `ConversionDependencies.probe` — the real screens compose real ViewModels, and neither test is about probing. ## Acceptance: the mutations that must go red 1. Swap `viewModel::onInputPicked` and `viewModel::save` between `:70` and `:83`. 2. Narrow `arrayOf("*/*")` to `arrayOf("video/*")` at `:99`. Each must fail. Restore, confirm green.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: JMR-dev/LibreMediaConverter#200