SafPickerRoundTripTest is failing on gating legs 34/35/37: the fixture root never appears in the picker #93

Closed
opened 2026-08-25 04:05:30 +00:00 by JMR-dev · 1 comment
JMR-dev commented 2026-08-25 04:05:30 +00:00 (Migrated from github.com)

Blocking. These are gating legs, so this stops every PR in the repo until it is fixed or the tests stop gating.

The failure

Run 32806342548, on PR #89 — a docs-only change to two KDoc comments, which cannot plausibly affect it:

org.libremediaconverter.saf.SafPickerRoundTripTest > pickingAFileThroughTheSystemPickerFillsInTheFileCard FAILED
    java.lang.IllegalArgumentException: the system picker never showed BySelector [TEXT='\QLMC R38 fixtures\E']
        at SafPickerRoundTripTest.awaitPickerNode(SafPickerRoundTripTest.kt:280)
leg pickingAFile… thePickedInput…Rotation
E2E API 34 FAILED FAILED
E2E API 35 FAILED FAILED
E2E API 37 (gating) FAILED — (carries @FailsOnEmulatorApi37)
E2E API 33 passed passed
E2E API 36 passed passed

This is not the flake #80 already fixed

#80 found and fixed a StaleObjectException — UiObject2 caching an AccessibilityNodeInfo while DocumentsUI settles — and verified 5/5 green with pm clear on DocumentsUI between runs.

This is a different failure. The node is not going stale; the fixture root never appears in the picker at all. awaitPickerNode times out having never seen LMC R38 fixtures.

What that points at

FixtureDocumentsProvider is declared in the androidTest manifest and installed with the instrumentation APK. DocumentsUI discovers roots by querying the package manager for android.content.action.DOCUMENTS_PROVIDER. The likely shape is a race between package installation settling and DocumentsUI building its root list — a retry on the node cannot help, because by then the picker is already open and showing a list that does not contain the root.

Worth checking, in roughly this order:

  1. Does DocumentsUI cache roots per launch? If so, the fix is to make the picker open retryable, not the node lookup — close it, and reopen.
  2. Is there a settling signal to wait on — the provider answering a direct ContentResolver query for its root URI — before opening the picker at all? That would be a real wait rather than a sleep.
  3. Does it correlate with emulator cold start? #80 measured that its earlier flake was deterministic on a cold emulator and invisible on a warm one. CI emulators are always cold; the same asymmetry would explain why it passes locally and on some runs.

Why it must not just be retried away

A retry that reopens the picker until the root shows up is legitimate. A retry that swallows a genuinely missing provider is not — the MIME-filter mutation in #64 works precisely by making the root absent, and a fix that tolerates absence would make that mutation vacuous. Whatever lands must keep this red:

change the MIME filter to something the fixture does not match -> the picker test fails

Immediate mitigation taken

gh run rerun --failed on the blocked PR. That is a stopgap for one PR, not a fix, and it should not become the habit — three of five legs failing means roughly a 60% chance of blocking any given PR.

If it cannot be made reliable

The honest fallback is to stop these two gating and run them somewhere that reports without blocking, the way @FailsOnEmulatorApi37 already does for the API 37 pair. That is a downgrade, not a fix, and should be recorded as one — the SAF round trip is the only test of the picker that exists.

