diff --git a/example-new-architecture/android/app/build.gradle b/example-new-architecture/android/app/build.gradle index 972cde170..26e1bd033 100644 --- a/example-new-architecture/android/app/build.gradle +++ b/example-new-architecture/android/app/build.gradle @@ -153,6 +153,15 @@ dependencies { } else { implementation jscFlavor } + + constraints { + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0") { + because("kotlin-stdlib-jdk7 is now a part of kotlin-stdlib") + } + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0") { + because("kotlin-stdlib-jdk8 is now a part of kotlin-stdlib") + } + } } apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) diff --git a/example-new-architecture/ios/Podfile.lock b/example-new-architecture/ios/Podfile.lock index 9aa844b7c..8d125979a 100644 --- a/example-new-architecture/ios/Podfile.lock +++ b/example-new-architecture/ios/Podfile.lock @@ -1,13 +1,23 @@ PODS: - boost (1.76.0) - CocoaAsyncSocket (7.6.5) - - DatadogSDK (1.22.0) - - DatadogSDKCrashReporting (1.22.0): - - DatadogSDK (= 1.22.0) - - PLCrashReporter (~> 1.11.0) + - DatadogCore (2.2.1): + - DatadogInternal (= 2.2.1) + - DatadogCrashReporting (2.2.1): + - DatadogInternal (= 2.2.1) + - PLCrashReporter (~> 1.11.1) + - DatadogInternal (2.2.1) + - DatadogLogs (2.2.1): + - DatadogInternal (= 2.2.1) + - DatadogRUM (2.2.1): + - DatadogInternal (= 2.2.1) - DatadogSDKReactNative (1.8.5): - - DatadogSDK (~> 1.22.0) - - DatadogSDKCrashReporting (~> 1.22.0) + - DatadogCore (~> 2.2.1) + - DatadogCrashReporting (~> 2.2.1) + - DatadogLogs (~> 2.2.1) + - DatadogRUM (~> 2.2.1) + - DatadogTrace (~> 2.2.1) + - DatadogWebViewTracking (~> 2.2.1) - RCT-Folly (= 2021.07.22.00) - RCTRequired - RCTTypeSafety @@ -16,6 +26,10 @@ PODS: - React-RCTFabric - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core + - DatadogTrace (2.2.1): + - DatadogInternal (= 2.2.1) + - DatadogWebViewTracking (2.2.1): + - DatadogInternal (= 2.2.1) - DoubleConversion (1.1.6) - FBLazyVector (0.71.10) - FBReactNativeSpec (0.71.10): @@ -839,8 +853,13 @@ DEPENDENCIES: SPEC REPOS: trunk: - CocoaAsyncSocket - - DatadogSDK - - DatadogSDKCrashReporting + - DatadogCore + - DatadogCrashReporting + - DatadogInternal + - DatadogLogs + - DatadogRUM + - DatadogTrace + - DatadogWebViewTracking - Flipper - Flipper-Boost-iOSX - Flipper-DoubleConversion @@ -940,9 +959,14 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 57d2868c099736d80fcd648bf211b4431e51a558 CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 - DatadogSDK: 46e1a7363bc3130fae151ec4aedf11ea291517c5 - DatadogSDKCrashReporting: 24a0e6ec6db905cdeb174721962a7941b2f8a9d2 - DatadogSDKReactNative: f5aebd969f859454689ca16326520c65a4eb3725 + DatadogCore: 12218dd1bed5db394c5e26ec59dd793413ae55b9 + DatadogCrashReporting: d094c1eb1ecce59dbb6b1062b2e524dfa6579fc9 + DatadogInternal: bfa2b823bd47511425d696d36a1bc77c4d06b2f4 + DatadogLogs: a0eafa7bd2103511eac07bcd2ff95c851123e29b + DatadogRUM: 1e027ccfe4ba1eb81a185f3c58e0909bb12811be + DatadogSDKReactNative: 1c9c8a495f19bc77059c4d131d5b3be91b34bb7e + DatadogTrace: 74dc91a7a80e746dc4ef1af6d0db1735b5bfd993 + DatadogWebViewTracking: 9ca93299a2c900c68ba080f6e800fae1fa3c6b61 DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 FBLazyVector: ddb55c55295ea51ed98aa7e2e08add2f826309d5 FBReactNativeSpec: 33a87f65f1a467d5f63d11d0cc106a10d3b0639d diff --git a/example-new-architecture/metro.config.js b/example-new-architecture/metro.config.js index b011c48c7..2f9c5a36e 100644 --- a/example-new-architecture/metro.config.js +++ b/example-new-architecture/metro.config.js @@ -1,4 +1,36 @@ +const path = require('path'); +const exclusionList = require('metro-config/src/defaults/exclusionList'); +const escape = require('escape-string-regexp'); +const pakCore = require('../packages/core/package.json'); + +const root = path.resolve(__dirname, '..'); + +const modules = Object.keys({ + ...pakCore.peerDependencies, +}); + module.exports = { + projectRoot: __dirname, + watchFolders: [root], + + // We need to make sure that only one version is loaded for peerDependencies + // So we blacklist them at the root, and alias them to the versions in example's node_modules + // This block is very important, because otherwise things like React can be packed multiple times + // while it should be only one React instance in the runtime. exclusionList relies on the modules which are + // declared as peer dependencies in the core package. + resolver: { + blacklistRE: exclusionList( + modules.map( + m => new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`), + ), + ), + + extraNodeModules: modules.reduce((acc, name) => { + acc[name] = path.join(__dirname, 'node_modules', name); + return acc; + }, {}), + }, + transformer: { getTransformOptions: async () => ({ transform: { diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 65e1fc93a..ebd59c582 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -166,6 +166,15 @@ dependencies { } else { implementation jscFlavor } + + constraints { + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0") { + because("kotlin-stdlib-jdk7 is now a part of kotlin-stdlib") + } + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0") { + because("kotlin-stdlib-jdk8 is now a part of kotlin-stdlib") + } + } } apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) diff --git a/example/android/build.gradle b/example/android/build.gradle index 4dcd21c1c..0aa14077a 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -2,7 +2,6 @@ buildscript { ext { - RNNKotlinVersion = "1.5.31" buildToolsVersion = "33.0.0" minSdkVersion = 21 compileSdkVersion = 33 @@ -16,7 +15,7 @@ buildscript { mavenCentral() } dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.21" classpath("com.android.tools.build:gradle:7.3.1") classpath("com.facebook.react:react-native-gradle-plugin") } diff --git a/example/ios/Podfile b/example/ios/Podfile index 087ef4640..64c60a6cf 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -22,6 +22,8 @@ if linkage != nil end target 'ddSdkReactnativeExample' do + pod 'DatadogSDKReactNative', :path => '../../packages/core/DatadogSDKReactNative.podspec', :testspecs => ['Tests'] + config = use_native_modules! # Flags change depending on the env values. @@ -59,7 +61,7 @@ target 'ddSdkReactnativeExample' do ) __apply_Xcode_12_5_M1_post_install_workaround(installer) # Enable `DD_SDK_COMPILED_FOR_TESTING` condition when compiling `DatadogSDK` dependency: - datadog_sdk_target = installer.pods_project.targets.detect {|t| t.name == "DatadogSDK" } + datadog_sdk_target = installer.pods_project.targets.detect {|t| ["DatadogCore", "DatadogRUM", "DatadogLogs", "DatadogInternal", "DatadogTrace", "DatadogCrashReporting"].include?(t.name) } datadog_sdk_target.build_configurations.each do |config| config.build_settings['SWIFT_ACTIVE_COMPILATION_CONDITIONS'] = '$(inherited) DD_SDK_COMPILED_FOR_TESTING' end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index f763a338c..4744edc89 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,13 +1,35 @@ PODS: - boost (1.76.0) - - DatadogSDK (1.22.0) - - DatadogSDKCrashReporting (1.22.0): - - DatadogSDK (= 1.22.0) - - PLCrashReporter (~> 1.11.0) + - DatadogCore (2.2.1): + - DatadogInternal (= 2.2.1) + - DatadogCrashReporting (2.2.1): + - DatadogInternal (= 2.2.1) + - PLCrashReporter (~> 1.11.1) + - DatadogInternal (2.2.1) + - DatadogLogs (2.2.1): + - DatadogInternal (= 2.2.1) + - DatadogRUM (2.2.1): + - DatadogInternal (= 2.2.1) - DatadogSDKReactNative (1.8.5): - - DatadogSDK (~> 1.22.0) - - DatadogSDKCrashReporting (~> 1.22.0) + - DatadogCore (~> 2.2.1) + - DatadogCrashReporting (~> 2.2.1) + - DatadogLogs (~> 2.2.1) + - DatadogRUM (~> 2.2.1) + - DatadogTrace (~> 2.2.1) + - DatadogWebViewTracking (~> 2.2.1) - React-Core + - DatadogSDKReactNative/Tests (1.8.5): + - DatadogCore (~> 2.2.1) + - DatadogCrashReporting (~> 2.2.1) + - DatadogLogs (~> 2.2.1) + - DatadogRUM (~> 2.2.1) + - DatadogTrace (~> 2.2.1) + - DatadogWebViewTracking (~> 2.2.1) + - React-Core + - DatadogTrace (2.2.1): + - DatadogInternal (= 2.2.1) + - DatadogWebViewTracking (2.2.1): + - DatadogInternal (= 2.2.1) - DoubleConversion (1.1.6) - FBLazyVector (0.71.10) - FBReactNativeSpec (0.71.10): @@ -387,7 +409,8 @@ PODS: DEPENDENCIES: - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) - - "DatadogSDKReactNative (from `../node_modules/@datadog/mobile-react-native`)" + - DatadogSDKReactNative (from `../../packages/core/DatadogSDKReactNative.podspec`) + - DatadogSDKReactNative/Tests (from `../../packages/core/DatadogSDKReactNative.podspec`) - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`) @@ -433,8 +456,13 @@ DEPENDENCIES: SPEC REPOS: trunk: - - DatadogSDK - - DatadogSDKCrashReporting + - DatadogCore + - DatadogCrashReporting + - DatadogInternal + - DatadogLogs + - DatadogRUM + - DatadogTrace + - DatadogWebViewTracking - fmt - HMSegmentedControl - libevent @@ -444,7 +472,7 @@ EXTERNAL SOURCES: boost: :podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec" DatadogSDKReactNative: - :path: "../node_modules/@datadog/mobile-react-native" + :path: "../../packages/core/DatadogSDKReactNative.podspec" DoubleConversion: :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" FBLazyVector: @@ -528,9 +556,14 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 57d2868c099736d80fcd648bf211b4431e51a558 - DatadogSDK: 46e1a7363bc3130fae151ec4aedf11ea291517c5 - DatadogSDKCrashReporting: 24a0e6ec6db905cdeb174721962a7941b2f8a9d2 - DatadogSDKReactNative: 1cd2c5ca0673e9fc83ad4b44b2c14f90cce9d53c + DatadogCore: 12218dd1bed5db394c5e26ec59dd793413ae55b9 + DatadogCrashReporting: d094c1eb1ecce59dbb6b1062b2e524dfa6579fc9 + DatadogInternal: bfa2b823bd47511425d696d36a1bc77c4d06b2f4 + DatadogLogs: a0eafa7bd2103511eac07bcd2ff95c851123e29b + DatadogRUM: 1e027ccfe4ba1eb81a185f3c58e0909bb12811be + DatadogSDKReactNative: 6f16f15e8b3d5a60c5799d604843a0feb2010c9b + DatadogTrace: 74dc91a7a80e746dc4ef1af6d0db1735b5bfd993 + DatadogWebViewTracking: 9ca93299a2c900c68ba080f6e800fae1fa3c6b61 DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 FBLazyVector: ddb55c55295ea51ed98aa7e2e08add2f826309d5 FBReactNativeSpec: 90fc1a90b4b7a171e0a7c20ea426c1bf6ce4399c @@ -576,6 +609,6 @@ SPEC CHECKSUMS: RNScreens: f7ad633b2e0190b77b6a7aab7f914fad6f198d8d Yoga: e7ea9e590e27460d28911403b894722354d73479 -PODFILE CHECKSUM: ffc581c91d71c08d4a9374af21697b3d934fc7cf +PODFILE CHECKSUM: 59a4878659fbb7b053887dd9eec3df44ca9e0b28 COCOAPODS: 1.12.1 diff --git a/example/ios/PodfileForTests b/example/ios/PodfileForTests index aeb3d2919..eaa582754 100644 --- a/example/ios/PodfileForTests +++ b/example/ios/PodfileForTests @@ -44,7 +44,7 @@ target 'ddSdkReactnativeExample' do # An absolute path to your application root. :app_path => "#{Pod::Config.instance.installation_root}/.." ) - + target 'ddSdkReactnativeExampleTests' do inherit! :complete # Pods for testing @@ -59,7 +59,7 @@ target 'ddSdkReactnativeExample' do ) __apply_Xcode_12_5_M1_post_install_workaround(installer) # Enable `DD_SDK_COMPILED_FOR_TESTING` condition when compiling `DatadogSDK` dependency: - datadog_sdk_target = installer.pods_project.targets.detect {|t| t.name == "DatadogSDK" } + datadog_sdk_target = installer.pods_project.targets.detect {|t| ["DatadogCore", "DatadogRUM", "DatadogLogs", "DatadogInternal", "DatadogTrace", "DatadogCrashReporting"].include?(t.name) } datadog_sdk_target.build_configurations.each do |config| config.build_settings['SWIFT_ACTIVE_COMPILATION_CONDITIONS'] = '$(inherited) DD_SDK_COMPILED_FOR_TESTING' end diff --git a/packages/core/DatadogSDKReactNative.podspec b/packages/core/DatadogSDKReactNative.podspec index 9b2846f88..25a954606 100644 --- a/packages/core/DatadogSDKReactNative.podspec +++ b/packages/core/DatadogSDKReactNative.podspec @@ -17,9 +17,13 @@ Pod::Spec.new do |s| s.source_files = "ios/Sources/*.{h,m,mm,swift}" s.dependency "React-Core" - s.dependency 'DatadogSDK', '~> 1.22.0' - s.dependency 'DatadogSDKCrashReporting', '~> 1.22.0' - + s.dependency 'DatadogCore', '~> 2.2.1' + s.dependency 'DatadogLogs', '~> 2.2.1' + s.dependency 'DatadogTrace', '~> 2.2.1' + s.dependency 'DatadogRUM', '~> 2.2.1' + s.dependency 'DatadogCrashReporting', '~> 2.2.1' + s.dependency 'DatadogWebViewTracking', '~> 2.2.1' + s.test_spec 'Tests' do |test_spec| test_spec.source_files = 'ios/Tests/*.swift' end diff --git a/packages/core/android/build.gradle b/packages/core/android/build.gradle index 493864754..10b8a18e7 100644 --- a/packages/core/android/build.gradle +++ b/packages/core/android/build.gradle @@ -12,7 +12,7 @@ buildscript { classpath 'com.android.tools.build:gradle:7.2.2' // noinspection DifferentKotlinGradleVersion classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jlleitschuh.gradle:ktlint-gradle:10.2.1" + classpath "org.jlleitschuh.gradle:ktlint-gradle:11.5.1" classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.18.0" classpath 'com.github.bjoernq:unmockplugin:0.7.9' } @@ -159,7 +159,10 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" compileOnly "com.squareup.okhttp3:okhttp:3.12.13" - implementation "com.datadoghq:dd-sdk-android:1.19.2" + implementation "com.datadoghq:dd-sdk-android-rum:2.0.0" + implementation "com.datadoghq:dd-sdk-android-logs:2.0.0" + implementation "com.datadoghq:dd-sdk-android-trace:2.0.0" + implementation "com.datadoghq:dd-sdk-android-webview:2.0.0" testImplementation "org.junit.platform:junit-platform-launcher:1.6.2" testImplementation "org.junit.jupiter:junit-jupiter-api:5.6.2" testImplementation "org.junit.jupiter:junit-jupiter-engine:5.6.2" @@ -204,7 +207,6 @@ ktlint { outputToConsole.set(true) ignoreFailures.set(false) enableExperimentalRules.set(false) - additionalEditorconfigFile.set(file("${project.rootDir}/script/config/.editorconfig")) filter { exclude("**/generated/**") include("**/kotlin/**") diff --git a/packages/core/android/gradle.properties b/packages/core/android/gradle.properties index 0401e18b4..4f16df59e 100644 --- a/packages/core/android/gradle.properties +++ b/packages/core/android/gradle.properties @@ -1,4 +1,4 @@ -DdSdkReactNative_kotlinVersion=1.6.21 +DdSdkReactNative_kotlinVersion=1.7.21 DdSdkReactNative_compileSdkVersion=31 DdSdkReactNative_buildToolsVersion=31.0.0 DdSdkReactNative_targetSdkVersion=31 diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt index 8a501fecd..d037f8bdd 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt @@ -8,25 +8,65 @@ package com.datadog.reactnative import android.content.Context import com.datadog.android.Datadog +import com.datadog.android._InternalProxy import com.datadog.android.core.configuration.Configuration -import com.datadog.android.core.configuration.Credentials +import com.datadog.android.log.Logs +import com.datadog.android.log.LogsConfiguration import com.datadog.android.privacy.TrackingConsent -import com.datadog.android.rum.GlobalRum +import com.datadog.android.rum.GlobalRumMonitor +import com.datadog.android.rum.Rum +import com.datadog.android.rum.RumConfiguration import com.datadog.android.rum.RumMonitor +import com.datadog.android.trace.Trace +import com.datadog.android.trace.TraceConfiguration +import com.datadog.android.webview.WebViewTracking internal class DatadogSDKWrapper : DatadogWrapper { + // We use Kotlin backing field here to initialize once the telemetry proxy + // and make sure it is only after SDK is initialized. + private var telemetryProxy: _InternalProxy._TelemetryProxy? = null + get() { + if (field == null && isInitialized()) { + field = Datadog._internalProxy()._telemetry + } + + return field + } + + // We use Kotlin backing field here to initialize once the telemetry proxy + // and make sure it is only after SDK is initialized. + private var webViewProxy: WebViewTracking._InternalWebViewProxy? = null + get() { + if (field == null && isInitialized()) { + field = WebViewTracking._InternalWebViewProxy(Datadog.getInstance()) + } + + return field + } + override fun setVerbosity(level: Int) { Datadog.setVerbosity(level) } override fun initialize( context: Context, - credentials: Credentials, configuration: Configuration, consent: TrackingConsent ) { - Datadog.initialize(context, credentials, configuration, consent) + Datadog.initialize(context, configuration, consent) + } + + override fun enableRum(configuration: RumConfiguration) { + Rum.enable(configuration) + } + + override fun enableLogs(configuration: LogsConfiguration) { + Logs.enable(configuration) + } + + override fun enableTrace(configuration: TraceConfiguration) { + Trace.enable(configuration) } override fun setUserInfo( @@ -38,13 +78,10 @@ internal class DatadogSDKWrapper : DatadogWrapper { Datadog.setUserInfo(id, name, email, extraInfo) } - override fun registerRumMonitor(rumMonitor: RumMonitor) { - GlobalRum.registerIfAbsent(rumMonitor) - } - override fun addRumGlobalAttributes(attributes: Map) { + val rumMonitor = this.getRumMonitor() attributes.forEach { - GlobalRum.addAttribute(it.key, it.value) + rumMonitor.addAttribute(it.key, it.value) } } @@ -53,22 +90,26 @@ internal class DatadogSDKWrapper : DatadogWrapper { } override fun telemetryDebug(message: String) { - Datadog._internal._telemetry.debug(message) + telemetryProxy?.debug(message) } override fun telemetryError(message: String, stack: String?, kind: String?) { - Datadog._internal._telemetry.error(message, stack, kind) + telemetryProxy?.error(message, stack, kind) } override fun telemetryError(message: String, throwable: Throwable?) { - Datadog._internal._telemetry.error(message, throwable) + telemetryProxy?.error(message, throwable) } override fun consumeWebviewEvent(message: String) { - Datadog._internal.consumeWebviewEvent(message) + webViewProxy?.consumeWebviewEvent(message) } override fun isInitialized(): Boolean { return Datadog.isInitialized() } + + override fun getRumMonitor(): RumMonitor { + return GlobalRumMonitor.get() + } } diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt index 3c39e7303..94b9071c4 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt @@ -8,9 +8,12 @@ package com.datadog.reactnative import android.content.Context import com.datadog.android.core.configuration.Configuration -import com.datadog.android.core.configuration.Credentials +import com.datadog.android.log.LogsConfiguration import com.datadog.android.privacy.TrackingConsent +import com.datadog.android.rum.GlobalRumMonitor +import com.datadog.android.rum.RumConfiguration import com.datadog.android.rum.RumMonitor +import com.datadog.android.trace.TraceConfiguration import java.lang.IllegalArgumentException /** @@ -45,11 +48,37 @@ interface DatadogWrapper { */ fun initialize( context: Context, - credentials: Credentials, configuration: Configuration, consent: TrackingConsent ) + /** + * Enables the RUM feature of the SDK. + * + * @param configuration the configuration for the RUM feature + */ + fun enableRum( + configuration: RumConfiguration + ) + + /** + * Enables the Logs feature of the SDK. + * + * @param configuration the configuration for the Logs feature + */ + fun enableLogs( + configuration: LogsConfiguration + ) + + /** + * Enables the Trace feature of the SDK. + * + * @param configuration the configuration for the Trace feature + */ + fun enableTrace( + configuration: TraceConfiguration + ) + /** * Sets the user information. * @@ -66,13 +95,6 @@ interface DatadogWrapper { extraInfo: Map ) - /** - * Registers a given monitor in [GlobalRum]. - * - * @param rumMonitor to register - */ - fun registerRumMonitor(rumMonitor: RumMonitor) - /** * Adds global attributes. * @@ -109,4 +131,9 @@ interface DatadogWrapper { * Returns whether the SDK is initialized. */ fun isInitialized(): Boolean + + /** + * Returns the RUM Monitor for the default SDK core. + */ + fun getRumMonitor(): RumMonitor } diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdLogsImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdLogsImplementation.kt index 5dbcf41c8..f57b00ff8 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdLogsImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdLogsImplementation.kt @@ -20,9 +20,8 @@ class DdLogsImplementation( ) { private val reactNativeLogger: Logger by lazy { logger ?: Logger.Builder() - .setDatadogLogsEnabled(true) .setLogcatLogsEnabled(true) - .setLoggerName("DdLogs") + .setName("DdLogs") .build() } diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdRumImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdRumImplementation.kt index 4fba8f88a..3764248d0 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdRumImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdRumImplementation.kt @@ -6,7 +6,6 @@ package com.datadog.reactnative -import com.datadog.android.rum.GlobalRum import com.datadog.android.rum.RumActionType import com.datadog.android.rum.RumAttributes import com.datadog.android.rum.RumErrorSource @@ -19,7 +18,7 @@ import java.util.Locale * The entry point to use Datadog's RUM feature. */ @Suppress("TooManyFunctions") -class DdRumImplementation { +class DdRumImplementation(private val datadog: DatadogWrapper = DatadogSDKWrapper()) { /** * Start tracking a RUM View. * @param key The view unique key identifier. @@ -37,7 +36,7 @@ class DdRumImplementation { val attributes = context.toHashMap().toMutableMap().apply { put(RumAttributes.INTERNAL_TIMESTAMP, timestampMs.toLong()) } - GlobalRum.get().startView( + datadog.getRumMonitor().startView( key = key, name = name, attributes = attributes @@ -55,7 +54,7 @@ class DdRumImplementation { val attributes = context.toHashMap().toMutableMap().apply { put(RumAttributes.INTERNAL_TIMESTAMP, timestampMs.toLong()) } - GlobalRum.get().stopView( + datadog.getRumMonitor().stopView( key = key, attributes = attributes ) @@ -79,7 +78,7 @@ class DdRumImplementation { val attributes = context.toHashMap().toMutableMap().apply { put(RumAttributes.INTERNAL_TIMESTAMP, timestampMs.toLong()) } - GlobalRum.get().startUserAction( + datadog.getRumMonitor().startAction( type = type.asRumActionType(), name = name, attributes = attributes @@ -104,7 +103,7 @@ class DdRumImplementation { val attributes = context.toHashMap().toMutableMap().apply { put(RumAttributes.INTERNAL_TIMESTAMP, timestampMs.toLong()) } - GlobalRum.get().stopUserAction( + datadog.getRumMonitor().stopAction( type = type.asRumActionType(), name = name, attributes = attributes @@ -129,7 +128,7 @@ class DdRumImplementation { val attributes = context.toHashMap().toMutableMap().apply { put(RumAttributes.INTERNAL_TIMESTAMP, timestampMs.toLong()) } - GlobalRum.get().addUserAction( + datadog.getRumMonitor().addAction( type = type.asRumActionType(), name = name, attributes = attributes @@ -157,7 +156,7 @@ class DdRumImplementation { val attributes = context.toHashMap().toMutableMap().apply { put(RumAttributes.INTERNAL_TIMESTAMP, timestampMs.toLong()) } - GlobalRum.get().startResource( + datadog.getRumMonitor().startResource( key = key, method = method, url = url, @@ -193,7 +192,7 @@ class DdRumImplementation { } else { size.toLong() } - GlobalRum.get().stopResource( + datadog.getRumMonitor().stopResource( key = key, statusCode = statusCode.toInt(), kind = kind.asRumResourceKind(), @@ -223,7 +222,7 @@ class DdRumImplementation { val attributes = context.toHashMap().toMutableMap().apply { put(RumAttributes.INTERNAL_TIMESTAMP, timestampMs.toLong()) } - GlobalRum.get().addErrorWithStacktrace( + datadog.getRumMonitor().addErrorWithStacktrace( message = message, source = source.asErrorSource(), stacktrace = stacktrace, @@ -237,7 +236,7 @@ class DdRumImplementation { * @param name The name of the new custom timing attribute. Timings can be nested up to 8 levels deep. Names using more than 8 levels will be sanitized by SDK. */ fun addTiming(name: String, promise: Promise) { - GlobalRum.get().addTiming(name) + datadog.getRumMonitor().addTiming(name) promise.resolve(null) } @@ -245,7 +244,7 @@ class DdRumImplementation { * Stops the current RUM Session. */ fun stopSession(promise: Promise) { - GlobalRum.get().stopSession() + datadog.getRumMonitor().stopSession() promise.resolve(null) } @@ -253,12 +252,12 @@ class DdRumImplementation { * Adds result of evaluating a feature flag to the view. * Feature flag evaluations are local to the active view and are cleared when the view is stopped. * @param name The name of the feature flag - * @param value The value the feature flag evaluated to, encapsulated in a Map + * @param valueAsMap The value the feature flag evaluated to, encapsulated in a Map */ - fun addFeatureFlagEvaluation(name: String, value: ReadableMap, promise: Promise) { - val value = value.toHashMap()["value"] + fun addFeatureFlagEvaluation(name: String, valueAsMap: ReadableMap, promise: Promise) { + val value = valueAsMap.toHashMap()["value"] if (value != null) { - GlobalRum.get().addFeatureFlagEvaluation(name, value) + datadog.getRumMonitor().addFeatureFlagEvaluation(name, value) } promise.resolve(null) } diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkConfiguration.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkConfiguration.kt index a3f8f667b..87db41ad5 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkConfiguration.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkConfiguration.kt @@ -29,7 +29,7 @@ package com.datadog.reactnative data class DdSdkConfiguration( val clientToken: String, val env: String, - val applicationId: String? = null, + val applicationId: String, val nativeCrashReportEnabled: Boolean? = null, val nativeLongTaskThresholdMs: Double? = null, val longTaskThresholdMs: Double? = null, diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkConfigurationExt.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkConfigurationExt.kt index 23e2593c9..f1507f190 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkConfigurationExt.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkConfigurationExt.kt @@ -6,7 +6,7 @@ package com.datadog.reactnative -import com.datadog.android.tracing.TracingHeaderType +import com.datadog.android.trace.TracingHeaderType import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableMap import com.facebook.react.bridge.WritableNativeMap @@ -15,7 +15,7 @@ internal fun ReadableMap.asDdSdkConfiguration(): DdSdkConfiguration { return DdSdkConfiguration( clientToken = getString("clientToken").orEmpty(), env = getString("env").orEmpty(), - applicationId = getString("applicationId"), + applicationId = getString("applicationId").orEmpty(), nativeCrashReportEnabled = getBoolean("nativeCrashReportEnabled"), nativeLongTaskThresholdMs = getDouble("nativeLongTaskThresholdMs"), longTaskThresholdMs = getDouble("longTaskThresholdMs"), @@ -63,7 +63,7 @@ internal fun DdSdkConfiguration.toReadableMap(): ReadableMap { val map = WritableNativeMap() map.putString("clientToken", clientToken) map.putString("env", env) - applicationId?.let { map.putString("applicationId", it) } + map.putString("applicationId", applicationId) nativeCrashReportEnabled?.let { map.putBoolean("nativeCrashReportEnabled", it) } nativeLongTaskThresholdMs?.let { map.putDouble("nativeLongTaskThresholdMs", it) } longTaskThresholdMs?.let { map.putDouble("longTaskThresholdMs", it) } diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt index 3adcb9306..bcafd72c3 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt @@ -11,22 +11,22 @@ import android.content.pm.PackageManager import android.util.Log import android.view.Choreographer import com.datadog.android.DatadogSite -import com.datadog.android._InternalProxy import com.datadog.android.core.configuration.BatchSize import com.datadog.android.core.configuration.Configuration -import com.datadog.android.core.configuration.Credentials import com.datadog.android.core.configuration.UploadFrequency -import com.datadog.android.core.configuration.VitalsUpdateFrequency import com.datadog.android.event.EventMapper +import com.datadog.android.log.LogsConfiguration import com.datadog.android.privacy.TrackingConsent -import com.datadog.android.rum.GlobalRum -import com.datadog.android.rum.RumMonitor +import com.datadog.android.rum.configuration.VitalsUpdateFrequency +import com.datadog.android.rum.RumConfiguration import com.datadog.android.rum.RumPerformanceMetric +import com.datadog.android.rum._RumInternalProxy import com.datadog.android.rum.model.ActionEvent import com.datadog.android.rum.model.ResourceEvent import com.datadog.android.rum.tracking.ActivityViewTrackingStrategy import com.datadog.android.telemetry.model.TelemetryConfigurationEvent -import com.datadog.android.tracing.TracingHeaderType +import com.datadog.android.trace.TraceConfiguration +import com.datadog.android.trace.TracingHeaderType import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReadableArray @@ -54,16 +54,21 @@ class DdSdkImplementation( */ fun initialize(configuration: ReadableMap, promise: Promise) { val ddSdkConfiguration = configuration.asDdSdkConfiguration() - val credentials = buildCredentials(ddSdkConfiguration) - val nativeConfiguration = buildConfiguration(ddSdkConfiguration) + val sdkConfiguration = buildSdkConfiguration(ddSdkConfiguration) + val rumConfiguration = buildRumConfiguration(ddSdkConfiguration) val trackingConsent = buildTrackingConsent(ddSdkConfiguration.trackingConsent) configureSdkVerbosity(ddSdkConfiguration) - datadog.initialize(appContext, credentials, nativeConfiguration, trackingConsent) + datadog.initialize(appContext, sdkConfiguration, trackingConsent) - datadog.registerRumMonitor(RumMonitor.Builder().build()) + datadog.enableRum(rumConfiguration) monitorJsRefreshRate(ddSdkConfiguration) + + datadog.enableTrace(TraceConfiguration.Builder().build()) + + datadog.enableLogs(LogsConfiguration.Builder().build()) + initialized.set(true) promise.resolve(null) @@ -165,55 +170,31 @@ class DdSdkImplementation( return packageInfo?.let { // we need to use the deprecated method because getLongVersionCode method is only // available from API 28 and above - @Suppress("DEPRECATION") it.versionName ?: it.versionCode.toString() + @Suppress("DEPRECATION") + it.versionName ?: it.versionCode.toString() } ?: DEFAULT_APP_VERSION } @Suppress("ComplexMethod", "LongMethod", "UnsafeCallOnNullableType") - private fun buildConfiguration(configuration: DdSdkConfiguration): Configuration { - val additionalConfig = configuration.additionalConfig?.toMutableMap() - - val versionSuffix = configuration.additionalConfig?.get(DD_VERSION_SUFFIX) as? String - if (versionSuffix != null && additionalConfig != null) { - val defaultVersion = getDefaultAppVersion() - additionalConfig.put(DD_VERSION, defaultVersion + versionSuffix) - } - + private fun buildRumConfiguration(configuration: DdSdkConfiguration): RumConfiguration { val configBuilder = - Configuration.Builder( - logsEnabled = true, - tracesEnabled = true, - crashReportsEnabled = configuration.nativeCrashReportEnabled - ?: false, - rumEnabled = true + RumConfiguration.Builder( + applicationId = configuration.applicationId ) - .setAdditionalConfiguration( - additionalConfig?.filterValues { it != null }?.mapValues { - it.value!! - } - ?: emptyMap() - ) if (configuration.sampleRate != null) { - configBuilder.sampleRumSessions(configuration.sampleRate.toFloat()) + configBuilder.setSessionSampleRate(configuration.sampleRate.toFloat()) } configBuilder.trackFrustrations(configuration.trackFrustrations ?: true) - configBuilder.trackBackgroundRumEvents(configuration.trackBackgroundEvents ?: false) + configBuilder.trackBackgroundEvents(configuration.trackBackgroundEvents ?: false) - configBuilder.useSite(buildSite(configuration.site)) configBuilder.setVitalsUpdateFrequency( buildVitalUpdateFrequency(configuration.vitalsUpdateFrequency) ) - configBuilder.setUploadFrequency( - buildUploadFrequency(configuration.uploadFrequency) - ) - configBuilder.setBatchSize( - buildBatchSize(configuration.batchSize) - ) val telemetrySampleRate = (configuration.telemetrySampleRate as? Number)?.toFloat() - telemetrySampleRate?.let { configBuilder.sampleTelemetry(it) } + telemetrySampleRate?.let { configBuilder.setTelemetrySampleRate(it) } val longTask = (configuration.nativeLongTaskThresholdMs as? Number)?.toLong() if (longTask != null) { @@ -231,25 +212,10 @@ class DdSdkImplementation( val interactionTracking = configuration.additionalConfig?.get(DD_NATIVE_INTERACTION_TRACKING) as? Boolean if (interactionTracking == false) { - configBuilder.disableInteractionTracking() - } - - @Suppress("UNCHECKED_CAST") - val firstPartyHosts = - (configuration.additionalConfig?.get(DD_FIRST_PARTY_HOSTS) as? ReadableArray) - ?.toArrayList() as? - List - if (firstPartyHosts != null) { - val firstPartyHostsWithHeaderTypes = buildFirstPartyHosts(firstPartyHosts) - - configBuilder.setFirstPartyHostsWithHeaderType(firstPartyHostsWithHeaderTypes) + configBuilder.disableUserInteractionTracking() } - buildProxyConfiguration(configuration)?.let { (proxy, authenticator) -> - configBuilder.setProxy(proxy, authenticator) - } - - configBuilder.setRumResourceEventMapper( + configBuilder.setResourceEventMapper( object : EventMapper { override fun map(event: ResourceEvent): ResourceEvent? { if (event.context?.additionalProperties?.containsKey(DD_DROP_RESOURCE) == @@ -262,7 +228,7 @@ class DdSdkImplementation( } ) - configBuilder.setRumActionEventMapper( + configBuilder.setActionEventMapper( object : EventMapper { override fun map(event: ActionEvent): ActionEvent? { if (event.context?.additionalProperties?.containsKey(DD_DROP_ACTION) == true @@ -274,7 +240,7 @@ class DdSdkImplementation( } ) - _InternalProxy.setTelemetryConfigurationEventMapper( + _RumInternalProxy.setTelemetryConfigurationEventMapper( configBuilder, object : EventMapper { override fun map( @@ -341,15 +307,51 @@ class DdSdkImplementation( return firstPartyHostsWithHeaderTypes } - private fun buildCredentials(configuration: DdSdkConfiguration): Credentials { + private fun buildSdkConfiguration(configuration: DdSdkConfiguration): Configuration { val serviceName = configuration.additionalConfig?.get(DD_SERVICE_NAME) as? String - return Credentials( + val configBuilder = Configuration.Builder( clientToken = configuration.clientToken, - envName = configuration.env, - rumApplicationId = configuration.applicationId, + env = configuration.env, variant = "", - serviceName = serviceName + service = serviceName + ) + + val additionalConfig = configuration.additionalConfig?.toMutableMap() + val versionSuffix = configuration.additionalConfig?.get(DD_VERSION_SUFFIX) as? String + if (versionSuffix != null && additionalConfig != null) { + val defaultVersion = getDefaultAppVersion() + additionalConfig.put(DD_VERSION, defaultVersion + versionSuffix) + } + configBuilder.setAdditionalConfiguration( + additionalConfig?.filterValues { it != null }?.mapValues { + it.value + } as Map? ?: emptyMap() + ) + + configBuilder.setCrashReportsEnabled(configuration.nativeCrashReportEnabled ?: false) + configBuilder.useSite(buildSite(configuration.site)) + configBuilder.setUploadFrequency( + buildUploadFrequency(configuration.uploadFrequency) + ) + configBuilder.setBatchSize( + buildBatchSize(configuration.batchSize) ) + + buildProxyConfiguration(configuration)?.let { (proxy, authenticator) -> + configBuilder.setProxy(proxy, authenticator) + } + + @Suppress("UNCHECKED_CAST") + val firstPartyHosts = + (configuration.additionalConfig?.get(DD_FIRST_PARTY_HOSTS) as? ReadableArray) + ?.toArrayList() as? + List + if (firstPartyHosts != null) { + val firstPartyHostsWithHeaderTypes = buildFirstPartyHosts(firstPartyHosts) + configBuilder.setFirstPartyHostsWithHeaderType(firstPartyHostsWithHeaderTypes) + } + + return configBuilder.build() } internal fun buildTrackingConsent(trackingConsent: String?): TrackingConsent { @@ -435,8 +437,7 @@ class DdSdkImplementation( } private fun buildUploadFrequency(uploadFrequency: String?): UploadFrequency { - val uploadFrequency = uploadFrequency?.lowercase(Locale.US) - return when (uploadFrequency) { + return when (uploadFrequency?.lowercase(Locale.US)) { "rare" -> UploadFrequency.RARE "average" -> UploadFrequency.AVERAGE "frequent" -> UploadFrequency.FREQUENT @@ -489,7 +490,7 @@ class DdSdkImplementation( return { if (jsRefreshRateMonitoringEnabled && it > 0.0) { - GlobalRum.get() + datadog.getRumMonitor() ._getInternal() ?.updatePerformanceMetric(RumPerformanceMetric.JS_FRAME_TIME, it) } @@ -499,7 +500,7 @@ class DdSdkImplementation( ddSdkConfiguration.longTaskThresholdMs?.toLong() ?: 0L ) ) { - GlobalRum.get()._getInternal()?.addLongTask(it.toLong(), "javascript") + datadog.getRumMonitor()._getInternal()?.addLongTask(it.toLong(), "javascript") } } } diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdTraceImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdTraceImplementation.kt index c69b26ebe..7f47221f2 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdTraceImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdTraceImplementation.kt @@ -6,18 +6,26 @@ package com.datadog.reactnative -import com.datadog.android.tracing.AndroidTracer +import com.datadog.android.trace.AndroidTracer +import com.datadog.android.trace.Trace +import com.datadog.android.trace.TraceConfiguration import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReadableMap import io.opentracing.Span import io.opentracing.Tracer +import io.opentracing.util.GlobalTracer import java.util.concurrent.TimeUnit /** * The entry point to use Datadog's Trace feature. */ class DdTraceImplementation( - private val tracerProvider: () -> Tracer = { AndroidTracer.Builder().build() } + private val tracerProvider: () -> Tracer = { + val tracer = AndroidTracer.Builder().build() + GlobalTracer.registerIfAbsent(tracer) + + GlobalTracer.get() + } ) { private val spanMap: MutableMap = mutableMapOf() diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/NoOpViewTrackingStrategy.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/NoOpViewTrackingStrategy.kt index 198827a61..929857f0a 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/NoOpViewTrackingStrategy.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/NoOpViewTrackingStrategy.kt @@ -7,13 +7,14 @@ package com.datadog.reactnative import android.content.Context +import com.datadog.android.api.SdkCore import com.datadog.android.rum.tracking.ViewTrackingStrategy /** * No-op implementation of the [ViewTrackingStrategy]. */ object NoOpViewTrackingStrategy : ViewTrackingStrategy { - override fun register(context: Context) { + override fun register(sdkCore: SdkCore, context: Context) { // No-op } diff --git a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdTrace.kt b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdTrace.kt index 42687a692..7c26f4b75 100644 --- a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdTrace.kt +++ b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdTrace.kt @@ -16,7 +16,7 @@ import com.facebook.react.bridge.ReadableMap * The entry point to use Datadog's Trace feature. */ class DdTrace( - reactContext: ReactApplicationContext, + reactContext: ReactApplicationContext ) : ReactContextBaseJavaModule(reactContext) { private val implementation = DdTraceImplementation() diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdRumTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdRumTest.kt index e1f12c5a5..b9c3797d2 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdRumTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdRumTest.kt @@ -6,19 +6,17 @@ package com.datadog.reactnative -import com.datadog.android.rum.GlobalRum import com.datadog.android.rum.RumActionType 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.tools.unit.forge.BaseConfigurator -import com.datadog.tools.unit.getStaticValue -import com.datadog.tools.unit.setStaticValue import com.datadog.tools.unit.toReadableMap import com.facebook.react.bridge.Promise -import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.doReturn import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.whenever import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.BoolForgery import fr.xgouchet.elmyr.annotation.DoubleForgery @@ -29,7 +27,6 @@ import fr.xgouchet.elmyr.annotation.StringForgeryType import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension import java.util.Date -import java.util.concurrent.atomic.AtomicBoolean import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -55,6 +52,9 @@ internal class DdRumTest { @Mock lateinit var mockRumMonitor: RumMonitor + @Mock + lateinit var mockDatadog: DatadogWrapper + @Mock lateinit var mockPromise: Promise @@ -65,7 +65,7 @@ internal class DdRumTest { @BeforeEach fun `set up`(forge: Forge) { - GlobalRum.registerIfAbsent(mockRumMonitor) + whenever(mockDatadog.getRumMonitor()) doReturn mockRumMonitor fakeContext = forge.aMap { anAlphabeticalString() to aNullable { @@ -79,13 +79,11 @@ internal class DdRumTest { } } - testedDdRum = DdRumImplementation() + testedDdRum = DdRumImplementation(mockDatadog) } @AfterEach fun `tear down`() { - GlobalRum.javaClass.setStaticValue("monitor", mock()) - GlobalRum.javaClass.getStaticValue("isRegistered").set(false) } @Test @@ -133,12 +131,15 @@ internal class DdRumTest { // When testedDdRum.addAction( - type.name, name, fakeContext.toReadableMap(), - fakeTimestamp, mockPromise + type.name, + name, + fakeContext.toReadableMap(), + fakeTimestamp, + mockPromise ) // Then - verify(mockRumMonitor).addUserAction(type, name, updatedContext) + verify(mockRumMonitor).addAction(type, name, updatedContext) } @Test @@ -155,7 +156,7 @@ internal class DdRumTest { testedDdRum.addAction(type, name, fakeContext.toReadableMap(), fakeTimestamp, mockPromise) // Then - verify(mockRumMonitor).addUserAction(RumActionType.CUSTOM, name, updatedContext) + verify(mockRumMonitor).addAction(RumActionType.CUSTOM, name, updatedContext) } @Test @@ -170,12 +171,15 @@ internal class DdRumTest { // When testedDdRum.startAction( - type.name, name, fakeContext.toReadableMap(), - fakeTimestamp, mockPromise + type.name, + name, + fakeContext.toReadableMap(), + fakeTimestamp, + mockPromise ) // Then - verify(mockRumMonitor).startUserAction(type, name, updatedContext) + verify(mockRumMonitor).startAction(type, name, updatedContext) } @Test @@ -192,7 +196,7 @@ internal class DdRumTest { testedDdRum.startAction(type, name, fakeContext.toReadableMap(), fakeTimestamp, mockPromise) // Then - verify(mockRumMonitor).startUserAction(RumActionType.CUSTOM, name, updatedContext) + verify(mockRumMonitor).startAction(RumActionType.CUSTOM, name, updatedContext) } @Test @@ -207,12 +211,15 @@ internal class DdRumTest { // When testedDdRum.stopAction( - type.name, name, fakeContext.toReadableMap(), - fakeTimestamp, mockPromise + type.name, + name, + fakeContext.toReadableMap(), + fakeTimestamp, + mockPromise ) // Then - verify(mockRumMonitor).stopUserAction(type, name, updatedContext) + verify(mockRumMonitor).stopAction(type, name, updatedContext) } @Test @@ -229,7 +236,7 @@ internal class DdRumTest { testedDdRum.stopAction(type, name, fakeContext.toReadableMap(), fakeTimestamp, mockPromise) // Then - verify(mockRumMonitor).stopUserAction(RumActionType.CUSTOM, name, updatedContext) + verify(mockRumMonitor).stopAction(RumActionType.CUSTOM, name, updatedContext) } @Test @@ -245,8 +252,12 @@ internal class DdRumTest { // When testedDdRum.startResource( - key, method, url, fakeContext.toReadableMap(), - fakeTimestamp, mockPromise + key, + method, + url, + fakeContext.toReadableMap(), + fakeTimestamp, + mockPromise ) // Then @@ -364,8 +375,12 @@ internal class DdRumTest { // When testedDdRum.addError( - message, source.name, stackTrace, fakeContext.toReadableMap(), - fakeTimestamp, mockPromise + message, + source.name, + stackTrace, + fakeContext.toReadableMap(), + fakeTimestamp, + mockPromise ) // Then @@ -385,8 +400,12 @@ internal class DdRumTest { // When testedDdRum.addError( - message, source, stackTrace, fakeContext.toReadableMap(), - fakeTimestamp, mockPromise + message, + source, + stackTrace, + fakeContext.toReadableMap(), + fakeTimestamp, + mockPromise ) // Then @@ -400,7 +419,6 @@ internal class DdRumTest { @Test fun `M call addTiming W addTiming()`(@StringForgery timing: String) { - // When testedDdRum.addTiming(timing, mockPromise) @@ -410,7 +428,6 @@ internal class DdRumTest { @Test fun `M call stopSession W stopSession()`() { - // When testedDdRum.stopSession(mockPromise) diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt index 6ec62d4f6..2d2654384 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt @@ -12,24 +12,23 @@ import android.view.Choreographer import com.datadog.android.DatadogSite import com.datadog.android.core.configuration.BatchSize import com.datadog.android.core.configuration.Configuration -import com.datadog.android.core.configuration.Credentials import com.datadog.android.core.configuration.UploadFrequency -import com.datadog.android.core.configuration.VitalsUpdateFrequency import com.datadog.android.event.EventMapper -import com.datadog.android.plugin.DatadogPlugin +import com.datadog.android.log.LogsConfiguration import com.datadog.android.privacy.TrackingConsent -import com.datadog.android.rum.GlobalRum -import com.datadog.android.rum.RumMonitor +import com.datadog.android.rum.RumConfiguration import com.datadog.android.rum.RumPerformanceMetric import com.datadog.android.rum._RumInternalProxy +import com.datadog.android.rum.configuration.VitalsUpdateFrequency import com.datadog.android.rum.model.ActionEvent import com.datadog.android.rum.model.ResourceEvent import com.datadog.android.rum.tracking.ActivityViewTrackingStrategy import com.datadog.android.telemetry.model.TelemetryConfigurationEvent -import com.datadog.android.tracing.TracingHeaderType +import com.datadog.android.trace.TraceConfiguration +import com.datadog.android.trace.TracingHeaderType import com.datadog.tools.unit.GenericAssert.Companion.assertThat +import com.datadog.tools.unit.MockRumMonitor import com.datadog.tools.unit.forge.BaseConfigurator -import com.datadog.tools.unit.getStaticValue import com.datadog.tools.unit.setStaticValue import com.datadog.tools.unit.toReadableArray import com.datadog.tools.unit.toReadableJavaOnlyMap @@ -65,7 +64,6 @@ import fr.xgouchet.elmyr.junit5.ForgeExtension import java.net.InetSocketAddress import java.net.Proxy import java.util.Locale -import java.util.concurrent.atomic.AtomicBoolean import java.util.stream.Stream import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.AfterEach @@ -111,7 +109,7 @@ internal class DdSdkTest { lateinit var mockContext: ReactApplicationContext @Mock - lateinit var mockRumMonitor: RumMonitor + lateinit var mockRumMonitor: MockRumMonitor @Mock lateinit var mockRumInternalProxy: _RumInternalProxy @@ -133,7 +131,7 @@ internal class DdSdkTest { @BeforeEach fun `set up`() { - GlobalRum.registerIfAbsent(mockRumMonitor) + whenever(mockDatadog.getRumMonitor()) doReturn mockRumMonitor whenever(mockRumMonitor._getInternal()) doReturn mockRumInternalProxy doNothing().whenever(mockChoreographer).postFrameCallback(any()) @@ -156,8 +154,6 @@ internal class DdSdkTest { @AfterEach fun `tear down`() { GlobalState.globalAttributes.clear() - GlobalRum.javaClass.setStaticValue("monitor", mock()) - GlobalRum.javaClass.getStaticValue("isRegistered").set(false) } // region initialize / nativeCrashReportEnabled @@ -166,8 +162,10 @@ internal class DdSdkTest { fun `𝕄 initialize native SDK 𝕎 initialize() {nativeCrashReportEnabled=true}`() { // Given val bridgeConfiguration = fakeConfiguration.copy(nativeCrashReportEnabled = true) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -176,46 +174,38 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), + sdkConfigCaptor.capture(), any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) + assertThat(sdkConfigCaptor.firstValue) .hasField("coreConfig") { it.hasFieldEqualTo("needsClearTextHttp", false) it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) } - .hasField("logsConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("tracesConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("crashReportConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("rumConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") + .hasFieldEqualTo("crashReportsEnabled", true) .hasFieldEqualTo( "additionalConfig", fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() ) - val credentials = credentialCaptor.firstValue - assertThat(credentials.clientToken).isEqualTo(fakeConfiguration.clientToken) - assertThat(credentials.envName).isEqualTo(fakeConfiguration.env) - assertThat(credentials.rumApplicationId).isEqualTo(fakeConfiguration.applicationId) - assertThat(credentials.variant).isEqualTo("") + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) } @Test fun `𝕄 initialize native SDK 𝕎 initialize() {nativeCrashReportEnabled=false}`() { // Given fakeConfiguration = fakeConfiguration.copy(nativeCrashReportEnabled = false, site = null) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -224,44 +214,38 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), + sdkConfigCaptor.capture(), any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) + assertThat(sdkConfigCaptor.firstValue) .hasField("coreConfig") { it.hasFieldEqualTo("needsClearTextHttp", false) it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) } - .hasField("logsConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("tracesConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasFieldEqualTo("crashReportConfig", null) - .hasField("rumConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") + .hasFieldEqualTo("crashReportsEnabled", false) .hasFieldEqualTo( "additionalConfig", fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() ) - val credentials = credentialCaptor.firstValue - assertThat(credentials.clientToken).isEqualTo(fakeConfiguration.clientToken) - assertThat(credentials.envName).isEqualTo(fakeConfiguration.env) - assertThat(credentials.rumApplicationId).isEqualTo(fakeConfiguration.applicationId) - assertThat(credentials.variant).isEqualTo("") + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) } @Test fun `𝕄 initialize native SDK 𝕎 initialize() {nativeCrashReportEnabled=null}`() { // Given fakeConfiguration = fakeConfiguration.copy(nativeCrashReportEnabled = false, site = null) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -270,36 +254,28 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), + sdkConfigCaptor.capture(), any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) + assertThat(sdkConfigCaptor.firstValue) .hasField("coreConfig") { it.hasFieldEqualTo("needsClearTextHttp", false) it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) } - .hasField("logsConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("tracesConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasFieldEqualTo("crashReportConfig", null) - .hasField("rumConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") + .hasFieldEqualTo("crashReportsEnabled", false) .hasFieldEqualTo( "additionalConfig", fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() ) - val credentials = credentialCaptor.firstValue - assertThat(credentials.clientToken).isEqualTo(fakeConfiguration.clientToken) - assertThat(credentials.envName).isEqualTo(fakeConfiguration.env) - assertThat(credentials.rumApplicationId).isEqualTo(fakeConfiguration.applicationId) - assertThat(credentials.variant).isEqualTo("") + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) } // endregion @@ -309,8 +285,10 @@ internal class DdSdkTest { @Test fun `𝕄 initialize native with sample rate SDK 𝕎 initialize() {}`() { // Given - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() val expectedRumSampleRate = fakeConfiguration.sampleRate?.toFloat() ?: 100f // When @@ -320,36 +298,30 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), + sdkConfigCaptor.capture(), any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) + assertThat(sdkConfigCaptor.firstValue) .hasField("coreConfig") { it.hasFieldEqualTo("needsClearTextHttp", false) it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) } - .hasField("logsConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("tracesConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("rumConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - it.hasFieldEqualTo("samplingRate", expectedRumSampleRate) - } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") .hasFieldEqualTo( "additionalConfig", fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() ) - val credentials = credentialCaptor.firstValue - assertThat(credentials.clientToken).isEqualTo(fakeConfiguration.clientToken) - assertThat(credentials.envName).isEqualTo(fakeConfiguration.env) - assertThat(credentials.rumApplicationId).isEqualTo(fakeConfiguration.applicationId) - assertThat(credentials.variant).isEqualTo("") + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("sampleRate", expectedRumSampleRate) + } } // endregion @@ -359,8 +331,10 @@ internal class DdSdkTest { @Test fun `𝕄 initialize native with telemetry sample rate SDK 𝕎 initialize() {}`() { // Given - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() val expectedTelemetrySampleRate = fakeConfiguration.telemetrySampleRate?.toFloat() ?: 20f // When @@ -370,36 +344,30 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), + sdkConfigCaptor.capture(), any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) + assertThat(sdkConfigCaptor.firstValue) .hasField("coreConfig") { it.hasFieldEqualTo("needsClearTextHttp", false) it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) } - .hasField("logsConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("tracesConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("rumConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - it.hasFieldEqualTo("telemetrySamplingRate", expectedTelemetrySampleRate) - } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") .hasFieldEqualTo( "additionalConfig", fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() ) - val credentials = credentialCaptor.firstValue - assertThat(credentials.clientToken).isEqualTo(fakeConfiguration.clientToken) - assertThat(credentials.envName).isEqualTo(fakeConfiguration.env) - assertThat(credentials.rumApplicationId).isEqualTo(fakeConfiguration.applicationId) - assertThat(credentials.variant).isEqualTo("") + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("telemetrySampleRate", expectedTelemetrySampleRate) + } } // endregion @@ -410,8 +378,10 @@ internal class DdSdkTest { fun `𝕄 initialize native SDK 𝕎 initialize() {additionalConfig=null}`() { // Given fakeConfiguration = fakeConfiguration.copy(additionalConfig = null) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -420,39 +390,33 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), + sdkConfigCaptor.capture(), any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) + assertThat(sdkConfigCaptor.firstValue) .hasField("coreConfig") { it.hasFieldEqualTo("needsClearTextHttp", false) it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) } - .hasField("logsConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("tracesConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("rumConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") .hasFieldEqualTo("additionalConfig", emptyMap()) - val credentials = credentialCaptor.firstValue - assertThat(credentials.clientToken).isEqualTo(fakeConfiguration.clientToken) - assertThat(credentials.envName).isEqualTo(fakeConfiguration.env) - assertThat(credentials.rumApplicationId).isEqualTo(fakeConfiguration.applicationId) - assertThat(credentials.variant).isEqualTo("") + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) } @Test fun `𝕄 initialize native SDK 𝕎 initialize() {additionalConfig=nonNull}`() { // Given - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -461,35 +425,27 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), + sdkConfigCaptor.capture(), any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) + assertThat(sdkConfigCaptor.firstValue) .hasField("coreConfig") { it.hasFieldEqualTo("needsClearTextHttp", false) it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) } - .hasField("logsConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("tracesConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("rumConfig") { - it.hasFieldEqualTo("plugins", emptyList()) - } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") .hasFieldEqualTo( "additionalConfig", fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() ) - val credentials = credentialCaptor.firstValue - assertThat(credentials.clientToken).isEqualTo(fakeConfiguration.clientToken) - assertThat(credentials.envName).isEqualTo(fakeConfiguration.env) - assertThat(credentials.rumApplicationId).isEqualTo(fakeConfiguration.applicationId) - assertThat(credentials.variant).isEqualTo("") + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) } // endregion @@ -502,8 +458,10 @@ internal class DdSdkTest { ) { // Given fakeConfiguration = fakeConfiguration.copy(site = null, nativeCrashReportEnabled = true) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -512,42 +470,28 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), + sdkConfigCaptor.capture(), any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) + assertThat(sdkConfigCaptor.firstValue) .hasField("coreConfig") { it.hasFieldEqualTo("needsClearTextHttp", false) it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + it.hasFieldEqualTo("site", DatadogSite.US1) } - .hasField("logsConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.US1.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("tracesConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.US1.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("rumConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.US1.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("crashReportConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.US1.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") .hasFieldEqualTo( "additionalConfig", fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() ) - val credentials = credentialCaptor.firstValue - assertThat(credentials.clientToken).isEqualTo(fakeConfiguration.clientToken) - assertThat(credentials.envName).isEqualTo(fakeConfiguration.env) - assertThat(credentials.rumApplicationId).isEqualTo(fakeConfiguration.applicationId) - assertThat(credentials.variant).isEqualTo("") + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) } @Test @@ -557,8 +501,10 @@ internal class DdSdkTest { // Given val site = forge.randomizeCase("us1") fakeConfiguration = fakeConfiguration.copy(site = site, nativeCrashReportEnabled = true) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -567,42 +513,28 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), + sdkConfigCaptor.capture(), any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) + assertThat(sdkConfigCaptor.firstValue) .hasField("coreConfig") { it.hasFieldEqualTo("needsClearTextHttp", false) it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + it.hasFieldEqualTo("site", DatadogSite.US1) } - .hasField("logsConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.US1.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("tracesConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.US1.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("rumConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.US1.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("crashReportConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.US1.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") .hasFieldEqualTo( "additionalConfig", fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() ) - val credentials = credentialCaptor.firstValue - assertThat(credentials.clientToken).isEqualTo(fakeConfiguration.clientToken) - assertThat(credentials.envName).isEqualTo(fakeConfiguration.env) - assertThat(credentials.rumApplicationId).isEqualTo(fakeConfiguration.applicationId) - assertThat(credentials.variant).isEqualTo("") + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) } @Test @@ -612,8 +544,10 @@ internal class DdSdkTest { // Given val site = forge.randomizeCase("us3") fakeConfiguration = fakeConfiguration.copy(site = site, nativeCrashReportEnabled = true) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -622,42 +556,28 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), + sdkConfigCaptor.capture(), any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) + assertThat(sdkConfigCaptor.firstValue) .hasField("coreConfig") { it.hasFieldEqualTo("needsClearTextHttp", false) it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + it.hasFieldEqualTo("site", DatadogSite.US3) } - .hasField("logsConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.US3.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("tracesConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.US3.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("rumConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.US3.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("crashReportConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.US3.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") .hasFieldEqualTo( "additionalConfig", fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() ) - val credentials = credentialCaptor.firstValue - assertThat(credentials.clientToken).isEqualTo(fakeConfiguration.clientToken) - assertThat(credentials.envName).isEqualTo(fakeConfiguration.env) - assertThat(credentials.rumApplicationId).isEqualTo(fakeConfiguration.applicationId) - assertThat(credentials.variant).isEqualTo("") + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) } @Test @@ -667,8 +587,10 @@ internal class DdSdkTest { // Given val site = forge.randomizeCase("us5") fakeConfiguration = fakeConfiguration.copy(site = site, nativeCrashReportEnabled = true) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -677,42 +599,28 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), + sdkConfigCaptor.capture(), any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) + assertThat(sdkConfigCaptor.firstValue) .hasField("coreConfig") { it.hasFieldEqualTo("needsClearTextHttp", false) it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + it.hasFieldEqualTo("site", DatadogSite.US5) } - .hasField("logsConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.US5.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("tracesConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.US5.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("rumConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.US5.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("crashReportConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.US5.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") .hasFieldEqualTo( "additionalConfig", fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() ) - val credentials = credentialCaptor.firstValue - assertThat(credentials.clientToken).isEqualTo(fakeConfiguration.clientToken) - assertThat(credentials.envName).isEqualTo(fakeConfiguration.env) - assertThat(credentials.rumApplicationId).isEqualTo(fakeConfiguration.applicationId) - assertThat(credentials.variant).isEqualTo("") + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) } @Test @@ -722,8 +630,10 @@ internal class DdSdkTest { // Given val site = forge.randomizeCase("us1_fed") fakeConfiguration = fakeConfiguration.copy(site = site, nativeCrashReportEnabled = true) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -732,42 +642,28 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), + sdkConfigCaptor.capture(), any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) + assertThat(sdkConfigCaptor.firstValue) .hasField("coreConfig") { it.hasFieldEqualTo("needsClearTextHttp", false) it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + it.hasFieldEqualTo("site", DatadogSite.US1_FED) } - .hasField("logsConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.US1_FED.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("tracesConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.US1_FED.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("rumConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.US1_FED.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("crashReportConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.US1_FED.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") .hasFieldEqualTo( "additionalConfig", fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() ) - val credentials = credentialCaptor.firstValue - assertThat(credentials.clientToken).isEqualTo(fakeConfiguration.clientToken) - assertThat(credentials.envName).isEqualTo(fakeConfiguration.env) - assertThat(credentials.rumApplicationId).isEqualTo(fakeConfiguration.applicationId) - assertThat(credentials.variant).isEqualTo("") + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) } @Test @@ -777,8 +673,10 @@ internal class DdSdkTest { // Given val site = forge.randomizeCase("eu1") fakeConfiguration = fakeConfiguration.copy(site = site, nativeCrashReportEnabled = true) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -787,42 +685,28 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), + sdkConfigCaptor.capture(), any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) + assertThat(sdkConfigCaptor.firstValue) .hasField("coreConfig") { it.hasFieldEqualTo("needsClearTextHttp", false) it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + it.hasFieldEqualTo("site", DatadogSite.EU1) } - .hasField("logsConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.EU1.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("tracesConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.EU1.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("rumConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.EU1.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("crashReportConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.EU1.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") .hasFieldEqualTo( "additionalConfig", fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() ) - val credentials = credentialCaptor.firstValue - assertThat(credentials.clientToken).isEqualTo(fakeConfiguration.clientToken) - assertThat(credentials.envName).isEqualTo(fakeConfiguration.env) - assertThat(credentials.rumApplicationId).isEqualTo(fakeConfiguration.applicationId) - assertThat(credentials.variant).isEqualTo("") + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) } @Test @@ -832,8 +716,10 @@ internal class DdSdkTest { // Given val site = forge.randomizeCase("ap1") fakeConfiguration = fakeConfiguration.copy(site = site, nativeCrashReportEnabled = true) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -842,42 +728,28 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), + sdkConfigCaptor.capture(), any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) + assertThat(sdkConfigCaptor.firstValue) .hasField("coreConfig") { it.hasFieldEqualTo("needsClearTextHttp", false) it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + it.hasFieldEqualTo("site", DatadogSite.AP1) } - .hasField("logsConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.AP1.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("tracesConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.AP1.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("rumConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.AP1.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } - .hasField("crashReportConfig") { - it.hasFieldEqualTo("endpointUrl", DatadogSite.AP1.intakeEndpoint) - it.hasFieldEqualTo("plugins", emptyList()) - } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") .hasFieldEqualTo( "additionalConfig", fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() ) - val credentials = credentialCaptor.firstValue - assertThat(credentials.clientToken).isEqualTo(fakeConfiguration.clientToken) - assertThat(credentials.envName).isEqualTo(fakeConfiguration.env) - assertThat(credentials.rumApplicationId).isEqualTo(fakeConfiguration.applicationId) - assertThat(credentials.variant).isEqualTo("") + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) } // endregion @@ -888,8 +760,10 @@ internal class DdSdkTest { fun `𝕄 initialize native SDK 𝕎 initialize() {trackingConsent=null}`() { // Given fakeConfiguration = fakeConfiguration.copy(trackingConsent = null) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -898,11 +772,12 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), + sdkConfigCaptor.capture(), eq(TrackingConsent.PENDING) ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } } @@ -913,8 +788,10 @@ internal class DdSdkTest { // Given val consent = forge.randomizeCase("PENDING") fakeConfiguration = fakeConfiguration.copy(trackingConsent = consent) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -923,11 +800,12 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), + sdkConfigCaptor.capture(), eq(TrackingConsent.PENDING) ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } } @@ -938,8 +816,10 @@ internal class DdSdkTest { // Given val consent = forge.randomizeCase("GRANTED") fakeConfiguration = fakeConfiguration.copy(trackingConsent = consent) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -948,11 +828,12 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), + sdkConfigCaptor.capture(), eq(TrackingConsent.GRANTED) ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } } @@ -963,8 +844,10 @@ internal class DdSdkTest { // Given val consent = forge.randomizeCase("NOT_GRANTED") fakeConfiguration = fakeConfiguration.copy(trackingConsent = consent) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -973,11 +856,12 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), + sdkConfigCaptor.capture(), eq(TrackingConsent.NOT_GRANTED) ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } } @@ -989,8 +873,10 @@ internal class DdSdkTest { ) { // Given val bridgeConfiguration = configuration.copy(additionalConfig = null) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -999,14 +885,15 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) + sdkConfigCaptor.capture(), + any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) - .hasField("rumConfig") { + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { it.hasFieldEqualTo("viewTrackingStrategy", NoOpViewTrackingStrategy) } } @@ -1021,8 +908,10 @@ internal class DdSdkTest { DdSdkImplementation.DD_NATIVE_VIEW_TRACKING to false ) ) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -1031,14 +920,15 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) + sdkConfigCaptor.capture(), + any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) - .hasField("rumConfig") { + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { it.hasFieldEqualTo("viewTrackingStrategy", NoOpViewTrackingStrategy) } } @@ -1053,8 +943,10 @@ internal class DdSdkTest { DdSdkImplementation.DD_NATIVE_VIEW_TRACKING to true ) ) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -1063,14 +955,15 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) + sdkConfigCaptor.capture(), + any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) - .hasField("rumConfig") { + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { it.hasFieldEqualTo("viewTrackingStrategy", ActivityViewTrackingStrategy(false)) } } @@ -1085,8 +978,10 @@ internal class DdSdkTest { DdSdkImplementation.DD_NATIVE_INTERACTION_TRACKING to false ) ) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -1095,18 +990,16 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) + sdkConfigCaptor.capture(), + any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) - .hasField("rumConfig") { - it.hasFieldWithClass( - "userActionTrackingStrategy", - "com.datadog.android.rum.internal.tracking.NoOpUserActionTrackingStrategy" - ) + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("viewTrackingStrategy", NoOpViewTrackingStrategy) } } @@ -1118,8 +1011,10 @@ internal class DdSdkTest { val bridgeConfiguration = configuration.copy( trackFrustrations = true ) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -1128,18 +1023,16 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) + sdkConfigCaptor.capture(), + any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) - .hasField("rumConfig") { - it.hasFieldEqualTo( - "trackFrustrations", - true - ) + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("trackFrustrations", true) } } @@ -1151,8 +1044,10 @@ internal class DdSdkTest { val bridgeConfiguration = configuration.copy( trackFrustrations = false ) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -1161,18 +1056,16 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) + sdkConfigCaptor.capture(), + any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) - .hasField("rumConfig") { - it.hasFieldEqualTo( - "trackFrustrations", - false - ) + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("trackFrustrations", false) } } @@ -1186,8 +1079,10 @@ internal class DdSdkTest { DdSdkImplementation.DD_NATIVE_INTERACTION_TRACKING to true ) ) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -1196,19 +1091,16 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) - ) - verify(mockDatadog).registerRumMonitor(any()) - } - assertThat(configCaptor.firstValue) - .hasField("rumConfig") { - it.hasFieldWithClass( - "userActionTrackingStrategy", - "com.datadog.android.rum.internal" + - ".instrumentation.UserActionTrackingStrategyLegacy" - ) + sdkConfigCaptor.capture(), + any() + ) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) + } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("userActionTracking", true) } } @@ -1222,8 +1114,10 @@ internal class DdSdkTest { DdSdkImplementation.DD_NATIVE_INTERACTION_TRACKING to null ) ) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -1232,21 +1126,19 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) - ) - verify(mockDatadog).registerRumMonitor(any()) - } - assertThat(configCaptor.firstValue) - .hasField("rumConfig") { - it.hasFieldWithClass( - "userActionTrackingStrategy", - "com.datadog.android.rum.internal" + - ".instrumentation.UserActionTrackingStrategyLegacy" - ) + sdkConfigCaptor.capture(), + any() + ) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) + } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("userActionTracking", true) } } + @Test fun `𝕄 initialize native SDK 𝕎 initialize() {sdk verbosity}`( @Forgery configuration: DdSdkConfiguration, @@ -1303,20 +1195,40 @@ internal class DdSdkTest { DdSdkImplementation.DD_SERVICE_NAME to serviceName ) ) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) // Then - verify(mockDatadog).initialize( - same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) - ) - assertThat(credentialCaptor.firstValue.serviceName).isEqualTo(serviceName) + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) + } + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { + it.hasFieldEqualTo("needsClearTextHttp", false) + it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + } + .hasFieldEqualTo("clientToken", bridgeConfiguration.clientToken) + .hasFieldEqualTo("env", bridgeConfiguration.env) + .hasFieldEqualTo("variant", "") + .hasFieldEqualTo("service", serviceName) + .hasFieldEqualTo( + "additionalConfig", + bridgeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() + ) + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", bridgeConfiguration.applicationId) } @Test @@ -1330,21 +1242,27 @@ internal class DdSdkTest { val bridgeConfiguration = configuration.copy( nativeLongTaskThresholdMs = threshold ) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) // Then - verify(mockDatadog).initialize( - same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) - ) - assertThat(configCaptor.firstValue) - .hasField("rumConfig") { rumConfig -> + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) + } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { rumConfig -> rumConfig.hasField("longTaskTrackingStrategy") { longTaskTrackingStrategy -> longTaskTrackingStrategy .isInstanceOf( @@ -1365,21 +1283,27 @@ internal class DdSdkTest { val bridgeConfiguration = configuration.copy( nativeLongTaskThresholdMs = 0.0 ) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) // Then - verify(mockDatadog).initialize( - same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) - ) - assertThat(configCaptor.firstValue) - .hasField("rumConfig") { rumConfig -> + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) + } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { rumConfig -> rumConfig.doesNotHaveField("longTaskTrackingStrategy") } } @@ -1422,19 +1346,26 @@ internal class DdSdkTest { DdSdkImplementation.DD_FIRST_PARTY_HOSTS to firstPartyHosts.toReadableArray() ) ) - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) // Then - verify(mockDatadog).initialize( - same(mockContext), - any(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) - ) - assertThat(configCaptor.firstValue) + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) + } + assertThat(sdkConfigCaptor.firstValue) .hasField("coreConfig") { coreConfig -> coreConfig.hasFieldEqualTo( "firstPartyHostsWithHeaderTypes", @@ -1452,7 +1383,7 @@ internal class DdSdkTest { Pair( forge.aStringMatching("[a-z]+\\.[a-z]{3}"), setOf( - TracingHeaderType.DATADOG, + TracingHeaderType.DATADOG ) ) } @@ -1476,19 +1407,26 @@ internal class DdSdkTest { DdSdkImplementation.DD_FIRST_PARTY_HOSTS to firstPartyHosts.toReadableArray() ) ) - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) // Then - verify(mockDatadog).initialize( - same(mockContext), - any(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) - ) - assertThat(configCaptor.firstValue) + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) + } + assertThat(sdkConfigCaptor.firstValue) .hasField("coreConfig") { coreConfig -> coreConfig.hasFieldEqualTo( "firstPartyHostsWithHeaderTypes", @@ -1508,9 +1446,9 @@ internal class DdSdkTest { host, setOf( TracingHeaderType.DATADOG, - TracingHeaderType.B3, + TracingHeaderType.B3 ) - ), + ) ) val firstPartyHosts = mutableListOf() @@ -1518,7 +1456,7 @@ internal class DdSdkTest { mapOf( "match" to host, "propagatorTypes" to listOf( - TracingHeaderType.DATADOG.name.lowercase(), + TracingHeaderType.DATADOG.name.lowercase() ).toReadableArray() ).toReadableMap() ) @@ -1526,7 +1464,7 @@ internal class DdSdkTest { mapOf( "match" to host, "propagatorTypes" to listOf( - TracingHeaderType.B3.name.lowercase(), + TracingHeaderType.B3.name.lowercase() ).toReadableArray() ).toReadableMap() ) @@ -1537,19 +1475,26 @@ internal class DdSdkTest { DdSdkImplementation.DD_FIRST_PARTY_HOSTS to firstPartyHosts.toReadableArray() ) ) - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) // Then - verify(mockDatadog).initialize( - same(mockContext), - any(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) - ) - assertThat(configCaptor.firstValue) + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) + } + assertThat(sdkConfigCaptor.firstValue) .hasField("coreConfig") { coreConfig -> coreConfig.hasFieldEqualTo( "firstPartyHostsWithHeaderTypes", @@ -1567,10 +1512,12 @@ internal class DdSdkTest { ) { // Given val bridgeConfiguration = configuration.copy( - uploadFrequency = input, + uploadFrequency = input ) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -1579,15 +1526,19 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) + sdkConfigCaptor.capture(), + any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) - .hasField("coreConfig") { - it.hasFieldEqualTo("uploadFrequency", expectedUploadFrequency) + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { coreConfig -> + coreConfig.hasFieldEqualTo( + "uploadFrequency", + expectedUploadFrequency + ) } } @@ -1600,10 +1551,12 @@ internal class DdSdkTest { ) { // Given val bridgeConfiguration = configuration.copy( - batchSize = input, + batchSize = input ) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -1612,15 +1565,19 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) + sdkConfigCaptor.capture(), + any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) - .hasField("coreConfig") { - it.hasFieldEqualTo("batchSize", expectedBatchSize) + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { coreConfig -> + coreConfig.hasFieldEqualTo( + "batchSize", + expectedBatchSize + ) } } @@ -1632,10 +1589,12 @@ internal class DdSdkTest { // Given val trackBackgroundEvents = forge.aNullable { forge.aBool() } val bridgeConfiguration = configuration.copy( - trackBackgroundEvents = trackBackgroundEvents, + trackBackgroundEvents = trackBackgroundEvents ) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -1644,14 +1603,15 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) + sdkConfigCaptor.capture(), + any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) - .hasField("rumConfig") { + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { it.hasFieldEqualTo("backgroundEventTracking", trackBackgroundEvents ?: false) } } @@ -1664,8 +1624,10 @@ internal class DdSdkTest { val bridgeConfiguration = configuration.copy( vitalsUpdateFrequency = "RARE" ) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -1674,14 +1636,15 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) + sdkConfigCaptor.capture(), + any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) - .hasField("rumConfig") { + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { it.hasFieldEqualTo("vitalsMonitorUpdateFrequency", VitalsUpdateFrequency.RARE) } argumentCaptor { @@ -1700,8 +1663,10 @@ internal class DdSdkTest { vitalsUpdateFrequency = "NEVER", longTaskThresholdMs = 0.0 ) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) @@ -1710,14 +1675,15 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) + sdkConfigCaptor.capture(), + any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) - .hasField("rumConfig") { + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { it.hasFieldEqualTo("vitalsMonitorUpdateFrequency", VitalsUpdateFrequency.NEVER) } verifyZeroInteractions(mockChoreographer) @@ -1736,8 +1702,10 @@ internal class DdSdkTest { vitalsUpdateFrequency = fakeFrequency, longTaskThresholdMs = 0.0 ) - val credentialCaptor = argumentCaptor() - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() val frameDurationNs = threshold + frameDurationOverThreshold // When @@ -1747,14 +1715,15 @@ internal class DdSdkTest { inOrder(mockDatadog) { verify(mockDatadog).initialize( same(mockContext), - credentialCaptor.capture(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) + sdkConfigCaptor.capture(), + any() ) - verify(mockDatadog).registerRumMonitor(any()) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) } - assertThat(configCaptor.firstValue) - .hasField("rumConfig") { + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { it.hasFieldEqualTo("vitalsMonitorUpdateFrequency", VitalsUpdateFrequency.AVERAGE) } argumentCaptor { @@ -1787,7 +1756,7 @@ internal class DdSdkTest { // Given val bridgeConfiguration = configuration.copy( vitalsUpdateFrequency = "AVERAGE", - longTaskThresholdMs = (threshold / 1_000_000).toDouble(), + longTaskThresholdMs = (threshold / 1_000_000).toDouble() ) val frameDurationNs = threshold + frameDurationOverThreshold @@ -1824,7 +1793,7 @@ internal class DdSdkTest { // Given val bridgeConfiguration = configuration.copy( vitalsUpdateFrequency = "NEVER", - longTaskThresholdMs = (threshold / 1_000_000).toDouble(), + longTaskThresholdMs = (threshold / 1_000_000).toDouble() ) val frameDurationNs = threshold + frameDurationOverThreshold @@ -1866,19 +1835,26 @@ internal class DdSdkTest { DdSdkImplementation.DD_VERSION_SUFFIX to versionSuffix ) ) - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) // Then - verify(mockDatadog).initialize( - same(mockContext), - any(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) - ) - assertThat(configCaptor.firstValue) + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) + } + assertThat(sdkConfigCaptor.firstValue) .hasFieldEqualTo( "additionalConfig", mapOf( @@ -1919,22 +1895,31 @@ internal class DdSdkTest { reactNativeVersion = reactNativeVersion ) ) - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) // Then - verify(mockDatadog).initialize( - same(mockContext), - any(), - configCaptor.capture(), - eq(configuration.trackingConsent.asTrackingConsent()) - ) - assertThat(configCaptor.firstValue) - .hasField("rumConfig") { + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) + } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { val configurationMapper = it - .getActualValue>("rumEventMapper") + .getActualValue>( + "telemetryConfigurationMapper" + ) val result = configurationMapper.map(telemetryConfigurationEvent)!! assertThat(result.telemetry.configuration.trackNativeErrors!!).isEqualTo( trackNativeErrors @@ -1964,25 +1949,32 @@ internal class DdSdkTest { @Test fun `𝕄 set a resource mapper that does not drop resources 𝕎 initialize() {}`( - @Forgery resourceEvent: ResourceEvent, + @Forgery resourceEvent: ResourceEvent ) { // Given - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) // Then - verify(mockDatadog).initialize( - same(mockContext), - any(), - configCaptor.capture(), - any() - ) - assertThat(configCaptor.firstValue) - .hasField("rumConfig") { + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) + } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { val resourceMapper = it - .getActualValue>("rumEventMapper") + .getActualValue>("resourceEventMapper") val notDroppedEvent = resourceMapper.map(resourceEvent) assertThat(notDroppedEvent).isNotNull } @@ -1990,26 +1982,33 @@ internal class DdSdkTest { @Test fun `𝕄 set a resource mapper that drops flagged resources 𝕎 initialize() {}`( - @Forgery resourceEvent: ResourceEvent, + @Forgery resourceEvent: ResourceEvent ) { // Given - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() resourceEvent.context?.additionalProperties?.put("_dd.resource.drop_resource", true) // When testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) // Then - verify(mockDatadog).initialize( - same(mockContext), - any(), - configCaptor.capture(), - any() - ) - assertThat(configCaptor.firstValue) - .hasField("rumConfig") { + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) + } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { val resourceMapper = it - .getActualValue>("rumEventMapper") + .getActualValue>("resourceEventMapper") val droppedEvent = resourceMapper.map(resourceEvent) assertThat(droppedEvent).isNull() } @@ -2021,25 +2020,32 @@ internal class DdSdkTest { @Test fun `𝕄 set a action mapper that does not drop actions 𝕎 initialize() {}`( - @Forgery actionEvent: ActionEvent, + @Forgery actionEvent: ActionEvent ) { // Given - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() // When testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) // Then - verify(mockDatadog).initialize( - same(mockContext), - any(), - configCaptor.capture(), - any() - ) - assertThat(configCaptor.firstValue) - .hasField("rumConfig") { + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) + } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { val actionMapper = it - .getActualValue>("rumEventMapper") + .getActualValue>("actionEventMapper") val notDroppedEvent = actionMapper.map(actionEvent) assertThat(notDroppedEvent).isNotNull } @@ -2047,26 +2053,33 @@ internal class DdSdkTest { @Test fun `𝕄 set a action mapper that drops flagged actions 𝕎 initialize() {}`( - @Forgery actionEvent: ActionEvent, + @Forgery actionEvent: ActionEvent ) { // Given - val configCaptor = argumentCaptor() + val sdkConfigCaptor = argumentCaptor() + val rumConfigCaptor = argumentCaptor() + val logsConfigCaptor = argumentCaptor() + val traceConfigCaptor = argumentCaptor() actionEvent.context?.additionalProperties?.put("_dd.action.drop_action", true) // When testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) // Then - verify(mockDatadog).initialize( - same(mockContext), - any(), - configCaptor.capture(), - any() - ) - assertThat(configCaptor.firstValue) - .hasField("rumConfig") { + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + verify(mockDatadog).enableRum(rumConfigCaptor.capture()) + verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) + verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) + } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { val actionMapper = it - .getActualValue>("rumEventMapper") + .getActualValue>("actionEventMapper") val droppedEvent = actionMapper.map(actionEvent) assertThat(droppedEvent).isNull() } @@ -2266,7 +2279,6 @@ internal class DdSdkTest { @Test fun `𝕄 build Granted consent 𝕎 buildTrackingConsent {granted}`(forge: Forge) { - // When val consent = testedBridgeSdk.buildTrackingConsent( forge.anElementFrom("granted", "GRANTED") @@ -2278,7 +2290,6 @@ internal class DdSdkTest { @Test fun `𝕄 build Pending consent 𝕎 buildTrackingConsent {pending}`(forge: Forge) { - // When val consent = testedBridgeSdk.buildTrackingConsent( forge.anElementFrom("pending", "PENDING") @@ -2290,7 +2301,6 @@ internal class DdSdkTest { @Test fun `𝕄 build Granted consent 𝕎 buildTrackingConsent {not_granted}`(forge: Forge) { - // When val consent = testedBridgeSdk.buildTrackingConsent( forge.anElementFrom("not_granted", "NOT_GRANTED") @@ -2302,7 +2312,6 @@ internal class DdSdkTest { @Test fun `𝕄 build default Pending consent 𝕎 buildTrackingConsent {any}`(forge: Forge) { - // When val consent = testedBridgeSdk.buildTrackingConsent( forge.anElementFrom(null, "some-type") @@ -2314,7 +2323,6 @@ internal class DdSdkTest { @Test fun `𝕄 call setTrackingConsent 𝕎 setTrackingConsent ()`(forge: Forge) { - // Given val consent = forge.anElementFrom("pending", "granted", "not_granted") @@ -2329,7 +2337,6 @@ internal class DdSdkTest { fun `𝕄 not build proxy config 𝕎 no proxy config specified`( @Forgery configuration: DdSdkConfiguration ) { - // Given val config = configuration.copy(additionalConfig = null) @@ -2520,7 +2527,7 @@ internal class DdSdkTest { return Stream.of( Arguments.of("SMALL", BatchSize.SMALL), Arguments.of("MEDIUM", BatchSize.MEDIUM), - Arguments.of("LARGE", BatchSize.LARGE), + Arguments.of("LARGE", BatchSize.LARGE) ) } @@ -2529,7 +2536,7 @@ internal class DdSdkTest { return Stream.of( Arguments.of("RARE", UploadFrequency.RARE), Arguments.of("AVERAGE", UploadFrequency.AVERAGE), - Arguments.of("FREQUENT", UploadFrequency.FREQUENT), + Arguments.of("FREQUENT", UploadFrequency.FREQUENT) ) } } diff --git a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/DdSdkConfigurationExt.kt b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/DdSdkConfigurationExt.kt index 6e4e61b7e..ec19003cc 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/DdSdkConfigurationExt.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/DdSdkConfigurationExt.kt @@ -8,7 +8,7 @@ package com.datadog.tools.unit import com.datadog.android.core.configuration.BatchSize import com.datadog.android.core.configuration.UploadFrequency -import com.datadog.android.core.configuration.VitalsUpdateFrequency +import com.datadog.android.rum.configuration.VitalsUpdateFrequency import com.datadog.reactnative.ConfigurationForTelemetry import com.datadog.reactnative.DdSdkConfiguration import com.facebook.react.bridge.ReadableMap diff --git a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/MockRumMonitor.kt b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/MockRumMonitor.kt new file mode 100644 index 000000000..d389be984 --- /dev/null +++ b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/MockRumMonitor.kt @@ -0,0 +1,108 @@ +package com.datadog.tools.unit + +import com.datadog.android.rum.RumActionType +import com.datadog.android.rum.RumErrorSource +import com.datadog.android.rum.RumMonitor +import com.datadog.android.rum.RumResourceKind +import com.datadog.android.rum._RumInternalProxy + +class MockRumMonitor : RumMonitor { + override var debug = false + + override fun _getInternal(): _RumInternalProxy? { + return null + } + + override fun addAction( + type: RumActionType, + name: String, + attributes: Map + ) {} + + override fun addAttribute(key: String, value: Any?) {} + + override fun addError( + message: String, + source: RumErrorSource, + throwable: Throwable?, + attributes: Map + ) {} + + override fun addErrorWithStacktrace( + message: String, + source: RumErrorSource, + stacktrace: String?, + attributes: Map + ) {} + + override fun addFeatureFlagEvaluation(name: String, value: Any) {} + + override fun addTiming(name: String) {} + + override fun clearAttributes() {} + + override fun getAttributes(): Map { + return mapOf() + } + + override fun removeAttribute(key: String) {} + + override fun startAction( + type: RumActionType, + name: String, + attributes: Map + ) {} + + override fun startResource( + key: String, + method: String, + url: String, + attributes: Map + ) {} + + override fun startView( + key: Any, + name: String, + attributes: Map + ) {} + + override fun stopAction( + type: RumActionType, + name: String, + attributes: Map + ) {} + + override fun stopResource( + key: String, + statusCode: Int?, + size: Long?, + kind: RumResourceKind, + attributes: Map + ) {} + + override fun stopResourceWithError( + key: String, + statusCode: Int?, + message: String, + source: RumErrorSource, + stackTrace: String, + errorType: String?, + attributes: Map + ) {} + + override fun stopResourceWithError( + key: String, + statusCode: Int?, + message: String, + source: RumErrorSource, + throwable: Throwable, + attributes: Map + ) {} + + override fun stopSession() {} + + override fun stopView( + key: Any, + attributes: Map + ) {} +} diff --git a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/ReflectUtils.kt b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/ReflectUtils.kt index 291c840f8..49beb4465 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/ReflectUtils.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/ReflectUtils.kt @@ -81,7 +81,6 @@ inline fun getStaticValue( * @param fieldName the name of the field */ inline fun Class.getStaticValue(fieldName: String): R { - val field = getDeclaredField(fieldName) // make it accessible diff --git a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/ActionEventForgeryFactory.kt b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/ActionEventForgeryFactory.kt index e39a24f3c..994f28703 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/ActionEventForgeryFactory.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/ActionEventForgeryFactory.kt @@ -92,7 +92,7 @@ internal class ActionEventForgeryFactory : name = forge.anAlphabeticalString(), model = forge.anAlphabeticalString(), brand = forge.anAlphabeticalString(), - type = forge.aValueFrom(ActionEvent.DeviceType::class.java), + type = forge.aValueFrom(ActionEvent.DeviceType::class.java) ), context = ActionEvent.Context( additionalProperties = mutableMapOf() diff --git a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/DdSdkConfigurationForgeryFactory.kt b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/DdSdkConfigurationForgeryFactory.kt index adb0e2587..5294931b1 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/DdSdkConfigurationForgeryFactory.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/DdSdkConfigurationForgeryFactory.kt @@ -17,7 +17,7 @@ class DdSdkConfigurationForgeryFactory : ForgeryFactory { return DdSdkConfiguration( clientToken = forge.aStringMatching("pub[a-f0-9]{32}"), env = forge.anAlphabeticalString(), - applicationId = forge.aNullable { getForgery().toString() }, + applicationId = forge.getForgery().toString(), nativeCrashReportEnabled = forge.aNullable { aBool() }, nativeLongTaskThresholdMs = forge.aNullable { aDouble(100.0, 5000.0) }, longTaskThresholdMs = forge.aDouble(0.0, 100.0), @@ -63,9 +63,9 @@ class DdSdkConfigurationForgeryFactory : ForgeryFactory { trackInteractions = forge.aBool(), trackNetworkRequests = forge.aBool(), reactVersion = forge.aString(), - reactNativeVersion = forge.aString(), + reactNativeVersion = forge.aString() ), - trackFrustrations = forge.aNullable { aBool() }, + trackFrustrations = forge.aNullable { aBool() } ) } } diff --git a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/ResourceEventForgeryFactory.kt b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/ResourceEventForgeryFactory.kt index 6cfaf3930..dae18a67d 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/ResourceEventForgeryFactory.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/ResourceEventForgeryFactory.kt @@ -100,7 +100,7 @@ internal class ResourceEventForgeryFactory : name = forge.anAlphabeticalString(), model = forge.anAlphabeticalString(), brand = forge.anAlphabeticalString(), - type = forge.aValueFrom(ResourceEvent.DeviceType::class.java), + type = forge.aValueFrom(ResourceEvent.DeviceType::class.java) ), context = ResourceEvent.Context( additionalProperties = mutableMapOf() diff --git a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/TelemetryConfigurationEventForgeryFactory.kt b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/TelemetryConfigurationEventForgeryFactory.kt index 7f56c3219..98a550338 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/TelemetryConfigurationEventForgeryFactory.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/TelemetryConfigurationEventForgeryFactory.kt @@ -59,6 +59,7 @@ internal class TelemetryConfigurationEventForgeryFactory : forge.aNullable { aBool() }, forge.aNullable { aBool() }, forge.aNullable { aBool() }, + forge.aNullable { aBool() }, forge.aNullable { aString() }, forge.aNullable { aBool() }, forge.aNullable { aBool() }, diff --git a/packages/core/ios/Sources/DdLogsImplementation.swift b/packages/core/ios/Sources/DdLogsImplementation.swift index 9cc154813..a567ae863 100644 --- a/packages/core/ios/Sources/DdLogsImplementation.swift +++ b/packages/core/ios/Sources/DdLogsImplementation.swift @@ -5,51 +5,23 @@ */ import Foundation -import Datadog - -extension DDLogger: NativeLogger { - // Adding stubs until they are added to the the LoggerProtocol extension - func debug(_ message: String, errorKind: String?, errorMessage: String?, stackTrace: String?, attributes: [String : Encodable]?) { - log(level: .debug, message: message, errorKind: errorKind, errorMessage: errorMessage, stackTrace: stackTrace, attributes: attributes) - } - func info(_ message: String, errorKind: String?, errorMessage: String?, stackTrace: String?, attributes: [String : Encodable]?) { - log(level: .info, message: message, errorKind: errorKind, errorMessage: errorMessage, stackTrace: stackTrace, attributes: attributes) - } - func warn(_ message: String, errorKind: String?, errorMessage: String?, stackTrace: String?, attributes: [String : Encodable]?) { - log(level: .warn, message: message, errorKind: errorKind, errorMessage: errorMessage, stackTrace: stackTrace, attributes: attributes) - } - func error(_ message: String, errorKind: String?, errorMessage: String?, stackTrace: String?, attributes: [String : Encodable]?) { - log(level: .error, message: message, errorKind: errorKind, errorMessage: errorMessage, stackTrace: stackTrace, attributes: attributes) - } -} -internal protocol NativeLogger { - func debug(_ message: String, error: Error?, attributes: [String: Encodable]?) - func debug(_ message: String, errorKind: String?, errorMessage: String?, stackTrace: String?, attributes: [String: Encodable]?) - func info(_ message: String, error: Error?, attributes: [String: Encodable]?) - func info(_ message: String, errorKind: String?, errorMessage: String?, stackTrace: String?, attributes: [String: Encodable]?) - func warn(_ message: String, error: Error?, attributes: [String: Encodable]?) - func warn(_ message: String, errorKind: String?, errorMessage: String?, stackTrace: String?, attributes: [String: Encodable]?) - func error(_ message: String, error: Error?, attributes: [String: Encodable]?) - func error(_ message: String, errorKind: String?, errorMessage: String?, stackTrace: String?, attributes: [String: Encodable]?) -} +import DatadogLogs +import DatadogCore @objc public class DdLogsImplementation: NSObject { - private lazy var logger: NativeLogger = loggerProvider() - private let loggerProvider: () -> NativeLogger + private lazy var logger: LoggerProtocol = loggerProvider() + private let loggerProvider: () -> LoggerProtocol private let isSDKInitialized: () -> Bool - internal init(_ loggerProvider: @escaping () -> NativeLogger, _ isSDKInitialized: @escaping () -> Bool) { + internal init(_ loggerProvider: @escaping () -> LoggerProtocol, _ isSDKInitialized: @escaping () -> Bool) { self.loggerProvider = loggerProvider self.isSDKInitialized = isSDKInitialized } @objc public override convenience init() { - let builder = Logger.builder - .sendNetworkInfo(true) - .printLogsToConsole(true) - self.init({ builder.build() }, { Datadog.isInitialized }) + self.init({ Logger.create(with: Logger.Configuration(networkInfoEnabled: true, consoleLogFormat: .short)) }, { Datadog.isInitialized() }) } @objc @@ -103,7 +75,7 @@ public class DdLogsImplementation: NSObject { return } let attributes = castAttributesToSwift(context).mergeWithGlobalAttributes() - logger.debug(message, errorKind: errorKind, errorMessage: errorMessage, stackTrace: stacktrace, attributes: attributes) + logger._internal.log(level: .debug, message: message, errorKind: errorKind, errorMessage: errorMessage, stackTrace: stacktrace, attributes: attributes) resolve(nil) } @@ -114,7 +86,7 @@ public class DdLogsImplementation: NSObject { return } let attributes = castAttributesToSwift(context).mergeWithGlobalAttributes() - logger.info(message, errorKind: errorKind, errorMessage: errorMessage, stackTrace: stacktrace, attributes: attributes) + logger._internal.log(level: .info, message: message, errorKind: errorKind, errorMessage: errorMessage, stackTrace: stacktrace, attributes: attributes) resolve(nil) } @@ -125,7 +97,7 @@ public class DdLogsImplementation: NSObject { return } let attributes = castAttributesToSwift(context).mergeWithGlobalAttributes() - logger.warn(message, errorKind: errorKind, errorMessage: errorMessage, stackTrace: stacktrace, attributes: attributes) + logger._internal.log(level: .warn, message: message, errorKind: errorKind, errorMessage: errorMessage, stackTrace: stacktrace, attributes: attributes) resolve(nil) } @@ -136,7 +108,7 @@ public class DdLogsImplementation: NSObject { return } let attributes = castAttributesToSwift(context).mergeWithGlobalAttributes() - logger.error(message, errorKind: errorKind, errorMessage: errorMessage, stackTrace: stacktrace, attributes: attributes) + logger._internal.log(level: .error, message: message, errorKind: errorKind, errorMessage: errorMessage, stackTrace: stacktrace, attributes: attributes) resolve(nil) } } diff --git a/packages/core/ios/Sources/DdRumImplementation.swift b/packages/core/ios/Sources/DdRumImplementation.swift index 46049b80b..3e2d90f14 100644 --- a/packages/core/ios/Sources/DdRumImplementation.swift +++ b/packages/core/ios/Sources/DdRumImplementation.swift @@ -5,34 +5,10 @@ */ import Foundation -import Datadog - -extension DDRUMMonitor: NativeRUM { } -internal protocol NativeRUM { - func startView(key: String, name: String?, attributes: [String: Encodable]) - func stopView(key: String, attributes: [String: Encodable]) - func addError(message: String, type: String?, source: RUMErrorSource, stack: String?, attributes: [String: Encodable], file: StaticString?, line: UInt?) - func startResourceLoading(resourceKey: String, httpMethod: RUMMethod, urlString: String, attributes: [String: Encodable]) - func stopResourceLoading(resourceKey: String, statusCode: Int?, kind: RUMResourceType, size: Int64?, attributes: [String: Encodable]) - func startUserAction(type: RUMUserActionType, name: String, attributes: [String: Encodable]) - func stopUserAction(type: RUMUserActionType, name: String?, attributes: [String: Encodable]) - func addUserAction(type: RUMUserActionType, name: String, attributes: [String: Encodable]) - func addTiming(name: String) - func stopSession() - func addResourceMetrics(resourceKey: String, - fetch: (start: Date, end: Date), - redirection: (start: Date, end: Date)?, - dns: (start: Date, end: Date)?, - connect: (start: Date, end: Date)?, - ssl: (start: Date, end: Date)?, - firstByte: (start: Date, end: Date)?, - download: (start: Date, end: Date)?, - responseSize: Int64?, - attributes: [AttributeKey: AttributeValue]) - func addFeatureFlagEvaluation(name: String, value: Encodable) -} +import DatadogRUM +import DatadogInternal -private extension RUMUserActionType { +private extension RUMActionType { init(from string: String) { switch string.lowercased() { case "tap": self = .tap @@ -101,18 +77,27 @@ public class DdRumImplementation: NSObject { internal static let missingResourceSize = -1 - lazy var nativeRUM: NativeRUM = rumProvider() - private let rumProvider: () -> NativeRUM + lazy var nativeRUM: RUMMonitorProtocol = rumProvider() + lazy var rumInternal: RUMMonitorInternalProtocol? = rumInternalProvider() + private let rumProvider: () -> RUMMonitorProtocol + private let rumInternalProvider: () -> RUMMonitorInternalProtocol? - private typealias UserAction = (type: RUMUserActionType, name: String?) + private typealias UserAction = (type: RUMActionType, name: String?) - internal init(_ rumProvider: @escaping () -> NativeRUM) { + internal init( + _ rumProvider: @escaping () -> RUMMonitorProtocol, + _ rumInternalProvider: @escaping () -> RUMMonitorInternalProtocol? + ) { self.rumProvider = rumProvider + self.rumInternalProvider = rumInternalProvider } @objc public override convenience init() { - self.init { Global.rum } + self.init( + { RUMMonitor.shared() }, + { RUMMonitor.shared()._internal } + ) } @objc @@ -129,25 +114,25 @@ public class DdRumImplementation: NSObject { @objc public func startAction(type: String, name: String, context: NSDictionary, timestampMs: Double, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - nativeRUM.startUserAction(type: RUMUserActionType(from: type), name: name, attributes: attributes(from: context, with: timestampMs)) + nativeRUM.startAction(type: RUMActionType(from: type), name: name, attributes: attributes(from: context, with: timestampMs)) resolve(nil) } @objc public func stopAction(type: String, name: String, context: NSDictionary, timestampMs: Double, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - nativeRUM.stopUserAction(type: RUMUserActionType(from: type), name: name, attributes: attributes(from: context, with: timestampMs)) + nativeRUM.stopAction(type: RUMActionType(from: type), name: name, attributes: attributes(from: context, with: timestampMs)) resolve(nil) } @objc public func addAction(type: String, name: String, context: NSDictionary, timestampMs: Double, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - nativeRUM.addUserAction(type: RUMUserActionType(from: type), name: name, attributes: attributes(from: context, with: timestampMs)) + nativeRUM.addAction(type: RUMActionType(from: type), name: name, attributes: attributes(from: context, with: timestampMs)) resolve(nil) } @objc public func startResource(key: String, method: String, url: String, context: NSDictionary, timestampMs: Double, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - nativeRUM.startResourceLoading(resourceKey: key, httpMethod: RUMMethod(from: method), urlString: url, attributes: attributes(from: context, with: timestampMs)) + nativeRUM.startResource(resourceKey: key, httpMethod: RUMMethod(from: method), urlString: url, attributes: attributes(from: context, with: timestampMs)) resolve(nil) } @@ -160,7 +145,7 @@ public class DdRumImplementation: NSObject { addResourceMetrics(key: key, resourceTimings: resourceTimings) } - nativeRUM.stopResourceLoading( + nativeRUM.stopResource( resourceKey: key, statusCode: Int(statusCode), kind: RUMResourceType(from: kind), @@ -172,7 +157,7 @@ public class DdRumImplementation: NSObject { @objc public func addError(message: String, source: String, stacktrace: String, context: NSDictionary, timestampMs: Double, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - nativeRUM.addError(message: message, type: nil, source: RUMErrorSource(from: source), stack: stacktrace, attributes: attributes(from: context, with: timestampMs), file: nil, line: nil) + nativeRUM.addError(message: message, type: nil, stack: stacktrace, source: RUMErrorSource(from: source), attributes: attributes(from: context, with: timestampMs), file: nil, line: nil) resolve(nil) } @@ -214,8 +199,10 @@ public class DdRumImplementation: NSObject { let firstByte = timingValue(from: resourceTimings, for: Self.firstByteTimingKey) let download = timingValue(from: resourceTimings, for: Self.downloadTimingKey) + if let fetch = fetch { - nativeRUM.addResourceMetrics( + rumInternal?.addResourceMetrics( + at: Date.init(), resourceKey: key, fetch: fetch, redirection: redirect, diff --git a/packages/core/ios/Sources/DdSdkConfiguration.swift b/packages/core/ios/Sources/DdSdkConfiguration.swift index cff287057..975e35cd2 100644 --- a/packages/core/ios/Sources/DdSdkConfiguration.swift +++ b/packages/core/ios/Sources/DdSdkConfiguration.swift @@ -5,6 +5,8 @@ */ import Foundation +import DatadogCore +import DatadogInternal /** A configuration object to initialize Datadog's features. @@ -31,18 +33,18 @@ import Foundation public class DdSdkConfiguration: NSObject { public var clientToken: String = "" public var env: String = "" - public var applicationId: String? = nil + public var applicationId: String = "" public var nativeCrashReportEnabled: Bool? = nil public var nativeLongTaskThresholdMs: Double? = nil public var longTaskThresholdMs: Double = 0.0 public var sampleRate: Double? = nil - public var site: NSString? = nil + public var site: DatadogSite public var trackingConsent: NSString? = nil public var telemetrySampleRate: Double? = nil public var vitalsUpdateFrequency: NSString? = nil public var trackFrustrations: Bool? = nil - public var uploadFrequency: NSString? = nil - public var batchSize: NSString? = nil + public var uploadFrequency: Datadog.Configuration.UploadFrequency + public var batchSize: Datadog.Configuration.BatchSize public var trackBackgroundEvents: Bool? = nil public var additionalConfig: NSDictionary? = nil public var configurationForTelemetry: ConfigurationForTelemetry? = nil @@ -50,7 +52,7 @@ public class DdSdkConfiguration: NSObject { public init( clientToken: String, env: String, - applicationId: String?, + applicationId: String, nativeCrashReportEnabled: Bool?, nativeLongTaskThresholdMs: Double?, longTaskThresholdMs: Double, @@ -73,17 +75,62 @@ public class DdSdkConfiguration: NSObject { self.nativeLongTaskThresholdMs = nativeLongTaskThresholdMs self.longTaskThresholdMs = longTaskThresholdMs self.sampleRate = sampleRate - self.site = site + self.site = DdSdkConfiguration.buildSite(site: site) self.trackingConsent = trackingConsent self.telemetrySampleRate = telemetrySampleRate self.vitalsUpdateFrequency = vitalsUpdateFrequency self.trackFrustrations = trackFrustrations - self.uploadFrequency = uploadFrequency - self.batchSize = batchSize + self.uploadFrequency = DdSdkConfiguration.buildUploadFrequency(uploadFrequency: uploadFrequency) + self.batchSize = DdSdkConfiguration.buildBatchSize(batchSize: batchSize) self.trackBackgroundEvents = trackBackgroundEvents self.additionalConfig = additionalConfig self.configurationForTelemetry = configurationForTelemetry } + + static func buildSite(site: NSString?) -> DatadogSite { + switch site?.lowercased ?? "us" { + case "us1", "us": + return .us1 + case "eu1", "eu": + return .eu1 + case "us3": + return .us3 + case "us5": + return .us5 + case "us1_fed", "gov": + return .us1_fed + case "ap1": + return .ap1 + default: + return .us1 + } + } + + static func buildBatchSize(batchSize: NSString?) -> Datadog.Configuration.BatchSize { + switch batchSize?.lowercased ?? "" { + case "small": + return .small + case "medium": + return .medium + case "large": + return .large + default: + return .medium + } + } + + static func buildUploadFrequency(uploadFrequency: NSString?) -> Datadog.Configuration.UploadFrequency { + switch uploadFrequency?.lowercased ?? "" { + case "rare": + return .rare + case "average": + return .average + case "frequent": + return .frequent + default: + return .average + } + } } public class ConfigurationForTelemetry: NSObject { diff --git a/packages/core/ios/Sources/DdSdkImplementation.swift b/packages/core/ios/Sources/DdSdkImplementation.swift index af20d99b1..d97445df5 100644 --- a/packages/core/ios/Sources/DdSdkImplementation.swift +++ b/packages/core/ios/Sources/DdSdkImplementation.swift @@ -5,8 +5,13 @@ */ import Foundation -import Datadog +import DatadogCore +import DatadogRUM +import DatadogLogs +import DatadogTrace import DatadogCrashReporting +import DatadogWebViewTracking +import DatadogInternal import React func getDefaultAppVersion() -> String { @@ -20,18 +25,35 @@ public class DdSdkImplementation: NSObject { let jsDispatchQueue: DispatchQueueType let jsRefreshRateMonitor: RefreshRateMonitor let mainDispatchQueue: DispatchQueueType - + let RUMMonitorProvider: () -> RUMMonitorProtocol + let RUMMonitorInternalProvider: () -> RUMMonitorInternalProtocol? + var webviewMessageEmitter: InternalExtension.AbstractMessageEmitter? + private let jsLongTaskThresholdInSeconds: TimeInterval = 0.1; @objc public convenience init(bridge: RCTBridge) { - self.init(mainDispatchQueue: DispatchQueue.main, jsDispatchQueue: bridge, jsRefreshRateMonitor: JSRefreshRateMonitor.init()) + self.init( + mainDispatchQueue: DispatchQueue.main, + jsDispatchQueue: bridge, + jsRefreshRateMonitor: JSRefreshRateMonitor.init(), + RUMMonitorProvider: { RUMMonitor.shared() }, + RUMMonitorInternalProvider: { RUMMonitor.shared()._internal } + ) } - init(mainDispatchQueue: DispatchQueueType, jsDispatchQueue: DispatchQueueType, jsRefreshRateMonitor: RefreshRateMonitor) { + init( + mainDispatchQueue: DispatchQueueType, + jsDispatchQueue: DispatchQueueType, + jsRefreshRateMonitor: RefreshRateMonitor, + RUMMonitorProvider: @escaping () -> RUMMonitorProtocol, + RUMMonitorInternalProvider: @escaping () -> RUMMonitorInternalProtocol? + ) { self.mainDispatchQueue = mainDispatchQueue self.jsDispatchQueue = jsDispatchQueue self.jsRefreshRateMonitor = jsRefreshRateMonitor + self.RUMMonitorProvider = RUMMonitorProvider + self.RUMMonitorInternalProvider = RUMMonitorInternalProvider super.init() } @@ -42,7 +64,8 @@ public class DdSdkImplementation: NSObject { self.mainDispatchQueue.async { let sdkConfiguration = configuration.asDdSdkConfiguration() - if Datadog.isInitialized { + // TODO: see if this `if` is still needed + if Datadog.isInitialized() { // Initializing the SDK twice results in Global.rum and // Global.sharedTracer to be set to no-op instances consolePrint("Datadog SDK is already initialized, skipping initialization.") @@ -57,24 +80,39 @@ public class DdSdkImplementation: NSObject { } self.setVerbosityLevel(additionalConfig: sdkConfiguration.additionalConfig) - let ddConfig = self.buildConfiguration(configuration: sdkConfiguration) + let sdkConfig = self.buildSDKConfiguration(configuration: sdkConfiguration) let consent = self.buildTrackingConsent(consent: sdkConfiguration.trackingConsent) - Datadog.initialize(appContext: Datadog.AppContext(), trackingConsent: consent, configuration: ddConfig) - self.sendConfigurationAsTelemetry(rnConfiguration: sdkConfiguration) - - Global.rum = RUMMonitor.initialize() + let core = Datadog.initialize(with: sdkConfig, trackingConsent: consent) + self.enableFeatures(sdkConfiguration: sdkConfiguration, core: core) self.startJSRefreshRateMonitoring(sdkConfiguration: sdkConfiguration) - + resolve(nil) } } + + func enableFeatures(sdkConfiguration: DdSdkConfiguration, core: DatadogCoreProtocol) { + let rumConfig = buildRUMConfiguration(configuration: sdkConfiguration) + RUM.enable(with: rumConfig, in: core) + + Logs.enable(with: Logs.Configuration(), in: core) + + Trace.enable(with: Trace.Configuration(), in: core) + + if sdkConfiguration.nativeCrashReportEnabled ?? false { + CrashReporting.enable(in: core) + } + + self.webviewMessageEmitter = WebViewTracking._internal.messageEmitter(in: core) + + overrideReactNativeTelemetry(rnConfiguration: sdkConfiguration, core: core) + } @objc public func setAttributes(attributes: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { let castedAttributes = castAttributesToSwift(attributes) for (key, value) in castedAttributes { - Global.rum.addAttribute(forKey: key, value: value) + RUMMonitorProvider().addAttribute(forKey: key, value: value) GlobalState.addAttribute(forKey: key, value: value) } @@ -114,146 +152,107 @@ public class DdSdkImplementation: NSObject { @objc public func consumeWebviewEvent(message: NSString, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { do{ - try Datadog._internal.webEventBridge.send(message) + try self.webviewMessageEmitter?.send(body: message) } catch { Datadog._internal.telemetry.error(id: "datadog_react_native:\(error.localizedDescription)", message: "The message being sent was:\(message)" as String, kind: "WebViewEventBridgeError" as String, stack: String(describing: error) as String) } resolve(nil) } - func sendConfigurationAsTelemetry(rnConfiguration: DdSdkConfiguration) -> Void { - Datadog._internal.telemetry.setConfigurationMapper { event in - var event = event - - var configuration = event.telemetry.configuration - configuration.initializationType = rnConfiguration.configurationForTelemetry?.initializationType as? String - configuration.trackErrors = rnConfiguration.configurationForTelemetry?.trackErrors - configuration.trackInteractions = rnConfiguration.configurationForTelemetry?.trackInteractions - configuration.trackResources = rnConfiguration.configurationForTelemetry?.trackNetworkRequests - configuration.trackNetworkRequests = rnConfiguration.configurationForTelemetry?.trackNetworkRequests - configuration.reactVersion = rnConfiguration.configurationForTelemetry?.reactVersion as? String - configuration.reactNativeVersion = rnConfiguration.configurationForTelemetry?.reactNativeVersion as? String - - // trackCrossPlatformLongTasks will be deprecated for trackLongTask - configuration.trackCrossPlatformLongTasks = rnConfiguration.longTaskThresholdMs != 0 - configuration.trackLongTask = rnConfiguration.longTaskThresholdMs != 0 - configuration.trackNativeErrors = rnConfiguration.nativeCrashReportEnabled - configuration.trackNativeLongTasks = rnConfiguration.nativeLongTaskThresholdMs != 0 - event.telemetry.configuration = configuration - - return event - } + func overrideReactNativeTelemetry(rnConfiguration: DdSdkConfiguration, core: DatadogCoreProtocol) -> Void { + core.telemetry.configuration( + initializationType: rnConfiguration.configurationForTelemetry?.initializationType as? String, + reactNativeVersion: rnConfiguration.configurationForTelemetry?.reactNativeVersion as? String, + reactVersion: rnConfiguration.configurationForTelemetry?.reactVersion as? String, + trackCrossPlatformLongTasks: rnConfiguration.longTaskThresholdMs != 0, + trackErrors: rnConfiguration.configurationForTelemetry?.trackErrors, + trackInteractions: rnConfiguration.configurationForTelemetry?.trackInteractions, + trackLongTask: rnConfiguration.longTaskThresholdMs != 0, + trackNativeErrors: rnConfiguration.nativeLongTaskThresholdMs != 0, + trackNativeLongTasks: rnConfiguration.nativeLongTaskThresholdMs != 0, + trackNetworkRequests: rnConfiguration.configurationForTelemetry?.trackNetworkRequests + ) } - func buildConfiguration(configuration: DdSdkConfiguration, defaultAppVersion: String = getDefaultAppVersion()) -> Datadog.Configuration { - let ddConfigBuilder: Datadog.Configuration.Builder - if let rumAppID = configuration.applicationId { - ddConfigBuilder = Datadog.Configuration.builderUsing( - rumApplicationID: rumAppID, - clientToken: configuration.clientToken, - environment: configuration.env - ) - .set(rumSessionsSamplingRate: Float(configuration.sampleRate ?? 100.0)) - } else { - ddConfigBuilder = Datadog.Configuration.builderUsing( - clientToken: configuration.clientToken, - environment: configuration.env - ) - } - - switch configuration.site?.lowercased ?? "us" { - case "us1", "us": - _ = ddConfigBuilder.set(endpoint: .us1) - case "eu1", "eu": - _ = ddConfigBuilder.set(endpoint: .eu1) - case "us3": - _ = ddConfigBuilder.set(endpoint: .us3) - case "us5": - _ = ddConfigBuilder.set(endpoint: .us5) - case "us1_fed", "gov": - _ = ddConfigBuilder.set(endpoint: .us1_fed) - case "ap1": - _ = ddConfigBuilder.set(endpoint: .ap1) - default: - _ = ddConfigBuilder.set(endpoint: .us1) - } - - _ = ddConfigBuilder.set(mobileVitalsFrequency: buildVitalsUpdateFrequency(frequency: configuration.vitalsUpdateFrequency)) - - _ = ddConfigBuilder.set(uploadFrequency: buildUploadFrequency(frequency: configuration.uploadFrequency)) - - _ = ddConfigBuilder.set(batchSize: buildBatchSize(batchSize: configuration.batchSize)) - - if var telemetrySampleRate = (configuration.telemetrySampleRate as? NSNumber)?.floatValue { - _ = ddConfigBuilder.set(sampleTelemetry: telemetrySampleRate) - } - - if var trackFrustrations = (configuration.trackFrustrations) { - _ = ddConfigBuilder.trackFrustrations(trackFrustrations) - } + func buildSDKConfiguration(configuration: DdSdkConfiguration, defaultAppVersion: String = getDefaultAppVersion()) -> Datadog.Configuration { + var config = Datadog.Configuration( + clientToken: configuration.clientToken, + env: configuration.env, + site: configuration.site, + service: configuration.additionalConfig?[InternalConfigurationAttributes.serviceName] as? String ?? nil, + batchSize: configuration.batchSize, + uploadFrequency: configuration.uploadFrequency, + proxyConfiguration: buildProxyConfiguration(config: configuration.additionalConfig) + ) + + if var additionalConfiguration = configuration.additionalConfig as? [String: Any] { + if let versionSuffix = additionalConfiguration[InternalConfigurationAttributes.versionSuffix] as? String { + let datadogVersion = defaultAppVersion + versionSuffix + additionalConfiguration[CrossPlatformAttributes.version] = datadogVersion + } - if var trackBackgroundEvents = (configuration.trackBackgroundEvents) { - _ = ddConfigBuilder.trackBackgroundEvents(trackBackgroundEvents) + config._internal_mutation { + $0.additionalConfiguration = additionalConfiguration + } } + return config + } + + func buildRUMConfiguration(configuration: DdSdkConfiguration) -> RUM.Configuration { + var longTaskThreshold: TimeInterval? = nil if let threshold = configuration.nativeLongTaskThresholdMs as? TimeInterval { if (threshold != 0) { // `nativeLongTaskThresholdMs` attribute is in milliseconds - _ = ddConfigBuilder.trackRUMLongTasks(threshold: threshold / 1_000) + longTaskThreshold = threshold / 1_000 } } - - let additionalConfig = configuration.additionalConfig - - if var additionalConfiguration = additionalConfig as? [String: Any] { - if let versionSuffix = additionalConfig?[InternalConfigurationAttributes.versionSuffix] as? String { - let datadogVersion = defaultAppVersion + versionSuffix - additionalConfiguration[CrossPlatformAttributes.version] = datadogVersion - } - - _ = ddConfigBuilder.set(additionalConfiguration: additionalConfiguration) - } - - if let enableViewTracking = additionalConfig?[InternalConfigurationAttributes.nativeViewTracking] as? Bool, enableViewTracking { - _ = ddConfigBuilder.trackUIKitRUMViews() - } - - if let enableInteractionTracking = additionalConfig?[InternalConfigurationAttributes.nativeInteractionTracking] as? Bool, enableInteractionTracking { - _ = ddConfigBuilder.trackUIKitRUMActions() + + var uiKitViewsPredicate: UIKitRUMViewsPredicate? = nil + if let enableViewTracking = configuration.additionalConfig?[InternalConfigurationAttributes.nativeViewTracking] as? Bool, enableViewTracking { + uiKitViewsPredicate = DefaultUIKitRUMViewsPredicate() } - if let serviceName = additionalConfig?[InternalConfigurationAttributes.serviceName] as? String { - _ = ddConfigBuilder.set(serviceName: serviceName) + var uiKitActionsPredicate: UIKitRUMActionsPredicate? = nil + if let enableInteractionTracking = configuration.additionalConfig?[InternalConfigurationAttributes.nativeInteractionTracking] as? Bool, enableInteractionTracking { + uiKitActionsPredicate = DefaultUIKitRUMActionsPredicate() } - - if let firstPartyHosts = additionalConfig?[InternalConfigurationAttributes.firstPartyHosts] as? NSArray { + + var urlSessionTracking: RUM.Configuration.URLSessionTracking? = nil + if let firstPartyHosts = configuration.additionalConfig?[InternalConfigurationAttributes.firstPartyHosts] as? NSArray { // We will always fall under this condition as firstPartyHosts is an empty array by default - _ = ddConfigBuilder.trackURLSession(firstPartyHostsWithHeaderTypes: firstPartyHosts.asFirstPartyHosts()) - } - - if let proxyConfiguration = buildProxyConfiguration(config: additionalConfig) { - _ = ddConfigBuilder.set(proxyConfiguration: proxyConfiguration) - } - - if configuration.nativeCrashReportEnabled ?? false { - _ = ddConfigBuilder.enableCrashReporting(using: DDCrashReportingPlugin()) + urlSessionTracking = RUM.Configuration.URLSessionTracking( + firstPartyHostsTracing: .traceWithHeaders( + hostsWithHeaders: firstPartyHosts.asFirstPartyHosts(), + sampleRate: 100.0 + ) + ) } - - _ = ddConfigBuilder.setRUMResourceEventMapper({ resourceEvent in - if resourceEvent.context?.contextInfo[InternalConfigurationAttributes.dropResource] != nil { - return nil - } - return resourceEvent - }) - - _ = ddConfigBuilder.setRUMActionEventMapper({ actionEvent in - if actionEvent.context?.contextInfo[InternalConfigurationAttributes.dropResource] != nil { - return nil - } - return actionEvent - }) - - return ddConfigBuilder.build() + + return RUM.Configuration( + applicationID: configuration.applicationId, + sessionSampleRate: (configuration.sampleRate as? NSNumber)?.floatValue ?? 100.0, + uiKitViewsPredicate: uiKitViewsPredicate, + uiKitActionsPredicate: uiKitActionsPredicate, + urlSessionTracking: urlSessionTracking, + trackFrustrations: configuration.trackFrustrations ?? true, + trackBackgroundEvents: configuration.trackBackgroundEvents ?? false, + longTaskThreshold: longTaskThreshold, + vitalsUpdateFrequency: buildVitalsUpdateFrequency(frequency: configuration.vitalsUpdateFrequency), + resourceEventMapper: { resourceEvent in + if resourceEvent.context?.contextInfo[InternalConfigurationAttributes.dropResource] != nil { + return nil + } + return resourceEvent + }, + actionEventMapper: { actionEvent in + if actionEvent.context?.contextInfo[InternalConfigurationAttributes.dropResource] != nil { + return nil + } + return actionEvent + }, + telemetrySampleRate: (configuration.telemetrySampleRate as? NSNumber)?.floatValue ?? 20.0 + ) } func buildProxyConfiguration(config: NSDictionary?) -> [AnyHashable: Any]? { @@ -311,21 +310,19 @@ public class DdSdkImplementation: NSObject { return trackingConsent } - func buildVitalsUpdateFrequency(frequency: NSString?) -> Datadog.Configuration.VitalsFrequency { - let vitalsFrequency: Datadog.Configuration.VitalsFrequency + func buildVitalsUpdateFrequency(frequency: NSString?) -> RUM.Configuration.VitalsFrequency? { switch frequency?.lowercased { case "never": - vitalsFrequency = .never + return nil case "rare": - vitalsFrequency = .rare + return .rare case "average": - vitalsFrequency = .average + return .average case "frequent": - vitalsFrequency = .frequent + return .frequent default: - vitalsFrequency = .average + return .average } - return vitalsFrequency } func buildUploadFrequency(frequency: NSString?) -> Datadog.Configuration.UploadFrequency { @@ -364,7 +361,8 @@ public class DdSdkImplementation: NSObject { case "debug": Datadog.verbosityLevel = .debug case "info": - Datadog.verbosityLevel = .info + // .info is mapped to .debug + Datadog.verbosityLevel = .debug case "warn": Datadog.verbosityLevel = .warn case "error": @@ -382,7 +380,7 @@ public class DdSdkImplementation: NSObject { } func buildFrameTimeCallback(sdkConfiguration: DdSdkConfiguration)-> ((Double) -> ())? { - let jsRefreshRateMonitoringEnabled = buildVitalsUpdateFrequency(frequency: sdkConfiguration.vitalsUpdateFrequency) != .never + let jsRefreshRateMonitoringEnabled = buildVitalsUpdateFrequency(frequency: sdkConfiguration.vitalsUpdateFrequency) != nil let jsLongTaskMonitoringEnabled = sdkConfiguration.longTaskThresholdMs != 0 if (!jsRefreshRateMonitoringEnabled && !jsLongTaskMonitoringEnabled) { @@ -391,10 +389,10 @@ public class DdSdkImplementation: NSObject { func frameTimeCallback(frameTime: Double) { if (jsRefreshRateMonitoringEnabled && frameTime > 0) { - Global.rum._internal.updatePerformanceMetric(at: Date(), metric: .jsFrameTimeSeconds, value: frameTime) + RUMMonitorInternalProvider()?.updatePerformanceMetric(at: Date(), metric: .jsFrameTimeSeconds, value: frameTime, attributes: [:]) } if (jsLongTaskMonitoringEnabled && frameTime > sdkConfiguration.longTaskThresholdMs / 1_000) { - Global.rum._internal.addLongTask(at: Date(), duration: frameTime, attributes: ["long_task.target": "javascript"]) + RUMMonitorInternalProvider()?.addLongTask(at: Date(), duration: frameTime, attributes: ["long_task.target": "javascript"]) } } diff --git a/packages/core/ios/Sources/DdTraceImplementation.swift b/packages/core/ios/Sources/DdTraceImplementation.swift index f6a2d2886..01762c61f 100644 --- a/packages/core/ios/Sources/DdTraceImplementation.swift +++ b/packages/core/ios/Sources/DdTraceImplementation.swift @@ -5,7 +5,7 @@ */ import Foundation -import Datadog +import DatadogTrace @objc public class DdTraceImplementation: NSObject { @@ -19,7 +19,7 @@ public class DdTraceImplementation: NSObject { @objc public override convenience init() { - self.init { Tracer.initialize(configuration: Tracer.Configuration()) } + self.init { Tracer.shared() } } @objc diff --git a/packages/core/ios/Sources/RNDdSdkConfiguration.swift b/packages/core/ios/Sources/RNDdSdkConfiguration.swift index cb813fda0..c0ff3080c 100644 --- a/packages/core/ios/Sources/RNDdSdkConfiguration.swift +++ b/packages/core/ios/Sources/RNDdSdkConfiguration.swift @@ -4,7 +4,9 @@ * Copyright 2016-Present Datadog, Inc. */ -import Datadog +import DatadogCore +import DatadogRUM +import DatadogInternal import Foundation extension NSDictionary { @@ -30,7 +32,7 @@ extension NSDictionary { return DdSdkConfiguration( clientToken: (clientToken != nil) ? clientToken! : String(), env: (env != nil) ? env! : String(), - applicationId: applicationId, + applicationId: (applicationId != nil) ? applicationId! : String(), nativeCrashReportEnabled: nativeCrashReportEnabled, nativeLongTaskThresholdMs: nativeLongTaskThresholdMs, longTaskThresholdMs: (longTaskThresholdMs != nil) ? longTaskThresholdMs! : Double(), diff --git a/packages/core/ios/Sources/RUMMonitorInternalProtocol.swift b/packages/core/ios/Sources/RUMMonitorInternalProtocol.swift new file mode 100644 index 000000000..5eb91b174 --- /dev/null +++ b/packages/core/ios/Sources/RUMMonitorInternalProtocol.swift @@ -0,0 +1,40 @@ + +/* + * 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 2019-2020 Datadog, Inc. + */ + +import DatadogRUM +import DatadogInternal + +public protocol RUMMonitorInternalProtocol { + func addLongTask( + at time: Date, + duration: TimeInterval, + attributes: [AttributeKey: AttributeValue] + ) + + func updatePerformanceMetric( + at time: Date, + metric: PerformanceMetric, + value: Double, + attributes: [AttributeKey: AttributeValue] + ) + + func addResourceMetrics( + at time: Date, + resourceKey: String, + fetch: (start: Date, end: Date), + redirection: (start: Date, end: Date)?, + dns: (start: Date, end: Date)?, + connect: (start: Date, end: Date)?, + ssl: (start: Date, end: Date)?, + firstByte: (start: Date, end: Date)?, + download: (start: Date, end: Date)?, + responseSize: Int64?, + attributes: [AttributeKey: AttributeValue] + ) +} + +extension DatadogInternalInterface: RUMMonitorInternalProtocol {} diff --git a/packages/core/ios/Tests/DdLogsTests.swift b/packages/core/ios/Tests/DdLogsTests.swift index 67c1e3971..35a81ded6 100644 --- a/packages/core/ios/Tests/DdLogsTests.swift +++ b/packages/core/ios/Tests/DdLogsTests.swift @@ -6,6 +6,8 @@ import XCTest @testable import DatadogSDKReactNative +import DatadogLogs +import DatadogInternal func mockResolve(args: Any?) {} func mockReject(args: String?, arg: String?, err: Error?) {} @@ -374,7 +376,34 @@ internal class DdLogsTests: XCTestCase { } } -private class MockNativeLogger: NativeLogger { +private class MockNativeLogger: LoggerProtocol { + init () { + + } + + func log(level: DatadogLogs.LogLevel, message: String, error: Error?, attributes: [String : Encodable]?) { + receivedMethodCalls.append(MethodCall( + kind: MockNativeLogger.MethodCall.Kind(from: level), + message: message, + errorKind: nil, + errorMessage: nil, + stackTrace: nil, + attributes: attributes + )) + } + + func addAttribute(forKey key: DatadogInternal.AttributeKey, value: DatadogInternal.AttributeValue) {} + + func removeAttribute(forKey key: DatadogInternal.AttributeKey) {} + + func addTag(withKey key: String, value: String) {} + + func removeTag(withKey key: String) {} + + func add(tag: String) {} + + func remove(tag: String) {} + struct MethodCall { enum Kind { case debug @@ -389,30 +418,52 @@ private class MockNativeLogger: NativeLogger { let stackTrace: String? let attributes: [String: Encodable]? } + private(set) var receivedMethodCalls = [MethodCall]() func debug(_ message: String, error: Error?, attributes: [String: Encodable]?) { receivedMethodCalls.append(MethodCall(kind: .debug, message: message, errorKind: nil, errorMessage: nil, stackTrace: nil, attributes: attributes)) } - func debug(_ message: String, errorKind: String?, errorMessage: String?, stackTrace: String?, attributes: [String: Encodable]?) { - receivedMethodCalls.append(MethodCall(kind: .debug, message: message, errorKind: errorKind, errorMessage: errorMessage, stackTrace: stackTrace, attributes: attributes)) - } func info(_ message: String, error: Error?, attributes: [String: Encodable]?) { receivedMethodCalls.append(MethodCall(kind: .info, message: message, errorKind: nil, errorMessage: nil, stackTrace: nil, attributes: attributes)) } - func info(_ message: String, errorKind: String?, errorMessage: String?, stackTrace: String?, attributes: [String: Encodable]?) { - receivedMethodCalls.append(MethodCall(kind: .info, message: message, errorKind: errorKind, errorMessage: errorMessage, stackTrace: stackTrace, attributes: attributes)) - } func warn(_ message: String, error: Error?, attributes: [String: Encodable]?) { receivedMethodCalls.append(MethodCall(kind: .warn, message: message, errorKind: nil, errorMessage: nil, stackTrace: nil, attributes: attributes)) } - func warn(_ message: String, errorKind: String?, errorMessage: String?, stackTrace: String?, attributes: [String: Encodable]?) { - receivedMethodCalls.append(MethodCall(kind: .warn, message: message, errorKind: errorKind, errorMessage: errorMessage, stackTrace: stackTrace, attributes: attributes)) - } func error(_ message: String, error: Error?, attributes: [String: Encodable]?) { receivedMethodCalls.append(MethodCall(kind: .error, message: message, errorKind: nil, errorMessage: nil, stackTrace: nil, attributes: attributes)) } - func error(_ message: String, errorKind: String?, errorMessage: String?, stackTrace: String?, attributes: [String: Encodable]?) { - receivedMethodCalls.append(MethodCall(kind: .error, message: message, errorKind: errorKind, errorMessage: errorMessage, stackTrace: stackTrace, attributes: attributes)) +} + +extension MockNativeLogger: InternalLoggerProtocol { + func log(level: DatadogLogs.LogLevel, message: String, errorKind: String?, errorMessage: String?, stackTrace: String?, attributes: [String : Encodable]?) { + receivedMethodCalls.append(MethodCall( + kind: MockNativeLogger.MethodCall.Kind(from: level), + message: message, + errorKind: errorKind, + errorMessage: errorMessage, + stackTrace: stackTrace, + attributes: attributes + )) + } +} + +extension MockNativeLogger.MethodCall.Kind { + init (from level: DatadogLogs.LogLevel) { + switch level { + case .debug: + self = .debug + case .info: + self = .info + case .warn: + self = .warn + case .error: + self = .error + // unsupported cases + case .notice: + self = .debug + case .critical: + self = .debug + } } } diff --git a/packages/core/ios/Tests/DdRumTests.swift b/packages/core/ios/Tests/DdRumTests.swift index d10e41f31..1c3304de3 100644 --- a/packages/core/ios/Tests/DdRumTests.swift +++ b/packages/core/ios/Tests/DdRumTests.swift @@ -5,11 +5,13 @@ */ import XCTest +@testable import DatadogCore +@testable import DatadogRUM @testable import DatadogSDKReactNative -@testable import Datadog +@testable import DatadogInternal internal class DdRumTests: XCTestCase { - private let mockNativeRUM = MockNativeRUM() + private let mockNativeRUM = MockRUMMonitor() private var rum: DdRumImplementation! // swiftlint:disable:this implicitly_unwrapped_optional private func mockResolve(args: Any?) {} @@ -19,17 +21,17 @@ internal class DdRumTests: XCTestCase { override func setUpWithError() throws { try super.setUpWithError() - rum = DdRumImplementation { self.mockNativeRUM } + rum = DdRumImplementation({ self.mockNativeRUM }, { self.mockNativeRUM._internalMock }) } func testItInitializesNativeRumOnlyOnce() { // Given let expectation = self.expectation(description: "Initialize RUM once") - let rum = DdRumImplementation { [unowned self] in + let rum = DdRumImplementation({ [unowned self] in expectation.fulfill() return self.mockNativeRUM - } + }, { nil }) // When (0..<10).forEach { _ in rum.addTiming(name: "foo", resolve: mockResolve, reject: mockReject) } @@ -38,10 +40,12 @@ internal class DdRumTests: XCTestCase { waitForExpectations(timeout: 0.5, handler: nil) } - // TODO: Fix this test by removing ambiguity in names -// func testInternalTimestampKeyValue() { -// XCTAssertEqual(DdRumImplementation.timestampKey, CrossPlatformAttributes.timestampInMilliseconds) -// } + func testInternalTimestampKeyValue() { + let key = "_dd.timestamp" + + XCTAssertEqual(DdRumImplementation.timestampKey, DatadogInternal.CrossPlatformAttributes.timestampInMilliseconds) + XCTAssertEqual(DdRumImplementation.timestampKey, DatadogSDKReactNative.CrossPlatformAttributes.timestampInMilliseconds) + } func testStartView() throws { rum.startView(key: "view key", name: "view name", context: ["foo": 123], timestampMs: randomTimestamp, resolve: mockResolve, reject: mockReject) @@ -189,31 +193,31 @@ internal class DdRumTests: XCTestCase { mockNativeRUM.calledMethods.first, .addResourceMetrics( resourceKey: "resource key", - fetch: MockNativeRUM.Interval( + fetch: MockRUMMonitor.Interval( start: nanoTimeToDate(timestampNs: 0), end: nanoTimeToDate(timestampNs: 13) ), - redirection: MockNativeRUM.Interval( + redirection: MockRUMMonitor.Interval( start: nanoTimeToDate(timestampNs: 1), end: nanoTimeToDate(timestampNs: 2) ), - dns: MockNativeRUM.Interval( + dns: MockRUMMonitor.Interval( start: nanoTimeToDate(timestampNs: 3), end: nanoTimeToDate(timestampNs: 4) ), - connect: MockNativeRUM.Interval( + connect: MockRUMMonitor.Interval( start: nanoTimeToDate(timestampNs: 5), end: nanoTimeToDate(timestampNs: 6) ), - ssl: MockNativeRUM.Interval( + ssl: MockRUMMonitor.Interval( start: nanoTimeToDate(timestampNs: 7), end: nanoTimeToDate(timestampNs: 8) ), - firstByte: MockNativeRUM.Interval( + firstByte: MockRUMMonitor.Interval( start: nanoTimeToDate(timestampNs: 9), end: nanoTimeToDate(timestampNs: 10) ), - download: MockNativeRUM.Interval( + download: MockRUMMonitor.Interval( start: nanoTimeToDate(timestampNs: 11), end: nanoTimeToDate(timestampNs: 12) ), @@ -276,110 +280,3 @@ internal class DdRumTests: XCTestCase { return Date(timeIntervalSince1970: TimeInterval(fromNs: timestampNs)) } } - -private class MockNativeRUM: NativeRUM { - struct Interval: Equatable { - let start: Date? - let end: Date? - } - - enum CalledMethod: Equatable { - case startView(key: String, name: String?) - case stopView(key: String) - case addError(message: String, source: RUMErrorSource, stack: String?) - case startResourceLoading(resourceKey: String, httpMethod: RUMMethod, urlString: String) - case stopResourceLoading(resourceKey: String, statusCode: Int, kind: RUMResourceType, size: Int64?) - case startUserAction(type: RUMUserActionType, name: String) - case stopUserAction(type: RUMUserActionType, name: String?) - case addUserAction(type: RUMUserActionType, name: String) - case addTiming(name: String) - case stopSession(_: Int? = nil) // We need an attribute for the case to be Equatable - case addResourceMetrics(resourceKey: String, - fetch: Interval, - redirection: Interval, - dns: Interval, - connect: Interval, - ssl: Interval, - firstByte: Interval, - download: Interval, - responseSize: Int64?) - } - - private(set) var calledMethods = [CalledMethod]() - private(set) var receivedAttributes = [[String: Encodable]]() - private(set) var receivedFeatureFlags = [String: Encodable]() - - // swiftlint:disable force_cast - func startView(key: String, name: String?, attributes: [String: Encodable]) { - calledMethods.append(.startView(key: key, name: name)) - receivedAttributes.append(attributes) - } - - func stopView(key: String, attributes: [String: Encodable]) { - calledMethods.append(.stopView(key: key)) - receivedAttributes.append(attributes) - } - - func addError(message: String, type: String?, source: RUMErrorSource, stack: String?, attributes: [String: Encodable], file: StaticString?, line: UInt?) { - calledMethods.append(.addError(message: message, source: source, stack: stack)) - receivedAttributes.append(attributes) - } - - func startResourceLoading(resourceKey: String, httpMethod: RUMMethod, urlString: String, attributes: [String: Encodable]) { - calledMethods.append(.startResourceLoading(resourceKey: resourceKey, httpMethod: httpMethod, urlString: urlString)) - receivedAttributes.append(attributes) - } - func stopResourceLoading(resourceKey: String, statusCode: Int?, kind: RUMResourceType, size: Int64?, attributes: [String: Encodable]) { - calledMethods.append(.stopResourceLoading(resourceKey: resourceKey, statusCode: statusCode ?? 0, kind: kind, size: size)) - receivedAttributes.append(attributes) - } - func startUserAction(type: RUMUserActionType, name: String, attributes: [String: Encodable]) { - calledMethods.append(.startUserAction(type: type, name: name)) - receivedAttributes.append(attributes) - } - func stopUserAction(type: RUMUserActionType, name: String?, attributes: [String: Encodable]) { - calledMethods.append(.stopUserAction(type: type, name: name)) - receivedAttributes.append(attributes) - } - func addUserAction(type: RUMUserActionType, name: String, attributes: [String: Encodable]) { - calledMethods.append(.addUserAction(type: type, name: name)) - receivedAttributes.append(attributes) - } - func addTiming(name: String) { - calledMethods.append(.addTiming(name: name)) - } - func stopSession() { - calledMethods.append(.stopSession()) - } - func addFeatureFlagEvaluation(name: String, value: Encodable) { - receivedFeatureFlags[name] = value - } - func addResourceMetrics( - resourceKey: String, - fetch: (start: Date, end: Date), - redirection: (start: Date, end: Date)?, - dns: (start: Date, end: Date)?, - connect: (start: Date, end: Date)?, - ssl: (start: Date, end: Date)?, - firstByte: (start: Date, end: Date)?, - download: (start: Date, end: Date)?, - responseSize: Int64?, - attributes: [AttributeKey: AttributeValue] - ) { - calledMethods.append( - .addResourceMetrics( - resourceKey: resourceKey, - fetch: Interval(start: fetch.start, end: fetch.end), - redirection: Interval(start: redirection?.start, end: redirection?.end), - dns: Interval(start: dns?.start, end: dns?.end), - connect: Interval(start: connect?.start, end: connect?.end), - ssl: Interval(start: ssl?.start, end: ssl?.end), - firstByte: Interval(start: firstByte?.start, end: firstByte?.end), - download: Interval(start: download?.start, end: download?.end), - responseSize: responseSize - ) - ) - receivedAttributes.append(attributes) - } - // swiftlint:enable force_cast -} diff --git a/packages/core/ios/Tests/DdSdkTests.swift b/packages/core/ios/Tests/DdSdkTests.swift index 81ca6c286..557f0c18d 100644 --- a/packages/core/ios/Tests/DdSdkTests.swift +++ b/packages/core/ios/Tests/DdSdkTests.swift @@ -6,7 +6,13 @@ import XCTest @testable import DatadogSDKReactNative -@testable import Datadog +@testable import DatadogCore +@testable import DatadogRUM +@testable import DatadogInternal +@testable import DatadogLogs +@testable import DatadogTrace +@testable import DatadogCrashReporting +import DatadogLogs final class DispatchQueueMock: DispatchQueueType { func async(execute work: @escaping @convention(block) () -> Void) { @@ -32,11 +38,23 @@ internal class DdSdkTests: XCTestCase { var printedMessage = "" consolePrint = { msg in printedMessage += msg } - DdSdkImplementation(mainDispatchQueue: DispatchQueueMock(), jsDispatchQueue: DispatchQueueMock(), jsRefreshRateMonitor: JSRefreshRateMonitor()).initialize(configuration: .mockAny(), resolve: mockResolve, reject: mockReject) + DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: JSRefreshRateMonitor(), + RUMMonitorProvider: { MockRUMMonitor() }, + RUMMonitorInternalProvider: { nil } + ).initialize(configuration: .mockAny(), resolve: mockResolve, reject: mockReject) XCTAssertEqual(printedMessage, "") - DdSdkImplementation(mainDispatchQueue: DispatchQueueMock(), jsDispatchQueue: DispatchQueueMock(), jsRefreshRateMonitor: JSRefreshRateMonitor()).initialize(configuration: .mockAny(), resolve: mockResolve, reject: mockReject) + DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: JSRefreshRateMonitor(), + RUMMonitorProvider: { MockRUMMonitor() }, + RUMMonitorInternalProvider: { nil } + ).initialize(configuration: .mockAny(), resolve: mockResolve, reject: mockReject) XCTAssertEqual(printedMessage, "Datadog SDK is already initialized, skipping initialization.") @@ -46,57 +64,63 @@ internal class DdSdkTests: XCTestCase { func testBuildConfigurationNoUIKitViewsByDefault() { let configuration: DdSdkConfiguration = .mockAny() - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - XCTAssertNil(ddConfig.rumUIKitViewsPredicate) + XCTAssertNil(ddConfig.uiKitViewsPredicate) } func testBuildConfigurationUIKitViewsTrackingDisabled() { let configuration: DdSdkConfiguration = .mockAny(additionalConfig: ["_dd.native_view_tracking": false]) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - XCTAssertNil(ddConfig.rumUIKitViewsPredicate) + XCTAssertNil(ddConfig.uiKitViewsPredicate) } func testBuildConfigurationUIKitViewsTrackingEnabled() { let configuration: DdSdkConfiguration = .mockAny(additionalConfig: ["_dd.native_view_tracking": true]) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - XCTAssertNotNil(ddConfig.rumUIKitViewsPredicate) + XCTAssertNotNil(ddConfig.uiKitViewsPredicate) } func testBuildConfigurationNoUIKitUserActionsByDefault() { let configuration: DdSdkConfiguration = .mockAny() - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - XCTAssertNil(ddConfig.rumUIKitUserActionsPredicate) + XCTAssertNil(ddConfig.uiKitActionsPredicate) } func testBuildConfigurationUIKitUserActionsTrackingDisabled() { let configuration: DdSdkConfiguration = .mockAny(additionalConfig: ["_dd.native_interaction_tracking": false]) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - XCTAssertNil(ddConfig.rumUIKitUserActionsPredicate) + XCTAssertNil(ddConfig.uiKitActionsPredicate) } func testBuildConfigurationUIKitUserActionsTrackingEnabled() { let configuration: DdSdkConfiguration = .mockAny(additionalConfig: ["_dd.native_interaction_tracking": true]) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - XCTAssertNotNil(ddConfig.rumUIKitUserActionsPredicate) + XCTAssertNotNil(ddConfig.uiKitActionsPredicate) } func testSDKInitializationWithVerbosityDebug() { let validConfiguration: NSDictionary = .mockAny(additionalConfig: ["_dd.sdk_verbosity": "debug"]) - DdSdkImplementation(mainDispatchQueue: DispatchQueueMock(), jsDispatchQueue: DispatchQueueMock(), jsRefreshRateMonitor: JSRefreshRateMonitor()).initialize(configuration: validConfiguration, resolve: mockResolve, reject: mockReject) + DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: JSRefreshRateMonitor(), + RUMMonitorProvider: { MockRUMMonitor() }, + RUMMonitorInternalProvider: { nil } + ).initialize(configuration: validConfiguration, resolve: mockResolve, reject: mockReject) - XCTAssertEqual(Datadog.verbosityLevel, LogLevel.debug) + XCTAssertEqual(Datadog.verbosityLevel, CoreLoggerLevel.debug) Datadog.internalFlushAndDeinitialize() } @@ -104,9 +128,15 @@ internal class DdSdkTests: XCTestCase { func testSDKInitializationWithVerbosityInfo() { let validConfiguration: NSDictionary = .mockAny(additionalConfig: ["_dd.sdk_verbosity": "info"]) - DdSdkImplementation(mainDispatchQueue: DispatchQueueMock(), jsDispatchQueue: DispatchQueueMock(), jsRefreshRateMonitor: JSRefreshRateMonitor()).initialize(configuration: validConfiguration, resolve: mockResolve, reject: mockReject) + DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: JSRefreshRateMonitor(), + RUMMonitorProvider: { MockRUMMonitor() }, + RUMMonitorInternalProvider: { nil } + ).initialize(configuration: validConfiguration, resolve: mockResolve, reject: mockReject) - XCTAssertEqual(Datadog.verbosityLevel, LogLevel.info) + XCTAssertEqual(Datadog.verbosityLevel, CoreLoggerLevel.debug) Datadog.internalFlushAndDeinitialize() } @@ -114,9 +144,15 @@ internal class DdSdkTests: XCTestCase { func testSDKInitializationWithVerbosityWarn() { let validConfiguration: NSDictionary = .mockAny(additionalConfig: ["_dd.sdk_verbosity": "warn"]) - DdSdkImplementation(mainDispatchQueue: DispatchQueueMock(), jsDispatchQueue: DispatchQueueMock(), jsRefreshRateMonitor: JSRefreshRateMonitor()).initialize(configuration: validConfiguration, resolve: mockResolve, reject: mockReject) + DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: JSRefreshRateMonitor(), + RUMMonitorProvider: { MockRUMMonitor() }, + RUMMonitorInternalProvider: { nil } + ).initialize(configuration: validConfiguration, resolve: mockResolve, reject: mockReject) - XCTAssertEqual(Datadog.verbosityLevel, LogLevel.warn) + XCTAssertEqual(Datadog.verbosityLevel, CoreLoggerLevel.warn) Datadog.internalFlushAndDeinitialize() } @@ -124,9 +160,15 @@ internal class DdSdkTests: XCTestCase { func testSDKInitializationWithVerbosityError() { let validConfiguration: NSDictionary = .mockAny(additionalConfig: ["_dd.sdk_verbosity": "error"]) - DdSdkImplementation(mainDispatchQueue: DispatchQueueMock(), jsDispatchQueue: DispatchQueueMock(), jsRefreshRateMonitor: JSRefreshRateMonitor()).initialize(configuration: validConfiguration, resolve: mockResolve, reject: mockReject) + DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: JSRefreshRateMonitor(), + RUMMonitorProvider: { MockRUMMonitor() }, + RUMMonitorInternalProvider: { nil } + ).initialize(configuration: validConfiguration, resolve: mockResolve, reject: mockReject) - XCTAssertEqual(Datadog.verbosityLevel, LogLevel.error) + XCTAssertEqual(Datadog.verbosityLevel, CoreLoggerLevel.error) Datadog.internalFlushAndDeinitialize() } @@ -134,7 +176,13 @@ internal class DdSdkTests: XCTestCase { func testSDKInitializationWithVerbosityNil() { let validConfiguration: NSDictionary = .mockAny(additionalConfig: nil) - DdSdkImplementation(mainDispatchQueue: DispatchQueueMock(), jsDispatchQueue: DispatchQueueMock(), jsRefreshRateMonitor: JSRefreshRateMonitor()).initialize(configuration: validConfiguration, resolve: mockResolve, reject: mockReject) + DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: JSRefreshRateMonitor(), + RUMMonitorProvider: { MockRUMMonitor() }, + RUMMonitorInternalProvider: { nil } + ).initialize(configuration: validConfiguration, resolve: mockResolve, reject: mockReject) XCTAssertNil(Datadog.verbosityLevel) @@ -144,97 +192,114 @@ internal class DdSdkTests: XCTestCase { func testSDKInitializationWithVerbosityUnknown() { let validConfiguration: NSDictionary = .mockAny(additionalConfig: ["_dd.sdk_verbosity": "foo"]) - DdSdkImplementation(mainDispatchQueue: DispatchQueueMock(), jsDispatchQueue: DispatchQueueMock(), jsRefreshRateMonitor: JSRefreshRateMonitor()).initialize(configuration: validConfiguration, resolve: mockResolve, reject: mockReject) + DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: JSRefreshRateMonitor(), + RUMMonitorProvider: { MockRUMMonitor() }, + RUMMonitorInternalProvider: { nil } + ).initialize(configuration: validConfiguration, resolve: mockResolve, reject: mockReject) XCTAssertNil(Datadog.verbosityLevel) Datadog.internalFlushAndDeinitialize() } + + func testEnableAllFeatures() { + let core = MockDatadogCore() + let configuration: DdSdkConfiguration = .mockAny() + + DdSdkImplementation().enableFeatures(sdkConfiguration: configuration, core: core) + + XCTAssertNotNil(core.features[RUMFeature.name]) + XCTAssertNotNil(core.features[LogsFeature.name]) + XCTAssertNotNil(core.features[TraceFeature.name]) + } func testBuildConfigurationDefaultEndpoint() { let configuration: DdSdkConfiguration = .mockAny() - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.datadogEndpoint, .us1) + XCTAssertEqual(ddConfig.site, .us1) } func testBuildConfigurationUSEndpoint() { let configuration: DdSdkConfiguration = .mockAny(site: "US") - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.datadogEndpoint, .us1) + XCTAssertEqual(ddConfig.site, .us1) } func testBuildConfigurationUS1Endpoint() { let configuration: DdSdkConfiguration = .mockAny(site: "US1") - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.datadogEndpoint, .us1) + XCTAssertEqual(ddConfig.site, .us1) } func testBuildConfigurationUS3Endpoint() { let configuration: DdSdkConfiguration = .mockAny(site: "US3") - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.datadogEndpoint, .us3) + XCTAssertEqual(ddConfig.site, .us3) } func testBuildConfigurationUS5Endpoint() { let configuration: DdSdkConfiguration = .mockAny(site: "US5") - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.datadogEndpoint, .us5) + XCTAssertEqual(ddConfig.site, .us5) } func testBuildConfigurationUS1FEDEndpoint() { let configuration: DdSdkConfiguration = .mockAny(site: "US1_FED") - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.datadogEndpoint, .us1_fed) + XCTAssertEqual(ddConfig.site, .us1_fed) } func testBuildConfigurationGOVEndpoint() { let configuration: DdSdkConfiguration = .mockAny(site: "GOV") - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.datadogEndpoint, .us1_fed) + XCTAssertEqual(ddConfig.site, .us1_fed) } func testBuildConfigurationEUEndpoint() { let configuration: DdSdkConfiguration = .mockAny(site: "EU") - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.datadogEndpoint, .eu1) + XCTAssertEqual(ddConfig.site, .eu1) } func testBuildConfigurationEU1Endpoint() { let configuration: DdSdkConfiguration = .mockAny(site: "EU1") - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.datadogEndpoint, .eu1) + XCTAssertEqual(ddConfig.site, .eu1) } func testBuildConfigurationAP1Endpoint() { let configuration: DdSdkConfiguration = .mockAny(site: "AP1") - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.datadogEndpoint, .ap1) + XCTAssertEqual(ddConfig.site, .ap1) } func testBuildConfigurationAdditionalConfig() { let configuration: DdSdkConfiguration = .mockAny(additionalConfig: ["foo": "test", "bar": 42]) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration) // swiftlint:disable force_cast XCTAssertEqual(ddConfig.additionalConfiguration["foo"] as! String, "test") @@ -245,49 +310,50 @@ internal class DdSdkTests: XCTestCase { func testBuildConfigurationWithNilServiceNameByDefault() { let configuration: DdSdkConfiguration = .mockAny() - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration) - XCTAssertNil(ddConfig.serviceName) + XCTAssertNil(ddConfig.service) } func testBuildConfigurationWithServiceName() { let configuration: DdSdkConfiguration = .mockAny(additionalConfig: ["_dd.service_name": "com.example.app"]) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.serviceName, "com.example.app") + XCTAssertEqual(ddConfig.service, "com.example.app") } func testBuildConfigurationNoCrashReportByDefault() { + let core = MockDatadogCore() let configuration: DdSdkConfiguration = .mockAny(nativeCrashReportEnabled: nil) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) - - XCTAssertNil(ddConfig.crashReportingPlugin) + DdSdkImplementation().enableFeatures(sdkConfiguration: configuration, core: core) + + XCTAssertNil(core.features[CrashReportingFeature.name]) } func testBuildConfigurationNoCrashReport() { + let core = MockDatadogCore() let configuration: DdSdkConfiguration = .mockAny(nativeCrashReportEnabled: false) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) - - XCTAssertNil(ddConfig.crashReportingPlugin) + DdSdkImplementation().enableFeatures(sdkConfiguration: configuration, core: core) + + XCTAssertNil(core.features[CrashReportingFeature.name]) } func testBuildConfigurationWithCrashReport() { - let configuration: DdSdkConfiguration = .mockAny( - nativeCrashReportEnabled: true - ) + let core = MockDatadogCore() + let configuration: DdSdkConfiguration = .mockAny(nativeCrashReportEnabled: true) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) - - XCTAssertNotNil(ddConfig.crashReportingPlugin) + DdSdkImplementation().enableFeatures(sdkConfiguration: configuration, core: core) + + XCTAssertNotNil(core.features[CrashReportingFeature.name]) } func testBuildConfigurationWithVersionSuffix() { let configuration: DdSdkConfiguration = .mockAny(additionalConfig: ["_dd.version_suffix": ":codepush-3"]) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration, defaultAppVersion: "1.2.3") + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration, defaultAppVersion: "1.2.3") XCTAssertEqual(ddConfig.additionalConfiguration["_dd.version"] as! String, "1.2.3:codepush-3") } @@ -295,29 +361,35 @@ internal class DdSdkTests: XCTestCase { func testBuildConfigurationFrustrationTrackingEnabledByDefault() { let configuration: DdSdkConfiguration = .mockAny() - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.rumFrustrationSignalsTrackingEnabled, true) + XCTAssertEqual(ddConfig.trackFrustrations, true) } func testBuildConfigurationFrustrationTrackingEnabledExplicitly() { let configuration: DdSdkConfiguration = .mockAny(trackFrustrations: true) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.rumFrustrationSignalsTrackingEnabled, true) + XCTAssertEqual(ddConfig.trackFrustrations, true) } func testBuildConfigurationFrustrationTrackingDisabled() { let configuration: DdSdkConfiguration = .mockAny(trackFrustrations: false) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.rumFrustrationSignalsTrackingEnabled, false) + XCTAssertEqual(ddConfig.trackFrustrations, false) } func testSettingUserInfo() throws { - let bridge = DdSdkImplementation(mainDispatchQueue: DispatchQueueMock(), jsDispatchQueue: DispatchQueueMock(), jsRefreshRateMonitor: JSRefreshRateMonitor()) + let bridge = DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: JSRefreshRateMonitor(), + RUMMonitorProvider: { MockRUMMonitor() }, + RUMMonitorInternalProvider: { nil } + ) bridge.initialize(configuration: .mockAny(), resolve: mockResolve, reject: mockReject) bridge.setUser( @@ -335,23 +407,29 @@ internal class DdSdkTests: XCTestCase { reject: mockReject ) - let receivedUserInfo = try XCTUnwrap(defaultDatadogCore as? DatadogCore).userInfoProvider.value - XCTAssertEqual(receivedUserInfo.id, "abc-123") - XCTAssertEqual(receivedUserInfo.name, "John Doe") - XCTAssertEqual(receivedUserInfo.email, "john@doe.com") - XCTAssertEqual(receivedUserInfo.extraInfo["extra-info-1"] as? Int64, 123) - XCTAssertEqual(receivedUserInfo.extraInfo["extra-info-2"] as? String, "abc") - XCTAssertEqual(receivedUserInfo.extraInfo["extra-info-3"] as? Bool, true) + let ddContext = try XCTUnwrap(CoreRegistry.default as? DatadogCore).contextProvider.read() + let userInfo = try XCTUnwrap(ddContext.userInfo) + + XCTAssertEqual(userInfo.id, "abc-123") + XCTAssertEqual(userInfo.name, "John Doe") + XCTAssertEqual(userInfo.email, "john@doe.com") + XCTAssertEqual(userInfo.extraInfo["extra-info-1"] as? Int64, 123) + XCTAssertEqual(userInfo.extraInfo["extra-info-2"] as? String, "abc") + XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, true) Datadog.internalFlushAndDeinitialize() } func testSettingAttributes() { - let bridge = DdSdkImplementation(mainDispatchQueue: DispatchQueueMock(), jsDispatchQueue: DispatchQueueMock(), jsRefreshRateMonitor: JSRefreshRateMonitor()) - bridge.initialize(configuration: .mockAny(), resolve: mockResolve, reject: mockReject) - let rumMonitorMock = MockRUMMonitor() - Global.rum = rumMonitorMock + let bridge = DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: JSRefreshRateMonitor(), + RUMMonitorProvider: { rumMonitorMock }, + RUMMonitorInternalProvider: { nil } + ) + bridge.initialize(configuration: .mockAny(), resolve: mockResolve, reject: mockReject) bridge.setAttributes( attributes: NSDictionary( @@ -365,9 +443,9 @@ internal class DdSdkTests: XCTestCase { reject: mockReject ) - XCTAssertEqual(rumMonitorMock.receivedAttributes["attribute-1"] as? Int64, 123) - XCTAssertEqual(rumMonitorMock.receivedAttributes["attribute-2"] as? String, "abc") - XCTAssertEqual(rumMonitorMock.receivedAttributes["attribute-3"] as? Bool, true) + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-1"] as? Int64, 123) + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-2"] as? String, "abc") + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-3"] as? Bool, true) XCTAssertEqual(GlobalState.globalAttributes["attribute-1"] as? Int64, 123) XCTAssertEqual(GlobalState.globalAttributes["attribute-2"] as? String, "abc") @@ -408,17 +486,17 @@ internal class DdSdkTests: XCTestCase { func testBuildLongTaskThreshold() { let configuration: DdSdkConfiguration = .mockAny(nativeLongTaskThresholdMs: 2_500) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.rumLongTaskDurationThreshold, 2.5) + XCTAssertEqual(ddConfig.longTaskThreshold, 2.5) } func testBuildNoLongTaskTracking() { let configuration: DdSdkConfiguration = .mockAny(nativeLongTaskThresholdMs: 0) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.rumLongTaskDurationThreshold, nil) + XCTAssertEqual(ddConfig.longTaskThreshold, nil) } func testBuildFirstPartyHosts() { @@ -427,12 +505,18 @@ internal class DdSdkTests: XCTestCase { ["match": "datadog.com", "propagatorTypes": ["b3multi", "tracecontext"]] ]]) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) - - var firstPartyHosts: FirstPartyHosts? = FirstPartyHosts(["example.com": [.datadog, .b3]]) - firstPartyHosts += FirstPartyHosts(["datadog.com": [.b3multi, .tracecontext]]) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) + + let expectedFirstPartyHosts: [String: Set]? = ["example.com": [.datadog, .b3], "datadog.com": [.b3multi, .tracecontext]] + var actualFirstPartyHosts: [String: Set]? + switch ddConfig.urlSessionTracking?.firstPartyHostsTracing { + case .trace(_,_): break + case let .traceWithHeaders(hostsWithHeaders, _): + return actualFirstPartyHosts = hostsWithHeaders + case .none: break + } - XCTAssertEqual(ddConfig.firstPartyHosts, firstPartyHosts) + XCTAssertEqual(actualFirstPartyHosts, expectedFirstPartyHosts) } func testBuildMalformedFirstPartyHosts() { @@ -440,11 +524,18 @@ internal class DdSdkTests: XCTestCase { ["match": "example.com", "propagatorTypes": ["badPropagatorType", "b3"]], ]]) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - let firstPartyHosts: FirstPartyHosts? = FirstPartyHosts(["example.com": [.b3]]) + let expectedFirstPartyHosts: [String: Set]? = ["example.com": [.b3]] + var actualFirstPartyHosts: [String: Set]? + switch ddConfig.urlSessionTracking?.firstPartyHostsTracing { + case .trace(_,_): break + case let .traceWithHeaders(hostsWithHeaders, _): + return actualFirstPartyHosts = hostsWithHeaders + case .none: break + } - XCTAssertEqual(ddConfig.firstPartyHosts, firstPartyHosts) + XCTAssertEqual(actualFirstPartyHosts, expectedFirstPartyHosts) } func testBuildFirstPartyHostsWithDuplicatedMatchKey() { @@ -453,19 +544,26 @@ internal class DdSdkTests: XCTestCase { ["match": "example.com", "propagatorTypes": ["tracecontext"]], ]]) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - var firstPartyHosts: FirstPartyHosts? = FirstPartyHosts(["example.com": [.b3, .tracecontext]]) + let expectedFirstPartyHosts: [String: Set]? = ["example.com": [.b3, .tracecontext]] + var actualFirstPartyHosts: [String: Set]? + switch ddConfig.urlSessionTracking?.firstPartyHostsTracing { + case .trace(_,_): break + case let .traceWithHeaders(hostsWithHeaders, _): + return actualFirstPartyHosts = hostsWithHeaders + case .none: break + } - XCTAssertEqual(ddConfig.firstPartyHosts, firstPartyHosts) + XCTAssertEqual(actualFirstPartyHosts, expectedFirstPartyHosts) } func testBuildTelemetrySampleRate() { let configuration: DdSdkConfiguration = .mockAny(telemetrySampleRate: 42.0) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.rumTelemetrySamplingRate, 42.0) + XCTAssertEqual(ddConfig.telemetrySampleRate, 42.0) } func testBuildProxyConfiguration() { @@ -521,7 +619,7 @@ internal class DdSdkTests: XCTestCase { ] ) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration) XCTAssertEqual(ddConfig.proxyConfiguration?["HTTPProxy"] as? String, "host") XCTAssertEqual(ddConfig.proxyConfiguration?["HTTPPort"] as? NSNumber, 99) @@ -532,23 +630,23 @@ internal class DdSdkTests: XCTestCase { func testBuildConfigurationAverageVitalsUpdateFrequency() { let configuration: DdSdkConfiguration = .mockAny(vitalsUpdateFrequency: "average") - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.mobileVitalsFrequency, .average) + XCTAssertEqual(ddConfig.vitalsUpdateFrequency, .average) } func testBuildConfigurationNeverVitalsUpdateFrequency() { let configuration: DdSdkConfiguration = .mockAny(vitalsUpdateFrequency: "never") - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.mobileVitalsFrequency, .never) + XCTAssertEqual(ddConfig.vitalsUpdateFrequency, nil) } func testBuildConfigurationAverageUploadFrequency() { let configuration: DdSdkConfiguration = .mockAny(uploadFrequency: "AVERAGE") - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration) XCTAssertEqual(ddConfig.uploadFrequency, .average) } @@ -556,7 +654,7 @@ internal class DdSdkTests: XCTestCase { func testBuildConfigurationFrequentUploadFrequency() { let configuration: DdSdkConfiguration = .mockAny(uploadFrequency: "FREQUENT") - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration) XCTAssertEqual(ddConfig.uploadFrequency, .frequent) } @@ -564,7 +662,7 @@ internal class DdSdkTests: XCTestCase { func testBuildConfigurationRareUploadFrequency() { let configuration: DdSdkConfiguration = .mockAny(uploadFrequency: "RARE") - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration) XCTAssertEqual(ddConfig.uploadFrequency, .rare) } @@ -572,7 +670,7 @@ internal class DdSdkTests: XCTestCase { func testBuildConfigurationMediumBatchSize() { let configuration: DdSdkConfiguration = .mockAny(batchSize: "MEDIUM") - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration) XCTAssertEqual(ddConfig.batchSize, .medium) } @@ -580,7 +678,7 @@ internal class DdSdkTests: XCTestCase { func testBuildConfigurationLargeBatchSize() { let configuration: DdSdkConfiguration = .mockAny(batchSize: "LARGE") - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration) XCTAssertEqual(ddConfig.batchSize, .large) } @@ -588,7 +686,7 @@ internal class DdSdkTests: XCTestCase { func testBuildConfigurationSmallBatchSize() { let configuration: DdSdkConfiguration = .mockAny(batchSize: "SMALL") - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildSDKConfiguration(configuration: configuration) XCTAssertEqual(ddConfig.batchSize, .small) } @@ -597,8 +695,13 @@ internal class DdSdkTests: XCTestCase { let mockRefreshRateMonitor = MockJSRefreshRateMonitor() let rumMonitorMock = MockRUMMonitor() - DdSdkImplementation(mainDispatchQueue: DispatchQueueMock(), jsDispatchQueue: DispatchQueueMock(), jsRefreshRateMonitor: mockRefreshRateMonitor).initialize(configuration: .mockAny(longTaskThresholdMs: 0.0), resolve: mockResolve, reject: mockReject) - Global.rum = rumMonitorMock + DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: mockRefreshRateMonitor, + RUMMonitorProvider: { rumMonitorMock }, + RUMMonitorInternalProvider: { rumMonitorMock._internalMock } + ).initialize(configuration: .mockAny(longTaskThresholdMs: 0.0), resolve: mockResolve, reject: mockReject) XCTAssertTrue(mockRefreshRateMonitor.isStarted) @@ -613,8 +716,13 @@ internal class DdSdkTests: XCTestCase { let mockRefreshRateMonitor = MockJSRefreshRateMonitor() let rumMonitorMock = MockRUMMonitor() - DdSdkImplementation(mainDispatchQueue: DispatchQueueMock(), jsDispatchQueue: DispatchQueueMock(), jsRefreshRateMonitor: mockRefreshRateMonitor).initialize(configuration: .mockAny(longTaskThresholdMs: 0.0, vitalsUpdateFrequency: "never"), resolve: mockResolve, reject: mockReject) - Global.rum = rumMonitorMock + DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: mockRefreshRateMonitor, + RUMMonitorProvider: { rumMonitorMock }, + RUMMonitorInternalProvider: { rumMonitorMock._internalMock } + ).initialize(configuration: .mockAny(longTaskThresholdMs: 0.0, vitalsUpdateFrequency: "never"), resolve: mockResolve, reject: mockReject) XCTAssertFalse(mockRefreshRateMonitor.isStarted) @@ -629,8 +737,13 @@ internal class DdSdkTests: XCTestCase { let mockRefreshRateMonitor = MockJSRefreshRateMonitor() let rumMonitorMock = MockRUMMonitor() - DdSdkImplementation(mainDispatchQueue: DispatchQueueMock(), jsDispatchQueue: DispatchQueueMock(), jsRefreshRateMonitor: mockRefreshRateMonitor).initialize(configuration: .mockAny(longTaskThresholdMs: 0.2, vitalsUpdateFrequency: "never"), resolve: mockResolve, reject: mockReject) - Global.rum = rumMonitorMock + DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: mockRefreshRateMonitor, + RUMMonitorProvider: { rumMonitorMock }, + RUMMonitorInternalProvider: { rumMonitorMock._internalMock } + ).initialize(configuration: .mockAny(longTaskThresholdMs: 0.2, vitalsUpdateFrequency: "never"), resolve: mockResolve, reject: mockReject) XCTAssertTrue(mockRefreshRateMonitor.isStarted) @@ -646,8 +759,13 @@ internal class DdSdkTests: XCTestCase { let mockRefreshRateMonitor = MockJSRefreshRateMonitor() let rumMonitorMock = MockRUMMonitor() - DdSdkImplementation(mainDispatchQueue: DispatchQueueMock(), jsDispatchQueue: DispatchQueueMock(), jsRefreshRateMonitor: mockRefreshRateMonitor).initialize(configuration: .mockAny(longTaskThresholdMs: 200, vitalsUpdateFrequency: "average"), resolve: mockResolve, reject: mockReject) - Global.rum = rumMonitorMock + DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: mockRefreshRateMonitor, + RUMMonitorProvider: { rumMonitorMock }, + RUMMonitorInternalProvider: { rumMonitorMock._internalMock } + ).initialize(configuration: .mockAny(longTaskThresholdMs: 200, vitalsUpdateFrequency: "average"), resolve: mockResolve, reject: mockReject) XCTAssertTrue(mockRefreshRateMonitor.isStarted) @@ -665,108 +783,55 @@ internal class DdSdkTests: XCTestCase { func testBackgroundTrackingEnabled() { let configuration: DdSdkConfiguration = .mockAny(trackBackgroundEvents: true) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.rumBackgroundEventTrackingEnabled, true) + XCTAssertEqual(ddConfig.trackBackgroundEvents, true) } func testBackgroundTrackingDisabled() { let configuration: DdSdkConfiguration = .mockAny(trackBackgroundEvents: false) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.rumBackgroundEventTrackingEnabled, false) + XCTAssertEqual(ddConfig.trackBackgroundEvents, false) } func testBackgroundTrackingUndefined() { let configuration: DdSdkConfiguration = .mockAny(trackBackgroundEvents: nil) - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - XCTAssertEqual(ddConfig.rumBackgroundEventTrackingEnabled, false) + XCTAssertEqual(ddConfig.trackBackgroundEvents, false) } - func testConfigurationTelemetryEventMapper() throws { - DdSdkImplementation( - mainDispatchQueue: DispatchQueueMock(), - jsDispatchQueue: DispatchQueueMock(), - jsRefreshRateMonitor: JSRefreshRateMonitor()) - .initialize( - configuration: .mockAny( - nativeCrashReportEnabled: false, - nativeLongTaskThresholdMs: 0.0, - longTaskThresholdMs: 0.1, - configurationForTelemetry: ["initializationType": "LEGACY", "trackErrors": true, "trackInteractions": true, "trackNetworkRequests": true, "reactVersion": "18.2.0", "reactNativeVersion": "0.71.0"] - ), - resolve: mockResolve, - reject: mockReject - ) - - - guard let configurationEventMapper = try XCTUnwrap(DD.telemetry as? RUMTelemetry).configurationEventMapper else { return } - - let mappedEvent = configurationEventMapper( - TelemetryConfigurationEvent( - dd: TelemetryConfigurationEvent.DD(), - action: nil, - application: nil, - date: Int64(), - experimentalFeatures: nil, - service: "mockService", - session: nil, - source: .reactNative, - telemetry: TelemetryConfigurationEvent.Telemetry( - configuration: TelemetryConfigurationEvent.Telemetry.Configuration( - actionNameAttribute: nil, - batchSize: nil, - batchUploadFrequency: nil, - forwardConsoleLogs: nil, - forwardErrorsToLogs: nil, - forwardReports: nil, - premiumSampleRate: nil, - replaySampleRate: nil, - selectedTracingPropagators: nil, - sessionSampleRate: nil, - silentMultipleInit: nil, - telemetryConfigurationSampleRate: nil, - telemetrySampleRate: nil, - traceSampleRate: nil, - trackSessionAcrossSubdomains: nil, - useAllowedTracingOrigins: nil, - useAllowedTracingUrls: nil, - useBeforeSend: nil, - useCrossSiteSessionCookie: nil, - useExcludedActivityUrls: nil, - useLocalEncryption: nil, - useSecureSessionCookie: nil, - useTracing: nil, - viewTrackingStrategy: nil - ) - ), - version: "1.0.0", - view: nil - ) + func testConfigurationTelemetryOverride() throws { + let core = MockDatadogCore() + let configuration: DdSdkConfiguration = .mockAny( + nativeCrashReportEnabled: false, + nativeLongTaskThresholdMs: 0.0, + longTaskThresholdMs: 0.1, + configurationForTelemetry: ["initializationType": "LEGACY", "trackErrors": true, "trackInteractions": true, "trackNetworkRequests": true, "reactVersion": "18.2.0", "reactNativeVersion": "0.71.0"] ) - XCTAssertEqual(mappedEvent.telemetry.configuration.initializationType, "LEGACY") - XCTAssertEqual(mappedEvent.telemetry.configuration.trackErrors, true) - XCTAssertEqual(mappedEvent.telemetry.configuration.trackInteractions, true) - XCTAssertEqual(mappedEvent.telemetry.configuration.trackNetworkRequests, true) - XCTAssertEqual(mappedEvent.telemetry.configuration.trackNativeErrors, false) - XCTAssertEqual(mappedEvent.telemetry.configuration.trackNativeLongTasks, false) - XCTAssertEqual(mappedEvent.telemetry.configuration.trackLongTask, true) - XCTAssertEqual(mappedEvent.telemetry.configuration.reactVersion, "18.2.0") - XCTAssertEqual(mappedEvent.telemetry.configuration.reactNativeVersion, "0.71.0") + DdSdkImplementation().overrideReactNativeTelemetry(rnConfiguration: configuration, core: core) - Datadog.internalFlushAndDeinitialize() + XCTAssertEqual(core.configuration?.initializationType, "LEGACY") + XCTAssertEqual(core.configuration?.trackErrors, true) + XCTAssertEqual(core.configuration?.trackInteractions, true) + XCTAssertEqual(core.configuration?.trackNetworkRequests, true) + XCTAssertEqual(core.configuration?.trackNativeErrors, false) + XCTAssertEqual(core.configuration?.trackNativeLongTasks, false) + XCTAssertEqual(core.configuration?.trackLongTask, true) + XCTAssertEqual(core.configuration?.reactVersion, "18.2.0") + XCTAssertEqual(core.configuration?.reactNativeVersion, "0.71.0") } func testDropsResourceMarkedAsDropped() throws { let configuration: DdSdkConfiguration = .mockAny() - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - let resourceEventMapper = try XCTUnwrap(ddConfig.rumResourceEventMapper) + let resourceEventMapper = try XCTUnwrap(ddConfig.resourceEventMapper) let mockDroppedResourceEvent = RUMResourceEvent.mockRandomDropped() let mappedDroppedEvent = resourceEventMapper(mockDroppedResourceEvent) @@ -780,9 +845,9 @@ internal class DdSdkTests: XCTestCase { func testDropsActionMarkedAsDropped() throws { let configuration: DdSdkConfiguration = .mockAny() - let ddConfig = DdSdkImplementation().buildConfiguration(configuration: configuration) + let ddConfig = DdSdkImplementation().buildRUMConfiguration(configuration: configuration) - let actionEventMapper = try XCTUnwrap(ddConfig.rumActionEventMapper) + let actionEventMapper = try XCTUnwrap(ddConfig.actionEventMapper) let mockDroppedActionEvent = RUMActionEvent.mockRandomDropped() let mappedDroppedEvent = actionEventMapper(mockDroppedActionEvent) @@ -797,34 +862,36 @@ internal class DdSdkTests: XCTestCase { let bridge = DispatchQueueMock() let mockJSRefreshRateMonitor = MockJSRefreshRateMonitor() - DdSdkImplementation(mainDispatchQueue: DispatchQueueMock(), jsDispatchQueue: bridge, jsRefreshRateMonitor: mockJSRefreshRateMonitor).initialize(configuration: .mockAny(longTaskThresholdMs: 0.2), resolve: mockResolve, reject: mockReject) + DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: bridge, + jsRefreshRateMonitor: mockJSRefreshRateMonitor, + RUMMonitorProvider: { MockRUMMonitor() }, + RUMMonitorInternalProvider: { nil } + ).initialize(configuration: .mockAny(longTaskThresholdMs: 0.2), resolve: mockResolve, reject: mockReject) XCTAssertTrue(bridge.isSameQueue(queue: mockJSRefreshRateMonitor.jsQueue!)) Datadog.internalFlushAndDeinitialize() } -} - -private class MockRUMMonitor: DDRUMMonitor, RUMCommandSubscriber { - private(set) var receivedAttributes = [AttributeKey: AttributeValue]() - private(set) var lastReceivedPerformanceMetrics = [PerformanceMetric: Double]() - private(set) var receivedLongTasks = [Date: TimeInterval]() - override func addAttribute(forKey key: AttributeKey, value: AttributeValue) { - receivedAttributes[key] = value + func testConsumeWebviewEventBeforeInitialization() throws { + XCTAssertNoThrow(try DdSdkImplementation().consumeWebviewEvent(message: "TestMessage", resolve: mockResolve, reject: mockReject)) } - func process(command: RUMCommand) { - if (command is RUMAddLongTaskCommand) { - receivedLongTasks[(command as! RUMAddLongTaskCommand).time] = (command as! RUMAddLongTaskCommand).duration - } - if (command is RUMUpdatePerformanceMetric) { - lastReceivedPerformanceMetrics[.jsFrameTimeSeconds] = (command as! RUMUpdatePerformanceMetric).value - } + func testConsumeWebviewEvent() throws { + let sdk = DdSdkImplementation() + let configuration: DdSdkConfiguration = .mockAny() + let core = MockDatadogCore() + + sdk.enableFeatures(sdkConfiguration: configuration, core: core) + + sdk.consumeWebviewEvent(message: "{\"eventType\":\"RUM\",\"event\":{\"blabla\":\"custom message\"}}", resolve: mockResolve, reject: mockReject) + + XCTAssertNotNil(core.baggages["browser-rum-event"]) } } - private final class MockJSRefreshRateMonitor: RefreshRateMonitor { private var refreshRateListener: RefreshRateListener? private var frameTimeCallback: frame_time_callback? @@ -930,7 +997,46 @@ extension NSDictionary { extension DdSdkImplementation { internal override convenience init() { - self.init(mainDispatchQueue: DispatchQueue.main, jsDispatchQueue: DispatchQueue.main, jsRefreshRateMonitor: JSRefreshRateMonitor.init()) + self.init( + mainDispatchQueue: DispatchQueue.main, + jsDispatchQueue: DispatchQueue.main, + jsRefreshRateMonitor: JSRefreshRateMonitor.init(), + RUMMonitorProvider: { MockRUMMonitor() }, + RUMMonitorInternalProvider: { nil } + ) } +} + +internal class MockDatadogCore: DatadogCoreProtocol { + func send(message: FeatureMessage, else fallback: @escaping () -> Void) { + if // Configuration Telemetry Message + case .telemetry(let telemetry) = message, + case .configuration(let configuration) = telemetry { + self.configuration = configuration + } + + if case .baggage(let key, let baggage) = message { + self.baggages[key] = baggage + } + } + + private(set) var configuration: ConfigurationTelemetry? + private(set) var features: [String: DatadogFeature] = [:] + private(set) var baggages: [String: Any] = [:] + + func register(feature: T) throws where T : DatadogFeature { + features[T.name] = feature + } + + func get(feature type: T.Type) -> T? where T : DatadogFeature { + return nil + } + + func scope(for feature: String) -> FeatureScope? { + return nil + } + + func set(feature: String, attributes: @escaping () -> FeatureBaggage) {} + func update(feature: String, attributes: @escaping () -> FeatureBaggage) {} } diff --git a/packages/core/ios/Tests/DdTraceTests.swift b/packages/core/ios/Tests/DdTraceTests.swift index 0f559cd34..6c86a6e92 100644 --- a/packages/core/ios/Tests/DdTraceTests.swift +++ b/packages/core/ios/Tests/DdTraceTests.swift @@ -6,7 +6,8 @@ import XCTest @testable import DatadogSDKReactNative -@testable import Datadog +@testable import DatadogCore +@testable import DatadogTrace internal class DdTraceTests: XCTestCase { diff --git a/packages/core/ios/Tests/MockRUMMonitor.swift b/packages/core/ios/Tests/MockRUMMonitor.swift new file mode 100644 index 000000000..df8b27ac1 --- /dev/null +++ b/packages/core/ios/Tests/MockRUMMonitor.swift @@ -0,0 +1,159 @@ +/* + * 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 2019-2020 Datadog, Inc. + */ + +@testable import DatadogCore +@testable import DatadogRUM +@testable import DatadogInternal +@testable import DatadogSDKReactNative + +internal class MockRUMMonitor: RUMMonitorProtocol { + init () { + self.debug = false + } + + func addAttribute(forKey key: DatadogInternal.AttributeKey, value: DatadogInternal.AttributeValue) { + addedAttributes[key] = value + } + + func removeAttribute(forKey key: DatadogInternal.AttributeKey) {} + + var debug: Bool + + struct Interval: Equatable { + let start: Date? + let end: Date? + } + + enum CalledMethod: Equatable { + case startView(key: String, name: String?) + case stopView(key: String) + case addError(message: String, source: RUMErrorSource, stack: String?) + case startResourceLoading(resourceKey: String, httpMethod: RUMMethod, urlString: String) + case stopResourceLoading(resourceKey: String, statusCode: Int, kind: RUMResourceType, size: Int64?) + case startUserAction(type: RUMActionType, name: String) + case stopUserAction(type: RUMActionType, name: String?) + case addUserAction(type: RUMActionType, name: String) + case addTiming(name: String) + case stopSession(_: Int? = nil) // We need an attribute for the case to be Equatable + case addResourceMetrics(resourceKey: String, + fetch: Interval, + redirection: Interval, + dns: Interval, + connect: Interval, + ssl: Interval, + firstByte: Interval, + download: Interval, + responseSize: Int64?) + case addLongTasks(time: Date, duration: TimeInterval) + case updatePerformanceMetric(time: Date, metric: DatadogRUM.PerformanceMetric, value: Double) + + } + + public var calledMethods = [CalledMethod]() + public var receivedAttributes = [[AttributeKey: AttributeValue]]() + private(set) var addedAttributes = [AttributeKey: AttributeValue]() + private(set) var receivedFeatureFlags = [String: Encodable]() + public var lastReceivedPerformanceMetrics = [PerformanceMetric: Double]() + public var receivedLongTasks = [Date: TimeInterval]() + + func startView(key: String, name: String?, attributes: [AttributeKey: AttributeValue]) { + calledMethods.append(.startView(key: key, name: name)) + receivedAttributes.append(attributes) + } + + func stopView(key: String, attributes: [AttributeKey: AttributeValue]) { + calledMethods.append(.stopView(key: key)) + receivedAttributes.append(attributes) + } + + func addError(message: String, type: String?, stack: String?, source: RUMErrorSource, attributes: [String: Encodable], file: StaticString?, line: UInt?) { + calledMethods.append(.addError(message: message, source: source, stack: stack)) + receivedAttributes.append(attributes) + } + + func startResource(resourceKey: String, httpMethod: RUMMethod, urlString: String, attributes: [String: Encodable]) { + calledMethods.append(.startResourceLoading(resourceKey: resourceKey, httpMethod: httpMethod, urlString: urlString)) + receivedAttributes.append(attributes) + } + func stopResource(resourceKey: String, statusCode: Int?, kind: RUMResourceType, size: Int64?, attributes: [String: Encodable]) { + calledMethods.append(.stopResourceLoading(resourceKey: resourceKey, statusCode: statusCode ?? 0, kind: kind, size: size)) + receivedAttributes.append(attributes) + } + func startAction(type: RUMActionType, name: String, attributes: [String: Encodable]) { + calledMethods.append(.startUserAction(type: type, name: name)) + receivedAttributes.append(attributes) + } + func stopAction(type: RUMActionType, name: String?, attributes: [String: Encodable]) { + calledMethods.append(.stopUserAction(type: type, name: name)) + receivedAttributes.append(attributes) + } + func addAction(type: RUMActionType, name: String, attributes: [String: Encodable]) { + calledMethods.append(.addUserAction(type: type, name: name)) + receivedAttributes.append(attributes) + } + func addTiming(name: String) { + calledMethods.append(.addTiming(name: name)) + } + func stopSession() { + calledMethods.append(.stopSession()) + } + func addFeatureFlagEvaluation(name: String, value: Encodable) { + receivedFeatureFlags[name] = value + } + + var _internalMock: MockRUMMonitorInternal { + MockRUMMonitorInternal(monitor: self) + } +} + +public struct MockRUMMonitorInternal: RUMMonitorInternalProtocol { + let monitor: MockRUMMonitor + + public func addLongTask(at time: Date, duration: TimeInterval, attributes: [AttributeKey : AttributeValue]) { + monitor.calledMethods.append( + .addLongTasks(time: time, duration: duration) + ) + monitor.receivedAttributes.append(attributes) + monitor.receivedLongTasks[time] = duration + } + + public func updatePerformanceMetric(at time: Date, metric: DatadogRUM.PerformanceMetric, value: Double, attributes: [AttributeKey : AttributeValue]) { + monitor.calledMethods.append( + .updatePerformanceMetric(time: time, metric: metric, value: value) + ) + monitor.receivedAttributes.append(attributes) + monitor.lastReceivedPerformanceMetrics[metric] = value + } + + public func addResourceMetrics( + at time: Date, + resourceKey: String, + fetch: (start: Date, end: Date), + redirection: (start: Date, end: Date)?, + dns: (start: Date, end: Date)?, + connect: (start: Date, end: Date)?, + ssl: (start: Date, end: Date)?, + firstByte: (start: Date, end: Date)?, + download: (start: Date, end: Date)?, + responseSize: Int64?, + attributes: [AttributeKey: AttributeValue] + ) { + monitor.calledMethods.append( + .addResourceMetrics( + resourceKey: resourceKey, + fetch: MockRUMMonitor.Interval(start: fetch.start, end: fetch.end), + redirection: MockRUMMonitor.Interval(start: redirection?.start, end: redirection?.end), + dns: MockRUMMonitor.Interval(start: dns?.start, end: dns?.end), + connect: MockRUMMonitor.Interval(start: connect?.start, end: connect?.end), + ssl: MockRUMMonitor.Interval(start: ssl?.start, end: ssl?.end), + firstByte: MockRUMMonitor.Interval(start: firstByte?.start, end: firstByte?.end), + download: MockRUMMonitor.Interval(start: download?.start, end: download?.end), + responseSize: responseSize + ) + ) + monitor.receivedAttributes.append(attributes) + } +} diff --git a/packages/core/ios/Tests/RUMMocks.swift b/packages/core/ios/Tests/RUMMocks.swift index 0d4c28b31..7c95b4834 100644 --- a/packages/core/ios/Tests/RUMMocks.swift +++ b/packages/core/ios/Tests/RUMMocks.swift @@ -4,7 +4,7 @@ * Copyright 2019-Present Datadog, Inc. */ -@testable import Datadog +@testable import DatadogRUM // MARK: - Foundation Mocks protocol RandomMockable { @@ -257,6 +257,7 @@ extension RUMResourceEvent: RandomMockable { return RUMResourceEvent( dd: .init( browserSdkVersion: nil, + configuration: nil, discarded: nil, rulePsr: nil, session: .init(plan: .plan1), @@ -314,6 +315,7 @@ extension RUMResourceEvent: RandomMockable { return RUMResourceEvent( dd: .init( browserSdkVersion: nil, + configuration: nil, discarded: nil, rulePsr: nil, session: .init(plan: .plan1), @@ -381,6 +383,7 @@ extension RUMActionEvent: RandomMockable { ) ), browserSdkVersion: nil, + configuration: nil, session: .init(plan: .plan1) ), action: .init( @@ -433,6 +436,7 @@ extension RUMActionEvent: RandomMockable { ) ), browserSdkVersion: nil, + configuration: nil, session: .init(plan: .plan1) ), action: .init(