Skip to content

Commit f21cea2

Browse files
committed
Add extra unit test coverage
1 parent 680b6ef commit f21cea2

3 files changed

Lines changed: 198 additions & 0 deletions

File tree

dd-sdk-android-internal/src/test/java/com/datadog/android/rum/AppLaunchPreInitCollectorTest.kt

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,4 +846,62 @@ internal class AppLaunchPreInitCollectorTest {
846846
}
847847

848848
// endregion
849+
850+
// region computeProcessStartNs OEM guard branches
851+
852+
@Test
853+
fun `M fall back to createTimeNs W computeProcessStartNs() {API 24+, computed is after createTimeNs}`() {
854+
// Given: createTimeNs set to 1 so computed (≈ nanoTime - uptime, a large value) > fallback
855+
whenever(stubBuildSdkVersionProvider.isAtLeastN).thenReturn(true)
856+
DdRumContentProvider.createTimeNs = 1L
857+
858+
// When
859+
val result = AppLaunchPreInitCollector.computeProcessStartNs()
860+
861+
// Then: computed > fallback guard → returns fallback
862+
assertThat(result).isEqualTo(1L)
863+
}
864+
865+
@Test
866+
fun `M fall back to createTimeNs W computeProcessStartNs() {API 24+, computed too far before createTimeNs}`() {
867+
// Given: createTimeNs set to far future so (fallback - computed) >> 10s threshold
868+
whenever(stubBuildSdkVersionProvider.isAtLeastN).thenReturn(true)
869+
val farFuture = Long.MAX_VALUE / 2
870+
DdRumContentProvider.createTimeNs = farFuture
871+
872+
// When
873+
val result = AppLaunchPreInitCollector.computeProcessStartNs()
874+
875+
// Then: isTooFarBefore guard → returns fallback
876+
assertThat(result).isEqualTo(farFuture)
877+
}
878+
879+
// endregion
880+
881+
// region isFirstActivityForProcess tracking
882+
883+
@Test
884+
fun `M set isFirstActivityForProcess false W onBeforeActivityCreated() {previous activity was destroyed}`() {
885+
// Given: install, then simulate a prior activity being destroyed before our capture
886+
AppLaunchPreInitCollector.install(mockApplication)
887+
val captor = argumentCaptor<Application.ActivityLifecycleCallbacks>()
888+
verify(mockApplication).registerActivityLifecycleCallbacks(captor.capture())
889+
val callbacks = captor.firstValue
890+
891+
// Simulate an activity destroyed before the first capture
892+
callbacks.onActivityDestroyed(mockActivity)
893+
894+
// A new activity is then created (the one we capture)
895+
val mockNewActivity = mock<Activity>()
896+
whenever(mockNewActivity.window).thenReturn(mockWindow)
897+
whenever(mockNewActivity.application).thenReturn(mockApplication)
898+
899+
// When
900+
callbacks.onActivityPreCreated(mockNewActivity, null)
901+
902+
// Then: because _isFirstActivityForProcess was set to false, isFirstActivityForProcess is false
903+
assertThat(AppLaunchPreInitCollector.isFirstActivityForProcess).isFalse()
904+
}
905+
906+
// endregion
849907
}

features/dd-sdk-android-rum-prelaunch/src/test/kotlin/com/datadog/android/rum/prelaunch/AppLaunchCollectorProviderTest.kt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package com.datadog.android.rum.prelaunch
99
import android.app.ActivityManager
1010
import android.app.Application
1111
import android.content.Context
12+
import android.net.Uri
1213
import com.datadog.android.rum.AppLaunchPreInitCollector
1314
import com.datadog.android.rum.DdRumContentProvider
1415
import com.datadog.android.rum.prelaunch.forge.Configurator
@@ -24,6 +25,7 @@ import org.junit.jupiter.api.extension.Extensions
2425
import org.mockito.Mock
2526
import org.mockito.junit.jupiter.MockitoExtension
2627
import org.mockito.junit.jupiter.MockitoSettings
28+
import org.mockito.kotlin.mock
2729
import org.mockito.kotlin.whenever
2830
import org.mockito.quality.Strictness
2931
import java.util.concurrent.atomic.AtomicReference
@@ -122,5 +124,53 @@ internal class AppLaunchCollectorProviderTest {
122124
.isEqualTo(AppLaunchPreInitCollector.State.IDLE)
123125
}
124126

127+
@Test
128+
fun `M return false W onCreate() {applicationContext is not Application}`() {
129+
// Given: applicationContext returns a plain Context, not Application
130+
whenever(mockContext.applicationContext).thenReturn(mockContext)
131+
132+
// When
133+
val result = testedProvider.onCreate()
134+
135+
// Then
136+
assertThat(result).isFalse()
137+
assertThat(AppLaunchPreInitCollector.state)
138+
.isEqualTo(AppLaunchPreInitCollector.State.NOT_INSTALLED)
139+
}
140+
141+
// endregion
142+
143+
// region stub methods
144+
145+
@Test
146+
fun `M return null W query() {stub}`() {
147+
val mockUri = mock<Uri>()
148+
assertThat(testedProvider.query(mockUri, null, null, null, null)).isNull()
149+
}
150+
151+
@Test
152+
fun `M return null W getType() {stub}`() {
153+
val mockUri = mock<Uri>()
154+
assertThat(testedProvider.getType(mockUri)).isNull()
155+
}
156+
157+
@Test
158+
fun `M return null W insert() {stub}`() {
159+
val mockUri = mock<Uri>()
160+
assertThat(testedProvider.insert(mockUri, null)).isNull()
161+
}
162+
163+
@Test
164+
fun `M return 0 W delete() {stub}`() {
165+
val mockUri = mock<Uri>()
166+
assertThat(testedProvider.delete(mockUri, null, null)).isEqualTo(0)
167+
}
168+
169+
@Test
170+
fun `M return 0 W update() {stub}`() {
171+
val mockUri = mock<Uri>()
172+
assertThat(testedProvider.update(mockUri, null, null, null)).isEqualTo(0)
173+
}
174+
125175
// endregion
126176
}

