diff --git a/dd-sdk-android/apiSurface b/dd-sdk-android/apiSurface index 831170a775..2326a44a47 100644 --- a/dd-sdk-android/apiSurface +++ b/dd-sdk-android/apiSurface @@ -66,6 +66,7 @@ class com.datadog.android._InternalProxy fun error(String, Throwable? = null) fun error(String, String?, String?) val _telemetry: _TelemetryProxy + fun setCustomAppVersion(String) enum com.datadog.android.core.configuration.BatchSize constructor(Long) - SMALL diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/Datadog.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/Datadog.kt index 1685e310c8..38101a2005 100644 --- a/dd-sdk-android/src/main/kotlin/com/datadog/android/Datadog.kt +++ b/dd-sdk-android/src/main/kotlin/com/datadog/android/Datadog.kt @@ -351,6 +351,7 @@ object Datadog { ) } + @Suppress("ComplexMethod") private fun applyAdditionalConfiguration( additionalConfiguration: Map ) { @@ -368,6 +369,12 @@ object Datadog { CoreFeature.sdkVersion = it } } + + additionalConfiguration[DD_APP_VERSION_TAG]?.let { + if (it is String && it.isNotBlank()) { + CoreFeature.packageVersionProvider.version = it + } + } } @Suppress("ThrowingInternalException") @@ -426,6 +433,7 @@ object Datadog { internal const val DD_SOURCE_TAG = "_dd.source" internal const val DD_SDK_VERSION_TAG = "_dd.sdk_version" + internal const val DD_APP_VERSION_TAG = "_dd.version" // endregion } diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/_InternalProxy.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/_InternalProxy.kt index 052ddaab89..8255cee6e8 100644 --- a/dd-sdk-android/src/main/kotlin/com/datadog/android/_InternalProxy.kt +++ b/dd-sdk-android/src/main/kotlin/com/datadog/android/_InternalProxy.kt @@ -6,6 +6,7 @@ package com.datadog.android +import com.datadog.android.core.internal.CoreFeature import com.datadog.android.telemetry.internal.Telemetry /** @@ -22,16 +23,12 @@ import com.datadog.android.telemetry.internal.Telemetry "UndocumentedPublicClass", "UndocumentedPublicFunction", "UndocumentedPublicProperty", + "ClassName", "ClassNaming", "VariableNaming" ) -class _InternalProxy { - class _TelemetryProxy { - private val telemetry: Telemetry - - internal constructor(telemetry: Telemetry) { - this.telemetry = telemetry - } +class _InternalProxy internal constructor(telemetry: Telemetry) { + class _TelemetryProxy internal constructor(private val telemetry: Telemetry) { fun debug(message: String) { telemetry.debug(message) @@ -46,9 +43,9 @@ class _InternalProxy { } } - val _telemetry: _TelemetryProxy + val _telemetry: _TelemetryProxy = _TelemetryProxy(telemetry) - internal constructor(telemetry: Telemetry) { - _telemetry = _TelemetryProxy(telemetry) + fun setCustomAppVersion(version: String) { + CoreFeature.packageVersionProvider.version = version } } diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt index 7dcc50305e..a1a34ca693 100644 --- a/dd-sdk-android/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt +++ b/dd-sdk-android/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt @@ -33,9 +33,12 @@ import com.datadog.android.core.internal.privacy.ConsentProvider import com.datadog.android.core.internal.privacy.NoOpConsentProvider import com.datadog.android.core.internal.privacy.TrackingConsentProvider import com.datadog.android.core.internal.system.AndroidInfoProvider +import com.datadog.android.core.internal.system.AppVersionProvider import com.datadog.android.core.internal.system.BroadcastReceiverSystemInfoProvider import com.datadog.android.core.internal.system.DefaultAndroidInfoProvider +import com.datadog.android.core.internal.system.DefaultAppVersionProvider import com.datadog.android.core.internal.system.NoOpAndroidInfoProvider +import com.datadog.android.core.internal.system.NoOpAppVersionProvider import com.datadog.android.core.internal.system.NoOpSystemInfoProvider import com.datadog.android.core.internal.system.SystemInfoProvider import com.datadog.android.core.internal.time.KronosTimeProvider @@ -134,7 +137,7 @@ internal object CoreFeature { internal var clientToken: String = "" internal var packageName: String = "" - internal var packageVersion: String = "" + internal var packageVersionProvider: AppVersionProvider = NoOpAppVersionProvider() internal var serviceName: String = "" internal var sourceName: String = DEFAULT_SOURCE_NAME internal var sdkVersion: String = DEFAULT_SDK_VERSION @@ -259,7 +262,7 @@ internal object CoreFeature { timeProvider, sdkVersion, envName, - packageVersion + packageVersionProvider ), NdkCrashLogDeserializer(sdkLogger), RumEventDeserializer(), @@ -306,12 +309,14 @@ internal object CoreFeature { devLogger.e("Unable to read your application's version name", e) null } - packageVersion = 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() - } ?: DEFAULT_APP_VERSION + packageVersionProvider = DefaultAppVersionProvider( + 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() + } ?: DEFAULT_APP_VERSION + ) clientToken = credentials.clientToken serviceName = credentials.serviceName ?: appContext.packageName rumApplicationId = credentials.rumApplicationId @@ -468,7 +473,7 @@ internal object CoreFeature { private fun cleanupApplicationInfo() { clientToken = "" packageName = "" - packageVersion = "" + packageVersionProvider = NoOpAppVersionProvider() serviceName = "" sourceName = DEFAULT_SOURCE_NAME rumApplicationId = null diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/core/internal/system/AppVersionProvider.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/core/internal/system/AppVersionProvider.kt new file mode 100644 index 0000000000..d671f4af04 --- /dev/null +++ b/dd-sdk-android/src/main/kotlin/com/datadog/android/core/internal/system/AppVersionProvider.kt @@ -0,0 +1,11 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +package com.datadog.android.core.internal.system + +internal interface AppVersionProvider { + var version: String +} diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/core/internal/system/DefaultAppVersionProvider.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/core/internal/system/DefaultAppVersionProvider.kt new file mode 100644 index 0000000000..7c751ab68c --- /dev/null +++ b/dd-sdk-android/src/main/kotlin/com/datadog/android/core/internal/system/DefaultAppVersionProvider.kt @@ -0,0 +1,24 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +package com.datadog.android.core.internal.system + +import java.util.concurrent.atomic.AtomicReference + +internal class DefaultAppVersionProvider(initialVersion: String) : AppVersionProvider { + + private val value: AtomicReference + + override var version: String + get() = value.get() + set(value) { + this.value.set(value) + } + + init { + this.value = AtomicReference(initialVersion) + } +} diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/core/internal/system/NoOpAppVersionProvider.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/core/internal/system/NoOpAppVersionProvider.kt new file mode 100644 index 0000000000..5b21d161f7 --- /dev/null +++ b/dd-sdk-android/src/main/kotlin/com/datadog/android/core/internal/system/NoOpAppVersionProvider.kt @@ -0,0 +1,14 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +package com.datadog.android.core.internal.system + +internal class NoOpAppVersionProvider : AppVersionProvider { + @Suppress("UNUSED_PARAMETER") + override var version: String + get() = "" + set(value) {} +} diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/error/internal/CrashReportsFeature.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/error/internal/CrashReportsFeature.kt index 4b8e250411..8fecc77a67 100644 --- a/dd-sdk-android/src/main/kotlin/com/datadog/android/error/internal/CrashReportsFeature.kt +++ b/dd-sdk-android/src/main/kotlin/com/datadog/android/error/internal/CrashReportsFeature.kt @@ -79,7 +79,7 @@ internal object CrashReportsFeature : SdkFeature() { CoreFeature.sourceName, CoreFeature.sdkVersion, CoreFeature.okHttpClient, - CoreFeature.androidInfoProvider + CoreFeature.androidInfoProvider, + CoreFeature.packageVersionProvider ) } diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/rum/internal/net/RumOkHttpUploaderV2.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/rum/internal/net/RumOkHttpUploaderV2.kt index 3173f441b5..8a75f95b3b 100644 --- a/dd-sdk-android/src/main/kotlin/com/datadog/android/rum/internal/net/RumOkHttpUploaderV2.kt +++ b/dd-sdk-android/src/main/kotlin/com/datadog/android/rum/internal/net/RumOkHttpUploaderV2.kt @@ -9,6 +9,7 @@ package com.datadog.android.rum.internal.net import com.datadog.android.core.internal.CoreFeature import com.datadog.android.core.internal.net.DataOkHttpUploaderV2 import com.datadog.android.core.internal.system.AndroidInfoProvider +import com.datadog.android.core.internal.system.AppVersionProvider import com.datadog.android.core.internal.utils.sdkLogger import com.datadog.android.rum.RumAttributes import okhttp3.Call @@ -19,7 +20,8 @@ internal open class RumOkHttpUploaderV2( source: String, sdkVersion: String, callFactory: Call.Factory, - androidInfoProvider: AndroidInfoProvider + androidInfoProvider: AndroidInfoProvider, + private val appVersionProvider: AppVersionProvider ) : DataOkHttpUploaderV2( buildUrl(endpoint, TrackType.RUM), clientToken, @@ -31,20 +33,21 @@ internal open class RumOkHttpUploaderV2( sdkLogger ) { - private val tags: String by lazy { - val elements = mutableListOf( - "${RumAttributes.SERVICE_NAME}:${CoreFeature.serviceName}", - "${RumAttributes.APPLICATION_VERSION}:${CoreFeature.packageVersion}", - "${RumAttributes.SDK_VERSION}:$sdkVersion", - "${RumAttributes.ENV}:${CoreFeature.envName}" - ) + private val tags: String + get() { + val elements = mutableListOf( + "${RumAttributes.SERVICE_NAME}:${CoreFeature.serviceName}", + "${RumAttributes.APPLICATION_VERSION}:${appVersionProvider.version}", + "${RumAttributes.SDK_VERSION}:$sdkVersion", + "${RumAttributes.ENV}:${CoreFeature.envName}" + ) - if (CoreFeature.variant.isNotEmpty()) { - elements.add("${RumAttributes.VARIANT}:${CoreFeature.variant}") - } + if (CoreFeature.variant.isNotEmpty()) { + elements.add("${RumAttributes.VARIANT}:${CoreFeature.variant}") + } - elements.joinToString(",") - } + return elements.joinToString(",") + } // region DataOkHttpUploaderV2 diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/TracingFeature.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/TracingFeature.kt index 975e56bc02..1407805931 100644 --- a/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/TracingFeature.kt +++ b/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/TracingFeature.kt @@ -34,6 +34,7 @@ internal object TracingFeature : SdkFeature { // region Mapper @@ -71,7 +73,7 @@ internal class DdSpanToSpanEventMapper( additionalProperties = userInfo.additionalProperties ) return SpanEvent.Meta( - version = CoreFeature.packageVersion, + version = appVersionProvider.version, dd = SpanEvent.Dd(source = CoreFeature.sourceName), span = SpanEvent.Span(), tracer = SpanEvent.Tracer( diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/webview/DatadogEventBridge.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/webview/DatadogEventBridge.kt index 22f2271e12..e0e0b05669 100644 --- a/dd-sdk-android/src/main/kotlin/com/datadog/android/webview/DatadogEventBridge.kt +++ b/dd-sdk-android/src/main/kotlin/com/datadog/android/webview/DatadogEventBridge.kt @@ -139,7 +139,8 @@ internal constructor( WebViewLogEventConsumer( userLogsWriter = WebViewLogsFeature.persistenceStrategy.getWriter(), rumContextProvider = contextProvider, - timeProvider = CoreFeature.timeProvider + timeProvider = CoreFeature.timeProvider, + appVersionProvider = CoreFeature.packageVersionProvider ) ) } diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/webview/internal/log/WebViewLogEventConsumer.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/webview/internal/log/WebViewLogEventConsumer.kt index a5061cce99..d138564e41 100644 --- a/dd-sdk-android/src/main/kotlin/com/datadog/android/webview/internal/log/WebViewLogEventConsumer.kt +++ b/dd-sdk-android/src/main/kotlin/com/datadog/android/webview/internal/log/WebViewLogEventConsumer.kt @@ -8,6 +8,7 @@ package com.datadog.android.webview.internal.log import com.datadog.android.core.internal.CoreFeature import com.datadog.android.core.internal.persistence.DataWriter +import com.datadog.android.core.internal.system.AppVersionProvider import com.datadog.android.core.internal.time.TimeProvider import com.datadog.android.core.internal.utils.sdkLogger import com.datadog.android.log.LogAttributes @@ -21,13 +22,15 @@ import kotlin.NumberFormatException internal class WebViewLogEventConsumer( private val userLogsWriter: DataWriter, private val rumContextProvider: WebViewRumEventContextProvider, - private val timeProvider: TimeProvider + private val timeProvider: TimeProvider, + private val appVersionProvider: AppVersionProvider ) : WebViewEventConsumer> { - private val ddTags: String by lazy { - "${LogAttributes.APPLICATION_VERSION}:${CoreFeature.packageVersion}" + - ",${LogAttributes.ENV}:${CoreFeature.envName}" - } + private val ddTags: String + get() { + return "${LogAttributes.APPLICATION_VERSION}:${appVersionProvider.version}" + + ",${LogAttributes.ENV}:${CoreFeature.envName}" + } override fun consume(event: Pair) { map(event.first).let { diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumFeature.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumFeature.kt index 8345d4bcf5..db1d56a43e 100644 --- a/dd-sdk-android/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumFeature.kt +++ b/dd-sdk-android/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumFeature.kt @@ -43,7 +43,8 @@ internal object WebViewRumFeature : SdkFeature() CoreFeature.sourceName, CoreFeature.sdkVersion, CoreFeature.okHttpClient, - CoreFeature.androidInfoProvider + CoreFeature.androidInfoProvider, + CoreFeature.packageVersionProvider ) } } diff --git a/dd-sdk-android/src/test/kotlin/com/datadog/android/DatadogTest.kt b/dd-sdk-android/src/test/kotlin/com/datadog/android/DatadogTest.kt index bb725a2dd3..da02fb6a6c 100644 --- a/dd-sdk-android/src/test/kotlin/com/datadog/android/DatadogTest.kt +++ b/dd-sdk-android/src/test/kotlin/com/datadog/android/DatadogTest.kt @@ -795,6 +795,74 @@ internal class DatadogTest { .containsOnly(CoreFeature.DEFAULT_SDK_VERSION) } + @Test + fun `𝕄 apply app version 𝕎 applyAdditionalConfig(config) { with app version }`( + @StringForgery appVersion: String + ) { + // Given + val config = Configuration.Builder( + logsEnabled = true, + tracesEnabled = true, + crashReportsEnabled = true, + rumEnabled = true + ) + .setAdditionalConfiguration(mapOf(Datadog.DD_APP_VERSION_TAG to appVersion)) + .build() + val credentials = Credentials(fakeToken, fakeEnvName, fakeVariant, null, null) + + // When + Datadog.initialize(appContext.mockInstance, credentials, config, TrackingConsent.GRANTED) + + // Then + assertThat(CoreFeature.packageVersionProvider.version).isEqualTo(appVersion) + } + + @Test + fun `𝕄 use default app version 𝕎 applyAdditionalConfig(config) { with empty app version }`( + forge: Forge + ) { + // Given + val config = Configuration.Builder( + logsEnabled = true, + tracesEnabled = true, + crashReportsEnabled = true, + rumEnabled = true + ) + .setAdditionalConfiguration( + mapOf(Datadog.DD_APP_VERSION_TAG to forge.aWhitespaceString()) + ) + .build() + val credentials = Credentials(fakeToken, fakeEnvName, fakeVariant, null, null) + + // When + Datadog.initialize(appContext.mockInstance, credentials, config, TrackingConsent.GRANTED) + + // Then + assertThat(CoreFeature.packageVersionProvider.version).isEqualTo(appContext.fakeVersionName) + } + + @Test + fun `𝕄 use default app version 𝕎 applyAdditionalConfig(config) { with app version !string }`( + forge: Forge + ) { + // Given + val config = Configuration.Builder( + logsEnabled = true, + tracesEnabled = true, + crashReportsEnabled = true, + rumEnabled = true + ) + .setAdditionalConfiguration(mapOf(Datadog.DD_APP_VERSION_TAG to forge.anInt())) + .build() + val credentials = Credentials(fakeToken, fakeEnvName, fakeVariant, null, null) + + // When + Datadog.initialize(appContext.mockInstance, credentials, config, TrackingConsent.GRANTED) + + // Then + assertThat(CoreFeature.packageVersionProvider.version).isEqualTo(appContext.fakeVersionName) + } + @Test fun `𝕄 enable RUM debugging 𝕎 enableRumDebugging(true)`() { // Given diff --git a/dd-sdk-android/src/test/kotlin/com/datadog/android/InternalProxyTest.kt b/dd-sdk-android/src/test/kotlin/com/datadog/android/InternalProxyTest.kt index 9050c52e45..30570117f7 100644 --- a/dd-sdk-android/src/test/kotlin/com/datadog/android/InternalProxyTest.kt +++ b/dd-sdk-android/src/test/kotlin/com/datadog/android/InternalProxyTest.kt @@ -6,17 +6,20 @@ package com.datadog.android +import com.datadog.android.core.internal.CoreFeature +import com.datadog.android.core.internal.system.DefaultAppVersionProvider import com.datadog.android.telemetry.internal.Telemetry import com.datadog.android.utils.forge.Configurator +import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.verify import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.api.extension.Extensions -import org.mockito.Mockito.mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoSettings import org.mockito.quality.Strictness @@ -34,7 +37,7 @@ internal class InternalProxyTest { @StringForgery message: String ) { // Given - val mockTelemetry = mock(Telemetry::class.java) + val mockTelemetry = mock() val proxy = _InternalProxy(mockTelemetry) // When @@ -51,7 +54,7 @@ internal class InternalProxyTest { @StringForgery kind: String ) { // Given - val mockTelemetry = mock(Telemetry::class.java) + val mockTelemetry = mock() val proxy = _InternalProxy(mockTelemetry) // When @@ -67,7 +70,7 @@ internal class InternalProxyTest { @Forgery throwable: Throwable ) { // Given - val mockTelemetry = mock(Telemetry::class.java) + val mockTelemetry = mock() val proxy = _InternalProxy(mockTelemetry) // When @@ -76,4 +79,19 @@ internal class InternalProxyTest { // Then verify(mockTelemetry).error(message, throwable) } + + @Test + fun `M set app version W setCustomAppVersion()`( + @StringForgery version: String + ) { + // Given + CoreFeature.packageVersionProvider = DefaultAppVersionProvider("") + val proxy = _InternalProxy(telemetry = mock()) + + // When + proxy.setCustomAppVersion(version) + + // Then + assertThat(CoreFeature.packageVersionProvider.version).isEqualTo(version) + } } diff --git a/dd-sdk-android/src/test/kotlin/com/datadog/android/core/internal/CoreFeatureTest.kt b/dd-sdk-android/src/test/kotlin/com/datadog/android/core/internal/CoreFeatureTest.kt index 69d4fdc651..bc11b69b73 100644 --- a/dd-sdk-android/src/test/kotlin/com/datadog/android/core/internal/CoreFeatureTest.kt +++ b/dd-sdk-android/src/test/kotlin/com/datadog/android/core/internal/CoreFeatureTest.kt @@ -265,7 +265,7 @@ internal class CoreFeatureTest { // Then assertThat(CoreFeature.clientToken).isEqualTo(fakeCredentials.clientToken) assertThat(CoreFeature.packageName).isEqualTo(appContext.fakePackageName) - assertThat(CoreFeature.packageVersion).isEqualTo(appContext.fakeVersionName) + assertThat(CoreFeature.packageVersionProvider.version).isEqualTo(appContext.fakeVersionName) assertThat(CoreFeature.serviceName).isEqualTo(fakeCredentials.serviceName) assertThat(CoreFeature.envName).isEqualTo(fakeCredentials.envName) assertThat(CoreFeature.variant).isEqualTo(fakeCredentials.variant) @@ -288,7 +288,7 @@ internal class CoreFeatureTest { // Then assertThat(CoreFeature.clientToken).isEqualTo(fakeCredentials.clientToken) assertThat(CoreFeature.packageName).isEqualTo(appContext.fakePackageName) - assertThat(CoreFeature.packageVersion).isEqualTo(appContext.fakeVersionName) + assertThat(CoreFeature.packageVersionProvider.version).isEqualTo(appContext.fakeVersionName) assertThat(CoreFeature.serviceName).isEqualTo(appContext.fakePackageName) assertThat(CoreFeature.envName).isEqualTo(fakeCredentials.envName) assertThat(CoreFeature.variant).isEqualTo(fakeCredentials.variant) @@ -311,7 +311,7 @@ internal class CoreFeatureTest { // Then assertThat(CoreFeature.clientToken).isEqualTo(fakeCredentials.clientToken) assertThat(CoreFeature.packageName).isEqualTo(appContext.fakePackageName) - assertThat(CoreFeature.packageVersion).isEqualTo(appContext.fakeVersionName) + assertThat(CoreFeature.packageVersionProvider.version).isEqualTo(appContext.fakeVersionName) assertThat(CoreFeature.serviceName).isEqualTo(fakeCredentials.serviceName) assertThat(CoreFeature.envName).isEqualTo(fakeCredentials.envName) assertThat(CoreFeature.variant).isEqualTo(fakeCredentials.variant) @@ -339,7 +339,9 @@ internal class CoreFeatureTest { // Then assertThat(CoreFeature.clientToken).isEqualTo(fakeCredentials.clientToken) assertThat(CoreFeature.packageName).isEqualTo(appContext.fakePackageName) - assertThat(CoreFeature.packageVersion).isEqualTo(appContext.fakeVersionCode.toString()) + assertThat(CoreFeature.packageVersionProvider.version).isEqualTo( + appContext.fakeVersionCode.toString() + ) assertThat(CoreFeature.serviceName).isEqualTo(fakeCredentials.serviceName) assertThat(CoreFeature.envName).isEqualTo(fakeCredentials.envName) assertThat(CoreFeature.variant).isEqualTo(fakeCredentials.variant) @@ -368,7 +370,9 @@ internal class CoreFeatureTest { // Then assertThat(CoreFeature.clientToken).isEqualTo(fakeCredentials.clientToken) assertThat(CoreFeature.packageName).isEqualTo(appContext.fakePackageName) - assertThat(CoreFeature.packageVersion).isEqualTo(CoreFeature.DEFAULT_APP_VERSION) + assertThat(CoreFeature.packageVersionProvider.version).isEqualTo( + CoreFeature.DEFAULT_APP_VERSION + ) assertThat(CoreFeature.serviceName).isEqualTo(fakeCredentials.serviceName) assertThat(CoreFeature.envName).isEqualTo(fakeCredentials.envName) assertThat(CoreFeature.variant).isEqualTo(fakeCredentials.variant) @@ -510,7 +514,7 @@ internal class CoreFeatureTest { // Then assertThat(CoreFeature.clientToken).isEqualTo(fakeCredentials.clientToken) assertThat(CoreFeature.packageName).isEqualTo(appContext.fakePackageName) - assertThat(CoreFeature.packageVersion).isEqualTo(appContext.fakeVersionName) + assertThat(CoreFeature.packageVersionProvider.version).isEqualTo(appContext.fakeVersionName) assertThat(CoreFeature.serviceName).isEqualTo(fakeCredentials.serviceName) assertThat(CoreFeature.envName).isEqualTo(fakeCredentials.envName) assertThat(CoreFeature.variant).isEqualTo(fakeCredentials.variant) @@ -653,7 +657,7 @@ internal class CoreFeatureTest { // Then assertThat(CoreFeature.clientToken).isEqualTo("") assertThat(CoreFeature.packageName).isEqualTo("") - assertThat(CoreFeature.packageVersion).isEqualTo("") + assertThat(CoreFeature.packageVersionProvider.version).isEqualTo("") assertThat(CoreFeature.serviceName).isEqualTo("") assertThat(CoreFeature.envName).isEqualTo("") assertThat(CoreFeature.variant).isEqualTo("") diff --git a/dd-sdk-android/src/test/kotlin/com/datadog/android/core/internal/system/DefaultAppVersionProviderTest.kt b/dd-sdk-android/src/test/kotlin/com/datadog/android/core/internal/system/DefaultAppVersionProviderTest.kt new file mode 100644 index 0000000000..84cc35c3d9 --- /dev/null +++ b/dd-sdk-android/src/test/kotlin/com/datadog/android/core/internal/system/DefaultAppVersionProviderTest.kt @@ -0,0 +1,75 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +package com.datadog.android.core.internal.system + +import com.datadog.android.utils.forge.Configurator +import fr.xgouchet.elmyr.annotation.StringForgery +import fr.xgouchet.elmyr.junit5.ForgeConfiguration +import fr.xgouchet.elmyr.junit5.ForgeExtension +import java.util.concurrent.CountDownLatch +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.RepeatedTest +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extensions +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.junit.jupiter.MockitoSettings +import org.mockito.quality.Strictness + +@Extensions( + ExtendWith(MockitoExtension::class), + ExtendWith(ForgeExtension::class) +) +@MockitoSettings(strictness = Strictness.LENIENT) +@ForgeConfiguration(Configurator::class) +internal class DefaultAppVersionProviderTest { + + private lateinit var testedProvider: AppVersionProvider + + @StringForgery + lateinit var fakeVersion: String + + @BeforeEach + fun setUp() { + testedProvider = DefaultAppVersionProvider(fakeVersion) + } + + @Test + fun `M return initial version W get`() { + assertThat(testedProvider.version).isEqualTo(fakeVersion) + } + + @Test + fun `M return a new version W set() + get()`( + @StringForgery fakeNewVersion: String + ) { + // When + testedProvider.version = fakeNewVersion + + // Then + assertThat(testedProvider.version).isEqualTo(fakeNewVersion) + } + + @RepeatedTest(10) + fun `M return a new version W set + get() { multi-threaded }`( + @StringForgery fakeNewVersion: String + ) { + // Given + val lock = CountDownLatch(1) + + // When + Thread { + testedProvider.version = fakeNewVersion + lock.countDown() + }.start() + lock.await() + + // Then + assertThat(testedProvider.version).isEqualTo(fakeNewVersion) + } +} diff --git a/dd-sdk-android/src/test/kotlin/com/datadog/android/error/internal/DatadogExceptionHandlerTest.kt b/dd-sdk-android/src/test/kotlin/com/datadog/android/error/internal/DatadogExceptionHandlerTest.kt index e6bb0ed7a9..4c733e244e 100644 --- a/dd-sdk-android/src/test/kotlin/com/datadog/android/error/internal/DatadogExceptionHandlerTest.kt +++ b/dd-sdk-android/src/test/kotlin/com/datadog/android/error/internal/DatadogExceptionHandlerTest.kt @@ -153,7 +153,7 @@ internal class DatadogExceptionHandlerTest { mockTimeProvider, CoreFeature.sdkVersion, CoreFeature.envName, - CoreFeature.packageVersion + CoreFeature.packageVersionProvider ), writer = mockLogWriter, appContext = appContext.mockInstance diff --git a/dd-sdk-android/src/test/kotlin/com/datadog/android/log/internal/domain/LogGeneratorTest.kt b/dd-sdk-android/src/test/kotlin/com/datadog/android/log/internal/domain/LogGeneratorTest.kt index 0fde34ae92..8a50685187 100644 --- a/dd-sdk-android/src/test/kotlin/com/datadog/android/log/internal/domain/LogGeneratorTest.kt +++ b/dd-sdk-android/src/test/kotlin/com/datadog/android/log/internal/domain/LogGeneratorTest.kt @@ -7,6 +7,7 @@ package com.datadog.android.log.internal.domain import com.datadog.android.core.internal.net.info.NetworkInfoProvider +import com.datadog.android.core.internal.system.AppVersionProvider import com.datadog.android.core.internal.time.TimeProvider import com.datadog.android.core.model.NetworkInfo import com.datadog.android.core.model.UserInfo @@ -76,6 +77,10 @@ internal class LogGeneratorTest { @Mock lateinit var mockTimeProvider: TimeProvider + + @Mock + lateinit var mockAppVersionProvider: AppVersionProvider + lateinit var fakeServiceName: String lateinit var fakeLoggerName: String lateinit var fakeAttributes: Map @@ -128,7 +133,8 @@ internal class LogGeneratorTest { whenever(mockSpan.context()) doReturn mockSpanContext whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId whenever(mockSpanContext.toTraceId()) doReturn fakeTraceId - whenever(mockTimeProvider.getServerOffsetMillis()).thenReturn(fakeTimeOffset) + whenever(mockTimeProvider.getServerOffsetMillis()) doReturn fakeTimeOffset + whenever(mockAppVersionProvider.version) doReturn fakeAppVersion GlobalTracer.registerIfAbsent(mockTracer) testedLogGenerator = LogGenerator( fakeServiceName, @@ -138,7 +144,7 @@ internal class LogGeneratorTest { mockTimeProvider, fakeSdkVersion, fakeEnvName, - fakeAppVersion + mockAppVersionProvider ) } @@ -384,7 +390,7 @@ internal class LogGeneratorTest { mockTimeProvider, fakeSdkVersion, fakeEnvName, - fakeAppVersion + mockAppVersionProvider ) // WHEN val log = testedLogGenerator.generateLog( @@ -413,7 +419,7 @@ internal class LogGeneratorTest { mockTimeProvider, fakeSdkVersion, fakeEnvName, - fakeAppVersion + mockAppVersionProvider ) // WHEN val log = testedLogGenerator.generateLog( @@ -458,7 +464,7 @@ internal class LogGeneratorTest { mockTimeProvider, fakeSdkVersion, "", - fakeAppVersion + mockAppVersionProvider ) // WHEN @@ -497,6 +503,8 @@ internal class LogGeneratorTest { @Test fun `M not add the appVersionTag W not empty`() { // GIVEN + whenever(mockAppVersionProvider.version) doReturn "" + testedLogGenerator = LogGenerator( fakeServiceName, fakeLoggerName, @@ -505,7 +513,7 @@ internal class LogGeneratorTest { mockTimeProvider, fakeSdkVersion, fakeEnvName, - "" + mockAppVersionProvider ) // WHEN diff --git a/dd-sdk-android/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt b/dd-sdk-android/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt index 10e96c436b..9e2f78bc80 100644 --- a/dd-sdk-android/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt +++ b/dd-sdk-android/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt @@ -12,10 +12,10 @@ import android.view.Choreographer import com.datadog.android.Datadog import com.datadog.android.core.configuration.Configuration import com.datadog.android.core.configuration.Credentials -import com.datadog.android.core.internal.CoreFeature import com.datadog.android.core.internal.net.info.NetworkInfoProvider import com.datadog.android.core.internal.persistence.DataWriter import com.datadog.android.core.internal.sampling.Sampler +import com.datadog.android.core.internal.system.AppVersionProvider import com.datadog.android.core.internal.time.TimeProvider import com.datadog.android.core.model.NetworkInfo import com.datadog.android.core.model.UserInfo @@ -105,6 +105,9 @@ internal class DatadogLogHandlerTest { @Mock lateinit var mockTimeProvider: TimeProvider + @Mock + lateinit var mockAppVersionProvider: AppVersionProvider + @Mock lateinit var mockSampler: Sampler @@ -135,11 +138,10 @@ internal class DatadogLogHandlerTest { fakeAttributes = forge.aMap { anAlphabeticalString() to anInt() } fakeTags = forge.aList { anAlphabeticalString() }.toSet() fakeSdkVersion = forge.anAlphabeticalString() - CoreFeature.sdkVersion = fakeSdkVersion - CoreFeature.envName = fakeEnvName - CoreFeature.packageVersion = fakeAppVersion + whenever(mockNetworkInfoProvider.getLatestNetworkInfo()) doReturn fakeNetworkInfo whenever(mockUserInfoProvider.getUserInfo()) doReturn fakeUserInfo + whenever(mockAppVersionProvider.version) doReturn fakeAppVersion testedHandler = DatadogLogHandler( LogGenerator( @@ -150,7 +152,7 @@ internal class DatadogLogHandlerTest { mockTimeProvider, fakeSdkVersion, fakeEnvName, - fakeAppVersion + mockAppVersionProvider ), mockWriter ) @@ -218,7 +220,7 @@ internal class DatadogLogHandlerTest { mockTimeProvider, fakeSdkVersion, fakeEnvName, - fakeAppVersion + mockAppVersionProvider ), mockWriter, minLogPriority = forge.anInt(min = fakeLevel + 1) @@ -443,7 +445,7 @@ internal class DatadogLogHandlerTest { mockTimeProvider, fakeSdkVersion, fakeEnvName, - fakeAppVersion + mockAppVersionProvider ), mockWriter ) @@ -497,7 +499,7 @@ internal class DatadogLogHandlerTest { mockTimeProvider, fakeSdkVersion, fakeEnvName, - fakeAppVersion + mockAppVersionProvider ), mockWriter ) @@ -654,7 +656,7 @@ internal class DatadogLogHandlerTest { mockTimeProvider, fakeSdkVersion, fakeEnvName, - fakeAppVersion + mockAppVersionProvider ), mockWriter, bundleWithTraces = false @@ -691,7 +693,7 @@ internal class DatadogLogHandlerTest { mockTimeProvider, fakeSdkVersion, fakeEnvName, - fakeAppVersion + mockAppVersionProvider ), mockWriter, bundleWithTraces = false, @@ -725,7 +727,7 @@ internal class DatadogLogHandlerTest { mockTimeProvider, fakeSdkVersion, fakeEnvName, - fakeAppVersion + mockAppVersionProvider ), mockWriter, bundleWithTraces = false, diff --git a/dd-sdk-android/src/test/kotlin/com/datadog/android/rum/internal/net/RumOkHttpUploaderV2Test.kt b/dd-sdk-android/src/test/kotlin/com/datadog/android/rum/internal/net/RumOkHttpUploaderV2Test.kt index baf0d9d820..add39d78f3 100644 --- a/dd-sdk-android/src/test/kotlin/com/datadog/android/rum/internal/net/RumOkHttpUploaderV2Test.kt +++ b/dd-sdk-android/src/test/kotlin/com/datadog/android/rum/internal/net/RumOkHttpUploaderV2Test.kt @@ -9,6 +9,7 @@ package com.datadog.android.rum.internal.net import android.app.Application import com.datadog.android.core.internal.net.DataOkHttpUploaderV2 import com.datadog.android.core.internal.net.DataOkHttpUploaderV2Test +import com.datadog.android.core.internal.system.AppVersionProvider import com.datadog.android.rum.RumAttributes import com.datadog.android.utils.config.ApplicationContextTestConfiguration import com.datadog.android.utils.config.CoreFeatureTestConfiguration @@ -16,11 +17,16 @@ import com.datadog.android.utils.forge.Configurator import com.datadog.tools.unit.annotations.TestConfigurationsProvider import com.datadog.tools.unit.extensions.TestConfigurationExtension import com.datadog.tools.unit.extensions.config.TestConfiguration +import com.nhaarman.mockitokotlin2.doReturn +import com.nhaarman.mockitokotlin2.whenever +import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension import okhttp3.Call +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.api.extension.Extensions +import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoSettings import org.mockito.quality.Strictness @@ -34,6 +40,15 @@ import org.mockito.quality.Strictness @ForgeConfiguration(Configurator::class) internal class RumOkHttpUploaderV2Test : DataOkHttpUploaderV2Test() { + @Mock + lateinit var mockAppVersionProvider: AppVersionProvider + + @BeforeEach + override fun `set up`(forge: Forge) { + super.`set up`(forge) + whenever(mockAppVersionProvider.version) doReturn appContext.fakeVersionName + } + override fun buildTestedInstance(callFactory: Call.Factory): RumOkHttpUploaderV2 { return RumOkHttpUploaderV2( fakeEndpoint, @@ -41,7 +56,8 @@ internal class RumOkHttpUploaderV2Test : DataOkHttpUploaderV2Test { + // SDK version is also in the header, where it is sanitized. So we should sanitize + // it here as well. + val sanitizedSdkVersion = + fakeSdkVersion.filter { it == '\t' || it in '\u0020' until '\u007F' } val tags = mutableListOf( "${RumAttributes.SERVICE_NAME}:${coreFeature.fakeServiceName}", "${RumAttributes.APPLICATION_VERSION}:${appContext.fakeVersionName}", - "${RumAttributes.SDK_VERSION}:$fakeSdkVersion", + "${RumAttributes.SDK_VERSION}:$sanitizedSdkVersion", "${RumAttributes.ENV}:${coreFeature.fakeEnvName}" ) diff --git a/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/internal/domain/event/DdSpanToSpanEventMapperTest.kt b/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/internal/domain/event/DdSpanToSpanEventMapperTest.kt index 61db4978b8..d3b4aa05b5 100644 --- a/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/internal/domain/event/DdSpanToSpanEventMapperTest.kt +++ b/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/internal/domain/event/DdSpanToSpanEventMapperTest.kt @@ -8,6 +8,7 @@ package com.datadog.android.tracing.internal.domain.event import com.datadog.android.core.internal.CoreFeature import com.datadog.android.core.internal.net.info.NetworkInfoProvider +import com.datadog.android.core.internal.system.AppVersionProvider import com.datadog.android.core.internal.time.TimeProvider import com.datadog.android.core.internal.utils.toHexString import com.datadog.android.core.model.NetworkInfo @@ -61,6 +62,9 @@ internal class DdSpanToSpanEventMapperTest { @Mock lateinit var mockNetworkInfoProvider: NetworkInfoProvider + @Mock + lateinit var mockAppVersionProvider: AppVersionProvider + @StringForgery(regex = "[0-9]\\.[0-9]\\.[0-9]") lateinit var fakeClientPackageVersion: String @@ -75,16 +79,19 @@ internal class DdSpanToSpanEventMapperTest { @BeforeEach fun `set up`() { - CoreFeature.packageVersion = fakeClientPackageVersion CoreFeature.sdkVersion = fakeSdkVersion CoreFeature.sourceName = fakeSource - whenever(mockTimeProvider.getServerOffsetNanos()).thenReturn(fakeServerOffsetNanos) + + whenever(mockTimeProvider.getServerOffsetNanos()) doReturn fakeServerOffsetNanos whenever(mockUserInfoProvider.getUserInfo()) doReturn fakeUserInfo whenever(mockNetworkInfoProvider.getLatestNetworkInfo()) doReturn fakeNetworkInfo + whenever(mockAppVersionProvider.version) doReturn fakeClientPackageVersion + testedMapper = DdSpanToSpanEventMapper( mockTimeProvider, mockNetworkInfoProvider, - mockUserInfoProvider + mockUserInfoProvider, + mockAppVersionProvider ) } diff --git a/dd-sdk-android/src/test/kotlin/com/datadog/android/utils/config/CoreFeatureTestConfiguration.kt b/dd-sdk-android/src/test/kotlin/com/datadog/android/utils/config/CoreFeatureTestConfiguration.kt index e9c466eb22..2ed0047e99 100644 --- a/dd-sdk-android/src/test/kotlin/com/datadog/android/utils/config/CoreFeatureTestConfiguration.kt +++ b/dd-sdk-android/src/test/kotlin/com/datadog/android/utils/config/CoreFeatureTestConfiguration.kt @@ -11,6 +11,7 @@ import com.datadog.android.core.internal.CoreFeature import com.datadog.android.core.internal.net.info.NetworkInfoProvider import com.datadog.android.core.internal.privacy.ConsentProvider import com.datadog.android.core.internal.system.AndroidInfoProvider +import com.datadog.android.core.internal.system.AppVersionProvider import com.datadog.android.core.internal.system.SystemInfoProvider import com.datadog.android.core.internal.time.TimeProvider import com.datadog.android.log.internal.user.MutableUserInfoProvider @@ -45,6 +46,7 @@ internal class CoreFeatureTestConfiguration( lateinit var mockUserInfoProvider: MutableUserInfoProvider lateinit var mockTrackingConsentProvider: ConsentProvider lateinit var mockAndroidInfoProvider: AndroidInfoProvider + lateinit var mockAppVersionProvider: AppVersionProvider // region CoreFeatureTestConfiguration @@ -85,6 +87,7 @@ internal class CoreFeatureTestConfiguration( mockUserInfoProvider = mock() mockAndroidInfoProvider = mock() mockTrackingConsentProvider = mock { on { getConsent() } doReturn TrackingConsent.PENDING } + mockAppVersionProvider = mock { on { version } doReturn appContext.fakeVersionName } } private fun configureCoreFeature() { @@ -93,7 +96,7 @@ internal class CoreFeatureTestConfiguration( CoreFeature.envName = fakeEnvName CoreFeature.serviceName = fakeServiceName CoreFeature.packageName = appContext.fakePackageName - CoreFeature.packageVersion = appContext.fakeVersionName + CoreFeature.packageVersionProvider = mockAppVersionProvider CoreFeature.variant = appContext.fakeVariant CoreFeature.rumApplicationId = fakeRumApplicationId CoreFeature.sourceName = fakeSourceName diff --git a/dd-sdk-android/src/test/kotlin/com/datadog/android/webview/internal/log/WebViewLogEventConsumerTest.kt b/dd-sdk-android/src/test/kotlin/com/datadog/android/webview/internal/log/WebViewLogEventConsumerTest.kt index 7cd4275ba4..892be111a9 100644 --- a/dd-sdk-android/src/test/kotlin/com/datadog/android/webview/internal/log/WebViewLogEventConsumerTest.kt +++ b/dd-sdk-android/src/test/kotlin/com/datadog/android/webview/internal/log/WebViewLogEventConsumerTest.kt @@ -8,6 +8,7 @@ package com.datadog.android.webview.internal.log import com.datadog.android.core.internal.CoreFeature import com.datadog.android.core.internal.persistence.DataWriter +import com.datadog.android.core.internal.system.AppVersionProvider import com.datadog.android.core.internal.time.TimeProvider import com.datadog.android.log.LogAttributes import com.datadog.android.log.internal.utils.ERROR_WITH_TELEMETRY_LEVEL @@ -24,6 +25,7 @@ import com.google.gson.JsonArray import com.google.gson.JsonObject import com.nhaarman.mockitokotlin2.argThat import com.nhaarman.mockitokotlin2.argumentCaptor +import com.nhaarman.mockitokotlin2.doReturn import com.nhaarman.mockitokotlin2.eq import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.verifyZeroInteractions @@ -63,6 +65,9 @@ internal class WebViewLogEventConsumerTest { @Mock lateinit var mockRumContextProvider: WebViewRumEventContextProvider + @Mock + lateinit var mockAppVersionProvider: AppVersionProvider + @StringForgery(regex = "[0-9]\\.[0-9]\\.[0-9]") lateinit var fakePackageVersion: String @@ -83,14 +88,15 @@ internal class WebViewLogEventConsumerTest { fakeWebLogEvent = forge.aWebLogEvent() fakeTimeOffset = forge.aLong() CoreFeature.envName = fakeEnvName - CoreFeature.packageVersion = fakePackageVersion testedConsumer = WebViewLogEventConsumer( mockUserLogsWriter, mockRumContextProvider, - mockTimeProvider + mockTimeProvider, + mockAppVersionProvider ) - whenever(mockTimeProvider.getServerOffsetMillis()).thenReturn(fakeTimeOffset) + whenever(mockTimeProvider.getServerOffsetMillis()) doReturn fakeTimeOffset + whenever(mockAppVersionProvider.version) doReturn fakePackageVersion } @Test diff --git a/detekt.yml b/detekt.yml index c2cd9c289d..c4e294df77 100644 --- a/detekt.yml +++ b/detekt.yml @@ -920,9 +920,11 @@ datadog: - "java.util.concurrent.atomic.AtomicReference.compareAndSet(io.opentracing.Tracer, io.opentracing.Tracer)" - "java.util.concurrent.atomic.AtomicReference.constructor()" - "java.util.concurrent.atomic.AtomicReference.constructor(com.datadog.android.rum.internal.domain.RumContext)" + - "java.util.concurrent.atomic.AtomicReference.constructor(kotlin.String)" - "java.util.concurrent.atomic.AtomicReference.get()" - "java.util.concurrent.atomic.AtomicReference.set(com.datadog.android.rum.internal.domain.RumContext)" - "java.util.concurrent.atomic.AtomicReference.set(io.opentracing.Tracer)" + - "java.util.concurrent.atomic.AtomicReference.set(kotlin.String)" # endregion # region Java I/O - "java.io.File.constructor(java.io.File, kotlin.String)"