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