From d8a37d280c5a22931ab1bf9ef62cf7adcf0655ee Mon Sep 17 00:00:00 2001 From: Jason Ross Date: Fri, 3 Jul 2026 19:31:03 -0500 Subject: [PATCH] test(local): use ContextWrapper not mockk in provisioner test DatabaseProvisionerInstrumentedTest crashed in @Before setUp() on API 31/32 with ArrayIndexOutOfBoundsException (length=0; index=0), passing on API 29. The stack shows the throw is entirely in the test harness: mockk() -> MockK JvmMockFactoryHelper.isKotlinInline -> kotlin-reflect ReflectJavaMember.getValueParameters, which indexes parameterAnnotations[0] on an empty array. Mocking android.content.Context makes MockK walk the whole framework class with kotlin-reflect, and on Android 12/12L ART returns a parameter-annotation array shorter than the parameter-type array for some Context method, so kotlin-reflect throws. Production DatabaseProvisioner/DatabaseEncryption code never runs. Replace the mockk with a real ContextWrapper(appContext) that overrides getDatabasePath to route the cache file to the test DB and delegates everything else, sidestepping the framework-class reflection walk. Verified 3/3 pass on the dev36 GMD emulator; API 31/32 left to CI (images not installed locally). Co-Authored-By: Claude Opus 4.8 --- .../local/DatabaseProvisionerInstrumentedTest.kt | 12 ++++++++++-- emu36.txt | 0 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 emu36.txt diff --git a/app/src/androidTest/kotlin/org/libremail/data/local/DatabaseProvisionerInstrumentedTest.kt b/app/src/androidTest/kotlin/org/libremail/data/local/DatabaseProvisionerInstrumentedTest.kt index f3c5bbd..58f53dc 100644 --- a/app/src/androidTest/kotlin/org/libremail/data/local/DatabaseProvisionerInstrumentedTest.kt +++ b/app/src/androidTest/kotlin/org/libremail/data/local/DatabaseProvisionerInstrumentedTest.kt @@ -2,6 +2,7 @@ package org.libremail.data.local import android.content.Context +import android.content.ContextWrapper import androidx.room.Room import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -57,12 +58,19 @@ class DatabaseProvisionerInstrumentedTest { private val keyStore = mockk() private val settingsRepository = mockk() private val migrator = mockk() - private val context = mockk() + + // A real ContextWrapper, NOT a mockk: mocking android.content.Context makes MockK walk the + // whole framework class with kotlin-reflect (isKotlinInline), which trips an ART parameter-annotation + // length mismatch and throws ArrayIndexOutOfBoundsException on API 31/32 (it passes on API 29). The + // wrapper routes the provisioner's cache lookup to the test DB and delegates everything else. + private val context: Context = object : ContextWrapper(appContext) { + override fun getDatabasePath(name: String): File = + if (name == DatabaseFiles.NAME) dbFile else super.getDatabasePath(name) + } @Before fun setUp() { clean() - every { context.getDatabasePath(DatabaseFiles.NAME) } returns dbFile coEvery { keyStore.isClearPending() } returns false coEvery { keyStore.resolvePassphrase(any()) } returns passphrase coEvery { migrator.migrateIfNeeded() } just Runs diff --git a/emu36.txt b/emu36.txt new file mode 100644 index 0000000..e69de29