diff --git a/examples/android-room/build.gradle.kts b/examples/android-room/build.gradle.kts index 26de53b6f..087a1973f 100644 --- a/examples/android-room/build.gradle.kts +++ b/examples/android-room/build.gradle.kts @@ -73,6 +73,5 @@ sentry { tracingInstrumentation { forceInstrumentDependencies.set(true) - debug.set(true) } } diff --git a/examples/android-room/src/main/java/io/sentry/android/roomsample/SampleApp.kt b/examples/android-room/src/main/java/io/sentry/android/roomsample/SampleApp.kt index e49451e85..e677b61fb 100644 --- a/examples/android-room/src/main/java/io/sentry/android/roomsample/SampleApp.kt +++ b/examples/android-room/src/main/java/io/sentry/android/roomsample/SampleApp.kt @@ -37,10 +37,7 @@ class SampleApp : Application() { tracks.forEachIndexed { index, track -> // add lyrics for every 2nd track if (index % 2 == 0) { - val dir = File("$filesDir${File.separatorChar}lyrics") - dir.mkdirs() - - val file = File(dir, "${track.id}.txt") + val file = File(filesDir, "${track.id}.txt") file.writeText(DEFAULT_LYRICS) } } diff --git a/examples/android-room/src/main/java/io/sentry/android/roomsample/ui/LyricsActivity.kt b/examples/android-room/src/main/java/io/sentry/android/roomsample/ui/LyricsActivity.kt index f2f2ce82a..110b3b049 100644 --- a/examples/android-room/src/main/java/io/sentry/android/roomsample/ui/LyricsActivity.kt +++ b/examples/android-room/src/main/java/io/sentry/android/roomsample/ui/LyricsActivity.kt @@ -9,12 +9,14 @@ import io.sentry.Sentry import io.sentry.SpanStatus import io.sentry.android.roomsample.R import io.sentry.android.roomsample.data.Track +import io.sentry.android.roomsample.util.Filesystem import java.io.File @SuppressLint("SetTextI18n") class LyricsActivity : ComponentActivity() { - private lateinit var file: File private lateinit var lyricsInput: EditText + private lateinit var filesystem: Filesystem + private lateinit var track: Track override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -29,16 +31,14 @@ class LyricsActivity : ComponentActivity() { lyricsInput = findViewById(R.id.lyrics) val toolbar = findViewById(R.id.toolbar) - val track: Track = intent.getSerializableExtra(TRACK_EXTRA_KEY) as Track + track = intent.getSerializableExtra(TRACK_EXTRA_KEY) as Track + filesystem = intent.getSerializableExtra(FILESYSTEM_EXTRA_KEY) as Filesystem toolbar.title = "Lyrics for ${track.name}" val dir = File("$filesDir${File.separatorChar}lyrics") dir.mkdirs() - file = File(dir, "${track.id}.txt") - if (file.exists()) { - lyricsInput.setText(file.readText()) - } + lyricsInput.setText(filesystem.read(this, "${track.id}.txt")) transaction.finish(SpanStatus.OK) } @@ -48,15 +48,13 @@ class LyricsActivity : ComponentActivity() { "ui.action.lyrics_finish", true ) - if (!file.exists()) { - file.createNewFile() - } - file.writeText(lyricsInput.text.toString()) + filesystem.write(this, "${track.id}.txt", lyricsInput.text.toString()) transaction.finish(SpanStatus.OK) super.onBackPressed() } companion object { const val TRACK_EXTRA_KEY = "LyricsActivity.Track" + const val FILESYSTEM_EXTRA_KEY = "LyricsActivity.Filesystem" } } diff --git a/examples/android-room/src/main/java/io/sentry/android/roomsample/ui/list/TrackAdapter.kt b/examples/android-room/src/main/java/io/sentry/android/roomsample/ui/list/TrackAdapter.kt index 59d44fcb7..e101c623e 100644 --- a/examples/android-room/src/main/java/io/sentry/android/roomsample/ui/list/TrackAdapter.kt +++ b/examples/android-room/src/main/java/io/sentry/android/roomsample/ui/list/TrackAdapter.kt @@ -1,8 +1,10 @@ package io.sentry.android.roomsample.ui.list +import android.app.AlertDialog import android.content.Intent import android.view.LayoutInflater import android.view.ViewGroup +import android.widget.ArrayAdapter import androidx.recyclerview.widget.RecyclerView import io.sentry.Sentry import io.sentry.SpanStatus @@ -11,6 +13,7 @@ import io.sentry.android.roomsample.SampleApp import io.sentry.android.roomsample.data.Track import io.sentry.android.roomsample.ui.EditActivity import io.sentry.android.roomsample.ui.LyricsActivity +import io.sentry.android.roomsample.util.Filesystem import kotlinx.coroutines.runBlocking class TrackAdapter : RecyclerView.Adapter() { @@ -62,12 +65,32 @@ class TrackAdapter : RecyclerView.Adapter() { holder.row.infoButton.setOnClickListener { val context = holder.row.context val track = data[holder.bindingAdapterPosition] - context.startActivity( - Intent( - context, - LyricsActivity::class.java - ).putExtra(LyricsActivity.TRACK_EXTRA_KEY, track) - ) + + /* ktlint-disable experimental:argument-list-wrapping */ + AlertDialog.Builder(context) + .setTitle("Choose File API") + .setNegativeButton("Cancel") { dialog, _ -> dialog.dismiss() } + .setAdapter( + ArrayAdapter( + context, + android.R.layout.select_dialog_item, + listOf( + "FileIOStream", + "FileReader/FileWriter", + "Context.openFileInput/Output" + ) + ) + ) { dialog, which -> + context.startActivity( + Intent( + context, + LyricsActivity::class.java + ).putExtra(LyricsActivity.TRACK_EXTRA_KEY, track) + .putExtra(LyricsActivity.FILESYSTEM_EXTRA_KEY, Filesystem.from(which)) + ) + dialog.dismiss() + }.show() + /* ktlint-enable experimental:argument-list-wrapping */ } } @@ -75,6 +98,7 @@ class TrackAdapter : RecyclerView.Adapter() { super.onViewRecycled(holder) holder.row.deleteButton.setOnClickListener(null) holder.row.editButton.setOnClickListener(null) + holder.row.infoButton.setOnClickListener(null) } inner class ViewHolder(val row: TrackRow) : RecyclerView.ViewHolder(row) diff --git a/examples/android-room/src/main/java/io/sentry/android/roomsample/util/Filesystem.kt b/examples/android-room/src/main/java/io/sentry/android/roomsample/util/Filesystem.kt new file mode 100644 index 000000000..f6d756e72 --- /dev/null +++ b/examples/android-room/src/main/java/io/sentry/android/roomsample/util/Filesystem.kt @@ -0,0 +1,66 @@ +package io.sentry.android.roomsample.util + +import android.content.Context as AndroidContext +import java.io.File +import java.io.FileReader +import java.io.FileWriter +import java.io.Serializable + +sealed class Filesystem : Serializable { + + abstract fun read(context: AndroidContext, filename: String): String + + abstract fun write(context: AndroidContext, filename: String, text: String) + + companion object { + fun from(which: Int) = when (which) { + 0 -> IOStream + 1 -> ReaderWriter + 2 -> Context + else -> error("Unknown File API") + } + } + + object ReaderWriter : Filesystem() { + + override fun read(context: AndroidContext, filename: String): String { + val file = File(context.filesDir, filename) + return if (file.exists()) FileReader(file).use { it.readText() } else "" + } + + override fun write(context: AndroidContext, filename: String, text: String) { + val file = File(context.filesDir, filename) + if (!file.exists()) { + file.createNewFile() + } + FileWriter(file).use { it.write(text) } + } + } + + object IOStream : Filesystem() { + override fun read(context: AndroidContext, filename: String): String { + val file = File(context.filesDir, filename) + return if (file.exists()) file.readText() else "" + } + + override fun write(context: AndroidContext, filename: String, text: String) { + val file = File(context.filesDir, filename) + if (!file.exists()) { + file.createNewFile() + } + file.writeText(text) + } + } + + object Context : Filesystem() { + override fun read(context: AndroidContext, filename: String): String { + val fis = context.openFileInput(filename) + return fis.bufferedReader().readText() + } + + override fun write(context: AndroidContext, filename: String, text: String) { + val fos = context.openFileOutput(filename, AndroidContext.MODE_PRIVATE) + fos.bufferedWriter().write(text) + } + } +} diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/ChainedInstrumentable.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/ChainedInstrumentable.kt new file mode 100644 index 000000000..926a19b62 --- /dev/null +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/ChainedInstrumentable.kt @@ -0,0 +1,43 @@ +@file:Suppress("UnstableApiUsage") + +package io.sentry.android.gradle.instrumentation + +import com.android.build.api.instrumentation.ClassContext +import java.util.LinkedList +import org.objectweb.asm.ClassVisitor + +class ChainedInstrumentable( + private val instrumentables: List = emptyList() +) : ClassInstrumentable { + + override fun getVisitor( + instrumentableContext: ClassContext, + apiVersion: Int, + originalVisitor: ClassVisitor, + parameters: SpanAddingClassVisitorFactory.SpanAddingParameters + ): ClassVisitor { + // build a chain of visitors in order they are provdied + val queue = LinkedList(instrumentables) + var prevVisitor = originalVisitor + var visitor: ClassVisitor? = null + while (queue.isNotEmpty()) { + val instrumentable = queue.poll() + + visitor = if (instrumentable.isInstrumentable(instrumentableContext)) { + instrumentable.getVisitor( + instrumentableContext, + apiVersion, + prevVisitor, + parameters + ) + } else { + prevVisitor + } + prevVisitor = visitor + } + return visitor ?: originalVisitor + } + + override fun isInstrumentable(data: ClassContext): Boolean = + instrumentables.any { it.isInstrumentable(data) } +} diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/SpanAddingClassVisitorFactory.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/SpanAddingClassVisitorFactory.kt index 69a0c6976..1e03f590e 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/SpanAddingClassVisitorFactory.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/SpanAddingClassVisitorFactory.kt @@ -8,6 +8,7 @@ import io.sentry.android.gradle.SentryPlugin import io.sentry.android.gradle.instrumentation.androidx.room.AndroidXRoomDao import io.sentry.android.gradle.instrumentation.androidx.sqlite.database.AndroidXSQLiteDatabase import io.sentry.android.gradle.instrumentation.androidx.sqlite.statement.AndroidXSQLiteStatement +import io.sentry.android.gradle.instrumentation.remap.RemappingInstrumentable import io.sentry.android.gradle.instrumentation.wrap.WrappingInstrumentable import io.sentry.android.gradle.util.warn import java.io.File @@ -44,7 +45,9 @@ abstract class SpanAddingClassVisitorFactory : AndroidXSQLiteDatabase(), AndroidXSQLiteStatement(), AndroidXRoomDao(), - WrappingInstrumentable() + ChainedInstrumentable( + listOf(WrappingInstrumentable(), RemappingInstrumentable()) + ) ) } diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/remap/RemappingInstrumentable.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/remap/RemappingInstrumentable.kt new file mode 100644 index 000000000..56c1b2b47 --- /dev/null +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/remap/RemappingInstrumentable.kt @@ -0,0 +1,30 @@ +@file:Suppress("UnstableApiUsage") + +package io.sentry.android.gradle.instrumentation.remap + +import com.android.build.api.instrumentation.ClassContext +import io.sentry.android.gradle.instrumentation.ClassInstrumentable +import io.sentry.android.gradle.instrumentation.SpanAddingClassVisitorFactory +import io.sentry.android.gradle.instrumentation.util.isSentryClass +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.commons.ClassRemapper +import org.objectweb.asm.commons.SimpleRemapper + +class RemappingInstrumentable : ClassInstrumentable { + companion object { + private val mapping = mapOf( + "java/io/FileReader" to "io/sentry/instrumentation/file/SentryFileReader", + "java/io/FileWriter" to "io/sentry/instrumentation/file/SentryFileWriter" + ) + } + + override fun getVisitor( + instrumentableContext: ClassContext, + apiVersion: Int, + originalVisitor: ClassVisitor, + parameters: SpanAddingClassVisitorFactory.SpanAddingParameters + ): ClassVisitor = + ClassRemapper(originalVisitor, SimpleRemapper(mapping)) + + override fun isInstrumentable(data: ClassContext): Boolean = !data.isSentryClass() +} diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/util/SentryPackageNameUtils.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/util/SentryPackageNameUtils.kt new file mode 100644 index 000000000..ac7026b63 --- /dev/null +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/util/SentryPackageNameUtils.kt @@ -0,0 +1,14 @@ +@file:Suppress("UnstableApiUsage") + +package io.sentry.android.gradle.instrumentation.util + +import com.android.build.api.instrumentation.ClassContext + +fun ClassContext.isSentryClass(): Boolean = + when { + currentClassData.className.startsWith("io.sentry") && + !currentClassData.className.startsWith("io.sentry.android.roomsample") && + !currentClassData.className.startsWith("io.sentry.samples") && + !currentClassData.className.startsWith("io.sentry.mobile") -> true + else -> false + } diff --git a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/wrap/WrappingInstrumentable.kt b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/wrap/WrappingInstrumentable.kt index 1cbfd566b..6183ab9c8 100644 --- a/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/wrap/WrappingInstrumentable.kt +++ b/plugin-build/src/main/kotlin/io/sentry/android/gradle/instrumentation/wrap/WrappingInstrumentable.kt @@ -9,6 +9,7 @@ import io.sentry.android.gradle.instrumentation.CommonClassVisitor import io.sentry.android.gradle.instrumentation.MethodContext import io.sentry.android.gradle.instrumentation.MethodInstrumentable import io.sentry.android.gradle.instrumentation.SpanAddingClassVisitorFactory +import io.sentry.android.gradle.instrumentation.util.isSentryClass import io.sentry.android.gradle.instrumentation.wrap.visitor.WrappingVisitor import org.objectweb.asm.ClassVisitor import org.objectweb.asm.MethodVisitor @@ -32,13 +33,7 @@ class WrappingInstrumentable : ClassInstrumentable { ) } - override fun isInstrumentable(data: ClassContext): Boolean { - return when { - data.currentClassData.className.startsWith("io.sentry") && - !data.currentClassData.className.startsWith("io.sentry.android.roomsample") -> false - else -> true - } - } + override fun isInstrumentable(data: ClassContext): Boolean = !data.isSentryClass() } class Wrap(private val classContext: ClassData) : MethodInstrumentable { diff --git a/plugin-build/src/test/kotlin/androidx/sqlite/db/SupportSQLiteOpenHelper.kt b/plugin-build/src/test/kotlin/androidx/sqlite/db/SupportSQLiteOpenHelper.kt new file mode 100644 index 000000000..849719678 --- /dev/null +++ b/plugin-build/src/test/kotlin/androidx/sqlite/db/SupportSQLiteOpenHelper.kt @@ -0,0 +1,5 @@ +package androidx.sqlite.db + +interface SupportSQLiteOpenHelper { + abstract class Callback +} diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/ChainedInstrumentableTest.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/ChainedInstrumentableTest.kt new file mode 100644 index 000000000..8354d378a --- /dev/null +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/ChainedInstrumentableTest.kt @@ -0,0 +1,108 @@ +package io.sentry.android.gradle.instrumentation + +import com.android.build.api.instrumentation.ClassContext +import io.sentry.android.gradle.instrumentation.fakes.TestClassContext +import io.sentry.android.gradle.instrumentation.fakes.TestClassData +import io.sentry.android.gradle.instrumentation.fakes.TestSpanAddingParameters +import java.io.File +import kotlin.test.assertTrue +import org.junit.Test +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.Opcodes + +class ChainedInstrumentableTest { + class Fixture { + fun getSut( + originalVisitor: ClassVisitor, + instrumentables: List = emptyList() + ): ClassVisitor { + return ChainedInstrumentable(instrumentables).getVisitor( + TestClassContext(TestClassData("RandomClass")), + Opcodes.ASM9, + originalVisitor, + TestSpanAddingParameters(inMemoryDir = File("")) + ) + } + } + + private val fixture = Fixture() + + @Test + fun `when empty instrumentables list returns original visitor`() { + val sut = fixture.getSut(OriginalVisitor()) + + assertTrue { sut is OriginalVisitor } + } + + @Test + fun `when no isInstrumentable found returns original visitor`() { + val sut = fixture.getSut( + OriginalVisitor(), + listOf( + FirstInstrumentable(isInstrumentable = false), + SecondInstrumentable(isInstrumentable = false) + ) + ) + + assertTrue { sut is OriginalVisitor } + } + + @Test + fun `skip non-instrumentables in the chain`() { + val sut = fixture.getSut( + OriginalVisitor(), + listOf( + FirstInstrumentable(isInstrumentable = false), + SecondInstrumentable(isInstrumentable = true) + ) + ) + + assertTrue { + sut is SecondInstrumentable.SecondVisitor && sut.prevVisitor is OriginalVisitor + } + } + + @Test + fun `all instrumentables`() { + val sut = + fixture.getSut(OriginalVisitor(), listOf(FirstInstrumentable(), SecondInstrumentable())) + + assertTrue { + sut is SecondInstrumentable.SecondVisitor && + sut.prevVisitor is FirstInstrumentable.FirstVisitor && + (sut.prevVisitor as FirstInstrumentable.FirstVisitor).prevVisitor is OriginalVisitor + } + } +} + +class OriginalVisitor : ClassVisitor(Opcodes.ASM9) + +class FirstInstrumentable(val isInstrumentable: Boolean = true) : ClassInstrumentable { + class FirstVisitor(prevVisitor: ClassVisitor) : ClassVisitor(Opcodes.ASM9, prevVisitor) { + val prevVisitor get() = cv + } + + override fun getVisitor( + instrumentableContext: ClassContext, + apiVersion: Int, + originalVisitor: ClassVisitor, + parameters: SpanAddingClassVisitorFactory.SpanAddingParameters + ): ClassVisitor = FirstVisitor(originalVisitor) + + override fun isInstrumentable(data: ClassContext): Boolean = isInstrumentable +} + +class SecondInstrumentable(val isInstrumentable: Boolean = true) : ClassInstrumentable { + class SecondVisitor(prevVisitor: ClassVisitor) : ClassVisitor(Opcodes.ASM9, prevVisitor) { + val prevVisitor get() = cv + } + + override fun getVisitor( + instrumentableContext: ClassContext, + apiVersion: Int, + originalVisitor: ClassVisitor, + parameters: SpanAddingClassVisitorFactory.SpanAddingParameters + ): ClassVisitor = SecondVisitor(originalVisitor) + + override fun isInstrumentable(data: ClassContext): Boolean = isInstrumentable +} 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 9742e2bec..37ae8f285 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 @@ -8,6 +8,8 @@ import io.sentry.android.gradle.instrumentation.classloader.GeneratingMissingCla import io.sentry.android.gradle.instrumentation.fakes.TestClassContext import io.sentry.android.gradle.instrumentation.fakes.TestClassData import io.sentry.android.gradle.instrumentation.fakes.TestSpanAddingParameters +import io.sentry.android.gradle.instrumentation.remap.RemappingInstrumentable +import io.sentry.android.gradle.instrumentation.wrap.WrappingInstrumentable import java.io.FileInputStream import java.io.PrintWriter import java.io.StringWriter @@ -115,7 +117,15 @@ class VisitorTest( selectDaoTestParameters("LiveDataSingle"), selectDaoTestParameters("LiveDataList"), selectDaoTestParameters("Paging"), - selectDaoTestParameters("Impl") + selectDaoTestParameters("Impl"), + arrayOf("fileIO", "SQLiteCopyOpenHelper", WrappingInstrumentable(), null), + arrayOf("fileIO", "TypefaceCompatUtil", WrappingInstrumentable(), null), + arrayOf( + "fileIO", + "Test", + ChainedInstrumentable(listOf(WrappingInstrumentable(), RemappingInstrumentable())), + null + ) ) private fun roomDaoTestParameters(suffix: String = "") = arrayOf( diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/classloader/GeneratingMissingClassesClassLoader.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/classloader/GeneratingMissingClassesClassLoader.kt index c840e14d8..ff97ae63e 100644 --- a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/classloader/GeneratingMissingClassesClassLoader.kt +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/classloader/GeneratingMissingClassesClassLoader.kt @@ -3,6 +3,7 @@ package io.sentry.android.gradle.instrumentation.classloader import io.sentry.android.gradle.instrumentation.classloader.mapping.deletionDaoMissingClasses import io.sentry.android.gradle.instrumentation.classloader.mapping.insertionDaoMissingClasses import io.sentry.android.gradle.instrumentation.classloader.mapping.selectDaoMissingClasses +import io.sentry.android.gradle.instrumentation.classloader.mapping.sqliteCopyOpenHelperMissingClasses import io.sentry.android.gradle.instrumentation.classloader.mapping.updateDaoMissingClasses class GeneratingMissingClassesClassLoader : ClassLoader(getSystemClassLoader()) { @@ -12,7 +13,8 @@ class GeneratingMissingClassesClassLoader : ClassLoader(getSystemClassLoader()) *deletionDaoMissingClasses, *insertionDaoMissingClasses, *updateDaoMissingClasses, - *selectDaoMissingClasses + *selectDaoMissingClasses, + *sqliteCopyOpenHelperMissingClasses ) } diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/classloader/mapping/SQLiteCopyOpenHelperMapping.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/classloader/mapping/SQLiteCopyOpenHelperMapping.kt new file mode 100644 index 000000000..91f650d06 --- /dev/null +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/classloader/mapping/SQLiteCopyOpenHelperMapping.kt @@ -0,0 +1,9 @@ +package io.sentry.android.gradle.instrumentation.classloader.mapping + +import io.sentry.android.gradle.instrumentation.classloader.standardClassSource + +val sqliteCopyOpenHelperMissingClasses = arrayOf String>>( + "androidx.room.SQLiteCopyOpenHelper$1" to { name -> + standardClassSource(name, "androidx.sqlite.db.SupportSQLiteOpenHelper.Callback") + } +) diff --git a/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/util/SentryPackageNameUtilsTest.kt b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/util/SentryPackageNameUtilsTest.kt new file mode 100644 index 000000000..325f23559 --- /dev/null +++ b/plugin-build/src/test/kotlin/io/sentry/android/gradle/instrumentation/util/SentryPackageNameUtilsTest.kt @@ -0,0 +1,26 @@ +package io.sentry.android.gradle.instrumentation.util + +import io.sentry.android.gradle.instrumentation.fakes.TestClassContext +import kotlin.test.assertFalse +import kotlin.test.assertTrue +import org.junit.Test + +class SentryPackageNameUtilsTest { + + @Test + fun `test is sentry class or not`() { + assertTrue { TestClassContext("io.sentry.Sentry").isSentryClass() } + assertFalse { + TestClassContext("io.sentry.android.roomsample.MainActivity").isSentryClass() + } + assertFalse { + TestClassContext("io.sentry.samples.MainActivity").isSentryClass() + } + assertFalse { + TestClassContext("io.sentry.mobile.StartActivity").isSentryClass() + } + assertFalse { + TestClassContext("androidx.room.Dao").isSentryClass() + } + } +} diff --git a/plugin-build/src/test/resources/testFixtures/instrumentation/fileIO/Test.class b/plugin-build/src/test/resources/testFixtures/instrumentation/fileIO/Test.class new file mode 100644 index 000000000..7a174b2c3 Binary files /dev/null and b/plugin-build/src/test/resources/testFixtures/instrumentation/fileIO/Test.class differ