From d48029b378dba6b8c7fa0d94f0124da30660c416 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Tue, 3 Mar 2026 15:08:03 +0100 Subject: [PATCH 01/17] fix(test): Check notification permission before launching request to fix flaky API 34 tests On API 34, Maestro's `permissions: all: allow` pre-grants POST_NOTIFICATIONS via `pm grant`. The ActivityResult callback from `requestPermissionLauncher` is not always reliably invoked when the permission is already granted, causing the notification to never be posted. This adds a checkSelfPermission guard so the notification is posted directly when already permitted. Co-Authored-By: Claude Opus 4.6 --- .../io/sentry/uitest/android/critical/MainActivity.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/MainActivity.kt b/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/MainActivity.kt index f6b81c869ef..8b885873bed 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/MainActivity.kt +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/MainActivity.kt @@ -1,6 +1,7 @@ package io.sentry.uitest.android.critical import android.content.Intent +import android.content.pm.PackageManager import android.os.Build import android.os.Bundle import android.widget.Toast @@ -22,6 +23,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import androidx.core.content.ContextCompat import io.sentry.Sentry import io.sentry.android.core.performance.AppStartMetrics import io.sentry.uitest.android.critical.NotificationHelper.showNotification @@ -82,7 +84,13 @@ class MainActivity : ComponentActivity() { Button(onClick = { finish() }) { Text("Finish Activity") } Button( onClick = { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if ( + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && + ContextCompat.checkSelfPermission( + this@MainActivity, + android.Manifest.permission.POST_NOTIFICATIONS, + ) != PackageManager.PERMISSION_GRANTED + ) { requestPermissionLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS) } else { postNotification() From 9840af31ecc714940ae097fd8f9894004ffcb447 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Tue, 3 Mar 2026 15:14:22 +0100 Subject: [PATCH 02/17] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccff564763f..6346e67acc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,10 @@ - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0131) - [diff](https://github.com/getsentry/sentry-native/compare/0.12.7...0.13.1) +### Internal + +- Check notification permission before launching request to fix flaky API 34 tests ([#5146](https://github.com/getsentry/sentry-java/pull/5146)) + ## 8.33.0 ### Features From 2a3bbbe6daf9d785dd5f5e05defb8ecb56eebab0 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Tue, 3 Mar 2026 15:37:31 +0100 Subject: [PATCH 03/17] fix(test): Dismiss ANR dialog in Maestro tests before each flow The Pixel Launcher can become unresponsive on CI emulators, showing a system dialog that blocks Maestro from interacting with the app under test. Co-Authored-By: Claude Opus 4.6 --- .../maestro/.maestro/config.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/.maestro/config.yaml b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/.maestro/config.yaml index ebb5a3a8e6d..5a86c43da13 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/.maestro/config.yaml +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/.maestro/config.yaml @@ -1,3 +1,9 @@ platform: android: disableAnimations: true +onFlowStart: + - runFlow: + when: + visible: "Close app" + commands: + - tapOn: "Close app" From 5146f0a43070ff1759a05528810c93a643821697 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Tue, 3 Mar 2026 16:39:40 +0100 Subject: [PATCH 04/17] fix(test): Use IMPORTANCE_HIGH for test notifications IMPORTANCE_DEFAULT notifications may be silently delivered on some emulators, causing the notification to not appear in the shade when Maestro swipes down to find it. Co-Authored-By: Claude Opus 4.6 --- .../io/sentry/uitest/android/critical/NotificationHelper.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/NotificationHelper.kt b/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/NotificationHelper.kt index 4cbaede1b7f..0a65626882e 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/NotificationHelper.kt +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/NotificationHelper.kt @@ -21,7 +21,7 @@ object NotificationHelper { // Create notification channel for Android 8.0+ (API 26+) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = - NotificationChannel(CHANNEL_ID, "Notifications", NotificationManager.IMPORTANCE_DEFAULT) + NotificationChannel(CHANNEL_ID, "Notifications", NotificationManager.IMPORTANCE_HIGH) channel.description = "description" notificationManager.createNotificationChannel(channel) } From f9faaf5fb2f966b2fd4f02cd172e29e6d9a10385 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Tue, 3 Mar 2026 17:10:24 +0100 Subject: [PATCH 05/17] fix(test): Make notification shade swipe more reliable Use a shorter, faster swipe from the center-top (50%, 1%) to center (50%, 50%) with 1s duration instead of a slow full-screen swipe. Also increase retries from 3 to 5 to handle slower emulators. Co-Authored-By: Claude Opus 4.6 --- .../maestro/appStart.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml index 7356bd4bf08..febf258ad5c 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml @@ -27,14 +27,14 @@ name: App Start Tests - assertNotVisible: "Welcome!" - waitForAnimationToEnd - repeat: - times: 3 + times: 5 while: notVisible: "Sentry Test Notification" commands: - swipe: - start: 90%, 0% - end: 90%, 100% - duration: 4000 + start: 50%, 1% + end: 50%, 50% + duration: 1000 - tapOn: "Sentry Test Notification" - assertVisible: "App Start Type: WARM" @@ -50,14 +50,14 @@ name: App Start Tests - killApp - waitForAnimationToEnd - repeat: - times: 3 + times: 5 while: notVisible: "Sentry Test Notification" commands: - swipe: - start: 90%, 0% - end: 90%, 100% - duration: 4000 + start: 50%, 1% + end: 50%, 50% + duration: 1000 - tapOn: "Sentry Test Notification" - assertVisible: "App Start Type: COLD" # Test 5: Launch app after a broadcast receiver already created the application From a5b4cde4945d4d46c2223d2776b4c114061f736e Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Tue, 3 Mar 2026 23:23:08 +0100 Subject: [PATCH 06/17] fix(test): Handle permission dialog and clean up notification shade Three fixes for flaky notification tests on CI emulators: 1. Handle the POST_NOTIFICATIONS permission dialog explicitly in the Maestro flow, in case Maestro's `permissions: all: allow` doesn't grant it reliably. 2. Collapse the notification shade in onFlowStart to prevent cascade failures when a previous test left it open. 3. Add waitForAnimationToEnd inside the swipe retry loop to give the shade time to render. Co-Authored-By: Claude Opus 4.6 --- .../maestro/.maestro/config.yaml | 7 +++++++ .../maestro/appStart.yaml | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/.maestro/config.yaml b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/.maestro/config.yaml index 5a86c43da13..cad6b04817b 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/.maestro/config.yaml +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/.maestro/config.yaml @@ -2,8 +2,15 @@ platform: android: disableAnimations: true onFlowStart: + # Dismiss any ANR dialog (e.g. "Pixel Launcher isn't responding") - runFlow: when: visible: "Close app" commands: - tapOn: "Close app" + # Collapse notification shade if left open from a previous test + - swipe: + start: 50%, 50% + end: 50%, 1% + duration: 300 + - pressKey: home diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml index febf258ad5c..829ea54d2d0 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml @@ -23,6 +23,12 @@ name: App Start Tests all: allow - assertVisible: "Welcome!" - tapOn: "Trigger Notification" +# Grant notification permission if the system dialog appears +- runFlow: + when: + visible: "Allow" + commands: + - tapOn: "Allow" - tapOn: "Finish Activity" - assertNotVisible: "Welcome!" - waitForAnimationToEnd @@ -35,6 +41,7 @@ name: App Start Tests start: 50%, 1% end: 50%, 50% duration: 1000 + - waitForAnimationToEnd - tapOn: "Sentry Test Notification" - assertVisible: "App Start Type: WARM" @@ -45,6 +52,12 @@ name: App Start Tests all: allow - assertVisible: "Welcome!" - tapOn: "Trigger Notification" +# Grant notification permission if the system dialog appears +- runFlow: + when: + visible: "Allow" + commands: + - tapOn: "Allow" - tapOn: "Finish Activity" - assertNotVisible: "Welcome!" - killApp @@ -58,6 +71,7 @@ name: App Start Tests start: 50%, 1% end: 50%, 50% duration: 1000 + - waitForAnimationToEnd - tapOn: "Sentry Test Notification" - assertVisible: "App Start Type: COLD" # Test 5: Launch app after a broadcast receiver already created the application From 2ceb4d3ac9c4cea716ebc427beeff5f3c898c242 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 4 Mar 2026 00:13:54 +0100 Subject: [PATCH 07/17] fix(test): Use shorter swipe to open notification shade not quick settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The swipe from 50%,1% to 50%,50% was too long — it expanded straight to the quick settings panel, hiding the notification entries behind it. Use a shorter swipe (0% to 25%) to only open the notification shade where notification entries are visible. Co-Authored-By: Claude Opus 4.6 --- .../maestro/appStart.yaml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml index 829ea54d2d0..ef825f527ad 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml @@ -37,10 +37,11 @@ name: App Start Tests while: notVisible: "Sentry Test Notification" commands: + # Short swipe to open notification shade without expanding to quick settings - swipe: - start: 50%, 1% - end: 50%, 50% - duration: 1000 + start: 50%, 0% + end: 50%, 25% + duration: 500 - waitForAnimationToEnd - tapOn: "Sentry Test Notification" - assertVisible: "App Start Type: WARM" @@ -67,10 +68,11 @@ name: App Start Tests while: notVisible: "Sentry Test Notification" commands: + # Short swipe to open notification shade without expanding to quick settings - swipe: - start: 50%, 1% - end: 50%, 50% - duration: 1000 + start: 50%, 0% + end: 50%, 25% + duration: 500 - waitForAnimationToEnd - tapOn: "Sentry Test Notification" - assertVisible: "App Start Type: COLD" From 5b867fbfaa2c0edd6556491e42069cef6f0baf54 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 4 Mar 2026 11:31:01 +0100 Subject: [PATCH 08/17] fix(test): Reset shade state between retries and recreate notification channel Two issues found from analyzing the accessibility hierarchy dump: 1. Repeated swipe-downs accumulated into full quick settings expansion, hiding the notification list. Fix: press home before each swipe retry to reset the shade state, so each attempt opens a fresh notification shade instead of expanding quick settings further. 2. adb install -r preserves app data including notification channels. The old IMPORTANCE_DEFAULT channel persists and Android ignores the code change to IMPORTANCE_HIGH. Fix: delete the channel before recreating it to ensure correct importance. Co-Authored-By: Claude Opus 4.6 --- .../maestro/appStart.yaml | 12 ++++++++---- .../uitest/android/critical/NotificationHelper.kt | 3 +++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml index ef825f527ad..893109f409f 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml @@ -37,10 +37,12 @@ name: App Start Tests while: notVisible: "Sentry Test Notification" commands: - # Short swipe to open notification shade without expanding to quick settings + # Reset to home screen first to prevent swipes from accumulating + # into quick settings expansion + - pressKey: home - swipe: start: 50%, 0% - end: 50%, 25% + end: 50%, 40% duration: 500 - waitForAnimationToEnd - tapOn: "Sentry Test Notification" @@ -68,10 +70,12 @@ name: App Start Tests while: notVisible: "Sentry Test Notification" commands: - # Short swipe to open notification shade without expanding to quick settings + # Reset to home screen first to prevent swipes from accumulating + # into quick settings expansion + - pressKey: home - swipe: start: 50%, 0% - end: 50%, 25% + end: 50%, 40% duration: 500 - waitForAnimationToEnd - tapOn: "Sentry Test Notification" diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/NotificationHelper.kt b/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/NotificationHelper.kt index 0a65626882e..16abff26fd3 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/NotificationHelper.kt +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/NotificationHelper.kt @@ -19,7 +19,10 @@ object NotificationHelper { context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // Create notification channel for Android 8.0+ (API 26+) + // Delete first to ensure the channel is recreated with the correct importance, + // since Android preserves channel settings across app reinstalls (adb install -r). if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + notificationManager.deleteNotificationChannel(CHANNEL_ID) val channel = NotificationChannel(CHANNEL_ID, "Notifications", NotificationManager.IMPORTANCE_HIGH) channel.description = "description" From 2a803108f511d5e4c52e7c13c7d95d470b29a7ad Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 4 Mar 2026 12:01:53 +0100 Subject: [PATCH 09/17] fix(test): Use IMPORTANCE_DEFAULT to avoid heads-up dismissal IMPORTANCE_HIGH shows the notification as a heads-up banner at the top of the screen. When Maestro swipes down to open the notification shade, it instead dismisses the heads-up notification, causing it to disappear entirely. IMPORTANCE_DEFAULT puts the notification directly in the shade without a heads-up, so the swipe opens the shade as intended. Co-Authored-By: Claude Opus 4.6 --- .../io/sentry/uitest/android/critical/NotificationHelper.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/NotificationHelper.kt b/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/NotificationHelper.kt index 16abff26fd3..197fc378a39 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/NotificationHelper.kt +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/NotificationHelper.kt @@ -24,7 +24,7 @@ object NotificationHelper { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { notificationManager.deleteNotificationChannel(CHANNEL_ID) val channel = - NotificationChannel(CHANNEL_ID, "Notifications", NotificationManager.IMPORTANCE_HIGH) + NotificationChannel(CHANNEL_ID, "Notifications", NotificationManager.IMPORTANCE_DEFAULT) channel.description = "description" notificationManager.createNotificationChannel(channel) } From 4b8afe70e066a35bec49cacadcde390b2483ce97 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 4 Mar 2026 13:58:24 +0100 Subject: [PATCH 10/17] fix(test): Use original swipe values with home reset between retries Restore the original swipe values (90%,0% -> 90%,100%, 4s) that are proven to work on main, but keep the pressKey:home + waitForAnimationToEnd before each swipe to prevent quick settings accumulation. Co-Authored-By: Claude Opus 4.6 --- .../maestro/appStart.yaml | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml index 893109f409f..6214214d835 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml @@ -33,18 +33,18 @@ name: App Start Tests - assertNotVisible: "Welcome!" - waitForAnimationToEnd - repeat: - times: 5 + times: 3 while: notVisible: "Sentry Test Notification" commands: - # Reset to home screen first to prevent swipes from accumulating + # Reset to home screen to prevent swipes from accumulating # into quick settings expansion - pressKey: home - - swipe: - start: 50%, 0% - end: 50%, 40% - duration: 500 - waitForAnimationToEnd + - swipe: + start: 90%, 0% + end: 90%, 100% + duration: 4000 - tapOn: "Sentry Test Notification" - assertVisible: "App Start Type: WARM" @@ -66,18 +66,18 @@ name: App Start Tests - killApp - waitForAnimationToEnd - repeat: - times: 5 + times: 3 while: notVisible: "Sentry Test Notification" commands: - # Reset to home screen first to prevent swipes from accumulating + # Reset to home screen to prevent swipes from accumulating # into quick settings expansion - pressKey: home - - swipe: - start: 50%, 0% - end: 50%, 40% - duration: 500 - waitForAnimationToEnd + - swipe: + start: 90%, 0% + end: 90%, 100% + duration: 4000 - tapOn: "Sentry Test Notification" - assertVisible: "App Start Type: COLD" # Test 5: Launch app after a broadcast receiver already created the application From 2702cc7a774d7272512b62216be1436a2f4d3a35 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 4 Mar 2026 14:11:31 +0100 Subject: [PATCH 11/17] fix(test): Use half-screen swipe and pressKey:back to reliably open notification shade The full-screen swipe (90%,0% -> 90%,100%) was expanding into quick settings on API 34 instead of showing the notification shade. Changed to a half-screen swipe (90%,0% -> 90%,50%) which is long enough to open the shade on API 36 but short enough to avoid quick settings on API 34. Also uses pressKey:back + pressKey:home to reliably collapse any open shade/quick settings before retrying, and increased retries from 3 to 5. Co-Authored-By: Claude Opus 4.6 --- .../maestro/.maestro/config.yaml | 7 ++---- .../maestro/appStart.yaml | 22 ++++++++++--------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/.maestro/config.yaml b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/.maestro/config.yaml index cad6b04817b..f50373791f3 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/.maestro/config.yaml +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/.maestro/config.yaml @@ -8,9 +8,6 @@ onFlowStart: visible: "Close app" commands: - tapOn: "Close app" - # Collapse notification shade if left open from a previous test - - swipe: - start: 50%, 50% - end: 50%, 1% - duration: 300 + # Close notification shade if left open from a previous test + - pressKey: back - pressKey: home diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml index 6214214d835..5ad5eb42ce3 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml @@ -33,18 +33,19 @@ name: App Start Tests - assertNotVisible: "Welcome!" - waitForAnimationToEnd - repeat: - times: 3 + times: 5 while: notVisible: "Sentry Test Notification" commands: - # Reset to home screen to prevent swipes from accumulating - # into quick settings expansion + # Reset to home screen to collapse any open shade/quick settings + - pressKey: back - pressKey: home - waitForAnimationToEnd + # Half-screen swipe: long enough to open shade, short enough to avoid quick settings - swipe: start: 90%, 0% - end: 90%, 100% - duration: 4000 + end: 90%, 50% + duration: 2000 - tapOn: "Sentry Test Notification" - assertVisible: "App Start Type: WARM" @@ -66,18 +67,19 @@ name: App Start Tests - killApp - waitForAnimationToEnd - repeat: - times: 3 + times: 5 while: notVisible: "Sentry Test Notification" commands: - # Reset to home screen to prevent swipes from accumulating - # into quick settings expansion + # Reset to home screen to collapse any open shade/quick settings + - pressKey: back - pressKey: home - waitForAnimationToEnd + # Half-screen swipe: long enough to open shade, short enough to avoid quick settings - swipe: start: 90%, 0% - end: 90%, 100% - duration: 4000 + end: 90%, 50% + duration: 2000 - tapOn: "Sentry Test Notification" - assertVisible: "App Start Type: COLD" # Test 5: Launch app after a broadcast receiver already created the application From 89fd3f660b34f855e52f572ac429511834be5f6a Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 4 Mar 2026 14:13:18 +0100 Subject: [PATCH 12/17] fix(test): Always recreate AVD instead of using cache Co-Authored-By: Claude Opus 4.6 --- .github/workflows/integration-tests-ui-critical.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/integration-tests-ui-critical.yml b/.github/workflows/integration-tests-ui-critical.yml index 77691bfd65b..8afe3a4c1df 100644 --- a/.github/workflows/integration-tests-ui-critical.yml +++ b/.github/workflows/integration-tests-ui-critical.yml @@ -99,7 +99,6 @@ jobs: key: avd-api-${{ matrix.api-level }}-${{ matrix.arch }}-${{ matrix.target }} - name: Create AVD and generate snapshot for caching - if: steps.avd-cache.outputs.cache-hit != 'true' uses: reactivecircus/android-emulator-runner@b530d96654c385303d652368551fb075bc2f0b6b # pin@v2 with: api-level: ${{ matrix.api-level }} From ed4773b9027f29d273685205b212d1d7d3293985 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 4 Mar 2026 14:13:50 +0100 Subject: [PATCH 13/17] fix(test): Remove AVD cache step entirely Co-Authored-By: Claude Opus 4.6 --- .github/workflows/integration-tests-ui-critical.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/workflows/integration-tests-ui-critical.yml b/.github/workflows/integration-tests-ui-critical.yml index 8afe3a4c1df..e50cbbb38c3 100644 --- a/.github/workflows/integration-tests-ui-critical.yml +++ b/.github/workflows/integration-tests-ui-critical.yml @@ -89,15 +89,6 @@ jobs: sudo udevadm control --reload-rules sudo udevadm trigger --name-match=kvm - - name: AVD cache - uses: actions/cache@v5 - id: avd-cache - with: - path: | - ~/.android/avd/* - ~/.android/adb* - key: avd-api-${{ matrix.api-level }}-${{ matrix.arch }}-${{ matrix.target }} - - name: Create AVD and generate snapshot for caching uses: reactivecircus/android-emulator-runner@b530d96654c385303d652368551fb075bc2f0b6b # pin@v2 with: From cd5740b4efda9620142531b30ee9c16b445d8279 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 4 Mar 2026 14:43:24 +0100 Subject: [PATCH 14/17] fix(test): Move notification tap inside retry loop to avoid race condition The notification shade was closing between the repeat loop's visibility check and the subsequent tapOn command. By moving the tapOn inside the loop (with optional: true) and changing the loop condition to check for the app's expected screen instead, we tap the notification immediately after the swipe while the shade is still open. Co-Authored-By: Claude Opus 4.6 --- .../maestro/appStart.yaml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml index 5ad5eb42ce3..0818cd41e07 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml @@ -35,7 +35,7 @@ name: App Start Tests - repeat: times: 5 while: - notVisible: "Sentry Test Notification" + notVisible: "App Start Type: WARM" commands: # Reset to home screen to collapse any open shade/quick settings - pressKey: back @@ -46,7 +46,10 @@ name: App Start Tests start: 90%, 0% end: 90%, 50% duration: 2000 -- tapOn: "Sentry Test Notification" + # Tap immediately after swipe while shade is still open + - tapOn: + text: "Sentry Test Notification" + optional: true - assertVisible: "App Start Type: WARM" # Test 4: Notification (COLD start) @@ -69,7 +72,7 @@ name: App Start Tests - repeat: times: 5 while: - notVisible: "Sentry Test Notification" + notVisible: "App Start Type: COLD" commands: # Reset to home screen to collapse any open shade/quick settings - pressKey: back @@ -80,7 +83,10 @@ name: App Start Tests start: 90%, 0% end: 90%, 50% duration: 2000 -- tapOn: "Sentry Test Notification" + # Tap immediately after swipe while shade is still open + - tapOn: + text: "Sentry Test Notification" + optional: true - assertVisible: "App Start Type: COLD" # Test 5: Launch app after a broadcast receiver already created the application # Uncomment once https://github.com/mobile-dev-inc/Maestro/pull/2925 is merged From b500a30bb529d42da880c26f4bca303f6d01562e Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 4 Mar 2026 18:23:20 +0100 Subject: [PATCH 15/17] fix(test): Use Process.killProcess instead of am force-stop to preserve notifications am force-stop (used by Maestro's killApp) clears all notifications on API 33+, causing Test 4 (COLD start via notification) to always fail. Adds a "Kill Process" button that calls Process.killProcess() which kills the app process without clearing notifications. The Maestro test now taps this button instead of using killApp. Co-Authored-By: Claude Opus 4.6 --- .../sentry-uitest-android-critical/maestro/appStart.yaml | 6 +++--- .../java/io/sentry/uitest/android/critical/MainActivity.kt | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml index 0818cd41e07..92da7e887c8 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml @@ -65,9 +65,9 @@ name: App Start Tests visible: "Allow" commands: - tapOn: "Allow" -- tapOn: "Finish Activity" -- assertNotVisible: "Welcome!" -- killApp +# Use "Kill Process" button instead of killApp to avoid am force-stop +# which clears notifications on API 33+ +- tapOn: "Kill Process" - waitForAnimationToEnd - repeat: times: 5 diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/MainActivity.kt b/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/MainActivity.kt index 8b885873bed..acd7626377c 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/MainActivity.kt +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/MainActivity.kt @@ -99,6 +99,11 @@ class MainActivity : ComponentActivity() { ) { Text("Trigger Notification") } + Button( + onClick = { android.os.Process.killProcess(android.os.Process.myPid()) } + ) { + Text("Kill Process") + } Button( onClick = { startActivity( From bf0cfcf6f840dd92941f450be0c52244c94b40ad Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Wed, 4 Mar 2026 17:26:56 +0000 Subject: [PATCH 16/17] Format code --- .../java/io/sentry/uitest/android/critical/MainActivity.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/MainActivity.kt b/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/MainActivity.kt index acd7626377c..6b758173a89 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/MainActivity.kt +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/src/main/java/io/sentry/uitest/android/critical/MainActivity.kt @@ -99,9 +99,7 @@ class MainActivity : ComponentActivity() { ) { Text("Trigger Notification") } - Button( - onClick = { android.os.Process.killProcess(android.os.Process.myPid()) } - ) { + Button(onClick = { android.os.Process.killProcess(android.os.Process.myPid()) }) { Text("Kill Process") } Button( From 8816dc18511dd05e49b6ac9bc01766777d786b94 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Wed, 4 Mar 2026 20:47:54 +0100 Subject: [PATCH 17/17] fix(test): Restore original test flow with killApp and Finish Activity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous approach of using Process.killProcess() without Finish Activity didn't work because the notification wasn't surviving process death on API 33+. Analysis of main branch (where all API levels pass) shows that the original flow works: Trigger Notification → Finish Activity → killApp → open shade → tap notification. Restores original swipe values (90%,0% → 90%,100%, 4000ms) and killApp which work on main. Keeps the runFlow for permission dialog handling. Co-Authored-By: Claude Opus 4.6 --- .../maestro/appStart.yaml | 42 ++++++------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml index 92da7e887c8..f7000b21a82 100644 --- a/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml +++ b/sentry-android-integration-tests/sentry-uitest-android-critical/maestro/appStart.yaml @@ -33,23 +33,15 @@ name: App Start Tests - assertNotVisible: "Welcome!" - waitForAnimationToEnd - repeat: - times: 5 + times: 3 while: - notVisible: "App Start Type: WARM" + notVisible: "Sentry Test Notification" commands: - # Reset to home screen to collapse any open shade/quick settings - - pressKey: back - - pressKey: home - - waitForAnimationToEnd - # Half-screen swipe: long enough to open shade, short enough to avoid quick settings - swipe: start: 90%, 0% - end: 90%, 50% - duration: 2000 - # Tap immediately after swipe while shade is still open - - tapOn: - text: "Sentry Test Notification" - optional: true + end: 90%, 100% + duration: 4000 +- tapOn: "Sentry Test Notification" - assertVisible: "App Start Type: WARM" # Test 4: Notification (COLD start) @@ -65,28 +57,20 @@ name: App Start Tests visible: "Allow" commands: - tapOn: "Allow" -# Use "Kill Process" button instead of killApp to avoid am force-stop -# which clears notifications on API 33+ -- tapOn: "Kill Process" +- tapOn: "Finish Activity" +- assertNotVisible: "Welcome!" +- killApp - waitForAnimationToEnd - repeat: - times: 5 + times: 3 while: - notVisible: "App Start Type: COLD" + notVisible: "Sentry Test Notification" commands: - # Reset to home screen to collapse any open shade/quick settings - - pressKey: back - - pressKey: home - - waitForAnimationToEnd - # Half-screen swipe: long enough to open shade, short enough to avoid quick settings - swipe: start: 90%, 0% - end: 90%, 50% - duration: 2000 - # Tap immediately after swipe while shade is still open - - tapOn: - text: "Sentry Test Notification" - optional: true + end: 90%, 100% + duration: 4000 +- tapOn: "Sentry Test Notification" - assertVisible: "App Start Type: COLD" # Test 5: Launch app after a broadcast receiver already created the application # Uncomment once https://github.com/mobile-dev-inc/Maestro/pull/2925 is merged