Skip to content

Commit 1c884cb

Browse files
Integration test for app launch
1 parent 2f50f29 commit 1c884cb

File tree

8 files changed

+188
-0
lines changed

8 files changed

+188
-0
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
* Copyright 2016-Present Datadog, Inc.
5+
*/
6+
7+
package com.datadog.android.sdk.integration.rum
8+
9+
import androidx.test.ext.junit.runners.AndroidJUnit4
10+
import androidx.test.filters.LargeTest
11+
import androidx.test.platform.app.InstrumentationRegistry
12+
import com.datadog.android.sdk.integration.RuntimeConfig
13+
import com.datadog.android.sdk.rules.AppLaunchActivityTestRule
14+
import com.datadog.tools.unit.ConditionWatcher
15+
import org.junit.Rule
16+
import org.junit.Test
17+
import org.junit.runner.RunWith
18+
19+
@RunWith(AndroidJUnit4::class)
20+
@LargeTest
21+
internal class AppLaunchTest : RumTest<AppLaunchPlaygroundActivity, AppLaunchActivityTestRule>() {
22+
23+
@get:Rule
24+
val mockServerRule = AppLaunchActivityTestRule()
25+
26+
override fun runInstrumentationScenario(
27+
mockServerRule: AppLaunchActivityTestRule
28+
): MutableList<ExpectedEvent> {
29+
val expectedEvents = mutableListOf<ExpectedEvent>()
30+
val instrumentation = InstrumentationRegistry.getInstrumentation()
31+
32+
instrumentation.waitForIdleSync()
33+
waitForPendingRUMEvents()
34+
35+
expectedEvents.add(
36+
ExpectedApplicationLaunchViewEvent(
37+
docVersion = 2
38+
)
39+
)
40+
41+
/**
42+
* We aren't checking the startup type (cold or warm) here
43+
* because the tests are run within the same OS process and we don't
44+
* know which startup type it will be. If this test is executed the first,
45+
* it will be cold, otherwise warm.
46+
*/
47+
expectedEvents.add(
48+
ExpectedVitalAppLaunchEvent(
49+
appLaunchMetric = AppLaunchMetric.TTID
50+
)
51+
)
52+
53+
expectedEvents.add(
54+
ExpectedVitalAppLaunchEvent(
55+
appLaunchMetric = AppLaunchMetric.TTFD
56+
)
57+
)
58+
59+
instrumentation.waitForIdleSync()
60+
61+
return expectedEvents
62+
}
63+
64+
@Test
65+
fun verifyRumEvents() {
66+
val expectedEvents = runInstrumentationScenario(mockServerRule)
67+
68+
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
69+
70+
ConditionWatcher {
71+
verifyExpectedEvents(
72+
mockServerRule.getRequests(RuntimeConfig.rumEndpointUrl),
73+
expectedEvents
74+
)
75+
true
76+
}.doWait(timeoutMs = FINAL_WAIT_MS)
77+
}
78+
}

instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/rum/ExpectedEvents.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ internal data class ExpectedErrorEvent(
5959
override val rumContext: ExpectedRumContext = resolvedRumContext()
6060
) : ExpectedEvent
6161

62+
internal data class ExpectedVitalAppLaunchEvent(
63+
val appLaunchMetric: AppLaunchMetric,
64+
override val rumContext: ExpectedRumContext = resolvedRumContext()
65+
) : ExpectedEvent
66+
6267
internal interface ExpectedEvent {
6368
val rumContext: ExpectedRumContext
6469
}
@@ -72,6 +77,11 @@ internal enum class ErrorSource(val sourceName: String) {
7277
NETWORK("network")
7378
}
7479

80+
internal enum class AppLaunchMetric(val metric: String) {
81+
TTID("ttid"),
82+
TTFD("ttfd")
83+
}
84+
7585
private val registryField = Datadog::class.java.getDeclaredField("registry").apply {
7686
isAccessible = true
7787
}

instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/rum/RumTest.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ internal abstract class RumTest<R : Activity, T : MockServerActivityTestRule<R>>
3333
val sentActionEvents = LinkedList<JsonObject>()
3434
val sentResourceEvents = LinkedList<JsonObject>()
3535
val sentLaunchEvents = LinkedList<JsonObject>()
36+
val sentVitalAppLaunchEvents = LinkedList<JsonObject>()
3637
handledRequests
3738
.filter { it.url?.isRumUrl() ?: false }
3839
.forEach { request ->
@@ -53,6 +54,8 @@ internal abstract class RumTest<R : Activity, T : MockServerActivityTestRule<R>>
5354
sentActionEvents += it
5455
} else if (it.isResourceEvent) {
5556
sentResourceEvents += it
57+
} else if (it.isVitalAppLaunchEvent) {
58+
sentVitalAppLaunchEvents += it
5659
}
5760
}
5861
}
@@ -64,6 +67,7 @@ internal abstract class RumTest<R : Activity, T : MockServerActivityTestRule<R>>
6467
val expectedViewEvents = expectedEvents.filterIsInstance<ExpectedViewEvent>()
6568
val expectedActionEvents = expectedEvents.filterIsInstance<ExpectedGestureEvent>()
6669
val expectedResourceEvents = expectedEvents.filterIsInstance<ExpectedResourceEvent>()
70+
val expectedVitalAppLaunchEvents = expectedEvents.filterIsInstance<ExpectedVitalAppLaunchEvent>()
6771
if (expectedLaunchEvents.isNotEmpty()) {
6872
sentLaunchEvents
6973
.reduceViewEvents()
@@ -80,6 +84,9 @@ internal abstract class RumTest<R : Activity, T : MockServerActivityTestRule<R>>
8084
if (expectedResourceEvents.isNotEmpty()) {
8185
sentResourceEvents.verifyEventMatches(expectedResourceEvents)
8286
}
87+
if (expectedVitalAppLaunchEvents.isNotEmpty()) {
88+
sentVitalAppLaunchEvents.verifyEventMatches(expectedVitalAppLaunchEvents)
89+
}
8390
}
8491

