Skip to content

Commit 34a515e

Browse files
[SDK-115] Harden BCIT tests against Ubuntu+KVM launcher ANRs
CI run on PR #1064 surfaced four failures that all share the same root cause: the Pixel Launcher ANRs on Ubuntu+KVM during heavy parallel work and steals focus from the activity-under-test, so findObject().exists() returns false even though the view is in the tree (logcat shows `current package: android` instead of `com.iterable.integration.tests`). Three fixes: 1. BaseIntegrationTest.setUp now pressBack/pressHome to dismiss any lingering system dialog before the test runs. Universal CI-emulator hardening; benefits every test class that extends BaseIntegrationTest. 2. EmbeddedMessageIntegrationTest + DeepLinkIntegrationTest now use findObject().waitForExists(5000) instead of bare .exists(). ActivityScenario reports RESUMED before the view tree is fully rendered; waitForExists handles the race. Same pattern as PushNotificationIntegrationTest already uses. 3. activity_main.xml is now wrapped in a ScrollView so the Test Scenario buttons stay reachable even when the override card grows. Local verification: `Tests 7/7 completed. (0 skipped) (0 failed)` for the full non-push suite with `ci=true`. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 0fb5298 commit 34a515e

4 files changed

Lines changed: 31 additions & 13 deletions

File tree

integration-tests/src/androidTest/java/com/iterable/integration/tests/BaseIntegrationTest.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,17 @@ abstract class BaseIntegrationTest {
6868

6969
@Before
7070
open fun setUp() {
71+
// Dismiss any system UI surface (notification shade, ANR dialog, recents) left
72+
// open by a prior test or carried over from emulator boot. CI's Ubuntu+KVM
73+
// launcher routinely ANRs during heavy parallel work and steals focus from the
74+
// activity-under-test, so findObject().exists() returns false even though the
75+
// view is in the tree.
76+
val uiDevice = androidx.test.uiautomator.UiDevice.getInstance(
77+
InstrumentationRegistry.getInstrumentation()
78+
)
79+
uiDevice.pressBack()
80+
uiDevice.pressHome()
81+
7182
context = ApplicationProvider.getApplicationContext()
7283
testUtils = IntegrationTestUtils(context)
7384

integration-tests/src/androidTest/java/com/iterable/integration/tests/DeepLinkIntegrationTest.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ class DeepLinkIntegrationTest : BaseIntegrationTest() {
287287

288288
// Click Deep Linking button to navigate
289289
val deepLinkButton = uiDevice.findObject(UiSelector().resourceId("com.iterable.integration.tests:id/btnDeepLinking"))
290-
if (deepLinkButton.exists()) {
290+
if (deepLinkButton.waitForExists(5000)) {
291291
deepLinkButton.click()
292292
Thread.sleep(2000)
293293
}
@@ -365,16 +365,16 @@ class DeepLinkIntegrationTest : BaseIntegrationTest() {
365365

366366
// Verify URL is displayed
367367
val urlText = uiDevice.findObject(UiSelector().resourceId("com.iterable.integration.tests:id/tvDeepLinkUrl"))
368-
Assert.assertTrue("URL TextView should exist", urlText.exists())
368+
Assert.assertTrue("URL TextView should exist", urlText.waitForExists(5000))
369369
Assert.assertTrue(
370370
"URL should contain 'iterable://deeplink'",
371371
urlText.text.contains("iterable://deeplink")
372372
)
373373
Log.d(TAG, "✅ URL displayed: ${urlText.text}")
374-
374+
375375
// Verify path is displayed
376376
val pathText = uiDevice.findObject(UiSelector().resourceId("com.iterable.integration.tests:id/tvDeepLinkPath"))
377-
Assert.assertTrue("Path TextView should exist", pathText.exists())
377+
Assert.assertTrue("Path TextView should exist", pathText.waitForExists(5000))
378378
Assert.assertTrue(
379379
"Path should contain '/settings/notifications'",
380380
pathText.text.contains("/settings/notifications")
@@ -439,7 +439,7 @@ class DeepLinkIntegrationTest : BaseIntegrationTest() {
439439
// Step 2: Click the "Deep Linking" button
440440
Log.d(TAG, "🔧 Step 2: Clicking Deep Linking button...")
441441
val deepLinkButton = uiDevice.findObject(UiSelector().resourceId("com.iterable.integration.tests:id/btnDeepLinking"))
442-
Assert.assertTrue("Deep Linking button should exist", deepLinkButton.exists())
442+
Assert.assertTrue("Deep Linking button should exist", deepLinkButton.waitForExists(5000))
443443
deepLinkButton.click()
444444

445445
Thread.sleep(2000)

integration-tests/src/androidTest/java/com/iterable/integration/tests/EmbeddedMessageIntegrationTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,13 @@ class EmbeddedMessageIntegrationTest : BaseIntegrationTest() {
9393
// Step 2: Click the "Embedded Messages" button to navigate to EmbeddedMessageTestActivity
9494
Log.d(TAG, "🔧 Step 2: Clicking 'Embedded Messages' button...")
9595
val embeddedButton = uiDevice.findObject(UiSelector().resourceId("com.iterable.integration.tests:id/btnEmbeddedMessages"))
96-
if (embeddedButton.exists()) {
97-
embeddedButton.click()
98-
Log.d(TAG, "🔧 Clicked Embedded Messages button successfully")
99-
} else {
96+
// RESUMED fires before view inflation; waitForExists handles the race.
97+
if (!embeddedButton.waitForExists(5000)) {
10098
Log.e(TAG, "❌ Embedded Messages button not found!")
10199
Assert.fail("Embedded Messages button not found in MainActivity")
102100
}
101+
embeddedButton.click()
102+
Log.d(TAG, "🔧 Clicked Embedded Messages button successfully")
103103

104104
// Step 3: Wait for EmbeddedMessageTestActivity to load
105105
Log.d(TAG, "🔧 Step 3: Waiting for EmbeddedMessageTestActivity to load...")

integration-tests/src/main/res/layout/activity_main.xml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2+
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
33
xmlns:app="http://schemas.android.com/apk/res-auto"
44
xmlns:tools="http://schemas.android.com/tools"
55
android:layout_width="match_parent"
66
android:layout_height="match_parent"
7+
android:fillViewport="true"
8+
tools:context=".MainActivity">
9+
10+
<LinearLayout
11+
android:layout_width="match_parent"
12+
android:layout_height="wrap_content"
713
android:orientation="vertical"
814
android:padding="16dp"
9-
android:gravity="center"
10-
tools:context=".MainActivity">
15+
android:gravity="center">
1116

1217
<TextView
1318
android:layout_width="wrap_content"
@@ -196,4 +201,6 @@
196201
android:layout_marginTop="16dp"
197202
android:gravity="center" />
198203

199-
</LinearLayout>
204+
</LinearLayout>
205+
206+
</ScrollView>

0 commit comments

Comments
 (0)