From 2d65f6834523beed1f87fe36aa40f41fb8c03ddd Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 25 Aug 2025 14:27:48 +0200 Subject: [PATCH 1/2] Fix flaky SdkInitTests --- .../SystemEventsBreadcrumbsIntegration.java | 31 ++++++---- .../io/sentry/uitest/android/SdkInitTests.kt | 57 ++++++++++++++++++- 2 files changed, 75 insertions(+), 13 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java index 561f507c5a1..d7de89efa08 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java @@ -46,6 +46,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -186,21 +187,27 @@ private void unregisterReceiver() { return; } - options + try { + options .getExecutorService() .submit( - () -> { - final @Nullable SystemEventsBroadcastReceiver receiverRef; - try (final @NotNull ISentryLifecycleToken ignored = receiverLock.acquire()) { - isStopped = true; - receiverRef = receiver; - receiver = null; - } + () -> { + final @Nullable SystemEventsBroadcastReceiver receiverRef; + try (final @NotNull ISentryLifecycleToken ignored = receiverLock.acquire()) { + isStopped = true; + receiverRef = receiver; + receiver = null; + } - if (receiverRef != null) { - context.unregisterReceiver(receiverRef); - } - }); + if (receiverRef != null) { + context.unregisterReceiver(receiverRef); + } + }); + } catch (RejectedExecutionException e) { + if (options != null) { + options.getLogger().log(SentryLevel.DEBUG, "SystemEventsBreadcrumbsIntegration was unable to unregister receiver.", e); + } + } } @Override diff --git a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt index 38a3d6a0d7b..597b3356199 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt +++ b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt @@ -3,6 +3,7 @@ package io.sentry.uitest.android import androidx.lifecycle.Lifecycle import androidx.test.core.app.launchActivity import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.sentry.IConnectionStatusProvider import io.sentry.ProfilingTraceData import io.sentry.Sentry import io.sentry.SentryIntegrationPackageStorage @@ -23,6 +24,34 @@ import shark.AndroidReferenceMatchers import shark.IgnoredReferenceMatcher import shark.ReferencePattern +/** Test connection status provider that always reports connected. */ +private class AlwaysOnlineStatusProvider : IConnectionStatusProvider { + + override fun close() { + // No-op for test implementation + } + + override fun getConnectionStatus(): IConnectionStatusProvider.ConnectionStatus { + return IConnectionStatusProvider.ConnectionStatus.CONNECTED + } + + override fun getConnectionType(): String? { + return null + } + + override fun addConnectionStatusObserver( + observer: IConnectionStatusProvider.IConnectionStatusObserver + ): Boolean { + return false + } + + override fun removeConnectionStatusObserver( + observer: IConnectionStatusProvider.IConnectionStatusObserver + ) { + // no-op + } +} + @RunWith(AndroidJUnit4::class) class SdkInitTests : BaseUiTest() { @Test @@ -30,12 +59,16 @@ class SdkInitTests : BaseUiTest() { initSentry(false) { options: SentryAndroidOptions -> options.tracesSampleRate = 1.0 options.profilesSampleRate = 1.0 + options.readTimeoutMillis = 500 + options.connectionTimeoutMillis = 500 } val transaction = Sentry.startTransaction("e2etests", "testInit") val sampleScenario = launchActivity() initSentry(false) { options: SentryAndroidOptions -> options.tracesSampleRate = 1.0 options.profilesSampleRate = 1.0 + options.readTimeoutMillis = 500 + options.connectionTimeoutMillis = 500 } transaction.finish() sampleScenario.moveToState(Lifecycle.State.DESTROYED) @@ -53,6 +86,9 @@ class SdkInitTests : BaseUiTest() { // We use the same executorService before and after closing the SDK it.executorService = options.executorService it.isDebug = true + it.readTimeoutMillis = 500 + it.connectionTimeoutMillis = 500 + it.connectionStatusProvider = AlwaysOnlineStatusProvider() } val transaction = Sentry.startTransaction("e2etests", "testInit") val sampleScenario = launchActivity() @@ -62,6 +98,9 @@ class SdkInitTests : BaseUiTest() { // We use the same executorService before and after closing the SDK it.executorService = options.executorService it.isDebug = true + it.readTimeoutMillis = 500 + it.connectionTimeoutMillis = 500 + it.connectionStatusProvider = AlwaysOnlineStatusProvider() } relayIdlingResource.increment() relayIdlingResource.increment() @@ -103,7 +142,12 @@ class SdkInitTests : BaseUiTest() { // Let's make the first request timeout relay.addTimeoutResponse() - initSentry(true) { options: SentryAndroidOptions -> options.tracesSampleRate = 1.0 } + initSentry(true) { options: SentryAndroidOptions -> + options.tracesSampleRate = 1.0 + options.readTimeoutMillis = 500 + options.connectionTimeoutMillis = 500 + options.connectionStatusProvider = AlwaysOnlineStatusProvider() + } Sentry.startTransaction("beforeRestart", "emptyTransaction").finish() @@ -120,6 +164,9 @@ class SdkInitTests : BaseUiTest() { initSentry(true) { options: SentryAndroidOptions -> options.tracesSampleRate = 1.0 options.profilesSampleRate = 1.0 + options.readTimeoutMillis = 500 + options.connectionTimeoutMillis = 500 + options.connectionStatusProvider = AlwaysOnlineStatusProvider() } val afterRestart = System.currentTimeMillis() val restartMs = afterRestart - beforeRestart @@ -156,6 +203,7 @@ class SdkInitTests : BaseUiTest() { initSentry(true) { options: SentryAndroidOptions -> options.tracesSampleRate = 1.0 options.flushTimeoutMillis = 3000 + options.connectionStatusProvider = AlwaysOnlineStatusProvider() } Sentry.startTransaction("beforeRestart", "emptyTransaction").finish() @@ -170,6 +218,7 @@ class SdkInitTests : BaseUiTest() { initSentry(true) { options: SentryAndroidOptions -> options.tracesSampleRate = 1.0 options.profilesSampleRate = 1.0 + options.connectionStatusProvider = AlwaysOnlineStatusProvider() } val afterRestart = System.currentTimeMillis() val restartMs = afterRestart - beforeRestart @@ -224,6 +273,9 @@ class SdkInitTests : BaseUiTest() { initSentry(false) { options: SentryAndroidOptions -> options.tracesSampleRate = 1.0 options.profilesSampleRate = 1.0 + options.readTimeoutMillis = 500 + options.connectionTimeoutMillis = 500 + options.connectionStatusProvider = AlwaysOnlineStatusProvider() } } activityScenario.moveToState(Lifecycle.State.DESTROYED) @@ -244,6 +296,9 @@ class SdkInitTests : BaseUiTest() { initSentry(false) { options: SentryAndroidOptions -> options.tracesSampleRate = 1.0 options.profilesSampleRate = 1.0 + options.readTimeoutMillis = 500 + options.connectionTimeoutMillis = 500 + options.connectionStatusProvider = AlwaysOnlineStatusProvider() } initLatch.countDown() } From 575097f8ce85758cbfccd0d4e00b600d612e57f6 Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Mon, 25 Aug 2025 12:34:41 +0000 Subject: [PATCH 2/2] Format code --- .../SystemEventsBreadcrumbsIntegration.java | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java index d7de89efa08..9ba5eea9404 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java @@ -189,23 +189,28 @@ private void unregisterReceiver() { try { options - .getExecutorService() - .submit( - () -> { - final @Nullable SystemEventsBroadcastReceiver receiverRef; - try (final @NotNull ISentryLifecycleToken ignored = receiverLock.acquire()) { - isStopped = true; - receiverRef = receiver; - receiver = null; - } + .getExecutorService() + .submit( + () -> { + final @Nullable SystemEventsBroadcastReceiver receiverRef; + try (final @NotNull ISentryLifecycleToken ignored = receiverLock.acquire()) { + isStopped = true; + receiverRef = receiver; + receiver = null; + } - if (receiverRef != null) { - context.unregisterReceiver(receiverRef); - } - }); + if (receiverRef != null) { + context.unregisterReceiver(receiverRef); + } + }); } catch (RejectedExecutionException e) { if (options != null) { - options.getLogger().log(SentryLevel.DEBUG, "SystemEventsBreadcrumbsIntegration was unable to unregister receiver.", e); + options + .getLogger() + .log( + SentryLevel.DEBUG, + "SystemEventsBreadcrumbsIntegration was unable to unregister receiver.", + e); } } }