_**Blocking.** These are gating legs, so this stops every PR in the repo until it is fixed or the tests stop gating._ ### The failure Run [32806342548](https://github.com/JMR-dev/LibreMediaConverter/actions/runs/32806342548), on PR #89 — a **docs-only** change to two KDoc comments, which cannot plausibly affect it: ``` org.libremediaconverter.saf.SafPickerRoundTripTest > pickingAFileThroughTheSystemPickerFillsInTheFileCard FAILED java.lang.IllegalArgumentException: the system picker never showed BySelector [TEXT='\QLMC R38 fixtures\E'] at SafPickerRoundTripTest.awaitPickerNode(SafPickerRoundTripTest.kt:280) ``` | leg | `pickingAFile…` | `thePickedInput…Rotation` | |---|---|---| | **E2E API 34** | FAILED | FAILED | | **E2E API 35** | FAILED | FAILED | | **E2E API 37** (gating) | FAILED | — (carries `@FailsOnEmulatorApi37`) | | E2E API 33 | passed | passed | | E2E API 36 | passed | passed | ### This is not the flake #80 already fixed #80 found and fixed a `StaleObjectException` — `UiObject2` caching an `AccessibilityNodeInfo` while DocumentsUI settles — and verified 5/5 green with `pm clear` on DocumentsUI between runs. **This is a different failure.** The node is not going stale; the fixture root **never appears in the picker at all**. `awaitPickerNode` times out having never seen `LMC R38 fixtures`. ### What that points at `FixtureDocumentsProvider` is declared in the `androidTest` manifest and installed with the instrumentation APK. DocumentsUI discovers roots by querying the package manager for `android.content.action.DOCUMENTS_PROVIDER`. The likely shape is a **race between package installation settling and DocumentsUI building its root list** — a retry on the node cannot help, because by then the picker is already open and showing a list that does not contain the root. Worth checking, in roughly this order: 1. Does DocumentsUI cache roots per launch? If so, the fix is to make the *picker open* retryable, not the node lookup — close it, and reopen. 2. Is there a settling signal to wait on — the provider answering a direct `ContentResolver` query for its root URI — before opening the picker at all? That would be a real wait rather than a sleep. 3. Does it correlate with emulator cold start? #80 measured that its earlier flake was **deterministic on a cold emulator and invisible on a warm one**. CI emulators are always cold; the same asymmetry would explain why it passes locally and on some runs. ### Why it must not just be retried away A retry that reopens the picker until the root shows up is legitimate. A retry that **swallows a genuinely missing provider** is not — the MIME-filter mutation in #64 works precisely by making the root absent, and a fix that tolerates absence would make that mutation vacuous. Whatever lands must keep this red: > change the MIME filter to something the fixture does not match -> the picker test fails ### Immediate mitigation taken `gh run rerun --failed` on the blocked PR. That is a stopgap for one PR, not a fix, and it should not become the habit — three of five legs failing means roughly a 60% chance of blocking any given PR. ### If it cannot be made reliable The honest fallback is to stop these two gating and run them somewhere that reports without blocking, the way `@FailsOnEmulatorApi37` already does for the API 37 pair. **That is a downgrade, not a fix**, and should be recorded as one — the SAF round trip is the only test of the picker that exists.
JMR-dev commented 2026-08-25 04:25:21 +00:00 (Migrated from github.com)

Second failure variant, and it widens the fix.

Four more legs have failed since this was filed, all on diffs that cannot cause them (two KDoc comments, a MIME lookup table, a README paragraph).

Variant A — the root never appears (what this ticket describes):

never showed BySelector [TEXT='\QLMC R38 fixtures\E']

API 33 (#90), 34, 35, 37 (#89).

Variant B — the root appears, the FILE does not:

never showed BySelector [TEXT='\Qlmc-r38-fixture.mp4\E']

API 37 (#95).

So this is not only root discovery. The same "picker content has not settled" problem bites at two depths — the roots list, and the document list inside a root once opened. A fix that only makes the root wait or retry leaves variant B live, and variant B will then present as a new flake to whoever meets it next.

Running tally: #89 three legs, #91 one, #90 one, #95 one. Every gating level except 36 has been hit at least once. gh run rerun --failed has been the stopgap on each, which is not sustainable — at this rate roughly every PR needs at least one re-run.

The constraint is unchanged and matters more now, not less: #64's MIME mutation works by making the root absent. A fix that tolerates absence — at either depth — makes that mutation vacuous and leaves the picker test proving nothing. Whatever lands must keep it red.

**Second failure variant, and it widens the fix.** Four more legs have failed since this was filed, all on diffs that cannot cause them (two KDoc comments, a MIME lookup table, a README paragraph). **Variant A — the root never appears** (what this ticket describes): ``` never showed BySelector [TEXT='\QLMC R38 fixtures\E'] ``` API 33 (#90), 34, 35, 37 (#89). **Variant B — the root appears, the FILE does not:** ``` never showed BySelector [TEXT='\Qlmc-r38-fixture.mp4\E'] ``` API 37 (#95). So this is **not only root discovery**. The same "picker content has not settled" problem bites at two depths — the roots list, and the document list inside a root once opened. A fix that only makes the root wait or retry leaves variant B live, and variant B will then present as a *new* flake to whoever meets it next. **Running tally:** #89 three legs, #91 one, #90 one, #95 one. Every gating level except 36 has been hit at least once. `gh run rerun --failed` has been the stopgap on each, which is not sustainable — at this rate roughly every PR needs at least one re-run. **The constraint is unchanged and matters more now, not less:** #64's MIME mutation works *by making the root absent*. A fix that tolerates absence — at either depth — makes that mutation vacuous and leaves the picker test proving nothing. Whatever lands must keep it red.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: JMR-dev/LibreMediaConverter#93