Skip to content

Commit

Permalink
Improve RUM integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
xgouchet committed Oct 14, 2024
1 parent 4b83197 commit c3ffedd
Show file tree
Hide file tree
Showing 34 changed files with 2,219 additions and 204 deletions.
5 changes: 2 additions & 3 deletions features/dd-sdk-android-rum/api/apiSurface
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ interface com.datadog.android.rum.event.ViewEventMapper : com.datadog.android.ev
override fun map(com.datadog.android.rum.model.ViewEvent): com.datadog.android.rum.model.ViewEvent
data class com.datadog.android.rum.internal.domain.event.ResourceTiming
constructor(Long = 0L, Long = 0L, Long = 0L, Long = 0L, Long = 0L, Long = 0L, Long = 0L, Long = 0L, Long = 0L, Long = 0L)
interface com.datadog.android.rum.internal.monitor.AdvancedNetworkRumMonitor
interface com.datadog.android.rum.internal.monitor.AdvancedNetworkRumMonitor : com.datadog.android.rum.RumMonitor
fun waitForResourceTiming(Any)
fun addResourceTiming(Any, com.datadog.android.rum.internal.domain.event.ResourceTiming)
fun notifyInterceptorInstantiated()
Expand Down Expand Up @@ -223,15 +223,14 @@ abstract class com.datadog.android.rum.tracking.ActivityLifecycleTrackingStrateg
override fun onActivityStopped(android.app.Activity)
override fun onActivityCreated(android.app.Activity, android.os.Bundle?)
override fun onActivityResumed(android.app.Activity)
protected fun convertToRumAttributes(android.content.Intent?): Map<String, Any?>
protected fun convertToRumAttributes(android.os.Bundle?): Map<String, Any?>
protected fun <T> withSdkCore((com.datadog.android.api.feature.FeatureSdkCore) -> T): T?
class com.datadog.android.rum.tracking.ActivityViewTrackingStrategy : ActivityLifecycleTrackingStrategy, ViewTrackingStrategy
constructor(Boolean, ComponentPredicate<android.app.Activity> = AcceptAllActivities())
override fun onActivityResumed(android.app.Activity)
override fun onActivityStopped(android.app.Activity)
override fun equals(Any?): Boolean
override fun hashCode(): Int
fun android.os.Bundle?.convertToRumViewAttributes(): Map<String, Any?>
interface com.datadog.android.rum.tracking.ComponentPredicate<T>
fun accept(T): Boolean
fun getViewName(T): String?
Expand Down
8 changes: 5 additions & 3 deletions features/dd-sdk-android-rum/api/dd-sdk-android-rum.api
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ public abstract class com/datadog/android/rum/internal/instrumentation/gestures/
public fun onScroll (Landroid/view/MotionEvent;Landroid/view/MotionEvent;FF)Z
}

