Jetpack Compose UI (screens, gates) is currently JVM-unreachable, so it's excluded from JaCoCo JVM coverage via jacocoNonJvmTestableSurface in app/build.gradle.kts (each render-only file named individually) and only exercised by instrumented androidTest. That caps the coverage metric and leaves UI logic un-unit-tested.
Goal
Enable Robolectric-backed JVM unit testing of Compose UI. Robolectric runs the Android framework on the JVM; with the Compose test APIs (createComposeRule()) composables can be unit-tested without an emulator — bringing that UI into the JVM-testable + JaCoCo-counted surface, so those exclusions shrink and the coverage floor can be re-ratcheted up once the tests are stable.
Approach (this is the umbrella)
An Opus spike scopes it: verify Robolectric + Compose works on this stack (AGP 9.2 built-in Kotlin, Kotlin 2.4.0, Compose, JDK 21) with a runnable PoC, inventory the excluded UI, then decide:
One session → implement (infra + PoC + convert excluded UI to Robolectric JVM tests + drop those exclusions) + PR.
Too big → scoped sub-tickets (infra + per-area conversion batches + a final "re-ratchet the floor"), one Opus agent per ticket.
The JaCoCo floor re-ratchet is the FINAL step, only after the Compose tests are proven stable — NOT in the conversion PR(s).
Relates to #323 (coverage gate), #367 (excluded CacheEncryptionGate), and the "no mockk in instrumented tests" gotcha (Robolectric may sidestep it).
## Context
Jetpack Compose UI (screens, gates) is currently **JVM-unreachable**, so it's excluded from JaCoCo JVM coverage via `jacocoNonJvmTestableSurface` in `app/build.gradle.kts` (each render-only file named individually) and only exercised by instrumented `androidTest`. That caps the coverage metric and leaves UI logic un-unit-tested.
## Goal
Enable **Robolectric**-backed JVM unit testing of Compose UI. Robolectric runs the Android framework on the JVM; with the Compose test APIs (`createComposeRule()`) composables can be unit-tested without an emulator — bringing that UI into the JVM-testable + JaCoCo-counted surface, so those exclusions shrink and the coverage floor can be **re-ratcheted up** once the tests are stable.
## Approach (this is the umbrella)
An Opus spike scopes it: verify Robolectric + Compose works on this stack (AGP 9.2 built-in Kotlin, Kotlin 2.4.0, Compose, JDK 21) with a runnable PoC, inventory the excluded UI, then decide:
- **One session** → implement (infra + PoC + convert excluded UI to Robolectric JVM tests + drop those exclusions) + PR.
- **Too big** → scoped sub-tickets (infra + per-area conversion batches + a final "re-ratchet the floor"), one Opus agent per ticket.
The **JaCoCo floor re-ratchet is the FINAL step**, only after the Compose tests are proven stable — NOT in the conversion PR(s).
Relates to #323 (coverage gate), #367 (excluded `CacheEncryptionGate`), and the "no mockk<Context> in instrumented tests" gotcha (Robolectric may sidestep it).
#385 — v1→v2 \createComposeRule/\createAndroidComposeRule\ migration (Done, via #458)
#386 — final JaCoCo line-coverage floor re-ratchet (Done)
The umbrella was left OPEN/In-progress after the last sub-ticket landed (orphaned by an earlier session crash). No work remains; closing as completed.
Umbrella complete — closing. All decomposed work has landed:
- **#375** — Robolectric + Compose JVM testing infra + PoC (merged)
- **#376–#384** — Compose UI Robolectric conversion batches 1–9 (all Done)
- **#385** — v1→v2 \createComposeRule\/\createAndroidComposeRule\ migration (Done, via #458)
- **#386** — final JaCoCo line-coverage floor re-ratchet (Done)
The umbrella was left OPEN/In-progress after the last sub-ticket landed (orphaned by an earlier session crash). No work remains; closing as completed.
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.
Context
Jetpack Compose UI (screens, gates) is currently JVM-unreachable, so it's excluded from JaCoCo JVM coverage via
jacocoNonJvmTestableSurfaceinapp/build.gradle.kts(each render-only file named individually) and only exercised by instrumentedandroidTest. That caps the coverage metric and leaves UI logic un-unit-tested.Goal
Enable Robolectric-backed JVM unit testing of Compose UI. Robolectric runs the Android framework on the JVM; with the Compose test APIs (
createComposeRule()) composables can be unit-tested without an emulator — bringing that UI into the JVM-testable + JaCoCo-counted surface, so those exclusions shrink and the coverage floor can be re-ratcheted up once the tests are stable.Approach (this is the umbrella)
An Opus spike scopes it: verify Robolectric + Compose works on this stack (AGP 9.2 built-in Kotlin, Kotlin 2.4.0, Compose, JDK 21) with a runnable PoC, inventory the excluded UI, then decide:
The JaCoCo floor re-ratchet is the FINAL step, only after the Compose tests are proven stable — NOT in the conversion PR(s).
Relates to #323 (coverage gate), #367 (excluded
CacheEncryptionGate), and the "no mockk in instrumented tests" gotcha (Robolectric may sidestep it).Umbrella complete — closing. All decomposed work has landed:
The umbrella was left OPEN/In-progress after the last sub-ticket landed (orphaned by an earlier session crash). No work remains; closing as completed.