diff --git a/packages/core/RNSentryAndroidTester/app/build.gradle b/packages/core/RNSentryAndroidTester/app/build.gradle index 9a1ba87087..5d3d6c6c79 100644 --- a/packages/core/RNSentryAndroidTester/app/build.gradle +++ b/packages/core/RNSentryAndroidTester/app/build.gradle @@ -34,6 +34,10 @@ android { testLogging { events 'passed', 'skipped', 'failed', 'standardOut', 'standardError' } + // Absolute path to the script plugin under test and the Android SDK, so the GradleTestKit + // functional tests can apply the real sentry.gradle.kts and point a fixture build at the SDK. + systemProperty 'sentry.gradle.script', new File(rootDir, '../sentry.gradle.kts').absolutePath + systemProperty 'sentry.android.sdkDir', android.sdkDirectory.absolutePath } } @@ -48,6 +52,7 @@ dependencies { implementation 'com.google.android.material:material:1.5.0' implementation 'androidx.test:core-ktx:1.6.1' testImplementation 'junit:junit:4.13.2' + testImplementation gradleTestKit() testImplementation 'org.mockito:mockito-core:5.10.0' testImplementation 'org.mockito.kotlin:mockito-kotlin:5.2.1' testImplementation 'org.robolectric:robolectric:4.14.1' diff --git a/packages/core/RNSentryAndroidTester/app/src/test/java/io/sentry/react/gradle/SentryModulesTaskTest.kt b/packages/core/RNSentryAndroidTester/app/src/test/java/io/sentry/react/gradle/SentryModulesTaskTest.kt new file mode 100644 index 0000000000..eda8d0e72f --- /dev/null +++ b/packages/core/RNSentryAndroidTester/app/src/test/java/io/sentry/react/gradle/SentryModulesTaskTest.kt @@ -0,0 +1,202 @@ +package io.sentry.react.gradle + +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import java.io.File + +/** + * Functional tests for the `CollectModulesTask` wiring declared in `sentry.gradle.kts` (issue #6750 / + * PR #6753): `modules.json` must be generated into the build folder and registered as a generated + * assets source, never written into the version-controlled `src/main/assets` tree. + * + * Unlike the options task, the modules task lives inside `processVariant`, which requires a React + * Native bundle task (`createBundleJsAndAssets`) that exposes an extractable + * `--sourcemap-output`. The fixture stubs that bundle task with a tiny shell script that writes a + * source map, and points `collectModulesScript` at a fake node script so no real JS bundle is needed. + */ +class SentryModulesTaskTest { + @get:Rule + val tempFolder = TemporaryFolder() + + private val scriptPath: String = + System.getProperty("sentry.gradle.script") ?: error("sentry.gradle.script system property not set") + private val sdkDir: String = + System.getProperty("sentry.android.sdkDir") ?: error("sentry.android.sdkDir system property not set") + + private lateinit var projectDir: File + + private val modulesTaskPath = ":createBundleReleaseJsAndAssets_SentryCollectModules" + + /** All generated `modules.json` files under the build tree (AGP relocates the task output dir). */ + private fun generatedModules(): List = File(projectDir, "build").walkTopDown().filter { it.name == "modules.json" }.toList() + + private fun srcAssetsModules(): File = File(projectDir, "src/main/assets/modules.json") + + private fun writeFixture( + skipCollectModules: Boolean = false, + produceSourcemap: Boolean = true, + ) { + projectDir = tempFolder.newFolder("android") + + // Shell stub for the RN bundle task: parses `--sourcemap-output ` out of its args and + // writes a minimal source map there, mimicking the real bundle task's observable output. + val makeSourcemap = File(projectDir, "make-sourcemap.sh") + makeSourcemap.writeText( + """ + #!/bin/sh + out="" + while [ ${'$'}# -gt 0 ]; do + if [ "${'$'}1" = "--sourcemap-output" ]; then out="${'$'}2"; fi + shift + done + if [ -n "${'$'}out" ]; then + mkdir -p "${'$'}(dirname "${'$'}out")" + printf '{"version":3,"sources":[]}' > "${'$'}out" + fi + """.trimIndent(), + ) + + // Fake collectModules script: `node