Skip to content

fix(test): stop androidApp unit tests opening the app database - #7070

Merged
jamesarich merged 1 commit into
mainfrom
fix/androidapp-robolectric-real-application
Sep 8, 2026
Merged

jamesarich merged 1 commit into
mainfrom
fix/androidapp-robolectric-real-application

Conversation

@jamesarich

@jamesarich jamesarich commented Sep 8, 2026 •

Copy link
Copy Markdown
Collaborator

androidApp unit tests keep taking the whole test fork down with a native SIGSEGV, which ejects whatever PR is in the merge queue with no failing test named. #7020 was ejected twice on 2026-09-08 and #7005 hit the same thing on 2026-09-03. In every case the PR's own run of that shard had passed.

Robolectric boots the manifest's MeshUtilApplication for any test that does not override it. Its background init opens the app's Room3 database on BundledSQLiteDriver, and onTerminate cannot reliably close it: DatabaseManager.close() bound-waits WRITER_DRAIN_TIMEOUT_MS (5s) for the in-flight open, then deliberately retains the pools for a later close retry that a test never makes. Robolectric destroys that test's temp data dir out from under the live connection, and the next test in the fork dies in the sqlite JNI (walIndexTryHdr, sqlite3VdbeHalt, sqlite3ExprDeleteNN), exit 134.

Four androidApp test classes were booting the production Application. Three of them (KmlToGoogleLayerTest, MapLayerOpacityTest, KmlImportTest) only wanted android.graphics and had no teardown at all.

🐛 Bug Fixes

  • robolectric.properties: default the unit-test Application to android.app.Application, so booting the production one is opt-in per test.
  • MeshUtilApplication: move the onCreate background launches into protected open fun startBackgroundInit(). CoilImageLoaderLifecycleTest now boots a subclass that no-ops it, so it still asserts the real Koin/Coil wiring without opening a database. GoogleMeshUtilApplication still launches its own AppFunctions sync after super.onCreate(); that test pins MeshUtilApplication::class so it never runs there.
  • ShareMessageDeepLinkTest no longer needs its cancelBackgroundInit teardown.
  • cancelBackgroundInit is private again and its doc no longer claims Robolectric skips onTerminate. It does call it, from AndroidTestEnvironment.tearDownApplication.

🧹 Chores

  • Upload hs_err_pid*.log from the test shards on failure. Both ejecting runs produced no artifacts at all, so the JVM crash report, the only thing that names the crashing thread, was gone.

Testing Performed

  • :androidApp:testGoogleDebugUnitTest and :androidApp:testFdroidDebugUnitTest with --rerun: green. No androidx_sqliteJni, BundledSQLiteDriver or MeshtasticDatabase reference is left in either task's results. The only database traffic remaining is WorkManager's own Room 2.x through androidx.sqlite.db.framework, which is the shadowed framework path sqliteMode=LEGACY already covers.
  • Full baseline: spotlessApply spotlessCheck detekt assembleDebug test allTests.

No behaviour change in the app. startBackgroundInit() runs from the same point in onCreate.

Summary by CodeRabbit

  • Bug Fixes

    • Improved Android test stability by preventing database-related native crashes during Robolectric teardown.
    • Added collection of JVM crash reports when Android test shards fail, making failures easier to investigate.
  • Refactor

    • Improved control over application background initialization, including isolated test setup that avoids unnecessary startup work.

Robolectric boots the manifest's MeshUtilApplication for any test that does not
override it, and its background init opens a Room3 database on
BundledSQLiteDriver. onTerminate does try to close that database at teardown,
but close() gives up after a 5s drain when the open is still in flight and
deliberately retains the pools for a later retry a test never makes. Robolectric
then deletes the temp data dir under the live connection, and the next test in
the fork dies with SIGSEGV in the sqlite JNI -- exit 134, no test named, and
whatever PR was in the merge queue is silently ejected.

Four test classes were booting the production Application, three of them
incidentally. Default the unit-test Application to android.app.Application so it
is opt-in, and give the one test that needs the real Koin graph a subclass with
background init suppressed.

Also upload hs_err_pid*.log from the test shards: that run produced no
artifacts, so a native crash there is currently undiagnosable.
@coderabbitai

coderabbitai Bot commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 3db1cc0d-4886-4a17-a131-9fdfa7cc152e

📥 Commits

Reviewing files that changed from the base of the PR and between fecc241 and 0d01adb.

📒 Files selected for processing (5)
  • .github/workflows/reusable-check.yml
  • androidApp/src/main/kotlin/org/meshtastic/app/MeshUtilApplication.kt
  • androidApp/src/test/kotlin/org/meshtastic/app/CoilImageLoaderLifecycleTest.kt
  • androidApp/src/test/kotlin/org/meshtastic/app/ShareMessageDeepLinkTest.kt
  • androidApp/src/test/resources/robolectric.properties
💤 Files with no reviewable changes (1)
  • androidApp/src/test/kotlin/org/meshtastic/app/ShareMessageDeepLinkTest.kt

Included review availability: Your plan provides up to 4 included reviews per hour; 2 remain after this review.


📝 Walkthrough

Walkthrough

The change separates application background initialization from onCreate, disables that initialization in affected Robolectric tests, changes the default test application, and uploads JVM crash logs from failed test shards.

Changes

Android test lifecycle and diagnostics

Layer / File(s) Summary
Background initialization seam
androidApp/src/main/kotlin/org/meshtastic/app/MeshUtilApplication.kt
MeshUtilApplication delegates background initialization to overridable startBackgroundInit(). cancelBackgroundInit() is now private.
Robolectric application isolation
androidApp/src/test/kotlin/org/meshtastic/app/CoilImageLoaderLifecycleTest.kt, androidApp/src/test/kotlin/org/meshtastic/app/ShareMessageDeepLinkTest.kt, androidApp/src/test/resources/robolectric.properties
Affected tests no longer start production background initialization. Robolectric defaults to android.app.Application, and the configuration documents native SQLite teardown failures.
CI JVM crash artifacts
.github/workflows/reusable-check.yml
Failed test shards upload hs_err_pid*.log and replay_pid*.log files with shard-specific names and seven-day retention.

Priority: ⬇️ Low — Defer this narrow test-isolation change because it prevents Robolectric database crashes without changing app behavior.

Estimated code review effort: 2 (Simple) | ~10 minutes

Severity of issue fixed: Low

Merge Risk: ⚪ Minimal · up to 0d01a

This change isolates Android unit tests from production database startup while preserving application initialization behavior and adds crash-log collection for failed test shards. No current merge-blocking risk is identified.

🚥 Pre-merge checks | ✅ 7 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Regression Coverage For Changed Behavior ⚠️ Warning Coverage gap: androidApp/src/test/resources/robolectric.properties changes Robolectric's default Application to android.app.Application, which is the direct fix for the native database teardown cr… Add a Robolectric regression test in the common Android unit-test source set with no @Config(application = ...) override. Obtain the application through ApplicationProvider and assert that its class is android.app.Application and not …
✅ Passed checks (7 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: preventing androidApp unit tests from opening the app database.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Sibling Call Sites And Presence Semantics ✅ Passed The custom check is not triggered. The pull request changes only the CI workflow, Robolectric application setup, and application lifecycle/test code. The exact HEAD commit diff contains no NodeItem, N…
Tests Prove The Path, Not The End State ✅ Passed PASS. The only changed test sources modify Robolectric setup and teardown; they add no weak end-state assertions. CoilImageLoaderLifecycleTest checks exact identity between the Koin-resolved `ImageL…
Moved Code Diffed Against Its Original ✅ Passed The extracted background-init body is mechanically identical to the old onCreate body, including all validation, exception behavior, coroutine scope, dispatcher, and launch policies. MeshUtilApplicati…
Full details: Regression Coverage For Changed Behavior

Explanation

Coverage gap: androidApp/src/test/resources/robolectric.properties changes Robolectric's default Application to android.app.Application, which is the direct fix for the native database teardown crash. No test asserts this default. ShareMessageDeepLinkTest and the KML/map tests inherit the setting but only test their feature behavior; CoilImageLoaderLifecycleTest explicitly supplies ImageLoaderOnlyApplication. Without the properties change, these tests can still pass while booting MeshUtilApplication, so the central regression condition is not directly covered. The startBackgroundInit() extraction itself is behavior-preserving in the diff and does not require a separate test under the stated policy.

Resolution

Add a Robolectric regression test in the common Android unit-test source set with no @Config(application = ...) override. Obtain the application through ApplicationProvider and assert that its class is android.app.Application and not MeshUtilApplication. Run it in both Google and F-Droid unit-test tasks, because both manifests provide production Application subclasses. This test must fail when the application=android.app.Application property is removed.

  • Fix all pre-merge checks with AI

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added bugfix PR tag repo Repository maintenance labels Sep 8, 2026
@jamesarich
jamesarich marked this pull request as ready for review September 8, 2026 14:29
@jamesarich
jamesarich enabled auto-merge September 8, 2026 14:30
@jamesarich
jamesarich added this pull request to the merge queue Sep 8, 2026
Merged via the queue into main with commit 796c9df Sep 8, 2026
17 checks passed
@jamesarich
jamesarich deleted the fix/androidapp-robolectric-real-application branch September 8, 2026 14:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix PR tag repo Repository maintenance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant