Skip to content

Commit 5b9a46e

Browse files
authored
Merge 9c3ca09 into 5fd37fa
2 parents 5fd37fa + 9c3ca09 commit 5b9a46e

File tree

15 files changed

+239
-18
lines changed

15 files changed

+239
-18
lines changed

.github/workflows/agp-matrix.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: AGP Matrix Sample Release
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- release/**
8+
pull_request:
9+
10+
jobs:
11+
cancel-previous-workflow:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Cancel Previous Runs
15+
uses: styfle/cancel-workflow-action@b173b6ec0100793626c2d9e6b90435061f4fc3e5 # [email protected]
16+
with:
17+
access_token: ${{ github.token }}
18+
19+
agp-matrix-sample-release:
20+
runs-on: ubuntu-latest
21+
strategy:
22+
fail-fast: false
23+
matrix:
24+
agp: ['7.3.0','7.4.0-rc01','8.0.0-alpha09']
25+
26+
name: AGP Matrix Sample Release - AGP ${{ matrix.agp }}
27+
env:
28+
VERSION_AGP: ${{ matrix.agp }}
29+
30+
steps:
31+
- name: Checkout Repo
32+
uses: actions/checkout@v2
33+
34+
- name: Setup Java Version
35+
uses: actions/setup-java@v2
36+
with:
37+
distribution: 'temurin'
38+
java-version: '17'
39+
40+
- name: Build the Release variant
41+
uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef # pin@v2
42+
with:
43+
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
44+
arguments: sentry-android-integration-tests:sentry-test-agp:assembleRelease

buildSrc/src/main/java/Config.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import java.math.BigDecimal
22

33
object Config {
4+
val AGP = System.getenv("VERSION_AGP") ?: "7.3.0"
45
val kotlinVersion = "1.6.10"
56
val kotlinStdLib = "stdlib-jdk8"
67

@@ -11,7 +12,7 @@ object Config {
1112
val composeVersion = "1.1.1"
1213

1314
object BuildPlugins {
14-
val androidGradle = "com.android.tools.build:gradle:7.3.0"
15+
val androidGradle = "com.android.tools.build:gradle:$AGP"
1516
val kotlinGradlePlugin = "gradle-plugin"
1617
val buildConfig = "com.github.gmazzo.buildconfig"
1718
val buildConfigVersion = "3.0.3"
@@ -130,6 +131,7 @@ object Config {
130131
val composeNavigation = "androidx.navigation:navigation-compose:$navigationVersion"
131132
val composeActivity = "androidx.activity:activity-compose:1.4.0"
132133
val composeFoundation = "androidx.compose.foundation:foundation:$composeVersion"
134+
val composeUi = "androidx.compose.ui:ui:$composeVersion"
133135
val composeFoundationLayout = "androidx.compose.foundation:foundation-layout:$composeVersion"
134136
val composeMaterial = "androidx.compose.material3:material3:1.0.0-alpha13"
135137

gradle.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ org.gradle.parallel=true
66
# AndroidX required by AGP >= 3.6.x
77
android.useAndroidX=true
88

9+
# Required by AGP >= 8.0.x
10+
android.defaults.buildfeatures.buildconfig=true
11+
912
# Release information
1013
versionName=6.10.0
1114

sentry-android-core/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ dependencies {
108108
testImplementation(projects.sentryTestSupport)
109109
testImplementation(projects.sentryAndroidFragment)
110110
testImplementation(projects.sentryAndroidTimber)
111+
testImplementation(projects.sentryComposeHelper)
112+
testImplementation(projects.sentryAndroidNdk)
111113
testRuntimeOnly(Config.Libs.timber)
112114
testRuntimeOnly(Config.Libs.fragment)
113115
}

sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@
4141
@SuppressWarnings("Convert2MethodRef") // older AGP versions do not support method references
4242
final class AndroidOptionsInitializer {
4343

44+
static final String SENTRY_COMPOSE_INTEGRATION_CLASS_NAME =
45+
"io.sentry.compose.gestures.ComposeGestureTargetLocator";
46+
47+
static final String COMPOSE_CLASS_NAME = "androidx.compose.ui.node.Owner";
48+
4449
/** private ctor */
4550
private AndroidOptionsInitializer() {}
4651

@@ -143,15 +148,15 @@ static void initializeIntegrationsAndProcessors(
143148
if (options.getGestureTargetLocators().isEmpty()) {
144149
final List<GestureTargetLocator> gestureTargetLocators = new ArrayList<>(2);
145150
gestureTargetLocators.add(new AndroidViewGestureTargetLocator(isAndroidXScrollViewAvailable));
146-
try {
151+
152+
final boolean isComposeUpstreamAvailable =
153+
loadClass.isClassAvailable(COMPOSE_CLASS_NAME, options);
154+
final boolean isComposeAvailable =
155+
(isComposeUpstreamAvailable
156+
&& loadClass.isClassAvailable(SENTRY_COMPOSE_INTEGRATION_CLASS_NAME, options));
157+
158+
if (isComposeAvailable) {
147159
gestureTargetLocators.add(new ComposeGestureTargetLocator());
148-
} catch (NoClassDefFoundError error) {
149-
options
150-
.getLogger()
151-
.log(
152-
SentryLevel.DEBUG,
153-
"ComposeGestureTargetLocator not available, consider adding the `sentry-compose` library.",
154-
error);
155160
}
156161
options.setGestureTargetLocators(gestureTargetLocators);
157162
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android">
3+
4+
<uses-sdk
5+
tools:overrideLibrary="
6+
io.sentry.compose,
7+
io.sentry.android.ndk,
8+
androidx.navigation.compose,
9+
androidx.activity.compose,
10+
androidx.compose.animation,
11+
androidx.lifecycle.viewmodel.compose,
12+
androidx.compose.foundation.layout,
13+
androidx.compose.animation.core,
14+
androidx.compose.ui,
15+
androidx.compose.ui.text,
16+
androidx.compose.runtime.saveable,
17+
androidx.compose.ui.graphics,
18+
androidx.compose.ui.unit,
19+
androidx.compose.ui.geometry,
20+
androidx.compose.ui.util"/>
21+
</manifest>

sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@ import io.sentry.ILogger
88
import io.sentry.MainEventProcessor
99
import io.sentry.SentryOptions
1010
import io.sentry.android.core.cache.AndroidEnvelopeCache
11+
import io.sentry.android.core.internal.gestures.AndroidViewGestureTargetLocator
1112
import io.sentry.android.core.internal.modules.AssetsModulesLoader
1213
import io.sentry.android.core.internal.util.AndroidMainThreadChecker
1314
import io.sentry.android.fragment.FragmentLifecycleIntegration
1415
import io.sentry.android.timber.SentryTimberIntegration
16+
import io.sentry.compose.gestures.ComposeGestureTargetLocator
1517
import org.junit.runner.RunWith
1618
import org.mockito.kotlin.any
19+
import org.mockito.kotlin.eq
1720
import org.mockito.kotlin.mock
1821
import org.mockito.kotlin.whenever
1922
import java.io.File
@@ -66,7 +69,7 @@ class AndroidOptionsInitializerTest {
6669

6770
fun initSutWithClassLoader(
6871
minApi: Int = 16,
69-
classToLoad: Class<*>? = null,
72+
classesToLoad: List<String> = emptyList(),
7073
isFragmentAvailable: Boolean = false,
7174
isTimberAvailable: Boolean = false
7275
) {
@@ -89,7 +92,7 @@ class AndroidOptionsInitializerTest {
8992
sentryOptions,
9093
context,
9194
buildInfo,
92-
createClassMock(classToLoad),
95+
createClassMock(classesToLoad),
9396
isFragmentAvailable,
9497
isTimberAvailable
9598
)
@@ -101,10 +104,14 @@ class AndroidOptionsInitializerTest {
101104
return buildInfo
102105
}
103106

104-
private fun createClassMock(clazz: Class<*>?): LoadClass {
107+
private fun createClassMock(classes: List<String>): LoadClass {
105108
val loadClassMock = mock<LoadClass>()
106-
whenever(loadClassMock.loadClass(any(), any())).thenReturn(clazz)
107-
whenever(loadClassMock.isClassAvailable(any(), any<ILogger>())).thenReturn(clazz != null)
109+
classes.forEach {
110+
whenever(loadClassMock.loadClass(eq(it), any()))
111+
.thenReturn(Class.forName(it, false, this::class.java.classLoader))
112+
whenever(loadClassMock.isClassAvailable(eq(it), any<SentryOptions>()))
113+
.thenReturn(true)
114+
}
108115
return loadClassMock
109116
}
110117
}
@@ -267,23 +274,23 @@ class AndroidOptionsInitializerTest {
267274

268275
@Test
269276
fun `NdkIntegration will load SentryNdk class and add to the integration list`() {
270-
fixture.initSutWithClassLoader(classToLoad = SentryNdk::class.java)
277+
fixture.initSutWithClassLoader(classesToLoad = listOfNotNull(NdkIntegration.SENTRY_NDK_CLASS_NAME))
271278

272279
val actual = fixture.sentryOptions.integrations.firstOrNull { it is NdkIntegration }
273280
assertNotNull((actual as NdkIntegration).sentryNdkClass)
274281
}
275282

276283
@Test
277284
fun `NdkIntegration won't be enabled because API is lower than 16`() {
278-
fixture.initSutWithClassLoader(minApi = 14, classToLoad = SentryNdk::class.java)
285+
fixture.initSutWithClassLoader(minApi = 14, classesToLoad = listOfNotNull(NdkIntegration.SENTRY_NDK_CLASS_NAME))
279286

280287
val actual = fixture.sentryOptions.integrations.firstOrNull { it is NdkIntegration }
281288
assertNull((actual as NdkIntegration).sentryNdkClass)
282289
}
283290

284291
@Test
285292
fun `NdkIntegration won't be enabled, if class not found`() {
286-
fixture.initSutWithClassLoader(classToLoad = null)
293+
fixture.initSutWithClassLoader(classesToLoad = emptyList())
287294

288295
val actual = fixture.sentryOptions.integrations.firstOrNull { it is NdkIntegration }
289296
assertNull((actual as NdkIntegration).sentryNdkClass)
@@ -455,4 +462,26 @@ class AndroidOptionsInitializerTest {
455462

456463
assertTrue { fixture.sentryOptions.mainThreadChecker is AndroidMainThreadChecker }
457464
}
465+
466+
@Test
467+
fun `does not install ComposeGestureTargetLocator, if sentry-compose is not available`() {
468+
fixture.initSutWithClassLoader()
469+
470+
assertTrue { fixture.sentryOptions.gestureTargetLocators.size == 1 }
471+
assertTrue { fixture.sentryOptions.gestureTargetLocators[0] is AndroidViewGestureTargetLocator }
472+
}
473+
474+
@Test
475+
fun `installs ComposeGestureTargetLocator, if sentry-compose is available`() {
476+
fixture.initSutWithClassLoader(
477+
classesToLoad = listOf(
478+
AndroidOptionsInitializer.COMPOSE_CLASS_NAME,
479+
AndroidOptionsInitializer.SENTRY_COMPOSE_INTEGRATION_CLASS_NAME
480+
)
481+
)
482+
483+
assertTrue { fixture.sentryOptions.gestureTargetLocators.size == 2 }
484+
assertTrue { fixture.sentryOptions.gestureTargetLocators[0] is AndroidViewGestureTargetLocator }
485+
assertTrue { fixture.sentryOptions.gestureTargetLocators[1] is ComposeGestureTargetLocator }
486+
}
458487
}

sentry-android-integration-tests/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
* [App metrics test specification (yaml)](./metrics-test.yml)
66
* [Espresso-based benchmarks](./sentry-uitest-android-benchmark) - run within SauceLabs (see /.sauce/*.yml)
77
* [Espresso-based UI tests](./sentry-uitest-android) - run within SauceLabs (see /.sauce/*.yml)
8+
* [Sample app for testing against AGP compatibility matrix](./sentry-test-agp)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
plugins {
2+
id("com.android.application")
3+
}
4+
5+
android {
6+
compileSdk = Config.Android.compileSdkVersion
7+
namespace = "io.sentry.test.agp"
8+
9+
defaultConfig {
10+
applicationId = "io.sentry.test.agp"
11+
minSdk = Config.Android.minSdkVersionOkHttp
12+
targetSdk = Config.Android.targetSdkVersion
13+
versionCode = 1
14+
versionName = "1.0"
15+
}
16+
17+
buildTypes {
18+
getByName("release") {
19+
isMinifyEnabled = true
20+
signingConfig = signingConfigs.getByName("debug") // to be able to run release mode
21+
proguardFiles(
22+
getDefaultProguardFile("proguard-android-optimize.txt"),
23+
"benchmark-proguard-rules.pro"
24+
)
25+
ndk {
26+
abiFilters.clear()
27+
abiFilters.add("arm64-v8a")
28+
}
29+
}
30+
}
31+
compileOptions {
32+
sourceCompatibility = JavaVersion.VERSION_1_8
33+
targetCompatibility = JavaVersion.VERSION_1_8
34+
}
35+
buildFeatures {
36+
viewBinding = true
37+
}
38+
signingConfigs {
39+
getByName("debug") {
40+
storeFile = rootProject.file("debug.keystore")
41+
storePassword = "android"
42+
keyAlias = "androiddebugkey"
43+
keyPassword = "android"
44+
}
45+
}
46+
47+
variantFilter {
48+
if (Config.Android.shouldSkipDebugVariant(buildType.name)) {
49+
ignore = true
50+
}
51+
}
52+
}
53+
dependencies {
54+
// just a mix of different dependencies to test how our logic for checking classes at runtime
55+
// works with r8
56+
implementation(projects.sentryAndroid)
57+
implementation(projects.sentryAndroidOkhttp)
58+
implementation(projects.sentryAndroidFragment)
59+
implementation(projects.sentryAndroidTimber)
60+
61+
implementation(Config.Libs.fragment)
62+
63+
implementation(Config.Libs.retrofit2)
64+
}

0 commit comments

Comments
 (0)