Skip to content

test(ble): add Robolectric coverage for the bonding-interruption fix (#5849)#5850

Merged
jamesarich merged 2 commits into
mainfrom
claude/great-engelbart-b42158
Jun 18, 2026
Merged

test(ble): add Robolectric coverage for the bonding-interruption fix (#5849)#5850
jamesarich merged 2 commits into
mainfrom
claude/great-engelbart-b42158

Conversation

@jamesarich

Copy link
Copy Markdown
Collaborator

The BLE bonding-interruption fix in #5849 shipped with no automated coverage
the affected classes are in androidMain and touch the Android Bluetooth framework,
and neither module had any Android-target (Robolectric) test harness. This PR closes
that gap and adds reusable scaffolding so BLE test coverage can grow on top of it.

No production code is changed — bond() and requestBonding() are exercised exactly
as shipped.

Note

Built on top of #5849 (currently in the merge queue). Once #5849 lands on main
this rebases cleanly; until then the two production methods under test come from
that PR.

🌟 New Tests

  • AndroidBluetoothRepositoryBondTest (core/ble, Robolectric) — covers the real
    bond(): createBond()==false re-check resolving to BONDED (mid-method race, via a
    scoped custom shadow), staying in BONDING then resuming on a later
    ACTION_BOND_STATE_CHANGED(BOND_BONDED) broadcast, and failing with
    "Failed to initiate bonding" otherwise; plus the already-bonded early return,
    createBond()==true happy path, rejection (BOND_NONE from BONDING), a spurious
    BOND_NONE being ignored, and isBonded/isValid.
  • AndroidScannerViewModelBondingTest (feature/connections, Robolectric) — covers
    requestBonding via the public onSelected: the transport is armed on bond success
    and on a generic exception (the interrupted-bonding case the fix targets), and is
    not armed — with an error surfaced — on a permissions SecurityException.

🛠️ Reusable test scaffolding

  • RobolectricBleBonding (core/testing/androidMain) — a small DSL over
    ShadowBluetoothDevice for driving bond state and firing bond-state broadcasts.
  • FakeBluetoothRepository made failure-injectable (additive, backward-compatible:
    bondOutcome defaults to the existing success behavior; records bondCalls).
  • ScannerViewModelHarness (feature/connections/commonTest) bundles the ViewModel's
    collaborators; the existing ScannerViewModelTest was refactored onto it (no
    duplicated construction).

🧹 Chores

  • Add androidHostTest dependencies (core:testing, coroutines-test, androidx-test-ext)
    to core/ble and feature/connections. No new modules/source-sets, so no CI
    path-filter change.

Testing Performed

  • New: AndroidBluetoothRepositoryBondTest (9), AndroidScannerViewModelBondingTest (4).
  • Refactored: ScannerViewModelTest onto the shared harness (assertions unchanged).
  • ./gradlew spotlessCheck detekt assembleDebug :core:testing:allTests :core:ble:allTests :feature:connections:allTests — all green.

Chose Robolectric (androidHostTest) over instrumented tests: it simulates
everything needed (bond state, createBond(), bond-state broadcasts) on the JVM, runs
without a device in CI, and required no production seam — consistent with current
Android guidance to prefer Robolectric for framework-dependent unit logic.

jamesarich and others added 2 commits June 18, 2026 12:51
…ectric harness

Adds the first Android-target (androidHostTest / Robolectric) test coverage for
the BLE bonding-interruption fix, plus reusable scaffolding so future BLE tests
have minimal boilerplate. No production code changed.

- core/testing: make the shared FakeBluetoothRepository failure-injectable
  (BondOutcome Success/Fail/Security + bondCalls recording, additive/backward
  compatible); add a reusable Robolectric bond-state DSL (RobolectricBleBonding);
  androidMain failBondWithSecurityException helper (SecurityException is JVM-only).
- core/ble: AndroidBluetoothRepositoryBondTest exercises the real bond() —
  createBond()==false → BONDING then broadcast resume / BOND_NONE → "Failed to
  initiate bonding" / mid-method race to BONDED (custom shadow); plus already-bonded
  early return, BOND_NONE-from-BONDING failure, isBonded/isValid.
- feature/connections: AndroidScannerViewModelBondingTest covers requestBonding via
  the public onSelected — armed on success and on generic Exception, not armed +
  error surfaced on SecurityException; extract ScannerViewModelHarness and refactor
  the existing ScannerViewModelTest onto it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…pers, add cases

Refinements from craft-review (no production change):
- AndroidScannerViewModelBondingTest: run on the harness TestDispatcher so the
  scheduler that advanceUntilIdle() drives is the one the ViewModel actually uses.
- AndroidBluetoothRepositoryBondTest: add explicit assertions to the two previously
  assertion-free tests (already-bonded now also asserts isBonded); add coverage for
  the createBond()==true happy path and for a spurious BOND_NONE (prev != BONDING)
  being ignored. Now 9 tests.
- RobolectricBleBonding: remove unused denyBluetoothConnectPermission and
  resetBleBondingShadows; document distinct-MAC isolation.
- FakeBluetoothRepository: note the BondOutcome Fail/Security split is call-site
  intent only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added the testing Test additions or modifications label Jun 18, 2026
@jamesarich jamesarich marked this pull request as ready for review June 18, 2026 18:15
@jamesarich jamesarich added this pull request to the merge queue Jun 18, 2026
Merged via the queue into main with commit 4e7e4c3 Jun 18, 2026
18 checks passed
@jamesarich jamesarich deleted the claude/great-engelbart-b42158 branch June 18, 2026 18:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

testing Test additions or modifications

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant