From 9720e9c81aca11c6d2e74f5f365f0a350fac7bd7 Mon Sep 17 00:00:00 2001 From: Nikita Ogorodnikov Date: Wed, 26 Oct 2022 16:22:41 +0200 Subject: [PATCH] RUMM-2608: Use event write context for traces --- .../datadog/android/tracing/AndroidTracer.kt | 9 +- .../tracing/internal/TracingFeature.kt | 41 ++- .../tracing/internal/data/NoOpWriter.kt | 28 ++ .../tracing/internal/data/TraceWriter.kt | 58 +++- .../domain/TracesFilePersistenceStrategy.kt | 42 --- .../domain/event/DdSpanToSpanEventMapper.kt | 30 +- .../domain/event/SpanEventSerializer.kt | 10 +- .../datadog/android/v2/core/DatadogCore.kt | 2 +- .../internal/storage/ContextAwareMapper.kt | 14 + .../storage/ContextAwareSerializer.kt | 20 ++ .../DatadogInterceptorWithoutTracesTest.kt | 4 +- .../TracingInterceptorNotSendingSpanTest.kt | 4 +- .../tracing/assertj/SpanEventAssert.kt | 4 +- .../tracing/internal/AndroidTracerTest.kt | 31 +- .../tracing/internal/TracingFeatureTest.kt | 44 +-- .../tracing/internal/data/TraceWriterTest.kt | 271 +++++++++++++----- .../event/DdSpanToSpanEventMapperTest.kt | 80 +----- .../domain/event/SpanEventSerializerTest.kt | 16 +- 18 files changed, 402 insertions(+), 306 deletions(-) create mode 100644 dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/data/NoOpWriter.kt delete mode 100644 dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/domain/TracesFilePersistenceStrategy.kt create mode 100644 dd-sdk-android/src/main/kotlin/com/datadog/android/v2/core/internal/storage/ContextAwareMapper.kt create mode 100644 dd-sdk-android/src/main/kotlin/com/datadog/android/v2/core/internal/storage/ContextAwareSerializer.kt diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/AndroidTracer.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/AndroidTracer.kt index 5cb348bebd..c8b60df3f9 100644 --- a/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/AndroidTracer.kt +++ b/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/AndroidTracer.kt @@ -7,17 +7,17 @@ package com.datadog.android.tracing import com.datadog.android.Datadog -import com.datadog.android.core.internal.persistence.NoOpDataWriter import com.datadog.android.core.internal.utils.devLogger import com.datadog.android.log.LogAttributes import com.datadog.android.log.Logger import com.datadog.android.rum.GlobalRum -import com.datadog.android.tracing.internal.data.TraceWriter +import com.datadog.android.tracing.internal.data.NoOpWriter import com.datadog.android.tracing.internal.handlers.AndroidSpanLogsHandler import com.datadog.android.v2.core.DatadogCore import com.datadog.opentracing.DDTracer import com.datadog.opentracing.LogHandler import com.datadog.trace.api.Config +import com.datadog.trace.common.writer.Writer import io.opentracing.Span import io.opentracing.log.Fields import java.security.SecureRandom @@ -34,7 +34,7 @@ import java.util.Random */ class AndroidTracer internal constructor( config: Config, - writer: TraceWriter, + writer: Writer, random: Random, private val logsHandler: LogHandler, private val bundleWithRum: Boolean @@ -95,10 +95,9 @@ class AndroidTracer internal constructor( devLogger.e(RUM_NOT_ENABLED_ERROR_MESSAGE) bundleWithRumEnabled = false } - val writer = tracingFeature?.persistenceStrategy?.getWriter() ?: NoOpDataWriter() return AndroidTracer( config(), - TraceWriter(writer), + tracingFeature?.dataWriter ?: NoOpWriter(), random, logsHandler, bundleWithRumEnabled 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 319c7c9980..2b427c838b 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 @@ -7,47 +7,44 @@ package com.datadog.android.tracing.internal import com.datadog.android.core.configuration.Configuration -import com.datadog.android.core.internal.CoreFeature -import com.datadog.android.core.internal.persistence.NoOpPersistenceStrategy -import com.datadog.android.core.internal.persistence.PersistenceStrategy import com.datadog.android.core.internal.utils.sdkLogger -import com.datadog.android.tracing.internal.domain.TracesFilePersistenceStrategy -import com.datadog.android.v2.core.internal.storage.Storage -import com.datadog.opentracing.DDSpan +import com.datadog.android.tracing.internal.data.NoOpWriter +import com.datadog.android.tracing.internal.data.TraceWriter +import com.datadog.android.tracing.internal.domain.event.DdSpanToSpanEventMapper +import com.datadog.android.tracing.internal.domain.event.SpanEventMapperWrapper +import com.datadog.android.tracing.internal.domain.event.SpanEventSerializer +import com.datadog.android.v2.api.SdkCore +import com.datadog.trace.common.writer.Writer import java.util.concurrent.atomic.AtomicBoolean internal class TracingFeature( - private val coreFeature: CoreFeature, - private val storage: Storage + private val sdkCore: SdkCore ) { - internal var persistenceStrategy: PersistenceStrategy = NoOpPersistenceStrategy() + internal var dataWriter: Writer = NoOpWriter() internal val initialized = AtomicBoolean(false) // region SdkFeature fun initialize(configuration: Configuration.Feature.Tracing) { - persistenceStrategy = createPersistenceStrategy(storage, configuration) + dataWriter = createDataWriter(configuration) initialized.set(true) } fun stop() { - persistenceStrategy = NoOpPersistenceStrategy() + dataWriter = NoOpWriter() initialized.set(false) } - private fun createPersistenceStrategy( - storage: Storage, + private fun createDataWriter( configuration: Configuration.Feature.Tracing - ): PersistenceStrategy { - return TracesFilePersistenceStrategy( - coreFeature.contextProvider, - coreFeature.persistenceExecutorService, - coreFeature, - coreFeature.envName, - sdkLogger, - configuration.spanEventMapper, - storage + ): Writer { + return TraceWriter( + sdkCore, + legacyMapper = DdSpanToSpanEventMapper(), + eventMapper = SpanEventMapperWrapper(configuration.spanEventMapper), + serializer = SpanEventSerializer(), + internalLogger = sdkLogger ) } diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/data/NoOpWriter.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/data/NoOpWriter.kt new file mode 100644 index 0000000000..6e90780787 --- /dev/null +++ b/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/data/NoOpWriter.kt @@ -0,0 +1,28 @@ +/* + * 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.tracing.internal.data + +import com.datadog.opentracing.DDSpan +import com.datadog.trace.common.writer.Writer + +internal class NoOpWriter : Writer { + override fun close() { + // no-op + } + + override fun write(trace: MutableList?) { + // no-op + } + + override fun start() { + // no-op + } + + override fun incrementTraceCount() { + // no-op + } +} diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/data/TraceWriter.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/data/TraceWriter.kt index 2b49d3eef5..f537334deb 100644 --- a/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/data/TraceWriter.kt +++ b/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/data/TraceWriter.kt @@ -6,12 +6,27 @@ package com.datadog.android.tracing.internal.data -import com.datadog.android.core.internal.persistence.DataWriter +import androidx.annotation.WorkerThread +import com.datadog.android.event.EventMapper +import com.datadog.android.log.Logger +import com.datadog.android.log.internal.utils.errorWithTelemetry +import com.datadog.android.tracing.internal.TracingFeature +import com.datadog.android.tracing.model.SpanEvent +import com.datadog.android.v2.api.EventBatchWriter +import com.datadog.android.v2.api.SdkCore +import com.datadog.android.v2.api.context.DatadogContext +import com.datadog.android.v2.core.internal.storage.ContextAwareMapper +import com.datadog.android.v2.core.internal.storage.ContextAwareSerializer import com.datadog.opentracing.DDSpan import com.datadog.trace.common.writer.Writer +import java.util.Locale internal class TraceWriter( - val writer: DataWriter + private val sdkCore: SdkCore, + private val legacyMapper: ContextAwareMapper, + internal val eventMapper: EventMapper, + private val serializer: ContextAwareSerializer, + private val internalLogger: Logger ) : Writer { // region Writer @@ -20,10 +35,14 @@ internal class TraceWriter( } override fun write(trace: MutableList?) { - trace?.let { - @Suppress("ThreadSafety") // TODO RUMM-1503 delegate to another thread - writer.write(it) - } + if (trace == null) return + sdkCore.getFeature(TracingFeature.TRACING_FEATURE_NAME) + ?.withWriteContext { datadogContext, eventBatchWriter -> + trace.forEach { span -> + @Suppress("ThreadSafety") // called in the worker context + writeSpan(datadogContext, eventBatchWriter, span) + } + } } override fun close() { @@ -35,4 +54,31 @@ internal class TraceWriter( } // endregion + + @WorkerThread + private fun writeSpan( + datadogContext: DatadogContext, + writer: EventBatchWriter, + span: DDSpan + ) { + val spanEvent = legacyMapper.map(datadogContext, span) + val mapped = eventMapper.map(spanEvent) ?: return + try { + val serialized = serializer + .serialize(datadogContext, mapped) + ?.toByteArray(Charsets.UTF_8) ?: return + synchronized(this) { + writer.write(serialized, null) + } + } catch (@Suppress("TooGenericExceptionCaught") e: Throwable) { + internalLogger.errorWithTelemetry( + ERROR_SERIALIZING.format(Locale.US, mapped.javaClass.simpleName), + e + ) + } + } + + companion object { + internal const val ERROR_SERIALIZING = "Error serializing %s model" + } } diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/domain/TracesFilePersistenceStrategy.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/domain/TracesFilePersistenceStrategy.kt deleted file mode 100644 index 0de52dab06..0000000000 --- a/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/domain/TracesFilePersistenceStrategy.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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.tracing.internal.domain - -import com.datadog.android.core.internal.CoreFeature -import com.datadog.android.core.internal.persistence.file.batch.BatchFilePersistenceStrategy -import com.datadog.android.event.SpanEventMapper -import com.datadog.android.log.Logger -import com.datadog.android.tracing.internal.domain.event.DdSpanToSpanEventMapper -import com.datadog.android.tracing.internal.domain.event.SpanEventMapperWrapper -import com.datadog.android.tracing.internal.domain.event.SpanEventSerializer -import com.datadog.android.tracing.internal.domain.event.SpanMapperSerializer -import com.datadog.android.v2.core.internal.ContextProvider -import com.datadog.android.v2.core.internal.storage.Storage -import com.datadog.opentracing.DDSpan -import java.util.concurrent.ExecutorService - -internal class TracesFilePersistenceStrategy( - contextProvider: ContextProvider, - executorService: ExecutorService, - coreFeature: CoreFeature, - envName: String, - internalLogger: Logger, - spanEventMapper: SpanEventMapper, - storage: Storage -) : BatchFilePersistenceStrategy( - contextProvider, - executorService, - SpanMapperSerializer( - DdSpanToSpanEventMapper( - coreFeature - ), - SpanEventMapperWrapper(spanEventMapper), - SpanEventSerializer(envName) - ), - internalLogger, - storage -) diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/domain/event/DdSpanToSpanEventMapper.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/domain/event/DdSpanToSpanEventMapper.kt index fb3e8565d2..afee95ad55 100644 --- a/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/domain/event/DdSpanToSpanEventMapper.kt +++ b/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/domain/event/DdSpanToSpanEventMapper.kt @@ -6,23 +6,21 @@ package com.datadog.android.tracing.internal.domain.event -import com.datadog.android.core.internal.CoreFeature -import com.datadog.android.core.internal.Mapper import com.datadog.android.core.internal.utils.toHexString -import com.datadog.android.core.model.NetworkInfo import com.datadog.android.tracing.model.SpanEvent +import com.datadog.android.v2.api.context.DatadogContext +import com.datadog.android.v2.api.context.NetworkInfo +import com.datadog.android.v2.core.internal.storage.ContextAwareMapper import com.datadog.opentracing.DDSpan -internal class DdSpanToSpanEventMapper( - private val coreFeature: CoreFeature -) : Mapper { +internal class DdSpanToSpanEventMapper : ContextAwareMapper { // region Mapper - override fun map(model: DDSpan): SpanEvent { - val serverOffset = coreFeature.timeProvider.getServerOffsetNanos() + override fun map(datadogContext: DatadogContext, model: DDSpan): SpanEvent { + val serverOffset = datadogContext.time.serverTimeOffsetNs val metrics = resolveMetrics(model) - val metadata = resolveMeta(model) + val metadata = resolveMeta(datadogContext, model) return SpanEvent( traceId = model.traceId.toHexString(), spanId = model.spanId.toHexString(), @@ -47,8 +45,8 @@ internal class DdSpanToSpanEventMapper( additionalProperties = event.metrics ) - private fun resolveMeta(event: DDSpan): SpanEvent.Meta { - val networkInfo = coreFeature.networkInfoProvider.getLatestNetworkInfo() + private fun resolveMeta(datadogContext: DatadogContext, event: DDSpan): SpanEvent.Meta { + val networkInfo = datadogContext.networkInfo val simCarrier = resolveSimCarrier(networkInfo) val networkInfoClient = SpanEvent.Client( simCarrier = simCarrier, @@ -58,19 +56,19 @@ internal class DdSpanToSpanEventMapper( connectivity = networkInfo.connectivity.toString() ) val networkInfoMeta = SpanEvent.Network(networkInfoClient) - val userInfo = coreFeature.userInfoProvider.getUserInfo() + val userInfo = datadogContext.userInfo val usrMeta = SpanEvent.Usr( id = userInfo.id, name = userInfo.name, email = userInfo.email, - additionalProperties = userInfo.additionalProperties + additionalProperties = userInfo.additionalProperties.toMutableMap() ) return SpanEvent.Meta( - version = coreFeature.packageVersionProvider.version, - dd = SpanEvent.Dd(source = coreFeature.sourceName), + version = datadogContext.version, + dd = SpanEvent.Dd(source = datadogContext.source), span = SpanEvent.Span(), tracer = SpanEvent.Tracer( - version = coreFeature.sdkVersion + version = datadogContext.sdkVersion ), usr = usrMeta, network = networkInfoMeta, diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/domain/event/SpanEventSerializer.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/domain/event/SpanEventSerializer.kt index 9f2d8a9efa..7583366e04 100644 --- a/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/domain/event/SpanEventSerializer.kt +++ b/dd-sdk-android/src/main/kotlin/com/datadog/android/tracing/internal/domain/event/SpanEventSerializer.kt @@ -8,29 +8,29 @@ package com.datadog.android.tracing.internal.domain.event import com.datadog.android.core.internal.constraints.DataConstraints import com.datadog.android.core.internal.constraints.DatadogDataConstraints -import com.datadog.android.core.internal.persistence.Serializer import com.datadog.android.core.internal.utils.NULL_MAP_VALUE import com.datadog.android.tracing.model.SpanEvent +import com.datadog.android.v2.api.context.DatadogContext +import com.datadog.android.v2.core.internal.storage.ContextAwareSerializer import com.google.gson.JsonArray import com.google.gson.JsonObject import com.google.gson.JsonPrimitive import java.util.Date internal class SpanEventSerializer( - private val envName: String, private val dataConstraints: DataConstraints = DatadogDataConstraints() -) : Serializer { +) : ContextAwareSerializer { // region Serializer - override fun serialize(model: SpanEvent): String { + override fun serialize(datadogContext: DatadogContext, model: SpanEvent): String { val span = sanitizeKeys(model).toJson() val spans = JsonArray(1) spans.add(span) val jsonObject = JsonObject() jsonObject.add(TAG_SPANS, spans) - jsonObject.addProperty(TAG_ENV, envName) + jsonObject.addProperty(TAG_ENV, datadogContext.env) return jsonObject.toString() } diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/v2/core/DatadogCore.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/v2/core/DatadogCore.kt index 4630cd3ac1..0a23710e61 100644 --- a/dd-sdk-android/src/main/kotlin/com/datadog/android/v2/core/DatadogCore.kt +++ b/dd-sdk-android/src/main/kotlin/com/datadog/android/v2/core/DatadogCore.kt @@ -331,7 +331,7 @@ internal class DatadogCore( ) features[TracingFeature.TRACING_FEATURE_NAME]?.let { it.initialize(appContext, configuration.plugins) - tracingFeature = TracingFeature(coreFeature, it.storage).also { + tracingFeature = TracingFeature(this).also { it.initialize(configuration) } } diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/v2/core/internal/storage/ContextAwareMapper.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/v2/core/internal/storage/ContextAwareMapper.kt new file mode 100644 index 0000000000..3d2e98184f --- /dev/null +++ b/dd-sdk-android/src/main/kotlin/com/datadog/android/v2/core/internal/storage/ContextAwareMapper.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.v2.core.internal.storage + +import com.datadog.android.v2.api.context.DatadogContext + +internal interface ContextAwareMapper { + + fun map(datadogContext: DatadogContext, model: R): T +} diff --git a/dd-sdk-android/src/main/kotlin/com/datadog/android/v2/core/internal/storage/ContextAwareSerializer.kt b/dd-sdk-android/src/main/kotlin/com/datadog/android/v2/core/internal/storage/ContextAwareSerializer.kt new file mode 100644 index 0000000000..0f2526a329 --- /dev/null +++ b/dd-sdk-android/src/main/kotlin/com/datadog/android/v2/core/internal/storage/ContextAwareSerializer.kt @@ -0,0 +1,20 @@ +/* + * 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.v2.core.internal.storage + +import com.datadog.android.v2.api.context.DatadogContext + +/** + * A class which can transform an object of type [T] into a formatted String. + */ +internal interface ContextAwareSerializer { + /** + * Serializes the data into a String. + * @return the String representing the data or null if any exception occurs + */ + fun serialize(datadogContext: DatadogContext, model: T): String? +} diff --git a/dd-sdk-android/src/test/kotlin/com/datadog/android/DatadogInterceptorWithoutTracesTest.kt b/dd-sdk-android/src/test/kotlin/com/datadog/android/DatadogInterceptorWithoutTracesTest.kt index 9cb7b376e7..7b0d5bb1aa 100644 --- a/dd-sdk-android/src/test/kotlin/com/datadog/android/DatadogInterceptorWithoutTracesTest.kt +++ b/dd-sdk-android/src/test/kotlin/com/datadog/android/DatadogInterceptorWithoutTracesTest.kt @@ -19,7 +19,6 @@ import com.datadog.android.rum.internal.RumFeature import com.datadog.android.tracing.TracedRequestListener import com.datadog.android.tracing.TracingInterceptor import com.datadog.android.tracing.TracingInterceptorTest -import com.datadog.android.tracing.internal.TracingFeature import com.datadog.android.utils.config.ApplicationContextTestConfiguration import com.datadog.android.utils.config.CoreFeatureTestConfiguration import com.datadog.android.utils.config.GlobalRumMonitorTestConfiguration @@ -170,8 +169,7 @@ internal class DatadogInterceptorWithoutTracesTest { traceSampler = mockTraceSampler ) { mockLocalTracer } Datadog.globalSdkCore = mock() - whenever((Datadog.globalSdkCore as DatadogCore).tracingFeature) doReturn - TracingFeature(coreFeature.mockInstance, storage = mock()) + whenever((Datadog.globalSdkCore as DatadogCore).tracingFeature) doReturn mock() whenever((Datadog.globalSdkCore as DatadogCore).rumFeature) doReturn RumFeature(coreFeature.mockInstance, storage = mock()) diff --git a/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/TracingInterceptorNotSendingSpanTest.kt b/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/TracingInterceptorNotSendingSpanTest.kt index e24ad03e49..27c88f0797 100644 --- a/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/TracingInterceptorNotSendingSpanTest.kt +++ b/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/TracingInterceptorNotSendingSpanTest.kt @@ -12,7 +12,6 @@ import com.datadog.android.Datadog import com.datadog.android.core.internal.net.FirstPartyHostDetector import com.datadog.android.core.internal.sampling.Sampler import com.datadog.android.core.internal.utils.loggableStackTrace -import com.datadog.android.tracing.internal.TracingFeature import com.datadog.android.utils.config.ApplicationContextTestConfiguration import com.datadog.android.utils.config.CoreFeatureTestConfiguration import com.datadog.android.utils.config.LoggerTestConfiguration @@ -172,8 +171,7 @@ internal open class TracingInterceptorNotSendingSpanTest { fakeUrl = forgeUrl(forge) fakeRequest = forgeRequest(forge) Datadog.globalSdkCore = mock() - whenever((Datadog.globalSdkCore as DatadogCore).tracingFeature) doReturn - TracingFeature(coreFeature.mockInstance, storage = mock()) + whenever((Datadog.globalSdkCore as DatadogCore).tracingFeature) doReturn mock() doAnswer { false }.whenever(mockDetector).isFirstPartyUrl(any()) doAnswer { true }.whenever(mockDetector).isFirstPartyUrl(HttpUrl.get(fakeUrl)) diff --git a/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/assertj/SpanEventAssert.kt b/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/assertj/SpanEventAssert.kt index e7b15b529f..dda767c059 100644 --- a/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/assertj/SpanEventAssert.kt +++ b/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/assertj/SpanEventAssert.kt @@ -6,9 +6,9 @@ package com.datadog.android.tracing.assertj -import com.datadog.android.core.model.NetworkInfo -import com.datadog.android.core.model.UserInfo import com.datadog.android.tracing.model.SpanEvent +import com.datadog.android.v2.api.context.NetworkInfo +import com.datadog.android.v2.api.context.UserInfo import org.assertj.core.api.AbstractObjectAssert import org.assertj.core.api.Assertions.assertThat diff --git a/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/internal/AndroidTracerTest.kt b/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/internal/AndroidTracerTest.kt index 9ec058bc2f..da7f86ba73 100644 --- a/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/internal/AndroidTracerTest.kt +++ b/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/internal/AndroidTracerTest.kt @@ -9,7 +9,6 @@ package com.datadog.android.tracing.internal import android.content.Context import android.util.Log import com.datadog.android.Datadog -import com.datadog.android.core.configuration.Configuration import com.datadog.android.log.LogAttributes import com.datadog.android.rum.GlobalRum import com.datadog.android.rum.internal.RumFeature @@ -19,8 +18,6 @@ import com.datadog.android.utils.config.ApplicationContextTestConfiguration import com.datadog.android.utils.config.CoreFeatureTestConfiguration import com.datadog.android.utils.config.GlobalRumMonitorTestConfiguration import com.datadog.android.utils.config.LoggerTestConfiguration -import com.datadog.android.utils.config.MainLooperTestConfiguration -import com.datadog.android.utils.extension.mockChoreographerInstance import com.datadog.android.utils.forge.Configurator import com.datadog.android.v2.api.NoOpSdkCore import com.datadog.android.v2.core.DatadogCore @@ -31,6 +28,7 @@ import com.datadog.tools.unit.annotations.TestConfigurationsProvider import com.datadog.tools.unit.extensions.TestConfigurationExtension import com.datadog.tools.unit.extensions.config.TestConfiguration import com.datadog.trace.api.Config +import com.datadog.trace.common.writer.Writer import com.nhaarman.mockitokotlin2.argumentCaptor import com.nhaarman.mockitokotlin2.doReturn import com.nhaarman.mockitokotlin2.inOrder @@ -81,32 +79,28 @@ internal class AndroidTracerTest { @Mock lateinit var mockLogsHandler: LogHandler - lateinit var tracingFeature: TracingFeature + @Mock + lateinit var mockTracingFeature: TracingFeature + + @Mock + lateinit var mockTraceWriter: Writer - lateinit var rumFeature: RumFeature + @Mock + lateinit var mockRumFeature: RumFeature @BeforeEach fun `set up`(forge: Forge) { - // Prevent crash when initializing RumFeature - mockChoreographerInstance() - fakeServiceName = forge.anAlphabeticalString() fakeEnvName = forge.anAlphabeticalString() fakeToken = forge.anHexadecimalString() val mockCore = mock() - tracingFeature = TracingFeature( - coreFeature.mockInstance, - storage = mock() - ) - rumFeature = RumFeature(coreFeature.mockInstance, storage = mock()) - whenever(mockCore.tracingFeature) doReturn tracingFeature - whenever(mockCore.rumFeature) doReturn rumFeature + whenever(mockCore.tracingFeature) doReturn mockTracingFeature + whenever(mockCore.rumFeature) doReturn mockRumFeature whenever(mockCore.coreFeature) doReturn coreFeature.mockInstance - tracingFeature.initialize(Configuration.DEFAULT_TRACING_CONFIG) - rumFeature.initialize(appContext.mockInstance, Configuration.DEFAULT_RUM_CONFIG) + whenever(mockTracingFeature.dataWriter) doReturn mockTraceWriter Datadog.globalSdkCore = mockCore @@ -506,13 +500,12 @@ internal class AndroidTracerTest { val appContext = ApplicationContextTestConfiguration(Context::class.java) val coreFeature = CoreFeatureTestConfiguration(appContext) val rumMonitor = GlobalRumMonitorTestConfiguration() - val mainLooper = MainLooperTestConfiguration() val logger = LoggerTestConfiguration() @TestConfigurationsProvider @JvmStatic fun getTestConfigurations(): List { - return listOf(logger, appContext, coreFeature, rumMonitor, mainLooper) + return listOf(logger, appContext, coreFeature, rumMonitor) } } } diff --git a/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/internal/TracingFeatureTest.kt b/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/internal/TracingFeatureTest.kt index 8dd1818220..b304c0097d 100644 --- a/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/internal/TracingFeatureTest.kt +++ b/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/internal/TracingFeatureTest.kt @@ -6,20 +6,11 @@ package com.datadog.android.tracing.internal -import android.app.Application import com.datadog.android.core.configuration.Configuration -import com.datadog.android.core.internal.persistence.file.advanced.ScheduledWriter -import com.datadog.android.core.internal.persistence.file.batch.BatchFileDataWriter -import com.datadog.android.tracing.internal.domain.TracesFilePersistenceStrategy +import com.datadog.android.tracing.internal.data.TraceWriter import com.datadog.android.tracing.internal.domain.event.SpanEventMapperWrapper -import com.datadog.android.tracing.internal.domain.event.SpanMapperSerializer -import com.datadog.android.utils.config.ApplicationContextTestConfiguration -import com.datadog.android.utils.config.CoreFeatureTestConfiguration import com.datadog.android.utils.forge.Configurator -import com.datadog.android.v2.core.internal.storage.Storage -import com.datadog.tools.unit.annotations.TestConfigurationsProvider -import com.datadog.tools.unit.extensions.TestConfigurationExtension -import com.datadog.tools.unit.extensions.config.TestConfiguration +import com.datadog.android.v2.api.SdkCore import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension @@ -35,8 +26,7 @@ import org.mockito.quality.Strictness @Extensions( ExtendWith(MockitoExtension::class), - ExtendWith(ForgeExtension::class), - ExtendWith(TestConfigurationExtension::class) + ExtendWith(ForgeExtension::class) ) @MockitoSettings(strictness = Strictness.LENIENT) @ForgeConfiguration(Configurator::class) @@ -48,21 +38,21 @@ internal class TracingFeatureTest { lateinit var fakeConfigurationFeature: Configuration.Feature.Tracing @Mock - lateinit var mockStorage: Storage + lateinit var mockSdkCore: SdkCore @BeforeEach fun `set up`() { - testedFeature = TracingFeature(coreFeature.mockInstance, mockStorage) + testedFeature = TracingFeature(mockSdkCore) } @Test - fun `𝕄 initialize persistence strategy 𝕎 initialize()`() { + fun `𝕄 initialize writer 𝕎 initialize()`() { // When testedFeature.initialize(fakeConfigurationFeature) // Then - assertThat(testedFeature.persistenceStrategy) - .isInstanceOf(TracesFilePersistenceStrategy::class.java) + assertThat(testedFeature.dataWriter) + .isInstanceOf(TraceWriter::class.java) } @Test @@ -71,23 +61,9 @@ internal class TracingFeatureTest { testedFeature.initialize(fakeConfigurationFeature) // Then - val batchFileDataWriter = - (testedFeature.persistenceStrategy.getWriter() as? ScheduledWriter) - ?.delegateWriter as? BatchFileDataWriter - val spanSerializer = batchFileDataWriter?.serializer as? SpanMapperSerializer - val spanEventMapperWrapper = spanSerializer?.spanEventMapper as? SpanEventMapperWrapper + val dataWriter = testedFeature.dataWriter as? TraceWriter + val spanEventMapperWrapper = dataWriter?.eventMapper as? SpanEventMapperWrapper val spanEventMapper = spanEventMapperWrapper?.wrappedEventMapper assertThat(spanEventMapper).isSameAs(fakeConfigurationFeature.spanEventMapper) } - - companion object { - val appContext = ApplicationContextTestConfiguration(Application::class.java) - val coreFeature = CoreFeatureTestConfiguration(appContext) - - @TestConfigurationsProvider - @JvmStatic - fun getTestConfigurations(): List { - return listOf(appContext, coreFeature) - } - } } diff --git a/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/internal/data/TraceWriterTest.kt b/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/internal/data/TraceWriterTest.kt index 3f781c1040..9e8ff93811 100644 --- a/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/internal/data/TraceWriterTest.kt +++ b/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/internal/data/TraceWriterTest.kt @@ -6,18 +6,31 @@ package com.datadog.android.tracing.internal.data -import com.datadog.android.core.internal.persistence.DataWriter -import com.datadog.android.rum.GlobalRum -import com.datadog.android.utils.config.GlobalRumMonitorTestConfiguration +import com.datadog.android.event.EventMapper +import com.datadog.android.log.Logger +import com.datadog.android.log.internal.utils.errorWithTelemetry +import com.datadog.android.tracing.internal.TracingFeature +import com.datadog.android.tracing.model.SpanEvent import com.datadog.android.utils.forge.Configurator +import com.datadog.android.v2.api.EventBatchWriter +import com.datadog.android.v2.api.FeatureScope +import com.datadog.android.v2.api.SdkCore +import com.datadog.android.v2.api.context.DatadogContext +import com.datadog.android.v2.core.internal.storage.ContextAwareMapper +import com.datadog.android.v2.core.internal.storage.ContextAwareSerializer import com.datadog.opentracing.DDSpan -import com.datadog.tools.unit.annotations.TestConfigurationsProvider -import com.datadog.tools.unit.extensions.TestConfigurationExtension -import com.datadog.tools.unit.extensions.config.TestConfiguration -import com.datadog.trace.api.DDTags +import com.datadog.tools.unit.forge.aThrowable +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.doAnswer +import com.nhaarman.mockitokotlin2.doReturn +import com.nhaarman.mockitokotlin2.doThrow +import com.nhaarman.mockitokotlin2.times import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions import com.nhaarman.mockitokotlin2.verifyZeroInteractions +import com.nhaarman.mockitokotlin2.whenever import fr.xgouchet.elmyr.Forge +import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension import org.junit.jupiter.api.BeforeEach @@ -28,136 +41,250 @@ import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoSettings import org.mockito.quality.Strictness +import java.util.Locale @Extensions( ExtendWith(MockitoExtension::class), - ExtendWith(ForgeExtension::class), - ExtendWith(TestConfigurationExtension::class) + ExtendWith(ForgeExtension::class) ) @MockitoSettings(strictness = Strictness.LENIENT) @ForgeConfiguration(Configurator::class) internal class TraceWriterTest { - lateinit var testedWriter: TraceWriter + private lateinit var testedWriter: TraceWriter @Mock - lateinit var mockFilesWriter: DataWriter + lateinit var mockSdkCore: SdkCore + + @Mock + lateinit var mockLegacyMapper: ContextAwareMapper + + @Mock + lateinit var mockEventMapper: EventMapper + + @Mock + lateinit var mockSerializer: ContextAwareSerializer + + @Mock + lateinit var mockInternalLogger: Logger + + @Mock + lateinit var mockTracingFeatureScope: FeatureScope + + @Mock + lateinit var mockEventBatchWriter: EventBatchWriter + + @Forgery + lateinit var fakeDatadogContext: DatadogContext // region Unit Tests @BeforeEach fun `set up`() { - GlobalRum.isRegistered.set(false) - testedWriter = TraceWriter(mockFilesWriter) + whenever( + mockSdkCore.getFeature(TracingFeature.TRACING_FEATURE_NAME) + ) doReturn mockTracingFeatureScope + + whenever(mockTracingFeatureScope.withWriteContext(any())) doAnswer { + val callback = it.getArgument<(DatadogContext, EventBatchWriter) -> Unit>(0) + callback.invoke(fakeDatadogContext, mockEventBatchWriter) + } + + whenever(mockEventMapper.map(any())) doAnswer { it.getArgument(0) } + + testedWriter = TraceWriter( + sdkCore = mockSdkCore, + legacyMapper = mockLegacyMapper, + eventMapper = mockEventMapper, + serializer = mockSerializer, + internalLogger = mockInternalLogger + ) } @Test - fun `M use the wrapped writer W onWriting`(forge: Forge) { + fun `M write spans W write()`(forge: Forge) { // GIVEN - val spansList = ArrayList(2).apply { - add(forge.getForgery()) - add(forge.getForgery()) + val ddSpans = forge.aList { getForgery() }.toMutableList() + val spanEvents = ddSpans.map { forge.getForgery() } + val serializedSpans = ddSpans.map { forge.aString() } + + ddSpans.forEachIndexed { index, ddSpan -> + whenever(mockLegacyMapper.map(fakeDatadogContext, ddSpan)) doReturn spanEvents[index] + } + + spanEvents.forEachIndexed { index, spanEvent -> + whenever( + mockSerializer.serialize(fakeDatadogContext, spanEvent) + ) doReturn serializedSpans[index] } // WHEN - testedWriter.write(spansList) + testedWriter.write(ddSpans) // THEN - verify(mockFilesWriter).write(spansList) - spansList.forEach { + serializedSpans.forEach { + verify(mockEventBatchWriter).write(it.toByteArray(), null) + } + verifyNoMoreInteractions(mockEventBatchWriter) + + ddSpans.forEach { it.finish() } } @Test - fun `M not send a RUM Error event W onWritingErrorSpan(`(forge: Forge) { + fun `M not write non-mapped spans W write()`(forge: Forge) { // GIVEN - val spansList = ArrayList(2).apply { - add(forgeErrorSpan(forge)) - forgeErrorSpan(forge).apply { - this.context().setTag(DDTags.ERROR_TYPE, null) - } - forgeErrorSpan(forge).apply { - this.context().setTag(DDTags.ERROR_MSG, null) - } + val ddSpans = forge.aList { getForgery() }.toMutableList() + val spanEvents = ddSpans.map { forge.getForgery() } + val mappedEvents = spanEvents.map { forge.aNullable { it } } + + val serializedSpans = mappedEvents.filterNotNull().map { forge.aString() } + + ddSpans.forEachIndexed { index, ddSpan -> + whenever(mockLegacyMapper.map(fakeDatadogContext, ddSpan)) doReturn spanEvents[index] + } + + spanEvents.forEachIndexed { index, event -> + whenever(mockEventMapper.map(event)) doReturn mappedEvents[index] + } + + mappedEvents.filterNotNull().forEachIndexed { index, spanEvent -> + whenever( + mockSerializer.serialize(fakeDatadogContext, spanEvent) + ) doReturn serializedSpans[index] } // WHEN - testedWriter.write(spansList) + testedWriter.write(ddSpans) // THEN - verifyZeroInteractions(rumMonitor.mockInstance) - spansList.forEach { + serializedSpans.forEach { + verify(mockEventBatchWriter).write(it.toByteArray(), null) + } + verifyNoMoreInteractions(mockEventBatchWriter) + + ddSpans.forEach { it.finish() } } @Test - fun `M do nothing W onWritingErrorSpan and no AdvancedRumMonitor registered`(forge: Forge) { + fun `M not write non-serialized spans W write()`(forge: Forge) { // GIVEN - val spansList = ArrayList(2).apply { - add(forgeErrorFreeSpan(forge)) - add(forgeErrorFreeSpan(forge)) - } + val ddSpans = forge.aList { getForgery() }.toMutableList() + val spanEvents = ddSpans.map { forge.getForgery() } - // WHEN - testedWriter.write(spansList) + val serializedSpans = spanEvents.map { forge.aNullable { aString() } } - // THEN - verifyZeroInteractions(rumMonitor.mockInstance) - } + ddSpans.forEachIndexed { index, ddSpan -> + whenever(mockLegacyMapper.map(fakeDatadogContext, ddSpan)) doReturn spanEvents[index] + } - @Test - fun `M do nothing W onWritingErrorFreeSpan`(forge: Forge) { - // GIVEN - GlobalRum.isRegistered.set(false) - val spansList = ArrayList(2).apply { - add(forgeErrorFreeSpan(forge)) - add(forgeErrorFreeSpan(forge)) + spanEvents.forEachIndexed { index, spanEvent -> + whenever( + mockSerializer.serialize(fakeDatadogContext, spanEvent) + ) doReturn serializedSpans[index] } // WHEN - testedWriter.write(spansList) + testedWriter.write(ddSpans) // THEN - verifyZeroInteractions(rumMonitor.mockInstance) + serializedSpans.filterNotNull().forEach { + verify(mockEventBatchWriter).write(it.toByteArray(), null) + } + verifyNoMoreInteractions(mockEventBatchWriter) + + ddSpans.forEach { + it.finish() + } } @Test - fun `M do nothing W handling null data`() { + fun `M do nothing W write() { null trace }`() { // WHEN testedWriter.write(null) // THEN - verifyZeroInteractions(mockFilesWriter) + verifyZeroInteractions( + mockEventBatchWriter, + mockEventMapper, + mockSerializer, + mockSdkCore, + mockLegacyMapper, + mockInternalLogger + ) } - // endregion + @Test + fun `M log error and proceed W write() { serialization failed }`(forge: Forge) { + // GIVEN + val ddSpans = forge.aList { getForgery() }.toMutableList() + val spanEvents = ddSpans.map { forge.getForgery() } + val serializedSpans = ddSpans.map { forge.aString() } - // region Internal + ddSpans.forEachIndexed { index, ddSpan -> + whenever(mockLegacyMapper.map(fakeDatadogContext, ddSpan)) doReturn spanEvents[index] + } - fun forgeErrorSpan(forge: Forge): DDSpan { - val fakeErrorSpan: DDSpan = forge.getForgery() - val throwable: Throwable = forge.getForgery() - fakeErrorSpan.setErrorMeta(throwable) - return fakeErrorSpan - } + val faultySpanIndex = forge.anInt(min = 0, max = spanEvents.size) + val fakeThrowable = forge.aThrowable() + spanEvents.forEachIndexed { index, spanEvent -> + if (index == faultySpanIndex) { + whenever( + mockSerializer.serialize( + fakeDatadogContext, + spanEvent + ) + ) doThrow fakeThrowable + } else { + whenever( + mockSerializer.serialize(fakeDatadogContext, spanEvent) + ) doReturn serializedSpans[index] + } + } + + // WHEN + testedWriter.write(ddSpans) - fun forgeErrorFreeSpan(forge: Forge): DDSpan { - val fakeErrorFreeSpan: DDSpan = forge.getForgery() - fakeErrorFreeSpan.isError = false - return fakeErrorFreeSpan + // THEN + serializedSpans.forEachIndexed { index, serializedSpan -> + if (index != faultySpanIndex) { + verify(mockEventBatchWriter).write(serializedSpan.toByteArray(), null) + } + } + verifyNoMoreInteractions(mockEventBatchWriter) + + verify(mockInternalLogger) + .errorWithTelemetry( + TraceWriter.ERROR_SERIALIZING.format(Locale.US, SpanEvent::class.java.simpleName), + fakeThrowable + ) + + ddSpans.forEach { + it.finish() + } } - // endregion + @Test + fun `M request event write context once W write()`(forge: Forge) { + // GIVEN + val ddSpans = forge.aList { getForgery() }.toMutableList() + + // WHEN + testedWriter.write(ddSpans) + + // THEN + verify(mockSdkCore, times(1)).getFeature(TracingFeature.TRACING_FEATURE_NAME) + verify(mockTracingFeatureScope, times(1)).withWriteContext(any()) - companion object { - val rumMonitor = GlobalRumMonitorTestConfiguration() + verifyNoMoreInteractions(mockSdkCore, mockTracingFeatureScope) - @TestConfigurationsProvider - @JvmStatic - fun getTestConfigurations(): List { - return listOf(rumMonitor) + ddSpans.forEach { + it.finish() } } + + // endregion } 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 a6e17d24a0..141ccd1b4a 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 @@ -6,24 +6,14 @@ 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 -import com.datadog.android.core.model.UserInfo -import com.datadog.android.log.internal.user.MutableUserInfoProvider import com.datadog.android.tracing.assertj.SpanEventAssert.Companion.assertThat import com.datadog.android.utils.forge.Configurator +import com.datadog.android.v2.api.context.DatadogContext import com.datadog.opentracing.DDSpan import com.datadog.tools.unit.setFieldValue -import com.nhaarman.mockitokotlin2.doReturn -import com.nhaarman.mockitokotlin2.whenever import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.Forgery -import fr.xgouchet.elmyr.annotation.LongForgery -import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension import org.junit.jupiter.api.BeforeEach @@ -31,7 +21,6 @@ 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.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoSettings import org.mockito.quality.Strictness @@ -48,64 +37,19 @@ internal class DdSpanToSpanEventMapperTest { lateinit var testedMapper: DdSpanToSpanEventMapper @Forgery - lateinit var fakeUserInfo: UserInfo - - @Forgery - lateinit var fakeNetworkInfo: NetworkInfo - - @Mock - lateinit var mockTimeProvider: TimeProvider - - @Mock - lateinit var mockUserInfoProvider: MutableUserInfoProvider - - @Mock - lateinit var mockNetworkInfoProvider: NetworkInfoProvider - - @Mock - lateinit var mockAppVersionProvider: AppVersionProvider - - @Mock - lateinit var mockCoreFeature: CoreFeature - - @StringForgery(regex = "[0-9]\\.[0-9]\\.[0-9]") - lateinit var fakeClientPackageVersion: String - - @StringForgery - lateinit var fakeSdkVersion: String - - @StringForgery - lateinit var fakeSource: String - - @LongForgery - var fakeServerOffsetNanos: Long = 0L + lateinit var fakeDatadogContext: DatadogContext @BeforeEach fun `set up`() { - whenever(mockCoreFeature.packageVersionProvider) doReturn mockAppVersionProvider - whenever(mockAppVersionProvider.version) doReturn fakeClientPackageVersion - whenever(mockCoreFeature.sdkVersion) doReturn fakeSdkVersion - whenever(mockCoreFeature.sourceName) doReturn fakeSource - - whenever(mockCoreFeature.timeProvider) doReturn mockTimeProvider - whenever(mockCoreFeature.networkInfoProvider) doReturn mockNetworkInfoProvider - whenever(mockCoreFeature.userInfoProvider) doReturn mockUserInfoProvider - - whenever(mockTimeProvider.getServerOffsetNanos()).thenReturn(fakeServerOffsetNanos) - whenever(mockUserInfoProvider.getUserInfo()) doReturn fakeUserInfo - whenever(mockNetworkInfoProvider.getLatestNetworkInfo()) doReturn fakeNetworkInfo - testedMapper = DdSpanToSpanEventMapper(mockCoreFeature) + testedMapper = DdSpanToSpanEventMapper() } @RepeatedTest(4) fun `M map a DdSpan to a SpanEvent W map`( @Forgery fakeSpan: DDSpan ) { - // GIVEN - whenever(mockTimeProvider.getServerOffsetNanos()).thenReturn(fakeServerOffsetNanos) - // WHEN - val event = testedMapper.map(fakeSpan) + val event = testedMapper.map(fakeDatadogContext, fakeSpan) // THEN assertThat(event) @@ -116,14 +60,14 @@ internal class DdSpanToSpanEventMapperTest { .hasOperationName(fakeSpan.operationName) .hasResourceName(fakeSpan.resourceName) .hasSpanType("custom") - .hasSpanSource(fakeSource) + .hasSpanSource(fakeDatadogContext.source) .hasErrorFlag(fakeSpan.error.toLong()) - .hasSpanStartTime(fakeSpan.startTime + fakeServerOffsetNanos) + .hasSpanStartTime(fakeSpan.startTime + fakeDatadogContext.time.serverTimeOffsetNs) .hasSpanDuration(fakeSpan.durationNano) - .hasTracerVersion(fakeSdkVersion) - .hasClientPackageVersion(fakeClientPackageVersion) - .hasNetworkInfo(fakeNetworkInfo) - .hasUserInfo(fakeUserInfo) + .hasTracerVersion(fakeDatadogContext.sdkVersion) + .hasClientPackageVersion(fakeDatadogContext.version) + .hasNetworkInfo(fakeDatadogContext.networkInfo) + .hasUserInfo(fakeDatadogContext.userInfo) .hasMeta(fakeSpan.meta) .hasMetrics(fakeSpan.metrics) } @@ -136,7 +80,7 @@ internal class DdSpanToSpanEventMapperTest { fakeSpan.setFieldValue("parentId", 0) // WHEN - val event = testedMapper.map(fakeSpan) + val event = testedMapper.map(fakeDatadogContext, fakeSpan) // THEN assertThat(event) @@ -152,7 +96,7 @@ internal class DdSpanToSpanEventMapperTest { fakeSpan.context().setFieldValue("parentId", BigInteger.valueOf(forge.aLong(min = 1))) // WHEN - val event = testedMapper.map(fakeSpan) + val event = testedMapper.map(fakeDatadogContext, fakeSpan) // THEN assertThat(event) diff --git a/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/internal/domain/event/SpanEventSerializerTest.kt b/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/internal/domain/event/SpanEventSerializerTest.kt index 9c592d7cf2..c045d38e9d 100644 --- a/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/internal/domain/event/SpanEventSerializerTest.kt +++ b/dd-sdk-android/src/test/kotlin/com/datadog/android/tracing/internal/domain/event/SpanEventSerializerTest.kt @@ -12,6 +12,7 @@ import com.datadog.android.tracing.model.SpanEvent import com.datadog.android.utils.extension.getString import com.datadog.android.utils.forge.Configurator import com.datadog.android.utils.forge.exhaustiveAttributes +import com.datadog.android.v2.api.context.DatadogContext import com.datadog.tools.unit.assertj.JsonObjectAssert import com.google.gson.JsonObject import com.google.gson.JsonParser @@ -20,7 +21,6 @@ import com.nhaarman.mockitokotlin2.anyOrNull import com.nhaarman.mockitokotlin2.whenever import fr.xgouchet.elmyr.Forge 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 @@ -42,8 +42,8 @@ import java.util.Date @ForgeConfiguration(Configurator::class) internal class SpanEventSerializerTest { - @StringForgery - lateinit var fakeEnvName: String + @Forgery + lateinit var fakeDatadogContext: DatadogContext @Mock lateinit var mockDatadogConstraints: DatadogDataConstraints @@ -63,7 +63,7 @@ internal class SpanEventSerializerTest { it.getArgument(0) } testedSerializer = - SpanEventSerializer(fakeEnvName, dataConstraints = mockDatadogConstraints) + SpanEventSerializer(dataConstraints = mockDatadogConstraints) } // region tests @@ -71,13 +71,13 @@ internal class SpanEventSerializerTest { @Test fun `M serialize a SpanEvent W serialize`(@Forgery fakeSpanEvent: SpanEvent) { // WHEN - val serialized = testedSerializer.serialize(fakeSpanEvent) + val serialized = testedSerializer.serialize(fakeDatadogContext, fakeSpanEvent) // THEN val jsonObject = JsonParser.parseString(serialized).asJsonObject val spanObject = jsonObject.getAsJsonArray(KEY_SPANS).first() as JsonObject assertJsonMatchesInputSpan(spanObject, fakeSpanEvent) - Assertions.assertThat(jsonObject.getString(KEY_ENV)).isEqualTo(fakeEnvName) + Assertions.assertThat(jsonObject.getString(KEY_ENV)).isEqualTo(fakeDatadogContext.env) } @Test @@ -97,7 +97,7 @@ internal class SpanEventSerializerTest { ).thenReturn(fakeSanitizedAttributes) // WHEN - val serialized = testedSerializer.serialize(fakeSpanEvent) + val serialized = testedSerializer.serialize(fakeDatadogContext, fakeSpanEvent) // THEN val jsonObject = JsonParser.parseString(serialized).asJsonObject @@ -128,7 +128,7 @@ internal class SpanEventSerializerTest { ).thenReturn(fakeSanitizedAttributes) // WHEN - val serialized = testedSerializer.serialize(fakeSpanEvent) + val serialized = testedSerializer.serialize(fakeDatadogContext, fakeSpanEvent) // THEN val jsonObject = JsonParser.parseString(serialized).asJsonObject