From 5e425652212801eecfe092b2ce62b909676806b5 Mon Sep 17 00:00:00 2001 From: Franco Zalamena Date: Tue, 24 Mar 2026 14:55:22 +0000 Subject: [PATCH 1/4] Move 13 criteria tests from androidTest to unit tests These tests use @RunWith(JUnit4) with only org.json imports and have no Android framework dependencies. Running them as JVM unit tests removes the emulator requirement and speeds up CI. Co-Authored-By: Claude Opus 4.6 --- .../iterableapi/util/CombinationComplexCriteriaCheckerTest.java | 0 .../iterableapi/util/CombinationLogicEventTypeCriteriaTest.java | 0 .../com/iterable/iterableapi/util/ComplexCriteriaCheckerTest.java | 0 .../iterable/iterableapi/util/CriteriaCompletionCheckerTest.java | 0 .../iterableapi/util/CriteriaCompletionComparatorTest.java | 0 .../util/DataTypeComparatorArrayInputCriteriaTest.java | 0 .../util/DataTypeComparatorSearchQueryCriteriaTest.java | 0 .../iterable/iterableapi/util/DoesNotEqualCriteriaMatchTest.java | 0 .../iterableapi/util/IsOneOfAndIsNotOneOfCriteriaMatchTest.java | 0 .../iterableapi/util/MultiLevelNestedCriteriaMatchTest.java | 0 .../util/MultiLevelNestedCriteriaWithArrayMatchTest.java | 0 .../com/iterable/iterableapi/util/NestedCriteriaMatchTest.java | 0 .../util/SinglePrimitiveArrayNestedCriteriaMatchTest.java | 0 13 files changed, 0 insertions(+), 0 deletions(-) rename iterableapi/src/{androidTest => test}/java/com/iterable/iterableapi/util/CombinationComplexCriteriaCheckerTest.java (100%) rename iterableapi/src/{androidTest => test}/java/com/iterable/iterableapi/util/CombinationLogicEventTypeCriteriaTest.java (100%) rename iterableapi/src/{androidTest => test}/java/com/iterable/iterableapi/util/ComplexCriteriaCheckerTest.java (100%) rename iterableapi/src/{androidTest => test}/java/com/iterable/iterableapi/util/CriteriaCompletionCheckerTest.java (100%) rename iterableapi/src/{androidTest => test}/java/com/iterable/iterableapi/util/CriteriaCompletionComparatorTest.java (100%) rename iterableapi/src/{androidTest => test}/java/com/iterable/iterableapi/util/DataTypeComparatorArrayInputCriteriaTest.java (100%) rename iterableapi/src/{androidTest => test}/java/com/iterable/iterableapi/util/DataTypeComparatorSearchQueryCriteriaTest.java (100%) rename iterableapi/src/{androidTest => test}/java/com/iterable/iterableapi/util/DoesNotEqualCriteriaMatchTest.java (100%) rename iterableapi/src/{androidTest => test}/java/com/iterable/iterableapi/util/IsOneOfAndIsNotOneOfCriteriaMatchTest.java (100%) rename iterableapi/src/{androidTest => test}/java/com/iterable/iterableapi/util/MultiLevelNestedCriteriaMatchTest.java (100%) rename iterableapi/src/{androidTest => test}/java/com/iterable/iterableapi/util/MultiLevelNestedCriteriaWithArrayMatchTest.java (100%) rename iterableapi/src/{androidTest => test}/java/com/iterable/iterableapi/util/NestedCriteriaMatchTest.java (100%) rename iterableapi/src/{androidTest => test}/java/com/iterable/iterableapi/util/SinglePrimitiveArrayNestedCriteriaMatchTest.java (100%) diff --git a/iterableapi/src/androidTest/java/com/iterable/iterableapi/util/CombinationComplexCriteriaCheckerTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/util/CombinationComplexCriteriaCheckerTest.java similarity index 100% rename from iterableapi/src/androidTest/java/com/iterable/iterableapi/util/CombinationComplexCriteriaCheckerTest.java rename to iterableapi/src/test/java/com/iterable/iterableapi/util/CombinationComplexCriteriaCheckerTest.java diff --git a/iterableapi/src/androidTest/java/com/iterable/iterableapi/util/CombinationLogicEventTypeCriteriaTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/util/CombinationLogicEventTypeCriteriaTest.java similarity index 100% rename from iterableapi/src/androidTest/java/com/iterable/iterableapi/util/CombinationLogicEventTypeCriteriaTest.java rename to iterableapi/src/test/java/com/iterable/iterableapi/util/CombinationLogicEventTypeCriteriaTest.java diff --git a/iterableapi/src/androidTest/java/com/iterable/iterableapi/util/ComplexCriteriaCheckerTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/util/ComplexCriteriaCheckerTest.java similarity index 100% rename from iterableapi/src/androidTest/java/com/iterable/iterableapi/util/ComplexCriteriaCheckerTest.java rename to iterableapi/src/test/java/com/iterable/iterableapi/util/ComplexCriteriaCheckerTest.java diff --git a/iterableapi/src/androidTest/java/com/iterable/iterableapi/util/CriteriaCompletionCheckerTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/util/CriteriaCompletionCheckerTest.java similarity index 100% rename from iterableapi/src/androidTest/java/com/iterable/iterableapi/util/CriteriaCompletionCheckerTest.java rename to iterableapi/src/test/java/com/iterable/iterableapi/util/CriteriaCompletionCheckerTest.java diff --git a/iterableapi/src/androidTest/java/com/iterable/iterableapi/util/CriteriaCompletionComparatorTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/util/CriteriaCompletionComparatorTest.java similarity index 100% rename from iterableapi/src/androidTest/java/com/iterable/iterableapi/util/CriteriaCompletionComparatorTest.java rename to iterableapi/src/test/java/com/iterable/iterableapi/util/CriteriaCompletionComparatorTest.java diff --git a/iterableapi/src/androidTest/java/com/iterable/iterableapi/util/DataTypeComparatorArrayInputCriteriaTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/util/DataTypeComparatorArrayInputCriteriaTest.java similarity index 100% rename from iterableapi/src/androidTest/java/com/iterable/iterableapi/util/DataTypeComparatorArrayInputCriteriaTest.java rename to iterableapi/src/test/java/com/iterable/iterableapi/util/DataTypeComparatorArrayInputCriteriaTest.java diff --git a/iterableapi/src/androidTest/java/com/iterable/iterableapi/util/DataTypeComparatorSearchQueryCriteriaTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/util/DataTypeComparatorSearchQueryCriteriaTest.java similarity index 100% rename from iterableapi/src/androidTest/java/com/iterable/iterableapi/util/DataTypeComparatorSearchQueryCriteriaTest.java rename to iterableapi/src/test/java/com/iterable/iterableapi/util/DataTypeComparatorSearchQueryCriteriaTest.java diff --git a/iterableapi/src/androidTest/java/com/iterable/iterableapi/util/DoesNotEqualCriteriaMatchTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/util/DoesNotEqualCriteriaMatchTest.java similarity index 100% rename from iterableapi/src/androidTest/java/com/iterable/iterableapi/util/DoesNotEqualCriteriaMatchTest.java rename to iterableapi/src/test/java/com/iterable/iterableapi/util/DoesNotEqualCriteriaMatchTest.java diff --git a/iterableapi/src/androidTest/java/com/iterable/iterableapi/util/IsOneOfAndIsNotOneOfCriteriaMatchTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/util/IsOneOfAndIsNotOneOfCriteriaMatchTest.java similarity index 100% rename from iterableapi/src/androidTest/java/com/iterable/iterableapi/util/IsOneOfAndIsNotOneOfCriteriaMatchTest.java rename to iterableapi/src/test/java/com/iterable/iterableapi/util/IsOneOfAndIsNotOneOfCriteriaMatchTest.java diff --git a/iterableapi/src/androidTest/java/com/iterable/iterableapi/util/MultiLevelNestedCriteriaMatchTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/util/MultiLevelNestedCriteriaMatchTest.java similarity index 100% rename from iterableapi/src/androidTest/java/com/iterable/iterableapi/util/MultiLevelNestedCriteriaMatchTest.java rename to iterableapi/src/test/java/com/iterable/iterableapi/util/MultiLevelNestedCriteriaMatchTest.java diff --git a/iterableapi/src/androidTest/java/com/iterable/iterableapi/util/MultiLevelNestedCriteriaWithArrayMatchTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/util/MultiLevelNestedCriteriaWithArrayMatchTest.java similarity index 100% rename from iterableapi/src/androidTest/java/com/iterable/iterableapi/util/MultiLevelNestedCriteriaWithArrayMatchTest.java rename to iterableapi/src/test/java/com/iterable/iterableapi/util/MultiLevelNestedCriteriaWithArrayMatchTest.java diff --git a/iterableapi/src/androidTest/java/com/iterable/iterableapi/util/NestedCriteriaMatchTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/util/NestedCriteriaMatchTest.java similarity index 100% rename from iterableapi/src/androidTest/java/com/iterable/iterableapi/util/NestedCriteriaMatchTest.java rename to iterableapi/src/test/java/com/iterable/iterableapi/util/NestedCriteriaMatchTest.java diff --git a/iterableapi/src/androidTest/java/com/iterable/iterableapi/util/SinglePrimitiveArrayNestedCriteriaMatchTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/util/SinglePrimitiveArrayNestedCriteriaMatchTest.java similarity index 100% rename from iterableapi/src/androidTest/java/com/iterable/iterableapi/util/SinglePrimitiveArrayNestedCriteriaMatchTest.java rename to iterableapi/src/test/java/com/iterable/iterableapi/util/SinglePrimitiveArrayNestedCriteriaMatchTest.java From 30cfbaa19f6162567013a1cfdba388373da34d0e Mon Sep 17 00:00:00 2001 From: Franco Zalamena Date: Tue, 24 Mar 2026 14:58:08 +0000 Subject: [PATCH 2/4] Replace non-deterministic test synchronization with looper control - Replace Mockito.timeout() with shadowOf(getMainLooper()).idle() + verify() in IterablePushRegistrationTaskTest and IterableInAppManagerTest - Replace Thread.sleep(1000) with shadowOf(getMainLooper()).idle() in IterableNotificationTest Co-Authored-By: Claude Opus 4.6 --- .../com/iterable/iterableapi/IterableInAppManagerTest.java | 3 +-- .../com/iterable/iterableapi/IterableNotificationTest.java | 5 +++-- .../iterableapi/IterablePushRegistrationTaskTest.java | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerTest.java index 7dcabe729..1c10654cf 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerTest.java @@ -44,7 +44,6 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.robolectric.Shadows.shadowOf; @@ -256,7 +255,7 @@ public void run() { backgroundExecutor.runAll(); shadowOf(getMainLooper()).idle(); - verify(listener, timeout(100)).onInboxUpdated(); + verify(listener).onInboxUpdated(); } @Test diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableNotificationTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableNotificationTest.java index a66d6002d..817547693 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableNotificationTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableNotificationTest.java @@ -20,6 +20,7 @@ import java.io.InputStream; import java.io.InputStreamReader; +import static android.os.Looper.getMainLooper; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; @@ -50,11 +51,11 @@ private Context getContext() { return getApplicationContext(); } - private IterableNotificationBuilder postNotification(Bundle notificationData) throws InterruptedException { + private IterableNotificationBuilder postNotification(Bundle notificationData) { getContext().getApplicationInfo().icon = android.R.drawable.sym_def_app_icon; IterableNotificationBuilder iterableNotification = IterableNotificationHelper.createNotification(getContext(), notificationData); IterableNotificationHelper.postNotificationOnDevice(appContext, iterableNotification); - Thread.sleep(1000); + shadowOf(getMainLooper()).idle(); return iterableNotification; } diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterablePushRegistrationTaskTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterablePushRegistrationTaskTest.java index 214b667ae..194ca8546 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterablePushRegistrationTaskTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterablePushRegistrationTaskTest.java @@ -19,7 +19,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; @@ -73,7 +72,8 @@ public void testEnableDevice() throws Exception { new IterablePushRegistrationTask().execute(data); deviceAttributes.put(DEVICE_ATTRIBUTES_KEY, DEVICE_ATTRIBUTES_VALUE); - verify(apiMock, timeout(100)).registerDeviceToken(eq(IterableTestUtils.userEmail), nullable(String.class), isNull(), eq(INTEGRATION_NAME), eq(TEST_TOKEN), eq(deviceAttributes)); + shadowOf(getMainLooper()).idle(); + verify(apiMock).registerDeviceToken(eq(IterableTestUtils.userEmail), nullable(String.class), isNull(), eq(INTEGRATION_NAME), eq(TEST_TOKEN), eq(deviceAttributes)); verify(apiMock, never()).disableToken(eq(IterableTestUtils.userEmail), nullable(String.class), nullable(String.class), any(String.class), nullable(IterableHelper.SuccessHandler.class), nullable(IterableHelper.FailureHandler.class)); } @@ -87,6 +87,6 @@ public void testDisableDevice() throws Exception { new IterablePushRegistrationTask().execute(data); shadowOf(getMainLooper()).idle(); - verify(apiMock, timeout(100)).disableToken(eq(IterableTestUtils.userEmail), isNull(), isNull(), eq(TEST_TOKEN), nullable(IterableHelper.SuccessHandler.class), nullable(IterableHelper.FailureHandler.class)); + verify(apiMock).disableToken(eq(IterableTestUtils.userEmail), isNull(), isNull(), eq(TEST_TOKEN), nullable(IterableHelper.SuccessHandler.class), nullable(IterableHelper.FailureHandler.class)); } } \ No newline at end of file From 9fba27f1e835a49510c7dd0b59d732221fb63ac8 Mon Sep 17 00:00:00 2001 From: Franco Zalamena Date: Tue, 24 Mar 2026 14:58:21 +0000 Subject: [PATCH 3/4] Document root causes in @Ignore annotations Update 23 @Ignore annotations with actionable descriptions: - JWT tests: blocked on IterableAuthManager.executor not being injectable - Universal link test: needs MockWebServer to stub HTTP redirect - Database logout test: IterableTaskStorage singleton state leakage - In-app stalling tests: Robolectric incompatible, need Espresso Co-Authored-By: Claude Opus 4.6 --- .../iterableapi/IterableApiAuthTests.java | 34 +++++++++---------- .../iterableapi/IterableApiRequestTest.java | 2 +- .../iterable/iterableapi/IterableApiTest.java | 4 +-- .../iterableapi/IterableInAppManagerTest.java | 6 ++-- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiAuthTests.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiAuthTests.java index 12d1b473b..ebc879508 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiAuthTests.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiAuthTests.java @@ -67,7 +67,7 @@ private void reInitIterableApi() { authHandler = mock(IterableAuthHandler.class); } - @Ignore ("Ignoring the JWT Tests") + @Ignore("Blocked: IterableAuthManager.executor is not injectable - auth token requests run on uncontrollable background thread") @Test public void testRefreshToken() throws Exception { IterableApi.initialize(getContext(), "apiKey"); @@ -95,7 +95,7 @@ public void testRefreshToken() throws Exception { timer = IterableApi.getInstance().getAuthManager().timer; } - @Ignore ("Ignoring the JWT Tests") + @Ignore("Blocked: IterableAuthManager.executor is not injectable - auth token requests run on uncontrollable background thread") @Test public void testSetEmailWithToken() throws Exception { IterableApi.initialize(getContext(), "apiKey"); @@ -119,7 +119,7 @@ public void testSetEmailWithToken() throws Exception { shadowOf(getMainLooper()).runToEndOfTasks(); } - @Ignore ("Ignoring the JWT Tests") + @Ignore("Blocked: IterableAuthManager.executor is not injectable - auth token requests run on uncontrollable background thread") @Test public void testSetEmailWithTokenExpired() throws Exception { IterableApi.initialize(getContext(), "apiKey"); @@ -133,7 +133,7 @@ public void testSetEmailWithTokenExpired() throws Exception { assertEquals(IterableApi.getInstance().getAuthToken(), expiredJWT); } - @Ignore ("Ignoring the JWT Tests") + @Ignore("Blocked: IterableAuthManager.executor is not injectable - auth token requests run on uncontrollable background thread") @Test public void testSetUserIdWithToken() throws Exception { IterableApi.initialize(getContext(), "apiKey"); @@ -157,7 +157,7 @@ public void testSetUserIdWithToken() throws Exception { assertEquals(expiredJWT, IterableApi.getInstance().getAuthToken()); } - @Ignore ("Ignoring the JWT Tests") + @Ignore("Blocked: IterableAuthManager.executor is not injectable - auth token requests run on uncontrollable background thread") @Test public void testSameEmailWithNewToken() throws Exception { IterableApi.initialize(getContext(), "apiKey"); @@ -181,7 +181,7 @@ public void testSameEmailWithNewToken() throws Exception { assertEquals(IterableApi.getInstance().getAuthToken(), newJWT); } - @Ignore ("Ignoring the JWT Tests") + @Ignore("Blocked: IterableAuthManager.executor is not injectable - auth token requests run on uncontrollable background thread") @Test public void testSameUserIdWithNewToken() throws Exception { IterableApi.initialize(getContext(), "apiKey"); @@ -200,7 +200,7 @@ public void testSameUserIdWithNewToken() throws Exception { assertEquals(IterableApi.getInstance().getAuthToken(), newJWT); } - @Ignore ("Ignoring the JWT Tests") + @Ignore("Blocked: IterableAuthManager.executor is not injectable - auth token requests run on uncontrollable background thread") @Test public void testSetSameEmailAndRemoveToken() throws Exception { IterableApi.initialize(getContext(), "apiKey"); @@ -219,7 +219,7 @@ public void testSetSameEmailAndRemoveToken() throws Exception { assertNull(IterableApi.getInstance().getAuthToken()); } - @Ignore ("Ignoring the JWT Tests") + @Ignore("Blocked: IterableAuthManager.executor is not injectable - auth token requests run on uncontrollable background thread") @Test public void testSetSameUserIdAndRemoveToken() throws Exception { IterableApi.initialize(getContext(), "apiKey"); @@ -277,7 +277,7 @@ public void testSetSameUserId() throws Exception { assertNull(IterableApi.getInstance().getAuthToken()); } - @Ignore ("Ignoring the JWT Tests") + @Ignore("Blocked: IterableAuthManager.executor is not injectable - auth token requests run on uncontrollable background thread") @Test public void testSetSameEmailWithSameToken() throws Exception { IterableApi.initialize(getContext(), "apiKey"); @@ -297,7 +297,7 @@ public void testSetSameEmailWithSameToken() throws Exception { assertEquals(IterableApi.getInstance().getAuthToken(), token); } - @Ignore ("Ignoring the JWT Tests") + @Ignore("Blocked: IterableAuthManager.executor is not injectable - auth token requests run on uncontrollable background thread") @Test public void testSetSameUserIdWithSameToken() throws Exception { IterableApi.initialize(getContext(), "apiKey"); @@ -352,7 +352,7 @@ public void testUserIdLogOut() throws Exception { assertNull(IterableApi.getInstance().getAuthToken()); } - @Ignore ("Ignoring the JWT Tests") + @Ignore("Blocked: IterableAuthManager.executor is not injectable - auth token requests run on uncontrollable background thread") @Test public void testAuthTokenPresentInRequest() throws Exception { // server.enqueue(new MockResponse().setResponseCode(200).setBody("{}")); @@ -392,7 +392,7 @@ public void testAuthTokenPresentInRequest() throws Exception { assertEquals(HEADER_SDK_AUTH_FORMAT + newJWT, getMessagesSet2Request.getHeader("Authorization")); } - @Ignore ("Ignoring the JWT Tests") + @Ignore("Blocked: IterableAuthManager.executor is not injectable - auth token requests run on uncontrollable background thread") @Test public void testAuthFailureReturns401() throws InterruptedException { doReturn(expiredJWT).when(authHandler).onAuthTokenRequested(); @@ -418,7 +418,7 @@ public void testAuthFailureReturns401() throws InterruptedException { assertEquals(IterableApi.getInstance().getAuthToken(), expiredJWT); } - @Ignore ("Ignoring the JWT Tests") + @Ignore("Blocked: IterableAuthManager.executor is not injectable - auth token requests run on uncontrollable background thread") @Test public void testAuthRequestedOnSetEmail() throws InterruptedException { doReturn(expiredJWT).when(authHandler).onAuthTokenRequested(); @@ -433,7 +433,7 @@ public void testAuthRequestedOnSetEmail() throws InterruptedException { } - @Ignore ("Ignoring the JWT Tests") + @Ignore("Blocked: IterableAuthManager.executor is not injectable - auth token requests run on uncontrollable background thread") @Test public void testAuthRequestedOnUpdateEmail() throws InterruptedException { doReturn(expiredJWT).when(authHandler).onAuthTokenRequested(); @@ -447,7 +447,7 @@ public void testAuthRequestedOnUpdateEmail() throws InterruptedException { //TODO: Shouldn't the update call also update the authToken in IterableAPI class? } - @Ignore ("Ignoring the JWT Tests") + @Ignore("Blocked: IterableAuthManager.executor is not injectable - auth token requests run on uncontrollable background thread") @Test public void testAuthRequestedOnSetUserId() throws InterruptedException { doReturn(expiredJWT).when(authHandler).onAuthTokenRequested(); @@ -456,7 +456,7 @@ public void testAuthRequestedOnSetUserId() throws InterruptedException { assertEquals(IterableApi.getInstance().getAuthToken(), expiredJWT); } - @Ignore ("Ignoring the JWT Tests") + @Ignore("Blocked: IterableAuthManager.executor is not injectable - auth token requests run on uncontrollable background thread") @Test public void testAuthSetToNullOnLogOut() throws InterruptedException { doReturn(expiredJWT).when(authHandler).onAuthTokenRequested(); @@ -469,7 +469,7 @@ public void testAuthSetToNullOnLogOut() throws InterruptedException { assertNull(IterableApi.getInstance().getAuthToken()); } - @Ignore ("Ignoring the JWT Tests") + @Ignore("Blocked: IterableAuthManager.executor is not injectable - auth token requests run on uncontrollable background thread") @Test public void testRegisterForPushInvokedAfterTokenRefresh() throws InterruptedException { doReturn(expiredJWT).when(authHandler).onAuthTokenRequested(); diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiRequestTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiRequestTest.java index bb781b9ad..166f1bcbb 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiRequestTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiRequestTest.java @@ -225,7 +225,7 @@ public void testPostRequestHeaders() throws Exception { Assert.assertEquals("fake_key", request.getHeader(IterableConstants.HEADER_API_KEY)); } - @Ignore("Ignoring the JWT related test error") + @Ignore("Blocked: IterableAuthManager.executor is not injectable - auth token requests run on uncontrollable background thread") @Test public void testUpdateEmailRequest() throws Exception { server.enqueue(new MockResponse().setResponseCode(200).setBody("{}")); diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiTest.java index 8d873993a..16266a896 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableApiTest.java @@ -238,7 +238,7 @@ public void testUpdateEmailWithUserId() throws Exception { assertEquals("testUserId", IterableApi.getInstance().getUserId()); } - @Ignore + @Ignore("handleAppLink performs real HTTP redirect - needs MockWebServer to stub the redirect endpoint") @Test public void testHandleUniversalLinkRewrite() throws Exception { IterableUrlHandler urlHandlerMock = mock(IterableUrlHandler.class); @@ -423,7 +423,7 @@ public void testInAppResetOnLogout() throws Exception { verify(IterableApi.sharedInstance.getInAppManager(), times(2)).reset(); } - @Ignore("Ignoring this test as it fails on CI for some reason") + @Ignore("Fails on CI: likely IterableTaskStorage singleton state leakage between tests - needs investigation") @Test public void databaseClearOnLogout() throws Exception { IterableTaskStorage taskStorage = IterableTaskStorage.sharedInstance(getContext()); diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerTest.java index 1c10654cf..9c7eeca7d 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerTest.java @@ -89,7 +89,7 @@ public void tearDown() throws IOException { server = null; } - @Ignore("Ignoring due to stalling") + @Ignore("Stalls under Robolectric: showIterableFragmentNotificationHTML requires real Activity lifecycle - candidate for androidTest with Espresso") @Test public void testDoNotShowMultipleTimes() throws Exception { ActivityController controller = Robolectric.buildActivity(FragmentActivity.class).create().start().resume(); @@ -103,7 +103,7 @@ public void testDoNotShowMultipleTimes() throws Exception { controller.pause().stop().destroy(); } - @Ignore("Ignoring due to stalling") + @Ignore("Stalls under Robolectric: showIterableFragmentNotificationHTML requires real Activity lifecycle - candidate for androidTest with Espresso") @Test public void testIfDialogDoesNotDestroysAfterConfigurationChange() throws Exception { ActivityController controller = Robolectric.buildActivity(FragmentActivity.class).create().start().resume(); @@ -117,7 +117,7 @@ public void testIfDialogDoesNotDestroysAfterConfigurationChange() throws Excepti controller.pause().stop().destroy(); } - @Ignore("Ignoring due to stalling") + @Ignore("Stalls under Robolectric: showIterableFragmentNotificationHTML requires real Activity lifecycle - candidate for androidTest with Espresso") @Test public void testIfDialogFragmentExistAfterRotation() throws Exception { ActivityController controller = Robolectric.buildActivity(FragmentActivity.class).create().start().resume(); From 3123c3f2bd1993c21a76ae2239c45bfe5637587f Mon Sep 17 00:00:00 2001 From: Franco Zalamena Date: Tue, 24 Mar 2026 16:52:07 +0000 Subject: [PATCH 4/4] Fix for deprecated test sleep --- .../iterableapi/IterableInAppManagerTest.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerTest.java b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerTest.java index 9c7eeca7d..fec6dc5c6 100644 --- a/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerTest.java +++ b/iterableapi/src/test/java/com/iterable/iterableapi/IterableInAppManagerTest.java @@ -277,9 +277,13 @@ public IterableConfig.Builder run(IterableConfig.Builder builder) { }); doReturn(true).when(urlHandler).handleIterableURL(any(Uri.class), any(IterableActionContext.class)); + // Flush the looper so the constructor's syncInApp() completes and sets lastSyncTime. + // Without this, the foreground transition triggers a second syncInApp() that clears messages. + shadowOf(getMainLooper()).idle(); + // Bring the app into foreground to trigger in-app display Robolectric.buildActivity(Activity.class).create().start().resume(); - Robolectric.flushForegroundThreadScheduler(); + shadowOf(getMainLooper()).idle(); ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(IterableHelper.IterableUrlCallback.class); verify(inAppDisplayerMock).showMessage(any(IterableInAppMessage.class), eq(IterableInAppLocation.IN_APP), callbackCaptor.capture()); IterableInAppMessage message = inAppManager.getMessages().get(0); @@ -341,9 +345,13 @@ public IterableConfig.Builder run(IterableConfig.Builder builder) { }); doReturn(true).when(urlHandler).handleIterableURL(any(Uri.class), any(IterableActionContext.class)); + // Flush the looper so the constructor's syncInApp() completes and sets lastSyncTime. + // Without this, the foreground transition triggers a second syncInApp() that clears messages. + shadowOf(getMainLooper()).idle(); + // Bring the app into foreground Robolectric.buildActivity(Activity.class).create().start().resume(); - Robolectric.flushForegroundThreadScheduler(); + shadowOf(getMainLooper()).idle(); IterableInAppMessage message = inAppManager.getMessages().get(0); // Verify that message is not consumed by default if consume = false and iterable://dismiss is clicked @@ -378,7 +386,7 @@ public void testInAppAutoDisplayPause() throws Exception { inAppManager.setAutoDisplayPaused(true); ActivityController activityController = Robolectric.buildActivity(Activity.class).create().start().resume(); - Robolectric.flushForegroundThreadScheduler(); + shadowOf(getMainLooper()).idle(); ArgumentCaptor inAppMessageCaptor = ArgumentCaptor.forClass(IterableInAppMessage.class); verify(inAppHandler, times(0)).onNewInApp(inAppMessageCaptor.capture());