diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 1306c7fff..0667dfea1 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -3,7 +3,7 @@ agp = "8.10.1"
asm = "9.4" # // compatibility matrix -> https://developer.android.com/reference/tools/gradle-api/7.1/com/android/build/api/instrumentation/InstrumentationContext#apiversion
ktfmt = "0.51"
-sqlite = "2.1.0"
+sqlite = "2.3.0"
sentry = "8.46.0"
# Pinned to the last release that shipped sentry-android-okhttp; the module was removed
# in 8.0.0, so there is no newer version to update to. Used only to verify the legacy
@@ -60,9 +60,9 @@ sentryAndroidOkhttp = { group = "io.sentry", name = "sentry-android-okhttp", ver
sentrySpringBootJakarta = { group = "io.sentry", name = "sentry-spring-boot-starter-jakarta", version.ref = "sentry" }
# test
+androidxCore = { group = "androidx.core", name = "core", version = "1.15.0" }
arscLib = { group = "io.github.reandroid", name = "ARSCLib", version = "1.1.4" }
mockitoKotlin = { group = "org.mockito.kotlin", name = "mockito-kotlin", version = "5.4.0" }
-# Room & Room 3 runtime versions must match RoomDatabase$Builder bytecode fixtures (see SQLiteDriverBytecodeTestUtil)
roomRuntimeAndroid = { group = "androidx.room", name = "room-runtime-android", version = "2.7.0" }
room3RuntimeAndroid = { group = "androidx.room3", name = "room3-runtime-android", version = "3.0.0-alpha06" }
zip4j = { group = "net.lingala.zip4j", name = "zip4j", version = "2.11.5" }
diff --git a/plugin-build/build.gradle.kts b/plugin-build/build.gradle.kts
index d211558b7..94e76f5c5 100644
--- a/plugin-build/build.gradle.kts
+++ b/plugin-build/build.gradle.kts
@@ -55,6 +55,7 @@ dependencies {
testImplementation(libs.asmCommons)
// we need these dependencies for tests, because the bytecode verifier also analyzes superclasses
+ testImplementationAar(libs.androidxCore)
testImplementationAar(libs.roomRuntimeAndroid)
testImplementationAar(libs.room3RuntimeAndroid)
testImplementation(libs.sample.coroutines.core)
diff --git a/plugin-build/gradle.lockfile b/plugin-build/gradle.lockfile
index dc202c615..b2041cf4b 100644
--- a/plugin-build/gradle.lockfile
+++ b/plugin-build/gradle.lockfile
@@ -1,12 +1,13 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
+androidx.core:core:1.15.0=testImplementationAar
androidx.databinding:databinding-common:8.10.1=testRuntimeClasspath
androidx.databinding:databinding-compiler-common:8.10.1=testRuntimeClasspath
androidx.room3:room3-runtime-android:3.0.0-alpha06=testImplementationAar
androidx.room:room-runtime-android:2.7.0=testImplementationAar
-androidx.sqlite:sqlite-framework:2.1.0=testImplementationAar
-androidx.sqlite:sqlite:2.1.0=testImplementationAar
+androidx.sqlite:sqlite-framework:2.3.0=testImplementationAar
+androidx.sqlite:sqlite:2.3.0=testImplementationAar
com.android.databinding:baseLibrary:8.10.1=testRuntimeClasspath
com.android.tools.analytics-library:crash:31.10.1=testRuntimeClasspath
com.android.tools.analytics-library:protos:31.10.1=testRuntimeClasspath
diff --git a/plugin-build/gradle/verification-metadata.xml b/plugin-build/gradle/verification-metadata.xml
index 402e9968f..2479ada4a 100644
--- a/plugin-build/gradle/verification-metadata.xml
+++ b/plugin-build/gradle/verification-metadata.xml
@@ -5,6 +5,14 @@
false
+
+
+
+
+
+
+
+
@@ -45,6 +53,14 @@
+
+
+
+
+
+
+
+
@@ -53,6 +69,14 @@
+
+
+
+
+
+
+
+
diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/InstrumentationBytecodeTestUtil.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/InstrumentationBytecodeTestUtil.kt
new file mode 100644
index 000000000..201332287
--- /dev/null
+++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/InstrumentationBytecodeTestUtil.kt
@@ -0,0 +1,13 @@
+package io.sentry.android.gradle.instrumentation
+
+internal object InstrumentationBytecodeTestUtil {
+
+ fun loadClasspathFixture(className: String): ByteArray? {
+ val resourcePath = className.replace('.', '/') + ".class"
+ return InstrumentationBytecodeTestUtil::class
+ .java
+ .classLoader
+ .getResourceAsStream(resourcePath)
+ ?.use { it.readBytes() }
+ }
+}
diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/VisitorTest.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/VisitorTest.kt
index 819d56cc3..a0f07d07b 100644
--- a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/VisitorTest.kt
+++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/VisitorTest.kt
@@ -50,11 +50,15 @@ class VisitorTest(
fun `instrumented class passes Java verifier`() {
// first we read the original bytecode and pass it through the ClassWriter, so it computes
// MAXS for us automatically (that's what AGP will do as well)
- val inputStream =
- FileInputStream(
- "src/test/resources/testFixtures/instrumentation/" + "$instrumentedProject/$className.class"
- )
- val classReader = ClassReader(inputStream)
+ val inputBytes =
+ when {
+ classContext != null ->
+ InstrumentationBytecodeTestUtil.loadClasspathFixture(
+ classContext.currentClassData.className
+ ) ?: loadFilesystemFixture()
+ else -> loadFilesystemFixture()
+ }
+ val classReader = ClassReader(inputBytes)
val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
val classContext = this.classContext ?: TestClassContext(instrumentable.fqName)
val classVisitor =
@@ -93,6 +97,12 @@ class VisitorTest(
)
}
+ private fun loadFilesystemFixture(): ByteArray =
+ FileInputStream(
+ "src/test/resources/testFixtures/instrumentation/" + "$instrumentedProject/$className.class"
+ )
+ .use { it.readBytes() }
+
@After
fun printLogs() {
// only print bytecode when running locally
@@ -111,29 +121,39 @@ class VisitorTest(
fun parameters() =
listOf(
arrayOf(
- "androidxSqlite",
+ "androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory",
"FrameworkSQLiteOpenHelperFactory",
AndroidXSQLiteOpenHelper(),
- null,
+ TestClassContext(
+ TestClassData(
+ className = "androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory",
+ interfaces = listOf("androidx.sqlite.db.SupportSQLiteOpenHelper\$Factory"),
+ )
+ ),
),
- arrayOf("androidxSqlite", "FrameworkSQLiteDatabase", AndroidXSQLiteDatabase(), null),
arrayOf(
- "androidxSqlite",
+ "androidx.sqlite.db.framework.FrameworkSQLiteDatabase",
+ "FrameworkSQLiteDatabase",
+ AndroidXSQLiteDatabase(),
+ TestClassContext("androidx.sqlite.db.framework.FrameworkSQLiteDatabase"),
+ ),
+ arrayOf(
+ "androidx.sqlite.db.framework.FrameworkSQLiteStatement",
"FrameworkSQLiteStatement",
AndroidXSQLiteStatement(SemVer(2, 3, 0)),
- null,
+ TestClassContext("androidx.sqlite.db.framework.FrameworkSQLiteStatement"),
),
- // RoomDatabase$Builder fixtures: see SQLiteDriverBytecodeTestUtil (extracted from published
- // AARs).
+ // RoomDatabase$Builder bytecode: loaded by FQN from Room runtime AARs on the test
+ // classpath.
arrayOf(
- "androidxRoom",
+ "androidx.room.RoomDatabase\$Builder",
"RoomDatabase\$Builder",
AndroidXSQLiteDriver(),
TestClassContext("androidx.room.RoomDatabase\$Builder"),
),
arrayOf(
- "androidxRoom",
- "RoomDatabase3\$Builder",
+ "androidx.room3.RoomDatabase\$Builder",
+ "RoomDatabase\$Builder",
AndroidXSQLiteDriver(),
TestClassContext("androidx.room3.RoomDatabase\$Builder"),
),
@@ -172,7 +192,12 @@ class VisitorTest(
kspTracksDaoTestParameters("insertAll"),
kspTracksDaoTestParameters("update"),
arrayOf("fileIO", "SQLiteCopyOpenHelper", WrappingInstrumentable(), null),
- arrayOf("fileIO", "TypefaceCompatUtil", WrappingInstrumentable(), null),
+ arrayOf(
+ "androidx.core.graphics.TypefaceCompatUtil",
+ "TypefaceCompatUtil",
+ WrappingInstrumentable(),
+ TestClassContext("androidx.core.graphics.TypefaceCompatUtil"),
+ ),
arrayOf(
"fileIO",
"Test",
diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/androidx/sqlite/driver/SQLiteDriverBytecodeTestUtil.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/androidx/sqlite/driver/SQLiteDriverBytecodeTestUtil.kt
index 918f6d5c1..b30c0386f 100644
--- a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/androidx/sqlite/driver/SQLiteDriverBytecodeTestUtil.kt
+++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/androidx/sqlite/driver/SQLiteDriverBytecodeTestUtil.kt
@@ -1,7 +1,6 @@
package io.sentry.android.gradle.instrumentation.androidx.sqlite.driver
import io.sentry.android.gradle.instrumentation.androidx.sqlite.driver.visitor.SetDriverMethodVisitor
-import java.io.FileInputStream
import org.objectweb.asm.ClassReader
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Type
@@ -11,31 +10,6 @@ import org.objectweb.asm.tree.MethodNode
internal object SQLiteDriverBytecodeTestUtil {
- private const val FIXTURES_ROOT = "src/test/resources/testFixtures/instrumentation/androidxRoom"
-
- /**
- * Room `Builder` bytecode fixtures extracted from published AARs:
- * - `RoomDatabase$Builder.class`: `androidx.room:room-runtime-android:2.7.0`
- * - `RoomDatabase3$Builder.class`: `androidx.room3:room3-runtime-android:3.0.0-alpha06`
- *
- * Extract from Google Maven by unzipping each AAR's `classes.jar` and copying
- * `androidx/room/RoomDatabase$Builder.class` (or `androidx/room3/...`).
- *
- * `VisitorTest` needs matching Room runtime AARs (and coroutines) on the test classpath so ASM
- * can resolve types referenced by the real bytecode.
- */
- private val CLASS_NAME_TO_FIXTURE =
- mapOf(
- "androidx.room.RoomDatabase\$Builder" to "RoomDatabase\$Builder",
- "androidx.room3.RoomDatabase\$Builder" to "RoomDatabase3\$Builder",
- )
-
- fun loadRoomBuilderFixture(className: String): ByteArray {
- val fixtureName =
- CLASS_NAME_TO_FIXTURE[className] ?: error("No committed fixture for class $className")
- return FileInputStream("$FIXTURES_ROOT/$fixtureName.class").use { it.readBytes() }
- }
-
fun isWrapCall(insn: MethodInsnNode): Boolean =
insn.opcode == Opcodes.INVOKESTATIC &&
insn.owner == Type.getType(SetDriverMethodVisitor.SENTRY_SQLITE_DRIVER_TYPE).internalName &&
diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/androidx/sqlite/driver/visitor/SetDriverMethodVisitorTest.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/androidx/sqlite/driver/visitor/SetDriverMethodVisitorTest.kt
index 92f728473..30e6843ae 100644
--- a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/androidx/sqlite/driver/visitor/SetDriverMethodVisitorTest.kt
+++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/androidx/sqlite/driver/visitor/SetDriverMethodVisitorTest.kt
@@ -2,6 +2,7 @@
package io.sentry.android.gradle.instrumentation.androidx.sqlite.driver.visitor
+import io.sentry.android.gradle.instrumentation.InstrumentationBytecodeTestUtil
import io.sentry.android.gradle.instrumentation.androidx.sqlite.driver.AndroidXSQLiteDriver
import io.sentry.android.gradle.instrumentation.androidx.sqlite.driver.SQLiteDriverBytecodeTestUtil
import io.sentry.android.gradle.instrumentation.androidx.sqlite.driver.SetDriverMethodInstrumentable
@@ -50,7 +51,10 @@ class SetDriverMethodVisitorTest {
}
private fun instrument(className: String): ByteArray {
- val bytes = SQLiteDriverBytecodeTestUtil.loadRoomBuilderFixture(className)
+ val bytes =
+ requireNotNull(InstrumentationBytecodeTestUtil.loadClasspathFixture(className)) {
+ "Could not load $className from test classpath"
+ }
val classReader = ClassReader(bytes)
val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
val classVisitor =
diff --git a/plugin-build/src/test/resources/testFixtures/instrumentation/androidxSqlite/FrameworkSQLiteDatabase.class b/plugin-build/src/test/resources/testFixtures/instrumentation/androidxSqlite/FrameworkSQLiteDatabase.class
deleted file mode 100644
index d8cb1aeb2..000000000
Binary files a/plugin-build/src/test/resources/testFixtures/instrumentation/androidxSqlite/FrameworkSQLiteDatabase.class and /dev/null differ
diff --git a/plugin-build/src/test/resources/testFixtures/instrumentation/androidxSqlite/FrameworkSQLiteOpenHelperFactory.class b/plugin-build/src/test/resources/testFixtures/instrumentation/androidxSqlite/FrameworkSQLiteOpenHelperFactory.class
deleted file mode 100644
index 55010d3c1..000000000
Binary files a/plugin-build/src/test/resources/testFixtures/instrumentation/androidxSqlite/FrameworkSQLiteOpenHelperFactory.class and /dev/null differ
diff --git a/plugin-build/src/test/resources/testFixtures/instrumentation/androidxSqlite/FrameworkSQLiteStatement.class b/plugin-build/src/test/resources/testFixtures/instrumentation/androidxSqlite/FrameworkSQLiteStatement.class
deleted file mode 100644
index 668bf025d..000000000
Binary files a/plugin-build/src/test/resources/testFixtures/instrumentation/androidxSqlite/FrameworkSQLiteStatement.class and /dev/null differ
diff --git a/plugin-build/src/test/resources/testFixtures/instrumentation/fileIO/TypefaceCompatUtil.class b/plugin-build/src/test/resources/testFixtures/instrumentation/fileIO/TypefaceCompatUtil.class
deleted file mode 100644
index 58e3b5b53..000000000
Binary files a/plugin-build/src/test/resources/testFixtures/instrumentation/fileIO/TypefaceCompatUtil.class and /dev/null differ