diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogCore.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogCore.kt index e111bf5073..0ac44f27de 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogCore.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogCore.kt @@ -252,8 +252,12 @@ internal class DatadogCore( feature.featureContextLock.writeLock().safeTryWithLock(1, TimeUnit.SECONDS, internalLogger) { val currentContext = feature.featureContext updateCallback(currentContext) - featureContextUpdateReceivers.forEach { - it.onContextUpdate(featureName, currentContext) + if (featureContextUpdateReceivers.isNotEmpty()) { + @Suppress("UnsafeThirdPartyFunctionCall") // currentContext is non-null + val contextSnapshot = HashMap(currentContext) + featureContextUpdateReceivers.forEach { + it.onContextUpdate(featureName, contextSnapshot) + } } } } diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt index c4cae46c2d..dfefe4d819 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt @@ -69,6 +69,7 @@ import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn import org.mockito.kotlin.doThrow +import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.reset import org.mockito.kotlin.times @@ -422,6 +423,42 @@ internal class DatadogCoreTest { verify(mockFeatureContextUpdateListener).onContextUpdate(feature, fakeContext) } + @Test + fun `M publish independent snapshots W updateFeatureContext()`( + @StringForgery fakeFeatureName: String, + @StringForgery fakeKey: String, + @StringForgery fakeFirstValue: String, + @StringForgery fakeSecondValue: String + ) { + // Given + val mockFeature = + mock().apply { + whenever(featureContextLock) doReturn ReentrantReadWriteLock() + whenever(featureContext) doReturn mutableMapOf() + } + testedCore.features[fakeFeatureName] = mockFeature + val mockReceiver = mock() + testedCore.setContextUpdateReceiver(mockReceiver) + + // When + testedCore.updateFeatureContext(fakeFeatureName, useContextThread = false) { + it[fakeKey] = fakeFirstValue + } + testedCore.updateFeatureContext(fakeFeatureName, useContextThread = false) { + it.clear() + it[fakeKey] = fakeSecondValue + } + + // Then + argumentCaptor> { + verify(mockReceiver, times(2)).onContextUpdate(eq(fakeFeatureName), capture()) + assertThat(firstValue).isEqualTo(mapOf(fakeKey to fakeFirstValue)) + assertThat(secondValue).isEqualTo(mapOf(fakeKey to fakeSecondValue)) + assertThat(firstValue).isNotSameAs(mockFeature.featureContext) + assertThat(secondValue).isNotSameAs(mockFeature.featureContext) + } + } + @Test fun `M update feature context W updateFeatureContext() { concurrent access }`( @StringForgery feature: String, diff --git a/dd-sdk-android-internal/api/apiSurface b/dd-sdk-android-internal/api/apiSurface index dbc030ce51..d55c1b79c0 100644 --- a/dd-sdk-android-internal/api/apiSurface +++ b/dd-sdk-android-internal/api/apiSurface @@ -214,14 +214,15 @@ enum com.datadog.android.internal.profiling.quota.QuotaReason - TIMEOUT - API_ERROR data class com.datadog.android.internal.rum.RumContext - constructor(String = NULL_UUID, String = NULL_UUID, Boolean = false, String? = null, String? = null, String? = null, String? = null, RumSessionState = RumSessionState.NOT_TRACKED, RumSessionStartReason = RumSessionStartReason.USER_APP_LAUNCH, RumViewType = RumViewType.NONE, String? = null, String? = null, Long = 0L, Long = 0L, Boolean = false, Boolean = false, Float = SAMPLE_ALL_RATE) + constructor(String, RumSessionContext? = null, String? = null, String? = null, String? = null, String? = null, RumViewType = RumViewType.NONE, String? = null, String? = null, Long = 0L, Long = 0L, Boolean = false) companion object const val NULL_UUID: String const val SAMPLE_ALL_RATE: Float fun Map.rumContext(): RumContext? object com.datadog.android.internal.rum.RumSessionConstants const val EMPTY_RUM_SESSION_ID: String - const val TRACKED_SESSION_STATE: String +data class com.datadog.android.internal.rum.RumSessionContext + constructor(String, RumSessionState, RumSessionStartReason, Boolean, Float, Boolean) enum com.datadog.android.internal.rum.RumSessionStartReason constructor(String) - USER_APP_LAUNCH diff --git a/dd-sdk-android-internal/api/dd-sdk-android-internal.api b/dd-sdk-android-internal/api/dd-sdk-android-internal.api index 625fe03ecf..f1c847a97d 100644 --- a/dd-sdk-android-internal/api/dd-sdk-android-internal.api +++ b/dd-sdk-android-internal/api/dd-sdk-android-internal.api @@ -494,37 +494,27 @@ public final class com/datadog/android/internal/rum/RumContext { public static final field Companion Lcom/datadog/android/internal/rum/RumContext$Companion; public static final field NULL_UUID Ljava/lang/String; public static final field SAMPLE_ALL_RATE F - public fun ()V - public fun (Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/datadog/android/internal/rum/RumSessionState;Lcom/datadog/android/internal/rum/RumSessionStartReason;Lcom/datadog/android/internal/rum/RumViewType;Ljava/lang/String;Ljava/lang/String;JJZZF)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/datadog/android/internal/rum/RumSessionState;Lcom/datadog/android/internal/rum/RumSessionStartReason;Lcom/datadog/android/internal/rum/RumViewType;Ljava/lang/String;Ljava/lang/String;JJZZFILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Lcom/datadog/android/internal/rum/RumSessionContext;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/datadog/android/internal/rum/RumViewType;Ljava/lang/String;Ljava/lang/String;JJZ)V + public synthetic fun (Ljava/lang/String;Lcom/datadog/android/internal/rum/RumSessionContext;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/datadog/android/internal/rum/RumViewType;Ljava/lang/String;Ljava/lang/String;JJZILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; - public final fun component10 ()Lcom/datadog/android/internal/rum/RumViewType; - public final fun component11 ()Ljava/lang/String; - public final fun component12 ()Ljava/lang/String; - public final fun component13 ()J - public final fun component14 ()J - public final fun component15 ()Z - public final fun component16 ()Z - public final fun component17 ()F - public final fun component2 ()Ljava/lang/String; - public final fun component3 ()Z + public final fun component10 ()J + public final fun component11 ()J + public final fun component12 ()Z + public final fun component2 ()Lcom/datadog/android/internal/rum/RumSessionContext; + public final fun component3 ()Ljava/lang/String; public final fun component4 ()Ljava/lang/String; public final fun component5 ()Ljava/lang/String; public final fun component6 ()Ljava/lang/String; - public final fun component7 ()Ljava/lang/String; - public final fun component8 ()Lcom/datadog/android/internal/rum/RumSessionState; - public final fun component9 ()Lcom/datadog/android/internal/rum/RumSessionStartReason; - public final fun copy (Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/datadog/android/internal/rum/RumSessionState;Lcom/datadog/android/internal/rum/RumSessionStartReason;Lcom/datadog/android/internal/rum/RumViewType;Ljava/lang/String;Ljava/lang/String;JJZZF)Lcom/datadog/android/internal/rum/RumContext; - public static synthetic fun copy$default (Lcom/datadog/android/internal/rum/RumContext;Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/datadog/android/internal/rum/RumSessionState;Lcom/datadog/android/internal/rum/RumSessionStartReason;Lcom/datadog/android/internal/rum/RumViewType;Ljava/lang/String;Ljava/lang/String;JJZZFILjava/lang/Object;)Lcom/datadog/android/internal/rum/RumContext; + public final fun component7 ()Lcom/datadog/android/internal/rum/RumViewType; + public final fun component8 ()Ljava/lang/String; + public final fun component9 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;Lcom/datadog/android/internal/rum/RumSessionContext;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/datadog/android/internal/rum/RumViewType;Ljava/lang/String;Ljava/lang/String;JJZ)Lcom/datadog/android/internal/rum/RumContext; + public static synthetic fun copy$default (Lcom/datadog/android/internal/rum/RumContext;Ljava/lang/String;Lcom/datadog/android/internal/rum/RumSessionContext;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/datadog/android/internal/rum/RumViewType;Ljava/lang/String;Ljava/lang/String;JJZILjava/lang/Object;)Lcom/datadog/android/internal/rum/RumContext; public fun equals (Ljava/lang/Object;)Z public final fun getActionId ()Ljava/lang/String; public final fun getApplicationId ()Ljava/lang/String; public final fun getHasReplay ()Z - public final fun getHasTimeseries ()Z - public final fun getSessionId ()Ljava/lang/String; - public final fun getSessionSampleRate ()F - public final fun getSessionStartReason ()Lcom/datadog/android/internal/rum/RumSessionStartReason; - public final fun getSessionState ()Lcom/datadog/android/internal/rum/RumSessionState; + public final fun getSession ()Lcom/datadog/android/internal/rum/RumSessionContext; public final fun getSyntheticsResultId ()Ljava/lang/String; public final fun getSyntheticsTestId ()Ljava/lang/String; public final fun getViewId ()Ljava/lang/String; @@ -534,7 +524,6 @@ public final class com/datadog/android/internal/rum/RumContext { public final fun getViewType ()Lcom/datadog/android/internal/rum/RumViewType; public final fun getViewUrl ()Ljava/lang/String; public fun hashCode ()I - public final fun isSessionActive ()Z public fun toString ()Ljava/lang/String; } @@ -548,7 +537,27 @@ public final class com/datadog/android/internal/rum/RumContextKt { public final class com/datadog/android/internal/rum/RumSessionConstants { public static final field EMPTY_RUM_SESSION_ID Ljava/lang/String; public static final field INSTANCE Lcom/datadog/android/internal/rum/RumSessionConstants; - public static final field TRACKED_SESSION_STATE Ljava/lang/String; +} + +public final class com/datadog/android/internal/rum/RumSessionContext { + public fun (Ljava/lang/String;Lcom/datadog/android/internal/rum/RumSessionState;Lcom/datadog/android/internal/rum/RumSessionStartReason;ZFZ)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lcom/datadog/android/internal/rum/RumSessionState; + public final fun component3 ()Lcom/datadog/android/internal/rum/RumSessionStartReason; + public final fun component4 ()Z + public final fun component5 ()F + public final fun component6 ()Z + public final fun copy (Ljava/lang/String;Lcom/datadog/android/internal/rum/RumSessionState;Lcom/datadog/android/internal/rum/RumSessionStartReason;ZFZ)Lcom/datadog/android/internal/rum/RumSessionContext; + public static synthetic fun copy$default (Lcom/datadog/android/internal/rum/RumSessionContext;Ljava/lang/String;Lcom/datadog/android/internal/rum/RumSessionState;Lcom/datadog/android/internal/rum/RumSessionStartReason;ZFZILjava/lang/Object;)Lcom/datadog/android/internal/rum/RumSessionContext; + public fun equals (Ljava/lang/Object;)Z + public final fun getHasTimeseries ()Z + public final fun getId ()Ljava/lang/String; + public final fun getSampleRate ()F + public final fun getStartReason ()Lcom/datadog/android/internal/rum/RumSessionStartReason; + public final fun getState ()Lcom/datadog/android/internal/rum/RumSessionState; + public fun hashCode ()I + public final fun isActive ()Z + public fun toString ()Ljava/lang/String; } public final class com/datadog/android/internal/rum/RumSessionStartReason : java/lang/Enum { diff --git a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/rum/RumContext.kt b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/rum/RumContext.kt index d4b487c65b..7134bc6a4c 100644 --- a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/rum/RumContext.kt +++ b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/rum/RumContext.kt @@ -14,46 +14,36 @@ import com.datadog.android.internal.FeatureContextKeys * Public for access across SDK modules. The data-class contract preserves the copying and * equality semantics used by RUM scopes; it is an SDK-internal API. * - * @property applicationId RUM application identifier, or [NULL_UUID] when unavailable. - * @property sessionId RUM session identifier, or [NULL_UUID] before a session is created. - * @property isSessionActive Whether the session scope is active. + * @property applicationId Configured RUM application identifier. + * @property session Session information, or null when no session is available. * @property viewId Identifier of the associated view, if any. * @property viewName Name of the associated view, if any. * @property viewUrl URL of the associated view, if any. * @property actionId Identifier of the associated action, if any. - * @property sessionState Tracking state of the session. - * @property sessionStartReason Trigger that created the session. * @property viewType Category of the associated view, or [RumViewType.NONE] when unavailable. * @property syntheticsTestId Identifier of the Synthetics test, if any. * @property syntheticsResultId Identifier of the Synthetics test result, if any. * @property viewTimestamp Start timestamp of the view in milliseconds. * @property viewTimestampOffset Server time offset used by the view, in milliseconds. * @property hasReplay Whether the view has Session Replay data. - * @property hasTimeseries Whether the session has collected timeseries data. - * @property sessionSampleRate Session sampling rate as a percentage, from 0 to 100. */ data class RumContext( - val applicationId: String = NULL_UUID, - val sessionId: String = NULL_UUID, - val isSessionActive: Boolean = false, + val applicationId: String, + val session: RumSessionContext? = null, val viewId: String? = null, val viewName: String? = null, val viewUrl: String? = null, val actionId: String? = null, - val sessionState: RumSessionState = RumSessionState.NOT_TRACKED, - val sessionStartReason: RumSessionStartReason = RumSessionStartReason.USER_APP_LAUNCH, val viewType: RumViewType = RumViewType.NONE, val syntheticsTestId: String? = null, val syntheticsResultId: String? = null, val viewTimestamp: Long = 0L, val viewTimestampOffset: Long = 0L, - val hasReplay: Boolean = false, - val hasTimeseries: Boolean = false, - val sessionSampleRate: Float = SAMPLE_ALL_RATE + val hasReplay: Boolean = false ) { companion object { - /** Sentinel used when an application or session identifier is unavailable. */ + /** Legacy sentinel used at boundaries that require an identifier. */ const val NULL_UUID: String = RumSessionConstants.EMPTY_RUM_SESSION_ID /** Sampling percentage that keeps every session. */ diff --git a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/rum/RumSessionConstants.kt b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/rum/RumSessionConstants.kt index c13d3e8dc8..5a9f03345f 100644 --- a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/rum/RumSessionConstants.kt +++ b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/rum/RumSessionConstants.kt @@ -16,9 +16,4 @@ object RumSessionConstants { * ID equals this value. */ const val EMPTY_RUM_SESSION_ID: String = "00000000-0000-0000-0000-000000000000" - - /** - * Value the RUM session state takes when the session is tracked (sampled in). - */ - const val TRACKED_SESSION_STATE: String = "TRACKED" } diff --git a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/rum/RumSessionContext.kt b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/rum/RumSessionContext.kt new file mode 100644 index 0000000000..7e1ee9d6ce --- /dev/null +++ b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/rum/RumSessionContext.kt @@ -0,0 +1,26 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +package com.datadog.android.internal.rum + +/** + * Snapshot of an existing RUM session, including sampled-out and expired sessions. + * + * @property id RUM session identifier. + * @property state Tracking state of the session. + * @property startReason Trigger that created the session. + * @property isActive Whether the session scope is active. + * @property sampleRate Session sampling rate as a percentage, from 0 to 100. + * @property hasTimeseries Whether the session has collected timeseries data. + */ +data class RumSessionContext( + val id: String, + val state: RumSessionState, + val startReason: RumSessionStartReason, + val isActive: Boolean, + val sampleRate: Float, + val hasTimeseries: Boolean +) diff --git a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/rum/RumSessionState.kt b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/rum/RumSessionState.kt index a1dbc740c3..0884b55ec2 100644 --- a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/rum/RumSessionState.kt +++ b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/rum/RumSessionState.kt @@ -16,7 +16,7 @@ enum class RumSessionState(val asString: String) { NOT_TRACKED("NOT_TRACKED"), /** Session is sampled for RUM event collection. */ - TRACKED(RumSessionConstants.TRACKED_SESSION_STATE), + TRACKED("TRACKED"), /** Session has expired due to inactivity. */ EXPIRED("EXPIRED") diff --git a/dd-sdk-android-internal/src/testFixtures/kotlin/com/datadog/android/internal/tests/elmyr/RumContextForgeryFactory.kt b/dd-sdk-android-internal/src/testFixtures/kotlin/com/datadog/android/internal/tests/elmyr/RumContextForgeryFactory.kt index d119985e19..b5e56093a1 100644 --- a/dd-sdk-android-internal/src/testFixtures/kotlin/com/datadog/android/internal/tests/elmyr/RumContextForgeryFactory.kt +++ b/dd-sdk-android-internal/src/testFixtures/kotlin/com/datadog/android/internal/tests/elmyr/RumContextForgeryFactory.kt @@ -7,8 +7,7 @@ package com.datadog.android.internal.tests.elmyr import com.datadog.android.internal.rum.RumContext -import com.datadog.android.internal.rum.RumSessionStartReason -import com.datadog.android.internal.rum.RumSessionState +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.rum.RumViewType import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.ForgeryFactory @@ -16,21 +15,19 @@ import java.util.UUID class RumContextForgeryFactory : ForgeryFactory { override fun getForgery(forge: Forge): RumContext { + val applicationId = forge.getForgery().toString() + val session = forge.aNullable { getForgery() } + ?: return RumContext(applicationId = applicationId) return RumContext( - applicationId = forge.getForgery().toString(), - sessionId = forge.getForgery().toString(), - isSessionActive = forge.aBool(), + applicationId = applicationId, + session = session, viewId = forge.aNullable { getForgery().toString() }, viewName = forge.aNullable { forge.anAlphaNumericalString() }, viewUrl = forge.aStringMatching("http(s?)://[a-z]+\\.com/[a-z]+"), actionId = forge.aNullable { getForgery().toString() }, - sessionState = forge.aValueFrom(RumSessionState::class.java), - sessionStartReason = forge.aValueFrom(RumSessionStartReason::class.java), viewType = forge.aValueFrom(RumViewType::class.java), syntheticsTestId = forge.aNullable { forge.anAlphaNumericalString() }, - syntheticsResultId = forge.aNullable { forge.anAlphaNumericalString() }, - hasTimeseries = forge.aBool(), - sessionSampleRate = forge.aFloat(min = 0f, max = 100f) + syntheticsResultId = forge.aNullable { forge.anAlphaNumericalString() } ) } } diff --git a/dd-sdk-android-internal/src/testFixtures/kotlin/com/datadog/android/internal/tests/elmyr/RumSessionContextForgeryFactory.kt b/dd-sdk-android-internal/src/testFixtures/kotlin/com/datadog/android/internal/tests/elmyr/RumSessionContextForgeryFactory.kt new file mode 100644 index 0000000000..0fbf4a21aa --- /dev/null +++ b/dd-sdk-android-internal/src/testFixtures/kotlin/com/datadog/android/internal/tests/elmyr/RumSessionContextForgeryFactory.kt @@ -0,0 +1,25 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +package com.datadog.android.internal.tests.elmyr + +import com.datadog.android.internal.rum.RumSessionContext +import com.datadog.android.internal.rum.RumSessionStartReason +import com.datadog.android.internal.rum.RumSessionState +import fr.xgouchet.elmyr.Forge +import fr.xgouchet.elmyr.ForgeryFactory +import java.util.UUID + +class RumSessionContextForgeryFactory : ForgeryFactory { + override fun getForgery(forge: Forge): RumSessionContext = RumSessionContext( + id = forge.getForgery().toString(), + state = forge.aValueFrom(RumSessionState::class.java), + startReason = forge.aValueFrom(RumSessionStartReason::class.java), + isActive = forge.aBool(), + sampleRate = forge.aFloat(min = 0f, max = 100f), + hasTimeseries = forge.aBool() + ) +} diff --git a/detekt_custom_safe_calls_third_party.yml b/detekt_custom_safe_calls_third_party.yml index 43a79a878e..b29df1a545 100644 --- a/detekt_custom_safe_calls_third_party.yml +++ b/detekt_custom_safe_calls_third_party.yml @@ -728,6 +728,7 @@ datadog: - "kotlin.collections.MutableSet.contains(kotlin.String)" - "kotlin.collections.MutableSet.contains(kotlin.String?)" - "kotlin.collections.MutableSet.isEmpty()" + - "kotlin.collections.MutableSet.isNotEmpty()" - "kotlin.collections.MutableSet.remove(kotlin.String)" - "kotlin.collections.MutableSet.filter(kotlin.Function1)" - "kotlin.collections.MutableSet.firstOrNull()" @@ -940,6 +941,7 @@ datadog: - "kotlin.String.removePrefix(kotlin.CharSequence)" - "kotlin.String.removeSuffix(kotlin.CharSequence)" - "kotlin.String.takeIf(kotlin.Function1)" + - "kotlin.String.takeUnless(kotlin.Function1)" - "kotlin.String.toBigIntegerOrNull()" - "kotlin.String.toBoolean()" - "kotlin.String.toBooleanStrictOrNull()" diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/utils/forge/ForgeConfigurator.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/utils/forge/ForgeConfigurator.kt index e9d63d1439..9378a87b6f 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/utils/forge/ForgeConfigurator.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/utils/forge/ForgeConfigurator.kt @@ -7,6 +7,7 @@ package com.datadog.android.flags.utils.forge import com.datadog.android.internal.tests.elmyr.RumContextForgeryFactory +import com.datadog.android.internal.tests.elmyr.RumSessionContextForgeryFactory import com.datadog.android.tests.elmyr.useCoreFactories import com.datadog.tools.unit.forge.BaseConfigurator import fr.xgouchet.elmyr.Forge @@ -19,6 +20,7 @@ internal class ForgeConfigurator : BaseConfigurator() { // so that we can forge DatadogContext forge.useCoreFactories() forge.addFactory(RumContextForgeryFactory()) + forge.addFactory(RumSessionContextForgeryFactory()) forge.addFactory(FlagsConfigurationForgeryFactory()) forge.addFactory(PrecomputedFlagForgeryFactory()) diff --git a/features/dd-sdk-android-logs/src/main/kotlin/com/datadog/android/log/internal/domain/DatadogLogGenerator.kt b/features/dd-sdk-android-logs/src/main/kotlin/com/datadog/android/log/internal/domain/DatadogLogGenerator.kt index fa9e5744b0..139aeabdbe 100644 --- a/features/dd-sdk-android-logs/src/main/kotlin/com/datadog/android/log/internal/domain/DatadogLogGenerator.kt +++ b/features/dd-sdk-android-logs/src/main/kotlin/com/datadog/android/log/internal/domain/DatadogLogGenerator.kt @@ -379,10 +379,14 @@ internal class DatadogLogGenerator( if (bundleWithRum) { datadogContext.featuresContext[Feature.RUM_FEATURE_NAME]?.let { val rumContext = it.rumContext() - combinedAttributes[LogAttributes.RUM_APPLICATION_ID] = rumContext?.applicationId - combinedAttributes[LogAttributes.RUM_SESSION_ID] = rumContext?.sessionId - combinedAttributes[LogAttributes.RUM_VIEW_ID] = rumContext?.viewId - combinedAttributes[LogAttributes.RUM_ACTION_ID] = rumContext?.actionId + combinedAttributes.remove(LogAttributes.RUM_APPLICATION_ID) + combinedAttributes.remove(LogAttributes.RUM_SESSION_ID) + combinedAttributes.remove(LogAttributes.RUM_VIEW_ID) + combinedAttributes.remove(LogAttributes.RUM_ACTION_ID) + rumContext?.applicationId?.let { value -> combinedAttributes[LogAttributes.RUM_APPLICATION_ID] = value } + rumContext?.session?.let { session -> combinedAttributes[LogAttributes.RUM_SESSION_ID] = session.id } + rumContext?.viewId?.let { value -> combinedAttributes[LogAttributes.RUM_VIEW_ID] = value } + rumContext?.actionId?.let { value -> combinedAttributes[LogAttributes.RUM_ACTION_ID] = value } } } return combinedAttributes diff --git a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/LogsFeatureTest.kt b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/LogsFeatureTest.kt index 9a086e01cf..32c42916de 100644 --- a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/LogsFeatureTest.kt +++ b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/LogsFeatureTest.kt @@ -441,10 +441,10 @@ internal class LogsFeatureTest { .hasExactlyAttributes( fakeAttributes + mapOf( LogAttributes.RUM_APPLICATION_ID to fakeRumContext.applicationId, - LogAttributes.RUM_SESSION_ID to fakeRumContext.sessionId, + LogAttributes.RUM_SESSION_ID to fakeRumContext.session?.id, LogAttributes.RUM_VIEW_ID to fakeRumContext.viewId, LogAttributes.RUM_ACTION_ID to fakeRumContext.actionId - ) + ).filterValues { it != null } ) .hasExactlyTags( setOf( diff --git a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/DatadogLogGeneratorTest.kt b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/DatadogLogGeneratorTest.kt index 9aa45a1b0b..a47c1ca2d3 100644 --- a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/DatadogLogGeneratorTest.kt +++ b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/DatadogLogGeneratorTest.kt @@ -15,6 +15,7 @@ import com.datadog.android.api.feature.Feature import com.datadog.android.core.feature.event.ThreadDump import com.datadog.android.internal.FeatureContextKeys import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.log.LogAttributes import com.datadog.android.log.assertj.LogEventAssert.Companion.assertThat import com.datadog.android.log.model.LogEvent @@ -43,6 +44,7 @@ import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.quality.Strictness import java.util.Locale +import java.util.UUID @Extensions( ExtendWith(MockitoExtension::class), @@ -1110,10 +1112,10 @@ internal class DatadogLogGeneratorTest { // THEN val expectedAttributes = fakeAttributes + mapOf( LogAttributes.RUM_APPLICATION_ID to fakeRumContext.applicationId, - LogAttributes.RUM_SESSION_ID to fakeRumContext.sessionId, + LogAttributes.RUM_SESSION_ID to fakeRumContext.session?.id, LogAttributes.RUM_VIEW_ID to fakeRumContext.viewId, LogAttributes.RUM_ACTION_ID to fakeRumContext.actionId - ) + ).filterValues { it != null } assertThat(log).hasExactlyAttributes(expectedAttributes) } @@ -1143,10 +1145,10 @@ internal class DatadogLogGeneratorTest { // THEN val expectedAttributes = fakeAttributes + mapOf( LogAttributes.RUM_APPLICATION_ID to fakeRumContext.applicationId, - LogAttributes.RUM_SESSION_ID to fakeRumContext.sessionId, + LogAttributes.RUM_SESSION_ID to fakeRumContext.session?.id, LogAttributes.RUM_VIEW_ID to fakeRumContext.viewId, LogAttributes.RUM_ACTION_ID to fakeRumContext.actionId - ) + ).filterValues { it != null } assertThat(log).hasExactlyAttributes(expectedAttributes) } @@ -1170,10 +1172,10 @@ internal class DatadogLogGeneratorTest { // THEN val expectedAttributes = fakeAttributes + mapOf( LogAttributes.RUM_APPLICATION_ID to fakeRumContext.applicationId, - LogAttributes.RUM_SESSION_ID to fakeRumContext.sessionId, + LogAttributes.RUM_SESSION_ID to fakeRumContext.session?.id, LogAttributes.RUM_VIEW_ID to fakeRumContext.viewId, LogAttributes.RUM_ACTION_ID to fakeRumContext.actionId - ) + ).filterValues { it != null } assertThat(log).hasExactlyAttributes(expectedAttributes) } @@ -1197,16 +1199,79 @@ internal class DatadogLogGeneratorTest { assertThat(log.additionalProperties).containsAllEntriesOf( mapOf( LogAttributes.RUM_APPLICATION_ID to fakeRumContext.applicationId, - LogAttributes.RUM_SESSION_ID to fakeRumContext.sessionId, + LogAttributes.RUM_SESSION_ID to fakeRumContext.session?.id, LogAttributes.RUM_VIEW_ID to fakeRumContext.viewId, LogAttributes.RUM_ACTION_ID to fakeRumContext.actionId + ).filterValues { it != null } + ) + } + + @Test + fun `M replace customer correlation W generateLog() {RUM context present}`( + @Forgery fakeSession: RumSessionContext, + forge: Forge + ) { + // Given + val fakeViewId = forge.getForgery().toString() + val fakeActionId = forge.getForgery().toString() + val hasApplicationId = forge.aBool() + val hasSession = hasApplicationId && forge.aBool() + val hasView = hasSession && forge.aBool() + val hasAction = hasView && forge.aBool() + val fakeSdkCorrelation = mapOf( + LogAttributes.RUM_APPLICATION_ID to fakeRumContext.applicationId.takeIf { hasApplicationId }, + LogAttributes.RUM_SESSION_ID to fakeSession.id.takeIf { hasSession }, + LogAttributes.RUM_VIEW_ID to fakeViewId.takeIf { hasView }, + LogAttributes.RUM_ACTION_ID to fakeActionId.takeIf { hasAction } + ) + fakeDatadogContext = fakeDatadogContext.copy( + featuresContext = mapOf( + Feature.RUM_FEATURE_NAME to mapOf( + FeatureContextKeys.RUM_CONTEXT to if (hasApplicationId) { + fakeRumContext.copy( + session = fakeSession.takeIf { hasSession }, + viewId = fakeViewId.takeIf { hasView }, + actionId = fakeActionId.takeIf { hasAction } + ) + } else { + null + } + ) ) ) + val fakeCustomerCorrelation = if (forge.aBool()) { + fakeSdkCorrelation.keys.associateWith { forge.aString() } + } else { + emptyMap() + } + + // When + val log = testedLogGenerator.generateLog( + fakeLevel, + fakeLogMessage, + fakeThrowable, + fakeAttributes + fakeCustomerCorrelation, + fakeTags, + fakeTimestamp, + fakeThreadName, + fakeDatadogContext, + attachNetworkInfo = true, + fakeLoggerName + ) + + // Then + assertThat(log).hasExactlyAttributes(fakeAttributes + fakeSdkCorrelation.filterValues { it != null }) } @Test - fun `M do nothing W required to bundle the rum information {no RUM context}`() { + fun `M do nothing W required to bundle the rum information {no RUM context}`(forge: Forge) { // GIVEN + fakeAttributes = fakeAttributes + mapOf( + LogAttributes.RUM_APPLICATION_ID to forge.aString(), + LogAttributes.RUM_SESSION_ID to forge.aString(), + LogAttributes.RUM_VIEW_ID to forge.aString(), + LogAttributes.RUM_ACTION_ID to forge.aString() + ) fakeDatadogContext = fakeDatadogContext.copy( featuresContext = fakeDatadogContext.featuresContext.toMutableMap().apply { remove(Feature.RUM_FEATURE_NAME) @@ -1236,7 +1301,15 @@ internal class DatadogLogGeneratorTest { } @Test - fun `M do nothing W not required to bundle the rum information`() { + fun `M do nothing W not required to bundle the rum information`(forge: Forge) { + // Given + fakeAttributes = fakeAttributes + mapOf( + LogAttributes.RUM_APPLICATION_ID to forge.aString(), + LogAttributes.RUM_SESSION_ID to forge.aString(), + LogAttributes.RUM_VIEW_ID to forge.aString(), + LogAttributes.RUM_ACTION_ID to forge.aString() + ) + // WHEN val log = testedLogGenerator.generateLog( fakeLevel, diff --git a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt index 0f8d67ce59..601bd9a61a 100644 --- a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt +++ b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt @@ -203,10 +203,10 @@ internal class DatadogLogHandlerTest { .hasExactlyAttributes( fakeAttributes + mapOf( LogAttributes.RUM_APPLICATION_ID to fakeRumContext.applicationId, - LogAttributes.RUM_SESSION_ID to fakeRumContext.sessionId, + LogAttributes.RUM_SESSION_ID to fakeRumContext.session?.id, LogAttributes.RUM_VIEW_ID to fakeRumContext.viewId, LogAttributes.RUM_ACTION_ID to fakeRumContext.actionId - ) + ).filterValues { it != null } ) .hasExactlyTags( fakeTags + setOf( @@ -284,10 +284,10 @@ internal class DatadogLogHandlerTest { .hasExactlyAttributes( fakeAttributes + mapOf( LogAttributes.RUM_APPLICATION_ID to fakeRumContext.applicationId, - LogAttributes.RUM_SESSION_ID to fakeRumContext.sessionId, + LogAttributes.RUM_SESSION_ID to fakeRumContext.session?.id, LogAttributes.RUM_VIEW_ID to fakeRumContext.viewId, LogAttributes.RUM_ACTION_ID to fakeRumContext.actionId - ) + ).filterValues { it != null } ) .hasExactlyTags( fakeTags + setOf( @@ -348,10 +348,10 @@ internal class DatadogLogHandlerTest { .hasExactlyAttributes( fakeAttributes + mapOf( LogAttributes.RUM_APPLICATION_ID to fakeRumContext.applicationId, - LogAttributes.RUM_SESSION_ID to fakeRumContext.sessionId, + LogAttributes.RUM_SESSION_ID to fakeRumContext.session?.id, LogAttributes.RUM_VIEW_ID to fakeRumContext.viewId, LogAttributes.RUM_ACTION_ID to fakeRumContext.actionId - ) + ).filterValues { it != null } ) .hasExactlyTags( fakeTags + setOf( @@ -609,10 +609,10 @@ internal class DatadogLogHandlerTest { .hasExactlyAttributes( fakeAttributes + mapOf( LogAttributes.RUM_APPLICATION_ID to fakeRumContext.applicationId, - LogAttributes.RUM_SESSION_ID to fakeRumContext.sessionId, + LogAttributes.RUM_SESSION_ID to fakeRumContext.session?.id, LogAttributes.RUM_VIEW_ID to fakeRumContext.viewId, LogAttributes.RUM_ACTION_ID to fakeRumContext.actionId - ) + ).filterValues { it != null } ) .hasExactlyTags( fakeTags + setOf( @@ -672,10 +672,10 @@ internal class DatadogLogHandlerTest { .hasExactlyAttributes( fakeAttributes + mapOf( LogAttributes.RUM_APPLICATION_ID to fakeRumContext.applicationId, - LogAttributes.RUM_SESSION_ID to fakeRumContext.sessionId, + LogAttributes.RUM_SESSION_ID to fakeRumContext.session?.id, LogAttributes.RUM_VIEW_ID to fakeRumContext.viewId, LogAttributes.RUM_ACTION_ID to fakeRumContext.actionId - ) + ).filterValues { it != null } ) .hasExactlyTags( fakeTags + setOf( @@ -734,10 +734,10 @@ internal class DatadogLogHandlerTest { .hasExactlyAttributes( fakeAttributes + mapOf( LogAttributes.RUM_APPLICATION_ID to fakeRumContext.applicationId, - LogAttributes.RUM_SESSION_ID to fakeRumContext.sessionId, + LogAttributes.RUM_SESSION_ID to fakeRumContext.session?.id, LogAttributes.RUM_VIEW_ID to fakeRumContext.viewId, LogAttributes.RUM_ACTION_ID to fakeRumContext.actionId - ) + ).filterValues { it != null } ) .hasExactlyTags( fakeTags + setOf( @@ -986,14 +986,14 @@ internal class DatadogLogHandlerTest { argumentCaptor { verify(mockWriter).write(eq(mockEventBatchWriter), capture(), eq(EventType.DEFAULT)) - assertThat(lastValue.additionalProperties) - .containsEntry( - LogAttributes.RUM_APPLICATION_ID, - fakeRumContext.applicationId - ) - .containsEntry(LogAttributes.RUM_SESSION_ID, fakeRumContext.sessionId) - .containsEntry(LogAttributes.RUM_VIEW_ID, fakeRumContext.viewId) - .containsEntry(LogAttributes.RUM_ACTION_ID, fakeRumContext.actionId) + assertThat(lastValue.additionalProperties).containsAllEntriesOf( + mapOf( + LogAttributes.RUM_APPLICATION_ID to fakeRumContext.applicationId, + LogAttributes.RUM_SESSION_ID to fakeRumContext.session?.id, + LogAttributes.RUM_VIEW_ID to fakeRumContext.viewId, + LogAttributes.RUM_ACTION_ID to fakeRumContext.actionId + ).filterValues { it != null } + ) } } @@ -1108,10 +1108,10 @@ internal class DatadogLogHandlerTest { .hasExactlyAttributes( fakeAttributes + mapOf( LogAttributes.RUM_APPLICATION_ID to fakeRumContext.applicationId, - LogAttributes.RUM_SESSION_ID to fakeRumContext.sessionId, + LogAttributes.RUM_SESSION_ID to fakeRumContext.session?.id, LogAttributes.RUM_VIEW_ID to fakeRumContext.viewId, LogAttributes.RUM_ACTION_ID to fakeRumContext.actionId - ) + ).filterValues { it != null } ) .hasExactlyTags( fakeTags + setOf( diff --git a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/utils/forge/Configurator.kt b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/utils/forge/Configurator.kt index 66bb7923da..acef1ae980 100644 --- a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/utils/forge/Configurator.kt +++ b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/utils/forge/Configurator.kt @@ -7,6 +7,7 @@ package com.datadog.android.utils.forge import com.datadog.android.internal.tests.elmyr.RumContextForgeryFactory +import com.datadog.android.internal.tests.elmyr.RumSessionContextForgeryFactory import com.datadog.android.tests.elmyr.useCoreFactories import com.datadog.tools.unit.forge.BaseConfigurator import fr.xgouchet.elmyr.Forge @@ -22,6 +23,7 @@ internal class Configurator : BaseConfigurator() { forge.addFactory(LogEventForgeryFactory()) forge.addFactory(LogsConfigurationForgeryFactory()) forge.addFactory(RumContextForgeryFactory()) + forge.addFactory(RumSessionContextForgeryFactory()) forge.useJvmFactories() } diff --git a/features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/ProfilingFeature.kt b/features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/ProfilingFeature.kt index 9ee58f2732..84d068bec6 100644 --- a/features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/ProfilingFeature.kt +++ b/features/dd-sdk-android-profiling/src/main/java/com/datadog/android/profiling/internal/ProfilingFeature.kt @@ -287,15 +287,16 @@ internal class ProfilingFeature( override fun onContextUpdate(featureName: String, context: Map) { if (featureName != Feature.RUM_FEATURE_NAME) return val rumContext = context.rumContext() - val sessionId = rumContext?.sessionId - if (sessionId == null || - sessionId == RumSessionConstants.EMPTY_RUM_SESSION_ID || - sessionId == lastSeenRumSessionId + val session = rumContext?.session + if (session == null || + session.id == RumSessionConstants.EMPTY_RUM_SESSION_ID || + session.id == lastSeenRumSessionId ) { return } - val sampleRate = rumContext.sessionSampleRate - val isTracked = rumContext.sessionState == RumSessionState.TRACKED + val sessionId = session.id + val sampleRate = session.sampleRate + val isTracked = session.state == RumSessionState.TRACKED // Publish the new session and retire the previous session's quota check and queued trigger // profiles atomically: a trigger profile of the new session can't be queued, nor its quota // check started, until the previous session's state is gone. diff --git a/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/ProfilingFeatureTest.kt b/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/ProfilingFeatureTest.kt index 0d743c578b..fff3168088 100644 --- a/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/ProfilingFeatureTest.kt +++ b/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/ProfilingFeatureTest.kt @@ -25,6 +25,7 @@ import com.datadog.android.internal.profiling.ProfilingAnrDetectedEvent import com.datadog.android.internal.profiling.quota.QuotaReason import com.datadog.android.internal.rum.RumContext import com.datadog.android.internal.rum.RumSessionConstants +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.rum.RumSessionState import com.datadog.android.internal.sampling.SessionSamplingIdProvider import com.datadog.android.internal.system.BuildSdkVersionProvider @@ -105,6 +106,9 @@ import java.util.concurrent.ScheduledExecutorService @ForgeConfiguration(Configurator::class) internal class ProfilingFeatureTest { + @StringForgery + private lateinit var fakeApplicationId: String + private lateinit var testedFeature: ProfilingFeature @Mock @@ -173,6 +177,9 @@ internal class ProfilingFeatureTest { @Forgery private lateinit var fakeSessionContext: RumContext + @Forgery + private lateinit var fakeSession: RumSessionContext + @Forgery private lateinit var fakeConfiguration: ProfilingConfiguration @@ -213,6 +220,7 @@ internal class ProfilingFeatureTest { @BeforeEach fun `set up`() { + fakeSessionContext = fakeSessionContext.copy(session = fakeSession) whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider whenever(mockContext.packageManager) doReturn mockPackageManager @@ -570,9 +578,27 @@ internal class ProfilingFeatureTest { assertThat(testedFeature.lastSeenRumSessionId).isNull() } + @Test + fun `M ignore context update W onContextUpdate() {application-only RUM context}`( + @StringForgery fakeApplicationId: String + ) { + // Given + testedFeature.onInitialize(mockContext) + + // When + testedFeature.onContextUpdate( + Feature.RUM_FEATURE_NAME, + mapOf(FeatureContextKeys.RUM_CONTEXT to RumContext(applicationId = fakeApplicationId)) + ) + + // Then + assertThat(testedFeature.lastSeenRumSessionId).isNull() + } + @Test fun `M ignore context update W onContextUpdate {session id is NULL_UUID sentinel}`( - @Forgery fakeRumContext: RumContext + @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext ) { // Given — RUM has been initialised but no session has been created yet testedFeature.onInitialize(mockContext) @@ -582,7 +608,11 @@ internal class ProfilingFeatureTest { Feature.RUM_FEATURE_NAME, mapOf( FeatureContextKeys.RUM_CONTEXT to - fakeRumContext.copy(sessionId = RumSessionConstants.EMPTY_RUM_SESSION_ID) + fakeRumContext.copy( + session = fakeSession.copy( + id = RumSessionConstants.EMPTY_RUM_SESSION_ID + ) + ) ) ) @@ -591,7 +621,10 @@ internal class ProfilingFeatureTest { } @Test - fun `M sample session out W onContextUpdate {session sample rate is zero}`(@Forgery fakeRumContext: RumContext) { + fun `M sample session out W onContextUpdate {session sample rate is zero}`( + @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext + ) { // Given testedFeature = ProfilingFeature(mockSdkCore, fakeAllSampledConfiguration, mockProfiler) whenever(mockProfiler.isRunning()) doReturn false @@ -602,14 +635,19 @@ internal class ProfilingFeatureTest { Feature.RUM_FEATURE_NAME, mapOf( FeatureContextKeys.RUM_CONTEXT to - fakeRumContext.copy(sessionSampleRate = 0f, sessionState = RumSessionState.TRACKED) + fakeRumContext.copy( + session = fakeSession.copy( + sampleRate = 0f, + state = RumSessionState.TRACKED + ) + ) ) ) // Then val scheduler = checkNotNull(testedFeature.continuousProfilingScheduler) - assertThat(testedFeature.lastSeenRumSessionId).isEqualTo(fakeRumContext.sessionId) - assertThat(scheduler.currentSessionId).isEqualTo(fakeRumContext.sessionId) + assertThat(testedFeature.lastSeenRumSessionId).isEqualTo(fakeSession.id) + assertThat(scheduler.currentSessionId).isEqualTo(fakeSession.id) assertThat(scheduler.currentSessionSampled).isFalse() } @@ -1875,9 +1913,7 @@ internal class ProfilingFeatureTest { } @Test - fun `M not fire quota check W onContextUpdate {RUM session state defaults to not tracked}`( - @StringForgery fakeNewSessionId: String - ) { + fun `M not fire quota check W onContextUpdate {no session}`() { // Given testedFeature = ProfilingFeature(mockSdkCore, fakeAllSampledConfiguration, mockProfiler) whenever(mockProfiler.isRunning()) doReturn true @@ -1887,7 +1923,10 @@ internal class ProfilingFeatureTest { // When testedFeature.onContextUpdate( Feature.RUM_FEATURE_NAME, - mapOf(FeatureContextKeys.RUM_CONTEXT to RumContext(sessionId = fakeNewSessionId, sessionSampleRate = 100f)) + mapOf( + FeatureContextKeys.RUM_CONTEXT to + RumContext(applicationId = fakeApplicationId) + ) ) // Then @@ -2306,9 +2345,11 @@ internal class ProfilingFeatureTest { mapOf( FeatureContextKeys.RUM_CONTEXT to fakeSessionContext.copy( - sessionId = sessionId, - sessionSampleRate = sampleRate, - sessionState = sessionState + session = fakeSession.copy( + id = sessionId, + sampleRate = sampleRate, + state = sessionState + ) ) ) ) diff --git a/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/forge/Configurator.kt b/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/forge/Configurator.kt index 355e3b3059..9cdc9844cf 100644 --- a/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/forge/Configurator.kt +++ b/features/dd-sdk-android-profiling/src/test/kotlin/com/datadog/android/profiling/forge/Configurator.kt @@ -13,6 +13,7 @@ import com.datadog.android.internal.tests.elmyr.ProfilingAnrDetectedEventForgery import com.datadog.android.internal.tests.elmyr.ProfilingRumContextFactory import com.datadog.android.internal.tests.elmyr.ProfilingThreadDumpForgeryFactory import com.datadog.android.internal.tests.elmyr.RumContextForgeryFactory +import com.datadog.android.internal.tests.elmyr.RumSessionContextForgeryFactory import com.datadog.android.profiling.ExperimentalProfilingApi import com.datadog.android.tests.elmyr.useCoreFactories import com.datadog.tools.unit.forge.BaseConfigurator @@ -25,6 +26,7 @@ class Configurator : BaseConfigurator() { super.configure(forge) forge.useCoreFactories() forge.addFactory(RumContextForgeryFactory()) + forge.addFactory(RumSessionContextForgeryFactory()) forge.addFactory(ProfilingConfigurationForgeryFactory()) forge.addFactory(PerfettoResultFactory()) forge.addFactory(ProfilerEventRumVitalEventFactory()) diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/Rum.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/Rum.kt index 671f4b7921..a129c7a05f 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/Rum.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/Rum.kt @@ -197,6 +197,7 @@ object Rum { sessionEndedMetricDispatcher: SessionEndedMetricDispatcher ) = TelemetryEventHandler( sdkCore = sdkCore, + applicationId = rumFeature.applicationId, eventSampler = RateBasedSampler(rumFeature.telemetrySampleRate), sessionEndedMetricDispatcher = sessionEndedMetricDispatcher, configurationExtraSampler = RateBasedSampler( diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporter.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporter.kt index 649c9ba4d5..43b9e57c9f 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporter.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporter.kt @@ -378,7 +378,7 @@ internal class DatadogLateCrashReporter( } private val DatadogContext.rumSessionId: String? - get() = featuresContext[Feature.RUM_FEATURE_NAME]?.rumContext()?.sessionId + get() = featuresContext[Feature.RUM_FEATURE_NAME]?.rumContext()?.session?.id // endregion diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/RumFeatureContext.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/RumFeatureContext.kt index fa5e087e8e..4faec72a8d 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/RumFeatureContext.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/RumFeatureContext.kt @@ -18,10 +18,12 @@ internal fun MutableMap.setRumContext(context: RumContext?) { // Keep the flat fields for cross-platform consumers such as Flutter Session Replay. // TODO RUM-19153: Remove after all supported consumers have migrated to the typed snapshot. this["application_id"] = context.applicationId - this["session_id"] = context.sessionId - this["session_active"] = context.isSessionActive - this["session_state"] = context.sessionState.asString - this["session_start_reason"] = context.sessionStartReason.asString + val session = context.session ?: return + + this["session_id"] = session.id + this["session_active"] = session.isActive + this["session_state"] = session.state.asString + this["session_start_reason"] = session.startReason.asString this["view_id"] = context.viewId this["view_name"] = context.viewName this["view_url"] = context.viewUrl @@ -31,7 +33,7 @@ internal fun MutableMap.setRumContext(context: RumContext?) { this["synthetics_result_id"] = context.syntheticsResultId this["view_timestamp"] = context.viewTimestamp this["view_has_replay"] = context.hasReplay - this["session_has_timeseries"] = context.hasTimeseries + this["session_has_timeseries"] = session.hasTimeseries this["view_timestamp_offset"] = context.viewTimestampOffset - this["session_sample_rate"] = context.sessionSampleRate + this["session_sample_rate"] = session.sampleRate } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScope.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScope.kt index 99b6b331c8..c72aa530bc 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScope.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScope.kt @@ -239,6 +239,12 @@ internal class RumActionScope( val actualType = type val rumContext = getRumContext() + val session = rumContext.session + if (session == null) { + sent = true + return + } + val sessionId = session.id // make a copy so that closure captures at the state as of now // normally not needed, because it should be only single event for this scope, but @@ -309,11 +315,11 @@ internal class RumActionScope( currentLocale = datadogContext.deviceInfo.localeInfo.currentLocale ), session = ActionEvent.ActionEventSession( - id = rumContext.sessionId, + id = sessionId, type = sessionType, hasReplay = hasReplay, isMainProcess = datadogContext.processInfo.isMainProcess, - hasTimeseries = rumContext.hasTimeseries + hasTimeseries = session.hasTimeseries ), synthetics = syntheticsAttribute, source = ActionEvent.ActionEventSource.tryFromSource( @@ -358,7 +364,7 @@ internal class RumActionScope( context = ActionEvent.Context(additionalProperties = getCustomAttributes().toMutableMap()), dd = ActionEvent.Dd( session = ActionEvent.DdSession( - sessionPrecondition = rumContext.sessionStartReason.toActionSessionPrecondition() + sessionPrecondition = session.startReason.toActionSessionPrecondition() ), configuration = ActionEvent.Configuration(sessionSampleRate = sampleRate), action = heatmapResolver?.resolve(rumContext.viewUrl.orEmpty()) diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumResourceScope.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumResourceScope.kt index 39f98c4359..3e24977b4e 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumResourceScope.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumResourceScope.kt @@ -15,7 +15,9 @@ import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver import com.datadog.android.core.internal.utils.DdTagsUtils import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.utils.loggableStackTrace +import com.datadog.android.rum.GlobalRumMonitor import com.datadog.android.rum.RumAttributes import com.datadog.android.rum.RumErrorSource import com.datadog.android.rum.RumResourceKind @@ -27,6 +29,7 @@ import com.datadog.android.rum.internal.domain.event.ResourceTiming import com.datadog.android.rum.internal.instrumentation.insights.InsightsCollector import com.datadog.android.rum.internal.metric.networksettled.InternalResourceContext import com.datadog.android.rum.internal.metric.networksettled.NetworkSettledMetricResolver +import com.datadog.android.rum.internal.monitor.AdvancedRumMonitor import com.datadog.android.rum.internal.monitor.StorageEvent import com.datadog.android.rum.internal.toError import com.datadog.android.rum.internal.toResource @@ -231,7 +234,17 @@ internal class RumResourceScope( val rulePsr = resourceAttributes.remove(RumAttributes.RULE_PSR) as? Number val rumContext = getRumContext() - val hasTimeseries = resolveHasTimeseries(rumContext) + val session = rumContext.session + if (session == null) { + (GlobalRumMonitor.get(sdkCore) as? AdvancedRumMonitor)?.eventDropped( + rumContext.viewId.orEmpty(), + StorageEvent.Resource(resourceId, eventTime.nanoTime) + ) + sent = true + return + } + val sessionId = session.id + val hasTimeseries = resolveHasTimeseries(session) val syntheticsTestId = rumContext.syntheticsTestId val syntheticsResultId = rumContext.syntheticsResultId val syntheticsAttribute = if ( @@ -348,7 +361,7 @@ internal class RumResourceScope( currentLocale = datadogContext.deviceInfo.localeInfo.currentLocale ), session = ResourceEvent.ResourceEventSession( - id = rumContext.sessionId, + id = sessionId, type = sessionType, hasReplay = hasReplay, isMainProcess = datadogContext.processInfo.isMainProcess, @@ -382,7 +395,7 @@ internal class RumResourceScope( spanId = spanId, rulePsr = rulePsr, session = ResourceEvent.DdSession( - sessionPrecondition = rumContext.sessionStartReason.toResourceSessionPrecondition() + sessionPrecondition = session.startReason.toResourceSessionPrecondition() ), configuration = ResourceEvent.Configuration(sessionSampleRate = sampleRate) ), @@ -444,7 +457,17 @@ internal class RumResourceScope( ) { val errorFingerprint = resourceAttributes.remove(RumAttributes.ERROR_FINGERPRINT) as? String val rumContext = getRumContext() - val hasTimeseries = resolveHasTimeseries(rumContext) + val session = rumContext.session + if (session == null) { + (GlobalRumMonitor.get(sdkCore) as? AdvancedRumMonitor)?.eventDropped( + rumContext.viewId.orEmpty(), + StorageEvent.Error(resourceId, resourceStopTimestampInNanos) + ) + sent = true + return + } + val sessionId = session.id + val hasTimeseries = resolveHasTimeseries(session) val syntheticsTestId = rumContext.syntheticsTestId val syntheticsResultId = rumContext.syntheticsResultId @@ -519,7 +542,7 @@ internal class RumResourceScope( connectivity = networkInfo.toErrorConnectivity(), application = ErrorEvent.Application(rumContext.applicationId), session = ErrorEvent.ErrorEventSession( - id = rumContext.sessionId, + id = sessionId, type = sessionType, hasReplay = hasReplay, isMainProcess = datadogContext.processInfo.isMainProcess, @@ -548,7 +571,7 @@ internal class RumResourceScope( context = ErrorEvent.Context(additionalProperties = getCustomAttributes().toMutableMap()), dd = ErrorEvent.Dd( session = ErrorEvent.DdSession( - sessionPrecondition = rumContext.sessionStartReason.toErrorSessionPrecondition() + sessionPrecondition = session.startReason.toErrorSessionPrecondition() ), configuration = ErrorEvent.Configuration(sessionSampleRate = sampleRate) ), @@ -574,10 +597,10 @@ internal class RumResourceScope( // rumContext is frozen at resource start, while timeseries may be collected for its session afterwards. // The parent context is only trusted while it still belongs to the same session. - private fun resolveHasTimeseries(rumContext: RumContext): Boolean { - if (rumContext.hasTimeseries) return true - val parentContext = parentScope.getRumContext() - return parentContext.sessionId == rumContext.sessionId && parentContext.hasTimeseries + private fun resolveHasTimeseries(session: RumSessionContext): Boolean { + if (session.hasTimeseries) return true + val parentSession = parentScope.getRumContext().session + return parentSession != null && parentSession.id == session.id && parentSession.hasTimeseries } private fun resolveErrorProvider(): ErrorEvent.Provider? { diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScope.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScope.kt index ad8accb103..7a333f4ac0 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScope.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScope.kt @@ -17,6 +17,7 @@ import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver import com.datadog.android.core.sampling.Sampler import com.datadog.android.internal.heatmaps.HeatmapIdentifierRegistry import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.rum.RumSessionStartReason import com.datadog.android.internal.rum.RumSessionState import com.datadog.android.rum.RumSessionListener @@ -73,10 +74,12 @@ internal class RumSessionScope( private val timeseriesCollector: TimeseriesCollector = NoOpTimeseriesCollector() ) : RumScope { - internal var sessionId = RumContext.NULL_UUID - internal var sessionState: RumSessionState = RumSessionState.NOT_TRACKED + internal var session: RumSessionContext? = null + internal val sessionId: String? + get() = session?.id + internal val sessionState: RumSessionState? + get() = session?.state internal val sessionSampleRate: Float = sessionSampler.getSampleRate() ?: RumContext.SAMPLE_ALL_RATE - private var startReason: RumSessionStartReason = RumSessionStartReason.USER_APP_LAUNCH internal var isActive: Boolean = true private val sessionStartNs = AtomicLong(sdkCore.timeProvider.getDeviceElapsedRealtimeNanos()) @@ -190,12 +193,9 @@ internal class RumSessionScope( override fun getRumContext(): RumContext { val parentContext = parentScope.getRumContext() return parentContext.copy( - sessionId = sessionId, - sessionState = sessionState, - sessionStartReason = startReason, - isSessionActive = isActive, - sessionSampleRate = sessionSampleRate, - hasTimeseries = timeseriesCollector.isTimeseriesCollected(sessionId) + session = session?.let { + it.copy(isActive = isActive, hasTimeseries = timeseriesCollector.isTimeseriesCollected(it.id)) + } ) } @@ -218,6 +218,7 @@ internal class RumSessionScope( } private fun startTimeseries() { + val sessionId = sessionId ?: return timeseriesCollector.onSessionStart( sessionId = sessionId, sessionType = getActiveRumContext().resolveSessionType(rumSessionTypeOverride) @@ -225,13 +226,13 @@ internal class RumSessionScope( } internal fun stopTimeseries() { - timeseriesCollector.onSessionStop(sessionId) + sessionId?.let { timeseriesCollector.onSessionStop(it) } } private fun stopSession() { isActive = false stopTimeseries() - sessionEndedMetricDispatcher.onSessionStopped(sessionId) + sessionId?.let { sessionEndedMetricDispatcher.onSessionStopped(it) } } private fun isSessionComplete(): Boolean { @@ -241,7 +242,7 @@ internal class RumSessionScope( @Suppress("CyclomaticComplexMethod") private fun updateSession(event: RumRawEvent) { val nanoTime = sdkCore.timeProvider.getDeviceElapsedRealtimeNanos() - val isNewSession = sessionId == RumContext.NULL_UUID + val isNewSession = sessionId == null val timeSinceLastInteractionNs = nanoTime - lastUserInteractionNs.get() val isExpired = timeSinceLastInteractionNs >= sessionInactivityNanos @@ -258,7 +259,7 @@ internal class RumSessionScope( // When the session is expired, time-out or stopSession API is called, session ended metric should be sent if (isExpired || isTimedOut || isActive.not()) { - sessionEndedMetricDispatcher.endMetric(sessionId, sdkCore.time.serverTimeOffsetMs) + sessionId?.let { sessionEndedMetricDispatcher.endMetric(it, sdkCore.time.serverTimeOffsetMs) } } if (isInteraction || isSdkInitInForeground || isAppStart) { @@ -279,35 +280,40 @@ internal class RumSessionScope( lastUserInteractionNs.set(nanoTime) } else { stopTimeseries() - sessionState = RumSessionState.EXPIRED + session = session?.copy(state = RumSessionState.EXPIRED) } } else if (isTimedOut) { renewSession(event.eventTime, RumSessionStartReason.MAX_DURATION) } - updateSessionStateForSessionReplay(sessionId) + sessionId?.let { updateSessionStateForSessionReplay(it) } } private fun renewSession(time: Time, reason: RumSessionStartReason) { stopTimeseries() val newSessionId = UUID.randomUUID().toString() val keepSession = sessionSampler.sample(newSessionId) - startReason = reason - sessionState = if (keepSession) RumSessionState.TRACKED else RumSessionState.NOT_TRACKED - sessionId = newSessionId + session = RumSessionContext( + id = newSessionId, + state = if (keepSession) RumSessionState.TRACKED else RumSessionState.NOT_TRACKED, + startReason = reason, + isActive = isActive, + sampleRate = sessionSampleRate, + hasTimeseries = false + ) sessionStartNs.set(sdkCore.timeProvider.getDeviceElapsedRealtimeNanos()) rumSessionScopeStartupManager = rumSessionScopeStartupManagerFactory() childScope?.renewViewScopes(time) if (keepSession) { sessionEndedMetricDispatcher.startMetric( - sessionId = sessionId, + sessionId = newSessionId, startReason = reason, ntpOffsetAtStartMs = sdkCore.time.serverTimeOffsetMs, backgroundEventTracking = backgroundTrackingEnabled ) startTimeseries() } - sessionListener?.onSessionStarted(sessionId, !keepSession) + sessionListener?.onSessionStarted(newSessionId, !keepSession) } private fun updateSessionStateForSessionReplay(sessionId: String) { diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewManagerScope.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewManagerScope.kt index 9ac1878c83..11d7b4c582 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewManagerScope.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewManagerScope.kt @@ -269,9 +269,11 @@ internal class RumViewManagerScope( } } + val sessionId = parentScope.getRumContext().session?.id ?: return + // Track the orphan event both in foreground and background. SessionEndedMetric.MissedEventType.fromRawEvent(rawEvent = event)?.let { - sessionEndedMetricDispatcher.onMissedEventTracked(sessionId = parentScope.getRumContext().sessionId, it) + sessionEndedMetricDispatcher.onMissedEventTracked(sessionId = sessionId, it) } ?: sdkCore.internalLogger.log( InternalLogger.Level.INFO, InternalLogger.Target.MAINTAINER, diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScope.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScope.kt index ab4b14b57c..319f928a3b 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScope.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScope.kt @@ -23,6 +23,7 @@ import com.datadog.android.internal.heatmaps.HeatmapIdentifierRegistry import com.datadog.android.internal.profiling.ProfilerEvent import com.datadog.android.internal.profiling.ProfilingRumContext import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.rum.RumSessionState import com.datadog.android.internal.rum.RumViewType import com.datadog.android.internal.telemetry.InternalTelemetryEvent @@ -113,13 +114,14 @@ internal open class RumViewScope( private var memoizedParentAttributes: Map = emptyMap() internal val viewId: String = UUID.randomUUID().toString() - private val sessionId: String = parentScope.getRumContext().let { - if (it.syntheticsTestId != null) { + private val sessionId: String? = parentScope.getRumContext().let { + val parentSessionId = it.session?.id + if (it.syntheticsTestId != null && parentSessionId != null) { logSynthetics("_dd.application.id", it.applicationId) - logSynthetics("_dd.session.id", it.sessionId) + logSynthetics("_dd.session.id", parentSessionId) logSynthetics("_dd.view.id", viewId) } - it.sessionId + parentSessionId } private val startedNanos: Long = eventTime.nanoTime @@ -280,6 +282,7 @@ internal open class RumViewScope( val vitalId = UUID.randomUUID().toString() val capturedRumContext = getRumContext() + val session = capturedRumContext.session ?: return sdkCore.newRumEventWriteOperation(writeScope, writer) { newVitalEvent( vitalId, @@ -290,7 +293,8 @@ internal open class RumViewScope( stepType = VitalOperationStepEvent.StepType.START, failureReason = null, eventAttributes = event.attributes, - rumContext = capturedRumContext + rumContext = capturedRumContext, + session = session ) }.onSuccess { sendProfilingRumOperationEvent( @@ -313,6 +317,7 @@ internal open class RumViewScope( val vitalId = UUID.randomUUID().toString() val capturedRumContext = getRumContext() + val session = capturedRumContext.session ?: return sdkCore.newRumEventWriteOperation(writeScope, writer) { newVitalEvent( vitalId, @@ -323,7 +328,8 @@ internal open class RumViewScope( stepType = VitalOperationStepEvent.StepType.END, failureReason = event.failureReason?.toSchemaFailureReason(), eventAttributes = event.attributes, - rumContext = capturedRumContext + rumContext = capturedRumContext, + session = session ) }.onSuccess { sendProfilingRumOperationEvent( @@ -342,6 +348,8 @@ internal open class RumViewScope( eventName: String, eventTimestamp: Long ) { + val session = rumContext.session ?: return + val sessionId = session.id sdkCore.getFeature(Feature.PROFILING_FEATURE_NAME)?.sendEvent( ProfilerEvent.RumVitalEvent( id = vitalId, @@ -351,7 +359,7 @@ internal open class RumViewScope( durationNs = 0L, rumContext = ProfilingRumContext( applicationId = rumContext.applicationId, - sessionId = rumContext.sessionId, + sessionId = sessionId, viewId = rumContext.viewId, viewName = rumContext.viewName ) @@ -369,7 +377,8 @@ internal open class RumViewScope( stepType: VitalOperationStepEvent.StepType, failureReason: VitalOperationStepEvent.FailureReason?, eventAttributes: Map, - rumContext: RumContext + rumContext: RumContext, + session: RumSessionContext ): VitalOperationStepEvent { val syntheticsTestId = rumContext.syntheticsTestId val syntheticsResultId = rumContext.syntheticsResultId @@ -407,7 +416,7 @@ internal open class RumViewScope( ), dd = VitalOperationStepEvent.Dd( session = VitalOperationStepEvent.DdSession( - sessionPrecondition = rumContext.sessionStartReason.toVitalOperationStepSessionPrecondition() + sessionPrecondition = session.startReason.toVitalOperationStepSessionPrecondition() ), configuration = VitalOperationStepEvent.Configuration(sessionSampleRate = sampleRate), profiling = resolveVitalOperationStepProfilingStatus(datadogContext) @@ -418,11 +427,11 @@ internal open class RumViewScope( ), synthetics = syntheticsAttribute, session = VitalOperationStepEvent.VitalOperationStepEventSession( - id = rumContext.sessionId, + id = session.id, type = sessionType, hasReplay = hasReplay, isMainProcess = datadogContext.processInfo.isMainProcess, - hasTimeseries = rumContext.hasTimeseries + hasTimeseries = session.hasTimeseries ), view = VitalOperationStepEvent.VitalOperationStepEventView( id = rumContext.viewId.orEmpty(), @@ -749,9 +758,10 @@ internal open class RumViewScope( writer: DataWriter ) { delegateEventToChildren(event, datadogContext, writeScope, writer) - if (stopped) return - val rumContext = getRumContext() + val session = rumContext.session + if (stopped || session == null) return + val sessionId = session.id val errorCustomAttributes = getCustomAttributes().toMutableMap() errorCustomAttributes.putAll(event.attributes) @@ -876,11 +886,11 @@ internal open class RumViewScope( currentLocale = datadogContext.deviceInfo.localeInfo.currentLocale ), session = ErrorEvent.ErrorEventSession( - id = rumContext.sessionId, + id = sessionId, type = sessionType, hasReplay = hasReplay, isMainProcess = datadogContext.processInfo.isMainProcess, - hasTimeseries = rumContext.hasTimeseries + hasTimeseries = session.hasTimeseries ), synthetics = syntheticsAttribute, source = ErrorEvent.ErrorEventSource.tryFromSource( @@ -910,7 +920,7 @@ internal open class RumViewScope( context = ErrorEvent.Context(additionalProperties = errorCustomAttributes), dd = ErrorEvent.Dd( session = ErrorEvent.DdSession( - sessionPrecondition = rumContext.sessionStartReason.toErrorSessionPrecondition() + sessionPrecondition = session.startReason.toErrorSessionPrecondition() ), configuration = ErrorEvent.Configuration(sessionSampleRate = sampleRate), profiling = profilingStatus @@ -938,7 +948,7 @@ internal open class RumViewScope( durationNs = anrDurationNs, rumContext = ProfilingRumContext( applicationId = rumContext.applicationId, - sessionId = rumContext.sessionId, + sessionId = sessionId, viewId = rumContext.viewId, viewName = rumContext.viewName ) @@ -1288,6 +1298,8 @@ internal open class RumViewScope( } val durationNs = stoppedNanos - startedNanos val rumContext = getRumContext() + val session = rumContext.session ?: return + val sessionId = session.id val timings = resolveCustomTimings() val memoryInfo = lastMemoryInfo @@ -1313,7 +1325,7 @@ internal open class RumViewScope( val slowFramesRate = if (viewComplete) uiSlownessReport?.slowFramesRate(stoppedNanos) else null insightsCollector.onSlowFrameRate(uiSlownessReport?.slowFramesRate(stoppedNanos)) - if (viewComplete && rumContext.sessionState != RumSessionState.NOT_TRACKED) { + if (viewComplete && session.state != RumSessionState.NOT_TRACKED) { viewEndedMetricDispatcher.sendViewEnded( interactionToNextViewMetricResolver.getState(viewId), networkSettledMetricResolver.getState() @@ -1438,12 +1450,12 @@ internal open class RumViewScope( currentLocale = datadogContext.deviceInfo.localeInfo.currentLocale ), session = ViewEvent.ViewEventSession( - id = rumContext.sessionId, + id = session.id, type = sessionType, hasReplay = hasReplay, - isActive = rumContext.isSessionActive, + isActive = session.isActive, isMainProcess = datadogContext.processInfo.isMainProcess, - hasTimeseries = rumContext.hasTimeseries + hasTimeseries = session.hasTimeseries ), synthetics = syntheticsAttribute, source = ViewEvent.ViewEventSource.tryFromSource( @@ -1474,7 +1486,7 @@ internal open class RumViewScope( dd = ViewEvent.Dd( documentVersion = eventVersion, session = ViewEvent.DdSession( - sessionPrecondition = rumContext.sessionStartReason.toViewSessionPrecondition() + sessionPrecondition = session.startReason.toViewSessionPrecondition() ), replayStats = replayStats, configuration = ViewEvent.Configuration( @@ -1572,6 +1584,8 @@ internal open class RumViewScope( if (stopped) return insightsCollector.onLongTask(event.durationNs) val rumContext = getRumContext() + val session = rumContext.session ?: return + val sessionId = session.id val longTaskCustomAttributes = getCustomAttributes().toMutableMap().apply { put(RumAttributes.LONG_TASK_TARGET, event.target) } @@ -1641,11 +1655,11 @@ internal open class RumViewScope( connectivity = datadogContext.networkInfo.toLongTaskConnectivity(), application = LongTaskEvent.Application(rumContext.applicationId), session = LongTaskEvent.LongTaskEventSession( - id = rumContext.sessionId, + id = sessionId, type = sessionType, hasReplay = hasReplay, isMainProcess = datadogContext.processInfo.isMainProcess, - hasTimeseries = rumContext.hasTimeseries + hasTimeseries = session.hasTimeseries ), synthetics = syntheticsAttribute, source = LongTaskEvent.LongTaskEventSource.tryFromSource( @@ -1670,7 +1684,7 @@ internal open class RumViewScope( context = LongTaskEvent.Context(additionalProperties = longTaskCustomAttributes), dd = LongTaskEvent.Dd( session = LongTaskEvent.DdSession( - sessionPrecondition = rumContext.sessionStartReason.toLongTaskSessionPrecondition() + sessionPrecondition = session.startReason.toLongTaskSessionPrecondition() ), configuration = LongTaskEvent.Configuration(sessionSampleRate = sampleRate), profiling = resolveLongTaskProfilingStatus(datadogContext) @@ -1695,7 +1709,7 @@ internal open class RumViewScope( durationNs = event.durationNs, rumContext = ProfilingRumContext( applicationId = rumContext.applicationId, - sessionId = rumContext.sessionId, + sessionId = sessionId, viewId = rumContext.viewId, viewName = rumContext.viewName ) diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumVitalAppLaunchEventHelper.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumVitalAppLaunchEventHelper.kt index 1950894fbf..bd87af28c4 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumVitalAppLaunchEventHelper.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumVitalAppLaunchEventHelper.kt @@ -41,7 +41,9 @@ internal class RumVitalAppLaunchEventHelper( appLaunchMetric: VitalAppLaunchEvent.AppLaunchMetric, profilingStatus: VitalAppLaunchEvent.ProfilingStatus?, profilingQuotaReason: QuotaReason? = null - ): VitalAppLaunchEvent { + ): VitalAppLaunchEvent? { + val session = rumContext.session ?: return null + val sessionId = session.id val syntheticsTestId = rumContext.syntheticsTestId val syntheticsResultId = rumContext.syntheticsResultId val syntheticsAttribute = if ( @@ -82,7 +84,7 @@ internal class RumVitalAppLaunchEventHelper( ), dd = VitalAppLaunchEvent.Dd( session = VitalAppLaunchEvent.DdSession( - sessionPrecondition = rumContext.sessionStartReason.toVitalAppLaunchSessionPrecondition() + sessionPrecondition = session.startReason.toVitalAppLaunchSessionPrecondition() ), configuration = VitalAppLaunchEvent.Configuration(sessionSampleRate = sampleRate), profiling = VitalAppLaunchEvent.Profiling( @@ -96,11 +98,11 @@ internal class RumVitalAppLaunchEventHelper( ), synthetics = syntheticsAttribute, session = VitalAppLaunchEvent.VitalAppLaunchEventSession( - id = rumContext.sessionId, + id = sessionId, type = sessionType, hasReplay = hasReplay, isMainProcess = datadogContext.processInfo.isMainProcess, - hasTimeseries = rumContext.hasTimeseries + hasTimeseries = session.hasTimeseries ), view = view, source = VitalAppLaunchEvent.VitalAppLaunchEventSource.tryFromSource( diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitor.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitor.kt index 85c1889084..00d7ba3167 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitor.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitor.kt @@ -89,7 +89,7 @@ import com.datadog.android.rum.featureoperations.FailureReason as DeprecatedFail @Suppress("LongParameterList", "LargeClass", "TooManyFunctions") internal class DatadogRumMonitor( - applicationId: String, + private val applicationId: String, private val sdkCore: InternalSdkCore, internal val appPackageName: String, internal val sessionSampler: Sampler, @@ -122,6 +122,8 @@ internal class DatadogRumMonitor( @Volatile private var cachedViewUrl: String? = null + private val applicationContext = RumContext(applicationId = applicationId) + internal var rootScope = RumApplicationScope( applicationId = applicationId, sdkCore = sdkCore, @@ -173,16 +175,9 @@ internal class DatadogRumMonitor( ) { val activeSessionId = rootScope.activeSession ?.getRumContext() - ?.let { - val sessionId = it.sessionId - if (it.sessionState == RumSessionState.NOT_TRACKED || - sessionId == RumContext.NULL_UUID - ) { - null - } else { - sessionId - } - } + ?.session + ?.takeIf { it.state != RumSessionState.NOT_TRACKED } + ?.id callback(activeSessionId) } } @@ -974,9 +969,7 @@ internal class DatadogRumMonitor( @Suppress("ThreadSafety") // Crash handling, can't delegate to another thread rootScope.handleEvent(event, datadogContext, eventWriteScope, writer) val rumContext = currentRumContext() - sdkCore.updateFeatureContext(Feature.RUM_FEATURE_NAME, useContextThread = false) { - it.setRumContext(rumContext) - } + publishRumContext(rumContext) } } if (handled != true) { @@ -1011,21 +1004,25 @@ internal class DatadogRumMonitor( notifyDebugListenerWithState() val context = currentRumContext() updateCachedViewUrl(context) - sessionExpirationChecker.onEventHandled(context?.sessionState) + sessionExpirationChecker.onEventHandled(context.session?.state) context } } - ) - val rumContext = future?.getSafe("Rum get context", sdkCore.internalLogger) - // we are on the context thread already, so useContextThread=false - sdkCore.updateFeatureContext(Feature.RUM_FEATURE_NAME, useContextThread = false) { - it.setRumContext(rumContext) - } + ) ?: return@withWriteContext + val rumContext = future.getSafe("Rum get context", sdkCore.internalLogger) + ?: applicationContext + publishRumContext(rumContext) } } } } + private fun publishRumContext(rumContext: RumContext) { + sdkCore.updateFeatureContext(Feature.RUM_FEATURE_NAME, useContextThread = false) { + it.setRumContext(rumContext) + } + } + @WorkerThread private fun handleEventWithMethodCallPerf( event: RumRawEvent, @@ -1067,16 +1064,15 @@ internal class DatadogRumMonitor( override fun getCurrentViewUrl(): String? = cachedViewUrl - private fun updateCachedViewUrl(context: RumContext?) { - val newUrl = context?.viewUrl + private fun updateCachedViewUrl(context: RumContext) { + val newUrl = context.viewUrl if (cachedViewUrl != newUrl) cachedViewUrl = newUrl } - private fun currentRumContext(): RumContext? { - val activeSession = rootScope.activeSession ?: return null - val context = activeSession.activeView?.getRumContext() - ?: activeSession.getRumContext() - return if (context.sessionId == RumContext.NULL_UUID) null else context + private fun currentRumContext(): RumContext { + val session = rootScope.activeSession ?: return applicationContext + return (session.activeView?.getRumContext() ?: session.getRumContext()) + .takeIf { it.session != null } ?: applicationContext } internal fun notifyDebugListenerWithState() { diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumSessionScopeStartupManager.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumSessionScopeStartupManager.kt index 8456e76be5..0e3540be74 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumSessionScopeStartupManager.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumSessionScopeStartupManager.kt @@ -86,7 +86,7 @@ internal class RumSessionScopeStartupManagerImpl( appStartCount++ } - @Suppress("LongMethod") + @Suppress("LongMethod", "ReturnCount") override fun onTTIDEvent( event: RumRawEvent.AppStartTTIDEvent, isSessionTracked: Boolean, @@ -96,12 +96,14 @@ internal class RumSessionScopeStartupManagerImpl( rumContext: RumContext, customAttributes: Map ) { - if (!isSessionTracked) { + val session = rumContext.session + if (!isSessionTracked || session == null) { sdkCore.getFeature(Feature.PROFILING_FEATURE_NAME)?.sendEvent( ProfilerEvent.TTIDNotTracked ) return } + val sessionId = session.id ttidReportedForScenario = true rumAppStartupTelemetryReporter.reportTTID( @@ -115,7 +117,7 @@ internal class RumSessionScopeStartupManagerImpl( ttidSentForSession = true - val profilingStatus = datadogContext.getProfilingStatus(rumContext.sessionId) + val profilingStatus = datadogContext.getProfilingStatus(sessionId) val ttidEvent = rumVitalAppLaunchEventHelper.newVitalAppLaunchEvent( timestampMs = event.info.scenario.initialTime.timestamp + sdkCore.time.serverTimeOffsetMs, datadogContext = datadogContext, @@ -130,11 +132,11 @@ internal class RumSessionScopeStartupManagerImpl( // The quota reason is only meaningful when profiling is STOPPED; a running profiler // must not carry a denial reason (mirrors the Error/LongTask path in RumViewScope). profilingQuotaReason = if (profilingStatus == VitalAppLaunchEvent.ProfilingStatus.STOPPED) { - datadogContext.resolveProfilingQuotaReason(rumContext.sessionId) + datadogContext.resolveProfilingQuotaReason(sessionId) } else { null } - ) + ) ?: return sdkCore.getFeature(Feature.PROFILING_FEATURE_NAME)?.sendEvent( // startMs is different for different scenarios. For Cold, it will be process start, for Warm @@ -142,7 +144,7 @@ internal class RumSessionScopeStartupManagerImpl( ProfilerEvent.RumVitalEvent( rumContext = ProfilingRumContext( applicationId = rumContext.applicationId, - sessionId = rumContext.sessionId, + sessionId = sessionId, viewId = rumContext.viewId, viewName = rumContext.viewName ), @@ -236,6 +238,7 @@ internal class RumSessionScopeStartupManagerImpl( ) } + @Suppress("ReturnCount") private fun sendTTFDEvent( datadogContext: DatadogContext, writeScope: EventWriteScope, @@ -245,6 +248,8 @@ internal class RumSessionScopeStartupManagerImpl( durationNs: Long, scenario: RumStartupScenario ) { + val session = rumContext.session ?: return + val sessionId = session.id if (durationNs > MAX_TTFD_DURATION_NS) { sdkCore.internalLogger.log( level = InternalLogger.Level.WARN, @@ -259,7 +264,7 @@ internal class RumSessionScopeStartupManagerImpl( return } - val profilingStatus = datadogContext.getProfilingStatus(rumContext.sessionId) + val profilingStatus = datadogContext.getProfilingStatus(sessionId) val ttfdEvent = rumVitalAppLaunchEventHelper.newVitalAppLaunchEvent( timestampMs = scenario.initialTime.timestamp + sdkCore.time.serverTimeOffsetMs, datadogContext = datadogContext, @@ -274,17 +279,17 @@ internal class RumSessionScopeStartupManagerImpl( // The quota reason is only meaningful when profiling is STOPPED; a running profiler // must not carry a denial reason (mirrors the TTID and Error/LongTask paths). profilingQuotaReason = if (profilingStatus == VitalAppLaunchEvent.ProfilingStatus.STOPPED) { - datadogContext.resolveProfilingQuotaReason(rumContext.sessionId) + datadogContext.resolveProfilingQuotaReason(sessionId) } else { null } - ) + ) ?: return sdkCore.getFeature(Feature.PROFILING_FEATURE_NAME)?.sendEvent( ProfilerEvent.RumVitalEvent( rumContext = ProfilingRumContext( applicationId = rumContext.applicationId, - sessionId = rumContext.sessionId, + sessionId = sessionId, viewId = rumContext.viewId, viewName = rumContext.viewName ), diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/timeseries/Pipeline.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/timeseries/Pipeline.kt index adddd2dccd..1ffcd133c8 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/timeseries/Pipeline.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/timeseries/Pipeline.kt @@ -35,13 +35,15 @@ internal class Pipeline( @WorkerThread fun execute(rumContext: RumContext) { + val session = rumContext.session ?: return + val sessionId = session.id // reader.read() may hit the filesystem (/proc); kept outside the lock so a concurrent // flush() waits only for the buffer and never for I/O. val dataPoint = reader.read() synchronized(this) { if (dataPoint != null) { buffer.add(dataPoint) - collectedSessionsRegistry.setTimeseriesCollected(rumContext.sessionId) + collectedSessionsRegistry.setTimeseriesCollected(sessionId) } if (buffer.isFull()) drainAndWrite(rumContext) } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/timeseries/collector/DefaultTimeseriesCollector.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/timeseries/collector/DefaultTimeseriesCollector.kt index 08b7aba8d4..acd1b204e8 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/timeseries/collector/DefaultTimeseriesCollector.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/timeseries/collector/DefaultTimeseriesCollector.kt @@ -110,7 +110,8 @@ internal class DefaultTimeseriesCollector( @WorkerThread override fun onRumContextUpdate(newRumContext: RumContext) { - if (newRumContext.sessionId != activeSessionId) return + val sessionId = newRumContext.session?.id ?: return + if (sessionId != activeSessionId) return stateGate.setRumContext(newRumContext, onUpdated = ::tryStartCollection) } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/timeseries/factory/CpuEventFactory.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/timeseries/factory/CpuEventFactory.kt index cd3264e5c1..0d9c417d9a 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/timeseries/factory/CpuEventFactory.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/timeseries/factory/CpuEventFactory.kt @@ -45,7 +45,9 @@ internal class CpuEventFactory( rumContext: RumContext, dataPoints: List> ): TimeseriesCpuEvent? { - if (dataPoints.isEmpty()) return null + val session = rumContext.session + if (session == null || dataPoints.isEmpty()) return null + val sessionId = session.id val deviceInfo = datadogContext.deviceInfo val syntheticsTestId = rumContext.syntheticsTestId @@ -65,10 +67,10 @@ internal class CpuEventFactory( return TimeseriesCpuEvent( dd = TimeseriesCpuEvent.Dd( session = TimeseriesCpuEvent.DdSession( - sessionPrecondition = rumContext.sessionStartReason.toTimeseriesCpuSessionPrecondition() + sessionPrecondition = session.startReason.toTimeseriesCpuSessionPrecondition() ), configuration = TimeseriesCpuEvent.Configuration( - sessionSampleRate = rumContext.sessionSampleRate, + sessionSampleRate = session.sampleRate, sessionReplaySampleRate = datadogContext.resolveSessionReplaySampleRate(), traceSampleRate = datadogContext.resolveTraceSampleRate() ) @@ -78,7 +80,7 @@ internal class CpuEventFactory( currentLocale = deviceInfo.localeInfo.currentLocale ), session = TimeseriesCpuEvent.TimeseriesCpuEventSession( - id = rumContext.sessionId, + id = sessionId, type = sessionType.toTimeseriesCpuSessionType(), hasReplay = featuresContextResolver.resolveViewHasReplay( datadogContext, diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/timeseries/factory/MemoryEventFactory.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/timeseries/factory/MemoryEventFactory.kt index 5f297069ca..ba67bb712b 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/timeseries/factory/MemoryEventFactory.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/timeseries/factory/MemoryEventFactory.kt @@ -46,7 +46,9 @@ internal class MemoryEventFactory( rumContext: RumContext, dataPoints: List> ): TimeseriesMemoryEvent? { - if (totalRamBytes <= 0L || dataPoints.isEmpty()) return null + val session = rumContext.session + if (session == null || totalRamBytes <= 0L || dataPoints.isEmpty()) return null + val sessionId = session.id val deviceInfo = datadogContext.deviceInfo val syntheticsTestId = rumContext.syntheticsTestId @@ -66,10 +68,10 @@ internal class MemoryEventFactory( return TimeseriesMemoryEvent( dd = TimeseriesMemoryEvent.Dd( session = TimeseriesMemoryEvent.DdSession( - sessionPrecondition = rumContext.sessionStartReason.toTimeseriesMemorySessionPrecondition() + sessionPrecondition = session.startReason.toTimeseriesMemorySessionPrecondition() ), configuration = TimeseriesMemoryEvent.Configuration( - sessionSampleRate = rumContext.sessionSampleRate, + sessionSampleRate = session.sampleRate, sessionReplaySampleRate = datadogContext.resolveSessionReplaySampleRate(), traceSampleRate = datadogContext.resolveTraceSampleRate() ) @@ -79,7 +81,7 @@ internal class MemoryEventFactory( currentLocale = deviceInfo.localeInfo.currentLocale ), session = TimeseriesMemoryEvent.TimeseriesMemoryEventSession( - id = rumContext.sessionId, + id = sessionId, type = sessionType.toTimeseriesMemorySessionType(), hasReplay = featuresContextResolver.resolveViewHasReplay( datadogContext, diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/telemetry/internal/TelemetryEventHandler.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/telemetry/internal/TelemetryEventHandler.kt index 78bcd88ba5..55ebc401d5 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/telemetry/internal/TelemetryEventHandler.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/telemetry/internal/TelemetryEventHandler.kt @@ -47,6 +47,7 @@ import com.datadog.android.telemetry.model.TelemetryConfigurationEvent.ViewTrack @Suppress("TooManyFunctions") internal class TelemetryEventHandler( internal val sdkCore: InternalSdkCore, + private val applicationId: String, internal val eventSampler: Sampler, internal val configurationExtraSampler: Sampler = RateBasedSampler(DEFAULT_CONFIGURATION_SAMPLE_RATE), @@ -107,10 +108,12 @@ internal class TelemetryEventHandler( } is InternalTelemetryEvent.Log.Error -> { - sessionEndedMetricDispatcher.onSdkErrorTracked( - sessionId = datadogContext.rumContext().sessionId, - errorKind = event.kind - ) + datadogContext.rumContext()?.session?.let { session -> + sessionEndedMetricDispatcher.onSdkErrorTracked( + sessionId = session.id, + errorKind = event.kind + ) + } createErrorEvent( datadogContext = datadogContext, timestamp = timestamp, @@ -230,10 +233,10 @@ internal class TelemetryEventHandler( service = TELEMETRY_SERVICE_NAME, version = datadogContext.sdkVersion, effectiveSampleRate = effectiveSampleRate, - application = TelemetryDebugEvent.Application(rumContext.applicationId), - session = TelemetryDebugEvent.Session(rumContext.sessionId), - view = rumContext.viewId?.let { TelemetryDebugEvent.View(it) }, - action = rumContext.actionId?.let { TelemetryDebugEvent.Action(it) }, + application = TelemetryDebugEvent.Application(applicationId), + session = rumContext?.session?.let { TelemetryDebugEvent.Session(it.id) }, + view = rumContext?.viewId?.let { TelemetryDebugEvent.View(it) }, + action = rumContext?.actionId?.let { TelemetryDebugEvent.Action(it) }, telemetry = TelemetryDebugEvent.Telemetry( message = message, additionalProperties = resolvedAdditionalProperties, @@ -279,10 +282,10 @@ internal class TelemetryEventHandler( ) ?: TelemetryErrorEvent.Source.ANDROID, service = TELEMETRY_SERVICE_NAME, version = datadogContext.sdkVersion, - application = TelemetryErrorEvent.Application(rumContext.applicationId), - session = TelemetryErrorEvent.Session(rumContext.sessionId), - view = rumContext.viewId?.let { TelemetryErrorEvent.View(it) }, - action = rumContext.actionId?.let { TelemetryErrorEvent.Action(it) }, + application = TelemetryErrorEvent.Application(applicationId), + session = rumContext?.session?.let { TelemetryErrorEvent.Session(it.id) }, + view = rumContext?.viewId?.let { TelemetryErrorEvent.View(it) }, + action = rumContext?.actionId?.let { TelemetryErrorEvent.Action(it) }, effectiveSampleRate = effectiveSampleRate, telemetry = TelemetryErrorEvent.Telemetry( message = message, @@ -371,10 +374,10 @@ internal class TelemetryEventHandler( sdkCore.internalLogger ) ?: TelemetryConfigurationEvent.Source.ANDROID, version = datadogContext.sdkVersion, - application = TelemetryConfigurationEvent.Application(rumContext.applicationId), - session = TelemetryConfigurationEvent.Session(rumContext.sessionId), - view = rumContext.viewId?.let { TelemetryConfigurationEvent.View(it) }, - action = rumContext.actionId?.let { TelemetryConfigurationEvent.Action(it) }, + application = TelemetryConfigurationEvent.Application(applicationId), + session = rumContext?.session?.let { TelemetryConfigurationEvent.Session(it.id) }, + view = rumContext?.viewId?.let { TelemetryConfigurationEvent.View(it) }, + action = rumContext?.actionId?.let { TelemetryConfigurationEvent.Action(it) }, experimentalFeatures = null, effectiveSampleRate = effectiveSampleRate, telemetry = TelemetryConfigurationEvent.Telemetry( @@ -500,10 +503,10 @@ internal class TelemetryEventHandler( ) ?: TelemetryUsageEvent.Source.ANDROID, service = TELEMETRY_SERVICE_NAME, version = datadogContext.sdkVersion, - application = TelemetryUsageEvent.Application(rumContext.applicationId), - session = TelemetryUsageEvent.Session(rumContext.sessionId), - view = rumContext.viewId?.let { TelemetryUsageEvent.View(it) }, - action = rumContext.actionId?.let { TelemetryUsageEvent.Action(it) }, + application = TelemetryUsageEvent.Application(applicationId), + session = rumContext?.session?.let { TelemetryUsageEvent.Session(it.id) }, + view = rumContext?.viewId?.let { TelemetryUsageEvent.View(it) }, + action = rumContext?.actionId?.let { TelemetryUsageEvent.Action(it) }, effectiveSampleRate = effectiveSampleRate, telemetry = TelemetryUsageEvent.Telemetry( additionalProperties = resolvedAdditionalProperties, @@ -574,8 +577,8 @@ internal class TelemetryEventHandler( } } - private fun DatadogContext.rumContext(): RumContext { - return featuresContext[Feature.RUM_FEATURE_NAME]?.rumContext() ?: RumContext() + private fun DatadogContext.rumContext(): RumContext? { + return featuresContext[Feature.RUM_FEATURE_NAME]?.rumContext() } private fun computeEffectiveSampleRate( diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/ActionEventAssert.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/ActionEventAssert.kt index c0897e6b2b..5b9967fef7 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/ActionEventAssert.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/ActionEventAssert.kt @@ -275,7 +275,7 @@ internal class ActionEventAssert(actual: ActionEvent) : return this } - fun hasSessionId(expected: String): ActionEventAssert { + fun hasSessionId(expected: String?): ActionEventAssert { assertThat(actual.session.id) .overridingErrorMessage( "Expected event to have session.id $expected but was ${actual.session.id}" @@ -366,13 +366,13 @@ internal class ActionEventAssert(actual: ActionEvent) : return this } - fun hasStartReason(reason: RumSessionStartReason): ActionEventAssert { + fun hasStartReason(reason: RumSessionStartReason?): ActionEventAssert { assertThat(actual.dd.session?.sessionPrecondition) .overridingErrorMessage( - "Expected event to have a session sessionPrecondition of ${reason.name} " + + "Expected event to have a session sessionPrecondition of ${reason?.name} " + "but was ${actual.dd.session?.sessionPrecondition}" ) - .isEqualTo(reason.toActionSessionPrecondition()) + .isEqualTo(reason?.toActionSessionPrecondition()) return this } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/ErrorEventAssert.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/ErrorEventAssert.kt index b3bd576453..e4cc80ac3a 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/ErrorEventAssert.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/ErrorEventAssert.kt @@ -303,7 +303,7 @@ internal class ErrorEventAssert(actual: ErrorEvent) : return this } - fun hasSessionId(expected: String): ErrorEventAssert { + fun hasSessionId(expected: String?): ErrorEventAssert { assertThat(actual.session.id) .overridingErrorMessage( "Expected event to have session.id $expected but was ${actual.session.id}" @@ -474,13 +474,13 @@ internal class ErrorEventAssert(actual: ErrorEvent) : return this } - fun hasStartReason(reason: RumSessionStartReason): ErrorEventAssert { + fun hasStartReason(reason: RumSessionStartReason?): ErrorEventAssert { assertThat(actual.dd.session?.sessionPrecondition) .overridingErrorMessage( - "Expected event to have a session sessionPrecondition of ${reason.name} " + + "Expected event to have a session sessionPrecondition of ${reason?.name} " + "but was ${actual.dd.session?.sessionPrecondition}" ) - .isEqualTo(reason.toErrorSessionPrecondition()) + .isEqualTo(reason?.toErrorSessionPrecondition()) return this } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/LongTaskEventAssert.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/LongTaskEventAssert.kt index 57255f2dcf..0d9c0b8664 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/LongTaskEventAssert.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/LongTaskEventAssert.kt @@ -204,7 +204,7 @@ internal class LongTaskEventAssert(actual: LongTaskEvent) : return this } - fun hasSessionId(expected: String): LongTaskEventAssert { + fun hasSessionId(expected: String?): LongTaskEventAssert { assertThat(actual.session.id) .overridingErrorMessage( "Expected event to have session.id $expected but was ${actual.session.id}" @@ -282,13 +282,13 @@ internal class LongTaskEventAssert(actual: LongTaskEvent) : return this } - fun hasStartReason(reason: RumSessionStartReason): LongTaskEventAssert { + fun hasStartReason(reason: RumSessionStartReason?): LongTaskEventAssert { assertThat(actual.dd.session?.sessionPrecondition) .overridingErrorMessage( - "Expected event to have a session sessionPrecondition of ${reason.name} " + + "Expected event to have a session sessionPrecondition of ${reason?.name} " + "but was ${actual.dd.session?.sessionPrecondition}" ) - .isEqualTo(reason.toLongTaskSessionPrecondition()) + .isEqualTo(reason?.toLongTaskSessionPrecondition()) return this } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/ResourceEventAssert.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/ResourceEventAssert.kt index f1309db5fe..53709afe52 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/ResourceEventAssert.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/ResourceEventAssert.kt @@ -408,7 +408,7 @@ internal class ResourceEventAssert(actual: ResourceEvent) : return this } - fun hasSessionId(expected: String): ResourceEventAssert { + fun hasSessionId(expected: String?): ResourceEventAssert { assertThat(actual.session.id) .overridingErrorMessage( "Expected event data to have session.id $expected but was ${actual.session.id}" @@ -541,13 +541,13 @@ internal class ResourceEventAssert(actual: ResourceEvent) : return this } - fun hasStartReason(reason: RumSessionStartReason): ResourceEventAssert { + fun hasStartReason(reason: RumSessionStartReason?): ResourceEventAssert { assertThat(actual.dd.session?.sessionPrecondition) .overridingErrorMessage( - "Expected event to have a session sessionPrecondition of ${reason.name} " + + "Expected event to have a session sessionPrecondition of ${reason?.name} " + "but was ${actual.dd.session?.sessionPrecondition}" ) - .isEqualTo(reason.toResourceSessionPrecondition()) + .isEqualTo(reason?.toResourceSessionPrecondition()) return this } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/RumContextAssert.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/RumContextAssert.kt index 050aa1ea5d..59a99b7689 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/RumContextAssert.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/RumContextAssert.kt @@ -34,10 +34,10 @@ internal class RumContextAssert(actual: RumContext) : return this } - fun hasSessionId(expected: String): RumContextAssert { - assertThat(actual.sessionId) + fun hasSessionId(expected: String?): RumContextAssert { + assertThat(actual.session?.id) .overridingErrorMessage( - "Expected context to have sessionId $expected but was ${actual.sessionId}" + "Expected context to have sessionId $expected but was ${actual.session?.id}" ) .isEqualTo(expected) return this diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/ViewEventAssert.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/ViewEventAssert.kt index f1e3ff8274..09452a0ae2 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/ViewEventAssert.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/ViewEventAssert.kt @@ -188,7 +188,7 @@ internal class ViewEventAssert(actual: ViewEvent) : return this } - fun hasSessionId(expected: String): ViewEventAssert { + fun hasSessionId(expected: String?): ViewEventAssert { assertThat(actual.session.id) .overridingErrorMessage( "Expected event to have session.id $expected but was ${actual.session.id}" @@ -579,13 +579,13 @@ internal class ViewEventAssert(actual: ViewEvent) : return this } - fun hasStartReason(reason: RumSessionStartReason): ViewEventAssert { + fun hasStartReason(reason: RumSessionStartReason?): ViewEventAssert { assertThat(actual.dd.session?.sessionPrecondition) .overridingErrorMessage( - "Expected event to have a session sessionPrecondition of ${reason.name} " + + "Expected event to have a session sessionPrecondition of ${reason?.name} " + "but was ${actual.dd.session?.sessionPrecondition}" ) - .isEqualTo(reason.toViewSessionPrecondition()) + .isEqualTo(reason?.toViewSessionPrecondition()) return this } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/VitalAppLaunchEventAssert.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/VitalAppLaunchEventAssert.kt index 431027bc0a..995edbb705 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/VitalAppLaunchEventAssert.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/VitalAppLaunchEventAssert.kt @@ -49,13 +49,13 @@ internal class VitalAppLaunchEventAssert( .containsExactlyInAnyOrderEntriesOf(expected) } - fun hasStartReason(reason: RumSessionStartReason) = apply { + fun hasStartReason(reason: RumSessionStartReason?) = apply { assertThat(actual.dd.session?.sessionPrecondition) .overridingErrorMessage( - "Expected event to have a session sessionPrecondition of ${reason.name} " + + "Expected event to have a session sessionPrecondition of ${reason?.name} " + "but was ${actual.dd.session?.sessionPrecondition}" ) - .isEqualTo(reason.toVitalAppLaunchSessionPrecondition()) + .isEqualTo(reason?.toVitalAppLaunchSessionPrecondition()) } fun hasSampleRate(sampleRate: Float?) = apply { @@ -67,7 +67,7 @@ internal class VitalAppLaunchEventAssert( .isEqualTo(sampleRate) } - fun hasSessionId(expected: String) = apply { + fun hasSessionId(expected: String?) = apply { assertThat(actual.session.id) .overridingErrorMessage( "Expected event to have session.id $expected but was ${actual.session.id}" diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/VitalOperationStepEventAssert.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/VitalOperationStepEventAssert.kt index e25f25cddc..29412fbeb4 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/VitalOperationStepEventAssert.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/assertj/VitalOperationStepEventAssert.kt @@ -51,13 +51,13 @@ internal class VitalOperationStepEventAssert( .containsExactlyInAnyOrderEntriesOf(expected) } - fun hasStartReason(reason: RumSessionStartReason) = apply { + fun hasStartReason(reason: RumSessionStartReason?) = apply { assertThat(actual.dd.session?.sessionPrecondition) .overridingErrorMessage( - "Expected event to have a session sessionPrecondition of ${reason.name} " + + "Expected event to have a session sessionPrecondition of ${reason?.name} " + "but was ${actual.dd.session?.sessionPrecondition}" ) - .isEqualTo(reason.toVitalOperationStepSessionPrecondition()) + .isEqualTo(reason?.toVitalOperationStepSessionPrecondition()) } fun hasSampleRate(sampleRate: Float?) = apply { @@ -69,7 +69,7 @@ internal class VitalOperationStepEventAssert( .isEqualTo(sampleRate) } - fun hasSessionId(expected: String) = apply { + fun hasSessionId(expected: String?) = apply { assertThat(actual.session.id) .overridingErrorMessage( "Expected event to have session.id $expected but was ${actual.session.id}" diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporterTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporterTest.kt index 5d35bfbd62..aac078bf58 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporterTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporterTest.kt @@ -21,6 +21,7 @@ import com.datadog.android.core.feature.event.ThreadDump import com.datadog.android.core.internal.persistence.Deserializer import com.datadog.android.internal.FeatureContextKeys import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.RumErrorSource import com.datadog.android.rum.assertj.ErrorEventAssert @@ -1206,6 +1207,8 @@ internal class DatadogLateCrashReporterTest { @Test fun `M not send anything W handleAnrCrash() { last view event belongs to the current session }`( @Forgery viewEvent: ViewEvent, + @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext, forge: Forge ) { // Given @@ -1224,7 +1227,10 @@ internal class DatadogLateCrashReporterTest { ), featuresContext = mapOf( Feature.RUM_FEATURE_NAME to - mapOf(FeatureContextKeys.RUM_CONTEXT to RumContext(sessionId = viewEvent.session.id)) + mapOf( + FeatureContextKeys.RUM_CONTEXT to + fakeRumContext.copy(session = fakeSession.copy(id = viewEvent.session.id)) + ) ) ) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/RumFeatureContextTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/RumFeatureContextTest.kt index 5a3f783036..87a56ac3dc 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/RumFeatureContextTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/RumFeatureContextTest.kt @@ -8,6 +8,7 @@ package com.datadog.android.rum.internal.domain import com.datadog.android.internal.FeatureContextKeys import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.rum.utils.forge.Configurator import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.Forgery @@ -24,10 +25,12 @@ internal class RumFeatureContextTest { @Test fun `M publish typed snapshot and preserve legacy fields W setRumContext()`( @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext, forge: Forge ) { // Given val context = fakeRumContext.copy( + session = fakeSession, viewTimestamp = forge.aLong(), viewTimestampOffset = forge.aLong(), hasReplay = forge.aBool() @@ -43,10 +46,10 @@ internal class RumFeatureContextTest { mapOf( FeatureContextKeys.RUM_CONTEXT to context, "application_id" to context.applicationId, - "session_id" to context.sessionId, - "session_active" to context.isSessionActive, - "session_state" to context.sessionState.asString, - "session_start_reason" to context.sessionStartReason.asString, + "session_id" to fakeSession.id, + "session_active" to fakeSession.isActive, + "session_state" to fakeSession.state.asString, + "session_start_reason" to fakeSession.startReason.asString, "view_id" to context.viewId, "view_name" to context.viewName, "view_url" to context.viewUrl, @@ -57,19 +60,20 @@ internal class RumFeatureContextTest { "view_timestamp" to context.viewTimestamp, "view_timestamp_offset" to context.viewTimestampOffset, "view_has_replay" to context.hasReplay, - "session_has_timeseries" to context.hasTimeseries, - "session_sample_rate" to context.sessionSampleRate + "session_has_timeseries" to fakeSession.hasTimeseries, + "session_sample_rate" to fakeSession.sampleRate ) ) } @Test fun `M clear the previous snapshot and legacy fields W setRumContext() {no context}`( - @Forgery fakeRumContext: RumContext + @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext ) { // Given val featureContext = mutableMapOf() - featureContext.setRumContext(fakeRumContext) + featureContext.setRumContext(fakeRumContext.copy(session = fakeSession)) // When featureContext.setRumContext(null) @@ -77,4 +81,23 @@ internal class RumFeatureContextTest { // Then assertThat(featureContext).isEmpty() } + + @Test + fun `M clear stale legacy correlation W setRumContext() {application only}`( + @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext + ) { + // Given + val featureContext = mutableMapOf() + featureContext.setRumContext(fakeRumContext.copy(session = fakeSession)) + val context = RumContext(applicationId = fakeRumContext.applicationId) + + // When + featureContext.setRumContext(context) + + // Then + assertThat(featureContext).isEqualTo( + mapOf(FeatureContextKeys.RUM_CONTEXT to context, "application_id" to context.applicationId) + ) + } } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScopeAttributePropagationTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScopeAttributePropagationTest.kt index 096948629c..805d36c248 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScopeAttributePropagationTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScopeAttributePropagationTest.kt @@ -18,6 +18,7 @@ import com.datadog.android.api.storage.EventType import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.rum.RumActionType import com.datadog.android.rum.RumSessionType import com.datadog.android.rum.assertj.ActionEventAssert.Companion.assertThat @@ -113,6 +114,9 @@ internal class RumActionScopeAttributePropagationTest { @Forgery lateinit var fakeParentContext: RumContext + @Forgery + private lateinit var fakeParentSession: RumSessionContext + @Forgery lateinit var fakeEventTime: Time @@ -149,6 +153,7 @@ internal class RumActionScopeAttributePropagationTest { @BeforeEach fun `set up`(forge: Forge) { + fakeParentContext = fakeParentContext.copy(session = fakeParentSession) fakeParentAttributes = forge.exhaustiveAttributes() fakeActionAttributes = forge.exhaustiveAttributes() whenever(mockParentScope.getCustomAttributes()) doReturn fakeParentAttributes.toMutableMap() diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScopeTest.kt index 2a55bf0376..0ae466ba1f 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumActionScopeTest.kt @@ -17,6 +17,7 @@ import com.datadog.android.heatmaps.CrossPlatformHeatmapActionData import com.datadog.android.internal.heatmaps.HeatmapIdentifier import com.datadog.android.internal.heatmaps.HeatmapIdentifierRegistry import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.rum.RumActionType import com.datadog.android.rum.RumErrorSource import com.datadog.android.rum.RumResourceKind @@ -113,6 +114,9 @@ internal class RumActionScopeTest { @Forgery lateinit var fakeParentContext: RumContext + @Forgery + private lateinit var fakeParentSession: RumSessionContext + @Forgery lateinit var fakeNetworkInfoAtScopeStart: NetworkInfo @@ -143,6 +147,7 @@ internal class RumActionScopeTest { @BeforeEach fun `set up`(forge: Forge) { + fakeParentContext = fakeParentContext.copy(session = fakeParentSession) fakeSourceActionEvent = forge.aNullable { aValueFrom(ActionEvent.ActionEventSource::class.java) } fakeDatadogContext = fakeDatadogContext.copy( @@ -268,12 +273,12 @@ internal class RumActionScopeTest { hasReplay(fakeHasReplay) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasIsMainProcess(fakeDatadogContext.processInfo.isMainProcess) - hasSessionTimeseries(fakeParentContext.hasTimeseries) + hasSessionTimeseries(fakeParentContext.session?.hasTimeseries) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -389,10 +394,10 @@ internal class RumActionScopeTest { } hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -483,10 +488,10 @@ internal class RumActionScopeTest { } hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasDeviceInfo( @@ -646,10 +651,10 @@ internal class RumActionScopeTest { hasNoFrustration() hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -728,10 +733,10 @@ internal class RumActionScopeTest { } hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -796,10 +801,10 @@ internal class RumActionScopeTest { hasNoFrustration() hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -872,10 +877,10 @@ internal class RumActionScopeTest { } hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -958,10 +963,10 @@ internal class RumActionScopeTest { } hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -1022,10 +1027,10 @@ internal class RumActionScopeTest { hasNoFrustration() hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -1085,10 +1090,10 @@ internal class RumActionScopeTest { hasNoFrustration() hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -1154,10 +1159,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -1226,10 +1231,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -1289,10 +1294,10 @@ internal class RumActionScopeTest { hasNoFrustration() hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -1352,10 +1357,10 @@ internal class RumActionScopeTest { hasNoFrustration() hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -1421,10 +1426,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -1492,10 +1497,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -1555,10 +1560,10 @@ internal class RumActionScopeTest { hasNoFrustration() hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -1618,10 +1623,10 @@ internal class RumActionScopeTest { hasNoFrustration() hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -1687,10 +1692,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -1758,10 +1763,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -1858,10 +1863,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.SYNTHETICS) hasSyntheticsTest(fakeTestId, fakeResultId) - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceActionEvent) @@ -1950,10 +1955,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceActionEvent) @@ -2025,10 +2030,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceActionEvent) @@ -2089,10 +2094,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -2158,10 +2163,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -2231,10 +2236,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -2305,10 +2310,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -2370,10 +2375,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -2436,10 +2441,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -2500,10 +2505,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -2564,10 +2569,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -2628,10 +2633,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -2693,10 +2698,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -2797,10 +2802,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -2862,10 +2867,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -2927,10 +2932,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -2988,10 +2993,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) @@ -3089,10 +3094,10 @@ internal class RumActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeTest.kt index d16ea794e3..edc623d2ad 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeTest.kt @@ -295,12 +295,19 @@ internal class RumApplicationScopeTest { } @Test - fun `delegates all events to child scope`() { + fun `M delegate event W handleEvent()`() { + // Given + whenever(mockEvent.eventTime) doReturn fakeEventTime + whenever( + mockChildScope.handleEvent(any(), eq(fakeDatadogContext), eq(mockEventWriteScope), eq(mockWriter)) + ) doReturn mockChildScope testedScope.childScopes.clear() testedScope.childScopes.add(mockChildScope) + // When testedScope.handleEvent(mockEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) + // Then verify(mockChildScope).handleEvent(mockEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) verifyNoInteractions(mockWriter) } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumContinuousActionScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumContinuousActionScopeTest.kt index 7ec82d36b5..d714b36c97 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumContinuousActionScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumContinuousActionScopeTest.kt @@ -14,6 +14,7 @@ import com.datadog.android.api.storage.DataWriter import com.datadog.android.api.storage.EventBatchWriter import com.datadog.android.api.storage.EventType import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.rum.RumActionType import com.datadog.android.rum.RumErrorSource import com.datadog.android.rum.RumResourceKind @@ -102,6 +103,9 @@ internal class RumContinuousActionScopeTest { @Forgery lateinit var fakeParentContext: RumContext + @Forgery + private lateinit var fakeParentSession: RumSessionContext + @Forgery lateinit var fakeNetworkInfoAtScopeStart: NetworkInfo @@ -129,6 +133,7 @@ internal class RumContinuousActionScopeTest { @BeforeEach fun `set up`(forge: Forge) { + fakeParentContext = fakeParentContext.copy(session = fakeParentSession) fakeSourceActionEvent = forge.aNullable { aValueFrom(ActionEvent.ActionEventSource::class.java) } fakeDatadogContext = fakeDatadogContext.copy( @@ -267,10 +272,10 @@ internal class RumContinuousActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -344,10 +349,10 @@ internal class RumContinuousActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -424,10 +429,10 @@ internal class RumContinuousActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -507,10 +512,10 @@ internal class RumContinuousActionScopeTest { hasNoFrustration() hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -610,10 +615,10 @@ internal class RumContinuousActionScopeTest { } hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -717,10 +722,10 @@ internal class RumContinuousActionScopeTest { } hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasDeviceInfo( fakeDatadogContext.deviceInfo.deviceName, @@ -796,10 +801,10 @@ internal class RumContinuousActionScopeTest { hasNoFrustration() hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -893,10 +898,10 @@ internal class RumContinuousActionScopeTest { } hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -980,10 +985,10 @@ internal class RumContinuousActionScopeTest { } hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -1076,10 +1081,10 @@ internal class RumContinuousActionScopeTest { } hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -1140,10 +1145,10 @@ internal class RumContinuousActionScopeTest { hasNoFrustration() hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -1208,10 +1213,10 @@ internal class RumContinuousActionScopeTest { hasNoFrustration() hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -1276,10 +1281,10 @@ internal class RumContinuousActionScopeTest { hasNoFrustration() hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -1355,10 +1360,10 @@ internal class RumContinuousActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -1436,10 +1441,10 @@ internal class RumContinuousActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -1499,10 +1504,10 @@ internal class RumContinuousActionScopeTest { hasNoFrustration() hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -1567,10 +1572,10 @@ internal class RumContinuousActionScopeTest { hasNoFrustration() hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -1635,10 +1640,10 @@ internal class RumContinuousActionScopeTest { hasNoFrustration() hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -1714,10 +1719,10 @@ internal class RumContinuousActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -1795,10 +1800,10 @@ internal class RumContinuousActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -1866,10 +1871,10 @@ internal class RumContinuousActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -1973,10 +1978,10 @@ internal class RumContinuousActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.SYNTHETICS) hasSyntheticsTest(fakeTestId, fakeResultId) - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -2072,10 +2077,10 @@ internal class RumContinuousActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -2149,10 +2154,10 @@ internal class RumContinuousActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -2221,10 +2226,10 @@ internal class RumContinuousActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -2303,10 +2308,10 @@ internal class RumContinuousActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -2386,10 +2391,10 @@ internal class RumContinuousActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -2466,10 +2471,10 @@ internal class RumContinuousActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -2540,10 +2545,10 @@ internal class RumContinuousActionScopeTest { hasNoFrustration() hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -2609,10 +2614,10 @@ internal class RumContinuousActionScopeTest { hasNoFrustration() hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -2676,10 +2681,10 @@ internal class RumContinuousActionScopeTest { hasNoFrustration() hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -2746,10 +2751,10 @@ internal class RumContinuousActionScopeTest { hasNoFrustration() hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -2899,10 +2904,10 @@ internal class RumContinuousActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -2972,10 +2977,10 @@ internal class RumContinuousActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -3035,10 +3040,10 @@ internal class RumContinuousActionScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentContext.session?.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentContext.session?.startReason) containsExactlyContextAttributes(fakeAttributes) hasSource(fakeSourceActionEvent) hasDeviceInfo( diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumResourceScopeAttributePropagationTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumResourceScopeAttributePropagationTest.kt index 3949ea955e..2670917d2c 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumResourceScopeAttributePropagationTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumResourceScopeAttributePropagationTest.kt @@ -17,6 +17,7 @@ import com.datadog.android.api.storage.EventBatchWriter import com.datadog.android.api.storage.EventType import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.rum.RumErrorSource import com.datadog.android.rum.RumResourceKind import com.datadog.android.rum.RumResourceMethod @@ -120,6 +121,9 @@ internal class RumResourceScopeAttributePropagationTest { @Forgery lateinit var fakeParentContext: RumContext + @Forgery + private lateinit var fakeParentSession: RumSessionContext + @Forgery lateinit var fakeEventTime: Time @@ -162,6 +166,7 @@ internal class RumResourceScopeAttributePropagationTest { @BeforeEach fun `set up`(forge: Forge) { + fakeParentContext = fakeParentContext.copy(session = fakeParentSession) fakeParentAttributes = forge.exhaustiveAttributes() fakeResourceAttributes = forge.exhaustiveAttributes() fakeErrorAttributes = forge.exhaustiveAttributes() diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumResourceScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumResourceScopeTest.kt index bb8eba81d9..5357bb5701 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumResourceScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumResourceScopeTest.kt @@ -15,6 +15,7 @@ import com.datadog.android.api.storage.EventBatchWriter import com.datadog.android.api.storage.EventType import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.utils.loggableStackTrace import com.datadog.android.rum.RumAttributes import com.datadog.android.rum.RumErrorSource @@ -63,6 +64,7 @@ import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.api.extension.Extensions import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.EnumSource import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension @@ -78,6 +80,7 @@ import org.mockito.kotlin.isA import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.verifyNoMoreInteractions import org.mockito.kotlin.whenever import org.mockito.quality.Strictness @@ -137,6 +140,9 @@ internal class RumResourceScopeTest { @Forgery lateinit var fakeParentContext: RumContext + @Forgery + private lateinit var fakeParentSession: RumSessionContext + @Forgery lateinit var fakeDatadogContext: DatadogContext @@ -161,6 +167,7 @@ internal class RumResourceScopeTest { @BeforeEach fun `set up`(forge: Forge) { + fakeParentContext = fakeParentContext.copy(session = fakeParentSession) val isValidSource = forge.aBool() val fakeSource = if (isValidSource) { @@ -300,9 +307,9 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasIsMainProcess(fakeDatadogContext.processInfo.isMainProcess) - hasSessionTimeseries(fakeParentContext.hasTimeseries) + hasSessionTimeseries(fakeParentSession.hasTimeseries) hasActionId(fakeParentContext.actionId) hasTraceId(null) hasSpanId(null) @@ -311,7 +318,7 @@ internal class RumResourceScopeTest { doesNotHaveAResourceProvider() hasSessionType(fakeRumSessionType?.toResource() ?: ResourceEvent.ResourceEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceResourceEvent) hasDeviceInfo( @@ -377,7 +384,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasTraceId(null) hasSpanId(null) @@ -385,7 +392,7 @@ internal class RumResourceScopeTest { hasRulePsr(null) hasProviderType(ResourceEvent.ProviderType.FIRST_PARTY) hasProviderDomain(@Suppress("DEPRECATION") URL(fakeUrl).host) - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasSessionType(fakeRumSessionType?.toResource() ?: ResourceEvent.ResourceEventSessionType.USER) hasNoSyntheticsTest() containsExactlyContextAttributes(expectedAttributes) @@ -470,7 +477,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasTraceId(null) hasSpanId(null) @@ -478,7 +485,7 @@ internal class RumResourceScopeTest { hasRulePsr(null) hasProviderType(ResourceEvent.ProviderType.FIRST_PARTY) hasProviderDomain(brokenUrl) - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasSessionType(fakeRumSessionType?.toResource() ?: ResourceEvent.ResourceEventSessionType.USER) hasNoSyntheticsTest() containsExactlyContextAttributes(expectedAttributes) @@ -552,7 +559,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasTraceId(fakeTraceId) hasSpanId(fakeSpanId) @@ -561,7 +568,7 @@ internal class RumResourceScopeTest { doesNotHaveAResourceProvider() hasSessionType(fakeRumSessionType?.toResource() ?: ResourceEvent.ResourceEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceResourceEvent) hasDeviceInfo( @@ -628,7 +635,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasTraceId(null) hasSpanId(null) @@ -637,7 +644,7 @@ internal class RumResourceScopeTest { doesNotHaveAResourceProvider() hasSessionType(fakeRumSessionType?.toResource() ?: ResourceEvent.ResourceEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceResourceEvent) hasDeviceInfo( @@ -725,7 +732,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasTraceId(null) hasSpanId(null) @@ -736,7 +743,7 @@ internal class RumResourceScopeTest { fakeRumSessionType?.toResource() ?: ResourceEvent.ResourceEventSessionType.SYNTHETICS ) hasSyntheticsTest(fakeTestId, fakeResultId) - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceResourceEvent) hasDeviceInfo( @@ -794,7 +801,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasTraceId(null) hasSpanId(null) @@ -803,7 +810,7 @@ internal class RumResourceScopeTest { doesNotHaveAResourceProvider() hasSessionType(fakeRumSessionType?.toResource() ?: ResourceEvent.ResourceEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) containsExactlyContextAttributes(fakeResourceAttributes) hasSource(fakeSourceResourceEvent) hasDeviceInfo( @@ -856,9 +863,9 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasSessionType(fakeRumSessionType?.toResource() ?: ResourceEvent.ResourceEventSessionType.USER) hasNoSyntheticsTest() hasReplay(fakeHasReplay) @@ -987,7 +994,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasTraceId(null) hasSpanId(null) @@ -996,7 +1003,7 @@ internal class RumResourceScopeTest { doesNotHaveAResourceProvider() hasSessionType(fakeRumSessionType?.toResource() ?: ResourceEvent.ResourceEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceResourceEvent) hasDeviceInfo( @@ -1064,7 +1071,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasTraceId(null) hasSpanId(null) @@ -1073,7 +1080,7 @@ internal class RumResourceScopeTest { doesNotHaveAResourceProvider() hasSessionType(fakeRumSessionType?.toResource() ?: ResourceEvent.ResourceEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceResourceEvent) hasDeviceInfo( @@ -1142,7 +1149,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasTraceId(null) hasSpanId(null) @@ -1151,7 +1158,7 @@ internal class RumResourceScopeTest { doesNotHaveAResourceProvider() hasSessionType(fakeRumSessionType?.toResource() ?: ResourceEvent.ResourceEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceResourceEvent) hasDeviceInfo( @@ -1221,7 +1228,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasTraceId(null) hasSpanId(null) @@ -1230,7 +1237,7 @@ internal class RumResourceScopeTest { doesNotHaveAResourceProvider() hasSessionType(fakeRumSessionType?.toResource() ?: ResourceEvent.ResourceEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceResourceEvent) hasDeviceInfo( @@ -1303,9 +1310,9 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasIsMainProcess(fakeDatadogContext.processInfo.isMainProcess) - hasSessionTimeseries(fakeParentContext.hasTimeseries) + hasSessionTimeseries(fakeParentSession.hasTimeseries) hasActionId(fakeParentContext.actionId) hasErrorType(throwable.javaClass.canonicalName) hasErrorSourceType(ErrorEvent.SourceType.ANDROID) @@ -1313,7 +1320,7 @@ internal class RumResourceScopeTest { hasTimeSinceAppStart(null) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceErrorEvent) @@ -1392,14 +1399,14 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasErrorType(throwable.javaClass.canonicalName) hasErrorSourceType(ErrorEvent.SourceType.ANDROID) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) hasErrorFingerprint(fakeFingerprint) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceErrorEvent) @@ -1474,7 +1481,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasErrorType(errorType) hasErrorSourceType(ErrorEvent.SourceType.ANDROID) @@ -1482,7 +1489,7 @@ internal class RumResourceScopeTest { hasTimeSinceAppStart(null) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(expectedAttributes) hasDeviceInfo( @@ -1577,7 +1584,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasErrorType(throwable.javaClass.canonicalName) hasErrorSourceType(ErrorEvent.SourceType.ANDROID) @@ -1585,7 +1592,7 @@ internal class RumResourceScopeTest { hasTimeSinceAppStart(null) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.SYNTHETICS) hasSyntheticsTest(fakeTestId, fakeResultId) - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceErrorEvent) @@ -1683,7 +1690,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasErrorType(errorType) hasErrorSourceType(ErrorEvent.SourceType.ANDROID) @@ -1691,7 +1698,7 @@ internal class RumResourceScopeTest { hasTimeSinceAppStart(null) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.SYNTHETICS) hasSyntheticsTest(fakeTestId, fakeResultId) - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(expectedAttributes) hasDeviceInfo( @@ -1782,7 +1789,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasProviderDomain(brokenUrl) hasProviderType(ErrorEvent.ProviderType.FIRST_PARTY) @@ -1792,7 +1799,7 @@ internal class RumResourceScopeTest { hasTimeSinceAppStart(null) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceErrorEvent) @@ -1886,7 +1893,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasProviderDomain(brokenUrl) hasProviderType(ErrorEvent.ProviderType.FIRST_PARTY) @@ -1896,7 +1903,7 @@ internal class RumResourceScopeTest { hasTimeSinceAppStart(null) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(expectedAttributes) hasDeviceInfo( @@ -1971,7 +1978,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasProviderDomain(@Suppress("DEPRECATION") URL(fakeUrl).host) hasProviderType(ErrorEvent.ProviderType.FIRST_PARTY) @@ -1981,7 +1988,7 @@ internal class RumResourceScopeTest { hasTimeSinceAppStart(null) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceErrorEvent) @@ -2058,7 +2065,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasProviderDomain(@Suppress("DEPRECATION") URL(fakeUrl).host) hasProviderType(ErrorEvent.ProviderType.FIRST_PARTY) @@ -2068,7 +2075,7 @@ internal class RumResourceScopeTest { hasTimeSinceAppStart(null) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(expectedAttributes) hasDeviceInfo( @@ -2143,7 +2150,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasErrorType(throwable.javaClass.canonicalName) hasErrorSourceType(ErrorEvent.SourceType.ANDROID) @@ -2152,7 +2159,7 @@ internal class RumResourceScopeTest { hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) hasNoSyntheticsTest() doesNotHaveAResourceProvider() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceErrorEvent) @@ -2230,7 +2237,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasErrorType(errorType) hasErrorSourceType(ErrorEvent.SourceType.ANDROID) @@ -2239,7 +2246,7 @@ internal class RumResourceScopeTest { hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) hasNoSyntheticsTest() doesNotHaveAResourceProvider() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(expectedAttributes) hasDeviceInfo( @@ -2319,7 +2326,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasErrorType(throwable.javaClass.canonicalName) hasErrorSourceType(ErrorEvent.SourceType.ANDROID) @@ -2328,7 +2335,7 @@ internal class RumResourceScopeTest { hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) hasNoSyntheticsTest() doesNotHaveAResourceProvider() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceErrorEvent) @@ -2411,7 +2418,7 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) hasErrorType(errorType) hasErrorSourceType(ErrorEvent.SourceType.ANDROID) @@ -2420,7 +2427,7 @@ internal class RumResourceScopeTest { hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) hasNoSyntheticsTest() doesNotHaveAResourceProvider() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(expectedAttributes) hasDeviceInfo( @@ -2600,12 +2607,12 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) doesNotHaveAResourceProvider() hasSessionType(fakeRumSessionType?.toResource() ?: ResourceEvent.ResourceEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceResourceEvent) @@ -2674,12 +2681,12 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) doesNotHaveAResourceProvider() hasSessionType(fakeRumSessionType?.toResource() ?: ResourceEvent.ResourceEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceResourceEvent) @@ -2749,12 +2756,12 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) doesNotHaveAResourceProvider() hasSessionType(fakeRumSessionType?.toResource() ?: ResourceEvent.ResourceEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) containsExactlyContextAttributes(expectedAttributes) hasSource(fakeSourceResourceEvent) @@ -2880,12 +2887,12 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) doesNotHaveAResourceProvider() hasSessionType(fakeRumSessionType?.toResource() ?: ResourceEvent.ResourceEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasSource(fakeSourceResourceEvent) hasDeviceInfo( @@ -2953,12 +2960,12 @@ internal class RumResourceScopeTest { hasConnectivityInfo(fakeNetworkInfoAtScopeStart) hasView(fakeParentContext) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeParentContext.actionId) doesNotHaveAResourceProvider() hasSessionType(fakeRumSessionType?.toResource() ?: ResourceEvent.ResourceEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasSource(fakeSourceResourceEvent) hasDeviceInfo( @@ -3717,6 +3724,80 @@ internal class RumResourceScopeTest { ) } + @ParameterizedTest + @EnumSource(SessionlessResourceCompletion::class) + fun `M notify about dropped event W handleEvent() {no session}`( + completion: SessionlessResourceCompletion, + forge: Forge + ) { + // Given + fakeParentContext = fakeParentContext.copy(session = null) + whenever(mockParentScope.getRumContext()) doReturn fakeParentContext + testedScope = RumResourceScope( + parentScope = mockParentScope, + sdkCore = rumMonitor.mockSdkCore, + url = fakeUrl, + method = fakeMethod, + key = fakeKey, + eventTime = fakeEventTime, + initialAttributes = fakeResourceAttributes, + serverTimeOffsetInMs = fakeServerOffset, + firstPartyHostHeaderTypeResolver = mockResolver, + featuresContextResolver = mockFeaturesContextResolver, + sampleRate = fakeSampleRate, + networkSettledMetricResolver = mockNetworkSettledMetricResolver, + rumSessionTypeOverride = fakeRumSessionType, + insightsCollector = mockInsightsCollector + ) + val fakeStopTime = timeWithOffset(RESOURCE_DURATION_MS) + val fakeStopEvent = when (completion) { + SessionlessResourceCompletion.SUCCESS -> RumRawEvent.StopResource( + fakeKey, + forge.aLong(min = 200, max = 300), + forge.aLong(min = 0, max = 1024), + forge.aValueFrom(RumResourceKind::class.java), + emptyMap(), + fakeStopTime + ) + SessionlessResourceCompletion.ERROR -> RumRawEvent.StopResourceWithError( + fakeKey, + forge.aLong(min = 400, max = 600), + forge.aString(), + forge.aValueFrom(RumErrorSource::class.java), + forge.anException(), + emptyMap(), + fakeStopTime + ) + SessionlessResourceCompletion.STACK_TRACE -> RumRawEvent.StopResourceWithStackTrace( + fakeKey, + forge.aLong(min = 400, max = 600), + forge.aString(), + forge.aValueFrom(RumErrorSource::class.java), + forge.aString(), + forge.aString(), + emptyMap(), + fakeStopTime + ) + } + val expectedStorageEvent = when (completion) { + SessionlessResourceCompletion.SUCCESS -> StorageEvent.Resource( + testedScope.resourceId, + fakeStopTime.nanoTime + ) + else -> StorageEvent.Error(testedScope.resourceId, fakeStopTime.nanoTime) + } + + // When + val result = testedScope.handleEvent(fakeStopEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) + + // Then + assertThat(result).isNull() + verifyNoInteractions(mockWriter) + verify(rumMonitor.mockInstance as AdvancedRumMonitor) + .eventDropped(fakeParentContext.viewId.orEmpty(), expectedStorageEvent) + verifyNoMoreInteractions(rumMonitor.mockInstance) + } + @Test fun `M notify about error W handleEvent() { resource write failed }`( @Forgery kind: RumResourceKind, @@ -3875,9 +3956,11 @@ internal class RumResourceScopeTest { @LongForgery(0, 1024) size: Long ) { // Given - whenever(mockParentScope.getRumContext()) doReturn fakeParentContext.copy(hasTimeseries = false) + whenever(mockParentScope.getRumContext()) doReturn + fakeParentContext.copy(session = fakeParentSession.copy(hasTimeseries = false)) testedScope = createTestedScope() - whenever(mockParentScope.getRumContext()) doReturn fakeParentContext.copy(hasTimeseries = true) + whenever(mockParentScope.getRumContext()) doReturn + fakeParentContext.copy(session = fakeParentSession.copy(hasTimeseries = true)) // When mockEvent = RumRawEvent.StopResource( @@ -3894,7 +3977,7 @@ internal class RumResourceScopeTest { argumentCaptor { verify(mockWriter).write(eq(mockEventBatchWriter), capture(), eq(EventType.DEFAULT)) assertThat(firstValue) - .hasSessionId(fakeParentContext.sessionId) + .hasSessionId(fakeParentSession.id) .hasSessionTimeseries(true) } } @@ -3906,9 +3989,11 @@ internal class RumResourceScopeTest { @Forgery throwable: Throwable ) { // Given - whenever(mockParentScope.getRumContext()) doReturn fakeParentContext.copy(hasTimeseries = false) + whenever(mockParentScope.getRumContext()) doReturn + fakeParentContext.copy(session = fakeParentSession.copy(hasTimeseries = false)) testedScope = createTestedScope() - whenever(mockParentScope.getRumContext()) doReturn fakeParentContext.copy(hasTimeseries = true) + whenever(mockParentScope.getRumContext()) doReturn + fakeParentContext.copy(session = fakeParentSession.copy(hasTimeseries = true)) // When mockEvent = RumRawEvent.StopResourceWithError( @@ -3926,7 +4011,7 @@ internal class RumResourceScopeTest { argumentCaptor { verify(mockWriter).write(eq(mockEventBatchWriter), capture(), eq(EventType.DEFAULT)) assertThat(firstValue) - .hasSessionId(fakeParentContext.sessionId) + .hasSessionId(fakeParentSession.id) .hasSessionTimeseries(true) } } @@ -3939,12 +4024,16 @@ internal class RumResourceScopeTest { @Forgery fakeNewSessionId: UUID ) { // Given - whenever(mockParentScope.getRumContext()) doReturn fakeParentContext.copy(hasTimeseries = false) + whenever(mockParentScope.getRumContext()) doReturn + fakeParentContext.copy(session = fakeParentSession.copy(hasTimeseries = false)) testedScope = createTestedScope() - whenever(mockParentScope.getRumContext()) doReturn fakeParentContext.copy( - sessionId = fakeNewSessionId.toString(), - hasTimeseries = true - ) + whenever(mockParentScope.getRumContext()) doReturn + fakeParentContext.copy( + session = fakeParentSession.copy( + id = fakeNewSessionId.toString(), + hasTimeseries = true + ) + ) // When mockEvent = @@ -3955,7 +4044,7 @@ internal class RumResourceScopeTest { argumentCaptor { verify(mockWriter).write(eq(mockEventBatchWriter), capture(), eq(EventType.DEFAULT)) assertThat(firstValue) - .hasSessionId(fakeParentContext.sessionId) + .hasSessionId(fakeParentSession.id) .hasSessionTimeseries(false) } } @@ -3967,9 +4056,11 @@ internal class RumResourceScopeTest { @LongForgery(0, 1024) size: Long ) { // Given - whenever(mockParentScope.getRumContext()) doReturn fakeParentContext.copy(hasTimeseries = true) + whenever(mockParentScope.getRumContext()) doReturn + fakeParentContext.copy(session = fakeParentSession.copy(hasTimeseries = true)) testedScope = createTestedScope() - whenever(mockParentScope.getRumContext()) doReturn fakeParentContext.copy(hasTimeseries = false) + whenever(mockParentScope.getRumContext()) doReturn + fakeParentContext.copy(session = fakeParentSession.copy(hasTimeseries = false)) // When mockEvent = @@ -3980,7 +4071,7 @@ internal class RumResourceScopeTest { argumentCaptor { verify(mockWriter).write(eq(mockEventBatchWriter), capture(), eq(EventType.DEFAULT)) assertThat(firstValue) - .hasSessionId(fakeParentContext.sessionId) + .hasSessionId(fakeParentSession.id) .hasSessionTimeseries(true) } } @@ -4188,6 +4279,12 @@ internal class RumResourceScopeTest { // endregion + internal enum class SessionlessResourceCompletion { + SUCCESS, + ERROR, + STACK_TRACE + } + companion object { private const val RESOURCE_DURATION_MS = 50L diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeTest.kt index d2937da8e6..fa750bae83 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeTest.kt @@ -21,11 +21,14 @@ import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver import com.datadog.android.core.sampling.DeterministicSampler import com.datadog.android.core.sampling.Sampler import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.rum.RumSessionStartReason import com.datadog.android.internal.rum.RumSessionState import com.datadog.android.internal.rum.RumViewType import com.datadog.android.internal.sampling.SessionSamplingIdProvider import com.datadog.android.internal.tests.stub.StubTimeProvider +import com.datadog.android.rum.RumResourceKind +import com.datadog.android.rum.RumResourceMethod import com.datadog.android.rum.RumSessionListener import com.datadog.android.rum.RumSessionType import com.datadog.android.rum.configuration.ViewEventWriteConfig @@ -55,6 +58,7 @@ import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.BoolForgery import fr.xgouchet.elmyr.annotation.FloatForgery import fr.xgouchet.elmyr.annotation.Forgery +import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension import org.assertj.core.api.Assertions.assertThat @@ -170,6 +174,9 @@ internal class RumSessionScopeTest { @Forgery lateinit var fakeParentContext: RumContext + @Forgery + private lateinit var fakeParentSession: RumSessionContext + @FloatForgery(min = 0f, max = 100f) var fakeSampleRate: Float = 0f @@ -215,6 +222,7 @@ internal class RumSessionScopeTest { @BeforeEach fun `set up`(forge: Forge) { + fakeParentContext = fakeParentContext.copy(session = fakeParentSession) stubTimeProvider = StubTimeProvider( elapsedTimeNs = TEST_INACTIVITY_NS + 1, elapsedRealtimeNs = TEST_INACTIVITY_NS + 1 @@ -270,6 +278,58 @@ internal class RumSessionScopeTest { initializeTestedScope() } + @Test + fun `M complete startup resource W stopResource() {before first session}`( + @StringForgery fakeKey: String, + @StringForgery fakeUrl: String, + forge: Forge + ) { + // Given + val fakeStartResource = RumRawEvent.StartResource( + fakeKey, + fakeUrl, + forge.aValueFrom(RumResourceMethod::class.java), + emptyMap(), + currentFakeTime() + ) + val fakeStopResource = RumRawEvent.StopResource( + fakeKey, + forge.aLong(min = 200L, max = 300L), + null, + forge.aValueFrom(RumResourceKind::class.java), + emptyMap(), + currentFakeTime() + ) + initializeTestedScope(withMockChildScope = false, backgroundTrackingEnabled = false) + testedScope.handleEvent( + RumRawEvent.ApplicationStarted(forge.aLong(min = 0L, max = TimeUnit.SECONDS.toNanos(1)), currentFakeTime()), + fakeDatadogContext, + mockEventWriteScope, + mockWriter + ) + val fakeView = checkNotNull(testedScope.childScope).childrenScopes.single() + assertThat(testedScope.sessionId).isNull() + testedScope.handleEvent( + fakeStartResource, + fakeDatadogContext, + mockEventWriteScope, + mockWriter + ) + assertThat(fakeView.activeResourceScopes).containsKey(fakeStartResource.key) + + // When + testedScope.handleEvent( + fakeStopResource, + fakeDatadogContext, + mockEventWriteScope, + mockWriter + ) + + // Then + assertThat(fakeView.activeResourceScopes).isEmpty() + verifyNoInteractions(mockWriter) + } + // region childScope @Test @@ -291,7 +351,7 @@ internal class RumSessionScopeTest { forge: Forge ) { // Given - testedScope.sessionState = RumSessionState.TRACKED + testedScope.session = fakeParentSession.copy(state = RumSessionState.TRACKED) val event = forge.interactiveRumRawEvent() // When @@ -305,7 +365,7 @@ internal class RumSessionScopeTest { @Test fun `M delegate events to child scope W handleViewEvent() {NOT TRACKED}`() { // Given - testedScope.sessionState = RumSessionState.NOT_TRACKED + testedScope.session = fakeParentSession.copy(state = RumSessionState.NOT_TRACKED) val mockEvent: RumRawEvent = mock() // When @@ -324,7 +384,7 @@ internal class RumSessionScopeTest { @Test fun `M delegate events to child scope W handleViewEvent() {EXPIRED}`() { // Given - testedScope.sessionState = RumSessionState.EXPIRED + testedScope.session = fakeParentSession.copy(state = RumSessionState.EXPIRED) val mockEvent = mock() // When @@ -391,7 +451,7 @@ internal class RumSessionScopeTest { // Then val context = testedScope.getRumContext() assertThat(context.applicationId).isEqualTo(initialContext.applicationId) - assertThat(context.isSessionActive).isFalse + assertThat(context.session).isNull() } @Test @@ -462,9 +522,8 @@ internal class RumSessionScopeTest { val context = testedScope.getRumContext() // Then - assertThat(context.sessionId).isEqualTo(RumContext.NULL_UUID) - assertThat(context.sessionState).isEqualTo(RumSessionState.NOT_TRACKED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) + assertThat(context.session).isNull() + assertThat(testedScope.session).isNull() assertThat(context.applicationId).isEqualTo(fakeParentContext.applicationId) assertThat(context.viewId).isEqualTo(fakeParentContext.viewId) } @@ -484,9 +543,9 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId).isNotEqualTo(RumContext.NULL_UUID) - assertThat(context.sessionState).isEqualTo(RumSessionState.TRACKED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) + assertThat(context.session?.id).isNotNull() + assertThat(context.session?.state).isEqualTo(RumSessionState.TRACKED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) assertThat(context.applicationId).isEqualTo(fakeParentContext.applicationId) assertThat(context.viewId).isEqualTo(fakeParentContext.viewId) } @@ -506,9 +565,9 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId).isNotEqualTo(RumContext.NULL_UUID) - assertThat(context.sessionState).isEqualTo(RumSessionState.NOT_TRACKED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) + assertThat(context.session?.id).isNotNull() + assertThat(context.session?.state).isEqualTo(RumSessionState.NOT_TRACKED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) assertThat(context.applicationId).isEqualTo(fakeParentContext.applicationId) assertThat(context.viewId).isEqualTo(fakeParentContext.viewId) } @@ -595,9 +654,9 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId).isNotEqualTo(RumContext.NULL_UUID) - assertThat(context.sessionState).isEqualTo(RumSessionState.TRACKED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) + assertThat(context.session?.id).isNotNull() + assertThat(context.session?.state).isEqualTo(RumSessionState.TRACKED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) assertThat(context.applicationId).isEqualTo(fakeParentContext.applicationId) assertThat(context.viewId).isEqualTo(fakeParentContext.viewId) } @@ -621,9 +680,9 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId).isNotEqualTo(RumContext.NULL_UUID) - assertThat(context.sessionState).isEqualTo(RumSessionState.TRACKED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.BACKGROUND_LAUNCH) + assertThat(context.session?.id).isNotNull() + assertThat(context.session?.state).isEqualTo(RumSessionState.TRACKED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.BACKGROUND_LAUNCH) assertThat(context.applicationId).isEqualTo(fakeParentContext.applicationId) assertThat(context.viewId).isEqualTo(fakeParentContext.viewId) } @@ -647,9 +706,8 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId).isEqualTo(RumContext.NULL_UUID) - assertThat(context.sessionState).isEqualTo(RumSessionState.EXPIRED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) + assertThat(context.session).isNull() + assertThat(testedScope.session).isNull() assertThat(context.applicationId).isEqualTo(fakeParentContext.applicationId) assertThat(context.viewId).isEqualTo(fakeParentContext.viewId) } @@ -668,9 +726,9 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId).isEqualTo(initialContext.sessionId) - assertThat(context.sessionState).isEqualTo(RumSessionState.TRACKED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) + assertThat(context.session?.id).isEqualTo(initialContext.session?.id) + assertThat(context.session?.state).isEqualTo(RumSessionState.TRACKED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) } @Test @@ -688,9 +746,9 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId).isEqualTo(initialContext.sessionId) - assertThat(context.sessionState).isEqualTo(RumSessionState.TRACKED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) + assertThat(context.session?.id).isEqualTo(initialContext.session?.id) + assertThat(context.session?.state).isEqualTo(RumSessionState.TRACKED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) } @Test @@ -708,9 +766,9 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId).isEqualTo(initialContext.sessionId) - assertThat(context.sessionState).isEqualTo(RumSessionState.TRACKED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) + assertThat(context.session?.id).isEqualTo(initialContext.session?.id) + assertThat(context.session?.state).isEqualTo(RumSessionState.TRACKED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) } @Test @@ -729,9 +787,9 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId).isEqualTo(initialContext.sessionId) - assertThat(context.sessionState).isEqualTo(RumSessionState.EXPIRED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) + assertThat(context.session?.id).isEqualTo(initialContext.session?.id) + assertThat(context.session?.state).isEqualTo(RumSessionState.EXPIRED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) } @Test @@ -750,9 +808,9 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId).isEqualTo(initialContext.sessionId) - assertThat(context.sessionState).isEqualTo(RumSessionState.EXPIRED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) + assertThat(context.session?.id).isEqualTo(initialContext.session?.id) + assertThat(context.session?.state).isEqualTo(RumSessionState.EXPIRED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) } @Test @@ -771,11 +829,11 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId) - .isNotEqualTo(initialContext.sessionId) - .isNotEqualTo(RumContext.NULL_UUID) - assertThat(context.sessionState).isEqualTo(RumSessionState.TRACKED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.INACTIVITY_TIMEOUT) + assertThat(context.session?.id) + .isNotEqualTo(initialContext.session?.id) + .isNotNull() + assertThat(context.session?.state).isEqualTo(RumSessionState.TRACKED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.INACTIVITY_TIMEOUT) } @Test @@ -794,11 +852,11 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId) - .isNotEqualTo(initialContext.sessionId) - .isNotEqualTo(RumContext.NULL_UUID) - assertThat(context.sessionState).isEqualTo(RumSessionState.TRACKED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.INACTIVITY_TIMEOUT) + assertThat(context.session?.id) + .isNotEqualTo(initialContext.session?.id) + .isNotNull() + assertThat(context.session?.state).isEqualTo(RumSessionState.TRACKED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.INACTIVITY_TIMEOUT) } @Test @@ -818,11 +876,11 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId) - .isNotEqualTo(initialContext.sessionId) - .isNotEqualTo(RumContext.NULL_UUID) - assertThat(context.sessionState).isEqualTo(RumSessionState.TRACKED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.BACKGROUND_LAUNCH) + assertThat(context.session?.id) + .isNotEqualTo(initialContext.session?.id) + .isNotNull() + assertThat(context.session?.state).isEqualTo(RumSessionState.TRACKED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.BACKGROUND_LAUNCH) } @Test @@ -841,11 +899,11 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId) - .isNotEqualTo(initialContext.sessionId) - .isNotEqualTo(RumContext.NULL_UUID) - assertThat(context.sessionState).isEqualTo(RumSessionState.TRACKED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.BACKGROUND_LAUNCH) + assertThat(context.session?.id) + .isNotEqualTo(initialContext.session?.id) + .isNotNull() + assertThat(context.session?.state).isEqualTo(RumSessionState.TRACKED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.BACKGROUND_LAUNCH) } @Test @@ -865,9 +923,9 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId).isEqualTo(initialContext.sessionId) - assertThat(context.sessionState).isEqualTo(RumSessionState.EXPIRED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) + assertThat(context.session?.id).isEqualTo(initialContext.session?.id) + assertThat(context.session?.state).isEqualTo(RumSessionState.EXPIRED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) } @Test @@ -886,9 +944,9 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId).isEqualTo(initialContext.sessionId) - assertThat(context.sessionState).isEqualTo(RumSessionState.EXPIRED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) + assertThat(context.session?.id).isEqualTo(initialContext.session?.id) + assertThat(context.session?.state).isEqualTo(RumSessionState.EXPIRED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.USER_APP_LAUNCH) } @Test @@ -921,11 +979,11 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId) - .isNotEqualTo(initialContext.sessionId) - .isNotEqualTo(RumContext.NULL_UUID) - assertThat(context.sessionState).isEqualTo(RumSessionState.TRACKED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.MAX_DURATION) + assertThat(context.session?.id) + .isNotEqualTo(initialContext.session?.id) + .isNotNull() + assertThat(context.session?.state).isEqualTo(RumSessionState.TRACKED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.MAX_DURATION) } @Test @@ -963,11 +1021,11 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId) - .isNotEqualTo(initialContext.sessionId) - .isNotEqualTo(RumContext.NULL_UUID) - assertThat(context.sessionState).isEqualTo(RumSessionState.TRACKED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.MAX_DURATION) + assertThat(context.session?.id) + .isNotEqualTo(initialContext.session?.id) + .isNotNull() + assertThat(context.session?.state).isEqualTo(RumSessionState.TRACKED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.MAX_DURATION) } @Test @@ -1005,11 +1063,11 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId) - .isNotEqualTo(initialContext.sessionId) - .isNotEqualTo(RumContext.NULL_UUID) - assertThat(context.sessionState).isEqualTo(RumSessionState.TRACKED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.MAX_DURATION) + assertThat(context.session?.id) + .isNotEqualTo(initialContext.session?.id) + .isNotNull() + assertThat(context.session?.state).isEqualTo(RumSessionState.TRACKED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.MAX_DURATION) } @Test @@ -1059,11 +1117,11 @@ internal class RumSessionScopeTest { // wall-clock (sleep-including) time exceeded the max duration while the // inactivity threshold was not crossed. assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId) - .isNotEqualTo(initialContext.sessionId) - .isNotEqualTo(RumContext.NULL_UUID) - assertThat(context.sessionState).isEqualTo(RumSessionState.TRACKED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.MAX_DURATION) + assertThat(context.session?.id) + .isNotEqualTo(initialContext.session?.id) + .isNotNull() + assertThat(context.session?.state).isEqualTo(RumSessionState.TRACKED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.MAX_DURATION) } @Test @@ -1095,11 +1153,11 @@ internal class RumSessionScopeTest { // See RUMS-6221: currently this assertion FAILS because the SDK reuses the // old session id (the defect under investigation). assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId) - .isNotEqualTo(initialContext.sessionId) - .isNotEqualTo(RumContext.NULL_UUID) - assertThat(context.sessionState).isEqualTo(RumSessionState.TRACKED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.INACTIVITY_TIMEOUT) + assertThat(context.session?.id) + .isNotEqualTo(initialContext.session?.id) + .isNotNull() + assertThat(context.session?.state).isEqualTo(RumSessionState.TRACKED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.INACTIVITY_TIMEOUT) } @Test @@ -1122,11 +1180,11 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId) - .isNotEqualTo(initialContext.sessionId) - .isNotEqualTo(RumContext.NULL_UUID) - assertThat(context.sessionState).isEqualTo(RumSessionState.TRACKED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.EXPLICIT_STOP) + assertThat(context.session?.id) + .isNotEqualTo(initialContext.session?.id) + .isNotNull() + assertThat(context.session?.state).isEqualTo(RumSessionState.TRACKED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.EXPLICIT_STOP) assertThat(context.applicationId).isEqualTo(fakeParentContext.applicationId) assertThat(context.viewId).isEqualTo(fakeParentContext.viewId) } @@ -1211,12 +1269,12 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId) - .isNotEqualTo(RumContext.NULL_UUID) - .isNotEqualTo(initialContext.sessionId) + assertThat(context.session?.id) + .isNotNull() + .isNotEqualTo(initialContext.session?.id) verify(mockChildScope).renewViewScopes(eventTime = newEvent.eventTime) - verify(mockSessionListener).onSessionStarted(initialContext.sessionId, false) - verify(mockSessionListener).onSessionStarted(context.sessionId, false) + verify(mockSessionListener).onSessionStarted(checkNotNull(initialContext.session?.id), false) + verify(mockSessionListener).onSessionStarted(checkNotNull(context.session?.id), false) } @Test @@ -1235,12 +1293,12 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId) - .isNotEqualTo(RumContext.NULL_UUID) - .isNotEqualTo(initialContext.sessionId) + assertThat(context.session?.id) + .isNotNull() + .isNotEqualTo(initialContext.session?.id) verify(mockChildScope).renewViewScopes(eventTime = newEvent.eventTime) - verify(mockSessionListener).onSessionStarted(initialContext.sessionId, false) - verify(mockSessionListener).onSessionStarted(context.sessionId, false) + verify(mockSessionListener).onSessionStarted(checkNotNull(initialContext.session?.id), false) + verify(mockSessionListener).onSessionStarted(checkNotNull(context.session?.id), false) } @Test @@ -1259,12 +1317,12 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId) - .isNotEqualTo(RumContext.NULL_UUID) - .isNotEqualTo(initialContext.sessionId) + assertThat(context.session?.id) + .isNotNull() + .isNotEqualTo(initialContext.session?.id) verify(mockChildScope).renewViewScopes(eventTime = resetEvent.eventTime) - verify(mockSessionListener).onSessionStarted(initialContext.sessionId, false) - verify(mockSessionListener).onSessionStarted(context.sessionId, false) + verify(mockSessionListener).onSessionStarted(checkNotNull(initialContext.session?.id), false) + verify(mockSessionListener).onSessionStarted(checkNotNull(context.session?.id), false) } @Test @@ -1290,12 +1348,12 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId) - .isNotEqualTo(RumContext.NULL_UUID) - .isNotEqualTo(initialContext.sessionId) + assertThat(context.session?.id) + .isNotNull() + .isNotEqualTo(initialContext.session?.id) verify(mockChildScope).renewViewScopes(eventTime = newEvent.eventTime) - verify(mockSessionListener).onSessionStarted(initialContext.sessionId, true) - verify(mockSessionListener).onSessionStarted(context.sessionId, true) + verify(mockSessionListener).onSessionStarted(checkNotNull(initialContext.session?.id), true) + verify(mockSessionListener).onSessionStarted(checkNotNull(context.session?.id), true) } @Test @@ -1316,12 +1374,12 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId) - .isNotEqualTo(RumContext.NULL_UUID) - .isNotEqualTo(initialContext.sessionId) + assertThat(context.session?.id) + .isNotNull() + .isNotEqualTo(initialContext.session?.id) verify(mockChildScope).renewViewScopes(eventTime = newEvent.eventTime) - verify(mockSessionListener).onSessionStarted(initialContext.sessionId, true) - verify(mockSessionListener).onSessionStarted(context.sessionId, true) + verify(mockSessionListener).onSessionStarted(checkNotNull(initialContext.session?.id), true) + verify(mockSessionListener).onSessionStarted(checkNotNull(context.session?.id), true) } @Test @@ -1347,11 +1405,11 @@ internal class RumSessionScopeTest { // Then assertThat(result).isSameAs(testedScope) - assertThat(context.sessionId) - .isNotEqualTo(RumContext.NULL_UUID) - .isNotEqualTo(initialContext.sessionId) - verify(mockSessionListener).onSessionStarted(initialContext.sessionId, true) - verify(mockSessionListener).onSessionStarted(context.sessionId, true) + assertThat(context.session?.id) + .isNotNull() + .isNotEqualTo(initialContext.session?.id) + verify(mockSessionListener).onSessionStarted(checkNotNull(initialContext.session?.id), true) + verify(mockSessionListener).onSessionStarted(checkNotNull(context.session?.id), true) } // endregion @@ -1377,7 +1435,7 @@ internal class RumSessionScopeTest { RumSessionScope.SESSION_REPLAY_BUS_MESSAGE_TYPE_KEY to RumSessionScope.RUM_SESSION_RENEWED_BUS_MESSAGE, RumSessionScope.RUM_SESSION_ID_BUS_MESSAGE_KEY to - testedScope.getRumContext().sessionId, + testedScope.getRumContext().session?.id, RumSessionScope.RUM_SESSION_SAMPLE_RATE_BUS_MESSAGE_KEY to testedScope.sessionSampleRate, RumSessionScope.RUM_KEEP_SESSION_BUS_MESSAGE_KEY to (testedScope.sessionState == RumSessionState.TRACKED) @@ -1388,7 +1446,7 @@ internal class RumSessionScopeTest { RumSessionScope.SESSION_REPLAY_BUS_MESSAGE_TYPE_KEY to RumSessionScope.RUM_SESSION_RENEWED_BUS_MESSAGE, RumSessionScope.RUM_SESSION_ID_BUS_MESSAGE_KEY to - testedScope.getRumContext().sessionId, + testedScope.getRumContext().session?.id, RumSessionScope.RUM_SESSION_SAMPLE_RATE_BUS_MESSAGE_KEY to testedScope.sessionSampleRate, RumSessionScope.RUM_KEEP_SESSION_BUS_MESSAGE_KEY to (testedScope.sessionState == RumSessionState.TRACKED) @@ -1397,52 +1455,18 @@ internal class RumSessionScopeTest { } @Test - fun `M notify Session Replay feature W new non-interaction event received`( - forge: Forge + fun `M not notify Session Replay W handleEvent() {session absent}`( + @Forgery fakeEvent: RumRawEvent.AddError ) { // Given initializeTestedScope(backgroundTrackingEnabled = false) - val fakeNonInteractionEvent1 = forge.anyRumEvent( - excluding = listOf( - RumRawEvent.StartView::class, - RumRawEvent.StartAction::class - ) - ) - val fakeNonInteractionEvent2 = forge.anyRumEvent( - excluding = listOf( - RumRawEvent.StartView::class, - RumRawEvent.StartAction::class - ) - ) - testedScope.handleEvent(fakeNonInteractionEvent1, fakeDatadogContext, mockEventWriteScope, mockWriter) - testedScope.handleEvent(fakeNonInteractionEvent2, fakeDatadogContext, mockEventWriteScope, mockWriter) + + // When + testedScope.handleEvent(fakeEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) // Then - val argumentCaptor = argumentCaptor() - verify(mockSessionReplayFeatureScope, times(2)) - .sendEvent(argumentCaptor.capture()) - assertThat(argumentCaptor.firstValue).isEqualTo( - mapOf( - RumSessionScope.SESSION_REPLAY_BUS_MESSAGE_TYPE_KEY to - RumSessionScope.RUM_SESSION_RENEWED_BUS_MESSAGE, - RumSessionScope.RUM_SESSION_ID_BUS_MESSAGE_KEY to - testedScope.getRumContext().sessionId, - RumSessionScope.RUM_SESSION_SAMPLE_RATE_BUS_MESSAGE_KEY to testedScope.sessionSampleRate, - RumSessionScope.RUM_KEEP_SESSION_BUS_MESSAGE_KEY to - (testedScope.sessionState == RumSessionState.TRACKED) - ) - ) - assertThat(argumentCaptor.secondValue).isEqualTo( - mapOf( - RumSessionScope.SESSION_REPLAY_BUS_MESSAGE_TYPE_KEY to - RumSessionScope.RUM_SESSION_RENEWED_BUS_MESSAGE, - RumSessionScope.RUM_SESSION_ID_BUS_MESSAGE_KEY to - testedScope.getRumContext().sessionId, - RumSessionScope.RUM_SESSION_SAMPLE_RATE_BUS_MESSAGE_KEY to testedScope.sessionSampleRate, - RumSessionScope.RUM_KEEP_SESSION_BUS_MESSAGE_KEY to - (testedScope.sessionState == RumSessionState.TRACKED) - ) - ) + assertThat(testedScope.sessionId).isNull() + verifyNoInteractions(mockSessionReplayFeatureScope) } @Test @@ -1451,13 +1475,13 @@ internal class RumSessionScopeTest { ) { // Given testedScope.handleEvent(forge.startViewEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) - val firstSessionId = testedScope.getRumContext().sessionId + val firstSessionId = testedScope.getRumContext().session?.id // When advanceTimeByMs(TEST_MAX_DURATION_MS) val newEvent = forge.startViewEvent() testedScope.handleEvent(newEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) - val secondSessionId = testedScope.getRumContext().sessionId + val secondSessionId = testedScope.getRumContext().session?.id // Then val argumentCaptor = argumentCaptor() @@ -1492,13 +1516,13 @@ internal class RumSessionScopeTest { ) { // Given testedScope.handleEvent(forge.startViewEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) - val firstSessionId = testedScope.getRumContext().sessionId + val firstSessionId = testedScope.getRumContext().session?.id // When advanceTimeByMs(TEST_INACTIVITY_MS) val newEvent = forge.startViewEvent() testedScope.handleEvent(newEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) - val secondSessionId = testedScope.getRumContext().sessionId + val secondSessionId = testedScope.getRumContext().session?.id // Then val argumentCaptor = argumentCaptor() @@ -1535,14 +1559,14 @@ internal class RumSessionScopeTest { ) { // Given testedScope.handleEvent(forge.startViewEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) - val firstSessionId = testedScope.getRumContext().sessionId + val firstSessionId = testedScope.getRumContext().session?.id // When val resetEvent = RumRawEvent.ResetSession(eventTime = currentFakeTime()) testedScope.handleEvent(resetEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) testedScope.handleEvent(forge.startViewEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) - val secondSessionId = testedScope.getRumContext().sessionId + val secondSessionId = testedScope.getRumContext().session?.id // Then val argumentCaptor = argumentCaptor() @@ -1592,13 +1616,13 @@ internal class RumSessionScopeTest { whenever(mockSessionSampler.sample(any())).thenReturn(false) initializeTestedScope() testedScope.handleEvent(forge.startViewEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) - val firstSessionId = testedScope.getRumContext().sessionId + val firstSessionId = testedScope.getRumContext().session?.id // When advanceTimeByMs(TEST_MAX_DURATION_MS) val newEvent = forge.startViewEvent() testedScope.handleEvent(newEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) - val secondSessionId = testedScope.getRumContext().sessionId + val secondSessionId = testedScope.getRumContext().session?.id // Then val argumentCaptor = argumentCaptor() @@ -1635,13 +1659,13 @@ internal class RumSessionScopeTest { whenever(mockSessionSampler.sample(any())).thenReturn(false) initializeTestedScope() testedScope.handleEvent(forge.startViewEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) - val firstSessionId = testedScope.getRumContext().sessionId + val firstSessionId = testedScope.getRumContext().session?.id // When advanceTimeByMs(TEST_INACTIVITY_MS) val newEvent = forge.startViewEvent() testedScope.handleEvent(newEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) - val secondSessionId = testedScope.getRumContext().sessionId + val secondSessionId = testedScope.getRumContext().session?.id // Then val argumentCaptor = argumentCaptor() @@ -1678,13 +1702,13 @@ internal class RumSessionScopeTest { whenever(mockSessionSampler.sample(any())).thenReturn(false) initializeTestedScope() testedScope.handleEvent(forge.startViewEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) - val firstSessionId = testedScope.getRumContext().sessionId + val firstSessionId = testedScope.getRumContext().session?.id // When val resetEvent = RumRawEvent.ResetSession(eventTime = currentFakeTime()) testedScope.handleEvent(resetEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) testedScope.handleEvent(forge.startViewEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) - val secondSessionId = testedScope.getRumContext().sessionId + val secondSessionId = testedScope.getRumContext().session?.id // Then val argumentCaptor = argumentCaptor() @@ -1827,8 +1851,9 @@ internal class RumSessionScopeTest { writeScope = mockEventWriteScope, writer = mockWriter ) - testedScope.sessionState = - forge.aValueFrom(RumSessionState::class.java, exclude = listOf(RumSessionState.TRACKED)) + testedScope.session = checkNotNull(testedScope.session).copy( + state = forge.aValueFrom(RumSessionState::class.java, exclude = listOf(RumSessionState.TRACKED)) + ) // When val result = testedScope.handleEvent( @@ -1965,13 +1990,29 @@ internal class RumSessionScopeTest { // Given initializeTestedScope(timeseriesCollector = mockTimeseriesCollector) testedScope.handleEvent(forge.startViewEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) - whenever(mockTimeseriesCollector.isTimeseriesCollected(testedScope.sessionId)) doReturn fakeHasTimeseries + whenever(mockTimeseriesCollector.isTimeseriesCollected(checkNotNull(testedScope.sessionId))) doReturn + fakeHasTimeseries + + // When + val context = testedScope.getRumContext() + + // Then + assertThat(context.session?.hasTimeseries).isEqualTo(fakeHasTimeseries) + } + + @Test + fun `M report no timeseries W getRumContext() {no session}`() { + // Given + fakeParentContext = + fakeParentContext.copy(session = fakeParentSession.copy(hasTimeseries = true)) + initializeTestedScope(timeseriesCollector = mockTimeseriesCollector) // When val context = testedScope.getRumContext() // Then - assertThat(context.hasTimeseries).isEqualTo(fakeHasTimeseries) + assertThat(context.session).isNull() + verify(mockTimeseriesCollector, never()).isTimeseriesCollected(any()) } @Test @@ -1979,7 +2020,7 @@ internal class RumSessionScopeTest { // Given initializeTestedScope(timeseriesCollector = mockTimeseriesCollector) testedScope.handleEvent(forge.startViewEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) - val expiredSessionId = testedScope.sessionId + val expiredSessionId = checkNotNull(testedScope.sessionId) whenever(mockTimeseriesCollector.isTimeseriesCollected(expiredSessionId)) doReturn true advanceTimeByMs(TEST_INACTIVITY_MS) testedScope.handleEvent(forge.startViewEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) @@ -1988,8 +2029,9 @@ internal class RumSessionScopeTest { val context = testedScope.getRumContext() // Then - assertThat(context.sessionId).isNotEqualTo(expiredSessionId) - assertThat(context.hasTimeseries).isFalse() + val session = checkNotNull(context.session) + assertThat(session.id).isNotEqualTo(expiredSessionId) + assertThat(session.hasTimeseries).isFalse() } @Test @@ -2006,7 +2048,7 @@ internal class RumSessionScopeTest { ) // Then - verify(mockTimeseriesCollector).onSessionStart(eq(testedScope.sessionId), any()) + verify(mockTimeseriesCollector).onSessionStart(eq(checkNotNull(testedScope.sessionId)), any()) } @ParameterizedTest @@ -2070,7 +2112,7 @@ internal class RumSessionScopeTest { // Then verify(mockTimeseriesCollector, times(2)).onSessionStart(any(), any()) - verify(mockTimeseriesCollector, times(2)).onSessionStop(any()) + verify(mockTimeseriesCollector).onSessionStop(any()) verify(mockTimeseriesCollector, atLeastOnce()).onRumContextUpdate(fakeViewContext) } @@ -2094,7 +2136,7 @@ internal class RumSessionScopeTest { // Then assertThat(testedScope.sessionState).isEqualTo(RumSessionState.EXPIRED) - verify(mockTimeseriesCollector, times(2)).onSessionStop(any()) + verify(mockTimeseriesCollector).onSessionStop(any()) } @Test @@ -2116,8 +2158,8 @@ internal class RumSessionScopeTest { // Then assertThat(testedScope.sessionState).isEqualTo(RumSessionState.EXPIRED) assertThat(testedScope.sessionId).isEqualTo(sessionId) - verify(mockTimeseriesCollector).onSessionStop(sessionId) - verify(mockTimeseriesCollector).onSessionStart(eq(sessionId), any()) + verify(mockTimeseriesCollector).onSessionStop(checkNotNull(sessionId)) + verify(mockTimeseriesCollector).onSessionStart(eq(checkNotNull(sessionId)), any()) } @Test @@ -2175,11 +2217,11 @@ internal class RumSessionScopeTest { // Then val context = testedScope.getRumContext() - assertThat(context.sessionId).isNotEqualTo(initialSessionId) - assertThat(context.sessionState).isEqualTo(RumSessionState.TRACKED) - assertThat(context.sessionStartReason).isEqualTo(RumSessionStartReason.MAX_DURATION) - verify(mockTimeseriesCollector).onSessionStop(initialSessionId) - verify(mockTimeseriesCollector).onSessionStart(eq(context.sessionId), any()) + assertThat(context.session?.id).isNotEqualTo(initialSessionId) + assertThat(context.session?.state).isEqualTo(RumSessionState.TRACKED) + assertThat(context.session?.startReason).isEqualTo(RumSessionStartReason.MAX_DURATION) + verify(mockTimeseriesCollector).onSessionStop(checkNotNull(initialSessionId)) + verify(mockTimeseriesCollector).onSessionStart(eq(checkNotNull(context.session?.id)), any()) } @Test @@ -2216,7 +2258,7 @@ internal class RumSessionScopeTest { ) // Then - verify(mockTimeseriesCollector, times(2)).onSessionStop(any()) + verify(mockTimeseriesCollector).onSessionStop(any()) } @Test @@ -2278,7 +2320,7 @@ internal class RumSessionScopeTest { assertThat(rumContextCaptor.firstValue.viewType).isEqualTo(RumViewType.FOREGROUND) assertThat(rumContextCaptor.firstValue.viewName).isEqualTo(fakeStartViewEvent.key.name) assertThat(rumContextCaptor.firstValue.viewId).isNotNull - assertThat(rumContextCaptor.firstValue.sessionId).isEqualTo(testedScope.sessionId) + assertThat(rumContextCaptor.firstValue.session?.id).isEqualTo(testedScope.sessionId) } @Test @@ -2296,12 +2338,12 @@ internal class RumSessionScopeTest { ) // Then - verify(mockTimeseriesCollector, times(2)).onSessionStop(any()) + verify(mockTimeseriesCollector).onSessionStop(any()) verify(mockTimeseriesCollector, times(2)).onSessionStart(any(), any()) } @Test - fun `M stop timeseries W handleEvent() { StopSession, no tracked session }`() { + fun `M ignore timeseries W handleEvent() { StopSession, no session }`() { // Given initializeTestedScope(timeseriesCollector = mockTimeseriesCollector) @@ -2314,9 +2356,7 @@ internal class RumSessionScopeTest { ) // Then - verify(mockTimeseriesCollector, never()).onSessionStart(any(), any()) - verify(mockTimeseriesCollector, times(2)).onSessionStop(any()) - verify(mockTimeseriesCollector, never()).onRumContextUpdate(any()) + verifyNoInteractions(mockTimeseriesCollector) } // endregion diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumTimeseriesSessionTransitionTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumTimeseriesSessionTransitionTest.kt index 838b6f3b9c..d5e2b77851 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumTimeseriesSessionTransitionTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumTimeseriesSessionTransitionTest.kt @@ -300,7 +300,7 @@ internal class RumTimeseriesSessionTransitionTest { fun `M send has_timeseries=true in final view W handleEvent(StopSession) { buffer not full, no pending work }`() { // Given handle(startViewEvent(fakeLongViewKey)) - val sessionId = checkNotNull(testedScope.activeSession).sessionId + val sessionId = checkNotNull(checkNotNull(testedScope.activeSession).sessionId) runSamplingTicks(fakeBufferSize - 1) // When @@ -324,7 +324,7 @@ internal class RumTimeseriesSessionTransitionTest { handle(startViewEvent(fakeNextViewKey)) // Then - val newSessionId = checkNotNull(testedScope.activeSession).sessionId + val newSessionId = checkNotNull(checkNotNull(testedScope.activeSession).sessionId) assertThat(newSessionId).isNotEqualTo(previousSessionId) assertThat(viewEvents(newSessionId)).isNotEmpty.allSatisfy { assertThat(it.session.hasTimeseries).isFalse() @@ -337,7 +337,7 @@ internal class RumTimeseriesSessionTransitionTest { val fakeTimeseriesEvent = JsonObject() whenever(mockEventFactory.create(any(), any(), any())) doReturn fakeTimeseriesEvent handle(startViewEvent(fakeLongViewKey)) - val previousSessionId = checkNotNull(testedScope.activeSession).sessionId + val previousSessionId = checkNotNull(checkNotNull(testedScope.activeSession).sessionId) runSamplingTicks(fakeBufferSize - 1) advanceTimeByNs(RumSessionScope.DEFAULT_SESSION_INACTIVITY_NS) @@ -345,7 +345,7 @@ internal class RumTimeseriesSessionTransitionTest { handle(startViewEvent(fakeNextViewKey)) // Then - val newSessionId = checkNotNull(testedScope.activeSession).sessionId + val newSessionId = checkNotNull(checkNotNull(testedScope.activeSession).sessionId) assertThat(newSessionId).isNotEqualTo(previousSessionId) verify(mockTimeseriesWriter).write(mockEventBatchWriter, fakeTimeseriesEvent, EventType.DEFAULT) assertThat(testedCollector.isTimeseriesCollected(previousSessionId)).isTrue() @@ -365,7 +365,7 @@ internal class RumTimeseriesSessionTransitionTest { eventTime = currentTime() ) ) - return checkNotNull(testedScope.activeSession).sessionId + return checkNotNull(checkNotNull(testedScope.activeSession).sessionId) } // The long view of the stopped session stays alive until its pending resource is reported as sent. diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeAttributePropagationTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeAttributePropagationTest.kt index 43b714094b..f69fb2c82d 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeAttributePropagationTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeAttributePropagationTest.kt @@ -19,6 +19,7 @@ import com.datadog.android.api.storage.EventType import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.rum.RumViewType import com.datadog.android.rum.RumAttributes import com.datadog.android.rum.RumErrorSource @@ -157,6 +158,9 @@ internal class RumViewScopeAttributePropagationTest { @Forgery lateinit var fakeParentContext: RumContext + @Forgery + private lateinit var fakeParentSession: RumSessionContext + @Forgery lateinit var fakeEventTime: Time @@ -210,6 +214,7 @@ internal class RumViewScopeAttributePropagationTest { @BeforeEach fun `set up`(forge: Forge) { + fakeParentContext = fakeParentContext.copy(session = fakeParentSession) val fakeBrightness = forge.aFloat(0f, 255f) val fakeBatteryLevel = forge.aFloat(0f, 100f) val fakeLowPowerMode = forge.aBool() diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt index d1df416f0c..aae36b31e0 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt @@ -6,6 +6,7 @@ package com.datadog.android.rum.internal.domain.scope +import android.util.Log import com.datadog.android.api.InternalLogger import com.datadog.android.api.context.DatadogContext import com.datadog.android.api.context.NetworkInfo @@ -27,6 +28,7 @@ import com.datadog.android.internal.profiling.ProfilerEvent import com.datadog.android.internal.profiling.ProfilingRumContext import com.datadog.android.internal.profiling.quota.QuotaReason import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.rum.RumViewType import com.datadog.android.internal.telemetry.InternalTelemetryEvent import com.datadog.android.internal.utils.loggableStackTrace @@ -117,6 +119,7 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.EnumSource import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock +import org.mockito.Mockito import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoSettings import org.mockito.kotlin.any @@ -220,6 +223,9 @@ internal class RumViewScopeTest { @Forgery lateinit var fakeParentContext: RumContext + @Forgery + private lateinit var fakeParentSession: RumSessionContext + lateinit var fakeEventTime: Time lateinit var fakeEvent: RumRawEvent @@ -304,6 +310,7 @@ internal class RumViewScopeTest { @BeforeEach fun `set up`(forge: Forge) { + fakeParentContext = fakeParentContext.copy(session = fakeParentSession) fakeRumSessionType = forge.aNullable { aValueFrom(RumSessionType::class.java) } fakeNetworkSettledMetricValue = forge.aNullable { aPositiveLong() } fakeInteractionToNextViewMetricValue = forge.aNullable { aPositiveLong() } @@ -439,7 +446,7 @@ internal class RumViewScopeTest { // Then assertThat(context.applicationId).isEqualTo(fakeParentContext.applicationId) - assertThat(context.sessionId).isEqualTo(fakeParentContext.sessionId) + assertThat(context.session?.id).isEqualTo(fakeParentSession.id) assertThat(context.viewId).isEqualTo(testedScope.viewId) assertThat(context.viewName).isEqualTo(fakeKey.name) assertThat(context.viewUrl).isEqualTo(fakeUrl) @@ -462,12 +469,12 @@ internal class RumViewScopeTest { // Then assertThat(context.actionId).isEqualTo(fakeActionId) assertThat(context.applicationId).isEqualTo(fakeParentContext.applicationId) - assertThat(context.sessionId).isEqualTo(fakeParentContext.sessionId) + assertThat(context.session?.id).isEqualTo(fakeParentSession.id) assertThat(context.viewId).isEqualTo(testedScope.viewId) assertThat(context.viewName).isEqualTo(fakeKey.name) assertThat(context.viewUrl).isEqualTo(fakeUrl) assertThat(context.applicationId).isEqualTo(fakeParentContext.applicationId) - assertThat(context.sessionId).isEqualTo(fakeParentContext.sessionId) + assertThat(context.session?.id).isEqualTo(fakeParentSession.id) assertThat(context.viewType).isEqualTo(fakeViewType) assertThat(context.viewTimestamp) .isEqualTo(fakeEventTime.timestamp + fakeTimeInfoAtScopeStart.serverTimeOffsetMs) @@ -488,7 +495,7 @@ internal class RumViewScopeTest { // Then assertThat(context.applicationId).isEqualTo(fakeParentContext.applicationId) - assertThat(context.sessionId).isEqualTo(fakeParentContext.sessionId) + assertThat(context.session?.id).isEqualTo(fakeParentSession.id) assertThat(context.viewId).isEqualTo(testedScope.viewId) assertThat(context.viewName).isEqualTo(fakeKey.name) assertThat(context.viewUrl).isEqualTo(fakeUrl) @@ -539,7 +546,7 @@ internal class RumViewScopeTest { // Then assertThat(context.applicationId).isEqualTo(fakeParentContext.applicationId) - assertThat(context.sessionId).isEqualTo(fakeParentContext.sessionId) + assertThat(context.session?.id).isEqualTo(fakeParentSession.id) assertThat(context.viewId).isEqualTo(testedScope.viewId) assertThat(context.viewName).isEqualTo(fakeKey.name) assertThat(context.viewUrl).isEqualTo(fakeUrl) @@ -641,12 +648,12 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasIsMainProcess(fakeDatadogContext.processInfo.isMainProcess) - hasSessionTimeseries(fakeParentContext.hasTimeseries) + hasSessionTimeseries(fakeParentSession.hasTimeseries) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) hasSource(fakeSourceViewEvent) @@ -676,7 +683,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -730,11 +737,11 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() containsExactlyContextAttributes(fakeAttributes) - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) hasSource(fakeSourceViewEvent) @@ -763,7 +770,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -814,10 +821,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) hasSource(fakeSourceViewEvent) @@ -846,7 +853,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -1102,10 +1109,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) containsExactlyContextAttributes(expectedAttributes) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) @@ -1135,7 +1142,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -1192,10 +1199,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) containsExactlyContextAttributes(expectedAttributes) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) @@ -1221,7 +1228,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -1278,10 +1285,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) containsExactlyContextAttributes(expectedAttributes) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) @@ -1307,7 +1314,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -1352,10 +1359,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) hasSource(fakeSourceViewEvent) @@ -1384,7 +1391,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -1440,10 +1447,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) hasSource(fakeSourceViewEvent) @@ -1472,7 +1479,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -1535,10 +1542,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) hasSource(fakeSourceViewEvent) @@ -1567,7 +1574,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -1626,10 +1633,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) hasSource(fakeSourceViewEvent) @@ -1658,7 +1665,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -1714,10 +1721,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) hasSource(fakeSourceViewEvent) @@ -1742,7 +1749,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -1836,10 +1843,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) containsExactlyContextAttributes(fakeAttributes) @@ -1865,7 +1872,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -1934,10 +1941,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) containsExactlyContextAttributes(fakeAttributes) @@ -1963,7 +1970,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -2047,10 +2054,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) containsExactlyContextAttributes(fakeAttributes) @@ -2076,7 +2083,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -2155,12 +2162,12 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasIsMainProcess(fakeDatadogContext.processInfo.isMainProcess) - hasSessionTimeseries(fakeParentContext.hasTimeseries) + hasSessionTimeseries(fakeParentSession.hasTimeseries) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) containsExactlyContextAttributes(fakeAttributes) @@ -2186,7 +2193,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -2236,10 +2243,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) containsExactlyContextAttributes(fakeAttributes) @@ -2265,7 +2272,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -2335,10 +2342,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) containsExactlyContextAttributes(fakeAttributes) @@ -2368,7 +2375,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -2439,10 +2446,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) containsExactlyContextAttributes(fakeAttributes) @@ -2472,7 +2479,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -2559,10 +2566,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) containsExactlyContextAttributes(fakeAttributes) @@ -2592,7 +2599,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -2672,10 +2679,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) containsExactlyContextAttributes(fakeAttributes) @@ -2705,7 +2712,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -2913,10 +2920,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.SYNTHETICS) hasSyntheticsTest(fakeTestId, fakeResultId) - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) hasSource(fakeSourceViewEvent) @@ -2945,7 +2952,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -3097,7 +3104,7 @@ internal class RumViewScopeTest { hasLongTaskCount(0) hasView(testedScope.getRumContext()) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSampleRate(fakeSampleRate) } } @@ -3244,10 +3251,10 @@ internal class RumViewScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -3331,10 +3338,10 @@ internal class RumViewScopeTest { hasUserInfo(fakeDatadogContext.userInfo) hasAccountInfo(fakeDatadogContext.accountInfo) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toAction() ?: ActionEvent.ActionEventSessionType.SYNTHETICS) hasSyntheticsTest(fakeTestId, fakeResultId) - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasSource(fakeSourceActionEvent) hasDeviceInfo( @@ -3868,9 +3875,9 @@ internal class RumViewScopeTest { hasConnectivityInfo(fakeDatadogContext.networkInfo) hasView(testedScope.viewId, testedScope.key.name, testedScope.url) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeActionId) - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) hasNoSyntheticsTest() @@ -3955,9 +3962,9 @@ internal class RumViewScopeTest { hasConnectivityInfo(fakeDatadogContext.networkInfo) hasView(testedScope.viewId, testedScope.key.name, testedScope.url) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeActionId) - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.SYNTHETICS) hasSyntheticsTest(fakeTestId, fakeResultId) @@ -4034,9 +4041,9 @@ internal class RumViewScopeTest { hasConnectivityInfo(fakeDatadogContext.networkInfo) hasView(testedScope.viewId, testedScope.key.name, testedScope.url) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeActionId) - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasSource(fakeSourceErrorEvent) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) @@ -4113,9 +4120,9 @@ internal class RumViewScopeTest { hasConnectivityInfo(fakeDatadogContext.networkInfo) hasView(testedScope.viewId, testedScope.key.name, testedScope.url) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeActionId) - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasSource(fakeSourceErrorEvent) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) @@ -4283,7 +4290,7 @@ internal class RumViewScopeTest { assertThat(firstValue.rumContext).isEqualTo( ProfilingRumContext( applicationId = fakeParentContext.applicationId, - sessionId = fakeParentContext.sessionId, + sessionId = fakeParentSession.id, viewId = testedScope.viewId, viewName = fakeKey.name ) @@ -4427,9 +4434,9 @@ internal class RumViewScopeTest { hasConnectivityInfo(fakeDatadogContext.networkInfo) hasView(testedScope.viewId, testedScope.key.name, testedScope.url) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeActionId) - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasSource(fakeSourceErrorEvent) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) @@ -4510,9 +4517,9 @@ internal class RumViewScopeTest { hasConnectivityInfo(fakeDatadogContext.networkInfo) hasView(testedScope.viewId, testedScope.key.name, testedScope.url) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeActionId) - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasSource(fakeSourceErrorEvent) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) @@ -4589,9 +4596,9 @@ internal class RumViewScopeTest { hasConnectivityInfo(fakeDatadogContext.networkInfo) hasView(testedScope.viewId, testedScope.key.name, testedScope.url) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeActionId) - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasSource(fakeSourceErrorEvent) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) @@ -4650,13 +4657,13 @@ internal class RumViewScopeTest { hasConnectivityInfo(fakeDatadogContext.networkInfo) hasView(testedScope.viewId, testedScope.key.name, testedScope.url) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeActionId) hasErrorType(throwable.javaClass.canonicalName) hasErrorSourceType(sourceType.toSchemaSourceType()) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasSource(fakeSourceErrorEvent) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) @@ -4734,13 +4741,13 @@ internal class RumViewScopeTest { hasConnectivityInfo(fakeDatadogContext.networkInfo) hasView(testedScope.viewId, testedScope.key.name, testedScope.url) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeActionId) hasErrorType(null) hasErrorSourceType(sourceType.toSchemaSourceType()) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasSource(fakeSourceErrorEvent) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) @@ -4818,9 +4825,9 @@ internal class RumViewScopeTest { hasConnectivityInfo(fakeDatadogContext.networkInfo) hasView(testedScope.viewId, testedScope.key.name, testedScope.url) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasIsMainProcess(fakeDatadogContext.processInfo.isMainProcess) - hasSessionTimeseries(fakeParentContext.hasTimeseries) + hasSessionTimeseries(fakeParentSession.hasTimeseries) hasActionId(fakeActionId) hasErrorType(null) hasErrorSourceType(sourceType.toSchemaSourceType()) @@ -4828,7 +4835,7 @@ internal class RumViewScopeTest { hasTimeSinceAppStart(TimeUnit.NANOSECONDS.toMillis(timeSinceAppStart)) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasSource(fakeSourceErrorEvent) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) @@ -4883,10 +4890,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) containsExactlyContextAttributes(fakeAttributes) @@ -4911,7 +4918,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) } } verifyNoMoreInteractions(mockWriter) @@ -4969,9 +4976,9 @@ internal class RumViewScopeTest { hasConnectivityInfo(fakeDatadogContext.networkInfo) hasView(testedScope.viewId, testedScope.key.name, testedScope.url) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeActionId) - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasSource(fakeSourceErrorEvent) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) @@ -5050,7 +5057,7 @@ internal class RumViewScopeTest { hasConnectivityInfo(fakeDatadogContext.networkInfo) hasView(testedScope.viewId, testedScope.key.name, testedScope.url) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeActionId) hasErrorType(throwable.javaClass.canonicalName) hasErrorSourceType(sourceType.toSchemaSourceType()) @@ -5058,7 +5065,7 @@ internal class RumViewScopeTest { hasTimeSinceAppStart(TimeUnit.NANOSECONDS.toMillis(timeSinceAppStart)) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasSource(fakeSourceErrorEvent) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) @@ -5113,10 +5120,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) containsExactlyContextAttributes(fakeAttributes) @@ -5141,7 +5148,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) } } verifyNoMoreInteractions(mockWriter) @@ -5211,7 +5218,7 @@ internal class RumViewScopeTest { hasConnectivityInfo(fakeDatadogContext.networkInfo) hasView(testedScope.viewId, testedScope.key.name, testedScope.url) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeActionId) hasErrorType(throwable.javaClass.canonicalName) hasErrorSourceType(sourceType.toSchemaSourceType()) @@ -5221,7 +5228,7 @@ internal class RumViewScopeTest { hasTimeSinceAppStart(null) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasDeviceInfo( fakeDatadogContext.deviceInfo.deviceName, @@ -5273,10 +5280,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) containsExactlyContextAttributes(fakeAttributes) @@ -5301,7 +5308,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) } } verifyNoMoreInteractions(mockWriter) @@ -5354,7 +5361,7 @@ internal class RumViewScopeTest { hasConnectivityInfo(fakeDatadogContext.networkInfo) hasView(testedScope.viewId, testedScope.key.name, testedScope.url) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeActionId) hasErrorType(throwable.javaClass.canonicalName) hasErrorSourceType(sourceType.toSchemaSourceType()) @@ -5362,7 +5369,7 @@ internal class RumViewScopeTest { hasTimeSinceAppStart(null) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasSource(fakeSourceErrorEvent) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) @@ -5443,13 +5450,13 @@ internal class RumViewScopeTest { hasConnectivityInfo(fakeDatadogContext.networkInfo) hasView(testedScope.viewId, testedScope.key.name, testedScope.url) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasActionId(fakeActionId) hasErrorType(errorType) hasErrorSourceType(sourceType.toSchemaSourceType()) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasSource(fakeSourceErrorEvent) hasSessionType(fakeRumSessionType?.toError() ?: ErrorEvent.ErrorEventSessionType.USER) @@ -5688,8 +5695,8 @@ internal class RumViewScopeTest { hasConnectivityInfo(fakeDatadogContext.networkInfo) hasView(testedScope.viewId, testedScope.url) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) - hasStartReason(fakeParentContext.sessionStartReason) + hasSessionId(fakeParentSession.id) + hasStartReason(fakeParentSession.startReason) hasSessionType(fakeRumSessionType?.toLongTask() ?: LongTaskEvent.LongTaskEventSessionType.USER) hasNoSyntheticsTest() hasReplay(fakeHasReplay) @@ -5750,8 +5757,8 @@ internal class RumViewScopeTest { hasConnectivityInfo(fakeDatadogContext.networkInfo) hasView(testedScope.viewId, testedScope.url) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) - hasStartReason(fakeParentContext.sessionStartReason) + hasSessionId(fakeParentSession.id) + hasStartReason(fakeParentSession.startReason) hasSessionType(fakeRumSessionType?.toLongTask() ?: LongTaskEvent.LongTaskEventSessionType.USER) hasNoSyntheticsTest() hasReplay(fakeHasReplay) @@ -5821,8 +5828,8 @@ internal class RumViewScopeTest { hasConnectivityInfo(fakeDatadogContext.networkInfo) hasView(testedScope.viewId, testedScope.url) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) - hasStartReason(fakeParentContext.sessionStartReason) + hasSessionId(fakeParentSession.id) + hasStartReason(fakeParentSession.startReason) hasSessionType( fakeRumSessionType?.toLongTask() ?: LongTaskEvent.LongTaskEventSessionType.SYNTHETICS ) @@ -5893,8 +5900,8 @@ internal class RumViewScopeTest { hasConnectivityInfo(fakeDatadogContext.networkInfo) hasView(testedScope.viewId, testedScope.url) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) - hasStartReason(fakeParentContext.sessionStartReason) + hasSessionId(fakeParentSession.id) + hasStartReason(fakeParentSession.startReason) hasSessionType( fakeRumSessionType?.toLongTask() ?: LongTaskEvent.LongTaskEventSessionType.SYNTHETICS ) @@ -6065,7 +6072,7 @@ internal class RumViewScopeTest { mapOf( FeatureContextKeys.PROFILER_IS_RUNNING to false, FeatureContextKeys.PROFILING_QUOTA_REASON to fakeQuotaReason, - FeatureContextKeys.PROFILING_QUOTA_SESSION_ID to fakeParentContext.sessionId + FeatureContextKeys.PROFILING_QUOTA_SESSION_ID to fakeParentSession.id ) ) } @@ -6193,7 +6200,7 @@ internal class RumViewScopeTest { mapOf( FeatureContextKeys.PROFILER_IS_RUNNING to false, FeatureContextKeys.PROFILING_QUOTA_REASON to fakeQuotaReason, - FeatureContextKeys.PROFILING_QUOTA_SESSION_ID to fakeParentContext.sessionId + FeatureContextKeys.PROFILING_QUOTA_SESSION_ID to fakeParentSession.id ) ) } @@ -6273,7 +6280,7 @@ internal class RumViewScopeTest { assertThat(firstValue.rumContext).isEqualTo( ProfilingRumContext( applicationId = fakeParentContext.applicationId, - sessionId = fakeParentContext.sessionId, + sessionId = fakeParentSession.id, viewId = testedScope.viewId, viewName = fakeKey.name ) @@ -6447,7 +6454,7 @@ internal class RumViewScopeTest { mapOf( FeatureContextKeys.PROFILER_IS_RUNNING to false, FeatureContextKeys.PROFILING_QUOTA_REASON to fakeQuotaReason, - FeatureContextKeys.PROFILING_QUOTA_SESSION_ID to fakeParentContext.sessionId + FeatureContextKeys.PROFILING_QUOTA_SESSION_ID to fakeParentSession.id ) ) } @@ -6567,7 +6574,7 @@ internal class RumViewScopeTest { mapOf( FeatureContextKeys.PROFILER_IS_RUNNING to false, FeatureContextKeys.PROFILING_QUOTA_REASON to fakeQuotaReason, - FeatureContextKeys.PROFILING_QUOTA_SESSION_ID to fakeParentContext.sessionId + FeatureContextKeys.PROFILING_QUOTA_SESSION_ID to fakeParentSession.id ) ) } @@ -6695,7 +6702,7 @@ internal class RumViewScopeTest { mapOf( FeatureContextKeys.PROFILER_IS_RUNNING to false, FeatureContextKeys.PROFILING_QUOTA_REASON to fakeQuotaReason, - FeatureContextKeys.PROFILING_QUOTA_SESSION_ID to fakeParentContext.sessionId + FeatureContextKeys.PROFILING_QUOTA_SESSION_ID to fakeParentSession.id ) ) } @@ -6911,10 +6918,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) hasSource(fakeSourceViewEvent) @@ -6939,7 +6946,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -7006,10 +7013,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) containsExactlyContextAttributes(fakeAttributes) @@ -7069,10 +7076,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) containsExactlyContextAttributes(fakeAttributes) @@ -7098,7 +7105,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -7175,10 +7182,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) hasSource(fakeSourceViewEvent) @@ -7206,7 +7213,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -7275,10 +7282,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) hasSource(fakeSourceViewEvent) @@ -7306,7 +7313,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -7378,10 +7385,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) hasSource(fakeSourceViewEvent) @@ -7409,7 +7416,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -7463,10 +7470,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) hasSource(fakeSourceViewEvent) @@ -7494,7 +7501,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -7758,10 +7765,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) containsExactlyContextAttributes(fakeAttributes) @@ -7787,7 +7794,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -7843,10 +7850,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) containsExactlyContextAttributes(fakeAttributes) @@ -7872,7 +7879,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -7927,10 +7934,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) containsExactlyContextAttributes(fakeAttributes) @@ -7956,7 +7963,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -8021,10 +8028,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) containsExactlyContextAttributes(fakeAttributes) @@ -8050,7 +8057,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -8115,10 +8122,10 @@ internal class RumViewScopeTest { hasAccountInfo(fakeDatadogContext.accountInfo) hasViewId(testedScope.viewId) hasApplicationId(fakeParentContext.applicationId) - hasSessionId(fakeParentContext.sessionId) + hasSessionId(fakeParentSession.id) hasSessionType(fakeRumSessionType?.toView() ?: ViewEvent.ViewEventSessionType.USER) hasNoSyntheticsTest() - hasStartReason(fakeParentContext.sessionStartReason) + hasStartReason(fakeParentSession.startReason) hasReplay(fakeHasReplay) hasReplayStats(fakeReplayStats) containsExactlyContextAttributes(fakeAttributes) @@ -8148,7 +8155,7 @@ internal class RumViewScopeTest { hasVersion(fakeDatadogContext.version) hasBuildVersion(fakeDatadogContext.versionCode) hasBuildId(fakeDatadogContext.appBuildId) - hasSessionActive(fakeParentContext.isSessionActive) + hasSessionActive(fakeParentSession.isActive) hasSampleRate(fakeSampleRate) } } @@ -9386,7 +9393,7 @@ internal class RumViewScopeTest { fun `M set view to inactive and send update W handleEvent { StopSession }`() { // Given whenever(mockParentScope.getRumContext()) - .doReturn(fakeParentContext.copy(isSessionActive = false)) + .doReturn(fakeParentContext.copy(session = fakeParentSession.copy(isActive = false))) // When testedScope.handleEvent( @@ -10283,11 +10290,11 @@ internal class RumViewScopeTest { .hasDate(event.eventTime.timestamp + fakeTimeInfoAtScopeStart.serverTimeOffsetMs) .hasApplicationId(fakeParentContext.applicationId) .containsExactlyContextAttributes(expectedAttributes) - .hasStartReason(fakeParentContext.sessionStartReason) + .hasStartReason(fakeParentSession.startReason) .hasSampleRate(fakeSampleRate) - .hasSessionId(fakeParentContext.sessionId) + .hasSessionId(fakeParentSession.id) .hasIsMainProcess(fakeDatadogContext.processInfo.isMainProcess) - .hasSessionTimeseries(fakeParentContext.hasTimeseries) + .hasSessionTimeseries(fakeParentSession.hasTimeseries) .hasSessionType( fakeRumSessionType?.toVital() ?: VitalOperationStepEvent.VitalOperationStepEventSessionType.USER @@ -10360,9 +10367,9 @@ internal class RumViewScopeTest { .hasDate(event.eventTime.timestamp + fakeTimeInfoAtScopeStart.serverTimeOffsetMs) .hasApplicationId(fakeParentContext.applicationId) .containsExactlyContextAttributes(expectedAttributes) - .hasStartReason(fakeParentContext.sessionStartReason) + .hasStartReason(fakeParentSession.startReason) .hasSampleRate(fakeSampleRate) - .hasSessionId(fakeParentContext.sessionId) + .hasSessionId(fakeParentSession.id) .hasSessionType( fakeRumSessionType?.toVital() ?: VitalOperationStepEvent.VitalOperationStepEventSessionType.SYNTHETICS @@ -10432,9 +10439,9 @@ internal class RumViewScopeTest { .hasDate(event.eventTime.timestamp + fakeTimeInfoAtScopeStart.serverTimeOffsetMs) .hasApplicationId(fakeParentContext.applicationId) .containsExactlyContextAttributes(expectedAttributes) - .hasStartReason(fakeParentContext.sessionStartReason) + .hasStartReason(fakeParentSession.startReason) .hasSampleRate(fakeSampleRate) - .hasSessionId(fakeParentContext.sessionId) + .hasSessionId(fakeParentSession.id) .hasSessionType( fakeRumSessionType?.toVital() ?: VitalOperationStepEvent.VitalOperationStepEventSessionType.USER @@ -10507,9 +10514,9 @@ internal class RumViewScopeTest { .hasDate(event.eventTime.timestamp + fakeTimeInfoAtScopeStart.serverTimeOffsetMs) .hasApplicationId(fakeParentContext.applicationId) .containsExactlyContextAttributes(expectedAttributes) - .hasStartReason(fakeParentContext.sessionStartReason) + .hasStartReason(fakeParentSession.startReason) .hasSampleRate(fakeSampleRate) - .hasSessionId(fakeParentContext.sessionId) + .hasSessionId(fakeParentSession.id) .hasSessionType( fakeRumSessionType?.toVital() ?: VitalOperationStepEvent.VitalOperationStepEventSessionType.SYNTHETICS @@ -10580,9 +10587,9 @@ internal class RumViewScopeTest { .hasDate(event.eventTime.timestamp + fakeTimeInfoAtScopeStart.serverTimeOffsetMs) .hasApplicationId(fakeParentContext.applicationId) .containsExactlyContextAttributes(expectedAttributes) - .hasStartReason(fakeParentContext.sessionStartReason) + .hasStartReason(fakeParentSession.startReason) .hasSampleRate(fakeSampleRate) - .hasSessionId(fakeParentContext.sessionId) + .hasSessionId(fakeParentSession.id) .hasSessionType( fakeRumSessionType?.toVital() ?: VitalOperationStepEvent.VitalOperationStepEventSessionType.USER @@ -10657,10 +10664,10 @@ internal class RumViewScopeTest { .hasDate(event.eventTime.timestamp + fakeTimeInfoAtScopeStart.serverTimeOffsetMs) .hasApplicationId(fakeParentContext.applicationId) .containsExactlyContextAttributes(expectedAttributes) - .hasStartReason(fakeParentContext.sessionStartReason) + .hasStartReason(fakeParentSession.startReason) .hasSampleRate(fakeSampleRate) .hasSyntheticsTest(fakeTestId, fakeResultId) - .hasSessionId(fakeParentContext.sessionId) + .hasSessionId(fakeParentSession.id) .hasSessionType( fakeRumSessionType?.toVital() ?: VitalOperationStepEvent.VitalOperationStepEventSessionType.SYNTHETICS @@ -10737,7 +10744,7 @@ internal class RumViewScopeTest { assertThat(capturedProfilerEvent.rumContext).isEqualTo( ProfilingRumContext( applicationId = fakeParentContext.applicationId, - sessionId = fakeParentContext.sessionId, + sessionId = fakeParentSession.id, viewId = testedScope.viewId, viewName = fakeKey.name ) @@ -10781,7 +10788,7 @@ internal class RumViewScopeTest { assertThat(capturedProfilerEvent.rumContext).isEqualTo( ProfilingRumContext( applicationId = fakeParentContext.applicationId, - sessionId = fakeParentContext.sessionId, + sessionId = fakeParentSession.id, viewId = testedScope.viewId, viewName = fakeKey.name ) @@ -11059,6 +11066,42 @@ internal class RumViewScopeTest { // endregion + @Test + fun `M omit synthetics correlation W initialization {no session}`(@StringForgery fakeTestId: String) { + // Given + fakeParentContext = fakeParentContext.copy( + session = null, + syntheticsTestId = fakeTestId + ) + whenever(mockParentScope.getRumContext()) doReturn fakeParentContext + Mockito.mockStatic(Log::class.java).use { mockLog -> + // When + testedScope = newRumViewScope() + + // Then + mockLog.verifyNoInteractions() + } + } + + @Test + fun `M log synthetics correlation W initialization {session exists}`(@StringForgery fakeTestId: String) { + // Given + fakeParentContext = fakeParentContext.copy(syntheticsTestId = fakeTestId) + whenever(mockParentScope.getRumContext()) doReturn fakeParentContext + Mockito.mockStatic(Log::class.java).use { mockLog -> + // When + testedScope = newRumViewScope() + + // Then + mockLog.verify { Log.i("DatadogSynthetics", "_dd.application.id=${fakeParentContext.applicationId}") } + mockLog.verify { + Log.i("DatadogSynthetics", "_dd.session.id=${fakeParentSession.id}") + } + mockLog.verify { Log.i("DatadogSynthetics", "_dd.view.id=${testedScope.viewId}") } + mockLog.verifyNoMoreInteractions() + } + } + private fun newRumViewScope( parentScope: RumScope = mockParentScope, sdkCore: InternalSdkCore = rumMonitorConfiguration.mockSdkCore, diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitorTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitorTest.kt index 1db1fe6986..8bb983c229 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitorTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitorTest.kt @@ -24,6 +24,7 @@ import com.datadog.android.core.sampling.Sampler import com.datadog.android.heatmaps.CrossPlatformHeatmapActionData import com.datadog.android.internal.FeatureContextKeys import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.rum.RumSessionState import com.datadog.android.internal.telemetry.InternalTelemetryEvent import com.datadog.android.rum.DdRumContentProvider @@ -52,6 +53,7 @@ import com.datadog.android.rum.internal.domain.scope.RumScopeKey import com.datadog.android.rum.internal.domain.scope.RumSessionScope import com.datadog.android.rum.internal.domain.scope.RumViewManagerScope import com.datadog.android.rum.internal.domain.scope.RumViewScope +import com.datadog.android.rum.internal.domain.setRumContext import com.datadog.android.rum.internal.domain.state.ViewUIPerformanceReport import com.datadog.android.rum.internal.heatmaps.NativeHeatmapActionData import com.datadog.android.rum.internal.instrumentation.insights.InsightsCollector @@ -96,6 +98,7 @@ import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.api.extension.Extensions import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.EnumSource +import org.junit.jupiter.params.provider.ValueSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoSettings @@ -118,10 +121,14 @@ import org.mockito.quality.Strictness import java.util.Locale import java.util.UUID import java.util.concurrent.Callable +import java.util.concurrent.CancellationException +import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import java.util.concurrent.Future +import java.util.concurrent.FutureTask import java.util.concurrent.LinkedBlockingQueue +import java.util.concurrent.RejectedExecutionException import java.util.concurrent.ScheduledThreadPoolExecutor import java.util.concurrent.TimeUnit @@ -257,9 +264,9 @@ internal class DatadogRumMonitorTest { it.getArgument(0).run() mock>() } - whenever(mockExecutorService.submit(any>())) doAnswer { - val rumContext = it.getArgument>(0).call() - mock>().apply { whenever(get()) doReturn rumContext } + whenever(mockExecutorService.submit(any>())) doAnswer { + val rumContext = it.getArgument>(0).call() + mock>().apply { whenever(get()) doReturn rumContext } } whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger @@ -638,13 +645,16 @@ internal class DatadogRumMonitorTest { @Test fun `M return current view URL W getCurrentViewUrl()`( @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext, @Forgery type: RumActionType, @StringForgery name: String ) { // Given val fakeContextWithSession = fakeRumContext.copy( - sessionId = UUID.randomUUID().toString(), - sessionState = RumSessionState.TRACKED + session = fakeSession.copy( + id = UUID.randomUUID().toString(), + state = RumSessionState.TRACKED + ) ) val mockSessionScope = mock() val mockViewScope = mock() @@ -663,13 +673,16 @@ internal class DatadogRumMonitorTest { @Test fun `M return null W getCurrentViewUrl() { no active session }`( @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext, @Forgery type: RumActionType, @StringForgery name: String ) { // Given val primeContext = fakeRumContext.copy( - sessionId = UUID.randomUUID().toString(), - sessionState = RumSessionState.TRACKED + session = fakeSession.copy( + id = UUID.randomUUID().toString(), + state = RumSessionState.TRACKED + ) ) val mockSessionScope = mock() val mockViewScope = mock() @@ -690,13 +703,16 @@ internal class DatadogRumMonitorTest { @Test fun `M return null W getCurrentViewUrl() { session exists but has NULL_UUID, not yet sampled in }`( @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext, @Forgery type: RumActionType, @StringForgery name: String ) { // Given val primeContext = fakeRumContext.copy( - sessionId = UUID.randomUUID().toString(), - sessionState = RumSessionState.TRACKED + session = fakeSession.copy( + id = UUID.randomUUID().toString(), + state = RumSessionState.TRACKED + ) ) val mockSessionScope = mock() val mockViewScope = mock() @@ -705,7 +721,7 @@ internal class DatadogRumMonitorTest { whenever(mockViewScope.getRumContext()) doReturn primeContext testedMonitor.startAction(type, name, fakeAttributes) whenever(mockSessionScope.activeView) doReturn null - whenever(mockSessionScope.getRumContext()) doReturn RumContext(sessionId = RumContext.NULL_UUID) + whenever(mockSessionScope.getRumContext()) doReturn RumContext(applicationId = fakeApplicationId) testedMonitor.startAction(type, name, fakeAttributes) // When @@ -718,13 +734,16 @@ internal class DatadogRumMonitorTest { @Test fun `M return null W getCurrentViewUrl() { view stopped with no new view started }`( @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext, @Forgery type: RumActionType, @StringForgery name: String ) { // Given val primeContext = fakeRumContext.copy( - sessionId = UUID.randomUUID().toString(), - sessionState = RumSessionState.TRACKED + session = fakeSession.copy( + id = UUID.randomUUID().toString(), + state = RumSessionState.TRACKED + ) ) val mockSessionScope = mock() val mockViewScope = mock() @@ -1500,7 +1519,7 @@ internal class DatadogRumMonitorTest { // Then // the crash is handled on the context thread: the extra hop would only add latency and expose the // event to the RUM executor back-pressure, and ordering is already guaranteed by the context thread - verify(mockExecutorService, never()).submit(any>()) + verify(mockExecutorService, never()).submit(any>()) verify(mockApplicationScope).handleEvent( any(), same(fakeDatadogContext), @@ -2453,7 +2472,10 @@ internal class DatadogRumMonitorTest { val viewScopes = forge.aList { mock().apply { whenever(getRumContext()) doReturn - RumContext(viewName = forge.aNullable { forge.anAlphaNumericalString() }) + RumContext( + applicationId = fakeApplicationId, + viewName = forge.aNullable { forge.anAlphaNumericalString() } + ) whenever(isActive()) doReturn true } @@ -2486,7 +2508,10 @@ internal class DatadogRumMonitorTest { val viewScopes = forge.aList { mock().apply { whenever(getRumContext()) doReturn - RumContext(viewName = forge.aNullable { forge.anAlphaNumericalString() }) + RumContext( + applicationId = fakeApplicationId, + viewName = forge.aNullable { forge.anAlphaNumericalString() } + ) whenever(isActive()) doReturn false } @@ -2768,6 +2793,7 @@ internal class DatadogRumMonitorTest { fun `M notify expiration checker with session state W addAction()`( fakeState: RumSessionState, @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext, @Forgery fakeType: RumActionType, @StringForgery fakeName: String ) { @@ -2776,10 +2802,8 @@ internal class DatadogRumMonitorTest { testedMonitor.sessionExpirationChecker = mockChecker val mockSessionScope = mock() whenever(mockApplicationScope.activeSession) doReturn mockSessionScope - whenever(mockSessionScope.getRumContext()) doReturn fakeRumContext.copy( - sessionId = UUID.randomUUID().toString(), - sessionState = fakeState - ) + whenever(mockSessionScope.getRumContext()) doReturn + fakeRumContext.copy(session = fakeSession.copy(state = fakeState)) // When testedMonitor.addAction(fakeType, fakeName, fakeAttributes) @@ -3118,9 +3142,11 @@ internal class DatadogRumMonitorTest { @Test fun `M update feature context W handleEvent() { active view exists }`( @Forgery fakeRumEvent: RumRawEvent, - @Forgery fakeRumContext: RumContext + @Forgery fakeBaseRumContext: RumContext, + @Forgery fakeSession: RumSessionContext ) { // Given + val fakeRumContext = fakeBaseRumContext.copy(session = fakeSession) val mockApplicationScope = mock() val mockSessionScope = mock() val mockViewScope = mock() @@ -3134,7 +3160,7 @@ internal class DatadogRumMonitorTest { // Then argumentCaptor<(MutableMap) -> Unit> { - verify(mockSdkCore).updateFeatureContext(eq(Feature.RUM_FEATURE_NAME), any(), capture()) + verify(mockSdkCore).updateFeatureContext(eq(Feature.RUM_FEATURE_NAME), eq(false), capture()) val acc = mutableMapOf() firstValue.invoke(acc) assertThat(acc[FeatureContextKeys.RUM_CONTEXT]).isSameAs(fakeRumContext) @@ -3144,9 +3170,11 @@ internal class DatadogRumMonitorTest { @Test fun `M update feature context W handleEvent() { no active view, but active session }`( @Forgery fakeRumEvent: RumRawEvent, - @Forgery fakeRumContext: RumContext + @Forgery fakeBaseRumContext: RumContext, + @Forgery fakeSession: RumSessionContext ) { // Given + val fakeRumContext = fakeBaseRumContext.copy(session = fakeSession) val mockApplicationScope = mock() val mockSessionScope = mock() whenever(mockSessionScope.activeView) doReturn null @@ -3159,16 +3187,18 @@ internal class DatadogRumMonitorTest { // Then argumentCaptor<(MutableMap) -> Unit> { - verify(mockSdkCore).updateFeatureContext(eq(Feature.RUM_FEATURE_NAME), any(), capture()) + verify(mockSdkCore).updateFeatureContext(eq(Feature.RUM_FEATURE_NAME), eq(false), capture()) val acc = mutableMapOf() firstValue.invoke(acc) assertThat(acc[FeatureContextKeys.RUM_CONTEXT]).isSameAs(fakeRumContext) } } - @Test - fun `M clear feature context W handleEvent() { no active session }`( - @Forgery fakeRumEvent: RumRawEvent + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun `M retain application id W handleEvent() { no active session }`( + isFatal: Boolean, + @Forgery fakeRumEvent: RumRawEvent.AddError ) { // Given val mockApplicationScope = mock() @@ -3176,54 +3206,189 @@ internal class DatadogRumMonitorTest { testedMonitor.rootScope = mockApplicationScope // When - testedMonitor.handleEvent(fakeRumEvent) + testedMonitor.handleEvent(fakeRumEvent.copy(isFatal = isFatal)) // Then argumentCaptor<(MutableMap) -> Unit> { - verify(mockSdkCore).updateFeatureContext(eq(Feature.RUM_FEATURE_NAME), any(), capture()) - val acc = mutableMapOf("stale_key" to "stale_value") + verify(mockSdkCore).updateFeatureContext(eq(Feature.RUM_FEATURE_NAME), eq(false), capture()) + val acc = mutableMapOf( + FeatureContextKeys.RUM_SESSION_ID to "stale_session", + "view_id" to "stale_view", + "action_id" to "stale_action" + ) firstValue.invoke(acc) - assertThat(acc).isEmpty() + assertThat(acc).isEqualTo( + mapOf( + FeatureContextKeys.RUM_CONTEXT to RumContext(fakeApplicationId), + "application_id" to fakeApplicationId + ) + ) } } @Test - fun `M not update feature context W handleEvent() { event processing failed }`( - @Forgery fakeRumEvent: RumRawEvent + fun `M preserve feature context W handleEvent() {submission rejected}`( + @Forgery fakeRumEvent: RumRawEvent.AddError ) { // Given - val mockFeatureScope = mock() - whenever(mockSdkCore.getFeature(Feature.RUM_FEATURE_NAME)) doReturn mockFeatureScope whenever(mockExecutorService.submit(any>())) doAnswer { - mock>().apply { whenever(get()) doReturn null } + throw RejectedExecutionException() } // When - testedMonitor.handleEvent(fakeRumEvent) + testedMonitor.handleEvent(fakeRumEvent.copy(isFatal = false)) // Then + verify(mockExecutorService).submit(any>()) verify(mockSdkCore, never()).updateFeatureContext(eq(Feature.RUM_FEATURE_NAME), any(), any()) + verifyNoInteractions(mockApplicationScope) + } + + @ParameterizedTest + @EnumSource(FutureFailure::class) + fun `M retain only application context W handleEvent() {future unsuccessful}`( + failure: FutureFailure, + @Forgery fakeRumEvent: RumRawEvent.AddError, + @Forgery fakeRumContext: RumContext + ) { + // Given + whenever(mockExecutorService.submit(any>())) doAnswer { + mock>().apply { + whenever(get()) doAnswer { + when (failure) { + FutureFailure.CANCELLED -> throw CancellationException() + FutureFailure.INTERRUPTED -> throw InterruptedException() + FutureFailure.FAILED -> throw ExecutionException(IllegalStateException()) + } + } + } + } + + // When + testedMonitor.handleEvent(fakeRumEvent.copy(isFatal = false)) + + // Then + argumentCaptor<(MutableMap) -> Unit> { + verify(mockSdkCore).updateFeatureContext(eq(Feature.RUM_FEATURE_NAME), eq(false), capture()) + val fakeSharedContext = mutableMapOf().apply { setRumContext(fakeRumContext) } + firstValue.invoke(fakeSharedContext) + assertThat(fakeSharedContext).isEqualTo( + mapOf( + FeatureContextKeys.RUM_CONTEXT to RumContext(fakeApplicationId), + "application_id" to fakeApplicationId + ) + ) + } } @Test - fun `M clear feature context W handleEvent() { session id is NULL_UUID }`( - @Forgery fakeRumEvent: RumRawEvent, + fun `M clear stale correlation W stopSession() {processing fails after session stops}`( @Forgery fakeRumContext: RumContext ) { // Given - whenever(mockApplicationScope.getRumContext()) doReturn fakeRumContext.copy( - sessionId = RumContext.NULL_UUID - ) + val mockSessionScope = mock() + whenever(mockSessionScope.getRumContext()) doReturn fakeRumContext + whenever(mockApplicationScope.activeSession) doReturn mockSessionScope + whenever(mockExecutorService.submit(any>())) doAnswer { + FutureTask(it.getArgument>(0)).apply { run() } + } + whenever(mockApplicationScope.handleEvent(any(), any(), any(), any())) doAnswer { + whenever(mockApplicationScope.activeSession) doReturn null + error("Event processing failed after stopping the session") + } // When - testedMonitor.handleEvent(fakeRumEvent) + testedMonitor.stopSession() + + // Then + assertThat(mockApplicationScope.activeSession).isNull() + argumentCaptor<(MutableMap) -> Unit> { + verify(mockSdkCore).updateFeatureContext(eq(Feature.RUM_FEATURE_NAME), eq(false), capture()) + val fakeSharedContext = mutableMapOf().apply { setRumContext(fakeRumContext) } + firstValue.invoke(fakeSharedContext) + assertThat(fakeSharedContext).isEqualTo( + mapOf( + FeatureContextKeys.RUM_CONTEXT to RumContext(fakeApplicationId), + "application_id" to fakeApplicationId + ) + ) + } + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun `M retain application id W handleEvent() { session id is absent }`( + isFatal: Boolean, + @Forgery fakeRumEvent: RumRawEvent.AddError, + @Forgery fakeRumContext: RumContext + ) { + // Given + val mockSessionScope = mock() + whenever(mockApplicationScope.activeSession) doReturn mockSessionScope + whenever(mockSessionScope.getRumContext()) doReturn fakeRumContext.copy(session = null) + + // When + testedMonitor.handleEvent(fakeRumEvent.copy(isFatal = isFatal)) // Then argumentCaptor<(MutableMap) -> Unit> { - verify(mockSdkCore).updateFeatureContext(eq(Feature.RUM_FEATURE_NAME), any(), capture()) - val acc = mutableMapOf("stale_key" to "stale_value") + verify(mockSdkCore).updateFeatureContext(eq(Feature.RUM_FEATURE_NAME), eq(false), capture()) + val acc = mutableMapOf().apply { setRumContext(fakeRumContext) } firstValue.invoke(acc) - assertThat(acc).isEmpty() + assertThat(acc).isEqualTo( + mapOf( + FeatureContextKeys.RUM_CONTEXT to RumContext(fakeApplicationId), + "application_id" to fakeApplicationId + ) + ) + } + } + + @Test + fun `M publish session context changes W handleEvent() {active, absent, renewed session}`( + @Forgery fakeRumContext: RumContext, + @Forgery fakeNewRumContext: RumContext, + @StringForgery fakeViewKey: String + ) { + // Given + val fakeActiveContext = fakeRumContext.copy(applicationId = fakeApplicationId) + val fakeNewContext = fakeNewRumContext.copy(applicationId = fakeApplicationId) + val mockSessionScope = mock() + val mockNewSessionScope = mock() + whenever(mockSessionScope.getRumContext()) doReturn fakeActiveContext + whenever(mockNewSessionScope.getRumContext()) doReturn fakeNewContext + whenever(mockApplicationScope.activeSession) doReturn mockSessionScope + testedMonitor.startView(fakeViewKey, fakeViewKey) + + // When + whenever(mockApplicationScope.activeSession) doReturn null + testedMonitor.stopSession() + whenever(mockApplicationScope.activeSession) doReturn mockNewSessionScope + testedMonitor.startView(fakeViewKey, fakeViewKey) + + // Then + argumentCaptor<(MutableMap) -> Unit> { + verify(mockSdkCore, times(3)).updateFeatureContext(eq(Feature.RUM_FEATURE_NAME), eq(false), capture()) + val fakeSharedContext = mutableMapOf() + allValues[0].invoke(fakeSharedContext) + assertThat(fakeSharedContext).isEqualTo( + mutableMapOf().apply { + setRumContext(fakeActiveContext) + } + ) + allValues[1].invoke(fakeSharedContext) + assertThat(fakeSharedContext).isEqualTo( + mapOf( + FeatureContextKeys.RUM_CONTEXT to RumContext(fakeApplicationId), + "application_id" to fakeApplicationId + ) + ) + allValues[2].invoke(fakeSharedContext) + assertThat(fakeSharedContext).isEqualTo( + mutableMapOf().apply { + setRumContext(fakeNewContext) + } + ) } } @@ -3734,6 +3899,12 @@ internal class DatadogRumMonitorTest { verifyNoMoreInteractions(mockWriter) } + enum class FutureFailure { + CANCELLED, + FAILED, + INTERRUPTED + } + companion object { const val TIMESTAMP_MIN = 1000000000000 const val TIMESTAMP_MAX = 2000000000000 diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/startup/RumSessionScopeStartupManagerTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/startup/RumSessionScopeStartupManagerTest.kt index 7d4b3ca329..5e7b57ba03 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/startup/RumSessionScopeStartupManagerTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/startup/RumSessionScopeStartupManagerTest.kt @@ -23,6 +23,7 @@ import com.datadog.android.internal.profiling.ProfilerEvent import com.datadog.android.internal.profiling.ProfilingRumContext import com.datadog.android.internal.profiling.quota.QuotaReason import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.rum.RumSessionType import com.datadog.android.rum.assertj.VitalAppLaunchEventAssert.Companion.assertThat import com.datadog.android.rum.assertj.VitalAppLaunchPropertiesAssert.Companion.assertThat @@ -106,6 +107,9 @@ internal class RumSessionScopeStartupManagerTest { @Forgery lateinit var rumContext: RumContext + @Forgery + private lateinit var fakeSession: RumSessionContext + @Forgery lateinit var fakeTimeInfo: TimeInfo @@ -137,6 +141,7 @@ internal class RumSessionScopeStartupManagerTest { @BeforeEach fun `set up`(forge: Forge) { + rumContext = rumContext.copy(session = fakeSession) fakeParentAttributes = forge.exhaustiveAttributes() fakeRumSessionType = forge.aNullable { aValueFrom(RumSessionType::class.java) } @@ -269,7 +274,7 @@ internal class RumSessionScopeStartupManagerTest { mapOf( FeatureContextKeys.PROFILER_IS_RUNNING to true, FeatureContextKeys.PROFILING_QUOTA_REASON to fakeQuotaReason, - FeatureContextKeys.PROFILING_QUOTA_SESSION_ID to rumContext.sessionId + FeatureContextKeys.PROFILING_QUOTA_SESSION_ID to fakeSession.id ) ) } @@ -316,7 +321,7 @@ internal class RumSessionScopeStartupManagerTest { mapOf( FeatureContextKeys.PROFILER_IS_RUNNING to false, FeatureContextKeys.PROFILING_QUOTA_REASON to fakeQuotaReason, - FeatureContextKeys.PROFILING_QUOTA_SESSION_ID to rumContext.sessionId + FeatureContextKeys.PROFILING_QUOTA_SESSION_ID to fakeSession.id ) ) } @@ -411,7 +416,7 @@ internal class RumSessionScopeStartupManagerTest { mapOf( FeatureContextKeys.PROFILER_IS_RUNNING to false, FeatureContextKeys.PROFILING_QUOTA_REASON to fakeQuotaReason, - FeatureContextKeys.PROFILING_QUOTA_SESSION_ID to rumContext.sessionId + FeatureContextKeys.PROFILING_QUOTA_SESSION_ID to fakeSession.id ) ) } @@ -466,7 +471,7 @@ internal class RumSessionScopeStartupManagerTest { mapOf( FeatureContextKeys.PROFILER_IS_RUNNING to true, FeatureContextKeys.PROFILING_QUOTA_REASON to fakeQuotaReason, - FeatureContextKeys.PROFILING_QUOTA_SESSION_ID to rumContext.sessionId + FeatureContextKeys.PROFILING_QUOTA_SESSION_ID to fakeSession.id ) ) } @@ -606,7 +611,7 @@ internal class RumSessionScopeStartupManagerTest { ProfilerEvent.RumVitalEvent( rumContext = ProfilingRumContext( applicationId = rumContext.applicationId, - sessionId = rumContext.sessionId, + sessionId = fakeSession.id, viewId = rumContext.viewId, viewName = rumContext.viewName ), @@ -671,7 +676,7 @@ internal class RumSessionScopeStartupManagerTest { ProfilerEvent.RumVitalEvent( rumContext = ProfilingRumContext( applicationId = rumContext.applicationId, - sessionId = rumContext.sessionId, + sessionId = fakeSession.id, viewId = rumContext.viewId, viewName = rumContext.viewName ), @@ -1123,12 +1128,12 @@ internal class RumSessionScopeStartupManagerTest { hasDate(info.scenario.initialTime.timestamp + fakeTimeInfo.serverTimeOffsetMs) hasApplicationId(rumContext.applicationId) containsExactlyContextAttributes(fakeParentAttributes) - hasStartReason(rumContext.sessionStartReason) + hasStartReason(fakeSession.startReason) hasSampleRate(fakeSampleRate) hasNoSyntheticsTest() - hasSessionId(rumContext.sessionId) + hasSessionId(fakeSession.id) hasIsMainProcess(fakeDatadogContext.processInfo.isMainProcess) - hasSessionTimeseries(rumContext.hasTimeseries) + hasSessionTimeseries(fakeSession.hasTimeseries) hasSessionType( fakeRumSessionType?.toVitalAppLaunch() ?: VitalAppLaunchEvent.VitalAppLaunchEventSessionType.USER @@ -1193,12 +1198,12 @@ internal class RumSessionScopeStartupManagerTest { hasDate(scenario.initialTime.timestamp + fakeTimeInfo.serverTimeOffsetMs) hasApplicationId(rumContext.applicationId) containsExactlyContextAttributes(fakeParentAttributes) - hasStartReason(rumContext.sessionStartReason) + hasStartReason(fakeSession.startReason) hasSampleRate(fakeSampleRate) hasNoSyntheticsTest() - hasSessionId(rumContext.sessionId) + hasSessionId(fakeSession.id) hasIsMainProcess(fakeDatadogContext.processInfo.isMainProcess) - hasSessionTimeseries(rumContext.hasTimeseries) + hasSessionTimeseries(fakeSession.hasTimeseries) hasSessionType( fakeRumSessionType?.toVitalAppLaunch() ?: VitalAppLaunchEvent.VitalAppLaunchEventSessionType.USER diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/DefaultTimeseriesCollectorTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/DefaultTimeseriesCollectorTest.kt index 98f0ad3480..cd8fc3e8db 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/DefaultTimeseriesCollectorTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/DefaultTimeseriesCollectorTest.kt @@ -16,6 +16,7 @@ import com.datadog.android.api.storage.DataWriter import com.datadog.android.api.storage.EventBatchWriter import com.datadog.android.api.storage.EventType import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.rum.RumViewType import com.datadog.android.rum.RumSessionType import com.datadog.android.rum.internal.instrumentation.insights.InsightsCollector @@ -134,13 +135,19 @@ internal class DefaultTimeseriesCollectorTest { @Forgery lateinit var fakeRumContext: RumContext + @Forgery + private lateinit var fakeSession: RumSessionContext + @Forgery lateinit var fakeSessionType: RumSessionType private fun fakeRumContextOf( viewType: RumViewType, - sessionId: String = fakeRumContext.sessionId - ) = fakeRumContext.copy(viewType = viewType, sessionId = sessionId) + sessionId: String = fakeSession.id + ) = fakeRumContext.copy( + session = fakeSession.copy(id = sessionId), + viewType = viewType + ) private fun createTimeseries( pipelines: List> = listOf( @@ -181,7 +188,7 @@ internal class DefaultTimeseriesCollectorTest { } private fun DefaultTimeseriesCollector.startSession( - sessionId: String = fakeRumContext.sessionId, + sessionId: String = fakeSession.id, sessionType: RumSessionType = fakeSessionType, viewType: RumViewType = RumViewType.FOREGROUND ) { @@ -191,6 +198,7 @@ internal class DefaultTimeseriesCollectorTest { @BeforeEach fun `set up`() { + fakeRumContext = fakeRumContext.copy(session = fakeSession) whenever(mockReaderA.intervalMs) doReturn fakeIntervalAMs whenever(mockReaderB.intervalMs) doReturn fakeIntervalBMs whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger @@ -282,6 +290,23 @@ internal class DefaultTimeseriesCollectorTest { verify(mockExecutor).schedule(any(), eq(fakeIntervalBMs), eq(TimeUnit.MILLISECONDS)) } + @Test + fun `M wait for session context W onSessionStart() {application-only context received}`() { + // Given + val collector = createTimeseries() + collector.startInForeground() + collector.onRumContextUpdate(fakeRumContext.copy(session = null)) + + // When + collector.onSessionStart(fakeSession.id, fakeSessionType) + + // Then + verifyNoInteractions(mockExecutor) + collector.onRumContextUpdate(fakeRumContext) + verify(mockExecutor).schedule(any(), eq(fakeIntervalAMs), eq(TimeUnit.MILLISECONDS)) + verify(mockExecutor).schedule(any(), eq(fakeIntervalBMs), eq(TimeUnit.MILLISECONDS)) + } + @Test fun `M flush previous pipelines W onSessionStart() { session already active }`() { // Given @@ -293,7 +318,7 @@ internal class DefaultTimeseriesCollectorTest { val collector = createCollector(mockPipelinesFactory) collector.startInForeground() collector.startSession() - val fakeNextSessionId = "${fakeRumContext.sessionId}-next" + val fakeNextSessionId = "${fakeSession.id}-next" // When — another session starts before this one was explicitly stopped // (see RumApplicationScope's MULTIPLE_ACTIVE_SESSIONS_ERROR) @@ -307,7 +332,7 @@ internal class DefaultTimeseriesCollectorTest { @Test fun `M keep current session active W onSessionStop() { obsolete session }`() { // Given - val fakePreviousSessionId = fakeRumContext.sessionId + val fakePreviousSessionId = fakeSession.id val fakeCurrentSessionId = "$fakePreviousSessionId-current" val mockPreviousPipeline = mock>() val mockCurrentPipeline = mock>() @@ -333,7 +358,7 @@ internal class DefaultTimeseriesCollectorTest { @Test fun `M keep current context W onRumContextUpdate() { obsolete session }`(forge: Forge) { // Given - val fakePreviousSessionId = fakeRumContext.sessionId + val fakePreviousSessionId = fakeSession.id val fakeCurrentSessionId = "$fakePreviousSessionId-current" val fakeCurrentContext = fakeRumContextOf(RumViewType.FOREGROUND, fakeCurrentSessionId) val fakeObsoleteContext = fakeRumContextOf(RumViewType.FOREGROUND, fakePreviousSessionId).copy( @@ -360,7 +385,7 @@ internal class DefaultTimeseriesCollectorTest { @Test fun `M wait for current context W onSessionStart() { previous context is cached }`() { // Given - val fakeCurrentSessionId = "${fakeRumContext.sessionId}-current" + val fakeCurrentSessionId = "${fakeSession.id}-current" testedTimeseriesCollector.startSession() // When @@ -385,7 +410,7 @@ internal class DefaultTimeseriesCollectorTest { @Test fun `M do nothing W onSessionStop() { session never started }`() { // When - assertDoesNotThrow { testedTimeseriesCollector.onSessionStop(fakeRumContext.sessionId) } + assertDoesNotThrow { testedTimeseriesCollector.onSessionStop(fakeSession.id) } // Then verifyNoInteractions(mockDataWriter) @@ -399,7 +424,7 @@ internal class DefaultTimeseriesCollectorTest { testedTimeseriesCollector.startSession() // When - testedTimeseriesCollector.onSessionStop(fakeRumContext.sessionId) + testedTimeseriesCollector.onSessionStop(fakeSession.id) // Then verify(mockExecutor, never()).shutdown() @@ -432,7 +457,7 @@ internal class DefaultTimeseriesCollectorTest { captureScheduledRunnableForInterval(fakeIntervalBMs).run() // When - testedTimeseriesCollector.onSessionStop(fakeRumContext.sessionId) + testedTimeseriesCollector.onSessionStop(fakeSession.id) // Then mockInternalLogger.verifyLog( @@ -456,7 +481,7 @@ internal class DefaultTimeseriesCollectorTest { runnableA.run() // When - testedTimeseriesCollector.onSessionStop(fakeRumContext.sessionId) + testedTimeseriesCollector.onSessionStop(fakeSession.id) // Then verify(mockEventFactoryA).create(any(), any(), any()) @@ -624,7 +649,7 @@ internal class DefaultTimeseriesCollectorTest { @Test fun `M flush outgoing pipelines with outgoing context W suspend fires { session renewed while stopped }`() { // Given - val fakePreviousSessionId = fakeRumContext.sessionId + val fakePreviousSessionId = fakeSession.id val fakeNextSessionId = "$fakePreviousSessionId-next" val fakePreviousContext = fakeRumContextOf(RumViewType.FOREGROUND, fakePreviousSessionId) val mockPreviousPipeline = mock>() @@ -661,7 +686,7 @@ internal class DefaultTimeseriesCollectorTest { testedTimeseriesCollector.onUiHidden() // When - testedTimeseriesCollector.onSessionStop(fakeRumContext.sessionId) + testedTimeseriesCollector.onSessionStop(fakeSession.id) // Then verify(mockEventFactoryA).create( @@ -700,7 +725,7 @@ internal class DefaultTimeseriesCollectorTest { testedTimeseriesCollector.startInForeground() testedTimeseriesCollector.startSession() testedTimeseriesCollector.onUiHidden() - testedTimeseriesCollector.onSessionStop(fakeRumContext.sessionId) + testedTimeseriesCollector.onSessionStop(fakeSession.id) // When runScheduledSuspend() @@ -721,7 +746,7 @@ internal class DefaultTimeseriesCollectorTest { // When — onSessionStop closes the gate (sessionActive: true -> false) right away, so by the // time the debounced suspend runs, setForeground(false) is no longer an open -> closed // transition and Gate.setForeground's onUpdated callback is never invoked - testedCollector.onSessionStop(fakeRumContext.sessionId) + testedCollector.onSessionStop(fakeSession.id) verify(mockPipeline, times(1)).flush(fakeRumContextOf(RumViewType.FOREGROUND)) runScheduledSuspend() diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/PipelineTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/PipelineTest.kt index 05e56b1392..a03f5795af 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/PipelineTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/PipelineTest.kt @@ -17,6 +17,7 @@ import com.datadog.android.api.storage.EventBatchWriter import com.datadog.android.api.storage.EventType import com.datadog.android.internal.FeatureContextKeys import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.rum.RumSessionState import com.datadog.android.rum.internal.instrumentation.insights.InsightsCollector import com.datadog.android.rum.internal.timeseries.factory.EventFactory @@ -68,6 +69,9 @@ internal class PipelineTest { @Forgery lateinit var fakeRumContext: RumContext + @Forgery + private lateinit var fakeSession: RumSessionContext + @Mock lateinit var mockReader: DataPointsReader @@ -109,6 +113,7 @@ internal class PipelineTest { @BeforeEach fun `set up`() { + fakeRumContext = fakeRumContext.copy(session = fakeSession) whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger whenever(mockSdkCore.getFeature(Feature.RUM_FEATURE_NAME)) doReturn mockRumFeatureScope whenever(mockRumFeatureScope.withWriteContext(any(), any())) doAnswer { @@ -156,7 +161,20 @@ internal class PipelineTest { // Then assertThat(buffer.drain()).containsExactly(fakePoint) - verify(mockCollectedSessionsRegistry).setTimeseriesCollected(fakeRumContext.sessionId) + verify(mockCollectedSessionsRegistry).setTimeseriesCollected(fakeSession.id) + } + + @Test + fun `M skip sampling W execute() {no session}`() { + // Given + val fakeApplicationContext = fakeRumContext.copy(session = null) + + // When + testedPipeline.execute(fakeApplicationContext) + + // Then + assertThat(buffer.drain()).isEmpty() + verifyNoInteractions(mockReader, mockCollectedSessionsRegistry, mockEventFactory, mockDataWriter) } @Test diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/csv/CsvCollector.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/csv/CsvCollector.kt index 662404ec47..b19b6585d2 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/csv/CsvCollector.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/csv/CsvCollector.kt @@ -9,6 +9,7 @@ package com.datadog.android.rum.internal.timeseries.csv import com.datadog.android.api.InternalLogger import com.datadog.android.api.context.DatadogContext import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.RumSessionType import com.datadog.android.rum.internal.domain.InfoData @@ -89,7 +90,7 @@ internal class CsvCollector( forge: Forge, internalLogger: InternalLogger ): CsvCollector { - val rumContext = forge.getForgery() + val rumContext = forge.getForgery().copy(session = forge.getForgery()) val datadogContext = forge.getForgery() val batteryInfo = forge.getForgery() val displayInfo = forge.getForgery() diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/factory/CpuEventFactoryTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/factory/CpuEventFactoryTest.kt index d697a29a57..dfd83dfb07 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/factory/CpuEventFactoryTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/factory/CpuEventFactoryTest.kt @@ -11,6 +11,7 @@ import com.datadog.android.api.context.DatadogContext import com.datadog.android.api.feature.Feature import com.datadog.android.core.internal.utils.DdTagsUtils import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.rum.RumSessionStartReason import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.RumSessionType @@ -95,13 +96,20 @@ internal class CpuEventFactoryTest { @Forgery lateinit var fakeRumContext: RumContext + @Forgery + private lateinit var fakeSession: RumSessionContext + private val fakeSerializedRumContext: RumContext - get() = fakeRumContext.copy(applicationId = fakeApplicationId, sessionId = fakeSessionId) + get() = fakeRumContext.copy( + session = fakeSession.copy(id = fakeSessionId), + applicationId = fakeApplicationId + ) private val oneSample = listOf(DataPoint(1L, 1.0)) @BeforeEach fun `set up`(forge: Forge) { + fakeRumContext = fakeRumContext.copy(session = fakeSession) fakeSource = forge.aNullable { aValueFrom(TimeseriesCpuEvent.Source::class.java) } fakeDatadogContext = fakeDatadogContext.copy( source = fakeSource?.toJson()?.asString ?: forge.anAlphabeticalString() @@ -124,6 +132,18 @@ internal class CpuEventFactoryTest { internalLogger = mockInternalLogger ) + @Test + fun `M return null W create() {session absent}`() { + // Given + val context = fakeSerializedRumContext.copy(session = null) + + // When + val result = testedFactory().create(fakeDatadogContext, context, oneSample) + + // Then + assertThat(result).isNull() + } + @Test fun `M return null W create() { empty input }`() { // When @@ -273,8 +293,11 @@ internal class CpuEventFactoryTest { ) { // Given val rumContext = fakeSerializedRumContext.copy( - sessionStartReason = RumSessionStartReason.INACTIVITY_TIMEOUT, - sessionSampleRate = fakeSampleRate.toFloat() + session = fakeSession.copy( + id = fakeSessionId, + startReason = RumSessionStartReason.INACTIVITY_TIMEOUT, + sampleRate = fakeSampleRate.toFloat() + ) ) // When diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/factory/MemoryEventFactoryTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/factory/MemoryEventFactoryTest.kt index d2a5507d76..414a7addcb 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/factory/MemoryEventFactoryTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/timeseries/factory/MemoryEventFactoryTest.kt @@ -11,6 +11,7 @@ import com.datadog.android.api.context.DatadogContext import com.datadog.android.api.feature.Feature import com.datadog.android.core.internal.utils.DdTagsUtils import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.rum.RumSessionStartReason import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.RumSessionType @@ -99,13 +100,20 @@ internal class MemoryEventFactoryTest { @Forgery lateinit var fakeRumContext: RumContext + @Forgery + private lateinit var fakeSession: RumSessionContext + private val fakeSerializedRumContext: RumContext - get() = fakeRumContext.copy(applicationId = fakeApplicationId, sessionId = fakeSessionId) + get() = fakeRumContext.copy( + session = fakeSession.copy(id = fakeSessionId), + applicationId = fakeApplicationId + ) private val oneSample = listOf(DataPoint(1L, 1.0)) @BeforeEach fun `set up`(forge: Forge) { + fakeRumContext = fakeRumContext.copy(session = fakeSession) fakeSource = forge.aNullable { aValueFrom(TimeseriesMemoryEvent.Source::class.java) } fakeDatadogContext = fakeDatadogContext.copy( source = fakeSource?.toJson()?.asString ?: forge.anAlphabeticalString() @@ -130,6 +138,18 @@ internal class MemoryEventFactoryTest { internalLogger = mockInternalLogger ) + @Test + fun `M return null W create() {session absent}`() { + // Given + val context = fakeSerializedRumContext.copy(session = null) + + // When + val result = testedFactory().create(fakeDatadogContext, context, oneSample) + + // Then + assertThat(result).isNull() + } + @Test fun `M return null W create() { empty input }`() { // When @@ -301,8 +321,11 @@ internal class MemoryEventFactoryTest { ) { // Given val rumContext = fakeSerializedRumContext.copy( - sessionStartReason = RumSessionStartReason.INACTIVITY_TIMEOUT, - sessionSampleRate = fakeSampleRate.toFloat() + session = fakeSession.copy( + id = fakeSessionId, + startReason = RumSessionStartReason.INACTIVITY_TIMEOUT, + sampleRate = fakeSampleRate.toFloat() + ) ) // When diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/vitals/VitalReaderRunnableTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/vitals/VitalReaderRunnableTest.kt index 0b1afc2f91..06339db07c 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/vitals/VitalReaderRunnableTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/vitals/VitalReaderRunnableTest.kt @@ -96,6 +96,20 @@ internal class VitalReaderRunnableTest { ) } + @Test + fun `M stop reading vitals W onContextUpdate() {application missing}`() { + // Given + val context = emptyMap() + + // When + testedRunnable.onContextUpdate(Feature.RUM_FEATURE_NAME, context) + testedRunnable.run() + + // Then + assertThat(testedRunnable.currentRumContext).isNull() + verifyNoInteractions(mockReader, mockObserver) + } + @Test fun `M read data, notify observer and schedule W run { viewType == FOREGROUND }()`() { // Given diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/utils/forge/Configurator.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/utils/forge/Configurator.kt index 51990cf9f0..f41ab97b78 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/utils/forge/Configurator.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/utils/forge/Configurator.kt @@ -15,6 +15,7 @@ import com.datadog.android.internal.tests.elmyr.InternalTelemetryMetricForgeryFa import com.datadog.android.internal.tests.elmyr.ProfilingAnrDetectedEventForgeryFactory import com.datadog.android.internal.tests.elmyr.ProfilingThreadDumpForgeryFactory import com.datadog.android.internal.tests.elmyr.RumContextForgeryFactory +import com.datadog.android.internal.tests.elmyr.RumSessionContextForgeryFactory import com.datadog.android.internal.tests.elmyr.TracingHeaderTypesSetForgeryFactory import com.datadog.android.rum.tests.elmyr.BatteryInfoForgeryFactory import com.datadog.android.rum.tests.elmyr.DisplayInfoForgeryFactory @@ -41,6 +42,7 @@ internal class Configurator : BaseConfigurator() { forge.addFactory(MotionEventForgeryFactory()) forge.addFactory(RumEventMapperFactory()) forge.addFactory(RumContextForgeryFactory()) + forge.addFactory(RumSessionContextForgeryFactory()) forge.addFactory(VitalInfoForgeryFactory()) forge.addFactory(RumEventMetaForgeryFactory()) forge.addFactory(ViewEventMetaForgeryFactory()) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/telemetry/internal/TelemetryEventHandlerTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/telemetry/internal/TelemetryEventHandlerTest.kt index 46b4568221..55e99c171c 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/telemetry/internal/TelemetryEventHandlerTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/telemetry/internal/TelemetryEventHandlerTest.kt @@ -21,6 +21,7 @@ import com.datadog.android.core.sampling.Sampler import com.datadog.android.internal.FeatureContextKeys import com.datadog.android.internal.attributes.LocalAttribute import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.telemetry.InternalTelemetryEvent import com.datadog.android.internal.telemetry.TracingHeaderTypesSet import com.datadog.android.internal.utils.loggableStackTrace @@ -76,11 +77,13 @@ import org.mockito.invocation.InvocationOnMock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoSettings import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock +import org.mockito.kotlin.never import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions @@ -140,6 +143,12 @@ internal class TelemetryEventHandlerTest { @Forgery lateinit var fakeRumContext: RumContext + @Forgery + private lateinit var fakeSession: RumSessionContext + + @StringForgery + lateinit var fakeApplicationId: String + @StringForgery lateinit var fakeDeviceArchitecture: String @@ -174,6 +183,7 @@ internal class TelemetryEventHandlerTest { @BeforeEach fun `set up`(forge: Forge) { + fakeRumContext = fakeRumContext.copy(session = fakeSession) whenever(mockDeviceInfo.architecture).thenReturn(fakeDeviceArchitecture) whenever(mockDeviceInfo.deviceBrand).thenReturn(fakeDeviceBrand) whenever(mockDeviceInfo.deviceModel).thenReturn(fakeDeviceModel) @@ -231,6 +241,7 @@ internal class TelemetryEventHandlerTest { testedTelemetryHandler = TelemetryEventHandler( mockSdkCore, + fakeApplicationId, mockSampler, mockConfigurationSampler, sessionEndedMetricDispatcher, @@ -243,45 +254,65 @@ internal class TelemetryEventHandlerTest { GlobalDatadogTracer.clear() } - // region Debug Event - - @Test - fun `M create debug event W handleEvent(Log Debug)`(@Forgery fakeLogDebugEvent: InternalTelemetryEvent.Log.Debug) { + @ParameterizedTest + @MethodSource("noSessionRumContextParameters") + fun `M keep configured application id W handleEvent() {no session in RUM context}`( + eventClass: KClass, + featureContext: Map?, + forge: Forge + ) { // Given - val fakeWrappedEvent = RumRawEvent.TelemetryEventWrapper(fakeLogDebugEvent, eventTime = fakeEventTime) + val fakeEvent = forge.getForgery(eventClass.java) + val fakeWrappedEvent = RumRawEvent.TelemetryEventWrapper(fakeEvent, eventTime = fakeEventTime) + fakeDatadogContext = fakeDatadogContext.copy( + featuresContext = fakeDatadogContext.featuresContext.toMutableMap().apply { + if (featureContext == null) { + remove(Feature.RUM_FEATURE_NAME) + } else { + put(Feature.RUM_FEATURE_NAME, featureContext) + } + } + ) // When testedTelemetryHandler.handleEvent(fakeWrappedEvent, mockWriter) // Then - argumentCaptor { + argumentCaptor { verify(mockWriter).write(eq(mockEventBatchWriter), capture(), eq(EventType.TELEMETRY)) - assertDebugEventMatchesInternalEvent( - lastValue, - fakeLogDebugEvent, - fakeRumContext, - fakeWrappedEvent.eventTime.timestamp - ) + when (val event = firstValue) { + is TelemetryDebugEvent -> assertThat(event) + .hasApplicationId(fakeApplicationId) + .hasSessionId(null) + .hasViewId(null) + .hasActionId(null) + is TelemetryErrorEvent -> assertThat(event) + .hasApplicationId(fakeApplicationId) + .hasSessionId(null) + .hasViewId(null) + .hasActionId(null) + is TelemetryConfigurationEvent -> assertThat(event) + .hasApplicationId(fakeApplicationId) + .hasSessionId(null) + .hasViewId(null) + .hasActionId(null) + is TelemetryUsageEvent -> assertThat(event) + .hasApplicationId(fakeApplicationId) + .hasSessionId(null) + .hasViewId(null) + .hasActionId(null) + else -> error("Unexpected telemetry event: $event") + } } + verify(sessionEndedMetricDispatcher, never()).onSdkErrorTracked(any(), anyOrNull()) } + // region Debug Event + @Test - fun `M create debug event W handleEvent(Log Debug, no RUM)`( - @Forgery fakeLogDebugEvent: InternalTelemetryEvent.Log.Debug - ) { + fun `M create debug event W handleEvent(Log Debug)`(@Forgery fakeLogDebugEvent: InternalTelemetryEvent.Log.Debug) { // Given val fakeWrappedEvent = RumRawEvent.TelemetryEventWrapper(fakeLogDebugEvent, eventTime = fakeEventTime) - fakeDatadogContext = fakeDatadogContext.copy( - featuresContext = fakeDatadogContext.featuresContext.toMutableMap().apply { - remove(Feature.RUM_FEATURE_NAME) - } - ) - val noRumContext = RumContext( - applicationId = RumContext.NULL_UUID, - sessionId = RumContext.NULL_UUID, - viewId = null, - actionId = null - ) // When testedTelemetryHandler.handleEvent(fakeWrappedEvent, mockWriter) @@ -292,7 +323,7 @@ internal class TelemetryEventHandlerTest { assertDebugEventMatchesInternalEvent( lastValue, fakeLogDebugEvent, - noRumContext, + fakeRumContext, fakeWrappedEvent.eventTime.timestamp ) } @@ -302,6 +333,20 @@ internal class TelemetryEventHandlerTest { // region Error Event + @Test + fun `M track SDK error W handleEvent() {session present}`( + @Forgery fakeError: InternalTelemetryEvent.Log.Error + ) { + // Given + val fakeWrappedEvent = RumRawEvent.TelemetryEventWrapper(fakeError, eventTime = fakeEventTime) + + // When + testedTelemetryHandler.handleEvent(fakeWrappedEvent, mockWriter) + + // Then + verify(sessionEndedMetricDispatcher).onSdkErrorTracked(fakeSession.id, fakeError.kind) + } + @Test fun `M create error event W handleEvent(Log Error, only stacktrace)`(forge: Forge) { // Given @@ -319,12 +364,6 @@ internal class TelemetryEventHandlerTest { remove(Feature.RUM_FEATURE_NAME) } ) - val noRumContext = RumContext( - applicationId = RumContext.NULL_UUID, - sessionId = RumContext.NULL_UUID, - viewId = null, - actionId = null - ) // When testedTelemetryHandler.handleEvent(fakeWrappedEvent, mockWriter) @@ -335,7 +374,7 @@ internal class TelemetryEventHandlerTest { assertErrorEventMatchesInternalEvent( lastValue, fakeLogErrorEvent, - noRumContext, + null, fakeWrappedEvent.eventTime.timestamp, kind = null, stacktrace = expectedStackTrace @@ -360,12 +399,6 @@ internal class TelemetryEventHandlerTest { remove(Feature.RUM_FEATURE_NAME) } ) - val noRumContext = RumContext( - applicationId = RumContext.NULL_UUID, - sessionId = RumContext.NULL_UUID, - viewId = null, - actionId = null - ) // When testedTelemetryHandler.handleEvent(fakeWrappedEvent, mockWriter) @@ -376,7 +409,7 @@ internal class TelemetryEventHandlerTest { assertErrorEventMatchesInternalEvent( lastValue, fakeLogErrorEvent, - noRumContext, + null, fakeWrappedEvent.eventTime.timestamp, kind = expectedKind, stacktrace = null @@ -402,12 +435,6 @@ internal class TelemetryEventHandlerTest { remove(Feature.RUM_FEATURE_NAME) } ) - val noRumContext = RumContext( - applicationId = RumContext.NULL_UUID, - sessionId = RumContext.NULL_UUID, - viewId = null, - actionId = null - ) // When testedTelemetryHandler.handleEvent(fakeWrappedEvent, mockWriter) @@ -418,7 +445,7 @@ internal class TelemetryEventHandlerTest { assertErrorEventMatchesInternalEvent( lastValue, fakeLogErrorEvent, - noRumContext, + null, fakeWrappedEvent.eventTime.timestamp, kind = expectedKind, stacktrace = expectedStacktrace @@ -445,12 +472,6 @@ internal class TelemetryEventHandlerTest { remove(Feature.RUM_FEATURE_NAME) } ) - val noRumContext = RumContext( - applicationId = RumContext.NULL_UUID, - sessionId = RumContext.NULL_UUID, - viewId = null, - actionId = null - ) // When testedTelemetryHandler.handleEvent(fakeWrappedEvent, mockWriter) @@ -461,7 +482,7 @@ internal class TelemetryEventHandlerTest { assertErrorEventMatchesInternalEvent( lastValue, fakeLogErrorEvent, - noRumContext, + null, fakeWrappedEvent.eventTime.timestamp, kind = expectedKind, stacktrace = expectedStacktrace @@ -488,12 +509,6 @@ internal class TelemetryEventHandlerTest { remove(Feature.RUM_FEATURE_NAME) } ) - val noRumContext = RumContext( - applicationId = RumContext.NULL_UUID, - sessionId = RumContext.NULL_UUID, - viewId = null, - actionId = null - ) // When testedTelemetryHandler.handleEvent(fakeWrappedEvent, mockWriter) @@ -504,7 +519,7 @@ internal class TelemetryEventHandlerTest { assertErrorEventMatchesInternalEvent( lastValue, fakeLogErrorEvent, - noRumContext, + null, fakeWrappedEvent.eventTime.timestamp, kind = expectedKind, stacktrace = expectedStacktrace @@ -538,39 +553,6 @@ internal class TelemetryEventHandlerTest { } } - @Test - fun `M create config event W handleEvent() { Configuration, no RUM)`( - @Forgery fakeConfigurationEvent: InternalTelemetryEvent.Configuration - ) { - // Given - val fakeWrappedEvent = RumRawEvent.TelemetryEventWrapper(fakeConfigurationEvent, eventTime = fakeEventTime) - fakeDatadogContext = fakeDatadogContext.copy( - featuresContext = fakeDatadogContext.featuresContext.toMutableMap().apply { - remove(Feature.RUM_FEATURE_NAME) - } - ) - val noRumContext = RumContext( - applicationId = RumContext.NULL_UUID, - sessionId = RumContext.NULL_UUID, - viewId = null, - actionId = null - ) - - // When - testedTelemetryHandler.handleEvent(fakeWrappedEvent, mockWriter) - - // Then - argumentCaptor { - verify(mockWriter).write(eq(mockEventBatchWriter), capture(), eq(EventType.TELEMETRY)) - assertConfigEventMatchesInternalEvent( - firstValue, - fakeConfigurationEvent, - noRumContext, - fakeWrappedEvent.eventTime.timestamp - ) - } - } - @Test fun `M create config event W handleEvent() {Configuration, with RUM config }`( @Forgery fakeRumConfiguration: RumFeature.Configuration, @@ -1729,8 +1711,8 @@ internal class TelemetryEventHandlerTest { .hasSource(TelemetryUsageEvent.Source.ANDROID) .hasService(TelemetryEventHandler.TELEMETRY_SERVICE_NAME) .hasVersion(fakeDatadogContext.sdkVersion) - .hasApplicationId(rumContext.applicationId) - .hasSessionId(rumContext.sessionId) + .hasApplicationId(fakeApplicationId) + .hasSessionId(rumContext.session?.id) .hasViewId(rumContext.viewId) .hasActionId(rumContext.actionId) .hasAdditionalProperties(internalUsageEvent.additionalProperties) @@ -1755,8 +1737,8 @@ internal class TelemetryEventHandlerTest { .hasMessage(internalDebugEvent.message) .hasService(TelemetryEventHandler.TELEMETRY_SERVICE_NAME) .hasVersion(fakeDatadogContext.sdkVersion) - .hasApplicationId(rumContext.applicationId) - .hasSessionId(rumContext.sessionId) + .hasApplicationId(fakeApplicationId) + .hasSessionId(rumContext.session?.id) .hasViewId(rumContext.viewId) .hasActionId(rumContext.actionId) .hasAdditionalProperties(internalDebugEvent.additionalProperties?.withDiagnosticAttributes() ?: emptyMap()) @@ -1780,8 +1762,8 @@ internal class TelemetryEventHandlerTest { .hasMessage(internalMetricEvent.message) .hasService(TelemetryEventHandler.TELEMETRY_SERVICE_NAME) .hasVersion(fakeDatadogContext.sdkVersion) - .hasApplicationId(rumContext.applicationId) - .hasSessionId(rumContext.sessionId) + .hasApplicationId(fakeApplicationId) + .hasSessionId(rumContext.session?.id) .hasViewId(rumContext.viewId) .hasActionId(rumContext.actionId) .hasAdditionalProperties( @@ -1799,7 +1781,7 @@ internal class TelemetryEventHandlerTest { private fun assertErrorEventMatchesInternalEvent( actual: TelemetryErrorEvent, internalErrorEvent: InternalTelemetryEvent.Log.Error, - rumContext: RumContext, + rumContext: RumContext?, time: Long, stacktrace: String?, kind: String? @@ -1810,10 +1792,10 @@ internal class TelemetryEventHandlerTest { .hasMessage(internalErrorEvent.message) .hasService(TelemetryEventHandler.TELEMETRY_SERVICE_NAME) .hasVersion(fakeDatadogContext.sdkVersion) - .hasApplicationId(rumContext.applicationId) - .hasSessionId(rumContext.sessionId) - .hasViewId(rumContext.viewId) - .hasActionId(rumContext.actionId) + .hasApplicationId(fakeApplicationId) + .hasSessionId(rumContext?.session?.id) + .hasViewId(rumContext?.viewId) + .hasActionId(rumContext?.actionId) .hasErrorStack(stacktrace) .hasErrorKind(kind) .hasDeviceArchitecture(fakeDeviceArchitecture) @@ -1828,7 +1810,7 @@ internal class TelemetryEventHandlerTest { private fun assertErrorEventMatchesInternalEvent( actual: TelemetryErrorEvent, internalErrorEvent: InternalTelemetryEvent.Log.Error, - rumContext: RumContext, + rumContext: RumContext?, time: Long ) { val expectedStacktrace = internalErrorEvent.resolveStacktrace() @@ -1855,8 +1837,8 @@ internal class TelemetryEventHandlerTest { .hasSource(TelemetryConfigurationEvent.Source.ANDROID) .hasService(TelemetryEventHandler.TELEMETRY_SERVICE_NAME) .hasVersion(fakeDatadogContext.sdkVersion) - .hasApplicationId(rumContext.applicationId) - .hasSessionId(rumContext.sessionId) + .hasApplicationId(fakeApplicationId) + .hasSessionId(rumContext.session?.id) .hasViewId(rumContext.viewId) .hasActionId(rumContext.actionId) .hasBatchSize(internalConfigurationEvent.batchSize) @@ -1930,6 +1912,23 @@ internal class TelemetryEventHandlerTest { Configurator().configure(this) } + @JvmStatic + fun noSessionRumContextParameters() = listOf( + InternalTelemetryEvent.Log.Debug::class, + InternalTelemetryEvent.Log.Error::class, + InternalTelemetryEvent.Metric::class, + InternalTelemetryEvent.Configuration::class, + InternalTelemetryEvent.ApiUsage::class + ).flatMap { eventClass -> + listOf( + null, + emptyMap(), + mapOf(FeatureContextKeys.RUM_CONTEXT to RumContext(applicationId = forge.aString())) + ).map { context -> + Arguments.of(eventClass, context) + } + } + private fun InitialResourceIdentifier.resolveThreshold(): Long? { return (this as? TimeBasedInitialResourceIdentifier)?.timeThresholdInMilliseconds } diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/SessionReplayRumContextProvider.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/SessionReplayRumContextProvider.kt index cc0175edeb..7e68517e43 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/SessionReplayRumContextProvider.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/SessionReplayRumContextProvider.kt @@ -25,7 +25,7 @@ internal class SessionReplayRumContextProvider( return rumContext.let { SessionReplayRumContext( applicationId = it?.applicationId ?: NULL_UUID, - sessionId = it?.sessionId ?: NULL_UUID, + sessionId = it?.session?.id ?: NULL_UUID, viewId = it?.viewId ?: NULL_UUID, viewTimeOffsetMs = it?.viewTimestampOffset ?: 0L, viewUrl = it?.viewUrl diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/net/BatchesToSegmentsMapper.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/net/BatchesToSegmentsMapper.kt index 353a9dde0f..20a332c0ef 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/net/BatchesToSegmentsMapper.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/net/BatchesToSegmentsMapper.kt @@ -20,6 +20,7 @@ import com.google.gson.JsonArray import com.google.gson.JsonObject import com.google.gson.JsonParseException import com.google.gson.JsonParser +import com.google.gson.JsonPrimitive /** * Maps a batch to a Pair for uploading. @@ -163,10 +164,15 @@ internal class BatchesToSegmentsMapper(private val internalLogger: InternalLogge } private fun JsonObject.rumContext(): SessionReplayRumContext? { - val applicationId = get(EnrichedRecord.APPLICATION_ID_KEY)?.asString - val sessionId = get(EnrichedRecord.SESSION_ID_KEY)?.asString - val viewId = get(EnrichedRecord.VIEW_ID_KEY)?.asString - if (applicationId == null || sessionId == null || viewId == null) { + val applicationId = contextId(EnrichedRecord.APPLICATION_ID_KEY) + val sessionId = contextId(EnrichedRecord.SESSION_ID_KEY) + val viewId = contextId(EnrichedRecord.VIEW_ID_KEY) + val context = if (applicationId != null && sessionId != null && viewId != null) { + SessionReplayRumContext(applicationId, sessionId, viewId) + } else { + null + } + if (context == null || context.isNotValid()) { internalLogger.log( InternalLogger.Level.ERROR, InternalLogger.Target.TELEMETRY, @@ -176,11 +182,12 @@ internal class BatchesToSegmentsMapper(private val internalLogger: InternalLogge ) return null } - return SessionReplayRumContext( - applicationId = applicationId, - sessionId = sessionId, - viewId = viewId - ) + return context + } + + private fun JsonObject.contextId(key: String): String? { + val value = get(key) as? JsonPrimitive ?: return null + return if (value.isString) value.asString.takeUnless { it.isBlank() } else null } // endregion @@ -196,6 +203,6 @@ internal class BatchesToSegmentsMapper(private val internalLogger: InternalLogge internal const val UNABLE_TO_DESERIALIZE_ENRICHED_RECORD_ERROR_MESSAGE = "SR BatchesToSegmentMapper: unable to deserialize EnrichedRecord" internal const val ILLEGAL_STATE_ENRICHED_RECORD_ERROR_MESSAGE = - "SR BatchesToSegmentMapper: Enriched record was missing the context information" + "SR BatchesToSegmentMapper: Enriched record contained missing or invalid context information" } } diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/ForgeConfigurator.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/ForgeConfigurator.kt index f4810992de..410d3c2d1d 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/ForgeConfigurator.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/ForgeConfigurator.kt @@ -7,6 +7,7 @@ package com.datadog.android.sessionreplay.forge import com.datadog.android.internal.tests.elmyr.RumContextForgeryFactory +import com.datadog.android.internal.tests.elmyr.RumSessionContextForgeryFactory import com.datadog.android.tests.elmyr.useCoreFactories import com.datadog.tools.unit.forge.BaseConfigurator import fr.xgouchet.elmyr.Forge @@ -21,6 +22,7 @@ internal class ForgeConfigurator : BaseConfigurator() { // Core forge.useCoreFactories() forge.addFactory(RumContextForgeryFactory()) + forge.addFactory(RumSessionContextForgeryFactory()) // Session Replay forge.addFactory(NodeForgeryFactory()) diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/SessionReplayFeatureTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/SessionReplayFeatureTest.kt index 8e8d3e1130..4f4199b51b 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/SessionReplayFeatureTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/SessionReplayFeatureTest.kt @@ -20,6 +20,7 @@ import com.datadog.android.api.storage.EventType import com.datadog.android.api.storage.RawBatchEvent import com.datadog.android.internal.FeatureContextKeys import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.sessionreplay.NoOpSessionReplayInternalCallback import com.datadog.android.sessionreplay.SessionReplay import com.datadog.android.sessionreplay.SessionReplayConfiguration @@ -92,6 +93,9 @@ internal class SessionReplayFeatureTest { @Forgery lateinit var fakeRumContext: RumContext + @Forgery + private lateinit var fakeSession: RumSessionContext + @Forgery lateinit var fakeConfiguration: SessionReplayConfiguration @@ -116,6 +120,7 @@ internal class SessionReplayFeatureTest { @BeforeEach fun `set up`(forge: Forge) { + fakeRumContext = fakeRumContext.copy(session = fakeSession) fakeRumContext = fakeRumContext.copy(viewId = forge.getForgery().toString()) fakeSampleRate = forge.aFloat(min = 1f, max = 100f) fakeSessionId = forge.getForgery().toString() diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/SessionReplayRumContextProviderTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/SessionReplayRumContextProviderTest.kt index ba404c3af8..54308d5ddb 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/SessionReplayRumContextProviderTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/SessionReplayRumContextProviderTest.kt @@ -26,6 +26,9 @@ import java.util.UUID @ForgeConfiguration(ForgeConfigurator::class) internal class SessionReplayRumContextProviderTest { + @StringForgery + private lateinit var fakeApplicationId: String + @Test fun `M preserve snapshot and notify each new view W onContextUpdate {core reuses mutable map}`( @Forgery fakeRumContext: RumContext, @@ -125,7 +128,7 @@ internal class SessionReplayRumContextProviderTest { assertThat(context).isEqualTo( SessionReplayRumContext( applicationId = fakeSnapshot.applicationId, - sessionId = fakeSnapshot.sessionId, + sessionId = fakeSnapshot.session?.id ?: RumContext.NULL_UUID, viewId = fakeSnapshot.viewId ?: RumContext.NULL_UUID, viewTimeOffsetMs = fakeViewTimestampOffset, viewUrl = fakeSnapshot.viewUrl @@ -168,4 +171,23 @@ internal class SessionReplayRumContextProviderTest { SessionReplayRumContext(RumContext.NULL_UUID, RumContext.NULL_UUID, RumContext.NULL_UUID, 0L) ) } + + @Test + fun `M retain application without recording context W getRumContext {no session}`() { + // Given + val testedProvider = SessionReplayRumContextProvider() + testedProvider.onContextUpdate( + Feature.RUM_FEATURE_NAME, + mapOf(FeatureContextKeys.RUM_CONTEXT to RumContext(applicationId = fakeApplicationId)) + ) + + // When + val context = testedProvider.getRumContext() + + // Then + assertThat(context.applicationId).isEqualTo(fakeApplicationId) + assertThat(context.sessionId).isEqualTo(RumContext.NULL_UUID) + assertThat(context.viewId).isEqualTo(RumContext.NULL_UUID) + assertThat(context.isValid()).isFalse() + } } diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/net/BatchesToSegmentsMapperTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/net/BatchesToSegmentsMapperTest.kt index f74baee2ef..825235d565 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/net/BatchesToSegmentsMapperTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/net/BatchesToSegmentsMapperTest.kt @@ -13,6 +13,9 @@ import com.datadog.android.sessionreplay.internal.processor.EnrichedRecord import com.datadog.android.sessionreplay.internal.utils.SessionReplayRumContext import com.datadog.android.sessionreplay.model.MobileSegment import com.datadog.android.utils.verifyLog +import com.google.gson.JsonArray +import com.google.gson.JsonNull +import com.google.gson.JsonObject import com.google.gson.JsonParser import com.google.gson.JsonPrimitive import fr.xgouchet.elmyr.Forge @@ -25,6 +28,9 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.api.extension.Extensions +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoSettings @@ -317,274 +323,59 @@ internal class BatchesToSegmentsMapperTest { assertThat(recordsAsJsonArray?.size()).isEqualTo(expectedRecordsSize) } - @Test - fun `M return empty list W map { enriched record with missing application id key }`( - forge: Forge - ) { - // Given - val rumContext: SessionReplayRumContext = forge.getForgery() - val fakeRecordsSize = forge.anInt(min = 10, max = 20) - val fakeRecords: List = forge.aList(fakeRecordsSize) { - forge.getForgery() - } - val fakeEnrichedRecords = fakeRecords - .chunked(forge.anInt(min = 1, max = fakeRecordsSize)) - .map { - EnrichedRecord( - rumContext.applicationId, - rumContext.sessionId, - rumContext.sessionId, - it - ) - } - val fakeBatchData = fakeEnrichedRecords - .asSequence() - .map { JsonParser.parseString(it.toJson()).asJsonObject } - .onEach { - it.remove(EnrichedRecord.APPLICATION_ID_KEY) - } - .map { it.toString().toByteArray() } - .toList() - // When - assertThat(testedMapper.map(datadogContext, fakeBatchData)).isEmpty() - mockInternalLogger.verifyLog( - InternalLogger.Level.ERROR, - InternalLogger.Target.TELEMETRY, - BatchesToSegmentsMapper.ILLEGAL_STATE_ENRICHED_RECORD_ERROR_MESSAGE, - onlyOnce = true, - mode = times(fakeBatchData.size) - ) - } - - @Test - fun `M drop the broken records W map { some enriched records with missing application key }`( + @ParameterizedTest + @MethodSource("invalidContextParameters") + fun `M drop invalid context W map {all enriched records invalid}`( + contextKey: String, + invalidValue: InvalidContextValue, + @Forgery fakeEnrichedRecord: EnrichedRecord, forge: Forge ) { // Given - val rumContext: SessionReplayRumContext = forge.getForgery() - val fakeRecordsSize = forge.anInt(min = 20, max = 50) - val fakeRecords: List = forge.aList(fakeRecordsSize) { - forge.getForgery() - } - val fakeEnrichedRecords = fakeRecords - .chunked(forge.anInt(min = 1, max = fakeRecordsSize)) - .map { - EnrichedRecord( - rumContext.applicationId, - rumContext.sessionId, - rumContext.sessionId, - it - ) - } - var removedRecords = 0 - val fakeBatchData = fakeEnrichedRecords - .asSequence() - .map { JsonParser.parseString(it.toJson()).asJsonObject } - .mapIndexed { index, jsonObject -> - if (index == 0) { - val records = jsonObject.get(EnrichedRecord.RECORDS_KEY) - .asJsonArray - removedRecords = records.size() - jsonObject.remove(EnrichedRecord.APPLICATION_ID_KEY) - jsonObject - } else { - jsonObject - } - } - .map { it.toString().toByteArray() } - .toList() + val fakeBrokenRecord = fakeEnrichedRecord.withInvalidContext(contextKey, invalidValue, forge) // When - val mappedSegments = testedMapper.map(datadogContext, fakeBatchData) - assertThat(mappedSegments.size).isEqualTo(1) - val expectedRecordsSize = fakeRecords.size - removedRecords - assertThat(mappedSegments[0].first.recordsCount.toInt()).isEqualTo(expectedRecordsSize) - val recordsAsJsonArray = mappedSegments[0].second.getAsJsonArray( - BatchesToSegmentsMapper.RECORDS_KEY - ) - assertThat(recordsAsJsonArray?.size()).isEqualTo(expectedRecordsSize) - } - - @Test - fun `M return empty list W map { enriched records with missing session id key }`( - forge: Forge - ) { - // Given - val rumContext: SessionReplayRumContext = forge.getForgery() - val fakeRecordsSize = forge.anInt(min = 10, max = 20) - val fakeRecords: List = forge.aList(fakeRecordsSize) { - forge.getForgery() - } - val fakeEnrichedRecords = fakeRecords - .chunked(forge.anInt(min = 1, max = fakeRecordsSize)) - .map { - EnrichedRecord( - rumContext.applicationId, - rumContext.sessionId, - rumContext.sessionId, - it - ) - } - val fakeBatchData = fakeEnrichedRecords - .asSequence() - .map { JsonParser.parseString(it.toJson()).asJsonObject } - .onEach { - it.remove(EnrichedRecord.SESSION_ID_KEY) - } - .map { it.toString().toByteArray() } - .toList() + val mappedSegments = testedMapper.map(datadogContext, listOf(fakeBrokenRecord)) - // When - assertThat(testedMapper.map(datadogContext, fakeBatchData)).isEmpty() + // Then + assertThat(mappedSegments).isEmpty() mockInternalLogger.verifyLog( InternalLogger.Level.ERROR, InternalLogger.Target.TELEMETRY, BatchesToSegmentsMapper.ILLEGAL_STATE_ENRICHED_RECORD_ERROR_MESSAGE, - onlyOnce = true, - mode = times(fakeBatchData.size) + onlyOnce = true ) } - @Test - fun `M drop the broken records W map { some enriched records with missing session key }`( + @ParameterizedTest + @MethodSource("invalidContextParameters") + fun `M preserve valid records W map {mixed valid and invalid contexts}`( + contextKey: String, + invalidValue: InvalidContextValue, + @Forgery fakeEnrichedRecord: EnrichedRecord, forge: Forge ) { // Given - val rumContext: SessionReplayRumContext = forge.getForgery() - val fakeRecordsSize = forge.anInt(min = 20, max = 50) - val fakeRecords: List = forge.aList(fakeRecordsSize) { - forge.getForgery() - } - val fakeEnrichedRecords = fakeRecords - .chunked(forge.anInt(min = 1, max = fakeRecordsSize)) - .map { - EnrichedRecord( - rumContext.applicationId, - rumContext.sessionId, - rumContext.sessionId, - it - ) - } - var removedRecords = 0 - val fakeBatchData = fakeEnrichedRecords - .asSequence() - .map { JsonParser.parseString(it.toJson()).asJsonObject } - .mapIndexed { index, jsonObject -> - if (index == 0) { - val records = jsonObject.get(EnrichedRecord.RECORDS_KEY) - .asJsonArray - removedRecords = records.size() - jsonObject.remove(EnrichedRecord.SESSION_ID_KEY) - jsonObject - } else { - jsonObject - } - } - .map { it.toString().toByteArray() } - .toList() + val fakeSortedRecord = fakeEnrichedRecord.copy(records = fakeEnrichedRecord.records.sortedBy { it.timestamp() }) + val fakeBrokenRecord = fakeSortedRecord.withInvalidContext(contextKey, invalidValue, forge) + val fakeValidRecord = fakeSortedRecord.toJson().toByteArray() + val fakeExpectedSegment = fakeSortedRecord.toSegment() // When - val mappedSegments = testedMapper.map(datadogContext, fakeBatchData) - - // Then - val expectedRecordsSize = fakeRecords.size - removedRecords - assertThat(mappedSegments.size).isEqualTo(1) - assertThat(mappedSegments[0].first.recordsCount.toInt()).isEqualTo(expectedRecordsSize) - val recordsAsJsonArray = mappedSegments[0].second - .getAsJsonArray(BatchesToSegmentsMapper.RECORDS_KEY) - assertThat(recordsAsJsonArray?.size()).isEqualTo(expectedRecordsSize) - } - - @Test - fun `M return empty list W map { enriched records with missing view id key }`( - forge: Forge - ) { - // Given - val rumContext: SessionReplayRumContext = forge.getForgery() - val fakeRecordsSize = forge.anInt(min = 10, max = 20) - val fakeRecords: List = forge.aList(fakeRecordsSize) { - forge.getForgery() - } - val fakeEnrichedRecords = fakeRecords - .chunked(forge.anInt(min = 1, max = fakeRecordsSize)) - .map { - EnrichedRecord( - rumContext.applicationId, - rumContext.sessionId, - rumContext.sessionId, - it - ) - } - val fakeBatchData = fakeEnrichedRecords - .asSequence() - .map { JsonParser.parseString(it.toJson()).asJsonObject } - .onEach { - it.remove(EnrichedRecord.VIEW_ID_KEY) - } - .map { it.toString().toByteArray() } - .toList() + val mappedSegments = testedMapper.map(datadogContext, listOf(fakeBrokenRecord, fakeValidRecord)) // Then - assertThat(testedMapper.map(datadogContext, fakeBatchData)).isEmpty() + assertThat(mappedSegments).hasSize(1) + assertThat(mappedSegments.single().first).isEqualTo(fakeExpectedSegment.copy(records = emptyList())) + assertThat(mappedSegments.single().second.toString()).isEqualTo(fakeExpectedSegment.toJson().toString()) mockInternalLogger.verifyLog( InternalLogger.Level.ERROR, InternalLogger.Target.TELEMETRY, BatchesToSegmentsMapper.ILLEGAL_STATE_ENRICHED_RECORD_ERROR_MESSAGE, - onlyOnce = true, - mode = times(fakeBatchData.size) + onlyOnce = true ) } - @Test - fun `M drop the broken records W map { some enriched records with missing view id key }`( - forge: Forge - ) { - // Given - val rumContext: SessionReplayRumContext = forge.getForgery() - val fakeRecordsSize = forge.anInt(min = 20, max = 50) - val fakeRecords: List = forge.aList(fakeRecordsSize) { - forge.getForgery() - } - val fakeEnrichedRecords = fakeRecords - .chunked(forge.anInt(min = 1, max = fakeRecordsSize)) - .map { - EnrichedRecord( - rumContext.applicationId, - rumContext.sessionId, - rumContext.sessionId, - it - ) - } - var removedRecords = 0 - val fakeBatchData = fakeEnrichedRecords - .asSequence() - .map { JsonParser.parseString(it.toJson()).asJsonObject } - .mapIndexed { index, jsonObject -> - if (index == 0) { - val records = jsonObject.get(EnrichedRecord.RECORDS_KEY) - .asJsonArray - removedRecords = records.size() - jsonObject.remove(EnrichedRecord.VIEW_ID_KEY) - jsonObject - } else { - jsonObject - } - } - .map { it.toString().toByteArray() } - .toList() - - // When - val mappedSegments = testedMapper.map(datadogContext, fakeBatchData) - - // Then - val expectedRecordsSize = fakeRecords.size - removedRecords - assertThat(mappedSegments.size).isEqualTo(1) - assertThat(mappedSegments[0].first.recordsCount.toInt()).isEqualTo(expectedRecordsSize) - val recordsAsJsonArray = mappedSegments[0].second - .getAsJsonArray(BatchesToSegmentsMapper.RECORDS_KEY) - assertThat(recordsAsJsonArray?.size()).isEqualTo(expectedRecordsSize) - } - @Test fun `M drop the broken records W map { some records with wrong timestamp format key }`( forge: Forge @@ -692,6 +483,26 @@ internal class BatchesToSegmentsMapperTest { // region Internal + private fun EnrichedRecord.withInvalidContext( + contextKey: String, + invalidValue: InvalidContextValue, + forge: Forge + ): ByteArray { + val json = JsonParser.parseString(toJson()).asJsonObject + when (invalidValue) { + InvalidContextValue.MISSING -> json.remove(contextKey) + InvalidContextValue.NULL -> json.add(contextKey, JsonNull.INSTANCE) + InvalidContextValue.NUMBER -> json.addProperty(contextKey, forge.aLong()) + InvalidContextValue.BOOLEAN -> json.addProperty(contextKey, forge.aBool()) + InvalidContextValue.OBJECT -> json.add(contextKey, JsonObject()) + InvalidContextValue.ARRAY -> json.add(contextKey, JsonArray().apply { add(json.get(contextKey)) }) + InvalidContextValue.EMPTY -> json.addProperty(contextKey, "") + InvalidContextValue.BLANK -> json.addProperty(contextKey, " ") + InvalidContextValue.ZERO_UUID -> json.addProperty(contextKey, "00000000-0000-0000-0000-000000000000") + } + return json.toString().toByteArray() + } + private fun EnrichedRecord.toSegment(): MobileSegment { return MobileSegment( application = MobileSegment.Application(applicationId), @@ -748,4 +559,27 @@ internal class BatchesToSegmentsMapperTest { } // endregion + + enum class InvalidContextValue { + MISSING, + NULL, + NUMBER, + BOOLEAN, + OBJECT, + ARRAY, + EMPTY, + BLANK, + ZERO_UUID + } + + companion object { + @JvmStatic + fun invalidContextParameters(): List = listOf( + EnrichedRecord.APPLICATION_ID_KEY, + EnrichedRecord.SESSION_ID_KEY, + EnrichedRecord.VIEW_ID_KEY + ).flatMap { contextKey -> + InvalidContextValue.entries.map { Arguments.of(contextKey, it) } + } + } } diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/listener/WindowsOnDrawListenerTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/listener/WindowsOnDrawListenerTest.kt index b255d5a360..da4c2e260b 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/listener/WindowsOnDrawListenerTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/listener/WindowsOnDrawListenerTest.kt @@ -19,6 +19,7 @@ import com.datadog.android.core.metrics.PerformanceMetric import com.datadog.android.core.metrics.TelemetryMetricType import com.datadog.android.internal.FeatureContextKeys import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.time.TimeProvider import com.datadog.android.sessionreplay.ImagePrivacy import com.datadog.android.sessionreplay.TextAndInputPrivacy @@ -207,6 +208,7 @@ internal class WindowsOnDrawListenerTest { @Test fun `M resume without polling W context update {active RUM view disappears and returns}`( @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext, @Forgery fakeViewId: UUID ) { // Given: use the real context, queue and scheduler to exercise failed snapshot attempts. @@ -267,7 +269,7 @@ internal class WindowsOnDrawListenerTest { methodCallSamplingRate = 0f, rumContextProvider = fakeRumContextProvider ) - val fakeActiveRumContext = fakeRumContext.copy(viewId = fakeViewId.toString()) + val fakeActiveRumContext = fakeRumContext.copy(session = fakeSession, viewId = fakeViewId.toString()) val fakeContext = mutableMapOf(FeatureContextKeys.RUM_CONTEXT to fakeActiveRumContext) fakeRumContextProvider.onContextUpdate(Feature.RUM_FEATURE_NAME, fakeContext) advanceTo(0L) diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/RumContextPropagator.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/RumContextPropagator.kt index 1dcb1f3b73..15660a786b 100644 --- a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/RumContextPropagator.kt +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/RumContextPropagator.kt @@ -60,12 +60,12 @@ class RumContextPropagator(private val sdkCoreProvider: () -> FeatureSdkCore?) { ?.rumContext() instance.setTag(LogAttributes.RUM_APPLICATION_ID, rumContext?.applicationId) - instance.setTag(LogAttributes.RUM_SESSION_ID, rumContext?.sessionId) + instance.setTag(LogAttributes.RUM_SESSION_ID, rumContext?.session?.id) instance.setTag(LogAttributes.RUM_VIEW_ID, rumContext?.viewId) instance.setTag(LogAttributes.RUM_ACTION_ID, rumContext?.actionId) instance.setTag( LogAttributes.RUM_SESSION_SAMPLE_RATE, - rumContext?.sessionSampleRate + rumContext?.session?.sampleRate ) instance.setTag(HttpCodec.RUM_KEY_USER_ID, datadogContext.userInfo.id) instance.setTag(HttpCodec.RUM_KEY_ACCOUNT_ID, datadogContext.accountInfo?.id) diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/RumContextPropagatorTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/RumContextPropagatorTest.kt index ddf09e6753..16b96b9964 100644 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/RumContextPropagatorTest.kt +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/RumContextPropagatorTest.kt @@ -289,10 +289,10 @@ class RumContextPropagatorTest { verify(span).getTag(DATADOG_INITIAL_CONTEXT) val expectedTags = mapOf( LogAttributes.RUM_APPLICATION_ID to fakeRumContext.applicationId, - LogAttributes.RUM_SESSION_ID to fakeRumContext.sessionId, + LogAttributes.RUM_SESSION_ID to fakeRumContext.session?.id, LogAttributes.RUM_VIEW_ID to fakeRumContext.viewId, LogAttributes.RUM_ACTION_ID to fakeRumContext.actionId, - LogAttributes.RUM_SESSION_SAMPLE_RATE to fakeRumContext.sessionSampleRate, + LogAttributes.RUM_SESSION_SAMPLE_RATE to fakeRumContext.session?.sampleRate, HttpCodec.RUM_KEY_ACCOUNT_ID to fakeDatadogContext.accountInfo?.id, HttpCodec.RUM_KEY_USER_ID to fakeDatadogContext.userInfo.id, DATADOG_INITIAL_CONTEXT to null @@ -317,10 +317,10 @@ class RumContextPropagatorTest { verify(span).getTag(DATADOG_INITIAL_CONTEXT) val expectedTags = mapOf( LogAttributes.RUM_APPLICATION_ID to fakeRumContext.applicationId, - LogAttributes.RUM_SESSION_ID to fakeRumContext.sessionId, + LogAttributes.RUM_SESSION_ID to fakeRumContext.session?.id, LogAttributes.RUM_VIEW_ID to fakeRumContext.viewId, LogAttributes.RUM_ACTION_ID to fakeRumContext.actionId, - LogAttributes.RUM_SESSION_SAMPLE_RATE to fakeRumContext.sessionSampleRate, + LogAttributes.RUM_SESSION_SAMPLE_RATE to fakeRumContext.session?.sampleRate, HttpCodec.RUM_KEY_ACCOUNT_ID to fakeDatadogContext.accountInfo?.id, HttpCodec.RUM_KEY_USER_ID to fakeDatadogContext.userInfo.id, DATADOG_INITIAL_CONTEXT to null @@ -331,6 +331,46 @@ class RumContextPropagatorTest { verifyNoInteractions(mockInternalLogger) } + @Test + fun `M retain application attribution W extractRumContext() {DatadogSpan, application-only context}`(forge: Forge) { + // Given + val fakeApplicationId = fakeRumContext.applicationId + val fakeContext = forge.aDatadogContextWithRumContext( + RumContext(applicationId = fakeApplicationId) + ) + val mockSpan = newDatadogSpanWithLazyDatadogContext(completedFutureMock(fakeContext)) + + // When + mockSpan.extractRumContext(testedRumContextPropagator) + + // Then + verify(mockSpan).setTag(LogAttributes.RUM_APPLICATION_ID, fakeApplicationId as Any?) + verify(mockSpan).setTag(LogAttributes.RUM_SESSION_ID, null as Any?) + verify(mockSpan).setTag(LogAttributes.RUM_VIEW_ID, null as Any?) + verify(mockSpan).setTag(LogAttributes.RUM_ACTION_ID, null as Any?) + verify(mockSpan).setTag(LogAttributes.RUM_SESSION_SAMPLE_RATE, null as Any?) + } + + @Test + fun `M retain application attribution W extractRumContext() {DDSpan, application-only context}`(forge: Forge) { + // Given + val fakeApplicationId = fakeRumContext.applicationId + val fakeContext = forge.aDatadogContextWithRumContext( + RumContext(applicationId = fakeApplicationId) + ) + val mockSpan = newDDSpanWithLazyDatadogContext(completedFutureMock(fakeContext)) + + // When + mockSpan.extractRumContext(testedRumContextPropagator) + + // Then + verify(mockSpan).setTag(LogAttributes.RUM_APPLICATION_ID, fakeApplicationId as Any?) + verify(mockSpan).setTag(LogAttributes.RUM_SESSION_ID, null as Any?) + verify(mockSpan).setTag(LogAttributes.RUM_VIEW_ID, null as Any?) + verify(mockSpan).setTag(LogAttributes.RUM_ACTION_ID, null as Any?) + verify(mockSpan).setTag(LogAttributes.RUM_SESSION_SAMPLE_RATE, null as Any?) + } + @Test fun `M set null RUM tags W extractRumContext { DatadogSpan, rum context is empty }`(forge: Forge) { // Given diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/data/CoreTraceWriterTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/data/CoreTraceWriterTest.kt index 87547ab412..581de28ed8 100644 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/data/CoreTraceWriterTest.kt +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/data/CoreTraceWriterTest.kt @@ -181,7 +181,7 @@ internal class CoreTraceWriterTest { // THEN val expectedTags = mapOf( LogAttributes.RUM_APPLICATION_ID to fakeRumContext.applicationId, - LogAttributes.RUM_SESSION_ID to fakeRumContext.sessionId, + LogAttributes.RUM_SESSION_ID to fakeRumContext.session?.id, LogAttributes.RUM_VIEW_ID to fakeRumContext.viewId, LogAttributes.RUM_ACTION_ID to fakeRumContext.actionId, RumContextPropagator.DATADOG_INITIAL_CONTEXT to null diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/Configurator.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/Configurator.kt index 56e6c2784a..aaf4185977 100644 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/Configurator.kt +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/Configurator.kt @@ -7,6 +7,7 @@ package com.datadog.android.utils.forge import com.datadog.android.internal.tests.elmyr.RumContextForgeryFactory +import com.datadog.android.internal.tests.elmyr.RumSessionContextForgeryFactory import com.datadog.android.tests.elmyr.useCoreFactories import com.datadog.tools.unit.forge.BaseConfigurator import fr.xgouchet.elmyr.Forge @@ -19,6 +20,7 @@ internal class Configurator : BaseConfigurator() { // Core forge.useCoreFactories() forge.addFactory(RumContextForgeryFactory()) + forge.addFactory(RumSessionContextForgeryFactory()) // APM forge.addFactory(SpanEventForgeryFactory()) diff --git a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/DatadogEventBridge.kt b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/DatadogEventBridge.kt index 37ca99167a..4eaa80f9ee 100644 --- a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/DatadogEventBridge.kt +++ b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/DatadogEventBridge.kt @@ -98,21 +98,20 @@ internal class DatadogEventBridge( val rumContext = webViewRumFeature?.cachedRumContext val tracingContext = webViewRumFeature?.cachedTracingContext.orEmpty() - val sessionId = rumContext?.takeIf { it.sessionState == RumSessionState.TRACKED }?.sessionId - val sessionSampleRate = rumContext?.sessionSampleRate + val session = rumContext?.session?.takeIf { it.state == RumSessionState.TRACKED } val traceSampleRate = (tracingContext[TRACE_SAMPLE_RATE_KEY] as? Number)?.toFloat() - if (sessionId == null || sessionSampleRate == null || traceSampleRate == null) { + if (session == null || traceSampleRate == null) { return NULL_STRING } - val combinedRate = DeterministicSampling.combinedSampleRate(sessionSampleRate, traceSampleRate) + val combinedRate = DeterministicSampling.combinedSampleRate(session.sampleRate, traceSampleRate) val sampler = DeterministicSampler( SessionSamplingIdProvider::provideId, combinedRate ) - return if (sampler.sample(sessionId)) TRUE_STRING else FALSE_STRING + return if (sampler.sample(session.id)) TRUE_STRING else FALSE_STRING } // endregion diff --git a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/log/WebViewLogEventConsumer.kt b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/log/WebViewLogEventConsumer.kt index 5e5ae2f62d..c548c1dcb0 100644 --- a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/log/WebViewLogEventConsumer.kt +++ b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/log/WebViewLogEventConsumer.kt @@ -54,9 +54,19 @@ internal class WebViewLogEventConsumer( addDdTags(event, datadogContext) correctDate(event, datadogContext) addAnonymousId(event, datadogContext) - if (rumContext != null) { - event.addProperty(LogAttributes.RUM_APPLICATION_ID, rumContext.applicationId) - event.addProperty(LogAttributes.RUM_SESSION_ID, rumContext.sessionId) + val applicationId = rumContext?.applicationId + if (applicationId != null && applicationId != RumContext.NULL_UUID) { + event.addProperty(LogAttributes.RUM_APPLICATION_ID, applicationId) + val sessionId = rumContext.session?.id + if (sessionId != null) { + event.addProperty(LogAttributes.RUM_SESSION_ID, sessionId) + } else { + event.remove(LogAttributes.RUM_SESSION_ID) + event.remove(LogAttributes.RUM_VIEW_ID) + event.remove(LogAttributes.RUM_ACTION_ID) + (event.get(VIEW_KEY_NAME) as? JsonObject)?.remove(ID_KEY_NAME) + (event.get(USER_ACTION_KEY_NAME) as? JsonObject)?.remove(ID_KEY_NAME) + } } return event } @@ -168,6 +178,9 @@ internal class WebViewLogEventConsumer( const val DATE_KEY_NAME = "date" const val USER_LOG_EVENT_TYPE = "log" const val INTERNAL_LOG_EVENT_TYPE = "internal_log" + private const val VIEW_KEY_NAME = "view" + private const val USER_ACTION_KEY_NAME = "user_action" + private const val ID_KEY_NAME = "id" const val USR_KEY_NAME = "usr" const val ANONYMOUS_ID_KEY_NAME = "anonymous_id" const val JSON_PARSING_ERROR_MESSAGE = "The bundled web log event could not be deserialized" diff --git a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventConsumer.kt b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventConsumer.kt index 0b3ddf62ed..8bb584a90e 100644 --- a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventConsumer.kt +++ b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventConsumer.kt @@ -43,8 +43,7 @@ internal class WebViewReplayEventConsumer( val sessionReplayEnabled = sessionReplayFeatureContext?.get( SESSION_REPLAY_ENABLED_KEY ) as? Boolean ?: false - if (rumContext != null && - rumContext.sessionState == RumSessionState.TRACKED && + if (rumContext?.session?.state == RumSessionState.TRACKED && sessionReplayEnabled ) { writeScope { diff --git a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventMapper.kt b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventMapper.kt index b603875786..548ac68b82 100644 --- a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventMapper.kt +++ b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventMapper.kt @@ -53,7 +53,7 @@ internal class WebViewReplayEventMapper( ): JsonObject { return JsonObject().apply { addProperty(ENRICHED_RECORD_APPLICATION_ID_KEY, rumContext.applicationId) - addProperty(ENRICHED_RECORD_SESSION_ID_KEY, rumContext.sessionId) + addProperty(ENRICHED_RECORD_SESSION_ID_KEY, rumContext.session?.id) addProperty(ENRICHED_RECORD_VIEW_ID_KEY, browserViewId) add(RECORDS_KEY, JsonArray().apply { add(record) }) } diff --git a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventConsumer.kt b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventConsumer.kt index a9d57d7461..539da580d4 100644 --- a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventConsumer.kt +++ b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventConsumer.kt @@ -46,7 +46,7 @@ internal class WebViewRumEventConsumer( ) ) { datadogContext, writeScope -> val rumContext = contextProvider.getRumContext(datadogContext) - if (rumContext != null && rumContext.sessionState == RumSessionState.TRACKED) { + if (rumContext?.session?.state == RumSessionState.TRACKED) { val sessionReplayFeatureContext = datadogContext.featuresContext[ Feature.SESSION_REPLAY_FEATURE_NAME ] diff --git a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventContextProvider.kt b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventContextProvider.kt index 5ee1706133..bcca1569c4 100644 --- a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventContextProvider.kt +++ b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventContextProvider.kt @@ -14,28 +14,18 @@ import com.datadog.android.internal.rum.rumContext internal class WebViewRumEventContextProvider(private val internalLogger: InternalLogger) { - private var rumFeatureDisabled = false - fun getRumContext(datadogContext: DatadogContext): RumContext? { - if (rumFeatureDisabled) { - return null - } - val rumContext = datadogContext.featuresContext[Feature.RUM_FEATURE_NAME]?.rumContext() - return if (rumContext == null || - rumContext.applicationId == RumContext.NULL_UUID || - rumContext.sessionId == RumContext.NULL_UUID - ) { - rumFeatureDisabled = true + if (rumContext == null || rumContext.applicationId == RumContext.NULL_UUID) { internalLogger.log( InternalLogger.Level.WARN, InternalLogger.Target.USER, - { RUM_NOT_INITIALIZED_WARNING_MESSAGE } + { RUM_NOT_INITIALIZED_WARNING_MESSAGE }, + onlyOnce = true ) - null - } else { - rumContext + return null } + return rumContext } companion object { diff --git a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventMapper.kt b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventMapper.kt index 3fa37bd73c..4a9d461660 100644 --- a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventMapper.kt +++ b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventMapper.kt @@ -59,14 +59,15 @@ internal class WebViewRumEventMapper( } } - if (rumContext != null) { + val rumSession = rumContext?.session + if (rumSession != null) { val application = event.getAsJsonObject(APPLICATION_KEY_NAME)?.asJsonObject ?: JsonObject() val session = event.getAsJsonObject(SESSION_KEY_NAME)?.asJsonObject ?: JsonObject() application.addProperty(ID_KEY_NAME, rumContext.applicationId) - session.addProperty(ID_KEY_NAME, rumContext.sessionId) + session.addProperty(ID_KEY_NAME, rumSession.id) session.addProperty(SESSION_IS_MAIN_PROCESS_NAME, datadogContext.processInfo.isMainProcess) - session.addProperty(SESSION_HAS_TIMESERIES_NAME, rumContext.hasTimeseries) + session.addProperty(SESSION_HAS_TIMESERIES_NAME, rumSession.hasTimeseries) if (!sessionReplayEnabled) { // RUM-4084 disable webview SR if host app doesn't have SR session.remove(SESSION_HAS_REPLAY_NAME) diff --git a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/utils/forge/Configurator.kt b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/utils/forge/Configurator.kt index 44a45e214f..ef5639dd64 100644 --- a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/utils/forge/Configurator.kt +++ b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/utils/forge/Configurator.kt @@ -7,6 +7,7 @@ package com.datadog.android.utils.forge import com.datadog.android.internal.tests.elmyr.RumContextForgeryFactory +import com.datadog.android.internal.tests.elmyr.RumSessionContextForgeryFactory import com.datadog.android.rum.utils.forge.useCommonRumFactories import com.datadog.android.tests.elmyr.useCoreFactories import com.datadog.tools.unit.forge.BaseConfigurator @@ -22,5 +23,6 @@ internal class Configurator : BaseConfigurator() { // RUM forge.useCommonRumFactories() forge.addFactory(RumContextForgeryFactory()) + forge.addFactory(RumSessionContextForgeryFactory()) } } diff --git a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/WebViewTrackingTest.kt b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/WebViewTrackingTest.kt index 0cdfd9477b..6f7ad93681 100644 --- a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/WebViewTrackingTest.kt +++ b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/WebViewTrackingTest.kt @@ -24,6 +24,7 @@ import com.datadog.android.api.storage.NoOpDataWriter import com.datadog.android.api.storage.RawBatchEvent import com.datadog.android.internal.FeatureContextKeys import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.rum.RumSessionState import com.datadog.android.internal.telemetry.InternalTelemetryEvent import com.datadog.android.utils.forge.Configurator @@ -681,6 +682,7 @@ internal class WebViewTrackingTest { @Test fun `M pass web view event to RumWebEventConsumer W consumeWebViewEvent()`( @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext, forge: Forge ) { // Given @@ -698,9 +700,9 @@ internal class WebViewTrackingTest { add( "session", JsonObject().apply { - addProperty("id", fakeRumContext.sessionId) + addProperty("id", fakeSession.id) addProperty("is_main_process", fakeProcessInfo.isMainProcess) - addProperty("has_timeseries", fakeRumContext.hasTimeseries) + addProperty("has_timeseries", fakeSession.hasTimeseries) } ) add("usr", JsonObject().apply { addProperty("anonymous_id", fakeAnonymousId) }) @@ -724,7 +726,10 @@ internal class WebViewTrackingTest { } val fakeFeaturesContext = mapOf( Feature.RUM_FEATURE_NAME to mapOf( - FeatureContextKeys.RUM_CONTEXT to fakeRumContext.copy(sessionState = RumSessionState.TRACKED) + FeatureContextKeys.RUM_CONTEXT to + fakeRumContext.copy( + session = fakeSession.copy(state = RumSessionState.TRACKED) + ) ) ) whenever(mockDatadogContext.featuresContext) doReturn fakeFeaturesContext diff --git a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/DatadogEventBridgeTest.kt b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/DatadogEventBridgeTest.kt index 2722c63461..ea7d3f64b7 100644 --- a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/DatadogEventBridgeTest.kt +++ b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/DatadogEventBridgeTest.kt @@ -11,6 +11,7 @@ import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.FeatureScope import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.rum.RumSessionState import com.datadog.android.utils.forge.Configurator import com.datadog.android.webview.internal.rum.WebViewRumFeature @@ -320,11 +321,20 @@ internal class DatadogEventBridgeTest { } @Test - fun `M return true W getIsTraceSampled() { tracked session, sampled }`(@Forgery fakeRumContext: RumContext) { + fun `M return true W getIsTraceSampled() { tracked session, sampled }`( + @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext + ) { // Given val sessionId = "c5b3c4ab-fa4a-4de9-8199-a522131ec48a" whenever(mockWebViewRumFeature.cachedRumContext).thenReturn( - fakeRumContext.copy(sessionId = sessionId, sessionState = RumSessionState.TRACKED, sessionSampleRate = 100f) + fakeRumContext.copy( + session = fakeSession.copy( + id = sessionId, + state = RumSessionState.TRACKED, + sampleRate = 100f + ) + ) ) whenever(mockWebViewRumFeature.cachedTracingContext) .thenReturn(mapOf("okhttp_interceptor_sample_rate" to 100f)) @@ -337,12 +347,21 @@ internal class DatadogEventBridgeTest { } @Test - fun `M return false W getIsTraceSampled() { tracked session, not sampled }`(@Forgery fakeRumContext: RumContext) { + fun `M return false W getIsTraceSampled() { tracked session, not sampled }`( + @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext + ) { // Given // This UUID hashes to ~50.68%, so at combined rate 40% (100% session * 40% trace) it's not sampled val sessionId = "c5b3c4ab-fa4a-4de9-8199-a522131ec48a" whenever(mockWebViewRumFeature.cachedRumContext).thenReturn( - fakeRumContext.copy(sessionId = sessionId, sessionState = RumSessionState.TRACKED, sessionSampleRate = 100f) + fakeRumContext.copy( + session = fakeSession.copy( + id = sessionId, + state = RumSessionState.TRACKED, + sampleRate = 100f + ) + ) ) whenever(mockWebViewRumFeature.cachedTracingContext) .thenReturn(mapOf("okhttp_interceptor_sample_rate" to 40f)) @@ -386,10 +405,15 @@ internal class DatadogEventBridgeTest { } @Test - fun `M return null W getIsTraceSampled() { session not tracked }`(@Forgery fakeRumContext: RumContext) { + fun `M return null W getIsTraceSampled() { session not tracked }`( + @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext + ) { // Given whenever(mockWebViewRumFeature.cachedRumContext).thenReturn( - fakeRumContext.copy(sessionState = RumSessionState.NOT_TRACKED) + fakeRumContext.copy( + session = fakeSession.copy(state = RumSessionState.NOT_TRACKED) + ) ) // When @@ -400,10 +424,13 @@ internal class DatadogEventBridgeTest { } @Test - fun `M return null W getIsTraceSampled() { no trace sample rate configured }`(@Forgery fakeRumContext: RumContext) { + fun `M return null W getIsTraceSampled() { no trace sample rate configured }`( + @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext + ) { // Given whenever(mockWebViewRumFeature.cachedRumContext).thenReturn( - fakeRumContext.copy(sessionState = RumSessionState.TRACKED) + fakeRumContext.copy(session = fakeSession.copy(state = RumSessionState.TRACKED)) ) whenever(mockWebViewRumFeature.cachedTracingContext).thenReturn(emptyMap()) @@ -416,7 +443,8 @@ internal class DatadogEventBridgeTest { @Test fun `M update decision W getIsTraceSampled() { session rolls over to a different decision }`( - @Forgery fakeRumContext: RumContext + @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext ) { // Given // UUID hashes to ~50.68%: sampled at combined 60% (100% * 60%), not sampled at combined 30% (50% * 60%) @@ -426,16 +454,20 @@ internal class DatadogEventBridgeTest { whenever(mockWebViewRumFeature.cachedRumContext) .thenReturn( fakeRumContext.copy( - sessionId = sessionId, - sessionState = RumSessionState.TRACKED, - sessionSampleRate = 100f + session = fakeSession.copy( + id = sessionId, + state = RumSessionState.TRACKED, + sampleRate = 100f + ) ) ) .thenReturn( fakeRumContext.copy( - sessionId = sessionId, - sessionState = RumSessionState.TRACKED, - sessionSampleRate = 50f + session = fakeSession.copy( + id = sessionId, + state = RumSessionState.TRACKED, + sampleRate = 50f + ) ) ) @@ -450,7 +482,8 @@ internal class DatadogEventBridgeTest { @Test fun `M return null W getIsTraceSampled() { session stops after being tracked }`( - @Forgery fakeRumContext: RumContext + @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext ) { // Given: active tracked session val sessionId = "c5b3c4ab-fa4a-4de9-8199-a522131ec48a" @@ -459,13 +492,20 @@ internal class DatadogEventBridgeTest { whenever(mockWebViewRumFeature.cachedRumContext) .thenReturn( fakeRumContext.copy( - sessionId = sessionId, - sessionState = RumSessionState.TRACKED, - sessionSampleRate = 100f + session = fakeSession.copy( + id = sessionId, + state = RumSessionState.TRACKED, + sampleRate = 100f + ) ) ) .thenReturn( - fakeRumContext.copy(sessionId = sessionId, sessionState = RumSessionState.NOT_TRACKED) + fakeRumContext.copy( + session = fakeSession.copy( + id = sessionId, + state = RumSessionState.NOT_TRACKED + ) + ) ) // When @@ -479,20 +519,25 @@ internal class DatadogEventBridgeTest { @Test fun `M return null then decision W getIsTraceSampled() { new session starts after stop }`( - @Forgery fakeRumContext: RumContext + @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext ) { // Given: no active session initially whenever(mockWebViewRumFeature.cachedTracingContext) .thenReturn(mapOf("okhttp_interceptor_sample_rate" to 100f)) whenever(mockWebViewRumFeature.cachedRumContext) .thenReturn( - fakeRumContext.copy(sessionState = RumSessionState.NOT_TRACKED) + fakeRumContext.copy( + session = fakeSession.copy(state = RumSessionState.NOT_TRACKED) + ) ) .thenReturn( fakeRumContext.copy( - sessionId = "c5b3c4ab-fa4a-4de9-8199-a522131ec48a", - sessionState = RumSessionState.TRACKED, - sessionSampleRate = 100f + session = fakeSession.copy( + id = "c5b3c4ab-fa4a-4de9-8199-a522131ec48a", + state = RumSessionState.TRACKED, + sampleRate = 100f + ) ) ) diff --git a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/log/WebViewLogEventConsumerTest.kt b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/log/WebViewLogEventConsumerTest.kt index 0b57d420c5..db1f3d734b 100644 --- a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/log/WebViewLogEventConsumerTest.kt +++ b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/log/WebViewLogEventConsumerTest.kt @@ -15,7 +15,9 @@ import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.api.storage.DataWriter import com.datadog.android.api.storage.EventBatchWriter import com.datadog.android.api.storage.EventType +import com.datadog.android.internal.FeatureContextKeys import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.log.LogAttributes import com.datadog.android.utils.forge.Configurator import com.datadog.android.utils.verifyLog @@ -36,6 +38,7 @@ import org.junit.jupiter.api.extension.Extensions import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource +import org.junit.jupiter.params.provider.ValueSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoSettings @@ -49,7 +52,9 @@ import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.whenever import org.mockito.quality.Strictness +import java.util.UUID import java.util.stream.Stream +import com.datadog.android.internal.rum.RumContext as SdkRumContext @Extensions( ExtendWith(MockitoExtension::class), @@ -211,9 +216,18 @@ internal class WebViewLogEventConsumerTest { @Test fun `M write a mapped event W consume { user event type, rum context }`( - @Forgery fakeRumContext: RumContext + @Forgery fakeBaseRumContext: RumContext, + @Forgery fakeSession: RumSessionContext, + forge: Forge ) { // Given + val fakeRumContext = fakeBaseRumContext.copy(session = fakeSession) + val fakeBrowserViewId = forge.getForgery().toString() + val fakeBrowserActionId = forge.getForgery().toString() + fakeWebLogEvent.add("view", JsonObject().apply { addProperty("id", fakeBrowserViewId) }) + fakeWebLogEvent.add("user_action", JsonObject().apply { addProperty("id", fakeBrowserActionId) }) + fakeWebLogEvent.addProperty(LogAttributes.RUM_APPLICATION_ID, forge.getForgery().toString()) + fakeWebLogEvent.addProperty(LogAttributes.RUM_SESSION_ID, forge.getForgery().toString()) whenever(mockRumContextProvider.getRumContext(fakeDatadogContext)) doReturn fakeRumContext val expectedDate = fakeTimeOffset + fakeWebLogEvent.get(WebViewLogEventConsumer.DATE_KEY_NAME).asLong @@ -227,6 +241,8 @@ internal class WebViewLogEventConsumerTest { // Then argumentCaptor { verify(mockUserLogsWriter).write(eq(mockEventBatchWriter), capture(), eq(EventType.DEFAULT)) + assertThat(firstValue.getAsJsonObject("view")).hasField("id", fakeBrowserViewId) + assertThat(firstValue.getAsJsonObject("user_action")).hasField("id", fakeBrowserActionId) assertThat(firstValue).hasField(WebViewLogEventConsumer.DATE_KEY_NAME, expectedDate) assertThat(firstValue.ddTags).containsExactlyInAnyOrderElementsOf(expectedTags) assertThat(firstValue).hasField( @@ -235,8 +251,108 @@ internal class WebViewLogEventConsumerTest { ) assertThat(firstValue).hasField( LogAttributes.RUM_SESSION_ID, - fakeRumContext.sessionId + fakeSession.id + ) + } + } + + @Test + fun `M retain application attribution W consume() {no native session}`( + @Forgery fakeRumContext: RumContext, + forge: Forge + ) { + // Given + fakeWebLogEvent.addProperty(LogAttributes.RUM_APPLICATION_ID, forge.getForgery().toString()) + fakeWebLogEvent.addProperty(LogAttributes.RUM_SESSION_ID, forge.getForgery().toString()) + val fakeBrowserViewUrl = forge.aString() + fakeWebLogEvent.addProperty(LogAttributes.RUM_VIEW_ID, forge.getForgery().toString()) + fakeWebLogEvent.addProperty(LogAttributes.RUM_ACTION_ID, forge.getForgery().toString()) + fakeWebLogEvent.add( + "view", + JsonObject().apply { + addProperty("id", forge.getForgery().toString()) + addProperty("url", fakeBrowserViewUrl) + } + ) + fakeWebLogEvent.add( + "user_action", + JsonObject().apply { + addProperty("id", forge.getForgery().toString()) + } + ) + val fakeFeatureContext = mapOf( + FeatureContextKeys.RUM_CONTEXT to SdkRumContext(applicationId = fakeRumContext.applicationId) + ) + fakeDatadogContext = fakeDatadogContext.copy( + featuresContext = mapOf(Feature.RUM_FEATURE_NAME to fakeFeatureContext) + ) + testedConsumer = WebViewLogEventConsumer( + mockSdkCore, + mockUserLogsWriter, + WebViewRumEventContextProvider(mockInternalLogger), + sampleRate = 100f + ) + + // When + testedConsumer.consume(fakeWebLogEvent to WebViewLogEventConsumer.USER_LOG_EVENT_TYPE) + + // Then + argumentCaptor { + verify(mockUserLogsWriter).write(eq(mockEventBatchWriter), capture(), eq(EventType.DEFAULT)) + assertThat(firstValue) + .hasField(LogAttributes.RUM_APPLICATION_ID, fakeRumContext.applicationId) + .doesNotHaveField(LogAttributes.RUM_SESSION_ID) + .doesNotHaveField(LogAttributes.RUM_VIEW_ID) + .doesNotHaveField(LogAttributes.RUM_ACTION_ID) + assertThat(firstValue.getAsJsonObject("view")) + .doesNotHaveField("id") + .hasField("url", fakeBrowserViewUrl) + assertThat(firstValue.getAsJsonObject("user_action")).doesNotHaveField("id") + } + verifyNoInteractions(mockInternalLogger) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun `M preserve browser correlation W consume() {no valid native application}`( + hasZeroApplicationId: Boolean, + forge: Forge + ) { + // Given + val fakeBrowserViewId = forge.getForgery().toString() + val fakeBrowserActionId = forge.getForgery().toString() + fakeWebLogEvent.add("view", JsonObject().apply { addProperty("id", fakeBrowserViewId) }) + fakeWebLogEvent.add("user_action", JsonObject().apply { addProperty("id", fakeBrowserActionId) }) + val fakeBrowserApplicationId = forge.getForgery().toString() + val fakeBrowserSessionId = forge.getForgery().toString() + fakeWebLogEvent.addProperty(LogAttributes.RUM_APPLICATION_ID, fakeBrowserApplicationId) + fakeWebLogEvent.addProperty(LogAttributes.RUM_SESSION_ID, fakeBrowserSessionId) + fakeDatadogContext = fakeDatadogContext.copy( + featuresContext = mapOf( + Feature.RUM_FEATURE_NAME to mapOf( + FeatureContextKeys.RUM_CONTEXT to + SdkRumContext(RumContext.NULL_UUID).takeIf { hasZeroApplicationId } + ) ) + ) + testedConsumer = WebViewLogEventConsumer( + mockSdkCore, + mockUserLogsWriter, + WebViewRumEventContextProvider(mockInternalLogger), + sampleRate = 100f + ) + + // When + testedConsumer.consume(fakeWebLogEvent to WebViewLogEventConsumer.USER_LOG_EVENT_TYPE) + + // Then + argumentCaptor { + verify(mockUserLogsWriter).write(eq(mockEventBatchWriter), capture(), eq(EventType.DEFAULT)) + assertThat(firstValue.getAsJsonObject("view")).hasField("id", fakeBrowserViewId) + assertThat(firstValue.getAsJsonObject("user_action")).hasField("id", fakeBrowserActionId) + assertThat(firstValue) + .hasField(LogAttributes.RUM_APPLICATION_ID, fakeBrowserApplicationId) + .hasField(LogAttributes.RUM_SESSION_ID, fakeBrowserSessionId) } } diff --git a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventConsumerTest.kt b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventConsumerTest.kt index 15a22bdf03..17bb123e4f 100644 --- a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventConsumerTest.kt +++ b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventConsumerTest.kt @@ -16,6 +16,7 @@ import com.datadog.android.api.storage.EventBatchWriter import com.datadog.android.api.storage.EventType import com.datadog.android.core.InternalSdkCore import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.rum.RumSessionState import com.datadog.android.utils.forge.Configurator import com.datadog.android.utils.verifyLog @@ -32,6 +33,7 @@ import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.api.extension.Extensions import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource +import org.junit.jupiter.params.provider.ValueSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoSettings @@ -81,6 +83,9 @@ internal class WebViewReplayEventConsumerTest { @Forgery lateinit var fakeRumContext: RumContext + @Forgery + private lateinit var fakeSession: RumSessionContext + lateinit var fakeSessionReplayFeatureContext: Map lateinit var fakeValidBrowserEvent: JsonObject @@ -96,13 +101,13 @@ internal class WebViewReplayEventConsumerTest { @BeforeEach fun `set up`(forge: Forge) { + fakeRumContext = fakeRumContext.copy(session = fakeSession) fakeSessionReplayFeatureContext = forge.aMap { WebViewReplayEventConsumer.SESSION_REPLAY_ENABLED_KEY to true } fakeValidBrowserEvent = forge.getForgery() - fakeRumContext = fakeRumContext.copy( - sessionState = RumSessionState.TRACKED - ) + fakeRumContext = + fakeRumContext.copy(session = fakeSession.copy(state = RumSessionState.TRACKED)) fakeDatadogContext = fakeDatadogContext.copy( time = fakeDatadogContext.time.copy( serverTimeOffsetMs = fakeServerTimeOffsetInMillis @@ -233,12 +238,13 @@ internal class WebViewReplayEventConsumerTest { verifyNoInteractions(mockDataWriter) } - @Test - fun `M do nothing W consume() { rumContext not there }`() { + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun `M do nothing W consume() {session absent}`(hasApplication: Boolean) { // Given whenever( mockRumContextProvider.getRumContext(fakeDatadogContext) - ) doReturn null + ) doReturn if (hasApplication) fakeRumContext.copy(session = null) else null // When testedConsumer.consume(fakeValidBrowserEvent) @@ -254,7 +260,9 @@ internal class WebViewReplayEventConsumerTest { mockRumContextProvider.getRumContext(fakeDatadogContext) ) doReturn fakeRumContext.copy( - sessionState = forge.anElementFrom(RumSessionState.NOT_TRACKED, RumSessionState.EXPIRED) + session = fakeSession.copy( + state = forge.anElementFrom(RumSessionState.NOT_TRACKED, RumSessionState.EXPIRED) + ) ) // When diff --git a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventMapperTest.kt b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventMapperTest.kt index 2d30c64642..07161b9dc2 100644 --- a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventMapperTest.kt +++ b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/replay/WebViewReplayEventMapperTest.kt @@ -8,6 +8,7 @@ package com.datadog.android.webview.internal.replay import com.datadog.android.api.context.DatadogContext import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.utils.forge.Configurator import com.datadog.android.webview.internal.rum.TimestampOffsetProvider import com.google.gson.JsonObject @@ -46,6 +47,9 @@ internal class WebViewReplayEventMapperTest { @Forgery lateinit var fakeRumContext: RumContext + @Forgery + private lateinit var fakeSession: RumSessionContext + @Forgery lateinit var fakeDatadogContext: DatadogContext @@ -60,6 +64,7 @@ internal class WebViewReplayEventMapperTest { @BeforeEach fun `set up`() { + fakeRumContext = fakeRumContext.copy(session = fakeSession) testedWebViewReplayEventMapper = WebViewReplayEventMapper(fakeWebViewId, mockOffsetProvider) } @@ -163,7 +168,7 @@ internal class WebViewReplayEventMapperTest { assertThat(result.get(WebViewReplayEventMapper.ENRICHED_RECORD_APPLICATION_ID_KEY).asString) .isEqualTo(fakeRumContext.applicationId) assertThat(result.get(WebViewReplayEventMapper.ENRICHED_RECORD_SESSION_ID_KEY).asString) - .isEqualTo(fakeRumContext.sessionId) + .isEqualTo(fakeRumContext.session?.id) assertThat(result.get(WebViewReplayEventMapper.ENRICHED_RECORD_VIEW_ID_KEY).asString) .isEqualTo(fakeBrowserRumViewId) } diff --git a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventConsumerTest.kt b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventConsumerTest.kt index 28dec42465..5ed5e54b29 100644 --- a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventConsumerTest.kt +++ b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventConsumerTest.kt @@ -16,6 +16,7 @@ import com.datadog.android.api.storage.DataWriter import com.datadog.android.api.storage.EventBatchWriter import com.datadog.android.api.storage.EventType import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.rum.RumSessionState import com.datadog.android.rum.model.ActionEvent import com.datadog.android.rum.model.ErrorEvent @@ -41,6 +42,7 @@ import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.api.extension.Extensions import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource +import org.junit.jupiter.params.provider.ValueSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoSettings @@ -114,12 +116,17 @@ internal class WebViewRumEventConsumerTest { @Forgery lateinit var fakeRumContext: RumContext + @Forgery + private lateinit var fakeSession: RumSessionContext + @BoolForgery var fakeSessionReplayEnabled = false @BeforeEach fun `set up`(forge: Forge) { - fakeRumContext = fakeRumContext.copy(sessionState = RumSessionState.TRACKED) + fakeRumContext = fakeRumContext.copy(session = fakeSession) + fakeRumContext = + fakeRumContext.copy(session = fakeSession.copy(state = RumSessionState.TRACKED)) fakeTags = if (forge.aBool()) { forge.aMap { forge.anAlphabeticalString() to forge.anAlphaNumericalString() @@ -245,10 +252,15 @@ internal class WebViewRumEventConsumerTest { verify(mockDataWriter).write(mockEventBatchWriter, fakeMappedViewEvent, EventType.DEFAULT) } - @Test - fun `M not write the mapped event W consume(){ no RUM context, view event }`(forge: Forge) { + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun `M not write the mapped event W consume(){ no session, view event }`( + hasApplication: Boolean, + forge: Forge + ) { // Given - whenever(mockRumContextProvider.getRumContext(fakeDatadogContext)) doReturn null + whenever(mockRumContextProvider.getRumContext(fakeDatadogContext)) doReturn + if (hasApplication) fakeRumContext.copy(session = null) else null val fakeViewEvent: ViewEvent = forge.getForgery() val fakeViewEventAsJson = fakeViewEvent.toJson().asJsonObject whenever( diff --git a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventContextProviderTest.kt b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventContextProviderTest.kt index 1ee528af66..03ef15e078 100644 --- a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventContextProviderTest.kt +++ b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventContextProviderTest.kt @@ -24,7 +24,8 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.EnumSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.times +import org.mockito.kotlin.verifyNoInteractions @Extensions( ExtendWith(MockitoExtension::class), @@ -62,34 +63,50 @@ internal class WebViewRumEventContextProviderTest { } @ParameterizedTest - @EnumSource(InvalidContext::class) - fun `M return null and warn once W getRumContext {invalid context}`(invalidContext: InvalidContext) { + @EnumSource(UnavailableContext::class) + fun `M recover correlation W getRumContext {context becomes available again}`( + unavailableContext: UnavailableContext + ) { // Given - val fakeInvalidContext = when (invalidContext) { - InvalidContext.NO_SNAPSHOT -> null - InvalidContext.NO_APPLICATION -> fakeRumContext.copy(applicationId = RumContext.NULL_UUID) - InvalidContext.NO_SESSION -> fakeRumContext.copy(sessionId = RumContext.NULL_UUID) + val originalContext = fakeDatadogContext.withRumContext(fakeRumContext) + val partialRumContext = when (unavailableContext) { + UnavailableContext.NO_SNAPSHOT -> null + UnavailableContext.ZERO_APPLICATION -> fakeRumContext.copy(applicationId = RumContext.NULL_UUID) + UnavailableContext.APPLICATION_ONLY -> RumContext(applicationId = fakeRumContext.applicationId) } - val fakeContext = fakeDatadogContext.withRumContext(fakeInvalidContext) + val partialContext = fakeDatadogContext.withRumContext(partialRumContext) + val original = testedContextProvider.getRumContext(originalContext) // When - val firstContext = testedContextProvider.getRumContext(fakeContext) - val secondContext = testedContextProvider.getRumContext(fakeContext) + val unavailable = testedContextProvider.getRumContext(partialContext) + val repeated = testedContextProvider.getRumContext(partialContext) + val restored = testedContextProvider.getRumContext(originalContext) // Then - assertThat(firstContext).isNull() - assertThat(secondContext).isNull() - mockInternalLogger.verifyLog( - InternalLogger.Level.WARN, - InternalLogger.Target.USER, - WebViewRumEventContextProvider.RUM_NOT_INITIALIZED_WARNING_MESSAGE - ) - verifyNoMoreInteractions(mockInternalLogger) + assertThat(restored).isEqualTo(original) + assertThat(repeated).isEqualTo(unavailable) + if (unavailableContext == UnavailableContext.NO_SNAPSHOT || + unavailableContext == UnavailableContext.ZERO_APPLICATION + ) { + assertThat(unavailable).isNull() + mockInternalLogger.verifyLog( + InternalLogger.Level.WARN, + InternalLogger.Target.USER, + WebViewRumEventContextProvider.RUM_NOT_INITIALIZED_WARNING_MESSAGE, + onlyOnce = true, + mode = times(2) + ) + } else { + val context = checkNotNull(unavailable) + assertThat(context.applicationId).isEqualTo(fakeRumContext.applicationId) + assertThat(context.session).isNull() + verifyNoInteractions(mockInternalLogger) + } } - enum class InvalidContext { + enum class UnavailableContext { NO_SNAPSHOT, - NO_APPLICATION, - NO_SESSION + ZERO_APPLICATION, + APPLICATION_ONLY } } diff --git a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventMapperTest.kt b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventMapperTest.kt index 0d3a068abd..aa490d3072 100644 --- a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventMapperTest.kt +++ b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventMapperTest.kt @@ -9,6 +9,7 @@ package com.datadog.android.webview.internal.rum import com.datadog.android.api.context.DatadogContext import com.datadog.android.core.internal.utils.DdTagsUtils import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.rum.model.ActionEvent import com.datadog.android.rum.model.ErrorEvent import com.datadog.android.rum.model.LongTaskEvent @@ -52,6 +53,9 @@ internal class WebViewRumEventMapperTest { @Forgery lateinit var fakeRumContext: RumContext + @Forgery + private lateinit var fakeSession: RumSessionContext + @Mock lateinit var mockNativeRumViewsCache: NativeRumViewsCache @@ -68,6 +72,7 @@ internal class WebViewRumEventMapperTest { @BeforeEach fun `set up`(forge: Forge) { + fakeRumContext = fakeRumContext.copy(session = fakeSession) fakeTags = if (forge.aBool()) { forge.aMap { forge.anAlphabeticalString() to forge.anAlphaNumericalString() @@ -635,12 +640,15 @@ internal class WebViewRumEventMapperTest { assertThat(mappedEvent.getAsJsonObject(WebViewRumEventMapper.APPLICATION_KEY_NAME)) .hasField(WebViewRumEventMapper.ID_KEY_NAME, fakeRumContext.applicationId) assertThat(mappedEvent.getAsJsonObject(WebViewRumEventMapper.SESSION_KEY_NAME)) - .hasField(WebViewRumEventMapper.ID_KEY_NAME, fakeRumContext.sessionId) + .hasField(WebViewRumEventMapper.ID_KEY_NAME, fakeSession.id) .hasField( WebViewRumEventMapper.SESSION_IS_MAIN_PROCESS_NAME, fakeDatadogContext.processInfo.isMainProcess ) - .hasField(WebViewRumEventMapper.SESSION_HAS_TIMESERIES_NAME, fakeRumContext.hasTimeseries) + .hasField( + WebViewRumEventMapper.SESSION_HAS_TIMESERIES_NAME, + fakeSession.hasTimeseries + ) assertThat(mappedEvent).hasField( WebViewRumEventMapper.DATE_KEY_NAME, expectedDate diff --git a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventMapperWithoutSRTest.kt b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventMapperWithoutSRTest.kt index 19c7c2a146..6c5943b855 100644 --- a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventMapperWithoutSRTest.kt +++ b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/WebViewRumEventMapperWithoutSRTest.kt @@ -8,6 +8,7 @@ package com.datadog.android.webview.internal.rum import com.datadog.android.api.context.DatadogContext import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.rum.model.ActionEvent import com.datadog.android.rum.model.ErrorEvent import com.datadog.android.rum.model.LongTaskEvent @@ -50,6 +51,9 @@ internal class WebViewRumEventMapperWithoutSRTest { @Forgery lateinit var fakeRumContext: RumContext + @Forgery + private lateinit var fakeSession: RumSessionContext + @Mock lateinit var mockNativeRumViewsCache: NativeRumViewsCache @@ -66,6 +70,7 @@ internal class WebViewRumEventMapperWithoutSRTest { @BeforeEach fun `set up`(forge: Forge) { + fakeRumContext = fakeRumContext.copy(session = fakeSession) fakeTags = if (forge.aBool()) { forge.aMap { forge.anAlphabeticalString() to forge.anAlphaNumericalString() @@ -347,10 +352,10 @@ internal class WebViewRumEventMapperWithoutSRTest { assertThat(mappedEvent.getAsJsonObject(WebViewRumEventMapper.APPLICATION_KEY_NAME)) .hasField(WebViewRumEventMapper.ID_KEY_NAME, fakeRumContext.applicationId) assertThat(mappedEvent.getAsJsonObject(WebViewRumEventMapper.SESSION_KEY_NAME)) - .hasField(WebViewRumEventMapper.ID_KEY_NAME, fakeRumContext.sessionId) + .hasField(WebViewRumEventMapper.ID_KEY_NAME, fakeSession.id) .doesNotHaveField(WebViewRumEventMapper.SESSION_HAS_REPLAY_NAME) assertThat(mappedEvent.getAsJsonObject(WebViewRumEventMapper.SESSION_KEY_NAME)) - .hasField(WebViewRumEventMapper.ID_KEY_NAME, fakeRumContext.sessionId) + .hasField(WebViewRumEventMapper.ID_KEY_NAME, fakeSession.id) .doesNotHaveField(WebViewRumEventMapper.SESSION_HAS_REPLAY_NAME) assertThat(mappedEvent.getAsJsonObject(WebViewRumEventMapper.DD_KEY_NAME)) .doesNotHaveField(WebViewRumEventMapper.DD_REPLAY_STATS) diff --git a/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/cross/CrossFeatureTest.kt b/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/cross/CrossFeatureTest.kt index a2c3bf1006..72773f581d 100644 --- a/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/cross/CrossFeatureTest.kt +++ b/instrumented/integration/src/androidTest/kotlin/com/datadog/android/sdk/integration/cross/CrossFeatureTest.kt @@ -37,7 +37,6 @@ import com.datadog.android.trace.TraceConfiguration import com.datadog.android.trace.api.tracer.DatadogTracer import com.datadog.android.trace.model.SpanEvent import com.datadog.tools.unit.ConditionWatcher -import com.google.gson.JsonNull import com.google.gson.JsonObject import com.google.gson.JsonParser import com.google.gson.JsonPrimitive @@ -461,7 +460,7 @@ class CrossFeatureTest { "Expected log event to not have" + " RUM View ID, but was $actualViewId." ) - .isInstanceOf(JsonNull::class.java) + .isNull() return this } @@ -483,7 +482,7 @@ class CrossFeatureTest { "Expected log event to not have" + " RUM Action ID, but was $actualActionId." ) - .isInstanceOf(JsonNull::class.java) + .isNull() return this } diff --git a/reliability/single-fit/logs/build.gradle.kts b/reliability/single-fit/logs/build.gradle.kts index 9a3c34b26d..15463c38df 100644 --- a/reliability/single-fit/logs/build.gradle.kts +++ b/reliability/single-fit/logs/build.gradle.kts @@ -35,6 +35,7 @@ dependencies { // Testing testImplementation(project(":dd-sdk-android-internal")) + testImplementation(testFixtures(project(":dd-sdk-android-internal"))) testImplementation(project(":tools:unit")) { attributes { attribute( diff --git a/reliability/single-fit/logs/src/test/kotlin/com/datadog/android/logs/integration/LoggerTest.kt b/reliability/single-fit/logs/src/test/kotlin/com/datadog/android/logs/integration/LoggerTest.kt index ef1d5aecb7..d8093ce000 100644 --- a/reliability/single-fit/logs/src/test/kotlin/com/datadog/android/logs/integration/LoggerTest.kt +++ b/reliability/single-fit/logs/src/test/kotlin/com/datadog/android/logs/integration/LoggerTest.kt @@ -16,6 +16,7 @@ import com.datadog.android.core.stub.StubSDKCore import com.datadog.android.event.EventMapper import com.datadog.android.internal.FeatureContextKeys import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.log.LogAttributes import com.datadog.android.log.Logger import com.datadog.android.log.Logs @@ -619,7 +620,7 @@ class LoggerTest { @RepeatedTest(16) fun `M send log with rum context W Logger#Builder#setBundleWithRumEnabled(true) + Logger#log()`( @StringForgery fakeRumApplicationId: String, - @StringForgery fakeRumSessionId: String, + @Forgery fakeRumSession: RumSessionContext, @StringForgery fakeRumViewId: String, @StringForgery fakeRumActionId: String, @StringForgery fakeMessage: String, @@ -629,8 +630,8 @@ class LoggerTest { val testedLogger = Logger.Builder(stubSdkCore).setBundleWithRumEnabled(true).build() stubSdkCore.updateFeatureContext(Feature.RUM_FEATURE_NAME) { it[FeatureContextKeys.RUM_CONTEXT] = RumContext( + session = fakeRumSession, applicationId = fakeRumApplicationId, - sessionId = fakeRumSessionId, viewId = fakeRumViewId, actionId = fakeRumActionId ) @@ -651,7 +652,7 @@ class LoggerTest { assertThat(event0.getString("message")).isEqualTo(fakeMessage) assertThat(event0.getString("status")).isEqualTo(LEVEL_NAMES[fakeLevel]) assertThat(event0.getString("application_id")).isEqualTo(fakeRumApplicationId) - assertThat(event0.getString("session_id")).isEqualTo(fakeRumSessionId) + assertThat(event0.getString("session_id")).isEqualTo(fakeRumSession.id) assertThat(event0.getString("view.id")).isEqualTo(fakeRumViewId) assertThat(event0.getString("user_action.id")).isEqualTo(fakeRumActionId) } diff --git a/reliability/single-fit/logs/src/test/kotlin/com/datadog/android/logs/integration/tests/elmyr/LogsIntegrationForgeConfigurator.kt b/reliability/single-fit/logs/src/test/kotlin/com/datadog/android/logs/integration/tests/elmyr/LogsIntegrationForgeConfigurator.kt index 0113838a4d..cbc00545b3 100644 --- a/reliability/single-fit/logs/src/test/kotlin/com/datadog/android/logs/integration/tests/elmyr/LogsIntegrationForgeConfigurator.kt +++ b/reliability/single-fit/logs/src/test/kotlin/com/datadog/android/logs/integration/tests/elmyr/LogsIntegrationForgeConfigurator.kt @@ -6,6 +6,7 @@ package com.datadog.android.logs.integration.tests.elmyr +import com.datadog.android.internal.tests.elmyr.RumSessionContextForgeryFactory import com.datadog.android.tests.elmyr.useCoreFactories import com.datadog.tools.unit.forge.BaseConfigurator import fr.xgouchet.elmyr.Forge @@ -16,5 +17,6 @@ class LogsIntegrationForgeConfigurator : BaseConfigurator() { super.configure(forge) forge.useJvmFactories() forge.useCoreFactories() + forge.addFactory(RumSessionContextForgeryFactory()) } } diff --git a/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/RumContextPropagationTest.kt b/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/RumContextPropagationTest.kt index 8392c139b9..d20ba170a9 100644 --- a/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/RumContextPropagationTest.kt +++ b/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/RumContextPropagationTest.kt @@ -17,6 +17,7 @@ import com.datadog.android.core.sampling.DeterministicSampler.Companion.MAX_ID import com.datadog.android.core.sampling.DeterministicSampler.Companion.SAMPLER_HASHER import com.datadog.android.core.stub.StubSDKCore import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.okhttp.RumContextPropagationTest.Companion.SAMPLING_THRESHOLD import com.datadog.android.okhttp.tests.elmyr.OkHttpConfigurator import com.datadog.android.okhttp.tests.utils.unregisterGlobalRumMonitor @@ -86,12 +87,18 @@ class RumContextPropagationTest { @Test fun `M send rum sessionId in baggage header W call is made`( @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext, forge: Forge ) { // Given + val rumContext = fakeRumContext.copy( + session = fakeSession.copy( + id = UUID(0, forge.anElementFrom(SAMPLED_IDS)).toString() + ) + ) val accountInfo = forge.getForgery() val userInfo = forge.getForgery() - val datadogContext = forge.aDatadogContextWithRumContext(fakeRumContext, accountInfo, userInfo) + val datadogContext = forge.aDatadogContextWithRumContext(rumContext, accountInfo, userInfo) stubSdkCore = forge.prepareStubSdkCore(datadogContext) Trace.enable(TraceConfiguration.Builder().build(), stubSdkCore) testedClient = prepareClient(stubSdkCore) @@ -106,7 +113,7 @@ class RumContextPropagationTest { listOfNotNull( "account.id=${accountInfo.id}", userInfo.id?.let { "user.id=$it" }, - "session.id=${fakeRumContext.sessionId}" + "session.id=${rumContext.session?.id}" ) ) } @@ -117,10 +124,15 @@ class RumContextPropagationTest { @Test fun `M set header x-datadog-sampling-priority=1 W call is made { sessionId sampled }`( @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext, forge: Forge ) { // Given - val rumContext = fakeRumContext.copy(sessionId = UUID(0, forge.anElementFrom(SAMPLED_IDS)).toString()) + val rumContext = fakeRumContext.copy( + session = fakeSession.copy( + id = UUID(0, forge.anElementFrom(SAMPLED_IDS)).toString() + ) + ) val datadogContext = forge.aDatadogContextWithRumContext(rumContext) stubSdkCore = forge.prepareStubSdkCore(datadogContext) Trace.enable(TraceConfiguration.Builder().build(), stubSdkCore) @@ -139,10 +151,15 @@ class RumContextPropagationTest { @Test fun `M set header x-datadog-sampling-priority=0 W call is made { sessionId not sampled }`( @Forgery fakeRumContext: RumContext, + @Forgery fakeSession: RumSessionContext, forge: Forge ) { // Given - val rumContext = fakeRumContext.copy(sessionId = UUID(0, forge.anElementFrom(DROPPED_IDS)).toString()) + val rumContext = fakeRumContext.copy( + session = fakeSession.copy( + id = UUID(0, forge.anElementFrom(DROPPED_IDS)).toString() + ) + ) val datadogContext = forge.aDatadogContextWithRumContext(rumContext) stubSdkCore = forge.prepareStubSdkCore(datadogContext) Trace.enable(TraceConfiguration.Builder().build(), stubSdkCore) diff --git a/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/TraceCrossProductSamplingTest.kt b/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/TraceCrossProductSamplingTest.kt index 14620f012b..3dfd26d882 100644 --- a/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/TraceCrossProductSamplingTest.kt +++ b/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/TraceCrossProductSamplingTest.kt @@ -12,6 +12,7 @@ import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.SdkFeatureMock import com.datadog.android.core.stub.StubSDKCore import com.datadog.android.internal.rum.RumContext +import com.datadog.android.internal.rum.RumSessionContext import com.datadog.android.internal.sampling.DeterministicSampling import com.datadog.android.okhttp.tests.elmyr.OkHttpConfigurator import com.datadog.android.rum.ExperimentalRumApi @@ -75,8 +76,12 @@ class TraceCrossProductSamplingTest { @Forgery private lateinit var fakeRumContext: RumContext + @Forgery + private lateinit var fakeSession: RumSessionContext + @BeforeEach fun `set up`(forge: Forge) { + fakeRumContext = fakeRumContext.copy(session = fakeSession) stubSdkCore = StubSDKCore(forge) val registry: Any = Datadog::class.java.getStaticValue("registry") val instances: MutableMap = registry.getFieldValue("instances") @@ -290,7 +295,12 @@ class TraceCrossProductSamplingTest { private fun stubRumSessionContext(sessionIdToken: Long, sessionSampleRate: Float) { val sessionId = sessionIdToFakeUuid(sessionIdToken) val datadogContext = stubSdkCore.getDatadogContext().withRumContext( - fakeRumContext.copy(sessionId = sessionId, sessionSampleRate = sessionSampleRate) + fakeRumContext.copy( + session = fakeSession.copy( + id = sessionId, + sampleRate = sessionSampleRate + ) + ) ) val rumFeature = mock { on { name } doReturn Feature.RUM_FEATURE_NAME diff --git a/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/tests/elmyr/OkHttpConfigurator.kt b/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/tests/elmyr/OkHttpConfigurator.kt index 5853b2bfdf..bb9ba1d279 100644 --- a/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/tests/elmyr/OkHttpConfigurator.kt +++ b/reliability/single-fit/okhttp/src/test/kotlin/com/datadog/android/okhttp/tests/elmyr/OkHttpConfigurator.kt @@ -7,6 +7,7 @@ package com.datadog.android.okhttp.tests.elmyr import com.datadog.android.internal.tests.elmyr.RumContextForgeryFactory +import com.datadog.android.internal.tests.elmyr.RumSessionContextForgeryFactory import com.datadog.android.tests.elmyr.useCoreFactories import com.datadog.tools.unit.forge.BaseConfigurator import fr.xgouchet.elmyr.Forge @@ -18,5 +19,6 @@ class OkHttpConfigurator : BaseConfigurator() { forge.useJvmFactories() forge.useCoreFactories() forge.addFactory(RumContextForgeryFactory()) + forge.addFactory(RumSessionContextForgeryFactory()) } } diff --git a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTracerProviderTest.kt b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTracerProviderTest.kt index e4d3467a35..22d8ecd728 100644 --- a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTracerProviderTest.kt +++ b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTracerProviderTest.kt @@ -1084,7 +1084,7 @@ internal class OtelTracerProviderTest { hasResource(fakeOperationName) hasDurationBetween(OP_DURATION_NS, fullDuration) hasApplicationId(fakeRumContext.applicationId) - hasSessionId(fakeRumContext.sessionId) + hasSessionId(fakeRumContext.session?.id) hasViewId(fakeRumViewId.toString()) } } diff --git a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/tests/elmyr/TraceIntegrationForgeConfigurator.kt b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/tests/elmyr/TraceIntegrationForgeConfigurator.kt index 2fbc68b41f..44103b0f4c 100644 --- a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/tests/elmyr/TraceIntegrationForgeConfigurator.kt +++ b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/tests/elmyr/TraceIntegrationForgeConfigurator.kt @@ -7,6 +7,7 @@ package com.datadog.android.trace.integration.tests.elmyr import com.datadog.android.internal.tests.elmyr.RumContextForgeryFactory +import com.datadog.android.internal.tests.elmyr.RumSessionContextForgeryFactory import com.datadog.android.tests.elmyr.useCoreFactories import com.datadog.tools.unit.forge.BaseConfigurator import fr.xgouchet.elmyr.Forge @@ -18,5 +19,6 @@ class TraceIntegrationForgeConfigurator : BaseConfigurator() { forge.useJvmFactories() forge.useCoreFactories() forge.addFactory(RumContextForgeryFactory()) + forge.addFactory(RumSessionContextForgeryFactory()) } }