8592
protected fun verifyNoRumPayloadSent(
@@ -114,6 +121,10 @@ internal abstract class RumTest<R : Activity, T : MockServerActivityTestRule<R>>
114121
private val JsonObject.isTelemetryEvent
115122
get() = get("type")?.asString == "telemetry"
116123

124+
private val JsonObject.isVitalAppLaunchEvent
125+
get() = (get("type")?.asString == "vital") &&
126+
(get("vital")?.asJsonObject?.get("type")?.asString == "app_launch")
127+
117128
// two methods below are expected to be called only on view events
118129
private val JsonObject.viewId
119130
get() = get("view").asJsonObject.get("id").asString

instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/rum/RumTestUtils.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ internal fun List<JsonObject>.verifyEventMatches(
4242
is ExpectedGestureEvent -> event.verifyEventMatches(expectedEvent)
4343
is ExpectedResourceEvent -> event.verifyEventMatches(expectedEvent)
4444
is ExpectedErrorEvent -> event.verifyEventMatches(expectedEvent)
45+
is ExpectedVitalAppLaunchEvent -> event.verifyEventMatches(expectedEvent)
4546
else -> {
4647
// Do nothing
4748
}
@@ -192,6 +193,15 @@ private fun JsonObject.verifyEventMatches(event: ExpectedErrorEvent) {
192193
}
193194
}
194195

196+
private fun JsonObject.verifyEventMatches(event: ExpectedVitalAppLaunchEvent) {
197+
verifyRootMatches(event)
198+
199+
assertThat(this)
200+
.hasField("vital") {
201+
hasField("app_launch_metric", event.appLaunchMetric.metric)
202+
}
203+
}
204+
195205
private fun JsonObject.verifyRootMatches(event: ExpectedEvent) {
196206
val applicationId = getAsJsonObject("application")
197207
.getAsJsonPrimitive("id").asString
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
* Copyright 2016-Present Datadog, Inc.
5+
*/
6+
7+
package com.datadog.android.sdk.rules
8+
9+
import android.app.ActivityManager
10+
import androidx.test.platform.app.InstrumentationRegistry
11+
import com.datadog.android.Datadog
12+
import com.datadog.android.privacy.TrackingConsent
13+
import com.datadog.android.rum.DdRumContentProvider
14+
import com.datadog.android.rum.Rum
15+
import com.datadog.android.rum.tracking.ActivityViewTrackingStrategy
16+
import com.datadog.android.sdk.integration.RuntimeConfig
17+
import com.datadog.android.sdk.integration.rum.AppLaunchPlaygroundActivity
18+
19+
internal class AppLaunchActivityTestRule : RumMockServerActivityTestRule<AppLaunchPlaygroundActivity>(
20+
activityClass = AppLaunchPlaygroundActivity::class.java,
21+
keepRequests = true,
22+
trackingConsent = TrackingConsent.GRANTED
23+
) {
24+
override fun beforeActivityLaunched() {
25+
super.beforeActivityLaunched()
26+
val config = RuntimeConfig.configBuilder()
27+
.build()
28+
29+
val sdkCore = Datadog.initialize(
30+
InstrumentationRegistry.getInstrumentation().targetContext.applicationContext,
31+
config,
32+
trackingConsent
33+
)
34+
checkNotNull(sdkCore)
35+
36+
val rumConfig = RuntimeConfig.rumConfigBuilder()
37+
.trackUserInteractions()
38+
.trackLongTasks(RuntimeConfig.LONG_TASK_LARGE_THRESHOLD)
39+
.useViewTrackingStrategy(ActivityViewTrackingStrategy(false))
40+
.build()
41+
Rum.enable(rumConfig, sdkCore)
42+
DdRumContentProvider.processImportance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
43+
}
44+
}

instrumented/integration/src/main/AndroidManifest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@
8484
<category android:name="android.intent.category.LAUNCHER" />
8585
</intent-filter>
8686
</activity>
87+
<activity android:name=".rum.AppLaunchPlaygroundActivity"
88+
android:label="@string/activity_app_launch"
89+
android:windowSoftInputMode="adjustResize"/>
8790
</application>
8891

8992
</manifest>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
* Copyright 2016-Present Datadog, Inc.
5+
*/
6+
7+
package com.datadog.android.sdk.integration.rum
8+
9+
import android.os.Bundle
10+
import androidx.appcompat.app.AppCompatActivity
11+
import com.datadog.android.rum.ExperimentalRumApi
12+
import com.datadog.android.rum.GlobalRumMonitor
13+
import com.datadog.android.sdk.integration.R
14+
15+
@OptIn(ExperimentalRumApi::class)
16+
internal class AppLaunchPlaygroundActivity : AppCompatActivity() {
17+
override fun onCreate(savedInstanceState: Bundle?) {
18+
super.onCreate(savedInstanceState)
19+
setContentView(R.layout.main_activity_layout)
20+
}
21+
22+
override fun onResume() {
23+
super.onResume()
24+
25+
@Suppress("MagicNumber")
26+
window.decorView.postDelayed(
27+
{ GlobalRumMonitor.get().reportAppFullyDisplayed() },
28+
3000
29+
)
30+
}
31+
}

instrumented/integration/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
<string name="activity_logs_end_to_end">Logs (End to End)</string>
1212
<string name="activity_traces_end_to_end">Traces (End to End)</string>
13+
<string name="activity_app_launch">App Launch (End to End)</string>
1314
<string name="activity_logs_profiling">Logs (Profiling)</string>
1415
<string name="button1">Button1</string>
1516
<string name="rum_gestures_tracking_end_to_end">Rum Gestures (End to End)</string>

0 commit comments

Comments
 (0)