From 2a61ca61dfe3a138e6e40bdf019c386dc8b3202e Mon Sep 17 00:00:00 2001 From: Marius Constantin Date: Wed, 7 Aug 2024 09:47:51 +0200 Subject: [PATCH] RUM-5527 Add integration tests for internal sdk core --- .../integration/InternalSdkCoreContextTest.kt | 625 ++++++++++++++++++ .../core/integration/SdkCoreContextTest.kt | 42 +- .../tests/utils/ConfigurationExt.kt | 50 ++ .../integration/tests/utils/MutableMapExt.kt | 22 + .../core-it/src/main/assets/datadog.buildId | 1 + 5 files changed, 719 insertions(+), 21 deletions(-) create mode 100644 reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/InternalSdkCoreContextTest.kt create mode 100644 reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/utils/ConfigurationExt.kt create mode 100644 reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/utils/MutableMapExt.kt create mode 100644 reliability/core-it/src/main/assets/datadog.buildId diff --git a/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/InternalSdkCoreContextTest.kt b/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/InternalSdkCoreContextTest.kt new file mode 100644 index 0000000000..d10182886a --- /dev/null +++ b/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/InternalSdkCoreContextTest.kt @@ -0,0 +1,625 @@ +/* + * 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.integration + +import android.content.Context +import android.content.pm.ApplicationInfo +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import android.os.Build +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.datadog.android.BuildConfig +import com.datadog.android.Datadog +import com.datadog.android.DatadogSite +import com.datadog.android._InternalProxy +import com.datadog.android.api.context.DeviceType +import com.datadog.android.api.context.NetworkInfo +import com.datadog.android.api.feature.Feature +import com.datadog.android.api.feature.stub.StubStorageBackedFeature +import com.datadog.android.core.InternalSdkCore +import com.datadog.android.core.configuration.Configuration +import com.datadog.android.core.integration.tests.MockServerTest +import com.datadog.android.core.integration.tests.forge.factories.ConfigurationCoreForgeryFactory +import com.datadog.android.core.integration.tests.utils.clientToken +import com.datadog.android.core.integration.tests.utils.env +import com.datadog.android.core.integration.tests.utils.getFirstPartyHostsWithHeaderTypes +import com.datadog.android.core.integration.tests.utils.isCrashReportsEnabled +import com.datadog.android.core.integration.tests.utils.isDeveloperModeEnabled +import com.datadog.android.core.integration.tests.utils.removeRandomEntries +import com.datadog.android.core.integration.tests.utils.service +import com.datadog.android.core.integration.tests.utils.site +import com.datadog.android.core.integration.tests.utils.variant +import com.datadog.android.core.thread.FlushableExecutorService +import com.datadog.android.privacy.TrackingConsent +import com.datadog.android.trace.TracingHeaderType +import com.datadog.tools.unit.forge.exhaustiveAttributes +import com.datadog.tools.unit.forge.useToolsFactories +import fr.xgouchet.elmyr.annotation.Forgery +import fr.xgouchet.elmyr.annotation.StringForgery +import fr.xgouchet.elmyr.annotation.StringForgeryType +import fr.xgouchet.elmyr.junit4.ForgeRule +import fr.xgouchet.elmyr.jvm.useJvmFactories +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.data.Offset +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import java.io.File +import java.util.UUID +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit + +/** + * Provides the tests for the InternalSdkCore API not related with the writing operations. + */ +@RunWith(AndroidJUnit4::class) +class InternalSdkCoreContextTest : MockServerTest() { + + @get:Rule + var forge = ForgeRule().useJvmFactories().useToolsFactories().withFactory(ConfigurationCoreForgeryFactory()) + + @StringForgery(type = StringForgeryType.ALPHABETICAL) + lateinit var fakeUserId: String + + @StringForgery(regex = "[A-Z][a-z]+ [A-Z]\\. [A-Z][a-z]+") + lateinit var fakeUserName: String + + @StringForgery(regex = "[a-z]+\\.[a-z]+@[a-z]+\\.[a-z]{3}") + lateinit var fakeUserEmail: String + private var fakeUserAdditionalProperties: Map = emptyMap() + private lateinit var stubFeature: Feature + + @StringForgery(type = StringForgeryType.ALPHABETICAL) + lateinit var fakeFeatureName: String + + private lateinit var fakeTrackingConsent: TrackingConsent + + private lateinit var testedInternalSdkCore: InternalSdkCore + + @Forgery + lateinit var fakeConfiguration: Configuration + private var currentDeviceTimeInMs: Long = 0L + + @Before + fun setUp() { + currentDeviceTimeInMs = System.currentTimeMillis() + stubFeature = StubStorageBackedFeature( + forge, + fakeFeatureName, + getMockServerWrapper().getServerUrl() + ) + fakeTrackingConsent = forge.aValueFrom(TrackingConsent::class.java) + fakeUserAdditionalProperties = forge.exhaustiveAttributes(excludedKeys = setOf("id", "name", "email")) + testedInternalSdkCore = Datadog.initialize( + ApplicationProvider.getApplicationContext(), + fakeConfiguration, + fakeTrackingConsent + ) as InternalSdkCore + Datadog.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeUserAdditionalProperties) + testedInternalSdkCore.registerFeature(stubFeature) + } + + @After + fun tearDown() { + Datadog.stopInstance() + } + + // region get Datadog Context + + @Test + fun must_returnCorrectDatadogContext_when_getDatadogContext() { + // Given + val expectedServiceName = fakeConfiguration.service() ?: PACKAGE_NAME + + // When + val context = testedInternalSdkCore.getDatadogContext() + + // Then + checkNotNull(context) + assertThat(context.sdkVersion).isEqualTo(BuildConfig.SDK_VERSION_NAME) + assertThat(context.clientToken).isEqualTo(fakeConfiguration.clientToken()) + assertThat(context.env).isEqualTo(fakeConfiguration.env()) + assertThat(context.service).isEqualTo(expectedServiceName) + assertThat(context.variant).isEqualTo(fakeConfiguration.variant()) + assertThat(context.site).isEqualTo(fakeConfiguration.site()) + assertThat(context.trackingConsent).isEqualTo(fakeTrackingConsent) + assertThat(context.version).isEqualTo(getAppVersion()) + assertThat(context.appBuildId).isEqualTo(BUILD_ID) + assertThat(context.source).isEqualTo(ANDROID_SOURCE) + assertThat(context.processInfo.isMainProcess).isTrue() + assertThat(context.networkInfo.connectivity).isEqualTo(NetworkInfo.Connectivity.NETWORK_WIFI) + assertThat(context.networkInfo.carrierName).isNull() + assertThat(context.deviceInfo.deviceName).isNotEmpty() + assertThat(context.deviceInfo.deviceBrand).isNotEmpty() + assertThat(context.deviceInfo.deviceType).isEqualTo(DeviceType.MOBILE) + assertThat(context.deviceInfo.deviceModel).isNotEmpty() + assertThat(context.time.serverTimeNs).isGreaterThan(0) + assertThat(context.time.deviceTimeNs).isCloseTo( + TimeUnit.MILLISECONDS.toNanos(currentDeviceTimeInMs), + Offset.offset(DEVICE_TIME_OFFSET_NS) + ) + assertThat(context.userInfo.id).isEqualTo(fakeUserId) + assertThat(context.userInfo.name).isEqualTo(fakeUserName) + assertThat(context.userInfo.email).isEqualTo(fakeUserEmail) + assertThat(context.userInfo.additionalProperties) + .containsExactlyInAnyOrderEntriesOf(fakeUserAdditionalProperties) + assertThat(context.featuresContext).isEmpty() + } + + @Test + fun mustReturnTheUpdatedFeatureContext_getDatadogContext_featureContextWasSet() { + // Given + val fakeKeyValues = forge.aMap { forge.anAlphabeticalString() to forge.anAlphabeticalString() } + testedInternalSdkCore.updateFeatureContext(fakeFeatureName) { + fakeKeyValues.forEach { (key, value) -> + it[key] = value + } + } + + // When + val context = testedInternalSdkCore.getDatadogContext() + + // Then + checkNotNull(context) + assertThat(context.featuresContext.entries).hasSize(1) + assertThat(context.featuresContext[fakeFeatureName]).containsExactlyInAnyOrderEntriesOf(fakeKeyValues) + } + + @Test + fun mustUseAtomicOperations_when_updateFeatureContext_addNewValues() { + // Given + val fakeKeyValues1 = forge.aMap( + size = forge.anInt( + min = 1, + max = 10 + ) + ) { forge.anAlphabeticalString() to forge.anAlphabeticalString() } + val fakeKeyValues2 = forge.aMap( + size = forge.anInt( + min = 1, + max = 10 + ) + ) { forge.anAlphabeticalString() to forge.anAlphabeticalString() } + val expectedKeyValues = fakeKeyValues1 + fakeKeyValues2 + val countDownLatch = CountDownLatch(2) + testedInternalSdkCore.setContextUpdateReceiver(fakeFeatureName) { _, _ -> countDownLatch.countDown() } + Thread { + testedInternalSdkCore.updateFeatureContext(fakeFeatureName) { + fakeKeyValues1.forEach { (key, value) -> + it[key] = value + } + } + }.start() + Thread { + testedInternalSdkCore.updateFeatureContext(fakeFeatureName) { + fakeKeyValues2.forEach { (key, value) -> + it[key] = value + } + } + }.start() + countDownLatch.await(SHORT_WAIT_MS, TimeUnit.MILLISECONDS) + + // When + val context = testedInternalSdkCore.getDatadogContext() + + // Then + checkNotNull(context) + assertThat(context.featuresContext.entries).hasSize(1) + val featureContext = context.featuresContext[fakeFeatureName] + assertThat(featureContext).containsExactlyInAnyOrderEntriesOf(expectedKeyValues) + } + + @Test + fun mustUseAtomicOperations_when_updateFeatureContext_modifyValues() { + // Given + val fakeKeyValues1 = forge.aMap( + size = forge.anInt( + min = 1, + max = 10 + ) + ) { forge.anAlphabeticalString() to forge.anAlphabeticalString() } + val fakeKeyValues2 = forge.aMap( + size = forge.anInt( + min = 1, + max = 10 + ) + ) { forge.anAlphabeticalString() to forge.anAlphabeticalString() } + val fakeModifiedValues = fakeKeyValues2.mapValues { (_, _) -> forge.anAlphabeticalString() } + val expectedKeyValues = fakeKeyValues1 + fakeModifiedValues + val countDownLatch = CountDownLatch(3) + testedInternalSdkCore.setContextUpdateReceiver(fakeFeatureName) { _, _ -> countDownLatch.countDown() } + Thread { + testedInternalSdkCore.updateFeatureContext(fakeFeatureName) { + fakeKeyValues1.forEach { (key, value) -> + it[key] = value + } + } + }.start() + Thread { + testedInternalSdkCore.updateFeatureContext(fakeFeatureName) { + fakeKeyValues2.forEach { (key, value) -> + it[key] = value + } + } + }.start() + // Give some time to the first 2 threads to start + Thread.sleep(100) + Thread { + testedInternalSdkCore.updateFeatureContext(fakeFeatureName) { + fakeKeyValues2.forEach { (key, _) -> + it[key] = fakeModifiedValues[key] + } + } + }.start() + countDownLatch.await(SHORT_WAIT_MS, TimeUnit.MILLISECONDS) + + // When + val context = testedInternalSdkCore.getDatadogContext() + + // Then + checkNotNull(context) + assertThat(context.featuresContext.entries).hasSize(1) + val featureContext = context.featuresContext[fakeFeatureName] + assertThat(featureContext).containsExactlyInAnyOrderEntriesOf(expectedKeyValues) + } + + @Test + fun mustUseAtomicOperations_when_updateFeatureContext_removeValues() { + // Given + val fakeKeyValues1 = forge.aMap( + size = forge.anInt( + min = 1, + max = 10 + ) + ) { forge.anAlphabeticalString() to forge.anAlphabeticalString() } + val fakeKeyValues2 = forge.aMap( + size = forge.anInt( + min = 1, + max = 10 + ) + ) { forge.anAlphabeticalString() to forge.anAlphabeticalString() } + val expectedKeyValues = (fakeKeyValues1 + fakeKeyValues2).toMutableMap() + val droppedKeyValues = expectedKeyValues.removeRandomEntries(forge) + val countDownLatch = CountDownLatch(3) + testedInternalSdkCore.setContextUpdateReceiver(fakeFeatureName) { _, _ -> countDownLatch.countDown() } + Thread { + testedInternalSdkCore.updateFeatureContext(fakeFeatureName) { + fakeKeyValues1.forEach { (key, value) -> + it[key] = value + } + } + }.start() + Thread { + testedInternalSdkCore.updateFeatureContext(fakeFeatureName) { + fakeKeyValues2.forEach { (key, value) -> + it[key] = value + } + } + }.start() + // give some time to the firs 2 threads to start + Thread.sleep(100) + Thread { + testedInternalSdkCore.updateFeatureContext(fakeFeatureName) { + droppedKeyValues.forEach { (key, _) -> + it.remove(key) + } + } + }.start() + countDownLatch.await(SHORT_WAIT_MS, TimeUnit.MILLISECONDS) + + // When + val context = testedInternalSdkCore.getDatadogContext() + + // Then + checkNotNull(context) + assertThat(context.featuresContext.entries).hasSize(1) + val featureContext = context.featuresContext[fakeFeatureName] + assertThat(featureContext).containsExactlyInAnyOrderEntriesOf(expectedKeyValues) + } + + // endregion + + // region get Network Info + + @Test + fun mustReturnTheCorrectNetworkInfo_when_getNetworkInfo() { + // Given + val expectedConnectivity = NetworkInfo.Connectivity.NETWORK_WIFI + + // When + val networkInfo = testedInternalSdkCore.networkInfo + + // Then + assertThat(networkInfo.connectivity).isEqualTo(expectedConnectivity) + assertThat(networkInfo.carrierName).isNull() + assertThat(networkInfo.carrierId).isNull() + assertThat(networkInfo.upKbps).isGreaterThan(0) + assertThat(networkInfo.downKbps).isGreaterThan(0) + assertThat(networkInfo.strength).isNotNull() + assertThat(networkInfo.cellularTechnology).isNull() + } + + // endregion + + // region Tracking consent + + @Test + fun mustReturnTheCorrectTrackingConsent_when_getTrackingConsent() { + // When + val trackingConsent = testedInternalSdkCore.trackingConsent + + // Then + assertThat(trackingConsent).isEqualTo(fakeTrackingConsent) + } + + @Test + fun mustReturnTheCorrectTrackingConsent_when_getTrackingConsent_trackingConsentChanged() { + // Given + val newFakeTrackingConsent = forge.aValueFrom(TrackingConsent::class.java) + Datadog.setTrackingConsent(newFakeTrackingConsent) + + // When + val trackingConsent = testedInternalSdkCore.trackingConsent + + // Then + assertThat(trackingConsent).isEqualTo(newFakeTrackingConsent) + } + + // endregion + + // root storage dir + + @Test + fun mustReturnTheCorrectRootStorageDir_when_getRootStorageDir() { + // Given + val expectedStorageDirFormat = ApplicationProvider.getApplicationContext().cacheDir.path + + File.separator + + DATADOG_STORAGE_DIR_NAME_FORMAT + + // When + val rootStorageDir = testedInternalSdkCore.rootStorageDir + val rootStorageDirPath = rootStorageDir?.path + + // Then + assertThat(rootStorageDir).isNotNull() + assertThat(rootStorageDirPath).matches(expectedStorageDirFormat) + } + + // endregion + + // region isDeveloperModeEnabled + + @Test + fun mustReturnCorrectIsDeveloperMode_when_isDeveloperModeEnabled() { + // Given + val expectedDeveloperMode = isAppDebuggable(ApplicationProvider.getApplicationContext()) && + fakeConfiguration.isDeveloperModeEnabled() + + // When + val isDeveloperModeEnabled = testedInternalSdkCore.isDeveloperModeEnabled + + // Then + assertThat(isDeveloperModeEnabled).isEqualTo(expectedDeveloperMode) + } + + @Test + fun mustReturnFalse_when_isDeveloperModeEnabled_isDeveloperModeDisabledInConfig() { + // Given + val fakeConfigDeveloperModeDisabled = Configuration.Builder( + UUID.randomUUID().toString(), + forge.anHexadecimalString(), + forge.anHexadecimalString(), + forge.aNullable { + anAlphaNumericalString() + } + ) + .setUseDeveloperModeWhenDebuggable(false) + .setFirstPartyHostsWithHeaderType( + forge.aMap { + val fakeUrl = forge.aStringMatching("https://[a-z0-9]+\\.com") + fakeUrl to aList { + aValueFrom( + TracingHeaderType::class.java + ) + }.toSet() + } + ) + .apply { + _InternalProxy.allowClearTextHttp(this) + } + .setBatchSize(forge.getForgery()) + .setUploadFrequency(forge.getForgery()) + .useSite(forge.aValueFrom(DatadogSite::class.java)) + .build() + + // When + // stop the current instance + Datadog.stopInstance() + Datadog.initialize( + ApplicationProvider.getApplicationContext(), + fakeConfigDeveloperModeDisabled, + fakeTrackingConsent + ) + + // When + val isDeveloperModeEnabled = testedInternalSdkCore.isDeveloperModeEnabled + + // Then + assertThat(isDeveloperModeEnabled).isEqualTo(false) + } + + // endregion + + // region FirstPartyHostResolver + + @Test + fun mustReturnTheCorrectFirstPartyHostResolver_when_getFirstPartyHostResolver() { + // Given + val expectedFirstPartyHostsWithHeaderTypes = fakeConfiguration.getFirstPartyHostsWithHeaderTypes() + + // When + val firstPartyHostResolver = testedInternalSdkCore.firstPartyHostResolver + + // Then + assertThat(firstPartyHostResolver).isNotNull() + expectedFirstPartyHostsWithHeaderTypes.forEach { (host, headerTypes) -> + assertThat(firstPartyHostResolver.isFirstPartyUrl(host)).isTrue() + assertThat(firstPartyHostResolver.headerTypesForUrl(host)) + .containsExactlyInAnyOrder(*headerTypes.toTypedArray()) + } + } + + // endregion + + // region App Start Time + + @Test + fun mustReturnTheCorrectAppStartTime_when_appStartTimeNs() { + // When + val appStartTimeNs = testedInternalSdkCore.appStartTimeNs + + // Then + assertThat(appStartTimeNs).isCloseTo( + APPROXIMATE_APP_START_TIME_NS, + Offset.offset(APP_START_TIME_OFFSET_NS) + ) + } + + // endregion + + // region Persistence Executor + + @Test + fun mustReturnTheCorrectPersistenceExecutor_when_persistenceExecutor() { + // When + val persistenceExecutor = testedInternalSdkCore.getPersistenceExecutorService() + + // Then + assertThat(persistenceExecutor.isShutdown).isFalse() + assertThat(persistenceExecutor.isTerminated).isFalse() + assertThat(persistenceExecutor).isInstanceOf(FlushableExecutorService::class.java) + } + + // endregion + + // region Get All Features + + @Test + fun mustReturnAllTheRegisteredFeatures_when_getAllFeatures_newFeaturesRegistered() { + // Given + val fakeFeatures = forge.aList(size = 2) { + StubStorageBackedFeature( + forge, + forge.anAlphabeticalString(), + getMockServerWrapper().getServerUrl() + ) + }.onEach { testedInternalSdkCore.registerFeature(it) } + val expectedRegisteredFeatures = fakeFeatures + stubFeature + val expectedRegisteredFeaturesSize = if (fakeConfiguration.isCrashReportsEnabled()) { + // + 1 for the Crash Report feature + expectedRegisteredFeatures.size + 1 + } else { + expectedRegisteredFeatures.size + } + + // When + val features = testedInternalSdkCore.getAllFeatures().map { it.unwrap() } + + // Then + assertThat(features).containsAll(expectedRegisteredFeatures) + assertThat(features.size).isEqualTo(expectedRegisteredFeaturesSize) + } + + @Test + fun mustReturnTheCrashReportFeature_when_getAllFeatures_crashReportEnabled() { + // Given + val fakeConfigCrashReportsEnabled = fakeConfiguration.copy(crashReportsEnabled = true) + + // When + // stop the current instance + Datadog.stopInstance() + val internalSdkCore = Datadog.initialize( + ApplicationProvider.getApplicationContext(), + fakeConfigCrashReportsEnabled, + fakeTrackingConsent + ) as InternalSdkCore + + // When + val features = internalSdkCore.getAllFeatures().map { it.unwrap() } + + // Then + assertThat(features).isNotEmpty + assertThat(features).anyMatch { it.name == "crash" } + } + + @Test + fun mustReturnEmptyFeatures_when_getAllFeatures_crashReportNotEnabled() { + // Given + val fakeConfigCrashReportsNotEnabled = fakeConfiguration.copy(crashReportsEnabled = false) + + // When + // stop the current instance + Datadog.stopInstance() + val internalSdkCore = Datadog.initialize( + ApplicationProvider.getApplicationContext(), + fakeConfigCrashReportsNotEnabled, + fakeTrackingConsent + ) as InternalSdkCore + + // When + val features = internalSdkCore.getAllFeatures().map { it.unwrap() } + + // Then + assertThat(features).isEmpty() + } + + // endregion + + // region internal + + private fun getAppVersion(): String? { + return getPackageInfo(ApplicationProvider.getApplicationContext())?.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() + } + } + + private fun getPackageInfo(appContext: Context): PackageInfo? { + return try { + with(appContext.packageManager) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + getPackageInfo(appContext.packageName, PackageManager.PackageInfoFlags.of(0)) + } else { + getPackageInfo(appContext.packageName, 0) + } + } + } catch (e: PackageManager.NameNotFoundException) { + null + } + } + + private fun isAppDebuggable(context: Context): Boolean { + return (context.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0 + } + + // endregion + + companion object { + private val APP_START_TIME_OFFSET_NS = TimeUnit.SECONDS.toNanos(1) + private val DEVICE_TIME_OFFSET_NS = TimeUnit.SECONDS.toNanos(1) + private val APPROXIMATE_APP_START_TIME_NS = System.nanoTime() + private const val ANDROID_SOURCE = "android" + private const val BUILD_ID = "core_it_build_id" + private const val PACKAGE_NAME = "com.datadog.android.core.integration.test" + internal const val DATADOG_STORAGE_DIR_NAME_FORMAT = "datadog-(.*)" + } +} diff --git a/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/SdkCoreContextTest.kt b/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/SdkCoreContextTest.kt index 874f1d7ef3..b72d39f5b2 100644 --- a/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/SdkCoreContextTest.kt +++ b/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/SdkCoreContextTest.kt @@ -60,7 +60,7 @@ class SdkCoreContextTest : MockServerTest() { private lateinit var fakeTrackingConsent: TrackingConsent private var featureSdkCore: FeatureSdkCore? = null - private var sdkCore: SdkCore? = null + private var testedSdkCore: SdkCore? = null @Before fun setUp() { @@ -72,12 +72,12 @@ class SdkCoreContextTest : MockServerTest() { fakeTrackingConsent = forge.aValueFrom(TrackingConsent::class.java) fakeUserAdditionalProperties = forge.exhaustiveAttributes(excludedKeys = setOf("id", "name", "email")) val configuration: Configuration = forge.getForgery() - sdkCore = Datadog.initialize( + testedSdkCore = Datadog.initialize( ApplicationProvider.getApplicationContext(), configuration, fakeTrackingConsent ) - featureSdkCore = sdkCore as? FeatureSdkCore + featureSdkCore = testedSdkCore as? FeatureSdkCore featureSdkCore?.registerFeature(stubFeature) } @@ -91,7 +91,7 @@ class SdkCoreContextTest : MockServerTest() { @Test fun must_addUserInformationIntoEvents_when_setUserInformation() { // When - sdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeUserAdditionalProperties) + testedSdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeUserAdditionalProperties) // Then val countDownLatch = CountDownLatch(1) @@ -115,11 +115,11 @@ class SdkCoreContextTest : MockServerTest() { @Test fun must_addUserExtraProperties_when_addUserProperties() { // Given - sdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeUserAdditionalProperties) + testedSdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeUserAdditionalProperties) val expectedUserExtraProperties = forge.exhaustiveAttributes() // When - sdkCore?.addUserProperties(expectedUserExtraProperties) + testedSdkCore?.addUserProperties(expectedUserExtraProperties) // Then val countDownLatch = CountDownLatch(1) @@ -141,7 +141,7 @@ class SdkCoreContextTest : MockServerTest() { // Given val fakeMutableProperties = forge.exhaustiveAttributes() val expectedMutableProperties = fakeMutableProperties.toMap() - sdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeMutableProperties) + testedSdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeMutableProperties) // When fakeMutableProperties.keys.forEach { key -> @@ -168,7 +168,7 @@ class SdkCoreContextTest : MockServerTest() { // Given val fakeMutableProperties = forge.exhaustiveAttributes() val expectedMutableProperties = fakeMutableProperties.toMap() - sdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeMutableProperties) + testedSdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeMutableProperties) // When repeat(forge.anInt(1, 10)) { @@ -195,7 +195,7 @@ class SdkCoreContextTest : MockServerTest() { // Given val fakeMutableProperties = forge.exhaustiveAttributes() val expectedMutableProperties = fakeMutableProperties.toMap() - sdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeMutableProperties) + testedSdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeMutableProperties) // When repeat(forge.anInt(1, 10)) { @@ -222,8 +222,8 @@ class SdkCoreContextTest : MockServerTest() { // Given val fakeExtraProperties = forge.exhaustiveAttributes() val expectedExtraProperties = fakeExtraProperties.toMap() - sdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeUserAdditionalProperties) - sdkCore?.addUserProperties(fakeExtraProperties) + testedSdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeUserAdditionalProperties) + testedSdkCore?.addUserProperties(fakeExtraProperties) // When fakeExtraProperties.keys.forEach { key -> @@ -250,8 +250,8 @@ class SdkCoreContextTest : MockServerTest() { // Given val fakeExtraProperties = forge.exhaustiveAttributes() val expectedExtraProperties = fakeExtraProperties.toMap() - sdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeUserAdditionalProperties) - sdkCore?.addUserProperties(fakeExtraProperties) + testedSdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeUserAdditionalProperties) + testedSdkCore?.addUserProperties(fakeExtraProperties) // When repeat(forge.anInt(1, 10)) { @@ -278,8 +278,8 @@ class SdkCoreContextTest : MockServerTest() { // Given val fakeExtraProperties = forge.exhaustiveAttributes() val expectedExtraProperties = fakeExtraProperties.toMap() - sdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeUserAdditionalProperties) - sdkCore?.addUserProperties(fakeExtraProperties) + testedSdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeUserAdditionalProperties) + testedSdkCore?.addUserProperties(fakeExtraProperties) // When repeat(forge.anInt(1, 10)) { @@ -304,16 +304,16 @@ class SdkCoreContextTest : MockServerTest() { @Test fun must_resetUserProperties_when_setUserPropertiesCalledSecondTime() { // Given - sdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeUserAdditionalProperties) + testedSdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeUserAdditionalProperties) val expectedUserExtraProperties = forge.exhaustiveAttributes() - sdkCore?.addUserProperties(expectedUserExtraProperties) + testedSdkCore?.addUserProperties(expectedUserExtraProperties) val fakeUserId2 = forge.anAlphabeticalString() val fakeUserName2 = forge.anAlphabeticalString() val fakeUserEmail2 = forge.anAlphabeticalString() val fakeUserAdditionalProperties2 = forge.exhaustiveAttributes() // When - sdkCore?.setUserInfo(fakeUserId2, fakeUserName2, fakeUserEmail2, fakeUserAdditionalProperties2) + testedSdkCore?.setUserInfo(fakeUserId2, fakeUserName2, fakeUserEmail2, fakeUserAdditionalProperties2) // Then val countDownLatch = CountDownLatch(1) @@ -334,10 +334,10 @@ class SdkCoreContextTest : MockServerTest() { fun must_resetUserProperties_when_setUserPropertiesCalled_afterAddUserProperties() { // Given val expectedUserExtraProperties = forge.exhaustiveAttributes() - sdkCore?.addUserProperties(expectedUserExtraProperties) + testedSdkCore?.addUserProperties(expectedUserExtraProperties) // When - sdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeUserAdditionalProperties) + testedSdkCore?.setUserInfo(fakeUserId, fakeUserName, fakeUserEmail, fakeUserAdditionalProperties) // Then val countDownLatch = CountDownLatch(1) @@ -376,7 +376,7 @@ class SdkCoreContextTest : MockServerTest() { val expectedTrackingConsent = forge.aValueFrom(TrackingConsent::class.java) // When - sdkCore?.setTrackingConsent(expectedTrackingConsent) + testedSdkCore?.setTrackingConsent(expectedTrackingConsent) // Then val countDownLatch = CountDownLatch(1) diff --git a/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/utils/ConfigurationExt.kt b/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/utils/ConfigurationExt.kt new file mode 100644 index 0000000000..80cce265ff --- /dev/null +++ b/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/utils/ConfigurationExt.kt @@ -0,0 +1,50 @@ +/* + * 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.integration.tests.utils + +import com.datadog.android.DatadogSite +import com.datadog.android.core.configuration.Configuration +import com.datadog.android.trace.TracingHeaderType +import com.datadog.tools.unit.getFieldValue + +fun Configuration.site(): DatadogSite { + return this.getFieldValue("coreConfig").getFieldValue("site") +} + +fun Configuration.clientToken(): String { + return this.getFieldValue("clientToken") +} + +fun Configuration.env(): String { + return this.getFieldValue("env") +} + +fun Configuration.variant(): String { + return this.getFieldValue("variant") +} + +fun Configuration.service(): String? { + return this.getFieldValue("service") +} + +fun Configuration.isCrashReportsEnabled(): Boolean { + return this.getFieldValue("crashReportsEnabled") +} + +fun Configuration.getFirstPartyHostsWithHeaderTypes(): Map> { + return this.getFieldValue("coreConfig") + .getFieldValue("firstPartyHostsWithHeaderTypes") +} + +fun Configuration.isDeveloperModeEnabled(): Boolean { + return this.getFieldValue("coreConfig") + .getFieldValue("enableDeveloperModeWhenDebuggable") +} + +fun Configuration.additionalConfig(): Map { + return this.getFieldValue, Configuration>("additionalConfig") +} diff --git a/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/utils/MutableMapExt.kt b/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/utils/MutableMapExt.kt new file mode 100644 index 0000000000..24ae43fa2e --- /dev/null +++ b/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/utils/MutableMapExt.kt @@ -0,0 +1,22 @@ +/* + * 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.integration.tests.utils + +import fr.xgouchet.elmyr.Forge + +internal fun MutableMap.removeRandomEntries(forge: Forge): Map { + val removedEntries = mutableMapOf() + val keys = this.keys.toList() + val numberOfElementsToRemove = forge.anInt(1, keys.size) + repeat(numberOfElementsToRemove) { + val randomIndex = keys.indices.random() + val key = keys[randomIndex] + this.remove(key)?.let { removedEntries[key] = it } + } + + return removedEntries +} diff --git a/reliability/core-it/src/main/assets/datadog.buildId b/reliability/core-it/src/main/assets/datadog.buildId new file mode 100644 index 0000000000..4fe161c26f --- /dev/null +++ b/reliability/core-it/src/main/assets/datadog.buildId @@ -0,0 +1 @@ +core_it_build_id \ No newline at end of file