Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve RUM integration tests #2317

Merged
merged 1 commit into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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}`(
xgouchet marked this conversation as resolved.
Show resolved Hide resolved
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