@@ -3,6 +3,8 @@ package io.sentry.android.core
33import android.app.Activity
44import android.app.Application
55import android.view.Window
6+ import androidx.lifecycle.Lifecycle
7+ import androidx.lifecycle.LifecycleOwner
68import androidx.test.ext.junit.runners.AndroidJUnit4
79import io.sentry.Scopes
810import io.sentry.android.core.internal.gestures.NoOpWindowCallback
@@ -11,13 +13,17 @@ import junit.framework.TestCase.assertNull
1113import org.junit.runner.RunWith
1214import org.mockito.kotlin.any
1315import org.mockito.kotlin.anyOrNull
16+ import org.mockito.kotlin.eq
1417import org.mockito.kotlin.mock
1518import org.mockito.kotlin.never
19+ import org.mockito.kotlin.times
1620import org.mockito.kotlin.verify
1721import org.mockito.kotlin.whenever
1822import org.robolectric.Robolectric.buildActivity
23+ import kotlin.test.BeforeTest
1924import kotlin.test.Test
2025import kotlin.test.assertIs
26+ import kotlin.test.assertIsNot
2127import kotlin.test.assertNotEquals
2228import kotlin.test.assertSame
2329
@@ -30,15 +36,17 @@ class UserInteractionIntegrationTest {
3036 val options = SentryAndroidOptions ().apply {
3137 dsn = " https://key@sentry.io/proj"
3238 }
33- val activity: Activity = buildActivity(EmptyActivity ::class .java).setup().get()
39+ val activity: EmptyActivity = buildActivity(EmptyActivity ::class .java).setup().get()
3440 val window: Window = activity.window
3541 val loadClass = mock<LoadClass >()
3642
3743 fun getSut (
3844 callback : Window .Callback ? = null,
39- isAndroidXAvailable : Boolean = true
45+ isAndroidXAvailable : Boolean = true,
46+ isLifecycleAvailable : Boolean = true
4047 ): UserInteractionIntegration {
41- whenever(loadClass.isClassAvailable(any(), anyOrNull<SentryAndroidOptions >())).thenReturn(isAndroidXAvailable)
48+ whenever(loadClass.isClassAvailable(eq(" androidx.core.view.GestureDetectorCompat" ), anyOrNull<SentryAndroidOptions >())).thenReturn(isAndroidXAvailable)
49+ whenever(loadClass.isClassAvailable(eq(" androidx.lifecycle.Lifecycle" ), anyOrNull<SentryAndroidOptions >())).thenReturn(isLifecycleAvailable)
4250 whenever(scopes.options).thenReturn(options)
4351 if (callback != null ) {
4452 window.callback = callback
@@ -49,6 +57,11 @@ class UserInteractionIntegrationTest {
4957
5058 private val fixture = Fixture ()
5159
60+ @BeforeTest
61+ fun setup () {
62+ CurrentActivityHolder .getInstance().clearActivity()
63+ }
64+
5265 @Test
5366 fun `when user interaction breadcrumb is enabled registers a callback` () {
5467 val sut = fixture.getSut()
@@ -156,6 +169,50 @@ class UserInteractionIntegrationTest {
156169
157170 assertNotEquals(existingCallback, (fixture.window.callback as SentryWindowCallback ).delegate)
158171 }
172+
173+ @Test
174+ fun `when androidx lifecycle is unavailable doesn't hook into activity` () {
175+ val sut = fixture.getSut(isLifecycleAvailable = false )
176+ CurrentActivityHolder .getInstance().setActivity(fixture.activity)
177+ sut.register(fixture.scopes, fixture.options)
178+ assertIsNot<SentryWindowCallback >(fixture.window)
179+ }
180+
181+ @Test
182+ fun `when activity is resumed and is a LifecycleOwner, starts tracking immediately` () {
183+ val sut = fixture.getSut()
184+ whenever(fixture.activity.lifecycle.currentState).thenReturn(Lifecycle .State .RESUMED )
185+ CurrentActivityHolder .getInstance().setActivity(fixture.activity)
186+
187+ sut.register(fixture.scopes, fixture.options)
188+ assertIs<SentryWindowCallback >(fixture.window.callback)
189+ }
190+
191+ @Test
192+ fun `when activity is resumed but not a LifecycleOwner, does not start tracking immediately` () {
193+ val sut = fixture.getSut()
194+ val activity = mock<Activity >()
195+ val window = mock<Window >()
196+ whenever(activity.window).thenReturn(window)
197+
198+ CurrentActivityHolder .getInstance().setActivity(activity)
199+ sut.register(fixture.scopes, fixture.options)
200+
201+ verify(window, never()).callback = any()
202+ }
203+
204+ @Test
205+ fun `when activity is not in RESUMED state, does not start tracking immediately` () {
206+ val sut = fixture.getSut()
207+ whenever(fixture.activity.lifecycle.currentState).thenReturn(Lifecycle .State .CREATED )
208+ CurrentActivityHolder .getInstance().setActivity(fixture.activity)
209+
210+ sut.register(fixture.scopes, fixture.options)
211+ assertIsNot<SentryWindowCallback >(fixture.activity.window.callback)
212+ }
159213}
160214
161- private class EmptyActivity : Activity ()
215+ private class EmptyActivity () : Activity(), LifecycleOwner {
216+
217+ override val lifecycle: Lifecycle = mock<Lifecycle >()
218+ }
0 commit comments