public abstract interface class com/datadog/android/rum/internal/monitor/AdvancedNetworkRumMonitor {
public abstract interface class com/datadog/android/rum/internal/monitor/AdvancedNetworkRumMonitor : com/datadog/android/rum/RumMonitor {
public abstract fun addResourceTiming (Ljava/lang/Object;Lcom/datadog/android/rum/internal/domain/event/ResourceTiming;)V
public abstract fun notifyInterceptorInstantiated ()V
public abstract fun startResource (Lcom/datadog/android/rum/resource/ResourceId;Lcom/datadog/android/rum/RumResourceMethod;Ljava/lang/String;Ljava/util/Map;)V
Expand Down Expand Up @@ -5062,8 +5062,6 @@ public class com/datadog/android/rum/tracking/AcceptAllSupportFragments : com/da
public abstract class com/datadog/android/rum/tracking/ActivityLifecycleTrackingStrategy : android/app/Application$ActivityLifecycleCallbacks, com/datadog/android/rum/tracking/TrackingStrategy {
protected field sdkCore Lcom/datadog/android/api/feature/FeatureSdkCore;
public fun <init> ()V
protected final fun convertToRumAttributes (Landroid/content/Intent;)Ljava/util/Map;
protected final fun convertToRumAttributes (Landroid/os/Bundle;)Ljava/util/Map;
protected final fun getSdkCore ()Lcom/datadog/android/api/feature/FeatureSdkCore;
public fun onActivityCreated (Landroid/app/Activity;Landroid/os/Bundle;)V
public fun onActivityDestroyed (Landroid/app/Activity;)V
Expand All @@ -5088,6 +5086,10 @@ public final class com/datadog/android/rum/tracking/ActivityViewTrackingStrategy
public fun onActivityStopped (Landroid/app/Activity;)V
}

public final class com/datadog/android/rum/tracking/BundleExtKt {
public static final fun convertToRumViewAttributes (Landroid/os/Bundle;)Ljava/util/Map;
}

public abstract interface class com/datadog/android/rum/tracking/ComponentPredicate {
public abstract fun accept (Ljava/lang/Object;)Z
public abstract fun getViewName (Ljava/lang/Object;)Ljava/lang/String;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package com.datadog.android.rum.internal.monitor
import com.datadog.android.lint.InternalApi
import com.datadog.android.rum.RumAttributes
import com.datadog.android.rum.RumErrorSource
import com.datadog.android.rum.RumMonitor
import com.datadog.android.rum.RumResourceKind
import com.datadog.android.rum.RumResourceMethod
import com.datadog.android.rum.internal.domain.event.ResourceTiming
Expand All @@ -19,7 +20,7 @@ import com.datadog.android.rum.resource.ResourceId
*/
@SuppressWarnings("UndocumentedPublicFunction")
@InternalApi
interface AdvancedNetworkRumMonitor {
interface AdvancedNetworkRumMonitor : RumMonitor {

@InternalApi
fun waitForResourceTiming(key: Any)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ package com.datadog.android.rum.tracking
import android.app.Activity
import android.app.Application
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.annotation.MainThread
import com.datadog.android.api.InternalLogger
Expand Down Expand Up @@ -106,57 +105,6 @@ abstract class ActivityLifecycleTrackingStrategy :

// endregion

// region Utils

/**
* Maps the Bundle key - value properties into compatible attributes for the Rum Events.
* @param intent the [Intent] we need to transform. Returns an empty Map if this is null.
*/
protected fun convertToRumAttributes(intent: Intent?): Map<String, Any?> {
if (intent == null) return emptyMap()

val attributes = mutableMapOf<String, Any?>()

intent.action?.let {
attributes[INTENT_ACTION_TAG] = it
}
intent.dataString?.let {
attributes[INTENT_URI_TAG] = it
}

intent.safeExtras?.let { bundle ->
bundle.keySet().forEach {
// TODO RUM-503 Bundle#get is deprecated, but there is no replacement for it.
// Issue is opened in the Google Issue Tracker.
@Suppress("DEPRECATION")
attributes["$ARGUMENT_TAG.$it"] = bundle.get(it)
}
}

return attributes
}

/**
* Maps the Bundle key - value properties into compatible attributes for the Rum Events.
* @param bundle the Bundle we need to transform. Returns an empty Map if this is null.
*/
protected fun convertToRumAttributes(bundle: Bundle?): Map<String, Any?> {
if (bundle == null) return emptyMap()

val attributes = mutableMapOf<String, Any?>()

bundle.keySet().forEach {
// TODO RUM-503 Bundle#get is deprecated, but there is no replacement for it.
// Issue is opened in the Google Issue Tracker.
@Suppress("DEPRECATION")
attributes["$ARGUMENT_TAG.$it"] = bundle.get(it)
}

return attributes
}

// endregion

// region Helper

/**
Expand All @@ -181,24 +129,9 @@ abstract class ActivityLifecycleTrackingStrategy :
}
}

private val Intent.safeExtras: Bundle?
get() = try {
// old Androids can throw different exceptions here making native calls
extras
} catch (@Suppress("TooGenericExceptionCaught") e: Exception) {
internalLogger.log(
InternalLogger.Level.ERROR,
InternalLogger.Target.USER,
{ "Error getting Intent extras, ignoring it." },
e
)
null
}
// endregion

internal companion object {
internal const val ARGUMENT_TAG = "view.arguments"
internal const val INTENT_ACTION_TAG = "view.intent.action"
internal const val INTENT_URI_TAG = "view.intent.uri"

internal const val EXTRA_SYNTHETICS_TEST_ID = "_dd.synthetics.test_id"
internal const val EXTRA_SYNTHETICS_RESULT_ID = "_dd.synthetics.result_id"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
package com.datadog.android.rum.tracking

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.annotation.MainThread
import com.datadog.android.api.InternalLogger
import com.datadog.android.core.internal.utils.scheduleSafe
import com.datadog.android.rum.GlobalRumMonitor
import com.datadog.android.rum.RumMonitor
Expand Down Expand Up @@ -101,9 +104,47 @@ constructor(
return withSdkCore { GlobalRumMonitor.get(it) }
}

/**
* Maps the Bundle key - value properties into compatible attributes for the Rum Events.
* @param intent the [Intent] we need to transform. Returns an empty Map if this is null.
*/
private fun convertToRumAttributes(intent: Intent?): Map<String, Any?> {
if (intent == null) return emptyMap()

val attributes = mutableMapOf<String, Any?>()

intent.action?.let {
attributes[INTENT_ACTION_TAG] = it
}
intent.dataString?.let {
attributes[INTENT_URI_TAG] = it
}

attributes.putAll(intent.safeExtras.convertToRumViewAttributes())

return attributes
}

private val Intent.safeExtras: Bundle?
get() = try {
// old Androids can throw different exceptions here making native calls
extras
} catch (@Suppress("TooGenericExceptionCaught") e: Exception) {
internalLogger.log(
InternalLogger.Level.ERROR,
InternalLogger.Target.USER,
{ "Error getting Intent extras, ignoring it." },
e
)
null
}

// endregion

internal companion object {
private const val STOP_VIEW_DELAY_MS = 200L

internal const val INTENT_ACTION_TAG = "view.intent.action"
internal const val INTENT_URI_TAG = "view.intent.uri"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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.rum.tracking

import android.os.Bundle

internal const val ARGUMENT_TAG = "view.arguments"

/**
* Converts this bundle into a Map of attributes to be included in a RUM View event.
*/
fun Bundle?.convertToRumViewAttributes(): Map<String, Any?> {
if (this == null) return emptyMap()

val attributes = mutableMapOf<String, Any?>()

keySet().forEach {
// TODO RUM-503 Bundle#get is deprecated, but there is no replacement for it.
// Issue is opened in the Google Issue Tracker.
@Suppress("DEPRECATION")
attributes["$ARGUMENT_TAG.$it"] = get(it)
}

return attributes
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ internal constructor(
if (rumFeature != null && rumMonitor != null) {
AndroidXFragmentLifecycleCallbacks(
argumentsProvider = {
if (trackArguments) convertToRumAttributes(it.arguments) else emptyMap()
if (trackArguments) it.arguments.convertToRumViewAttributes() else emptyMap()
},
componentPredicate = supportFragmentComponentPredicate,
rumMonitor = rumMonitor,
Expand All @@ -96,7 +96,7 @@ internal constructor(
) {
OreoFragmentLifecycleCallbacks(
argumentsProvider = {
if (trackArguments) convertToRumAttributes(it.arguments) else emptyMap()
if (trackArguments) it.arguments.convertToRumViewAttributes() else emptyMap()
},
componentPredicate = defaultFragmentComponentPredicate,
rumMonitor = rumMonitor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class NavigationViewTrackingStrategy(
) {
val rumMonitor = withSdkCore { GlobalRumMonitor.get(it) }
componentPredicate.runIfValid(destination, internalLogger) {
val attributes = if (trackArguments) convertToRumAttributes(arguments) else emptyMap()
val attributes = if (trackArguments) arguments.convertToRumViewAttributes() else emptyMap()
val viewName = componentPredicate.resolveViewName(destination)
rumMonitor?.startView(destination, viewName, attributes)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package com.datadog.android.rum.tracking

import android.os.Bundle
import com.datadog.android.rum.utils.forge.Configurator
import fr.xgouchet.elmyr.Forge
import fr.xgouchet.elmyr.junit5.ForgeConfiguration
import fr.xgouchet.elmyr.junit5.ForgeExtension
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.extension.Extensions
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.junit.jupiter.MockitoSettings
import org.mockito.quality.Strictness

@Extensions(
ExtendWith(ForgeExtension::class),
ExtendWith(MockitoExtension::class)
)
@MockitoSettings(strictness = Strictness.LENIENT)
@ForgeConfiguration(Configurator::class)
class BundleExtTest {
@Test
fun `M return empty map W convertToRumViewAttributes() {null bundle}`() {
// Given
val bundle: Bundle? = null

// When
val result = bundle.convertToRumViewAttributes()

// Then

assertThat(result).isEmpty()
}

@Test
fun `M return empty map W convertToRumViewAttributes() {empty bundle}`() {
// Given
val bundle = Bundle()

// When
val result = bundle.convertToRumViewAttributes()

// Then

assertThat(result).isEmpty()
}

@Test
fun `M return map with String attributes W convertToRumViewAttributes() {bundle}`(
forge: Forge
) {
// Given
val expectedAttributes = mutableMapOf<String, Any?>()
val bundle = Bundle()
repeat(forge.aSmallInt()) {
val key = forge.anAlphabeticalString()
val value = forge.aNullable { aString() }
bundle.putString(key, value)
expectedAttributes["view.arguments.$key"] = value
}

// When
val result = bundle.convertToRumViewAttributes()

// Then
assertThat(result).isEqualTo(expectedAttributes)
}

@Test
fun `M return map with Int attributes W convertToRumViewAttributes() {bundle}`(
forge: Forge
) {
// Given
val expectedAttributes = mutableMapOf<String, Any?>()
val bundle = Bundle()
repeat(forge.aSmallInt()) {
val key = forge.anAlphabeticalString()
val value = forge.anInt()
bundle.putInt(key, value)
expectedAttributes["view.arguments.$key"] = value
}

// When
val result = bundle.convertToRumViewAttributes()

// Then
assertThat(result).isEqualTo(expectedAttributes)
}

@Test
fun `M return map with Float attributes W convertToRumViewAttributes() {bundle}`(
forge: Forge
) {
// Given
val expectedAttributes = mutableMapOf<String, Any?>()
val bundle = Bundle()
repeat(forge.aSmallInt()) {
val key = forge.anAlphabeticalString()
val value = forge.aFloat()
bundle.putFloat(key, value)
expectedAttributes["view.arguments.$key"] = value
}

// When
val result = bundle.convertToRumViewAttributes()

// Then
assertThat(result).isEqualTo(expectedAttributes)
}
}
Loading

0 comments on commit c3ffedd

Please sign in to comment.