features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeaturePreInitStartupTest.kt

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,96 @@ internal class RumFeaturePreInitStartupTest {
667667

668668
// endregion
669669

670+
// region monitor not available at dispatch time
671+
672+
@Test
673+
fun `M skip events W pendingPreLaunchAction() { GlobalRumMonitor not an AdvancedRumMonitor }`() {
674+
// Given — COMPLETE state with valid data
675+
val mockActivity = mock<Activity>()
676+
setCollectorState(AppLaunchPreInitCollector.State.COMPLETE)
677+
configureCollectorData(
678+
processStartNs = 100_000L,
679+
activityOnCreateNs = 200_000L,
680+
firstFrameNs = 300_000L,
681+
isFirstActivityForProcess = true,
682+
activity = mockActivity
683+
)
684+
testedFeature.onInitialize(appContext.mockInstance)
685+
686+
// Clear the registered monitor so GlobalRumMonitor.get() returns a NoOp (not AdvancedRumMonitor)
687+
GlobalRumMonitor.clear()
688+
689+
// When
690+
testedFeature.pendingPreLaunchAction?.invoke()
691+
692+
// Then — early return: no events sent, no crash
693+
verify(mockRumMonitor, never()).sendAppStartEvent(any())
694+
verify(mockRumMonitor, never()).sendTTIDEvent(any())
695+
}
696+
697+
@Test
698+
fun `M skip first-frame events W addFirstFrameCallback() { CAPTURING + monitor not AdvancedRumMonitor }`() {
699+
// Given — CAPTURING state
700+
val mockActivity = mock<Activity>()
701+
setCollectorState(AppLaunchPreInitCollector.State.CAPTURING)
702+
configureCollectorData(
703+
processStartNs = 100_000L,
704+
activityOnCreateNs = 200_000L,
705+
isFirstActivityForProcess = true,
706+
activity = mockActivity
707+
)
708+
testedFeature.onInitialize(appContext.mockInstance)
709+
710+
// Clear monitor so GlobalRumMonitor.get() inside the callback returns NoOp
711+
GlobalRumMonitor.clear()
712+
713+
// When — fire the first-frame callback
714+
val fakeFirstFrameNs = 300_000L
715+
setCollectorState(AppLaunchPreInitCollector.State.COMPLETE)
716+
AppLaunchPreInitCollector.firstFrameNs = fakeFirstFrameNs
717+
val callbacks = getFirstFrameCallbacks()
718+
callbacks.forEach { it(fakeFirstFrameNs) }
719+
720+
// Then — early return inside callback: no events sent, no crash
721+
verify(mockRumMonitor, never()).sendAppStartEvent(any())
722+
verify(mockRumMonitor, never()).sendTTIDEvent(any())
723+
}
724+
725+
@Test
726+
fun `M not call view tracking strategy W pendingPreLaunchAction() { strategy is null }`() {
727+
// Given — null viewTrackingStrategy hits the else -> Unit branch
728+
val mockActivity = mock<Activity>()
729+
setCollectorState(AppLaunchPreInitCollector.State.COMPLETE)
730+
configureCollectorData(
731+
processStartNs = 100_000L,
732+
activityOnCreateNs = 200_000L,
733+
firstFrameNs = 300_000L,
734+
isFirstActivityForProcess = true,
735+
activity = mockActivity
736+
)
737+
testedFeature = RumFeature(
738+
mockSdkCore,
739+
fakeApplicationId.toString(),
740+
fakeConfiguration.copy(
741+
appStartupActivityPredicate = mockAppStartupActivityPredicate,
742+
viewTrackingStrategy = null
743+
),
744+
lateCrashReporterFactory = { mockLateCrashReporter }
745+
)
746+
GlobalRumMonitor.clear()
747+
GlobalRumMonitor.registerIfAbsent(mockRumMonitor, mockSdkCore)
748+
749+
// When
750+
testedFeature.onInitialize(appContext.mockInstance)
751+
testedFeature.pendingPreLaunchAction?.invoke()
752+
753+
// Then — no NPE, events still sent
754+
verify(mockRumMonitor, times(1)).sendAppStartEvent(any())
755+
verify(mockRumMonitor, times(1)).sendTTIDEvent(any())
756+
}
757+
758+
// endregion
759+
670760
companion object {
671761
val appContext = ApplicationContextTestConfiguration(Application::class.java)
672762
private val mainLooper = MainLooperTestConfiguration()

0 commit comments

Comments
 (0)