From 05535a268237ea5e223a27fa8c0076aec1afbd09 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Mon, 17 Nov 2025 19:30:21 +0000 Subject: [PATCH 01/29] RUM-10363: Replace all System static calls to TimeProvider --- .../api/dd-sdk-android-core.api | 9 + .../kotlin/com/datadog/android/api/SdkCore.kt | 6 + .../android/core/internal/CoreFeature.kt | 9 + .../android/core/internal/DatadogCore.kt | 4 + .../core/internal/NoOpInternalSdkCore.kt | 9 +- .../android/core/internal/SdkFeature.kt | 3 +- .../data/upload/RotatingDnsResolver.kt | 5 +- .../internal/metrics/MethodCalledTelemetry.kt | 5 +- .../file/advanced/FeatureFileOrchestrator.kt | 10 +- .../file/batch/BatchFileOrchestrator.kt | 18 +- .../thread/ObservableLinkedBlockingQueue.kt | 5 +- .../internal/thread/ThreadPoolExecutorExt.kt | 5 +- .../time/DefaultAppStartTimeProvider.kt | 7 +- .../core/internal/time/KronosTimeProvider.kt | 12 +- .../android/core/internal/utils/MiscUtils.kt | 7 +- .../android/core/time/SdkTimeProvider.kt | 32 ++++ .../internal/logger/SdkInternalLoggerTest.kt | 5 +- .../metrics/BatchMetricsDispatcherTest.kt | 3 +- .../metrics/MethodCalledTelemetryTest.kt | 5 +- .../advanced/FeatureFileOrchestratorTest.kt | 13 +- .../file/batch/BatchFileOrchestratorTest.kt | 155 ++++++++++-------- .../time/DefaultAppStartTimeProviderTest.kt | 3 +- .../internal/time/KronosTimeProviderTest.kt | 7 +- .../utils/forge/NdkCrashLogForgeryFactory.kt | 3 +- dd-sdk-android-internal/api/apiSurface | 2 + .../api/dd-sdk-android-internal.api | 2 + .../internal/profiler/DDExecutionTimer.kt | 10 +- .../internal/time/DefaultTimeProvider.kt | 2 + .../android/internal/time/TimeProvider.kt | 6 + .../android/rum/DdRumContentProvider.kt | 3 +- .../flags/internal/ExposureEventsProcessor.kt | 8 +- .../android/flags/internal/FlagsFeature.kt | 5 +- .../flags/internal/model/FlagsStateEntry.kt | 3 +- .../persistence/FlagsPersistenceManager.kt | 4 +- .../repository/DefaultFlagsRepository.kt | 3 +- .../internal/ExposureEventsProcessorTest.kt | 20 ++- .../flags/internal/FlagsFeatureTest.kt | 5 + .../persistence/FlagsStateDeserializerTest.kt | 11 +- .../persistence/FlagsStateSerializerTest.kt | 3 +- .../repository/DefaultFlagsRepositoryTest.kt | 5 + .../log/internal/logger/DatadogLogHandler.kt | 4 +- .../android/log/internal/LogsFeatureTest.kt | 3 +- .../domain/DatadogLogGeneratorTest.kt | 3 +- .../internal/logger/DatadogLogHandlerTest.kt | 23 ++- .../com/datadog/android/ndk/NdkTests.kt | 8 +- .../ndk/internal/NdkCrashReportsFeature.kt | 4 +- .../internal/NdkCrashReportsFeatureTest.kt | 2 + .../rum/internal/DatadogLateCrashReporter.kt | 2 +- .../android/rum/internal/RumFeature.kt | 3 +- .../android/rum/internal/domain/Time.kt | 27 +-- .../DefaultAccessibilityReader.kt | 7 +- .../internal/domain/scope/RumSessionScope.kt | 5 +- .../MainLooperLongTaskStrategy.kt | 3 +- .../metric/slowframes/SlowFramesListener.kt | 3 +- .../internal/startup/RumAppStartupDetector.kt | 3 +- .../startup/RumFirstDrawTimeReporter.kt | 3 +- .../rum/resource/RumResourceInputStream.kt | 19 ++- .../kotlin/com/datadog/android/rum/RumTest.kt | 5 + .../internal/DatadogLateCrashReporterTest.kt | 32 ++-- .../android/rum/internal/RumFeatureTest.kt | 5 + .../android/rum/internal/domain/TimeTest.kt | 17 +- .../DefaultAccessibilityReaderTest.kt | 7 +- .../battery/DefaultBatteryInfoProviderTest.kt | 3 +- ...pplicationScopeAttributePropagationTest.kt | 5 +- .../RumViewScopeAttributePropagationTest.kt | 5 +- .../internal/domain/scope/RumViewScopeTest.kt | 44 +++-- .../ActionTypeInteractionValidatorTest.kt | 13 +- ...InteractionToNextViewMetricResolverTest.kt | 23 +-- .../internal/monitor/DatadogRumMonitorTest.kt | 3 +- .../TimeBasedInteractionIdentifierTest.kt | 5 +- .../NetworkSettledMetricResolverTest.kt | 23 +-- .../async/RecordedDataQueueHandler.kt | 3 +- .../internal/async/RecordedDataQueueItem.kt | 3 +- .../processor/RecordedDataProcessor.kt | 5 +- .../internal/recorder/Debouncer.kt | 13 +- .../callback/RecorderWindowCallback.kt | 13 +- .../resources/ResourceDataStoreManager.kt | 5 +- .../async/RecordedDataQueueHandlerTest.kt | 13 +- .../resources/ResourceDataStoreManagerTest.kt | 5 +- .../java/com/datadog/trace/api/Config.java | 7 +- .../trace/api/IdGenerationStrategy.java | 8 +- .../trace/api/time/SystemTimeSource.java | 12 +- .../scopemanager/ContinuableScopeManager.java | 7 +- .../trace/util/AgentTaskScheduler.java | 10 +- .../trace/api/IdGenerationStrategyTest.kt | 5 +- .../datadog/trace/core/CoreSpanBuilderTest.kt | 5 +- .../com/datadog/trace/core/DDSpanTest.kt | 45 ++--- .../trace/core/PendingTraceTestBase.kt | 3 +- .../trace/internal/DatadogSpanLogger.kt | 2 +- .../trace/internal/DatadogSpanLoggerTest.kt | 7 + .../utils/forge/SpanEventForgeryFactory.kt | 3 +- .../rum/domain/WebViewNativeRumViewsCache.kt | 7 +- .../domain/WebViewNativeRumViewsCacheTest.kt | 9 +- .../integration/tests/InternalSdkCoreTest.kt | 6 +- .../rum/integration/ManualTrackingRumTest.kt | 15 +- .../ViewLoadingTimeMetricsTests.kt | 17 +- .../otel/OtelTracerProviderTest.kt | 22 ++- .../core/stub/StubScheduledExecutorService.kt | 21 ++- .../sample/data/db/room/RoomDataSource.kt | 6 +- .../db/sqldelight/SqlDelightDataSource.kt | 6 +- .../sample/data/db/sqlite/SQLiteDataSource.kt | 5 +- .../internal/reader/FpsVitalReader.kt | 7 +- .../java/forge/SpanEventForgeryFactory.kt | 3 +- 103 files changed, 685 insertions(+), 357 deletions(-) create mode 100644 dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/time/SdkTimeProvider.kt diff --git a/dd-sdk-android-core/api/dd-sdk-android-core.api b/dd-sdk-android-core/api/dd-sdk-android-core.api index 7735430a37..93ea2a208c 100644 --- a/dd-sdk-android-core/api/dd-sdk-android-core.api +++ b/dd-sdk-android-core/api/dd-sdk-android-core.api @@ -135,6 +135,7 @@ public abstract interface class com/datadog/android/api/SdkCore { public abstract fun getName ()Ljava/lang/String; public abstract fun getService ()Ljava/lang/String; public abstract fun getTime ()Lcom/datadog/android/api/context/TimeInfo; + public abstract fun getTimeProvider ()Lcom/datadog/android/internal/time/TimeProvider; public abstract fun isCoreActive ()Z public abstract fun setAccountInfo (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)V public abstract fun setTrackingConsent (Lcom/datadog/android/privacy/TrackingConsent;)V @@ -956,6 +957,14 @@ public abstract interface class com/datadog/android/core/thread/FlushableExecuto public abstract fun create (Lcom/datadog/android/api/InternalLogger;Ljava/lang/String;Lcom/datadog/android/core/configuration/BackPressureStrategy;)Lcom/datadog/android/core/thread/FlushableExecutorService; } +public final class com/datadog/android/core/time/SdkTimeProvider { + public static final field INSTANCE Lcom/datadog/android/core/time/SdkTimeProvider; + public final fun elapsed ()J + public final fun getProvider ()Lcom/datadog/android/internal/time/TimeProvider; + public final fun now ()J + public final fun setProvider (Lcom/datadog/android/internal/time/TimeProvider;)V +} + public abstract interface class com/datadog/android/event/EventMapper { public abstract fun map (Ljava/lang/Object;)Ljava/lang/Object; } diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/api/SdkCore.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/api/SdkCore.kt index 5ad1b6aebc..f75c1a6001 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/api/SdkCore.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/api/SdkCore.kt @@ -9,6 +9,7 @@ package com.datadog.android.api import androidx.annotation.AnyThread import com.datadog.android.api.context.TimeInfo import com.datadog.android.api.context.UserInfo +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.privacy.TrackingConsent /** @@ -27,6 +28,11 @@ interface SdkCore { */ val time: TimeInfo + /** + * The [TimeProvider] used by this core instance for current timestamps. + */ + val timeProvider: TimeProvider + /** * Name of the service (given during the SDK initialization, otherwise package name is used). */ diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt index 06aa2b35dd..8911ffcccd 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt @@ -72,6 +72,7 @@ import com.datadog.android.core.internal.utils.executeSafe import com.datadog.android.core.internal.utils.unboundInternalLogger import com.datadog.android.core.persistence.PersistenceStrategy import com.datadog.android.core.thread.FlushableExecutorService +import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.internal.utils.allowThreadDiskReads @@ -155,6 +156,14 @@ internal class CoreFeature( internal var networkInfoProvider: NetworkInfoProvider = NoOpNetworkInfoProvider() internal var systemInfoProvider: SystemInfoProvider = NoOpSystemInfoProvider() internal var timeProvider: TimeProvider = DefaultTimeProvider() + set(value) { + field = value + SdkTimeProvider.provider = value + } + + init { + SdkTimeProvider.provider = timeProvider + } internal var trackingConsentProvider: ConsentProvider = NoOpConsentProvider() internal var userInfoProvider: MutableUserInfoProvider = NoOpMutableUserInfoProvider() internal var accountInfoProvider: MutableAccountInfoProvider = NoOpMutableAccountInfoProvider() diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogCore.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogCore.kt index a6bc76c13f..b230de29c6 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogCore.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogCore.kt @@ -41,6 +41,7 @@ import com.datadog.android.core.internal.utils.submitSafe import com.datadog.android.core.thread.FlushableExecutorService import com.datadog.android.error.internal.CrashReportsFeature import com.datadog.android.internal.telemetry.InternalTelemetryEvent +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.privacy.TrackingConsent import com.google.gson.JsonObject import okhttp3.Call @@ -117,6 +118,9 @@ internal class DatadogCore( override val service: String get() = coreFeature.serviceName + override val timeProvider: TimeProvider + get() = coreFeature.timeProvider + /** @inheritDoc */ override val firstPartyHostResolver: FirstPartyHostHeaderTypeResolver get() = coreFeature.firstPartyHostHeaderTypeResolver diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpInternalSdkCore.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpInternalSdkCore.kt index 2775bb0bfd..977be7548d 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpInternalSdkCore.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpInternalSdkCore.kt @@ -19,6 +19,8 @@ import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.internal.logger.SdkInternalLogger import com.datadog.android.core.internal.net.DefaultFirstPartyHostHeaderTypeResolver import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver +import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.privacy.TrackingConsent import com.google.gson.JsonObject import okhttp3.Call @@ -43,9 +45,11 @@ import java.util.concurrent.TimeUnit */ internal object NoOpInternalSdkCore : InternalSdkCore { + private val internalTimeProvider: TimeProvider = DefaultTimeProvider() + override val name: String = "no-op" - override val time: TimeInfo = with(System.currentTimeMillis()) { + override val time: TimeInfo = with(internalTimeProvider.getDeviceTimestamp()) { TimeInfo( deviceTimeNs = TimeUnit.MILLISECONDS.toNanos(this), serverTimeNs = TimeUnit.MILLISECONDS.toNanos(this), @@ -54,6 +58,9 @@ internal object NoOpInternalSdkCore : InternalSdkCore { ) } + override val timeProvider: TimeProvider + get() = internalTimeProvider + override val service: String get() = "" diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/SdkFeature.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/SdkFeature.kt index 73f46f8d11..b0d736e45e 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/SdkFeature.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/SdkFeature.kt @@ -366,7 +366,8 @@ internal class SdkFeature( executorService = coreFeature.persistenceExecutorService, filePersistenceConfig = filePersistenceConfig, internalLogger = internalLogger, - metricsDispatcher = metricsDispatcher + metricsDispatcher = metricsDispatcher, + timeProvider = coreFeature.timeProvider ) this.fileOrchestrator = fileOrchestrator diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/data/upload/RotatingDnsResolver.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/data/upload/RotatingDnsResolver.kt index a176a6bdb6..faef621878 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/data/upload/RotatingDnsResolver.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/data/upload/RotatingDnsResolver.kt @@ -6,6 +6,7 @@ package com.datadog.android.core.internal.data.upload +import com.datadog.android.core.time.SdkTimeProvider import okhttp3.Dns import java.net.InetAddress import kotlin.time.Duration @@ -21,10 +22,10 @@ internal class RotatingDnsResolver( val hostname: String, val addresses: MutableList ) { - private val resolutionTimestamp: Long = System.nanoTime() + private val resolutionTimestamp: Long = SdkTimeProvider.elapsed() fun getAge(): Duration { - return (System.nanoTime() - resolutionTimestamp).nanoseconds + return (SdkTimeProvider.elapsed() - resolutionTimestamp).nanoseconds } fun rotate() { diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetry.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetry.kt index 1bb0c6d646..3efa7ae40b 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetry.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetry.kt @@ -10,6 +10,7 @@ import com.datadog.android.api.InternalLogger import com.datadog.android.core.metrics.MethodCallSamplingRate import com.datadog.android.core.metrics.PerformanceMetric import com.datadog.android.core.metrics.PerformanceMetric.Companion.METRIC_TYPE +import com.datadog.android.core.time.SdkTimeProvider /** * Performance metric to measure the execution time for a method. @@ -24,11 +25,11 @@ internal class MethodCalledTelemetry( internal val operationName: String, internal val callerClass: String, internal val creationSampleRate: Float, - internal val startTime: Long = System.nanoTime() + internal val startTime: Long = SdkTimeProvider.elapsed() ) : PerformanceMetric { override fun stopAndSend(isSuccessful: Boolean) { - val executionTime = System.nanoTime() - startTime + val executionTime = SdkTimeProvider.elapsed() - startTime val additionalProperties: MutableMap = mutableMapOf() additionalProperties[EXECUTION_TIME] = executionTime diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/advanced/FeatureFileOrchestrator.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/advanced/FeatureFileOrchestrator.kt index c776b234c6..43d32d786c 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/advanced/FeatureFileOrchestrator.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/advanced/FeatureFileOrchestrator.kt @@ -13,6 +13,7 @@ import com.datadog.android.core.internal.persistence.file.FileOrchestrator import com.datadog.android.core.internal.persistence.file.FilePersistenceConfig import com.datadog.android.core.internal.persistence.file.batch.BatchFileOrchestrator import com.datadog.android.core.internal.privacy.ConsentProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.privacy.TrackingConsent import java.io.File import java.util.Locale @@ -41,20 +42,23 @@ internal class FeatureFileOrchestrator( executorService: ExecutorService, filePersistenceConfig: FilePersistenceConfig, internalLogger: InternalLogger, - metricsDispatcher: MetricsDispatcher + metricsDispatcher: MetricsDispatcher, + timeProvider: TimeProvider ) : this( consentProvider, BatchFileOrchestrator( File(storageDir, PENDING_DIR.format(Locale.US, featureName)), filePersistenceConfig, internalLogger, - metricsDispatcher + metricsDispatcher, + timeProvider ), BatchFileOrchestrator( File(storageDir, GRANTED_DIR.format(Locale.US, featureName)), filePersistenceConfig, internalLogger, - metricsDispatcher + metricsDispatcher, + timeProvider ), ConsentAwareFileMigrator( FileMover(internalLogger), diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestrator.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestrator.kt index dc689cdd38..01a740f6b9 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestrator.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestrator.kt @@ -19,6 +19,7 @@ import com.datadog.android.core.internal.persistence.file.existsSafe import com.datadog.android.core.internal.persistence.file.lengthSafe import com.datadog.android.core.internal.persistence.file.listFilesSafe import com.datadog.android.core.internal.persistence.file.mkdirsSafe +import com.datadog.android.internal.time.TimeProvider import java.io.File import java.io.FileFilter import java.util.Locale @@ -33,6 +34,7 @@ internal class BatchFileOrchestrator( internal val config: FilePersistenceConfig, private val internalLogger: InternalLogger, private val metricsDispatcher: MetricsDispatcher, + private val timeProvider: TimeProvider, private val pendingFiles: AtomicInteger = AtomicInteger(0) ) : FileOrchestrator { @@ -64,7 +66,7 @@ internal class BatchFileOrchestrator( var files = listBatchFiles() files = deleteObsoleteFiles(files) freeSpaceIfNeeded(files) - lastCleanupTimestamp = System.currentTimeMillis() + lastCleanupTimestamp = timeProvider.getDeviceTimestamp() } return getReusableWritableFile() ?: createNewFile() @@ -79,7 +81,7 @@ internal class BatchFileOrchestrator( val files = listSortedBatchFiles().let { deleteObsoleteFiles(it) } - lastCleanupTimestamp = System.currentTimeMillis() + lastCleanupTimestamp = timeProvider.getDeviceTimestamp() pendingFiles.set(files.count()) return files.firstOrNull { @@ -202,7 +204,7 @@ internal class BatchFileOrchestrator( } private fun createNewFile(): File { - val newFileName = System.currentTimeMillis().toString() + val newFileName = timeProvider.getDeviceTimestamp().toString() val newFile = File(rootDir, newFileName) val closedFile = previousFile val closedFileLastAccessTimestamp = lastFileAccessTimestamp @@ -217,7 +219,7 @@ internal class BatchFileOrchestrator( } previousFile = newFile previousFileItemCount = 1 - lastFileAccessTimestamp = System.currentTimeMillis() + lastFileAccessTimestamp = timeProvider.getDeviceTimestamp() pendingFiles.incrementAndGet() return newFile } @@ -244,7 +246,7 @@ internal class BatchFileOrchestrator( return if (isRecentEnough && hasRoomForMore && hasSlotForMore) { previousFileItemCount = lastKnownFileItemCount + 1 - lastFileAccessTimestamp = System.currentTimeMillis() + lastFileAccessTimestamp = timeProvider.getDeviceTimestamp() lastFile } else { null @@ -252,13 +254,13 @@ internal class BatchFileOrchestrator( } private fun isFileRecent(file: File, delayMs: Long): Boolean { - val now = System.currentTimeMillis() + val now = timeProvider.getDeviceTimestamp() val fileTimestamp = file.name.toLongOrNull() ?: 0L return fileTimestamp >= (now - delayMs) } private fun deleteObsoleteFiles(files: List): List { - val threshold = System.currentTimeMillis() - config.oldFileThreshold + val threshold = timeProvider.getDeviceTimestamp() - config.oldFileThreshold return files .mapNotNull { val isOldFile = (it.name.toLongOrNull() ?: 0) < threshold @@ -328,7 +330,7 @@ internal class BatchFileOrchestrator( } private fun canDoCleanup(): Boolean { - return System.currentTimeMillis() - lastCleanupTimestamp > config.cleanupFrequencyThreshold + return timeProvider.getDeviceTimestamp() - lastCleanupTimestamp > config.cleanupFrequencyThreshold } private val File.metadata: File diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/ObservableLinkedBlockingQueue.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/ObservableLinkedBlockingQueue.kt index 8402dc322e..e170f5bf8c 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/ObservableLinkedBlockingQueue.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/ObservableLinkedBlockingQueue.kt @@ -7,6 +7,7 @@ package com.datadog.android.core.internal.thread import com.datadog.android.internal.thread.NamedExecutionUnit +import com.datadog.android.internal.time.DefaultTimeProvider import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicLong @@ -16,12 +17,12 @@ internal open class ObservableLinkedBlockingQueue : LinkedBlockingQueue private val currentTimeProvider: () -> Long constructor( - currentTimeProvider: () -> Long = { System.currentTimeMillis() } + currentTimeProvider: () -> Long = { DefaultTimeProvider().getDeviceTimestamp() } ) : this(Int.MAX_VALUE, currentTimeProvider) constructor( capacity: Int, - currentTimeProvider: () -> Long = { System.currentTimeMillis() } + currentTimeProvider: () -> Long = { DefaultTimeProvider().getDeviceTimestamp() } ) : super(capacity) { this.currentTimeProvider = currentTimeProvider } diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/ThreadPoolExecutorExt.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/ThreadPoolExecutorExt.kt index 7def26113d..2654bb9899 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/ThreadPoolExecutorExt.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/ThreadPoolExecutorExt.kt @@ -7,13 +7,14 @@ package com.datadog.android.core.internal.thread import com.datadog.android.api.InternalLogger +import com.datadog.android.core.time.SdkTimeProvider import java.util.concurrent.ThreadPoolExecutor import java.util.concurrent.TimeUnit internal const val MAX_SLEEP_DURATION_IN_MS = 10L internal fun ThreadPoolExecutor.waitToIdle(timeoutInMs: Long, internalLogger: InternalLogger): Boolean { - val startTime = System.nanoTime() + val startTime = SdkTimeProvider.elapsed() val timeoutInNs = TimeUnit.MILLISECONDS.toNanos(timeoutInMs) val sleepDurationInMs = timeoutInMs.coerceIn(0, MAX_SLEEP_DURATION_IN_MS) var interrupted: Boolean @@ -22,7 +23,7 @@ internal fun ThreadPoolExecutor.waitToIdle(timeoutInMs: Long, internalLogger: In return true } interrupted = sleepSafe(sleepDurationInMs, internalLogger) - } while (((System.nanoTime() - startTime) < timeoutInNs) && !interrupted) + } while (((SdkTimeProvider.elapsed() - startTime) < timeoutInNs) && !interrupted) return isIdle() } diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProvider.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProvider.kt index 1209ec2130..d9ca77cb6b 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProvider.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProvider.kt @@ -11,11 +11,14 @@ import android.os.Build import android.os.Process import android.os.SystemClock import com.datadog.android.core.internal.system.BuildSdkVersionProvider +import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.DdRumContentProvider import java.util.concurrent.TimeUnit internal class DefaultAppStartTimeProvider( - buildSdkVersionProvider: BuildSdkVersionProvider = BuildSdkVersionProvider.DEFAULT + buildSdkVersionProvider: BuildSdkVersionProvider = BuildSdkVersionProvider.DEFAULT, + private val timeProvider: TimeProvider = DefaultTimeProvider() ) : AppStartTimeProvider { override val appStartTimeNs: Long by lazy(LazyThreadSafetyMode.PUBLICATION) { @@ -23,7 +26,7 @@ internal class DefaultAppStartTimeProvider( when { buildSdkVersionProvider.version >= Build.VERSION_CODES.N -> { val diffMs = SystemClock.elapsedRealtime() - Process.getStartElapsedRealtime() - System.nanoTime() - TimeUnit.MILLISECONDS.toNanos(diffMs) + timeProvider.getDeviceElapsedTimeNs() - TimeUnit.MILLISECONDS.toNanos(diffMs) } else -> DdRumContentProvider.createTimeNs } diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/KronosTimeProvider.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/KronosTimeProvider.kt index 809c47706c..2fc7e6c403 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/KronosTimeProvider.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/KronosTimeProvider.kt @@ -6,25 +6,31 @@ package com.datadog.android.core.internal.time +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.lyft.kronos.Clock import java.util.concurrent.TimeUnit internal class KronosTimeProvider( - private val clock: Clock + private val clock: Clock, + private val deviceTimeProvider: DefaultTimeProvider = DefaultTimeProvider() ) : TimeProvider { override fun getDeviceTimestamp(): Long { - return System.currentTimeMillis() + return deviceTimeProvider.getDeviceTimestamp() } override fun getServerTimestamp(): Long { return clock.getCurrentTimeMs() } + override fun getDeviceElapsedTimeNs(): Long { + return deviceTimeProvider.getDeviceElapsedTimeNs() + } + override fun getServerOffsetMillis(): Long { val server = clock.getCurrentTimeMs() - val device = System.currentTimeMillis() + val device = deviceTimeProvider.getDeviceTimestamp() val delta = server - device return delta } diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/utils/MiscUtils.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/utils/MiscUtils.kt index 2663481ea3..8d05258139 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/utils/MiscUtils.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/utils/MiscUtils.kt @@ -7,6 +7,7 @@ package com.datadog.android.core.internal.utils import com.datadog.android.api.InternalLogger +import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.internal.utils.NULL_MAP_VALUE import com.datadog.android.lint.InternalApi import com.google.gson.JsonArray @@ -37,9 +38,9 @@ internal inline fun retryWithDelay( ): Boolean { var retryCounter = 1 var wasSuccessful = false - var loopTimeOrigin = System.nanoTime() - loopsDelayInNanos + var loopTimeOrigin = SdkTimeProvider.elapsed() - loopsDelayInNanos while (retryCounter <= times && !wasSuccessful) { - if ((System.nanoTime() - loopTimeOrigin) >= loopsDelayInNanos) { + if ((SdkTimeProvider.elapsed() - loopTimeOrigin) >= loopsDelayInNanos) { wasSuccessful = try { block() } catch (e: Exception) { @@ -54,7 +55,7 @@ internal inline fun retryWithDelay( ) false } - loopTimeOrigin = System.nanoTime() + loopTimeOrigin = SdkTimeProvider.elapsed() retryCounter++ } } diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/time/SdkTimeProvider.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/time/SdkTimeProvider.kt new file mode 100644 index 0000000000..10d2969308 --- /dev/null +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/time/SdkTimeProvider.kt @@ -0,0 +1,32 @@ +/* + * 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.time + +import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.TimeProvider + +/** + * Global accessor to the [com.datadog.android.internal.time.TimeProvider] configured by the core. + */ +object SdkTimeProvider { + + /** + * Returns the [com.datadog.android.internal.time.TimeProvider] currently used by the SDK. + */ + @Volatile + var provider: TimeProvider = DefaultTimeProvider() + + /** + * Returns our best estimate of the current wall-clock time in milliseconds. + */ + fun now(): Long = provider.getDeviceTimestamp() + + /** + * Returns the monotonic elapsed time in nanoseconds since boot. + */ + fun elapsed(): Long = provider.getDeviceElapsedTimeNs() +} diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/logger/SdkInternalLoggerTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/logger/SdkInternalLoggerTest.kt index 9f3894f102..9c6df3c809 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/logger/SdkInternalLoggerTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/logger/SdkInternalLoggerTest.kt @@ -16,6 +16,7 @@ import com.datadog.android.core.internal.metrics.MethodCalledTelemetry import com.datadog.android.core.metrics.TelemetryMetricType import com.datadog.android.internal.attributes.LocalAttribute import com.datadog.android.internal.telemetry.InternalTelemetryEvent +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.utils.forge.Configurator import com.datadog.tools.unit.forge.aThrowable import com.datadog.tools.unit.forge.exhaustiveAttributes @@ -766,7 +767,7 @@ internal class SdkInternalLoggerTest { @StringForgery fakeOperation: String ) { // Given - val startNs = System.nanoTime() + val startNs = DefaultTimeProvider().getDeviceElapsedTimeNs() // When val result = testedInternalLogger.startPerformanceMeasure( @@ -775,7 +776,7 @@ internal class SdkInternalLoggerTest { 100f, fakeOperation ) - val endNs = System.nanoTime() + val endNs = DefaultTimeProvider().getDeviceElapsedTimeNs() // Then val methodCalledTelemetry = result as? MethodCalledTelemetry diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/BatchMetricsDispatcherTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/BatchMetricsDispatcherTest.kt index 5e0a9a4b48..e6e479fdbe 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/BatchMetricsDispatcherTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/BatchMetricsDispatcherTest.kt @@ -10,6 +10,7 @@ import com.datadog.android.api.InternalLogger import com.datadog.android.api.feature.Feature import com.datadog.android.core.internal.configuration.DataUploadConfiguration import com.datadog.android.core.internal.persistence.file.FilePersistenceConfig +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import fr.xgouchet.elmyr.Forge @@ -57,7 +58,7 @@ internal class BatchMetricsDispatcherTest { @Mock lateinit var mockDateTimeProvider: TimeProvider - private var currentTimeInMillis: Long = System.currentTimeMillis() + private var currentTimeInMillis: Long = DefaultTimeProvider().getDeviceTimestamp() @Mock lateinit var mockInternalLogger: InternalLogger diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetryTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetryTest.kt index 2485f16e0d..ceafa093c0 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetryTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetryTest.kt @@ -17,6 +17,7 @@ import com.datadog.android.core.internal.metrics.MethodCalledTelemetry.Companion import com.datadog.android.core.internal.metrics.MethodCalledTelemetry.Companion.METRIC_TYPE_VALUE import com.datadog.android.core.internal.metrics.MethodCalledTelemetry.Companion.OPERATION_NAME import com.datadog.android.core.metrics.PerformanceMetric.Companion.METRIC_TYPE +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.tools.unit.extensions.TestConfigurationExtension import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.FloatForgery @@ -105,7 +106,7 @@ internal class MethodCalledTelemetryTest { whenever(mockDeviceInfo.osVersion).thenReturn(fakeOsVersion) whenever(mockDeviceInfo.deviceBuildId).thenReturn(fakeOsBuild) - fakeStartTime = System.nanoTime() + fakeStartTime = DefaultTimeProvider().getDeviceElapsedTimeNs() testedMethodCalledTelemetry = MethodCalledTelemetry( internalLogger = mockInternalLogger, operationName = fakeOperationName, @@ -137,7 +138,7 @@ internal class MethodCalledTelemetryTest { verify(mockInternalLogger).logMetric(any(), mapCaptor.capture(), eq(100.0f), eq(fakeCreationSampleRate)) val executionTime = mapCaptor.firstValue[EXECUTION_TIME] as Long - assertThat(executionTime).isLessThan(System.nanoTime() - fakeStartTime) + assertThat(executionTime).isLessThan(DefaultTimeProvider().getDeviceElapsedTimeNs() - fakeStartTime) } @Test diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/FeatureFileOrchestratorTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/FeatureFileOrchestratorTest.kt index 27fcecabbf..651b1234ce 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/FeatureFileOrchestratorTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/FeatureFileOrchestratorTest.kt @@ -11,6 +11,7 @@ import com.datadog.android.core.internal.metrics.MetricsDispatcher import com.datadog.android.core.internal.persistence.file.FilePersistenceConfig import com.datadog.android.core.internal.persistence.file.batch.BatchFileOrchestrator import com.datadog.android.core.internal.privacy.ConsentProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.privacy.TrackingConsent import com.datadog.android.utils.forge.Configurator import fr.xgouchet.elmyr.annotation.Forgery @@ -64,6 +65,9 @@ internal class FeatureFileOrchestratorTest { @Mock lateinit var mockMetricsDispatcher: MetricsDispatcher + @Mock + lateinit var mockTimeProvider: TimeProvider + @BeforeEach fun `set up`() { whenever(mockConsentProvider.getConsent()) doReturn fakeConsent @@ -81,7 +85,8 @@ internal class FeatureFileOrchestratorTest { mockExecutorService, fakeFilePersistenceConfig, mockInternalLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + mockTimeProvider ) // Then @@ -103,7 +108,8 @@ internal class FeatureFileOrchestratorTest { mockExecutorService, fakeFilePersistenceConfig, mockInternalLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + mockTimeProvider ) // Then @@ -125,7 +131,8 @@ internal class FeatureFileOrchestratorTest { mockExecutorService, fakeFilePersistenceConfig, mockInternalLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + mockTimeProvider ) // Then diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt index 8d83ceeee4..483b214e4f 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt @@ -12,6 +12,8 @@ import com.datadog.android.core.internal.metrics.MetricsDispatcher import com.datadog.android.core.internal.metrics.RemovalReason import com.datadog.android.core.internal.persistence.file.FileOrchestrator import com.datadog.android.core.internal.persistence.file.FilePersistenceConfig +import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import com.datadog.android.utils.verifyLog import fr.xgouchet.elmyr.Forge @@ -35,7 +37,6 @@ import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock -import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.verifyNoMoreInteractions @@ -77,8 +78,11 @@ internal class BatchFileOrchestratorTest { @Mock lateinit var mockPendingFiles: AtomicInteger + lateinit var fakeTimeProvider: TimeProvider + @BeforeEach fun `set up`() { + fakeTimeProvider = DefaultTimeProvider() whenever(mockPendingFiles.decrementAndGet()).thenReturn(fakePendingBatches) whenever(mockPendingFiles.incrementAndGet()).thenReturn(fakePendingBatches) fakeRootDir = File(tempDir, fakeRootDirName) @@ -88,6 +92,7 @@ internal class BatchFileOrchestratorTest { config = TEST_PERSISTENCE_CONFIG, internalLogger = mockLogger, metricsDispatcher = mockMetricsDispatcher, + timeProvider = fakeTimeProvider, pendingFiles = mockPendingFiles ) } @@ -110,9 +115,9 @@ internal class BatchFileOrchestratorTest { @Test fun `M send batch_closed metric W getWritableFile()`() { // Given - val lowerTimestamp = System.currentTimeMillis() + val lowerTimestamp = DefaultTimeProvider().getDeviceTimestamp() val oldFile = testedOrchestrator.getWritableFile() - val upperTimestamp = System.currentTimeMillis() + val upperTimestamp = DefaultTimeProvider().getDeviceTimestamp() Thread.sleep(RECENT_DELAY_MS + 1) // When @@ -143,10 +148,11 @@ internal class BatchFileOrchestratorTest { val notADir = File(fakeRootDir, fileName) notADir.createNewFile() testedOrchestrator = BatchFileOrchestrator( - notADir, - TEST_PERSISTENCE_CONFIG, - mockLogger, - mockMetricsDispatcher + rootDir = notADir, + config = TEST_PERSISTENCE_CONFIG, + internalLogger = mockLogger, + metricsDispatcher = mockMetricsDispatcher, + timeProvider = fakeTimeProvider ) // When @@ -170,10 +176,11 @@ internal class BatchFileOrchestratorTest { whenever(corruptedDir.mkdirs()).thenReturn(false) whenever(corruptedDir.path) doReturn fakeRootDir.path testedOrchestrator = BatchFileOrchestrator( - corruptedDir, - TEST_PERSISTENCE_CONFIG, - mockLogger, - mockMetricsDispatcher + rootDir = corruptedDir, + config = TEST_PERSISTENCE_CONFIG, + internalLogger = mockLogger, + metricsDispatcher = mockMetricsDispatcher, + timeProvider = fakeTimeProvider ) // When @@ -198,10 +205,11 @@ internal class BatchFileOrchestratorTest { whenever(restrictedDir.canWrite()).thenReturn(false) whenever(restrictedDir.path) doReturn fakeRootDir.path testedOrchestrator = BatchFileOrchestrator( - restrictedDir, - TEST_PERSISTENCE_CONFIG, - mockLogger, - mockMetricsDispatcher + rootDir = restrictedDir, + config = TEST_PERSISTENCE_CONFIG, + internalLogger = mockLogger, + metricsDispatcher = mockMetricsDispatcher, + timeProvider = fakeTimeProvider ) // When @@ -236,19 +244,19 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val oldTimestamp = System.currentTimeMillis() - oldFileAge + val oldTimestamp = DefaultTimeProvider().getDeviceTimestamp() - oldFileAge val oldFile = File(fakeRootDir, oldTimestamp.toString()) oldFile.createNewFile() val oldFileMeta = File("${oldFile.path}_metadata") oldFileMeta.createNewFile() - val youngTimestamp = System.currentTimeMillis() - RECENT_DELAY_MS - 1 + val youngTimestamp = DefaultTimeProvider().getDeviceTimestamp() - RECENT_DELAY_MS - 1 val youngFile = File(fakeRootDir, youngTimestamp.toString()) youngFile.createNewFile() // When - val start = System.currentTimeMillis() + val start = DefaultTimeProvider().getDeviceTimestamp() val result = testedOrchestrator.getWritableFile() - val end = System.currentTimeMillis() + val end = DefaultTimeProvider().getDeviceTimestamp() // Then checkNotNull(result) @@ -274,19 +282,19 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val oldTimestamp = System.currentTimeMillis() - oldFileAge + val oldTimestamp = DefaultTimeProvider().getDeviceTimestamp() - oldFileAge val oldFile = File(fakeRootDir, oldTimestamp.toString()) oldFile.createNewFile() val oldFileMeta = File("${oldFile.path}_metadata") oldFileMeta.createNewFile() - val youngTimestamp = System.currentTimeMillis() - RECENT_DELAY_MS - 1 + val youngTimestamp = DefaultTimeProvider().getDeviceTimestamp() - RECENT_DELAY_MS - 1 val youngFile = File(fakeRootDir, youngTimestamp.toString()) youngFile.createNewFile() // When - val start = System.currentTimeMillis() + val start = DefaultTimeProvider().getDeviceTimestamp() val result = testedOrchestrator.getWritableFile() - val end = System.currentTimeMillis() + val end = DefaultTimeProvider().getDeviceTimestamp() // let's add very old file after the previous cleanup call. If threshold is respected, // cleanup shouldn't be performed during the next getWritableFile call val evenOlderFile = File(fakeRootDir, (oldTimestamp - 1).toString()) @@ -317,16 +325,16 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val oldTimestamp = System.currentTimeMillis() - oldFileAge + val oldTimestamp = DefaultTimeProvider().getDeviceTimestamp() - oldFileAge val oldFile = File(fakeRootDir, oldTimestamp.toString()) oldFile.createNewFile() val oldFileMeta = File("${oldFile.path}_metadata") oldFileMeta.createNewFile() // When - val start = System.currentTimeMillis() + val start = DefaultTimeProvider().getDeviceTimestamp() val result = testedOrchestrator.getWritableFile() - val end = System.currentTimeMillis() + val end = DefaultTimeProvider().getDeviceTimestamp() Thread.sleep(CLEANUP_FREQUENCY_THRESHOLD_MS + 1) val evenOlderFile = File(fakeRootDir, (oldTimestamp - 1).toString()) evenOlderFile.createNewFile() @@ -370,9 +378,9 @@ internal class BatchFileOrchestratorTest { assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) // When - val start = System.currentTimeMillis() + val start = DefaultTimeProvider().getDeviceTimestamp() val result = testedOrchestrator.getWritableFile() - val end = System.currentTimeMillis() + val end = DefaultTimeProvider().getDeviceTimestamp() // Then checkNotNull(result) @@ -411,18 +419,18 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val beforeFileCreateTimestamp = System.currentTimeMillis() + val beforeFileCreateTimestamp = DefaultTimeProvider().getDeviceTimestamp() val previousFile = testedOrchestrator.getWritableFile() - val afterFileCreateTimestamp = System.currentTimeMillis() + val afterFileCreateTimestamp = DefaultTimeProvider().getDeviceTimestamp() checkNotNull(previousFile) previousFile.writeText(previousData) Thread.sleep(RECENT_DELAY_MS + 1) // When - val start = System.currentTimeMillis() + val start = DefaultTimeProvider().getDeviceTimestamp() val result = testedOrchestrator.getWritableFile() - val end = System.currentTimeMillis() + val end = DefaultTimeProvider().getDeviceTimestamp() // Then checkNotNull(result) @@ -447,14 +455,14 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val previousFile = File(fakeRootDir, System.currentTimeMillis().toString()) + val previousFile = File(fakeRootDir, DefaultTimeProvider().getDeviceTimestamp().toString()) previousFile.writeText(previousData) Thread.sleep(1) // When - val start = System.currentTimeMillis() + val start = DefaultTimeProvider().getDeviceTimestamp() val result = testedOrchestrator.getWritableFile() - val end = System.currentTimeMillis() + val end = DefaultTimeProvider().getDeviceTimestamp() // Then checkNotNull(result) @@ -471,18 +479,18 @@ internal class BatchFileOrchestratorTest { fun `M return new File W getWritableFile() {previous file is deleted}`() { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val beforeFileCreateTimestamp = System.currentTimeMillis() + val beforeFileCreateTimestamp = DefaultTimeProvider().getDeviceTimestamp() val previousFile = testedOrchestrator.getWritableFile() - val afterFileCreateTimestamp = System.currentTimeMillis() + val afterFileCreateTimestamp = DefaultTimeProvider().getDeviceTimestamp() checkNotNull(previousFile) previousFile.createNewFile() previousFile.delete() Thread.sleep(1) // When - val start = System.currentTimeMillis() + val start = DefaultTimeProvider().getDeviceTimestamp() val result = testedOrchestrator.getWritableFile() - val end = System.currentTimeMillis() + val end = DefaultTimeProvider().getDeviceTimestamp() // Then checkNotNull(result) @@ -507,17 +515,17 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val beforeFileCreateTimestamp = System.currentTimeMillis() + val beforeFileCreateTimestamp = DefaultTimeProvider().getDeviceTimestamp() val previousFile = testedOrchestrator.getWritableFile() - val afterFileCreateTimestamp = System.currentTimeMillis() + val afterFileCreateTimestamp = DefaultTimeProvider().getDeviceTimestamp() checkNotNull(previousFile) previousFile.writeText(previousData) Thread.sleep(1) // When - val start = System.currentTimeMillis() + val start = DefaultTimeProvider().getDeviceTimestamp() val result = testedOrchestrator.getWritableFile() - val end = System.currentTimeMillis() + val end = DefaultTimeProvider().getDeviceTimestamp() // Then checkNotNull(result) @@ -542,7 +550,7 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val beforeFileCreateTimestamp = System.currentTimeMillis() + val beforeFileCreateTimestamp = DefaultTimeProvider().getDeviceTimestamp() var previousFile = testedOrchestrator.getWritableFile() repeat(4) { @@ -559,12 +567,12 @@ internal class BatchFileOrchestratorTest { assumeTrue(file == previousFile) file?.appendText(previousData[i]) } - val afterLastFileUsageTimestamp = System.currentTimeMillis() + val afterLastFileUsageTimestamp = DefaultTimeProvider().getDeviceTimestamp() // When - val start = System.currentTimeMillis() + val start = DefaultTimeProvider().getDeviceTimestamp() val nextFile = testedOrchestrator.getWritableFile() - val end = System.currentTimeMillis() + val end = DefaultTimeProvider().getDeviceTimestamp() // Then checkNotNull(nextFile) @@ -604,9 +612,9 @@ internal class BatchFileOrchestratorTest { // When Thread.sleep(CLEANUP_FREQUENCY_THRESHOLD_MS + 1) - val start = System.currentTimeMillis() + val start = DefaultTimeProvider().getDeviceTimestamp() val result = testedOrchestrator.getWritableFile() - val end = System.currentTimeMillis() + val end = DefaultTimeProvider().getDeviceTimestamp() // Then checkNotNull(result) @@ -643,7 +651,8 @@ internal class BatchFileOrchestratorTest { notADir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + fakeTimeProvider ) // When @@ -669,7 +678,8 @@ internal class BatchFileOrchestratorTest { corruptedDir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + fakeTimeProvider ) // When @@ -696,7 +706,8 @@ internal class BatchFileOrchestratorTest { restrictedDir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + fakeTimeProvider ) // When @@ -717,12 +728,12 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val oldTimestamp = System.currentTimeMillis() - oldFileAge + val oldTimestamp = DefaultTimeProvider().getDeviceTimestamp() - oldFileAge val oldFile = File(fakeRootDir, oldTimestamp.toString()) oldFile.createNewFile() val oldFileMeta = File("${oldFile.path}_metadata") oldFileMeta.createNewFile() - val youngTimestamp = System.currentTimeMillis() - RECENT_DELAY_MS - 1 + val youngTimestamp = DefaultTimeProvider().getDeviceTimestamp() - RECENT_DELAY_MS - 1 val youngFile = File(fakeRootDir, youngTimestamp.toString()) youngFile.createNewFile() @@ -764,7 +775,7 @@ internal class BatchFileOrchestratorTest { fun `M return file W getReadableFile() {existing old enough file}`() { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val timestamp = System.currentTimeMillis() - (RECENT_DELAY_MS * 2) + val timestamp = DefaultTimeProvider().getDeviceTimestamp() - (RECENT_DELAY_MS * 2) val file = File(fakeRootDir, timestamp.toString()) file.createNewFile() @@ -782,7 +793,7 @@ internal class BatchFileOrchestratorTest { fun `M return null W getReadableFile() {file is too recent}`() { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val timestamp = System.currentTimeMillis() - (RECENT_DELAY_MS / 2) + val timestamp = DefaultTimeProvider().getDeviceTimestamp() - (RECENT_DELAY_MS / 2) val file = File(fakeRootDir, timestamp.toString()) file.createNewFile() @@ -797,7 +808,7 @@ internal class BatchFileOrchestratorTest { fun `M return null W getReadableFile() {file is in exclude list}`() { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val timestamp = System.currentTimeMillis() - (RECENT_DELAY_MS * 2) + val timestamp = DefaultTimeProvider().getDeviceTimestamp() - (RECENT_DELAY_MS * 2) val file = File(fakeRootDir, timestamp.toString()) file.createNewFile() @@ -823,7 +834,8 @@ internal class BatchFileOrchestratorTest { notADir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + fakeTimeProvider ) // When @@ -849,7 +861,8 @@ internal class BatchFileOrchestratorTest { corruptedDir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + fakeTimeProvider ) // When @@ -876,7 +889,8 @@ internal class BatchFileOrchestratorTest { restrictedDir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + fakeTimeProvider ) // When @@ -922,8 +936,8 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val old = System.currentTimeMillis() - (RECENT_DELAY_MS * 2) - val new = System.currentTimeMillis() - (RECENT_DELAY_MS / 2) + val old = DefaultTimeProvider().getDeviceTimestamp() - (RECENT_DELAY_MS * 2) + val new = DefaultTimeProvider().getDeviceTimestamp() - (RECENT_DELAY_MS / 2) val expectedFiles = mutableListOf() for (i in 1..count) { // create both non readable and non writable files @@ -968,8 +982,8 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val old = System.currentTimeMillis() - (RECENT_DELAY_MS * 2) - val new = System.currentTimeMillis() - (RECENT_DELAY_MS / 2) + val old = DefaultTimeProvider().getDeviceTimestamp() - (RECENT_DELAY_MS * 2) + val new = DefaultTimeProvider().getDeviceTimestamp() - (RECENT_DELAY_MS / 2) val expectedFiles = mutableListOf() for (i in 1..count) { // create both non readable and non writable files @@ -1015,7 +1029,8 @@ internal class BatchFileOrchestratorTest { notADir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + fakeTimeProvider ) // When @@ -1041,7 +1056,8 @@ internal class BatchFileOrchestratorTest { corruptedDir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + fakeTimeProvider ) // When @@ -1068,7 +1084,8 @@ internal class BatchFileOrchestratorTest { restrictedDir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + fakeTimeProvider ) // When @@ -1144,7 +1161,7 @@ internal class BatchFileOrchestratorTest { @Test fun `M return metadata file W getMetadataFile()`() { // Given - val fakeFileName = System.currentTimeMillis().toString() + val fakeFileName = DefaultTimeProvider().getDeviceTimestamp().toString() val fakeFile = File(fakeRootDir.path, fakeFileName) // When @@ -1160,7 +1177,7 @@ internal class BatchFileOrchestratorTest { @StringForgery fakeSuffix: String ) { // Given - val fakeFileName = System.currentTimeMillis().toString() + val fakeFileName = DefaultTimeProvider().getDeviceTimestamp().toString() val fakeFile = File("${fakeRootDir.parent}$fakeSuffix", fakeFileName) // When diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt index bb01164101..0704e2966e 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt @@ -10,6 +10,7 @@ import android.os.Build import android.os.Process import android.os.SystemClock import com.datadog.android.core.internal.system.BuildSdkVersionProvider +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.DdRumContentProvider import fr.xgouchet.elmyr.annotation.IntForgery import fr.xgouchet.elmyr.junit5.ForgeExtension @@ -35,7 +36,7 @@ class DefaultAppStartTimeProviderTest { val mockBuildSdkVersionProvider: BuildSdkVersionProvider = mock() whenever(mockBuildSdkVersionProvider.version) doReturn apiVersion val diffMs = SystemClock.elapsedRealtime() - Process.getStartElapsedRealtime() - val startTimeNs = System.nanoTime() - TimeUnit.MILLISECONDS.toNanos(diffMs) + val startTimeNs = DefaultTimeProvider().getDeviceElapsedTimeNs() - TimeUnit.MILLISECONDS.toNanos(diffMs) // WHEN val timeProvider = DefaultAppStartTimeProvider(mockBuildSdkVersionProvider) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/KronosTimeProviderTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/KronosTimeProviderTest.kt index 09be35bcfb..f4a209dbf4 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/KronosTimeProviderTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/KronosTimeProviderTest.kt @@ -6,6 +6,7 @@ package com.datadog.android.core.internal.time +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.utils.forge.Configurator import com.lyft.kronos.Clock import fr.xgouchet.elmyr.annotation.Forgery @@ -57,7 +58,7 @@ internal class KronosTimeProviderTest { @Test fun `returns server time offset in nanoseconds`() { - val now = System.currentTimeMillis() + val now = DefaultTimeProvider().getDeviceTimestamp() val result = testedTimeProvider.getServerOffsetNanos() val expectedOffset = TimeUnit.MILLISECONDS.toNanos(fakeDate.time - now) @@ -69,7 +70,7 @@ internal class KronosTimeProviderTest { @Test fun `returns server time offset in milliseconds`() { - val now = System.currentTimeMillis() + val now = DefaultTimeProvider().getDeviceTimestamp() val result = testedTimeProvider.getServerOffsetMillis() val expectedOffset = fakeDate.time - now @@ -81,7 +82,7 @@ internal class KronosTimeProviderTest { @Test fun `returns device time`() { - val now = System.currentTimeMillis() + val now = DefaultTimeProvider().getDeviceTimestamp() val result = testedTimeProvider.getDeviceTimestamp() assertThat(result).isCloseTo(now, Offset.offset(TEST_OFFSET)) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/forge/NdkCrashLogForgeryFactory.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/forge/NdkCrashLogForgeryFactory.kt index 2148b51fb4..78002701b7 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/forge/NdkCrashLogForgeryFactory.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/forge/NdkCrashLogForgeryFactory.kt @@ -6,6 +6,7 @@ package com.datadog.android.utils.forge +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.ndk.internal.NdkCrashLog import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.ForgeryFactory @@ -15,7 +16,7 @@ internal class NdkCrashLogForgeryFactory : override fun getForgery(forge: Forge): NdkCrashLog { return NdkCrashLog( signal = forge.anInt(min = 1), - timestamp = System.currentTimeMillis(), + timestamp = DefaultTimeProvider().getDeviceTimestamp(), timeSinceAppStartMs = forge.aNullable { aPositiveLong() }, signalName = forge.anAlphabeticalString(), message = forge.anAlphabeticalString(), diff --git a/dd-sdk-android-internal/api/apiSurface b/dd-sdk-android-internal/api/apiSurface index 6777c73e6f..8fc31d7ebe 100644 --- a/dd-sdk-android-internal/api/apiSurface +++ b/dd-sdk-android-internal/api/apiSurface @@ -94,11 +94,13 @@ class com.datadog.android.internal.thread.NamedCallable : NamedExecutionUnit, class com.datadog.android.internal.time.DefaultTimeProvider : TimeProvider override fun getDeviceTimestamp(): Long override fun getServerTimestamp(): Long + override fun getDeviceElapsedTimeNs(): Long override fun getServerOffsetNanos(): Long override fun getServerOffsetMillis(): Long interface com.datadog.android.internal.time.TimeProvider fun getDeviceTimestamp(): Long fun getServerTimestamp(): Long + fun getDeviceElapsedTimeNs(): Long fun getServerOffsetNanos(): Long fun getServerOffsetMillis(): Long fun ByteArray.toHexString(): String diff --git a/dd-sdk-android-internal/api/dd-sdk-android-internal.api b/dd-sdk-android-internal/api/dd-sdk-android-internal.api index 74321d9102..e1da327213 100644 --- a/dd-sdk-android-internal/api/dd-sdk-android-internal.api +++ b/dd-sdk-android-internal/api/dd-sdk-android-internal.api @@ -255,6 +255,7 @@ public final class com/datadog/android/internal/thread/NamedRunnable : com/datad public final class com/datadog/android/internal/time/DefaultTimeProvider : com/datadog/android/internal/time/TimeProvider { public fun ()V + public fun getDeviceElapsedTimeNs ()J public fun getDeviceTimestamp ()J public fun getServerOffsetMillis ()J public fun getServerOffsetNanos ()J @@ -262,6 +263,7 @@ public final class com/datadog/android/internal/time/DefaultTimeProvider : com/d } public abstract interface class com/datadog/android/internal/time/TimeProvider { + public abstract fun getDeviceElapsedTimeNs ()J public abstract fun getDeviceTimestamp ()J public abstract fun getServerOffsetMillis ()J public abstract fun getServerOffsetNanos ()J diff --git a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/profiler/DDExecutionTimer.kt b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/profiler/DDExecutionTimer.kt index a8f07bf9a2..8d9e36ac78 100644 --- a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/profiler/DDExecutionTimer.kt +++ b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/profiler/DDExecutionTimer.kt @@ -6,18 +6,22 @@ package com.datadog.android.internal.profiler +import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.TimeProvider + internal class DDExecutionTimer( private val track: String, - private val benchmarkSdkUploads: BenchmarkSdkUploads = GlobalBenchmark.getBenchmarkSdkUploads() + private val benchmarkSdkUploads: BenchmarkSdkUploads = GlobalBenchmark.getBenchmarkSdkUploads(), + private val timeProvider: TimeProvider = DefaultTimeProvider() ) : ExecutionTimer { override fun measure(action: () -> T): T { if (track.isEmpty()) { return action() } - val requestStartTime = System.nanoTime() + val requestStartTime = timeProvider.getDeviceElapsedTimeNs() val result = action() - val latencyInSeconds = (System.nanoTime() - requestStartTime) / NANOSECONDS_IN_A_SECOND + val latencyInSeconds = (timeProvider.getDeviceElapsedTimeNs() - requestStartTime) / NANOSECONDS_IN_A_SECOND responseLatencyReport(latencyInSeconds, track) return result } diff --git a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/DefaultTimeProvider.kt b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/DefaultTimeProvider.kt index b60ff61293..a21894852f 100644 --- a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/DefaultTimeProvider.kt +++ b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/DefaultTimeProvider.kt @@ -15,6 +15,8 @@ class DefaultTimeProvider : TimeProvider { override fun getServerTimestamp(): Long = System.currentTimeMillis() + override fun getDeviceElapsedTimeNs(): Long = System.nanoTime() + override fun getServerOffsetNanos(): Long = 0L override fun getServerOffsetMillis(): Long = 0L diff --git a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/TimeProvider.kt b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/TimeProvider.kt index 030f550d52..f76897c27a 100644 --- a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/TimeProvider.kt +++ b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/TimeProvider.kt @@ -23,6 +23,12 @@ interface TimeProvider { */ fun getServerTimestamp(): Long + /** + * Returns the current device elapsed time in nanoseconds. + * This value is monotonic and mirrors [System.nanoTime]. + */ + fun getDeviceElapsedTimeNs(): Long + /** * Returns the offset between the device and server time references in nanoseconds. */ diff --git a/dd-sdk-android-internal/src/main/java/com/datadog/android/rum/DdRumContentProvider.kt b/dd-sdk-android-internal/src/main/java/com/datadog/android/rum/DdRumContentProvider.kt index a26fc234ad..09966f75db 100644 --- a/dd-sdk-android-internal/src/main/java/com/datadog/android/rum/DdRumContentProvider.kt +++ b/dd-sdk-android-internal/src/main/java/com/datadog/android/rum/DdRumContentProvider.kt @@ -14,6 +14,7 @@ import android.database.Cursor import android.net.Uri import android.os.Process import android.util.Log +import com.datadog.android.internal.time.DefaultTimeProvider /** * A Content provider used to monitor the Application startup time efficiently. @@ -77,6 +78,6 @@ class DdRumContentProvider : ContentProvider() { /** * fallback for APIs below Android N, see [DefaultAppStartTimeProvider]. */ - val createTimeNs: Long = System.nanoTime() + val createTimeNs: Long = DefaultTimeProvider().getDeviceElapsedTimeNs() } } diff --git a/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessor.kt b/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessor.kt index 3e0299b38b..34a7f2ac11 100644 --- a/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessor.kt +++ b/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessor.kt @@ -11,8 +11,12 @@ import com.datadog.android.flags.internal.model.PrecomputedFlag import com.datadog.android.flags.internal.storage.RecordWriter import com.datadog.android.flags.model.EvaluationContext import com.datadog.android.flags.model.ExposureEvent +import com.datadog.android.internal.time.TimeProvider -internal class ExposureEventsProcessor(private val writer: RecordWriter) : EventsProcessor { +internal class ExposureEventsProcessor( + private val writer: RecordWriter, + private val timeProvider: TimeProvider +) : EventsProcessor { private data class CacheKey( val targetingKey: String, @@ -65,7 +69,7 @@ internal class ExposureEventsProcessor(private val writer: RecordWriter) : Event } private fun buildExposureEvent(flagName: String, context: EvaluationContext, data: PrecomputedFlag): ExposureEvent { - val now = System.currentTimeMillis() + val now = timeProvider.getDeviceTimestamp() return ExposureEvent( timestamp = now, allocation = ExposureEvent.Identifier(data.allocationKey), diff --git a/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/FlagsFeature.kt b/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/FlagsFeature.kt index 23b1fb7b52..62d10cd609 100644 --- a/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/FlagsFeature.kt +++ b/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/FlagsFeature.kt @@ -106,7 +106,10 @@ internal class FlagsFeature(private val sdkCore: FeatureSdkCore, internal val fl isInitialized = true sdkCore.setContextUpdateReceiver(this) dataWriter = createDataWriter() - processor = ExposureEventsProcessor(dataWriter) + processor = ExposureEventsProcessor( + writer = dataWriter, + timeProvider = sdkCore.timeProvider + ) } override fun onStop() { diff --git a/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/model/FlagsStateEntry.kt b/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/model/FlagsStateEntry.kt index d5a256b727..66bd49f70d 100644 --- a/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/model/FlagsStateEntry.kt +++ b/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/model/FlagsStateEntry.kt @@ -7,9 +7,10 @@ package com.datadog.android.flags.internal.model import com.datadog.android.flags.model.EvaluationContext +import com.datadog.android.internal.time.DefaultTimeProvider internal data class FlagsStateEntry( val evaluationContext: EvaluationContext, val flags: Map, - val lastUpdateTimestamp: Long = System.currentTimeMillis() + val lastUpdateTimestamp: Long = DefaultTimeProvider().getDeviceTimestamp() ) diff --git a/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/persistence/FlagsPersistenceManager.kt b/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/persistence/FlagsPersistenceManager.kt index 447139fa56..e907482788 100644 --- a/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/persistence/FlagsPersistenceManager.kt +++ b/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/persistence/FlagsPersistenceManager.kt @@ -14,11 +14,13 @@ import com.datadog.android.core.persistence.datastore.DataStoreContent import com.datadog.android.flags.internal.model.FlagsStateEntry import com.datadog.android.flags.internal.model.PrecomputedFlag import com.datadog.android.flags.model.EvaluationContext +import com.datadog.android.internal.time.TimeProvider internal class FlagsPersistenceManager( private val dataStore: DataStoreHandler, instanceName: String, private val internalLogger: InternalLogger, + private val timeProvider: TimeProvider, onStateLoaded: (FlagsStateEntry?) -> Unit ) { private val serializer = FlagsStateSerializer(internalLogger) @@ -37,7 +39,7 @@ internal class FlagsPersistenceManager( val entry = FlagsStateEntry( evaluationContext = context, flags = flags, - lastUpdateTimestamp = System.currentTimeMillis() + lastUpdateTimestamp = timeProvider.getDeviceTimestamp() ) dataStore.setValue( diff --git a/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/repository/DefaultFlagsRepository.kt b/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/repository/DefaultFlagsRepository.kt index 404aa82cf4..b15e093860 100644 --- a/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/repository/DefaultFlagsRepository.kt +++ b/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/repository/DefaultFlagsRepository.kt @@ -33,7 +33,8 @@ internal class DefaultFlagsRepository( private val persistenceManager = FlagsPersistenceManager( dataStore = dataStore, instanceName = instanceName, - internalLogger = internalLogger + internalLogger = internalLogger, + timeProvider = featureSdkCore.timeProvider ) { persistedState -> try { persistedState?.let { diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessorTest.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessorTest.kt index 602ea1a461..0e0895c107 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessorTest.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessorTest.kt @@ -11,6 +11,8 @@ import com.datadog.android.flags.internal.storage.RecordWriter import com.datadog.android.flags.model.EvaluationContext import com.datadog.android.flags.model.ExposureEvent import com.datadog.android.flags.utils.forge.ForgeConfigurator +import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.TimeProvider import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration @@ -26,8 +28,10 @@ import org.mockito.kotlin.any import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.atLeast import org.mockito.kotlin.atMost +import org.mockito.kotlin.doReturn import org.mockito.kotlin.times import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @ExtendWith(MockitoExtension::class, ForgeExtension::class) @ForgeConfiguration(ForgeConfigurator::class) @@ -36,6 +40,9 @@ internal class ExposureEventsProcessorTest { @Mock lateinit var mockRecordWriter: RecordWriter + @Mock + lateinit var mockTimeProvider: TimeProvider + @StringForgery lateinit var fakeFlagName: String @@ -53,7 +60,9 @@ internal class ExposureEventsProcessorTest { @BeforeEach fun `set up`(forge: Forge) { - testedProcessor = ExposureEventsProcessor(mockRecordWriter) + val fakeTimestamp = forge.aLong(min = 1) + whenever(mockTimeProvider.getDeviceTimestamp()) doReturn fakeTimestamp + testedProcessor = ExposureEventsProcessor(mockRecordWriter, mockTimeProvider) fakeFlag = forge.getForgery().copy( allocationKey = fakeAllocationKey, variationKey = fakeVariationKey @@ -218,6 +227,9 @@ internal class ExposureEventsProcessorTest { @Test fun `M generate consistent timestamps W processEvent() { multiple calls }`(forge: Forge) { // Given + val realTimeProvider = DefaultTimeProvider() + whenever(mockTimeProvider.getDeviceTimestamp()).thenAnswer { realTimeProvider.getDeviceTimestamp() } + val fakeContext1 = EvaluationContext( targetingKey = forge.anAlphabeticalString(), attributes = mapOf("user_id" to forge.anAlphabeticalString()) @@ -227,13 +239,13 @@ internal class ExposureEventsProcessorTest { attributes = mapOf("user_id" to forge.anAlphabeticalString()) ) - val beforeTime = System.currentTimeMillis() + val beforeTime = realTimeProvider.getDeviceTimestamp() // When testedProcessor.processEvent(fakeFlagName, fakeContext1, fakeFlag) testedProcessor.processEvent(fakeFlagName, fakeContext2, fakeFlag) - val afterTime = System.currentTimeMillis() + val afterTime = realTimeProvider.getDeviceTimestamp() // Then val eventCaptor = argumentCaptor() @@ -481,7 +493,7 @@ internal class ExposureEventsProcessorTest { Thread { repeat(10) { Thread.sleep(5) - testedProcessor = ExposureEventsProcessor(mockRecordWriter) + testedProcessor = ExposureEventsProcessor(mockRecordWriter, mockTimeProvider) } } } diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/FlagsFeatureTest.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/FlagsFeatureTest.kt index 74f53391ed..33d68f3538 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/FlagsFeatureTest.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/FlagsFeatureTest.kt @@ -15,6 +15,7 @@ import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.flags.FlagsConfiguration import com.datadog.android.flags.internal.storage.ExposureEventRecordWriter import com.datadog.android.flags.internal.storage.NoOpRecordWriter +import com.datadog.android.internal.time.TimeProvider import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeExtension @@ -51,6 +52,9 @@ internal class FlagsFeatureTest { @Mock lateinit var mockInternalLogger: InternalLogger + @Mock + lateinit var mockTimeProvider: TimeProvider + @Mock lateinit var mockContext: Context @@ -62,6 +66,7 @@ internal class FlagsFeatureTest { @BeforeEach fun `set up`() { whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger + whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider whenever(mockSdkCore.createSingleThreadExecutorService(any())) doReturn mockExecutorService // Setup mockContext with default release build (flags = 0) diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateDeserializerTest.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateDeserializerTest.kt index 957a1ed416..269a37ef6a 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateDeserializerTest.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateDeserializerTest.kt @@ -7,6 +7,7 @@ package com.datadog.android.flags.internal.persistence import com.datadog.android.api.InternalLogger +import com.datadog.android.internal.time.DefaultTimeProvider import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.junit5.ForgeExtension import org.assertj.core.api.Assertions.assertThat @@ -45,7 +46,7 @@ internal class FlagsStateDeserializerTest { val stringAttr = forge.anAlphabeticalString() val numberAttr = forge.anInt().toString() val booleanAttr = forge.aBool().toString() - val timestamp = System.currentTimeMillis() + val timestamp = DefaultTimeProvider().getDeviceTimestamp() val json = JSONObject().apply { put( @@ -107,7 +108,7 @@ internal class FlagsStateDeserializerTest { fun `M deserialize empty state W deserialize() { valid JSON with empty data }`(forge: Forge) { // Given val targetingKey = forge.anAlphabeticalString() - val timestamp = System.currentTimeMillis() + val timestamp = DefaultTimeProvider().getDeviceTimestamp() val json = JSONObject().apply { put( @@ -172,7 +173,7 @@ internal class FlagsStateDeserializerTest { fun `M deserialize with empty attributes W deserialize() { missing attributes field }`(forge: Forge) { // Given val targetingKey = forge.anAlphabeticalString() - val timestamp = System.currentTimeMillis() + val timestamp = DefaultTimeProvider().getDeviceTimestamp() val json = JSONObject().apply { put( @@ -200,7 +201,7 @@ internal class FlagsStateDeserializerTest { fun `M skip invalid flag and process others W deserialize() { one invalid flag }`(forge: Forge) { // Given val targetingKey = forge.anAlphabeticalString() - val timestamp = System.currentTimeMillis() + val timestamp = DefaultTimeProvider().getDeviceTimestamp() val json = JSONObject().apply { put( @@ -282,7 +283,7 @@ internal class FlagsStateDeserializerTest { } ) put("flags", JSONObject()) - put("lastUpdateTimestamp", System.currentTimeMillis()) + put("lastUpdateTimestamp", DefaultTimeProvider().getDeviceTimestamp()) } // When diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateSerializerTest.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateSerializerTest.kt index 762aac3651..07f4f76f25 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateSerializerTest.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateSerializerTest.kt @@ -10,6 +10,7 @@ import com.datadog.android.api.InternalLogger import com.datadog.android.flags.internal.model.FlagsStateEntry import com.datadog.android.flags.internal.model.PrecomputedFlag import com.datadog.android.flags.model.EvaluationContext +import com.datadog.android.internal.time.DefaultTimeProvider import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.junit5.ForgeExtension import org.assertj.core.api.Assertions.assertThat @@ -65,7 +66,7 @@ internal class FlagsStateSerializerTest { ) ) - val timestamp = System.currentTimeMillis() + val timestamp = DefaultTimeProvider().getDeviceTimestamp() val flagsState = FlagsStateEntry(evaluationContext, flags, timestamp) // When diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/repository/DefaultFlagsRepositoryTest.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/repository/DefaultFlagsRepositoryTest.kt index 1f7ae7ad4e..dbb28f050b 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/repository/DefaultFlagsRepositoryTest.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/repository/DefaultFlagsRepositoryTest.kt @@ -14,6 +14,7 @@ import com.datadog.android.flags.internal.model.FlagsStateEntry import com.datadog.android.flags.internal.model.PrecomputedFlag import com.datadog.android.flags.model.EvaluationContext import com.datadog.android.flags.utils.forge.ForgeConfigurator +import com.datadog.android.internal.time.TimeProvider import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension @@ -50,11 +51,15 @@ internal class DefaultFlagsRepositoryTest { @Mock lateinit var mockInternalLogger: InternalLogger + @Mock + lateinit var mockTimeProvider: TimeProvider + private lateinit var testedRepository: DefaultFlagsRepository @BeforeEach fun `set up`() { whenever(mockFeatureSdkCore.internalLogger) doReturn mockInternalLogger + whenever(mockFeatureSdkCore.timeProvider) doReturn mockTimeProvider whenever( mockDataStore.value( key = any(), diff --git a/features/dd-sdk-android-logs/src/main/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandler.kt b/features/dd-sdk-android-logs/src/main/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandler.kt index 10372fd1b0..739fb6e3aa 100644 --- a/features/dd-sdk-android-logs/src/main/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandler.kt +++ b/features/dd-sdk-android-logs/src/main/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandler.kt @@ -45,7 +45,7 @@ internal class DatadogLogHandler( return } - val resolvedTimeStamp = timestamp ?: System.currentTimeMillis() + val resolvedTimeStamp = timestamp ?: sdkCore.timeProvider.getDeviceTimestamp() val combinedAttributes = mutableMapOf() val logsFeature = sdkCore.getFeature(Feature.LOGS_FEATURE_NAME) if (logsFeature != null) { @@ -112,7 +112,7 @@ internal class DatadogLogHandler( return } - val resolvedTimeStamp = timestamp ?: System.currentTimeMillis() + val resolvedTimeStamp = timestamp ?: sdkCore.timeProvider.getDeviceTimestamp() val combinedAttributes = mutableMapOf() val logsFeature = sdkCore.getFeature(Feature.LOGS_FEATURE_NAME) if (logsFeature != null) { diff --git a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/LogsFeatureTest.kt b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/LogsFeatureTest.kt index 0dfec2fcec..0e5fca2239 100644 --- a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/LogsFeatureTest.kt +++ b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/LogsFeatureTest.kt @@ -19,6 +19,7 @@ import com.datadog.android.api.storage.EventType import com.datadog.android.api.storage.FeatureStorageConfiguration import com.datadog.android.event.EventMapper import com.datadog.android.event.MapperSerializer +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.utils.NULL_MAP_VALUE import com.datadog.android.log.LogAttributes import com.datadog.android.log.internal.domain.event.LogEventMapperWrapper @@ -130,7 +131,7 @@ internal class LogsFeatureTest { fun `set up`( forge: Forge ) { - val now = System.currentTimeMillis() + val now = DefaultTimeProvider().getDeviceTimestamp() fakeServerTimeOffset = forge.aLong(min = -now, max = Long.MAX_VALUE - now) whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger diff --git a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/DatadogLogGeneratorTest.kt b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/DatadogLogGeneratorTest.kt index 1bb443e537..5e2d4f2f56 100644 --- a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/DatadogLogGeneratorTest.kt +++ b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/DatadogLogGeneratorTest.kt @@ -12,6 +12,7 @@ import com.datadog.android.api.context.NetworkInfo import com.datadog.android.api.context.UserInfo import com.datadog.android.api.feature.Feature import com.datadog.android.core.feature.event.ThreadDump +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.log.LogAttributes import com.datadog.android.log.assertj.LogEventAssert.Companion.assertThat import com.datadog.android.log.model.LogEvent @@ -88,7 +89,7 @@ internal class DatadogLogGeneratorTest { fakeAttributes = forge.aMap { anAlphabeticalString() to anInt() } fakeTags = forge.aList { anAlphabeticalString() }.toSet() fakeThrowable = forge.aThrowable() - fakeTimestamp = System.currentTimeMillis() + fakeTimestamp = DefaultTimeProvider().getDeviceTimestamp() fakeThreadName = forge.anAlphabeticalString() fakeTimeOffset = forge.aLong( min = -fakeTimestamp, diff --git a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt index c91515e208..ddb19ff01e 100644 --- a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt +++ b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt @@ -15,6 +15,7 @@ import com.datadog.android.api.storage.DataWriter import com.datadog.android.api.storage.EventBatchWriter import com.datadog.android.api.storage.EventType import com.datadog.android.core.sampling.Sampler +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.log.LogAttributes import com.datadog.android.log.assertj.LogEventAssert.Companion.assertThat import com.datadog.android.log.internal.LogsFeature @@ -69,6 +70,7 @@ internal class DatadogLogHandlerTest { private lateinit var fakeTags: Set private lateinit var fakeAttributes: Map private var fakeLevel: Int = 0 + private var fakeCurrentTimestamp: Long = 0 @Forgery lateinit var fakeThrowable: Throwable @@ -112,12 +114,16 @@ internal class DatadogLogHandlerTest { @Mock lateinit var mockSampler: Sampler + @Mock + lateinit var mockTimeProvider: TimeProvider + @BeforeEach fun `set up`(forge: Forge) { fakeServiceName = forge.anAlphabeticalString() fakeLoggerName = forge.anAlphabeticalString() fakeMessage = forge.anAlphabeticalString() fakeLevel = forge.anInt(2, 8) + fakeCurrentTimestamp = forge.aLong(min = 1) fakeAttributes = forge.aMap { anAlphabeticalString() to anInt() } fakeTags = forge.aList { anAlphabeticalString() }.toSet() fakeDatadogContext = fakeDatadogContext.copy( @@ -137,6 +143,9 @@ internal class DatadogLogHandlerTest { } ) + whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider + whenever(mockTimeProvider.getDeviceTimestamp()) doReturn fakeCurrentTimestamp + whenever( mockSdkCore.getFeature(Feature.LOGS_FEATURE_NAME) ) doReturn mockLogsFeatureScope @@ -168,7 +177,7 @@ internal class DatadogLogHandlerTest { @Test fun `forward log to LogWriter`() { // Given - val now = System.currentTimeMillis() + val now = fakeCurrentTimestamp // When testedHandler.handleLog( @@ -248,7 +257,7 @@ internal class DatadogLogHandlerTest { @Test fun `forward log to LogWriter with throwable`() { // Given - val now = System.currentTimeMillis() + val now = fakeCurrentTimestamp // When testedHandler.handleLog( @@ -311,7 +320,7 @@ internal class DatadogLogHandlerTest { @StringForgery errorStack: String ) { // Given - val now = System.currentTimeMillis() + val now = fakeCurrentTimestamp // When testedHandler.handleLog( @@ -623,7 +632,7 @@ internal class DatadogLogHandlerTest { @Test fun `forward log to LogWriter on background thread`(forge: Forge) { // Given - val now = System.currentTimeMillis() + val now = fakeCurrentTimestamp val threadName = forge.anAlphabeticalString() val countDownLatch = CountDownLatch(1) @@ -685,7 +694,7 @@ internal class DatadogLogHandlerTest { @Test fun `forward log to LogWriter without network info`() { // Given - val now = System.currentTimeMillis() + val now = fakeCurrentTimestamp testedHandler = DatadogLogHandler( loggerName = fakeLoggerName, logGenerator = DatadogLogGenerator( @@ -745,7 +754,7 @@ internal class DatadogLogHandlerTest { @Test fun `forward minimal log to LogWriter`() { // Given - val now = System.currentTimeMillis() + val now = fakeCurrentTimestamp fakeDatadogContext = fakeDatadogContext.copy( featuresContext = fakeDatadogContext.featuresContext.toMutableMap().apply { remove(Feature.RUM_FEATURE_NAME) @@ -1055,7 +1064,7 @@ internal class DatadogLogHandlerTest { @Test fun `it will sample in the logs when required`() { // Given - val now = System.currentTimeMillis() + val now = fakeCurrentTimestamp whenever(mockSampler.sample(Unit)).thenReturn(true) testedHandler = DatadogLogHandler( loggerName = fakeLoggerName, diff --git a/features/dd-sdk-android-ndk/src/androidTest/kotlin/com/datadog/android/ndk/NdkTests.kt b/features/dd-sdk-android-ndk/src/androidTest/kotlin/com/datadog/android/ndk/NdkTests.kt index 825cbd9c81..950d001cee 100644 --- a/features/dd-sdk-android-ndk/src/androidTest/kotlin/com/datadog/android/ndk/NdkTests.kt +++ b/features/dd-sdk-android-ndk/src/androidTest/kotlin/com/datadog/android/ndk/NdkTests.kt @@ -7,6 +7,7 @@ package com.datadog.android.ndk import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.tools.unit.ConditionWatcher import com.datadog.tools.unit.assertj.JsonObjectAssert.Companion.assertThat import com.google.gson.JsonParser @@ -59,10 +60,13 @@ class NdkTests { val fakeErrorStack = forge.anAlphabeticalString() initNdkErrorHandler(temporaryFolder.root.absolutePath) updateTrackingConsent(1) - val fakeAppStartTimeMs = forge.aLong(min = 0L, max = System.currentTimeMillis()) + val fakeAppStartTimeMs = forge.aLong( + min = 0L, + max = DefaultTimeProvider().getDeviceTimestamp() + ) updateAppStartTime(fakeAppStartTimeMs) - val expectedTimestamp = System.currentTimeMillis() + val expectedTimestamp = DefaultTimeProvider().getDeviceTimestamp() val expectedTimeSinceAppStartMs = expectedTimestamp - fakeAppStartTimeMs simulateSignalInterception( fakeSignal, diff --git a/features/dd-sdk-android-ndk/src/main/kotlin/com/datadog/android/ndk/internal/NdkCrashReportsFeature.kt b/features/dd-sdk-android-ndk/src/main/kotlin/com/datadog/android/ndk/internal/NdkCrashReportsFeature.kt index 7862619c7c..2e80d2e56b 100644 --- a/features/dd-sdk-android-ndk/src/main/kotlin/com/datadog/android/ndk/internal/NdkCrashReportsFeature.kt +++ b/features/dd-sdk-android-ndk/src/main/kotlin/com/datadog/android/ndk/internal/NdkCrashReportsFeature.kt @@ -64,8 +64,10 @@ internal class NdkCrashReportsFeature( ) return } + val nowMs = sdkCore.timeProvider.getDeviceTimestamp() + val nowElapsedNs = sdkCore.timeProvider.getDeviceElapsedTimeNs() val appStartTimestamp = - TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis()) - System.nanoTime() + sdkCore.appStartTimeNs + TimeUnit.MILLISECONDS.toNanos(nowMs) - nowElapsedNs + sdkCore.appStartTimeNs registerSignalHandler( ndkCrashesDirs.absolutePath, consentToInt(internalSdkCore.trackingConsent), diff --git a/features/dd-sdk-android-ndk/src/test/kotlin/com/datadog/android/ndk/internal/NdkCrashReportsFeatureTest.kt b/features/dd-sdk-android-ndk/src/test/kotlin/com/datadog/android/ndk/internal/NdkCrashReportsFeatureTest.kt index 330686958c..dff95f082e 100644 --- a/features/dd-sdk-android-ndk/src/test/kotlin/com/datadog/android/ndk/internal/NdkCrashReportsFeatureTest.kt +++ b/features/dd-sdk-android-ndk/src/test/kotlin/com/datadog/android/ndk/internal/NdkCrashReportsFeatureTest.kt @@ -9,6 +9,7 @@ package com.datadog.android.ndk.internal import android.content.Context import com.datadog.android.api.InternalLogger import com.datadog.android.core.InternalSdkCore +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.privacy.TrackingConsent import com.datadog.tools.unit.setFieldValue import fr.xgouchet.elmyr.junit5.ForgeExtension @@ -50,6 +51,7 @@ class NdkCrashReportsFeatureTest { @BeforeEach fun `set up`() { + whenever(mockSdkCore.timeProvider) doReturn DefaultTimeProvider() testedFeature = NdkCrashReportsFeature(mockSdkCore) } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporter.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporter.kt index 88f18dbacc..f8dd487ae3 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporter.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporter.kt @@ -312,7 +312,7 @@ internal class DatadogLateCrashReporter( private val ViewEvent.isWithinSessionAvailability: Boolean get() { - val now = System.currentTimeMillis() + val now = sdkCore.timeProvider.getDeviceTimestamp() val sessionsTimeDifference = now - this.date return sessionsTimeDifference < VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/RumFeature.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/RumFeature.kt index 433dc93d9c..dbd319d870 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/RumFeature.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/RumFeature.kt @@ -185,7 +185,8 @@ internal class RumFeature( if (configuration.collectAccessibility) { accessibilityReader = DefaultAccessibilityReader( internalLogger = sdkCore.internalLogger, - applicationContext = appContext + applicationContext = appContext, + timeProvider = sdkCore.timeProvider ) accessibilitySnapshotManager = DefaultAccessibilitySnapshotManager(accessibilityReader) } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/Time.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/Time.kt index c24493be8b..68dce87e0b 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/Time.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/Time.kt @@ -6,19 +6,26 @@ package com.datadog.android.rum.internal.domain +import com.datadog.android.core.time.SdkTimeProvider import java.util.concurrent.TimeUnit internal data class Time( - val timestamp: Long = System.currentTimeMillis(), - val nanoTime: Long = System.nanoTime() -) + val timestamp: Long, + val nanoTime: Long +) { + constructor() : this( + timestamp = SdkTimeProvider.now(), + nanoTime = SdkTimeProvider.elapsed() + ) +} internal fun Long.asTime(): Time { - // Because nanoTime only measures the nanoseconds since the beginning - // of the current JVM lifetime, we need to approximate the nanotime we want. - // We simply convert the delay between the desired and real timestamp and - // apply it to the measured nanotime - val now = Time() - val offset = this - now.timestamp - return Time(this, TimeUnit.MILLISECONDS.toNanos(offset) + now.nanoTime) + val currentTimestamp = SdkTimeProvider.now() + val currentNano = SdkTimeProvider.elapsed() + val offset = this - currentTimestamp + val adjustedNano = currentNano + TimeUnit.MILLISECONDS.toNanos(offset) + return Time( + timestamp = this, + nanoTime = adjustedNano + ) } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReader.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReader.kt index 46bf71569b..f334c0b63f 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReader.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReader.kt @@ -21,6 +21,8 @@ import android.view.View import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener import com.datadog.android.api.InternalLogger +import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.internal.domain.InfoProvider import java.util.concurrent.atomic.AtomicLong @@ -35,7 +37,8 @@ internal class DefaultAccessibilityReader( applicationContext.getSystemService(Context.ACCESSIBILITY_SERVICE) as? AccessibilityManager, private val secureWrapper: SecureWrapper = SecureWrapper(), private val globalWrapper: GlobalWrapper = GlobalWrapper(), - private val handler: Handler = Handler(Looper.getMainLooper()) + private val handler: Handler = Handler(Looper.getMainLooper()), + private val timeProvider: TimeProvider = DefaultTimeProvider() ) : InfoProvider, ComponentCallbacks { private val displayInversionListener = object : ContentObserver(handler) { @@ -97,7 +100,7 @@ internal class DefaultAccessibilityReader( @Synchronized override fun getState(): AccessibilityInfo { - val currentTime = System.currentTimeMillis() + val currentTime = timeProvider.getDeviceTimestamp() val shouldPoll = currentTime - lastPollTime.get() >= POLL_THRESHOLD if (shouldPoll) { lastPollTime.set(currentTime) diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScope.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScope.kt index 1855ac2814..e0534cef98 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScope.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScope.kt @@ -14,6 +14,7 @@ import com.datadog.android.api.storage.DataWriter import com.datadog.android.api.storage.NoOpDataWriter import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver +import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.rum.RumSessionListener import com.datadog.android.rum.RumSessionType import com.datadog.android.rum.internal.domain.InfoProvider @@ -65,7 +66,7 @@ internal class RumSessionScope( internal var sessionState: State = State.NOT_TRACKED private var startReason: StartReason = StartReason.USER_APP_LAUNCH internal var isActive: Boolean = true - private val sessionStartNs = AtomicLong(System.nanoTime()) + private val sessionStartNs = AtomicLong(SdkTimeProvider.elapsed()) private val lastUserInteractionNs = AtomicLong(0L) @@ -206,7 +207,7 @@ internal class RumSessionScope( @Suppress("ComplexMethod") private fun updateSession(event: RumRawEvent) { - val nanoTime = System.nanoTime() + val nanoTime = SdkTimeProvider.elapsed() val isNewSession = sessionId == RumContext.NULL_UUID val timeSinceLastInteractionNs = nanoTime - lastUserInteractionNs.get() diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/instrumentation/MainLooperLongTaskStrategy.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/instrumentation/MainLooperLongTaskStrategy.kt index 0d23e18f3d..1468df031b 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/instrumentation/MainLooperLongTaskStrategy.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/instrumentation/MainLooperLongTaskStrategy.kt @@ -10,6 +10,7 @@ import android.content.Context import android.os.Looper import android.util.Printer import com.datadog.android.api.SdkCore +import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.rum.GlobalRumMonitor import com.datadog.android.rum.internal.monitor.AdvancedRumMonitor import com.datadog.android.rum.tracking.TrackingStrategy @@ -79,7 +80,7 @@ internal class MainLooperLongTaskStrategy( // region Internal private fun detectLongTask(message: String) { - val now = System.nanoTime() + val now = SdkTimeProvider.elapsed() if (message.startsWith(PREFIX_START)) { @Suppress("UnsafeThirdPartyFunctionCall") // substring can't throw IndexOutOfBounds target = message.substring(PREFIX_START_LENGTH) diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/metric/slowframes/SlowFramesListener.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/metric/slowframes/SlowFramesListener.kt index 16f2f53741..b1676ce3e3 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/metric/slowframes/SlowFramesListener.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/metric/slowframes/SlowFramesListener.kt @@ -6,6 +6,7 @@ package com.datadog.android.rum.internal.metric.slowframes import androidx.metrics.performance.FrameData +import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.rum.configuration.SlowFramesConfiguration import com.datadog.android.rum.internal.domain.FrameMetricsData import com.datadog.android.rum.internal.domain.state.SlowFrameRecord @@ -29,7 +30,7 @@ internal class DefaultSlowFramesListener( private var currentViewId: String? = null @Volatile - private var currentViewStartedTimeStampNs: Long = System.nanoTime() + private var currentViewStartedTimeStampNs: Long = SdkTimeProvider.elapsed() private val slowFramesRecords = ConcurrentHashMap() diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumAppStartupDetector.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumAppStartupDetector.kt index f2c875cc24..1cea3bc692 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumAppStartupDetector.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumAppStartupDetector.kt @@ -9,6 +9,7 @@ package com.datadog.android.rum.internal.startup import android.app.Application import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.internal.system.BuildSdkVersionProvider +import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.rum.DdRumContentProvider internal interface RumAppStartupDetector { @@ -29,7 +30,7 @@ internal interface RumAppStartupDetector { buildSdkVersionProvider = BuildSdkVersionProvider.DEFAULT, appStartupTimeProviderNs = { sdkCore.appStartTimeNs }, processImportanceProvider = { DdRumContentProvider.processImportance }, - timeProviderNs = { System.nanoTime() }, + timeProviderNs = { SdkTimeProvider.elapsed() }, listener ) } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumFirstDrawTimeReporter.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumFirstDrawTimeReporter.kt index 6a95e53a60..028281b06c 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumFirstDrawTimeReporter.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumFirstDrawTimeReporter.kt @@ -10,6 +10,7 @@ import android.app.Activity import android.os.Handler import android.os.Looper import com.datadog.android.core.InternalSdkCore +import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.rum.internal.utils.window.RumWindowCallbacksRegistryImpl internal interface RumFirstDrawTimeReporter { @@ -23,7 +24,7 @@ internal interface RumFirstDrawTimeReporter { fun create(sdkCore: InternalSdkCore): RumFirstDrawTimeReporter { return RumFirstDrawTimeReporterImpl( internalLogger = sdkCore.internalLogger, - timeProviderNs = { System.nanoTime() }, + timeProviderNs = { SdkTimeProvider.elapsed() }, windowCallbacksRegistry = RumWindowCallbacksRegistryImpl(), handler = Handler(Looper.getMainLooper()) ) diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/resource/RumResourceInputStream.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/resource/RumResourceInputStream.kt index 6af626f5de..ed312cc8e9 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/resource/RumResourceInputStream.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/resource/RumResourceInputStream.kt @@ -8,6 +8,7 @@ package com.datadog.android.rum.resource import com.datadog.android.Datadog import com.datadog.android.api.SdkCore +import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.rum.GlobalRumMonitor import com.datadog.android.rum.RumErrorSource import com.datadog.android.rum.RumResourceKind @@ -46,7 +47,7 @@ constructor( init { val rumMonitor = GlobalRumMonitor.get(sdkCore) rumMonitor.startResource(key, METHOD, url) - callStart = System.nanoTime() + callStart = nowNs() if (rumMonitor is AdvancedRumMonitor) { rumMonitor.waitForResourceTiming(key) } @@ -56,36 +57,36 @@ constructor( /** @inheritdoc */ override fun read(): Int { - if (firstByte == 0L) firstByte = System.nanoTime() + if (firstByte == 0L) firstByte = nowNs() return callWithErrorTracking(ERROR_READ) { @Suppress("UnsafeThirdPartyFunctionCall") // caller should handle the exception read().also { if (it >= 0) size++ - lastByte = System.nanoTime() + lastByte = nowNs() } } } /** @inheritdoc */ override fun read(b: ByteArray): Int { - if (firstByte == 0L) firstByte = System.nanoTime() + if (firstByte == 0L) firstByte = nowNs() return callWithErrorTracking(ERROR_READ) { @Suppress("UnsafeThirdPartyFunctionCall") // caller should handle the exception read(b).also { if (it >= 0) size += it - lastByte = System.nanoTime() + lastByte = nowNs() } } } /** @inheritdoc */ override fun read(b: ByteArray, off: Int, len: Int): Int { - if (firstByte == 0L) firstByte = System.nanoTime() + if (firstByte == 0L) firstByte = nowNs() return callWithErrorTracking(ERROR_READ) { @Suppress("UnsafeThirdPartyFunctionCall") // caller should handle the exception read(b, off, len).also { if (it >= 0) size += it - lastByte = System.nanoTime() + lastByte = nowNs() } } } @@ -175,6 +176,10 @@ constructor( } } + private fun nowNs(): Long { + return SdkTimeProvider.elapsed() + } + // endregion internal companion object { diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/RumTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/RumTest.kt index 564f260bf1..3b33563d16 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/RumTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/RumTest.kt @@ -13,6 +13,7 @@ import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.sampling.RateBasedSampler +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.internal.RumFeature import com.datadog.android.rum.internal.monitor.DatadogRumMonitor import com.datadog.android.rum.internal.monitor.NoOpAdvancedRumMonitor @@ -59,12 +60,16 @@ internal class RumTest { @Mock lateinit var mockSdkCore: InternalSdkCore + @Mock + lateinit var mockTimeProvider: TimeProvider + @BeforeEach fun `set up`() { whenever(mockSdkCore.internalLogger) doReturn mock() whenever(mockSdkCore.firstPartyHostResolver) doReturn mock() whenever(mockSdkCore.createSingleThreadExecutorService(any())) doReturn mock() whenever(mockSdkCore.createScheduledExecutorService(any())) doReturn mock() + whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider } @Test diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporterTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporterTest.kt index 938a0e8653..168703b189 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporterTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporterTest.kt @@ -19,6 +19,8 @@ import com.datadog.android.api.storage.EventType import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.feature.event.ThreadDump import com.datadog.android.core.internal.persistence.Deserializer +import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.RumErrorSource import com.datadog.android.rum.assertj.ErrorEventAssert import com.datadog.android.rum.assertj.ViewEventAssert @@ -93,12 +95,16 @@ internal class DatadogLateCrashReporterTest { @Mock lateinit var mockInternalLogger: InternalLogger + @Mock + lateinit var mockTimeProvider: TimeProvider + @Forgery lateinit var fakeDatadogContext: DatadogContext @BeforeEach fun `set up`() { whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger + whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider whenever(mockSdkCore.getFeature(Feature.RUM_FEATURE_NAME)) doReturn mockRumFeatureScope whenever(mockEventWriteScope.invoke(any())) doAnswer { @@ -140,7 +146,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = System.currentTimeMillis() - forge.aLong( + date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ), @@ -241,7 +247,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = System.currentTimeMillis() - forge.aLong( + date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ), @@ -343,7 +349,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = System.currentTimeMillis() - forge.aLong( + date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ), @@ -402,7 +408,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = System.currentTimeMillis() - forge.aLong( + date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ), @@ -487,7 +493,7 @@ internal class DatadogLateCrashReporterTest { ) ) val fakeViewEvent = viewEvent.copy( - date = System.currentTimeMillis() - forge.aLong( + date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( min = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD + 1 ), usr = ViewEvent.Usr( @@ -692,7 +698,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = System.currentTimeMillis() - forge.aLong( + date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ), @@ -789,7 +795,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = System.currentTimeMillis() - forge.aLong( + date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ), @@ -873,7 +879,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = System.currentTimeMillis() - forge.aLong( + date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( min = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD + 1 ), usr = ViewEvent.Usr( @@ -964,7 +970,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = System.currentTimeMillis() - forge.aLong( + date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ) @@ -1021,7 +1027,7 @@ internal class DatadogLateCrashReporterTest { ) { // Given val fakeViewEvent = viewEvent.copy( - date = System.currentTimeMillis() - forge.aLong( + date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ) @@ -1061,7 +1067,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = System.currentTimeMillis() - forge.aLong( + date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ) @@ -1098,7 +1104,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = System.currentTimeMillis() - forge.aLong( + date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ) @@ -1136,7 +1142,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = System.currentTimeMillis() - forge.aLong( + date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt index 78f5fdf4bc..f9ca422055 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt @@ -25,6 +25,7 @@ import com.datadog.android.event.EventMapper import com.datadog.android.event.MapperSerializer import com.datadog.android.internal.flags.RumFlagEvaluationMessage import com.datadog.android.internal.telemetry.InternalTelemetryEvent +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.GlobalRumMonitor import com.datadog.android.rum.RumErrorSource import com.datadog.android.rum.assertj.RumFeatureAssert @@ -147,10 +148,14 @@ internal class RumFeatureTest { @Mock lateinit var mockScheduledExecutorService: ScheduledExecutorService + @Mock + lateinit var mockTimeProvider: TimeProvider + @BeforeEach fun `set up`() { whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger whenever(mockSdkCore.createScheduledExecutorService(any())) doReturn mockScheduledExecutorService + whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider val mockContentResolver = mock() whenever(appContext.mockInstance.contentResolver) doReturn mockContentResolver diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/TimeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/TimeTest.kt index 292d46ba93..c4e79c3290 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/TimeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/TimeTest.kt @@ -6,6 +6,7 @@ package com.datadog.android.rum.internal.domain +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.utils.forge.Configurator import fr.xgouchet.elmyr.annotation.LongForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration @@ -22,13 +23,13 @@ internal class TimeTest { @Test fun `creates Time with current millis and nanos`() { - val startMs = System.currentTimeMillis() - val startNs = System.nanoTime() + val startMs = DefaultTimeProvider().getDeviceTimestamp() + val startNs = DefaultTimeProvider().getDeviceElapsedTimeNs() val time = Time() - val endNs = System.nanoTime() - val endMs = System.currentTimeMillis() + val endNs = DefaultTimeProvider().getDeviceElapsedTimeNs() + val endMs = DefaultTimeProvider().getDeviceTimestamp() assertThat(time.timestamp).isBetween(startMs, endMs) assertThat(time.nanoTime).isBetween(startNs, endNs) @@ -38,11 +39,11 @@ internal class TimeTest { fun `M convert timestamp to Time W asTime()`( @LongForgery(1000000000000, 2000000000000) timestamp: Long ) { - val startMs = System.currentTimeMillis() - val startNs = System.nanoTime() + val startMs = DefaultTimeProvider().getDeviceTimestamp() + val startNs = DefaultTimeProvider().getDeviceElapsedTimeNs() val time = timestamp.asTime() - val endNs = System.nanoTime() - val endMs = System.currentTimeMillis() + val endNs = DefaultTimeProvider().getDeviceElapsedTimeNs() + val endMs = DefaultTimeProvider().getDeviceTimestamp() assertThat(time.timestamp).isEqualTo(timestamp) val nanoOffset = time.nanoTime - ((startNs + endNs) / 2) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReaderTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReaderTest.kt index 2517883d08..c47ebd9a6e 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReaderTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReaderTest.kt @@ -20,6 +20,7 @@ import android.view.View import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener import com.datadog.android.api.InternalLogger +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.internal.domain.accessibility.DefaultAccessibilityReader.Companion.CAPTIONING_ENABLED_KEY import com.datadog.android.rum.utils.forge.Configurator import com.datadog.tools.unit.annotations.TestTargetApi @@ -763,13 +764,13 @@ internal class DefaultAccessibilityReaderTest { val lastPollTimeField = testedReader.javaClass.getDeclaredField("lastPollTime") lastPollTimeField.isAccessible = true val lastPollTime = lastPollTimeField.get(testedReader) as AtomicLong - val oldTime = System.currentTimeMillis() - 31_000 + val oldTime = DefaultTimeProvider().getDeviceTimestamp() - 31_000 lastPollTime.set(oldTime) // When - Call after threshold exceeded - val currentTimeBefore = System.currentTimeMillis() + val currentTimeBefore = DefaultTimeProvider().getDeviceTimestamp() testedReader.getState() - val currentTimeAfter = System.currentTimeMillis() + val currentTimeAfter = DefaultTimeProvider().getDeviceTimestamp() // Then - lastPollTime should be updated to current time (within reasonable range) val newPollTime = lastPollTime.get() diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/battery/DefaultBatteryInfoProviderTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/battery/DefaultBatteryInfoProviderTest.kt index bd52b06454..51cc4105e1 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/battery/DefaultBatteryInfoProviderTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/battery/DefaultBatteryInfoProviderTest.kt @@ -15,6 +15,7 @@ import android.os.BatteryManager import android.os.BatteryManager.BATTERY_PROPERTY_CAPACITY import android.os.Build import android.os.PowerManager +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.utils.forge.Configurator import com.datadog.tools.unit.annotations.TestTargetApi import com.datadog.tools.unit.extensions.ApiLevelExtension @@ -62,7 +63,7 @@ internal class DefaultBatteryInfoProviderTest { @Mock lateinit var mockBatteryManager: BatteryManager - private val testSuiteStartTime = System.currentTimeMillis() + private val testSuiteStartTime = DefaultTimeProvider().getDeviceTimestamp() private val shortPollingInterval = 200 diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeAttributePropagationTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeAttributePropagationTest.kt index df709cb7a9..1452c4dc99 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeAttributePropagationTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeAttributePropagationTest.kt @@ -14,6 +14,7 @@ import com.datadog.android.api.feature.EventWriteScope import com.datadog.android.api.feature.FeatureScope import com.datadog.android.api.storage.DataWriter import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.RumSessionListener import com.datadog.android.rum.RumSessionType import com.datadog.android.rum.internal.FeaturesContextResolver @@ -169,8 +170,8 @@ internal class RumApplicationScopeAttributePropagationTest { ) val fakeOffset = -forge.aLong(1000, 50000) - val fakeTimestamp = System.currentTimeMillis() + fakeOffset - val fakeNanos = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(fakeOffset) + val fakeTimestamp = DefaultTimeProvider().getDeviceTimestamp() + fakeOffset + val fakeNanos = DefaultTimeProvider().getDeviceElapsedTimeNs() + TimeUnit.MILLISECONDS.toNanos(fakeOffset) val maxLimit = max(Long.MAX_VALUE - fakeTimestamp, Long.MAX_VALUE) val minLimit = min(-fakeTimestamp, maxLimit) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeAttributePropagationTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeAttributePropagationTest.kt index 16e77a2794..cae78a6a92 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeAttributePropagationTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeAttributePropagationTest.kt @@ -18,6 +18,7 @@ import com.datadog.android.api.storage.EventBatchWriter import com.datadog.android.api.storage.EventType import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.RumAttributes import com.datadog.android.rum.RumErrorSource import com.datadog.android.rum.RumSessionType @@ -219,8 +220,8 @@ internal class RumViewScopeAttributePropagationTest { fakeParentContext.copy(syntheticsTestId = null, syntheticsResultId = null) val fakeOffset = -forge.aLong(1000, 50000) - val fakeTimestamp = System.currentTimeMillis() + fakeOffset - val fakeNanos = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(fakeOffset) + val fakeTimestamp = DefaultTimeProvider().getDeviceTimestamp() + fakeOffset + val fakeNanos = DefaultTimeProvider().getDeviceElapsedTimeNs() + TimeUnit.MILLISECONDS.toNanos(fakeOffset) val maxLimit = max(Long.MAX_VALUE - fakeTimestamp, Long.MAX_VALUE) val minLimit = min(-fakeTimestamp, maxLimit) fakeSampleRate = forge.aFloat(min = 0.0f, max = 100.0f) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt index ab92fe5be0..c272ea540d 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt @@ -19,6 +19,7 @@ import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.feature.event.ThreadDump import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver import com.datadog.android.internal.telemetry.InternalTelemetryEvent +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.utils.loggableStackTrace import com.datadog.android.rum.RumActionType import com.datadog.android.rum.RumAttributes @@ -330,8 +331,8 @@ internal class RumViewScopeTest { fakeParentContext = fakeParentContext.copy(syntheticsTestId = null, syntheticsResultId = null) val fakeOffset = -forge.aLong(1000, 50000) - val fakeTimestamp = System.currentTimeMillis() + fakeOffset - val fakeNanos = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(fakeOffset) + val fakeTimestamp = DefaultTimeProvider().getDeviceTimestamp() + fakeOffset + val fakeNanos = DefaultTimeProvider().getDeviceElapsedTimeNs() + TimeUnit.MILLISECONDS.toNanos(fakeOffset) val maxLimit = max(Long.MAX_VALUE - fakeTimestamp, Long.MAX_VALUE) val minLimit = min(-fakeTimestamp, maxLimit) val fakeBrightness = forge.aFloat(0f, 255f) @@ -5490,7 +5491,7 @@ internal class RumViewScopeTest { mockEventWriteScope, mockWriter ) - val customTimingEstimatedDuration = System.nanoTime() - fakeEventTime.nanoTime + val customTimingEstimatedDuration = DefaultTimeProvider().getDeviceElapsedTimeNs() - fakeEventTime.nanoTime // Then argumentCaptor { @@ -5567,14 +5568,14 @@ internal class RumViewScopeTest { mockEventWriteScope, mockWriter ) - val customTiming1EstimatedDuration = System.nanoTime() - fakeEventTime.nanoTime + val customTiming1EstimatedDuration = DefaultTimeProvider().getDeviceElapsedTimeNs() - fakeEventTime.nanoTime testedScope.handleEvent( RumRawEvent.AddCustomTiming(fakeTimingKey2), fakeDatadogContext, mockEventWriteScope, mockWriter ) - val customTiming2EstimatedDuration = System.nanoTime() - fakeEventTime.nanoTime + val customTiming2EstimatedDuration = DefaultTimeProvider().getDeviceElapsedTimeNs() - fakeEventTime.nanoTime // Then argumentCaptor { @@ -8375,7 +8376,10 @@ internal class RumViewScopeTest { fakeEvent = RumRawEvent.StopView( key = testedScope.key, attributes = forge.exhaustiveAttributes(), - eventTime = Time(nanoTime = fakeEventTime.nanoTime + fakeViewDurationNs) + eventTime = fakeEventTime.copy( + nanoTime = fakeEventTime.nanoTime + fakeViewDurationNs, + timestamp = fakeEventTime.timestamp + TimeUnit.NANOSECONDS.toMillis(fakeViewDurationNs) + ) ) // When @@ -8391,7 +8395,12 @@ internal class RumViewScopeTest { ) { // When testedScope.handleEvent( - forge.startViewEvent(eventTime = Time(nanoTime = fakeEventTime.nanoTime + fakeViewDurationNs)), + forge.startViewEvent( + eventTime = fakeEventTime.copy( + nanoTime = fakeEventTime.nanoTime + fakeViewDurationNs, + timestamp = fakeEventTime.timestamp + TimeUnit.NANOSECONDS.toMillis(fakeViewDurationNs) + ) + ), fakeDatadogContext, mockEventWriteScope, mockWriter @@ -8405,7 +8414,12 @@ internal class RumViewScopeTest { fun `M call resolveReport(viewId, true, Long) of slowFramesListener W handleEvent(StopSession)`() { // When testedScope.handleEvent( - RumRawEvent.StopSession(eventTime = Time(nanoTime = fakeEventTime.nanoTime + fakeViewDurationNs)), + RumRawEvent.StopSession( + eventTime = fakeEventTime.copy( + nanoTime = fakeEventTime.nanoTime + fakeViewDurationNs, + timestamp = fakeEventTime.timestamp + TimeUnit.NANOSECONDS.toMillis(fakeViewDurationNs) + ) + ), fakeDatadogContext, mockEventWriteScope, mockWriter @@ -8547,7 +8561,12 @@ internal class RumViewScopeTest { @Test fun `M onDurationResolved W closing scope(StopSession)`(@LongForgery(min = 1) expectedDuration: Long) { // Given - val stopEvent = RumRawEvent.StopSession(eventTime = Time(nanoTime = fakeEventTime.nanoTime + expectedDuration)) + val stopEvent = RumRawEvent.StopSession( + eventTime = fakeEventTime.copy( + nanoTime = fakeEventTime.nanoTime + expectedDuration, + timestamp = fakeEventTime.timestamp + TimeUnit.NANOSECONDS.toMillis(expectedDuration) + ) + ) testedScope = newRumViewScope() // When @@ -8562,7 +8581,10 @@ internal class RumViewScopeTest { // Given val stopEvent = RumRawEvent.AddViewLoadingTime( overwrite = false, - eventTime = Time(nanoTime = fakeEventTime.nanoTime + expectedDuration) + eventTime = fakeEventTime.copy( + nanoTime = fakeEventTime.nanoTime + expectedDuration, + timestamp = fakeEventTime.timestamp + TimeUnit.NANOSECONDS.toMillis(expectedDuration) + ) ) testedScope = newRumViewScope() @@ -8576,7 +8598,7 @@ internal class RumViewScopeTest { @Test fun `M return a new RumViewScope W renew the current one`() { // Given - val expectedTime = Time(nanoTime = fakeEventTime.nanoTime) + val expectedTime = fakeEventTime.copy() // When val newScope = testedScope.renew(expectedTime) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/ActionTypeInteractionValidatorTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/ActionTypeInteractionValidatorTest.kt index 7e3e1eb26a..ed358528cb 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/ActionTypeInteractionValidatorTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/ActionTypeInteractionValidatorTest.kt @@ -6,6 +6,7 @@ package com.datadog.android.rum.internal.metric.interactiontonextview +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.model.ActionEvent import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach @@ -64,22 +65,22 @@ internal class ActionTypeInteractionValidatorTest { InternalInteractionContext( "viewId", ActionEvent.ActionEventActionType.TAP, - System.nanoTime() + DefaultTimeProvider().getDeviceElapsedTimeNs() ), InternalInteractionContext( "viewId", ActionEvent.ActionEventActionType.CLICK, - System.nanoTime() + DefaultTimeProvider().getDeviceElapsedTimeNs() ), InternalInteractionContext( "viewId", ActionEvent.ActionEventActionType.SWIPE, - System.nanoTime() + DefaultTimeProvider().getDeviceElapsedTimeNs() ), InternalInteractionContext( "viewId", ActionEvent.ActionEventActionType.BACK, - System.nanoTime() + DefaultTimeProvider().getDeviceElapsedTimeNs() ) ) } @@ -90,12 +91,12 @@ internal class ActionTypeInteractionValidatorTest { InternalInteractionContext( "viewId", ActionEvent.ActionEventActionType.CUSTOM, - System.nanoTime() + DefaultTimeProvider().getDeviceElapsedTimeNs() ), InternalInteractionContext( "viewId", ActionEvent.ActionEventActionType.SCROLL, - System.nanoTime() + DefaultTimeProvider().getDeviceElapsedTimeNs() ) ) } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/InteractionToNextViewMetricResolverTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/InteractionToNextViewMetricResolverTest.kt index 2d8624b6c6..32545273f7 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/InteractionToNextViewMetricResolverTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/InteractionToNextViewMetricResolverTest.kt @@ -7,6 +7,7 @@ package com.datadog.android.rum.internal.metric.interactiontonextview import com.datadog.android.api.InternalLogger +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.internal.metric.NoValueReason import com.datadog.android.rum.internal.metric.ViewInitializationMetricsConfig import com.datadog.android.rum.metric.interactiontonextview.LastInteractionIdentifier @@ -53,7 +54,7 @@ internal class InteractionToNextViewMetricResolverTest { private lateinit var fakeViewId: String private lateinit var fakeFirstViewId: String - private val fakeFirstViewTimestampNs: Long = System.nanoTime() + private val fakeFirstViewTimestampNs: Long = DefaultTimeProvider().getDeviceElapsedTimeNs() // region setup @@ -135,7 +136,7 @@ internal class InteractionToNextViewMetricResolverTest { @Test fun `M return NO_ACTION W getState { no action was registered on previous view }`() { // When - testedMetric.onViewCreated(fakeViewId, System.nanoTime()) + testedMetric.onViewCreated(fakeViewId, DefaultTimeProvider().getDeviceElapsedTimeNs()) val result = testedMetric.getState(fakeViewId) // Then @@ -149,7 +150,7 @@ internal class InteractionToNextViewMetricResolverTest { ) { // Given val fakePreviousActions = forge.generateFakeActions(1, 10, fakeFirstViewId) - val fakeViewCreatedTimestamp = System.nanoTime() + val fakeViewCreatedTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() whenever(mockInteractionValidator.validate(any())).thenReturn(true) whenever(mockLastInteractionIdentifier.validate(any())).thenReturn(false) fakePreviousActions.forEach { testedMetric.onActionSent(it) } @@ -167,7 +168,7 @@ internal class InteractionToNextViewMetricResolverTest { fun `M return the right initializationTime and null noValueReason W getState`(forge: Forge) { // Given val fakePreviousActions = forge.generateFakeActions(1, 2, fakeFirstViewId) - val fakeCurrentViewCreatedTimestamp = System.nanoTime() + val fakeCurrentViewCreatedTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() fakePreviousActions.mockValidators(fakeCurrentViewCreatedTimestamp) fakePreviousActions.forEach { testedMetric.onActionSent(it) } val expectedMetricValue = fakeCurrentViewCreatedTimestamp - fakePreviousActions.last().eventCreatedAtNanos @@ -215,7 +216,7 @@ internal class InteractionToNextViewMetricResolverTest { fun `M return the right metric W resolveMetric`(forge: Forge) { // Given val fakePreviousActions = forge.generateFakeActions(1, 10, fakeFirstViewId) - val fakeViewCreatedTimestamp = System.nanoTime() + val fakeViewCreatedTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() fakePreviousActions.mockValidators(fakeViewCreatedTimestamp) fakePreviousActions.forEach { testedMetric.onActionSent(it) @@ -235,7 +236,7 @@ internal class InteractionToNextViewMetricResolverTest { forge: Forge ) { // Given - val fakeViewCreatedTimestamp = System.nanoTime() + val fakeViewCreatedTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() val fakePreviousActions = forge.generateFakeActions(1, 10, fakeFirstViewId, fakeViewCreatedTimestamp) fakePreviousActions.mockValidators(fakeViewCreatedTimestamp) fakePreviousActions.forEach { @@ -262,7 +263,7 @@ internal class InteractionToNextViewMetricResolverTest { ) { // Given val fakePreviousActions = forge.generateFakeActions(1, 10, fakeFirstViewId) - val fakeViewCreatedTimestamp = System.nanoTime() + val fakeViewCreatedTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() fakePreviousActions.forEach { whenever(mockInteractionValidator.validate(it)).thenReturn(false) whenever( @@ -291,7 +292,7 @@ internal class InteractionToNextViewMetricResolverTest { ) { // Given val fakePreviousActions = forge.generateFakeActions(1, 10, fakeFirstViewId) - val fakeViewCreatedTimestamp = System.nanoTime() + val fakeViewCreatedTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() fakePreviousActions.forEach { whenever(mockInteractionValidator.validate(it)).thenReturn(true) whenever( @@ -320,7 +321,7 @@ internal class InteractionToNextViewMetricResolverTest { ) { // Given val fakePreviousActions = forge.generateFakeActions(1, 10, forge.aString()) - val fakeViewCreatedTimestamp = System.nanoTime() + val fakeViewCreatedTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() fakePreviousActions.mockValidators(fakeViewCreatedTimestamp) testedMetric.onViewCreated(fakeViewId, fakeViewCreatedTimestamp) @@ -504,7 +505,7 @@ internal class InteractionToNextViewMetricResolverTest { fun `M be thread safe W resolveMetric`(forge: Forge) { // Given val fakePreviousActions = forge.generateFakeActions(1, 10, fakeFirstViewId) - val fakeViewCreatedTimestamp = System.nanoTime() + val fakeViewCreatedTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() fakePreviousActions.mockValidators(fakeViewCreatedTimestamp) fakePreviousActions.forEach { Thread { @@ -582,7 +583,7 @@ internal class InteractionToNextViewMetricResolverTest { min: Int, max: Int, viewId: String, - startTimestamp: Long = System.nanoTime() + startTimestamp: Long = DefaultTimeProvider().getDeviceElapsedTimeNs() ): List { return aList(size = anInt(min = min, max = max)) { getForgery() diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitorTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitorTest.kt index 11ac5a5eb2..95b3f61d79 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitorTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitorTest.kt @@ -20,6 +20,7 @@ import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.feature.event.ThreadDump import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver import com.datadog.android.internal.telemetry.InternalTelemetryEvent +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.DdRumContentProvider import com.datadog.android.rum.ExperimentalRumApi import com.datadog.android.rum.RumActionType @@ -1119,7 +1120,7 @@ internal class DatadogRumMonitorTest { mockRumFeatureScope.getWriteContextSync(setOf(Feature.SESSION_REPLAY_FEATURE_NAME)) ) doReturn (fakeDatadogContext to mockEventWriteScope) testedMonitor.drainExecutorService() - val now = System.nanoTime() + val now = DefaultTimeProvider().getDeviceElapsedTimeNs() val appStartTimeNs = forge.aLong(min = 0L, max = now) whenever(mockSdkCore.appStartTimeNs) doReturn appStartTimeNs diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/metric/interactiontonextview/TimeBasedInteractionIdentifierTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/metric/interactiontonextview/TimeBasedInteractionIdentifierTest.kt index 2c38b779b1..d78131ce96 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/metric/interactiontonextview/TimeBasedInteractionIdentifierTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/metric/interactiontonextview/TimeBasedInteractionIdentifierTest.kt @@ -6,6 +6,7 @@ package com.datadog.android.rum.metric.interactiontonextview +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.utils.forge.Configurator import com.datadog.tools.unit.ObjectTest import fr.xgouchet.elmyr.Forge @@ -71,7 +72,7 @@ internal class TimeBasedInteractionIdentifierTest : ObjectTest().toString() testedMetric.resourceWasStarted(InternalResourceContext(fakeResourceId, startTimestamp)) @@ -196,7 +197,7 @@ internal class NetworkSettledMetricResolverTest { fun `M return null W resolveMetric(){ view not created }`(forge: Forge) { // Given testedMetric = NetworkSettledMetricResolver(mockInitialResourceIdentifier, mockInternalLogger) - val startTimestamp = System.nanoTime() + val startTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() val stopTimestamp = startTimestamp + forge.aLong(min = 1, max = 1000) val fakeResourceId = forge.getForgery().toString() testedMetric.resourceWasStarted(InternalResourceContext(fakeResourceId, startTimestamp)) @@ -245,7 +246,7 @@ internal class NetworkSettledMetricResolverTest { fun `M pass the viewCreatedTimestamp to validator W resourceWasStarted()`(forge: Forge) { // Given testedMetric = NetworkSettledMetricResolver(mockInitialResourceIdentifier, mockInternalLogger) - val startTimestamp = System.nanoTime() + val startTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() val expectedViewCreatedTimestamp: Long? if (forge.aBool()) { testedMetric.viewWasCreated(fakeViewStartTime) @@ -414,7 +415,7 @@ internal class NetworkSettledMetricResolverTest { @Test fun `M return null W resolveMetric(){ resource was stopped with a different id }`(forge: Forge) { // Given - val startTimestamp = System.nanoTime() + val startTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() val stopTimestamp = startTimestamp + forge.aLong(min = 1, max = 1000) val fakeResourceContext = forge.getForgery() val fakeDifferentResourceContext = forge.getForgery() @@ -436,7 +437,7 @@ internal class NetworkSettledMetricResolverTest { @Test fun `M return null W resolveMetric(){ resource was dropped }`(forge: Forge) { // Given - val startTimestamp = System.nanoTime() + val startTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() val fakeResourceContext = forge.getForgery() testedMetric.resourceWasStarted(InternalResourceContext(fakeResourceContext.resourceId, startTimestamp)) testedMetric.resourceWasDropped(fakeResourceContext.resourceId) @@ -482,7 +483,7 @@ internal class NetworkSettledMetricResolverTest { forge: Forge ) { // Given - val startTimestamp = System.nanoTime() + val startTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() val stopTimestamp = startTimestamp + forge.aLong(min = 1, max = 1000) val fakeResourceContext = forge.getForgery() val thread1 = Thread { @@ -518,7 +519,7 @@ internal class NetworkSettledMetricResolverTest { forge: Forge ) { // Given - val startTimestamp = System.nanoTime() + val startTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() val stopTimestamp = startTimestamp + forge.aLong(min = 1, max = 1000) val fakeResourceContext = forge.getForgery() val thread1 = Thread { @@ -555,7 +556,7 @@ internal class NetworkSettledMetricResolverTest { forge: Forge ) { // Given - val startTimestamp = System.nanoTime() + val startTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() val stopTimestamp = startTimestamp + forge.aLong(min = 1, max = 1000) val fakeResourceContext = forge.getForgery() val thread1 = Thread { @@ -593,7 +594,7 @@ internal class NetworkSettledMetricResolverTest { // region Internal private fun Forge.forgeStartTimestamps(size: Int = anInt(min = 1, max = 10)) = aList(size = size) { - System.nanoTime() + aLong(min = 1, max = 1000) + DefaultTimeProvider().getDeviceElapsedTimeNs() + aLong(min = 1, max = 1000) } private fun List.mapToSettledIntervals(): List { diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandler.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandler.kt index 14eac29127..acf6425adc 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandler.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandler.kt @@ -12,6 +12,7 @@ import androidx.annotation.WorkerThread import com.datadog.android.api.InternalLogger import com.datadog.android.core.internal.utils.executeSafe import com.datadog.android.core.sampling.RateBasedSampler +import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.sessionreplay.internal.processor.RecordedDataProcessor import com.datadog.android.sessionreplay.internal.processor.RumContextDataHandler import com.datadog.android.sessionreplay.model.MobileSegment @@ -128,7 +129,7 @@ internal class RecordedDataQueueHandler( val nextItem = recordedDataQueue.peek() if (nextItem != null) { - val nextItemAgeInNs = System.nanoTime() - nextItem.creationTimeStampInNs + val nextItemAgeInNs = SdkTimeProvider.elapsed() - nextItem.creationTimeStampInNs if (!nextItem.isValid()) { if (sampler.sample(Unit)) { logInvalidQueueItemException(nextItem) diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueItem.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueItem.kt index 0e02939187..879337ba60 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueItem.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueItem.kt @@ -6,11 +6,12 @@ package com.datadog.android.sessionreplay.internal.async +import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.sessionreplay.internal.processor.RecordedQueuedItemContext internal abstract class RecordedDataQueueItem( internal val recordedQueuedItemContext: RecordedQueuedItemContext, - internal val creationTimeStampInNs: Long = System.nanoTime() + internal val creationTimeStampInNs: Long = SdkTimeProvider.elapsed() ) { internal abstract fun isValid(): Boolean diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/processor/RecordedDataProcessor.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/processor/RecordedDataProcessor.kt index 12db3af535..051921f0b7 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/processor/RecordedDataProcessor.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/processor/RecordedDataProcessor.kt @@ -8,6 +8,7 @@ package com.datadog.android.sessionreplay.internal.processor import android.content.res.Configuration import androidx.annotation.WorkerThread +import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.sessionreplay.internal.async.ResourceRecordedDataQueueItem import com.datadog.android.sessionreplay.internal.async.SnapshotRecordedDataQueueItem import com.datadog.android.sessionreplay.internal.async.TouchEventRecordedDataQueueItem @@ -162,8 +163,8 @@ internal class RecordedDataProcessor( } private fun isTimeForFullSnapshot(): Boolean { - return if (System.nanoTime() - lastSnapshotTimestamp >= FULL_SNAPSHOT_INTERVAL_IN_NS) { - lastSnapshotTimestamp = System.nanoTime() + return if (SdkTimeProvider.elapsed() - lastSnapshotTimestamp >= FULL_SNAPSHOT_INTERVAL_IN_NS) { + lastSnapshotTimestamp = SdkTimeProvider.elapsed() true } else { false diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/Debouncer.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/Debouncer.kt index f2cab2e730..e2200fb252 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/Debouncer.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/Debouncer.kt @@ -10,6 +10,7 @@ import android.os.Handler import android.os.Looper import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.FeatureSdkCore +import com.datadog.android.core.time.SdkTimeProvider import java.util.concurrent.TimeUnit internal class Debouncer( @@ -29,11 +30,11 @@ internal class Debouncer( // reason why we are not initializing this in the constructor is that in case the // component was initialized earlier than the first debounce request was requested // it will execute the runnable directly and will not pass through the handler. - lastTimeRecordWasPerformed = System.nanoTime() + lastTimeRecordWasPerformed = SdkTimeProvider.elapsed() firstRequest = false } handler.removeCallbacksAndMessages(null) - val timePassedSinceLastExecution = System.nanoTime() - lastTimeRecordWasPerformed + val timePassedSinceLastExecution = SdkTimeProvider.elapsed() - lastTimeRecordWasPerformed if (timePassedSinceLastExecution >= maxRecordDelayInNs) { executeRunnable(runnable) } else { @@ -49,14 +50,14 @@ internal class Debouncer( } else { runnable.run() } - lastTimeRecordWasPerformed = System.nanoTime() + lastTimeRecordWasPerformed = SdkTimeProvider.elapsed() } private fun runInTimeBalance(block: () -> Unit) { - if (timeBank.updateAndCheck(System.nanoTime())) { - val startTimeInNano = System.nanoTime() + if (timeBank.updateAndCheck(SdkTimeProvider.elapsed())) { + val startTimeInNano = SdkTimeProvider.elapsed() block() - val endTimeInNano = System.nanoTime() + val endTimeInNano = SdkTimeProvider.elapsed() timeBank.consume(endTimeInNano - startTimeInNano) } else { logSkippedFrame() diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/callback/RecorderWindowCallback.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/callback/RecorderWindowCallback.kt index e3772595e6..c31c061ad6 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/callback/RecorderWindowCallback.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/callback/RecorderWindowCallback.kt @@ -12,6 +12,7 @@ import android.view.MotionEvent import android.view.Window import androidx.annotation.MainThread import com.datadog.android.api.InternalLogger +import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.internal.utils.densityNormalized import com.datadog.android.sessionreplay.ImagePrivacy @@ -49,7 +50,7 @@ internal class RecorderWindowCallback( private val pixelsDensity = appContext.resources.displayMetrics.density internal val pointerInteractions: MutableList = LinkedList() private var lastOnMoveUpdateTimeInNs: Long = 0L - private var lastPerformedFlushTimeInNs: Long = System.nanoTime() + private var lastPerformedFlushTimeInNs: Long = SdkTimeProvider.elapsed() private var shouldRecordMotion: Boolean = false // region Window.Callback @@ -99,19 +100,19 @@ internal class RecorderWindowCallback( when (event.action.and(MotionEvent.ACTION_MASK)) { MotionEvent.ACTION_DOWN -> { // reset the flush time to avoid flush in the next event - lastPerformedFlushTimeInNs = System.nanoTime() + lastPerformedFlushTimeInNs = SdkTimeProvider.elapsed() updatePositions(event, MobileSegment.PointerEventType.DOWN) // reset the on move update time in order to take into account the first move event lastOnMoveUpdateTimeInNs = 0 } MotionEvent.ACTION_MOVE -> { - if (System.nanoTime() - lastOnMoveUpdateTimeInNs >= motionUpdateThresholdInNs) { + if (SdkTimeProvider.elapsed() - lastOnMoveUpdateTimeInNs >= motionUpdateThresholdInNs) { updatePositions(event, MobileSegment.PointerEventType.MOVE) - lastOnMoveUpdateTimeInNs = System.nanoTime() + lastOnMoveUpdateTimeInNs = SdkTimeProvider.elapsed() } // make sure we flush from time to time to avoid glitches in the player - if (System.nanoTime() - lastPerformedFlushTimeInNs >= + if (SdkTimeProvider.elapsed() - lastPerformedFlushTimeInNs >= flushPositionBufferThresholdInNs ) { flushPositions() @@ -161,7 +162,7 @@ internal class RecorderWindowCallback( } pointerInteractions.clear() - lastPerformedFlushTimeInNs = System.nanoTime() + lastPerformedFlushTimeInNs = SdkTimeProvider.elapsed() } private fun logOrRethrowWrappedCallbackException(e: NullPointerException) { diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/resources/ResourceDataStoreManager.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/resources/ResourceDataStoreManager.kt index de1e0837b9..5aee7a03c0 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/resources/ResourceDataStoreManager.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/resources/ResourceDataStoreManager.kt @@ -14,6 +14,7 @@ import com.datadog.android.api.storage.datastore.DataStoreWriteCallback import com.datadog.android.core.internal.persistence.Deserializer import com.datadog.android.core.persistence.Serializer import com.datadog.android.core.persistence.datastore.DataStoreContent +import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.sessionreplay.model.ResourceHashesEntry import java.util.Collections import java.util.concurrent.ConcurrentHashMap @@ -27,7 +28,7 @@ internal class ResourceDataStoreManager( ) { @Suppress("UnsafeThirdPartyFunctionCall") // map is initialized empty private val knownResources = Collections.newSetFromMap(ConcurrentHashMap()) - private val storedLastUpdateDateNs = AtomicLong(System.nanoTime()) + private val storedLastUpdateDateNs = AtomicLong(SdkTimeProvider.elapsed()) private val isInitialized = AtomicBoolean(false) // has init finished executing its async actions init { @@ -129,7 +130,7 @@ internal class ResourceDataStoreManager( ) private fun didDataStoreExpire(lastUpdateDate: Long): Boolean = - System.nanoTime() - lastUpdateDate > DATASTORE_EXPIRATION_NS + SdkTimeProvider.elapsed() - lastUpdateDate > DATASTORE_EXPIRATION_NS // endregion diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandlerTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandlerTest.kt index 0b48454d67..d2d550287b 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandlerTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandlerTest.kt @@ -8,6 +8,7 @@ package com.datadog.android.sessionreplay.internal.async import com.datadog.android.api.InternalLogger import com.datadog.android.core.sampling.RateBasedSampler +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.sessionreplay.forge.ForgeConfigurator import com.datadog.android.sessionreplay.internal.async.RecordedDataQueueHandler.Companion.ITEM_DROPPED_EXPIRED_MESSAGE import com.datadog.android.sessionreplay.internal.async.RecordedDataQueueHandler.Companion.ITEM_DROPPED_INVALID_MESSAGE @@ -290,7 +291,7 @@ internal class RecordedDataQueueHandlerTest { ) { // Given mockSnapshotItem.apply { - val expiredTime = System.nanoTime() - RecordedDataQueueHandler.MAX_DELAY_NS + val expiredTime = DefaultTimeProvider().getDeviceElapsedTimeNs() - RecordedDataQueueHandler.MAX_DELAY_NS whenever(creationTimeStampInNs).thenReturn(expiredTime) whenever(isValid()).thenReturn(true) whenever(isReady()).thenReturn(true) @@ -532,7 +533,7 @@ internal class RecordedDataQueueHandlerTest { mockSnapshotItem1.apply { whenever(systemInformation).thenReturn(mockSystemInformation) whenever(nodes).thenReturn(fakeNodeData) - whenever(creationTimeStampInNs).thenReturn(System.nanoTime()) + whenever(creationTimeStampInNs).thenReturn(DefaultTimeProvider().getDeviceElapsedTimeNs()) whenever(isValid()).thenReturn(true) whenever(isReady()).thenReturn(true) } @@ -541,7 +542,7 @@ internal class RecordedDataQueueHandlerTest { mockSnapshotItem2.apply { whenever(systemInformation).thenReturn(mockSystemInformation) whenever(nodes).thenReturn(fakeNodeData) - whenever(creationTimeStampInNs).thenReturn(System.nanoTime()) + whenever(creationTimeStampInNs).thenReturn(DefaultTimeProvider().getDeviceElapsedTimeNs()) whenever(isValid()).thenReturn(true) whenever(isReady()).thenReturn(false) } @@ -550,7 +551,7 @@ internal class RecordedDataQueueHandlerTest { mockSnapshotItem3.apply { whenever(systemInformation).thenReturn(mockSystemInformation) whenever(nodes).thenReturn(fakeNodeData) - whenever(creationTimeStampInNs).thenReturn(System.nanoTime()) + whenever(creationTimeStampInNs).thenReturn(DefaultTimeProvider().getDeviceElapsedTimeNs()) whenever(isValid()).thenReturn(true) whenever(isReady()).thenReturn(true) } @@ -732,7 +733,7 @@ internal class RecordedDataQueueHandlerTest { ) mockSnapshotItem.apply { - val expiredTime = System.nanoTime() - RecordedDataQueueHandler.MAX_DELAY_NS + val expiredTime = DefaultTimeProvider().getDeviceElapsedTimeNs() - RecordedDataQueueHandler.MAX_DELAY_NS whenever(creationTimeStampInNs).thenReturn(expiredTime) whenever(isValid()).thenReturn(true) whenever(isReady()).thenReturn(true) @@ -764,7 +765,7 @@ internal class RecordedDataQueueHandlerTest { private fun addSnapshotItemToQueue(): SnapshotRecordedDataQueueItem { val newRumContext = RecordedQueuedItemContext( - timestamp = System.currentTimeMillis(), + timestamp = DefaultTimeProvider().getDeviceTimestamp(), newRumContext = fakeRecordedQueuedItemContext.newRumContext ) diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/resources/ResourceDataStoreManagerTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/resources/ResourceDataStoreManagerTest.kt index 753958c3d9..60bc0dc9f4 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/resources/ResourceDataStoreManagerTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/resources/ResourceDataStoreManagerTest.kt @@ -15,6 +15,7 @@ import com.datadog.android.api.storage.datastore.DataStoreWriteCallback import com.datadog.android.core.internal.persistence.Deserializer import com.datadog.android.core.persistence.Serializer import com.datadog.android.core.persistence.datastore.DataStoreContent +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.sessionreplay.forge.ForgeConfigurator import com.datadog.android.sessionreplay.internal.resources.ResourceDataStoreManager.Companion.DATASTORE_EXPIRATION_NS import com.datadog.android.sessionreplay.internal.resources.ResourceDataStoreManager.Companion.DATASTORE_HASHES_ENTRY_NAME @@ -328,9 +329,9 @@ internal class ResourceDataStoreManagerTest { val resourceHashes = forge.aList { aString() }.distinct() val fakeVersionCode = forge.anInt(min = 0) val entryTime = if (isExpired) { - System.nanoTime() - DATASTORE_EXPIRATION_NS + DefaultTimeProvider().getDeviceElapsedTimeNs() - DATASTORE_EXPIRATION_NS } else { - System.nanoTime() + DefaultTimeProvider().getDeviceElapsedTimeNs() } val mockResourceHashesEntry: ResourceHashesEntry = mock { diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/Config.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/Config.java index 3fb302ba43..7210f1cdde 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/Config.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/Config.java @@ -6,6 +6,7 @@ package com.datadog.trace.api; +import com.datadog.android.internal.time.DefaultTimeProvider; import static com.datadog.trace.api.ConfigDefaults.DEFAULT_AGENT_HOST; import static com.datadog.trace.api.ConfigDefaults.DEFAULT_AGENT_TIMEOUT; import static com.datadog.trace.api.ConfigDefaults.DEFAULT_AGENT_WRITER_TYPE; @@ -299,6 +300,7 @@ import java.util.SortedSet; import java.util.UUID; import java.util.regex.Matcher; +import com.datadog.android.internal.time.TimeProvider; import java.util.regex.Pattern; /** @@ -324,10 +326,11 @@ public class Config { private static final Logger log = LoggerFactory.getLogger(Config.class); private static final Pattern COLON = Pattern.compile(":"); + private static final TimeProvider TIME_PROVIDER = new DefaultTimeProvider(); private final InstrumenterConfig instrumenterConfig; - private final long startTimeMillis = System.currentTimeMillis(); + private final long startTimeMillis = TIME_PROVIDER.getDeviceTimestamp(); private final boolean timelineEventsEnabled; /** @@ -2311,4 +2314,4 @@ public String toString() { + telemetryMetricsEnabled + '}'; } -} +} \ No newline at end of file diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/IdGenerationStrategy.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/IdGenerationStrategy.java index 01721b1a39..2ab364cae8 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/IdGenerationStrategy.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/IdGenerationStrategy.java @@ -1,5 +1,6 @@ package com.datadog.trace.api; +import com.datadog.android.internal.time.DefaultTimeProvider; import static java.lang.Long.MAX_VALUE; import android.os.Build; @@ -8,6 +9,7 @@ import java.security.SecureRandom; import java.util.Locale; +import com.datadog.android.internal.time.TimeProvider; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicLong; @@ -54,8 +56,10 @@ public long generateSpanId() { protected abstract long getNonZeroPositiveLong(); + private static final TimeProvider TIME_PROVIDER = new DefaultTimeProvider(); + protected long generateHighOrderBits() { - long timestamp = System.currentTimeMillis() / 1000; + long timestamp = TIME_PROVIDER.getDeviceTimestamp() / 1000; return timestamp << 32; } @@ -128,4 +132,4 @@ protected long getNonZeroPositiveLong() { return value; } } -} +} \ No newline at end of file diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/time/SystemTimeSource.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/time/SystemTimeSource.java index 3bb5c0630c..5915bef82a 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/time/SystemTimeSource.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/api/time/SystemTimeSource.java @@ -1,20 +1,26 @@ package com.datadog.trace.api.time; +import com.datadog.android.internal.time.DefaultTimeProvider; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import com.datadog.android.internal.time.TimeProvider; + public class SystemTimeSource implements TimeSource { + + private static final TimeProvider TIME_PROVIDER = new DefaultTimeProvider(); + public static final TimeSource INSTANCE = new SystemTimeSource(); private SystemTimeSource() {} @Override public long getNanoTicks() { - return System.nanoTime(); + return TIME_PROVIDER.getDeviceElapsedTimeNs(); } @Override public long getCurrentTimeMillis() { - return System.currentTimeMillis(); + return TIME_PROVIDER.getDeviceTimestamp(); } @Override @@ -26,4 +32,4 @@ public long getCurrentTimeMicros() { public long getCurrentTimeNanos() { return MILLISECONDS.toNanos(getCurrentTimeMillis()); } -} +} \ No newline at end of file diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/scopemanager/ContinuableScopeManager.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/scopemanager/ContinuableScopeManager.java index 76a7fdb051..02a1c847f8 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/scopemanager/ContinuableScopeManager.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/core/scopemanager/ContinuableScopeManager.java @@ -1,11 +1,13 @@ package com.datadog.trace.core.scopemanager; +import com.datadog.android.internal.time.DefaultTimeProvider; import static com.datadog.trace.api.ConfigDefaults.DEFAULT_ASYNC_PROPAGATING; import static com.datadog.trace.bootstrap.instrumentation.api.AgentTracer.NoopAgentSpan; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.SECONDS; import com.datadog.android.api.InternalLogger; +import com.datadog.android.internal.time.TimeProvider; import com.datadog.trace.api.Config; import com.datadog.trace.api.Stateful; import com.datadog.trace.api.scopemanager.ExtendedScopeListener; @@ -361,6 +363,7 @@ private void cancelRootIterationScopeCleanup(ScopeStack scopeStack, ContinuableS private static final class RootIterationCleaner implements AgentTaskScheduler.Task> { private static final RootIterationCleaner CLEANER = new RootIterationCleaner(); + private static final TimeProvider TIME_PROVIDER = new DefaultTimeProvider(); public static void scheduleFor(Map rootIterationScopes) { long period = Math.min(iterationKeepAlive, 10_000); @@ -373,7 +376,7 @@ public void run(Map rootIterationScopes) { Iterator> itr = rootIterationScopes.entrySet().iterator(); - long cutOff = System.currentTimeMillis() - iterationKeepAlive; + long cutOff = TIME_PROVIDER.getDeviceTimestamp() - iterationKeepAlive; while (itr.hasNext()) { Map.Entry entry = itr.next(); @@ -392,4 +395,4 @@ public void run(Map rootIterationScopes) { } } } -} +} \ No newline at end of file diff --git a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/util/AgentTaskScheduler.java b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/util/AgentTaskScheduler.java index 1c72137913..28f21bb79a 100644 --- a/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/util/AgentTaskScheduler.java +++ b/features/dd-sdk-android-trace-internal/src/main/java/com/datadog/trace/util/AgentTaskScheduler.java @@ -1,5 +1,6 @@ package com.datadog.trace.util; +import com.datadog.android.internal.time.DefaultTimeProvider; import static com.datadog.trace.util.AgentThreadFactory.AGENT_THREAD_GROUP; import static com.datadog.trace.util.AgentThreadFactory.AgentThread.TASK_SCHEDULER; import static com.datadog.trace.util.AgentThreadFactory.newAgentThread; @@ -7,6 +8,7 @@ import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.SECONDS; +import com.datadog.android.internal.time.TimeProvider; import com.datadog.trace.logger.Logger; import com.datadog.trace.logger.LoggerFactory; import com.datadog.trace.util.AgentThreadFactory.AgentThread; @@ -277,6 +279,8 @@ private static final class PeriodicTask implements Delayed { private long nextFireTime; + private static final TimeProvider TIME_PROVIDER = new DefaultTimeProvider(); + public PeriodicTask( final Task task, final Target target, @@ -289,7 +293,7 @@ public PeriodicTask( this.period = unit.toNanos(period); this.taskSequence = TASK_SEQUENCE_GENERATOR.getAndIncrement(); - nextFireTime = System.nanoTime() + unit.toNanos(initialDelay); + nextFireTime = TIME_PROVIDER.getDeviceElapsedTimeNs() + unit.toNanos(initialDelay); } public void run() { @@ -309,7 +313,7 @@ public boolean reschedule() { @Override public long getDelay(final TimeUnit unit) { - return unit.convert(nextFireTime - System.nanoTime(), NANOSECONDS); + return unit.convert(nextFireTime - TIME_PROVIDER.getDeviceElapsedTimeNs(), NANOSECONDS); } @Override @@ -344,4 +348,4 @@ public String toString() { return describeTask(task, target); } } -} +} \ No newline at end of file diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/api/IdGenerationStrategyTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/api/IdGenerationStrategyTest.kt index 8d302556fa..dc31fdd6f5 100644 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/api/IdGenerationStrategyTest.kt +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/api/IdGenerationStrategyTest.kt @@ -6,6 +6,7 @@ package com.datadog.trace.api +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.utils.forge.Configurator import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension @@ -36,7 +37,7 @@ internal class IdGenerationStrategyTest { @ValueSource(strings = ["RANDOM", "SEQUENTIAL", "SECURE_RANDOM"]) fun `M generate id with strategyName and tIdSize bits`(strategyName: String) { val isTid128 = listOf(false, true) - val highOrderLowerBound = (System.currentTimeMillis() / 1000).shl(32) + val highOrderLowerBound = (DefaultTimeProvider().getDeviceTimestamp() / 1000).shl(32) isTid128.forEach { tId128b -> val strategy = IdGenerationStrategy.fromName(strategyName, tId128b) val traceIds = (0..4096).map { strategy.generateTraceId() } @@ -55,7 +56,7 @@ internal class IdGenerationStrategyTest { assertThat(hashCode).isEqualTo(traceId.hashCode()) assertThat(checked).doesNotContain(traceId) if (tId128b && strategyName != "SEQUENTIAL") { - val highOrderUpperBound = (System.currentTimeMillis() / 1000).shl(32) + val highOrderUpperBound = (DefaultTimeProvider().getDeviceTimestamp() / 1000).shl(32) val lowOrderUpperBound = Long.MAX_VALUE assertThat(traceId.toLong()).isLessThanOrEqualTo(lowOrderUpperBound) assertThat(traceId.toHighOrderLong()).isLessThanOrEqualTo(highOrderUpperBound) diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/CoreSpanBuilderTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/CoreSpanBuilderTest.kt index 00c4080759..c83c9687f8 100644 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/CoreSpanBuilderTest.kt +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/CoreSpanBuilderTest.kt @@ -6,6 +6,7 @@ package com.datadog.trace.core +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.utils.safeGetThreadId import com.datadog.tools.unit.getFieldValue import com.datadog.trace.api.Config @@ -156,9 +157,9 @@ internal class CoreSpanBuilderTest : DDCoreSpecification() { // When // auto-timestamp in nanoseconds - val start = System.currentTimeMillis() + val start = DefaultTimeProvider().getDeviceTimestamp() val secondSpan = tracer.buildSpan(instrumentationName, expectedName).withServiceName("foo").start() - val stop = System.currentTimeMillis() + val stop = DefaultTimeProvider().getDeviceTimestamp() // Then // Give a range of +/- 1 millis diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanTest.kt index 8bd990d4fb..a76a82d615 100644 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanTest.kt +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanTest.kt @@ -6,6 +6,7 @@ package com.datadog.trace.core +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.tools.unit.getFieldValue import com.datadog.trace.api.DDSpanId import com.datadog.trace.api.DDTags @@ -142,18 +143,18 @@ internal class DDSpanTest : DDCoreSpecification() { // Given val mod = TimeUnit.MILLISECONDS.toNanos(1) val builder = tracer.buildSpan(instrumentationName, "test") - val start = System.nanoTime() + val start = DefaultTimeProvider().getDeviceElapsedTimeNs() val span = builder.start() - val between = System.nanoTime() - val betweenDur = System.nanoTime() - between - val total = System.nanoTime() - start + val between = DefaultTimeProvider().getDeviceElapsedTimeNs() + val betweenDur = DefaultTimeProvider().getDeviceElapsedTimeNs() - between + val total = DefaultTimeProvider().getDeviceElapsedTimeNs() - start // When span.finish() // Then val timeDifference = TimeUnit.NANOSECONDS.toSeconds(span.startTime) - - TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) + TimeUnit.MILLISECONDS.toSeconds(DefaultTimeProvider().getDeviceTimestamp()) assertThat(timeDifference).isLessThan(5) assertThat(span.durationNano).isGreaterThan(betweenDur) assertThat(span.durationNano).isLessThan(total) @@ -165,10 +166,10 @@ internal class DDSpanTest : DDCoreSpecification() { // Given val mod = TimeUnit.MILLISECONDS.toNanos(1) val builder = tracer.buildSpan(instrumentationName, "test") - val start = System.nanoTime() + val start = DefaultTimeProvider().getDeviceElapsedTimeNs() val span = builder.start() - val between = System.nanoTime() - val betweenDur = System.nanoTime() - between + val between = DefaultTimeProvider().getDeviceElapsedTimeNs() + val betweenDur = DefaultTimeProvider().getDeviceElapsedTimeNs() - between // When span.publish() @@ -179,14 +180,14 @@ internal class DDSpanTest : DDCoreSpecification() { // When var finish = span.phasedFinish() - val total = System.nanoTime() - start + val total = DefaultTimeProvider().getDeviceElapsedTimeNs() - start // Then assertThat(finish).isTrue() assertThat(writer).isEmpty() val actualDurationNano = span.durationNano and Long.MAX_VALUE val timeDifference = TimeUnit.NANOSECONDS.toSeconds(span.startTime) - - TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) + TimeUnit.MILLISECONDS.toSeconds(DefaultTimeProvider().getDeviceTimestamp()) assertThat(timeDifference).isLessThan(5) assertThat(actualDurationNano).isGreaterThan(betweenDur) assertThat(actualDurationNano).isLessThan(total) @@ -219,20 +220,20 @@ internal class DDSpanTest : DDCoreSpecification() { fun `starting with a timestamp disables nanotime`() { // Given val mod = TimeUnit.MILLISECONDS.toNanos(1) - val start = System.currentTimeMillis() + val start = DefaultTimeProvider().getDeviceTimestamp() val builder = tracer.buildSpan(instrumentationName, "test") - .withStartTimestamp(TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis())) + .withStartTimestamp(TimeUnit.MILLISECONDS.toMicros(DefaultTimeProvider().getDeviceTimestamp())) val span = builder.start() - val between = System.currentTimeMillis() - val betweenDur = System.currentTimeMillis() - between + val between = DefaultTimeProvider().getDeviceTimestamp() + val betweenDur = DefaultTimeProvider().getDeviceTimestamp() - between // When span.finish() // Then - val total = Math.max(1, System.currentTimeMillis() - start) + val total = Math.max(1, DefaultTimeProvider().getDeviceTimestamp() - start) val timeDifference = TimeUnit.NANOSECONDS.toSeconds(span.startTime) - - TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) + TimeUnit.MILLISECONDS.toSeconds(DefaultTimeProvider().getDeviceTimestamp()) assertThat(timeDifference).isLessThan(5) assertThat(span.durationNano).isGreaterThanOrEqualTo(TimeUnit.MILLISECONDS.toNanos(betweenDur)) assertThat(span.durationNano).isLessThanOrEqualTo(TimeUnit.MILLISECONDS.toNanos(total)) @@ -244,16 +245,16 @@ internal class DDSpanTest : DDCoreSpecification() { // Given val mod = TimeUnit.MILLISECONDS.toNanos(1) val builder = tracer.buildSpan(instrumentationName, "test") - val start = System.currentTimeMillis() + val start = DefaultTimeProvider().getDeviceTimestamp() val span = builder.start() - val between = System.currentTimeMillis() - val betweenDur = System.currentTimeMillis() - between + val between = DefaultTimeProvider().getDeviceTimestamp() + val betweenDur = DefaultTimeProvider().getDeviceTimestamp() - between // When - span.finish(TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis() + 1)) - val total = System.currentTimeMillis() - start + 1 + span.finish(TimeUnit.MILLISECONDS.toMicros(DefaultTimeProvider().getDeviceTimestamp() + 1)) + val total = DefaultTimeProvider().getDeviceTimestamp() - start + 1 val timeDifference = TimeUnit.NANOSECONDS.toSeconds(span.startTime) - - TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) + TimeUnit.MILLISECONDS.toSeconds(DefaultTimeProvider().getDeviceTimestamp()) // Then assertThat(timeDifference).isLessThan(5) diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/PendingTraceTestBase.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/PendingTraceTestBase.kt index ff18570dd6..2d8138f3f9 100644 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/PendingTraceTestBase.kt +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/PendingTraceTestBase.kt @@ -6,6 +6,7 @@ package com.datadog.trace.core +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.tools.unit.getFieldValue import com.datadog.trace.common.writer.ListWriter import org.assertj.core.api.Assertions.assertThat @@ -136,7 +137,7 @@ internal abstract class PendingTraceTestBase : DDCoreSpecification() { assertThat( Math.abs( TimeUnit.NANOSECONDS.toSeconds(trace.currentTimeNano) - - TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) + TimeUnit.MILLISECONDS.toSeconds(DefaultTimeProvider().getDeviceTimestamp()) ) ).isLessThan(5) } diff --git a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanLogger.kt b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanLogger.kt index b3eccacd5a..0938305e72 100644 --- a/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanLogger.kt +++ b/features/dd-sdk-android-trace/src/main/kotlin/com/datadog/android/trace/internal/DatadogSpanLogger.kt @@ -77,7 +77,7 @@ internal class DatadogSpanLogger( val logStatus = fields.remove(DatadogTracingConstants.LogAttributes.STATUS) ?: Log.VERBOSE fields[LogAttributes.DD_TRACE_ID] = span.context().traceId.toHexString() fields[LogAttributes.DD_SPAN_ID] = span.context().spanId.toString() - val timestamp = System.currentTimeMillis() + val timestamp = sdkCore.timeProvider.getDeviceTimestamp() logsFeature.sendEvent( buildMap { put("type", "span_log") diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanLoggerTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanLoggerTest.kt index bb9bba0d65..3c1421a2ac 100644 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanLoggerTest.kt +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanLoggerTest.kt @@ -9,6 +9,7 @@ import android.util.Log import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.FeatureScope import com.datadog.android.api.feature.FeatureSdkCore +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.internal.utils.loggableStackTrace import com.datadog.android.log.LogAttributes import com.datadog.android.trace.api.DatadogTracingConstants @@ -33,6 +34,7 @@ import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever import org.mockito.quality.Strictness @Extensions( @@ -60,13 +62,18 @@ class DatadogSpanLoggerTest { @Mock lateinit var mockLogFeatureScope: FeatureScope + @Mock + lateinit var mockTimeProvider: TimeProvider + private lateinit var testedLogger: DatadogSpanLogger @BeforeEach fun `set up`() { mockSdkCore = mock { on { getFeature(Feature.LOGS_FEATURE_NAME) } doReturn mockLogFeatureScope + on { timeProvider } doReturn mockTimeProvider } + whenever(mockTimeProvider.getDeviceTimestamp()) doReturn 0L testedLogger = DatadogSpanLogger(mockSdkCore) } diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/SpanEventForgeryFactory.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/SpanEventForgeryFactory.kt index cbeb9fb782..3ee26f399f 100644 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/SpanEventForgeryFactory.kt +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/SpanEventForgeryFactory.kt @@ -11,6 +11,7 @@ import com.datadog.android.api.context.DeviceInfo import com.datadog.android.api.context.DeviceType import com.datadog.android.api.context.NetworkInfo import com.datadog.android.api.context.UserInfo +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.utils.toHexString import com.datadog.android.trace.model.SpanEvent import fr.xgouchet.elmyr.Forge @@ -31,7 +32,7 @@ internal class SpanEventForgeryFactory : ForgeryFactory { val spanId = forge.aLong(min = 1).toHexString() val parentId = forge.aLong(min = 1).toHexString() val duration = forge.aLong(min = 0) - val startTime = TimeUnit.SECONDS.toNanos(System.currentTimeMillis()) + val startTime = TimeUnit.SECONDS.toNanos(DefaultTimeProvider().getDeviceTimestamp()) val appPackageVersion = forge.aStringMatching("[0-9]\\.[0-9]\\.[0-9]") val tracerVersion = forge.aStringMatching("[0-9]\\.[0-9]\\.[0-9]") val userInfo = forge.aNullable() diff --git a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCache.kt b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCache.kt index a20df43f65..98dee96010 100644 --- a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCache.kt +++ b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCache.kt @@ -6,11 +6,14 @@ package com.datadog.android.webview.internal.rum.domain +import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.TimeProvider import java.util.LinkedList import java.util.concurrent.TimeUnit internal class WebViewNativeRumViewsCache( - private val entriesTtlLimitInMs: Long = DATA_PURGE_TTL_LIMIT_IN_MS + private val entriesTtlLimitInMs: Long = DATA_PURGE_TTL_LIMIT_IN_MS, + private val timeProvider: TimeProvider = DefaultTimeProvider() ) : NativeRumViewsCache { internal val parentViewsHistoryQueue: LinkedList = LinkedList() @@ -87,7 +90,7 @@ internal class WebViewNativeRumViewsCache( private fun purgeHistory() { var cursor = parentViewsHistoryQueue.peekLast() while (cursor != null) { - val timeSinceLastSnapshot = System.currentTimeMillis() - cursor.timestamp + val timeSinceLastSnapshot = timeProvider.getDeviceTimestamp() - cursor.timestamp if (timeSinceLastSnapshot > entriesTtlLimitInMs) { parentViewsHistoryQueue.remove(cursor) cursor = parentViewsHistoryQueue.peekLast() diff --git a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCacheTest.kt b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCacheTest.kt index 0375ddebf2..686e2b4716 100644 --- a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCacheTest.kt +++ b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCacheTest.kt @@ -6,6 +6,7 @@ package com.datadog.android.webview.internal.rum.domain +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.utils.forge.Configurator import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.junit5.ForgeConfiguration @@ -168,7 +169,7 @@ internal class WebViewNativeRumViewsCacheTest { val fakeOldEntries = forge.aList(size = forge.anInt(min = 1, max = 10)) { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeIdGenerator.generateId(), - WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to System.currentTimeMillis(), + WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to DefaultTimeProvider().getDeviceTimestamp(), WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY to forge.aBool() ) } @@ -184,7 +185,7 @@ internal class WebViewNativeRumViewsCacheTest { ) { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeIdGenerator.generateId(), - WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to System.currentTimeMillis(), + WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to DefaultTimeProvider().getDeviceTimestamp(), WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY to forge.aBool() ) } @@ -246,7 +247,7 @@ internal class WebViewNativeRumViewsCacheTest { val fakeEntries = forge.aList(size = forge.anInt(min = 1, max = 10)) { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeViewId, - WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to System.currentTimeMillis(), + WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to DefaultTimeProvider().getDeviceTimestamp(), WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY to forge.aBool() ) } @@ -315,7 +316,7 @@ internal class WebViewNativeRumViewsCacheTest { private class FakeClock { - val initialTimeMillis = AtomicLong(System.currentTimeMillis()) + val initialTimeMillis = AtomicLong(DefaultTimeProvider().getDeviceTimestamp()) fun nextCurrentTimeMillis(): Long { return initialTimeMillis.incrementAndGet() diff --git a/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/InternalSdkCoreTest.kt b/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/InternalSdkCoreTest.kt index 0bdaedc00b..f9f1933e9b 100644 --- a/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/InternalSdkCoreTest.kt +++ b/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/InternalSdkCoreTest.kt @@ -36,6 +36,7 @@ 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.internal.time.DefaultTimeProvider import com.datadog.android.privacy.TrackingConsent import com.datadog.android.trace.TracingHeaderType import com.datadog.tools.unit.forge.exhaustiveAttributes @@ -101,7 +102,7 @@ class InternalSdkCoreTest : MockServerTest() { @Before fun setUp() { - currentDeviceTimeInMs = System.currentTimeMillis() + currentDeviceTimeInMs = DefaultTimeProvider().getDeviceTimestamp() stubFeature = StubStorageBackedFeature( forge, fakeFeatureName, @@ -605,7 +606,8 @@ class InternalSdkCoreTest : MockServerTest() { // endregion companion object { - private val APPLICATION_START_TIME_UPPER_BOUND_NS = System.nanoTime() + private val APPLICATION_START_TIME_UPPER_BOUND_NS = + DefaultTimeProvider().getDeviceElapsedTimeNs() private val DEVICE_TIME_OFFSET_NS = TimeUnit.SECONDS.toNanos(6) private const val ANDROID_SOURCE = "android" private const val BUILD_ID = "core_it_build_id" diff --git a/reliability/single-fit/rum/src/test/kotlin/com/datadog/android/rum/integration/ManualTrackingRumTest.kt b/reliability/single-fit/rum/src/test/kotlin/com/datadog/android/rum/integration/ManualTrackingRumTest.kt index 1ffd882303..850d54f7ea 100644 --- a/reliability/single-fit/rum/src/test/kotlin/com/datadog/android/rum/integration/ManualTrackingRumTest.kt +++ b/reliability/single-fit/rum/src/test/kotlin/com/datadog/android/rum/integration/ManualTrackingRumTest.kt @@ -8,6 +8,7 @@ package com.datadog.android.rum.integration import com.datadog.android.api.feature.Feature import com.datadog.android.core.stub.StubSDKCore +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.ExperimentalRumApi import com.datadog.android.rum.GlobalRumMonitor import com.datadog.android.rum.Rum @@ -485,11 +486,11 @@ class ManualTrackingRumTest { ) { // Given val rumMonitor = GlobalRumMonitor.get(stubSdkCore) - val startTime = System.nanoTime() + val startTime = DefaultTimeProvider().getDeviceElapsedTimeNs() rumMonitor.startView(key, name) // When - val endTime = System.nanoTime() + val endTime = DefaultTimeProvider().getDeviceElapsedTimeNs() val expectedViewLoadingTime = endTime - startTime rumMonitor.addViewLoadingTime(overwrite) @@ -578,14 +579,14 @@ class ManualTrackingRumTest { ) { // Given val rumMonitor = GlobalRumMonitor.get(stubSdkCore) - val startTime = System.nanoTime() + val startTime = DefaultTimeProvider().getDeviceElapsedTimeNs() rumMonitor.startView(key, name) - val intermediateTime = System.nanoTime() + val intermediateTime = DefaultTimeProvider().getDeviceElapsedTimeNs() rumMonitor.addViewLoadingTime(overwrite) // When Thread.sleep(100) - val endTime = System.nanoTime() + val endTime = DefaultTimeProvider().getDeviceElapsedTimeNs() rumMonitor.addViewLoadingTime(true) // Then @@ -645,9 +646,9 @@ class ManualTrackingRumTest { ) { // Given val rumMonitor = GlobalRumMonitor.get(stubSdkCore) - val startTime = System.nanoTime() + val startTime = DefaultTimeProvider().getDeviceElapsedTimeNs() rumMonitor.startView(key, name) - val intermediateTime = System.nanoTime() + val intermediateTime = DefaultTimeProvider().getDeviceElapsedTimeNs() rumMonitor.addViewLoadingTime(overwrite) // When diff --git a/reliability/single-fit/rum/src/test/kotlin/com/datadog/android/rum/integration/ViewLoadingTimeMetricsTests.kt b/reliability/single-fit/rum/src/test/kotlin/com/datadog/android/rum/integration/ViewLoadingTimeMetricsTests.kt index 35f8fb591c..fc05369789 100644 --- a/reliability/single-fit/rum/src/test/kotlin/com/datadog/android/rum/integration/ViewLoadingTimeMetricsTests.kt +++ b/reliability/single-fit/rum/src/test/kotlin/com/datadog/android/rum/integration/ViewLoadingTimeMetricsTests.kt @@ -8,6 +8,7 @@ package com.datadog.android.rum.integration import com.datadog.android.api.feature.Feature import com.datadog.android.core.stub.StubSDKCore +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.GlobalRumMonitor import com.datadog.android.rum.Rum import com.datadog.android.rum.RumActionType @@ -111,11 +112,11 @@ class ViewLoadingTimeMetricsTests { val monitor = GlobalRumMonitor.get(stubSdkCore) // When - val startViewTime = System.nanoTime() + val startViewTime = DefaultTimeProvider().getDeviceElapsedTimeNs() monitor.startView(viewKey, viewName) monitor.startResource(resourceKey, rumResourceMethod, resourceUrl) Thread.sleep(100) - val stopResourceTime = System.nanoTime() + val stopResourceTime = DefaultTimeProvider().getDeviceElapsedTimeNs() monitor.stopResource(resourceKey, resourceStatus, resourceSize, rumResourceKind) monitor.stopView(viewKey) val appExpectedTtnsTime = (stopResourceTime - startViewTime) @@ -184,11 +185,11 @@ class ViewLoadingTimeMetricsTests { val monitor = GlobalRumMonitor.get(stubSdkCore) // When - val startViewTime = System.nanoTime() + val startViewTime = DefaultTimeProvider().getDeviceElapsedTimeNs() monitor.startView(viewKey, viewName) monitor.startResource(resourceKey, rumResourceMethod, resourceUrl) Thread.sleep(100) - val stopResourceTime = System.nanoTime() + val stopResourceTime = DefaultTimeProvider().getDeviceElapsedTimeNs() monitor.stopResource(resourceKey, resourceStatus, resourceSize, rumResourceKind) monitor.addTiming(forge.anAlphabeticalString()) Thread.sleep(100) @@ -286,11 +287,11 @@ class ViewLoadingTimeMetricsTests { val monitor = GlobalRumMonitor.get(stubSdkCore) // When - val startViewTime = System.nanoTime() + val startViewTime = DefaultTimeProvider().getDeviceElapsedTimeNs() monitor.startView(viewKey, viewName) monitor.startResource(resourceKey, rumResourceMethod, resourceUrl) Thread.sleep(100) - val stopResourceTime = System.nanoTime() + val stopResourceTime = DefaultTimeProvider().getDeviceElapsedTimeNs() monitor.stopResourceWithError(resourceKey, resourceStatus, errorMessage, errorSource, throwable) monitor.stopView(viewKey) val appExpectedTtnsTime = (stopResourceTime - startViewTime) @@ -1390,10 +1391,10 @@ class ViewLoadingTimeMetricsTests { monitor.startView(previousViewKey, previousViewName) monitor.startAction(rumActionType, lastInteractionName) Thread.sleep(100) - val stopActionTime = System.nanoTime() + val stopActionTime = DefaultTimeProvider().getDeviceElapsedTimeNs() monitor.stopAction(rumActionType, lastInteractionName) monitor.stopView(previousViewKey) - val startViewTime = System.nanoTime() + val startViewTime = DefaultTimeProvider().getDeviceElapsedTimeNs() monitor.startView(viewKey, viewName) Thread.sleep(100) monitor.stopView(viewKey) diff --git a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTracerProviderTest.kt b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTracerProviderTest.kt index ddea228f7d..b7bd3c3056 100644 --- a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTracerProviderTest.kt +++ b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTracerProviderTest.kt @@ -9,6 +9,7 @@ package com.datadog.android.trace.integration.otel import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.SdkFeatureMock import com.datadog.android.core.stub.StubSDKCore +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.tests.ktx.getInt import com.datadog.android.trace.Trace import com.datadog.android.trace.TraceConfiguration @@ -448,7 +449,7 @@ internal class OtelTracerProviderTest { forge: Forge ) { // Given - val fakeStartTimestamp = System.currentTimeMillis() - forge.aPositiveLong() + val fakeStartTimestamp = DefaultTimeProvider().getDeviceTimestamp() - forge.aPositiveLong() val testedProvider = OtelTracerProvider.Builder(stubSdkCore).build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() @@ -868,9 +869,11 @@ internal class OtelTracerProviderTest { val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() // When - val startNanos = System.nanoTime() + val startNanos = DefaultTimeProvider().getDeviceElapsedTimeNs() var spansCounter = 0 - while ((System.nanoTime() - startNanos) < ONE_SECOND_AS_NANOS && (spansCounter < 200)) { + while ( + (DefaultTimeProvider().getDeviceElapsedTimeNs() - startNanos) < ONE_SECOND_AS_NANOS && (spansCounter < 200) + ) { tracer.spanBuilder(forge.anAlphabeticalString()).startSpan().end() spansCounter++ } @@ -907,9 +910,11 @@ internal class OtelTracerProviderTest { val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() // When - val startNanos = System.nanoTime() + val startNanos = DefaultTimeProvider().getDeviceElapsedTimeNs() var spansCounter = 0 - while ((System.nanoTime() - startNanos) < ONE_SECOND_AS_NANOS && (spansCounter < 200)) { + while ( + (DefaultTimeProvider().getDeviceElapsedTimeNs() - startNanos) < ONE_SECOND_AS_NANOS && (spansCounter < 200) + ) { tracer.spanBuilder(forge.anAlphabeticalString()).startSpan().end() spansCounter++ } @@ -943,9 +948,12 @@ internal class OtelTracerProviderTest { val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() // When - val startNanos = System.nanoTime() + val startNanos = DefaultTimeProvider().getDeviceElapsedTimeNs() var spansCounter = 0 - while (((System.nanoTime() - startNanos) < (ONE_SECOND_AS_NANOS * 2)) && (spansCounter < 200)) { + while ( + (DefaultTimeProvider().getDeviceElapsedTimeNs() - startNanos < (ONE_SECOND_AS_NANOS * 2)) && + (spansCounter < 200) + ) { tracer.spanBuilder(forge.anAlphabeticalString()).startSpan().end() spansCounter++ } diff --git a/reliability/stub-core/src/main/kotlin/com/datadog/android/core/stub/StubScheduledExecutorService.kt b/reliability/stub-core/src/main/kotlin/com/datadog/android/core/stub/StubScheduledExecutorService.kt index e060ffcb69..4030db2ea8 100644 --- a/reliability/stub-core/src/main/kotlin/com/datadog/android/core/stub/StubScheduledExecutorService.kt +++ b/reliability/stub-core/src/main/kotlin/com/datadog/android/core/stub/StubScheduledExecutorService.kt @@ -6,6 +6,7 @@ package com.datadog.android.core.stub +import com.datadog.android.internal.time.DefaultTimeProvider import java.util.concurrent.Callable import java.util.concurrent.Delayed import java.util.concurrent.Future @@ -24,6 +25,7 @@ class StubScheduledExecutorService(executorContext: String) : ScheduledExecutorS private var isShutdown = false private var isTerminated = false + private val timeProvider = DefaultTimeProvider() init { println("Stubbing a ScheduledExecutorService with context: $executorContext") @@ -97,10 +99,11 @@ class StubScheduledExecutorService(executorContext: String) : ScheduledExecutorS override fun schedule(command: Runnable?, delay: Long, unit: TimeUnit?): ScheduledFuture<*> { val futureTask = FutureTask(command, null) val delayMs = unit!!.toMillis(delay) - val triggerTimestamp = System.currentTimeMillis() + delayMs + val triggerTimestamp = timeProvider.getDeviceTimestamp() + delayMs val scheduledFutureTask = ScheduledFutureTask( - futureTask, - triggerTimestamp + delegate = futureTask, + triggerTimestamp = triggerTimestamp, + nowProvider = timeProvider::getDeviceTimestamp ) Thread { Thread.sleep(delayMs) @@ -112,10 +115,11 @@ class StubScheduledExecutorService(executorContext: String) : ScheduledExecutorS override fun schedule(callable: Callable?, delay: Long, unit: TimeUnit?): ScheduledFuture { val futureTask = FutureTask(callable) val delayMs = unit!!.toMillis(delay) - val triggerTimestamp = System.currentTimeMillis() + delayMs + val triggerTimestamp = timeProvider.getDeviceTimestamp() + delayMs val scheduledFutureTask = ScheduledFutureTask( - futureTask, - triggerTimestamp + delegate = futureTask, + triggerTimestamp = triggerTimestamp, + nowProvider = timeProvider::getDeviceTimestamp ) Thread { Thread.sleep(delayMs) @@ -150,7 +154,8 @@ class StubScheduledExecutorService(executorContext: String) : ScheduledExecutorS */ class ScheduledFutureTask( val delegate: FutureTask, - val triggerTimestamp: Long + val triggerTimestamp: Long, + private val nowProvider: () -> Long ) : RunnableFuture by delegate, ScheduledFuture { override fun compareTo(other: Delayed?): Int { val delay = getDelay(TimeUnit.MILLISECONDS) @@ -159,7 +164,7 @@ class StubScheduledExecutorService(executorContext: String) : ScheduledExecutorS } override fun getDelay(unit: TimeUnit?): Long { - val delayMs = triggerTimestamp - System.currentTimeMillis() + val delayMs = triggerTimestamp - nowProvider() return unit!!.convert(delayMs, TimeUnit.MILLISECONDS) } } diff --git a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/room/RoomDataSource.kt b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/room/RoomDataSource.kt index ef94408885..c2f7b14c54 100644 --- a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/room/RoomDataSource.kt +++ b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/room/RoomDataSource.kt @@ -7,6 +7,7 @@ package com.datadog.android.sample.data.db.room import android.content.Context +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.sample.data.db.DataSource import com.datadog.android.sample.data.model.Log import com.datadog.android.sample.data.model.LogAttributes @@ -19,6 +20,7 @@ import java.util.concurrent.TimeUnit internal class RoomDataSource(val context: Context) : DataSource { private val logDao = LogsDatabase.getInstance(context).logDao() + private val timeProvider = DefaultTimeProvider() // region LocalDataSource @@ -37,7 +39,7 @@ internal class RoomDataSource(val context: Context) : DataSource { // region Internal private fun insertLogs(logs: List) { - val currentTimeInMillis = System.currentTimeMillis() + val currentTimeInMillis = timeProvider.getDeviceTimestamp() val minTtlRequired = currentTimeInMillis - LOGS_EXPIRING_TTL_IN_MS // purge data first logDao.purge(minTtlRequired) @@ -56,7 +58,7 @@ internal class RoomDataSource(val context: Context) : DataSource { private val fetchLogsCallable = Callable> { val minTtlRequired = - System.currentTimeMillis() - LOGS_EXPIRING_TTL_IN_MS + timeProvider.getDeviceTimestamp() - LOGS_EXPIRING_TTL_IN_MS logDao.getAll(minTtlRequired).map { Log( id = it.uid, diff --git a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/sqldelight/SqlDelightDataSource.kt b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/sqldelight/SqlDelightDataSource.kt index e7fccd8f6b..2621394a33 100644 --- a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/sqldelight/SqlDelightDataSource.kt +++ b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/sqldelight/SqlDelightDataSource.kt @@ -7,6 +7,7 @@ package com.datadog.android.sample.data.db.sqldelight import android.content.Context +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.sample.data.db.DataSource import com.datadog.android.sample.data.model.Log import com.datadog.android.sample.data.model.LogAttributes @@ -21,6 +22,7 @@ import java.util.concurrent.TimeUnit internal class SqlDelightDataSource(val context: Context) : DataSource { private val logsDatabase = Database.getInstance(context) + private val timeProvider = DefaultTimeProvider() // region LocalDataSource @@ -39,7 +41,7 @@ internal class SqlDelightDataSource(val context: Context) : DataSource { // region Internal private fun insertLogs(logs: List) { - val currentTimeInMillis = System.currentTimeMillis() + val currentTimeInMillis = timeProvider.getDeviceTimestamp() val minTtlRequired = currentTimeInMillis - LOGS_EXPIRING_TTL_IN_MS // purge data first logsDatabase.logsQueries.purgeLogs(minTtlRequired) @@ -62,7 +64,7 @@ internal class SqlDelightDataSource(val context: Context) : DataSource { private val fetchLogsCallable = Callable> { val minTtlRequired = - System.currentTimeMillis() - LOGS_EXPIRING_TTL_IN_MS + timeProvider.getDeviceTimestamp() - LOGS_EXPIRING_TTL_IN_MS logsDatabase.logsQueries.getLogs(minTtlRequired).executeAsList().map { Log( it._id, diff --git a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/sqlite/SQLiteDataSource.kt b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/sqlite/SQLiteDataSource.kt index 17ee16a9f8..e90e008c51 100644 --- a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/sqlite/SQLiteDataSource.kt +++ b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/sqlite/SQLiteDataSource.kt @@ -21,6 +21,7 @@ import java.util.concurrent.Callable import java.util.concurrent.TimeUnit internal class SQLiteDataSource(val context: Context) : DataSource { + private val timeProvider = DefaultTimeProvider() // region LocalDataSource @@ -39,7 +40,7 @@ internal class SQLiteDataSource(val context: Context) : DataSource { // region Internal private fun insertLogs(logs: List) { - val currentTimeInMillis = System.currentTimeMillis() + val currentTimeInMillis = timeProvider.getDeviceTimestamp() val minTtlRequired = currentTimeInMillis - LOGS_EXPIRING_TTL_IN_MS // purge data first purgeData(minTtlRequired) @@ -74,7 +75,7 @@ internal class SQLiteDataSource(val context: Context) : DataSource { ) val whereClause = "${DatadogDbContract.Logs.COLUMN_NAME_TTL} >= ?" val minTtlRequired = - System.currentTimeMillis() - LOGS_EXPIRING_TTL_IN_MS + timeProvider.getDeviceTimestamp() - LOGS_EXPIRING_TTL_IN_MS val whereClauseArg = arrayOf(minTtlRequired.toString()) val cursor = context.contentResolver.query( diff --git a/tools/benchmark/src/main/java/com/datadog/benchmark/internal/reader/FpsVitalReader.kt b/tools/benchmark/src/main/java/com/datadog/benchmark/internal/reader/FpsVitalReader.kt index 1c5d59d388..e519fc643f 100644 --- a/tools/benchmark/src/main/java/com/datadog/benchmark/internal/reader/FpsVitalReader.kt +++ b/tools/benchmark/src/main/java/com/datadog/benchmark/internal/reader/FpsVitalReader.kt @@ -7,6 +7,8 @@ package com.datadog.benchmark.internal.reader import android.view.Choreographer +import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.TimeProvider import java.util.concurrent.TimeUnit internal class FpsVitalReader : VitalReader { @@ -15,15 +17,16 @@ internal class FpsVitalReader : VitalReader { private var frameCount = 0 private var lastFrameTime: Long = 0 private val intervalMs = FPS_SAMPLE_INTERVAL_IN_MS + private val timeProvider: TimeProvider = DefaultTimeProvider() private val frameCallback = object : Choreographer.FrameCallback { override fun doFrame(frameTimeNanos: Long) { if (lastFrameTime == 0L) { - lastFrameTime = System.nanoTime() + lastFrameTime = timeProvider.getDeviceElapsedTimeNs() } frameCount++ - val currentFrameTime = System.nanoTime() + val currentFrameTime = timeProvider.getDeviceElapsedTimeNs() val elapsedTime: Long = currentFrameTime - lastFrameTime if (elapsedTime >= TimeUnit.MILLISECONDS.toNanos(intervalMs)) { diff --git a/tools/benchmark/src/test/java/forge/SpanEventForgeryFactory.kt b/tools/benchmark/src/test/java/forge/SpanEventForgeryFactory.kt index e533e81d6d..6dbf314235 100644 --- a/tools/benchmark/src/test/java/forge/SpanEventForgeryFactory.kt +++ b/tools/benchmark/src/test/java/forge/SpanEventForgeryFactory.kt @@ -8,6 +8,7 @@ package forge import com.datadog.android.api.context.NetworkInfo import com.datadog.android.api.context.UserInfo +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.utils.toHexString import com.datadog.benchmark.internal.model.SpanEvent import fr.xgouchet.elmyr.Forge @@ -28,7 +29,7 @@ internal class SpanEventForgeryFactory : ForgeryFactory { val spanId = forge.aLong(min = 1).toHexString() val parentId = forge.aLong(min = 1).toHexString() val duration = forge.aLong(min = 0) - val startTime = TimeUnit.SECONDS.toNanos(System.currentTimeMillis()) + val startTime = TimeUnit.SECONDS.toNanos(DefaultTimeProvider().getDeviceTimestamp()) val appPackageVersion = forge.aStringMatching("[0-9]\\.[0-9]\\.[0-9]") val tracerVersion = forge.aStringMatching("[0-9]\\.[0-9]\\.[0-9]") val userInfo: UserInfo? = forge.aNullable() From 2475ff6b38831ef1ae45ab1c176d201958f2aa5f Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Tue, 18 Nov 2025 16:40:48 +0000 Subject: [PATCH 02/29] RUM-10363: Begin simplification --- .../main/kotlin/com/datadog/android/api/SdkCore.kt | 6 ------ .../com/datadog/android/core/internal/DatadogCore.kt | 4 ---- .../android/core/internal/NoOpInternalSdkCore.kt | 3 --- .../internal/time/DefaultAppStartTimeProvider.kt | 4 ++-- .../internal/time/DefaultAppStartTimeProviderTest.kt | 12 +++++++----- .../accessibility/DefaultAccessibilityReader.kt | 3 +-- .../rum/domain/WebViewNativeRumViewsCache.kt | 3 +-- 7 files changed, 11 insertions(+), 24 deletions(-) diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/api/SdkCore.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/api/SdkCore.kt index f75c1a6001..5ad1b6aebc 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/api/SdkCore.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/api/SdkCore.kt @@ -9,7 +9,6 @@ package com.datadog.android.api import androidx.annotation.AnyThread import com.datadog.android.api.context.TimeInfo import com.datadog.android.api.context.UserInfo -import com.datadog.android.internal.time.TimeProvider import com.datadog.android.privacy.TrackingConsent /** @@ -28,11 +27,6 @@ interface SdkCore { */ val time: TimeInfo - /** - * The [TimeProvider] used by this core instance for current timestamps. - */ - val timeProvider: TimeProvider - /** * Name of the service (given during the SDK initialization, otherwise package name is used). */ diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogCore.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogCore.kt index b230de29c6..a6bc76c13f 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogCore.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogCore.kt @@ -41,7 +41,6 @@ import com.datadog.android.core.internal.utils.submitSafe import com.datadog.android.core.thread.FlushableExecutorService import com.datadog.android.error.internal.CrashReportsFeature import com.datadog.android.internal.telemetry.InternalTelemetryEvent -import com.datadog.android.internal.time.TimeProvider import com.datadog.android.privacy.TrackingConsent import com.google.gson.JsonObject import okhttp3.Call @@ -118,9 +117,6 @@ internal class DatadogCore( override val service: String get() = coreFeature.serviceName - override val timeProvider: TimeProvider - get() = coreFeature.timeProvider - /** @inheritDoc */ override val firstPartyHostResolver: FirstPartyHostHeaderTypeResolver get() = coreFeature.firstPartyHostHeaderTypeResolver diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpInternalSdkCore.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpInternalSdkCore.kt index 977be7548d..776bde9a01 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpInternalSdkCore.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpInternalSdkCore.kt @@ -58,9 +58,6 @@ internal object NoOpInternalSdkCore : InternalSdkCore { ) } - override val timeProvider: TimeProvider - get() = internalTimeProvider - override val service: String get() = "" diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProvider.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProvider.kt index d9ca77cb6b..ad2084bd4b 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProvider.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProvider.kt @@ -17,8 +17,8 @@ import com.datadog.android.rum.DdRumContentProvider import java.util.concurrent.TimeUnit internal class DefaultAppStartTimeProvider( - buildSdkVersionProvider: BuildSdkVersionProvider = BuildSdkVersionProvider.DEFAULT, - private val timeProvider: TimeProvider = DefaultTimeProvider() + timeProvider: TimeProvider = DefaultTimeProvider(), + buildSdkVersionProvider: BuildSdkVersionProvider = BuildSdkVersionProvider.DEFAULT ) : AppStartTimeProvider { override val appStartTimeNs: Long by lazy(LazyThreadSafetyMode.PUBLICATION) { diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt index 0704e2966e..1a87c15919 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt @@ -35,12 +35,13 @@ class DefaultAppStartTimeProviderTest { // GIVEN val mockBuildSdkVersionProvider: BuildSdkVersionProvider = mock() whenever(mockBuildSdkVersionProvider.version) doReturn apiVersion + val timeProvider = DefaultTimeProvider() val diffMs = SystemClock.elapsedRealtime() - Process.getStartElapsedRealtime() - val startTimeNs = DefaultTimeProvider().getDeviceElapsedTimeNs() - TimeUnit.MILLISECONDS.toNanos(diffMs) + val startTimeNs = timeProvider.getDeviceElapsedTimeNs() - TimeUnit.MILLISECONDS.toNanos(diffMs) // WHEN - val timeProvider = DefaultAppStartTimeProvider(mockBuildSdkVersionProvider) - val providedStartTime = timeProvider.appStartTimeNs + val defaultAppStartTimeProvider = DefaultAppStartTimeProvider(timeProvider, mockBuildSdkVersionProvider) + val providedStartTime = defaultAppStartTimeProvider.appStartTimeNs // THEN assertThat(providedStartTime) @@ -57,8 +58,9 @@ class DefaultAppStartTimeProviderTest { val startTimeNs = DdRumContentProvider.createTimeNs // WHEN - val timeProvider = DefaultAppStartTimeProvider(mockBuildSdkVersionProvider) - val providedStartTime = timeProvider.appStartTimeNs + val timeProvider = DefaultTimeProvider() + val defaultAppStartTimeProvider = DefaultAppStartTimeProvider(timeProvider, mockBuildSdkVersionProvider) + val providedStartTime = defaultAppStartTimeProvider.appStartTimeNs // THEN assertThat(providedStartTime).isEqualTo(startTimeNs) diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReader.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReader.kt index f334c0b63f..cd9f3bd353 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReader.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReader.kt @@ -21,7 +21,6 @@ import android.view.View import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener import com.datadog.android.api.InternalLogger -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.internal.domain.InfoProvider import java.util.concurrent.atomic.AtomicLong @@ -38,7 +37,7 @@ internal class DefaultAccessibilityReader( private val secureWrapper: SecureWrapper = SecureWrapper(), private val globalWrapper: GlobalWrapper = GlobalWrapper(), private val handler: Handler = Handler(Looper.getMainLooper()), - private val timeProvider: TimeProvider = DefaultTimeProvider() + private val timeProvider: TimeProvider ) : InfoProvider, ComponentCallbacks { private val displayInversionListener = object : ContentObserver(handler) { diff --git a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCache.kt b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCache.kt index 98dee96010..b16f19ee42 100644 --- a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCache.kt +++ b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCache.kt @@ -6,14 +6,13 @@ package com.datadog.android.webview.internal.rum.domain -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import java.util.LinkedList import java.util.concurrent.TimeUnit internal class WebViewNativeRumViewsCache( private val entriesTtlLimitInMs: Long = DATA_PURGE_TTL_LIMIT_IN_MS, - private val timeProvider: TimeProvider = DefaultTimeProvider() + private val timeProvider: TimeProvider ) : NativeRumViewsCache { internal val parentViewsHistoryQueue: LinkedList = LinkedList() From bc0d9090f2e49596a58bcf8938c25a1862bac06a Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Tue, 18 Nov 2025 19:53:16 +0000 Subject: [PATCH 03/29] RUM-10363: Local CI tests pass --- dd-sdk-android-core/api/apiSurface | 5 + .../api/dd-sdk-android-core.api | 2 +- .../android/api/feature/FeatureSdkCore.kt | 6 + .../android/core/internal/CoreFeature.kt | 14 +- .../android/core/internal/DatadogCore.kt | 8 +- .../core/internal/NoOpInternalSdkCore.kt | 3 + .../metrics/BatchMetricsDispatcher.kt | 4 +- .../time/DefaultAppStartTimeProvider.kt | 3 +- .../core/internal/time/KronosTimeProvider.kt | 3 +- .../datadog/android/core/DatadogCoreTest.kt | 3 +- .../metrics/MethodCalledTelemetryTest.kt | 7 +- .../file/batch/BatchFileOrchestratorTest.kt | 124 +++++++++--------- .../time/DefaultAppStartTimeProviderTest.kt | 5 +- .../internal/time/KronosTimeProviderTest.kt | 10 +- .../android/internal/time/TimeProvider.kt | 5 +- .../internal/ExposureEventsProcessorTest.kt | 9 +- .../persistence/FlagsStateDeserializerTest.kt | 12 +- .../persistence/FlagsStateSerializerTest.kt | 4 +- .../android/log/internal/LogsFeatureTest.kt | 4 +- .../domain/DatadogLogGeneratorTest.kt | 4 +- .../internal/DatadogLateCrashReporterTest.kt | 28 ++-- .../android/rum/internal/domain/TimeTest.kt | 19 +-- .../DefaultAccessibilityReaderTest.kt | 25 +++- ...pplicationScopeAttributePropagationTest.kt | 6 +- .../RumViewScopeAttributePropagationTest.kt | 6 +- .../ActionTypeInteractionValidatorTest.kt | 14 +- ...InteractionToNextViewMetricResolverTest.kt | 9 +- .../TimeBasedInteractionIdentifierTest.kt | 7 +- .../NetworkSettledMetricResolverTest.kt | 23 ++-- .../internal/DefaultRecorderProvider.kt | 3 +- .../internal/SessionReplayFeatureTest.kt | 5 + .../async/RecordedDataQueueHandlerTest.kt | 15 ++- .../resources/ResourceDataStoreManagerTest.kt | 7 +- .../trace/api/IdGenerationStrategyTest.kt | 6 +- .../datadog/trace/core/CoreSpanBuilderTest.kt | 6 +- .../com/datadog/trace/core/DDSpanTest.kt | 47 +++---- .../webview/internal/rum/WebViewRumFeature.kt | 2 +- .../rum/domain/WebViewNativeRumViewsCache.kt | 4 +- .../android/webview/WebViewTrackingTest.kt | 5 + .../domain/WebViewNativeRumViewsCacheTest.kt | 12 +- .../otel/OtelTracerProviderTest.kt | 15 ++- .../sample/data/db/room/RoomDataSource.kt | 6 +- .../db/sqldelight/SqlDelightDataSource.kt | 6 +- .../sample/data/db/sqlite/SQLiteDataSource.kt | 5 +- .../internal/reader/FpsVitalReader.kt | 5 +- 45 files changed, 298 insertions(+), 223 deletions(-) diff --git a/dd-sdk-android-core/api/apiSurface b/dd-sdk-android-core/api/apiSurface index 3754148b4d..4fc3fc781a 100644 --- a/dd-sdk-android-core/api/apiSurface +++ b/dd-sdk-android-core/api/apiSurface @@ -131,6 +131,7 @@ fun com.datadog.android.api.InternalLogger.measureMethodCallPerf(Class fun FeatureScope.getContextFuture(Set = emptySet()): java.util.concurrent.Future? interface com.datadog.android.api.feature.FeatureSdkCore : com.datadog.android.api.SdkCore val internalLogger: com.datadog.android.api.InternalLogger + val timeProvider: com.datadog.android.internal.time.TimeProvider fun registerFeature(Feature) fun getFeature(String): FeatureScope? fun updateFeatureContext(String, Boolean = true, (MutableMap) -> Unit) @@ -364,6 +365,10 @@ interface com.datadog.android.core.thread.FlushableExecutorService : java.util.c fun drainTo(MutableCollection) interface Factory fun create(com.datadog.android.api.InternalLogger, String, com.datadog.android.core.configuration.BackPressureStrategy): FlushableExecutorService +object com.datadog.android.core.time.SdkTimeProvider + var provider: com.datadog.android.internal.time.TimeProvider + fun now(): Long + fun elapsed(): Long interface com.datadog.android.event.EventMapper fun map(T): T? class com.datadog.android.event.MapperSerializer : com.datadog.android.core.persistence.Serializer diff --git a/dd-sdk-android-core/api/dd-sdk-android-core.api b/dd-sdk-android-core/api/dd-sdk-android-core.api index 93ea2a208c..8f9132f537 100644 --- a/dd-sdk-android-core/api/dd-sdk-android-core.api +++ b/dd-sdk-android-core/api/dd-sdk-android-core.api @@ -135,7 +135,6 @@ public abstract interface class com/datadog/android/api/SdkCore { public abstract fun getName ()Ljava/lang/String; public abstract fun getService ()Ljava/lang/String; public abstract fun getTime ()Lcom/datadog/android/api/context/TimeInfo; - public abstract fun getTimeProvider ()Lcom/datadog/android/internal/time/TimeProvider; public abstract fun isCoreActive ()Z public abstract fun setAccountInfo (Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)V public abstract fun setTrackingConsent (Lcom/datadog/android/privacy/TrackingConsent;)V @@ -421,6 +420,7 @@ public abstract interface class com/datadog/android/api/feature/FeatureSdkCore : public abstract fun getFeature (Ljava/lang/String;)Lcom/datadog/android/api/feature/FeatureScope; public abstract fun getFeatureContext (Ljava/lang/String;Z)Ljava/util/Map; public abstract fun getInternalLogger ()Lcom/datadog/android/api/InternalLogger; + public abstract fun getTimeProvider ()Lcom/datadog/android/internal/time/TimeProvider; public abstract fun registerFeature (Lcom/datadog/android/api/feature/Feature;)V public abstract fun removeContextUpdateReceiver (Lcom/datadog/android/api/feature/FeatureContextUpdateReceiver;)V public abstract fun removeEventReceiver (Ljava/lang/String;)V diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/api/feature/FeatureSdkCore.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/api/feature/FeatureSdkCore.kt index aee88240c5..e8e47b07f3 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/api/feature/FeatureSdkCore.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/api/feature/FeatureSdkCore.kt @@ -8,6 +8,7 @@ package com.datadog.android.api.feature import com.datadog.android.api.InternalLogger import com.datadog.android.api.SdkCore +import com.datadog.android.internal.time.TimeProvider import okhttp3.Call import okhttp3.OkHttpClient import java.util.UUID @@ -27,6 +28,11 @@ interface FeatureSdkCore : SdkCore { */ val internalLogger: InternalLogger + /** + * The [TimeProvider] used by this core instance for current timestamps. + */ + val timeProvider: TimeProvider + /** * Registers a feature to this instance of the Datadog SDK. * diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt index 8911ffcccd..9236e0ccdb 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt @@ -72,7 +72,6 @@ import com.datadog.android.core.internal.utils.executeSafe import com.datadog.android.core.internal.utils.unboundInternalLogger import com.datadog.android.core.persistence.PersistenceStrategy import com.datadog.android.core.thread.FlushableExecutorService -import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.internal.utils.allowThreadDiskReads @@ -156,14 +155,6 @@ internal class CoreFeature( internal var networkInfoProvider: NetworkInfoProvider = NoOpNetworkInfoProvider() internal var systemInfoProvider: SystemInfoProvider = NoOpSystemInfoProvider() internal var timeProvider: TimeProvider = DefaultTimeProvider() - set(value) { - field = value - SdkTimeProvider.provider = value - } - - init { - SdkTimeProvider.provider = timeProvider - } internal var trackingConsentProvider: ConsentProvider = NoOpConsentProvider() internal var userInfoProvider: MutableUserInfoProvider = NoOpMutableUserInfoProvider() internal var accountInfoProvider: MutableAccountInfoProvider = NoOpMutableAccountInfoProvider() @@ -495,9 +486,10 @@ internal class CoreFeature( ) } } - - timeProvider = KronosTimeProvider(this) } + + // Switch to KronosTimeProvider for NTP-synced time + timeProvider = KronosTimeProvider(kronosClock!!, DefaultTimeProvider()) } @RequiresApi(Build.VERSION_CODES.N) diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogCore.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogCore.kt index a6bc76c13f..f1ac075ef8 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogCore.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogCore.kt @@ -41,6 +41,8 @@ import com.datadog.android.core.internal.utils.submitSafe import com.datadog.android.core.thread.FlushableExecutorService import com.datadog.android.error.internal.CrashReportsFeature import com.datadog.android.internal.telemetry.InternalTelemetryEvent +import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.privacy.TrackingConsent import com.google.gson.JsonObject import okhttp3.Call @@ -117,6 +119,10 @@ internal class DatadogCore( override val service: String get() = coreFeature.serviceName + /** @inheritDoc */ + override val timeProvider: TimeProvider + get() = coreFeature.timeProvider + /** @inheritDoc */ override val firstPartyHostResolver: FirstPartyHostHeaderTypeResolver get() = coreFeature.firstPartyHostHeaderTypeResolver @@ -458,7 +464,7 @@ internal class DatadogCore( executorServiceFactory ?: CoreFeature.DEFAULT_FLUSHABLE_EXECUTOR_SERVICE_FACTORY coreFeature = CoreFeature( internalLogger, - DefaultAppStartTimeProvider(), + DefaultAppStartTimeProvider(DefaultTimeProvider()), flushableExecutorServiceFactory, CoreFeature.DEFAULT_SCHEDULED_EXECUTOR_SERVICE_FACTORY ) diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpInternalSdkCore.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpInternalSdkCore.kt index 776bde9a01..977be7548d 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpInternalSdkCore.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpInternalSdkCore.kt @@ -58,6 +58,9 @@ internal object NoOpInternalSdkCore : InternalSdkCore { ) } + override val timeProvider: TimeProvider + get() = internalTimeProvider + override val service: String get() = "" diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/metrics/BatchMetricsDispatcher.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/metrics/BatchMetricsDispatcher.kt index 8b739bae42..925f1a483a 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/metrics/BatchMetricsDispatcher.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/metrics/BatchMetricsDispatcher.kt @@ -25,7 +25,7 @@ internal class BatchMetricsDispatcher( private val uploadConfiguration: DataUploadConfiguration?, private val filePersistenceConfig: FilePersistenceConfig, private val internalLogger: InternalLogger, - private val dateTimeProvider: TimeProvider + private val timeProvider: TimeProvider ) : MetricsDispatcher, ProcessLifecycleMonitor.Callback { @@ -90,7 +90,7 @@ internal class BatchMetricsDispatcher( numPendingBatches: Int ): Map? { val fileCreationTimestamp = file.nameAsTimestampSafe(internalLogger) ?: return null - val fileAgeInMillis = dateTimeProvider.getDeviceTimestamp() - fileCreationTimestamp + val fileAgeInMillis = timeProvider.getDeviceTimestamp() - fileCreationTimestamp if (fileAgeInMillis < 0) { // the device time was manually modified or the time zone changed // we are dropping this metric to not skew our charts diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProvider.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProvider.kt index ad2084bd4b..77654968c8 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProvider.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProvider.kt @@ -11,13 +11,12 @@ import android.os.Build import android.os.Process import android.os.SystemClock import com.datadog.android.core.internal.system.BuildSdkVersionProvider -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.DdRumContentProvider import java.util.concurrent.TimeUnit internal class DefaultAppStartTimeProvider( - timeProvider: TimeProvider = DefaultTimeProvider(), + timeProvider: TimeProvider, buildSdkVersionProvider: BuildSdkVersionProvider = BuildSdkVersionProvider.DEFAULT ) : AppStartTimeProvider { diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/KronosTimeProvider.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/KronosTimeProvider.kt index 2fc7e6c403..22d60365ec 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/KronosTimeProvider.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/KronosTimeProvider.kt @@ -6,14 +6,13 @@ package com.datadog.android.core.internal.time -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.lyft.kronos.Clock import java.util.concurrent.TimeUnit internal class KronosTimeProvider( private val clock: Clock, - private val deviceTimeProvider: DefaultTimeProvider = DefaultTimeProvider() + private val deviceTimeProvider: TimeProvider ) : TimeProvider { override fun getDeviceTimestamp(): Long { diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt index 8912f4e350..044bff4e8a 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt @@ -1018,8 +1018,9 @@ internal class DatadogCoreTest { fun `M provide time info without correction W time() {NoOpTimeProvider}`() { // Given testedCore.coreFeature = mock() + val timeProvider = DefaultTimeProvider() whenever(testedCore.coreFeature.initialized).thenReturn(AtomicBoolean()) - whenever(testedCore.coreFeature.timeProvider) doReturn DefaultTimeProvider() + whenever(testedCore.coreFeature.timeProvider) doReturn timeProvider // When val time = testedCore.time diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetryTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetryTest.kt index ceafa093c0..1c6c6f8102 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetryTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetryTest.kt @@ -45,6 +45,9 @@ import org.mockito.quality.Strictness ) @MockitoSettings(strictness = Strictness.LENIENT) internal class MethodCalledTelemetryTest { + + private val timeProvider = DefaultTimeProvider() + private lateinit var testedMethodCalledTelemetry: MethodCalledTelemetry @StringForgery @@ -106,7 +109,7 @@ internal class MethodCalledTelemetryTest { whenever(mockDeviceInfo.osVersion).thenReturn(fakeOsVersion) whenever(mockDeviceInfo.deviceBuildId).thenReturn(fakeOsBuild) - fakeStartTime = DefaultTimeProvider().getDeviceElapsedTimeNs() + fakeStartTime = timeProvider.getDeviceElapsedTimeNs() testedMethodCalledTelemetry = MethodCalledTelemetry( internalLogger = mockInternalLogger, operationName = fakeOperationName, @@ -138,7 +141,7 @@ internal class MethodCalledTelemetryTest { verify(mockInternalLogger).logMetric(any(), mapCaptor.capture(), eq(100.0f), eq(fakeCreationSampleRate)) val executionTime = mapCaptor.firstValue[EXECUTION_TIME] as Long - assertThat(executionTime).isLessThan(DefaultTimeProvider().getDeviceElapsedTimeNs() - fakeStartTime) + assertThat(executionTime).isLessThan(timeProvider.getDeviceElapsedTimeNs() - fakeStartTime) } @Test diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt index 483b214e4f..157770d816 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt @@ -13,7 +13,6 @@ import com.datadog.android.core.internal.metrics.RemovalReason import com.datadog.android.core.internal.persistence.file.FileOrchestrator import com.datadog.android.core.internal.persistence.file.FilePersistenceConfig import com.datadog.android.internal.time.DefaultTimeProvider -import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import com.datadog.android.utils.verifyLog import fr.xgouchet.elmyr.Forge @@ -78,11 +77,10 @@ internal class BatchFileOrchestratorTest { @Mock lateinit var mockPendingFiles: AtomicInteger - lateinit var fakeTimeProvider: TimeProvider + private val timeProvider = DefaultTimeProvider() @BeforeEach fun `set up`() { - fakeTimeProvider = DefaultTimeProvider() whenever(mockPendingFiles.decrementAndGet()).thenReturn(fakePendingBatches) whenever(mockPendingFiles.incrementAndGet()).thenReturn(fakePendingBatches) fakeRootDir = File(tempDir, fakeRootDirName) @@ -92,7 +90,7 @@ internal class BatchFileOrchestratorTest { config = TEST_PERSISTENCE_CONFIG, internalLogger = mockLogger, metricsDispatcher = mockMetricsDispatcher, - timeProvider = fakeTimeProvider, + timeProvider = timeProvider, pendingFiles = mockPendingFiles ) } @@ -115,9 +113,9 @@ internal class BatchFileOrchestratorTest { @Test fun `M send batch_closed metric W getWritableFile()`() { // Given - val lowerTimestamp = DefaultTimeProvider().getDeviceTimestamp() + val lowerTimestamp = timeProvider.getDeviceTimestamp() val oldFile = testedOrchestrator.getWritableFile() - val upperTimestamp = DefaultTimeProvider().getDeviceTimestamp() + val upperTimestamp = timeProvider.getDeviceTimestamp() Thread.sleep(RECENT_DELAY_MS + 1) // When @@ -152,7 +150,7 @@ internal class BatchFileOrchestratorTest { config = TEST_PERSISTENCE_CONFIG, internalLogger = mockLogger, metricsDispatcher = mockMetricsDispatcher, - timeProvider = fakeTimeProvider + timeProvider = timeProvider ) // When @@ -180,7 +178,7 @@ internal class BatchFileOrchestratorTest { config = TEST_PERSISTENCE_CONFIG, internalLogger = mockLogger, metricsDispatcher = mockMetricsDispatcher, - timeProvider = fakeTimeProvider + timeProvider = timeProvider ) // When @@ -209,7 +207,7 @@ internal class BatchFileOrchestratorTest { config = TEST_PERSISTENCE_CONFIG, internalLogger = mockLogger, metricsDispatcher = mockMetricsDispatcher, - timeProvider = fakeTimeProvider + timeProvider = timeProvider ) // When @@ -244,19 +242,19 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val oldTimestamp = DefaultTimeProvider().getDeviceTimestamp() - oldFileAge + val oldTimestamp = timeProvider.getDeviceTimestamp() - oldFileAge val oldFile = File(fakeRootDir, oldTimestamp.toString()) oldFile.createNewFile() val oldFileMeta = File("${oldFile.path}_metadata") oldFileMeta.createNewFile() - val youngTimestamp = DefaultTimeProvider().getDeviceTimestamp() - RECENT_DELAY_MS - 1 + val youngTimestamp = timeProvider.getDeviceTimestamp() - RECENT_DELAY_MS - 1 val youngFile = File(fakeRootDir, youngTimestamp.toString()) youngFile.createNewFile() // When - val start = DefaultTimeProvider().getDeviceTimestamp() + val start = timeProvider.getDeviceTimestamp() val result = testedOrchestrator.getWritableFile() - val end = DefaultTimeProvider().getDeviceTimestamp() + val end = timeProvider.getDeviceTimestamp() // Then checkNotNull(result) @@ -282,19 +280,19 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val oldTimestamp = DefaultTimeProvider().getDeviceTimestamp() - oldFileAge + val oldTimestamp = timeProvider.getDeviceTimestamp() - oldFileAge val oldFile = File(fakeRootDir, oldTimestamp.toString()) oldFile.createNewFile() val oldFileMeta = File("${oldFile.path}_metadata") oldFileMeta.createNewFile() - val youngTimestamp = DefaultTimeProvider().getDeviceTimestamp() - RECENT_DELAY_MS - 1 + val youngTimestamp = timeProvider.getDeviceTimestamp() - RECENT_DELAY_MS - 1 val youngFile = File(fakeRootDir, youngTimestamp.toString()) youngFile.createNewFile() // When - val start = DefaultTimeProvider().getDeviceTimestamp() + val start = timeProvider.getDeviceTimestamp() val result = testedOrchestrator.getWritableFile() - val end = DefaultTimeProvider().getDeviceTimestamp() + val end = timeProvider.getDeviceTimestamp() // let's add very old file after the previous cleanup call. If threshold is respected, // cleanup shouldn't be performed during the next getWritableFile call val evenOlderFile = File(fakeRootDir, (oldTimestamp - 1).toString()) @@ -325,16 +323,16 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val oldTimestamp = DefaultTimeProvider().getDeviceTimestamp() - oldFileAge + val oldTimestamp = timeProvider.getDeviceTimestamp() - oldFileAge val oldFile = File(fakeRootDir, oldTimestamp.toString()) oldFile.createNewFile() val oldFileMeta = File("${oldFile.path}_metadata") oldFileMeta.createNewFile() // When - val start = DefaultTimeProvider().getDeviceTimestamp() + val start = timeProvider.getDeviceTimestamp() val result = testedOrchestrator.getWritableFile() - val end = DefaultTimeProvider().getDeviceTimestamp() + val end = timeProvider.getDeviceTimestamp() Thread.sleep(CLEANUP_FREQUENCY_THRESHOLD_MS + 1) val evenOlderFile = File(fakeRootDir, (oldTimestamp - 1).toString()) evenOlderFile.createNewFile() @@ -378,9 +376,9 @@ internal class BatchFileOrchestratorTest { assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) // When - val start = DefaultTimeProvider().getDeviceTimestamp() + val start = timeProvider.getDeviceTimestamp() val result = testedOrchestrator.getWritableFile() - val end = DefaultTimeProvider().getDeviceTimestamp() + val end = timeProvider.getDeviceTimestamp() // Then checkNotNull(result) @@ -419,18 +417,18 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val beforeFileCreateTimestamp = DefaultTimeProvider().getDeviceTimestamp() + val beforeFileCreateTimestamp = timeProvider.getDeviceTimestamp() val previousFile = testedOrchestrator.getWritableFile() - val afterFileCreateTimestamp = DefaultTimeProvider().getDeviceTimestamp() + val afterFileCreateTimestamp = timeProvider.getDeviceTimestamp() checkNotNull(previousFile) previousFile.writeText(previousData) Thread.sleep(RECENT_DELAY_MS + 1) // When - val start = DefaultTimeProvider().getDeviceTimestamp() + val start = timeProvider.getDeviceTimestamp() val result = testedOrchestrator.getWritableFile() - val end = DefaultTimeProvider().getDeviceTimestamp() + val end = timeProvider.getDeviceTimestamp() // Then checkNotNull(result) @@ -455,14 +453,14 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val previousFile = File(fakeRootDir, DefaultTimeProvider().getDeviceTimestamp().toString()) + val previousFile = File(fakeRootDir, timeProvider.getDeviceTimestamp().toString()) previousFile.writeText(previousData) Thread.sleep(1) // When - val start = DefaultTimeProvider().getDeviceTimestamp() + val start = timeProvider.getDeviceTimestamp() val result = testedOrchestrator.getWritableFile() - val end = DefaultTimeProvider().getDeviceTimestamp() + val end = timeProvider.getDeviceTimestamp() // Then checkNotNull(result) @@ -479,18 +477,18 @@ internal class BatchFileOrchestratorTest { fun `M return new File W getWritableFile() {previous file is deleted}`() { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val beforeFileCreateTimestamp = DefaultTimeProvider().getDeviceTimestamp() + val beforeFileCreateTimestamp = timeProvider.getDeviceTimestamp() val previousFile = testedOrchestrator.getWritableFile() - val afterFileCreateTimestamp = DefaultTimeProvider().getDeviceTimestamp() + val afterFileCreateTimestamp = timeProvider.getDeviceTimestamp() checkNotNull(previousFile) previousFile.createNewFile() previousFile.delete() Thread.sleep(1) // When - val start = DefaultTimeProvider().getDeviceTimestamp() + val start = timeProvider.getDeviceTimestamp() val result = testedOrchestrator.getWritableFile() - val end = DefaultTimeProvider().getDeviceTimestamp() + val end = timeProvider.getDeviceTimestamp() // Then checkNotNull(result) @@ -515,17 +513,17 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val beforeFileCreateTimestamp = DefaultTimeProvider().getDeviceTimestamp() + val beforeFileCreateTimestamp = timeProvider.getDeviceTimestamp() val previousFile = testedOrchestrator.getWritableFile() - val afterFileCreateTimestamp = DefaultTimeProvider().getDeviceTimestamp() + val afterFileCreateTimestamp = timeProvider.getDeviceTimestamp() checkNotNull(previousFile) previousFile.writeText(previousData) Thread.sleep(1) // When - val start = DefaultTimeProvider().getDeviceTimestamp() + val start = timeProvider.getDeviceTimestamp() val result = testedOrchestrator.getWritableFile() - val end = DefaultTimeProvider().getDeviceTimestamp() + val end = timeProvider.getDeviceTimestamp() // Then checkNotNull(result) @@ -550,7 +548,7 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val beforeFileCreateTimestamp = DefaultTimeProvider().getDeviceTimestamp() + val beforeFileCreateTimestamp = timeProvider.getDeviceTimestamp() var previousFile = testedOrchestrator.getWritableFile() repeat(4) { @@ -567,12 +565,12 @@ internal class BatchFileOrchestratorTest { assumeTrue(file == previousFile) file?.appendText(previousData[i]) } - val afterLastFileUsageTimestamp = DefaultTimeProvider().getDeviceTimestamp() + val afterLastFileUsageTimestamp = timeProvider.getDeviceTimestamp() // When - val start = DefaultTimeProvider().getDeviceTimestamp() + val start = timeProvider.getDeviceTimestamp() val nextFile = testedOrchestrator.getWritableFile() - val end = DefaultTimeProvider().getDeviceTimestamp() + val end = timeProvider.getDeviceTimestamp() // Then checkNotNull(nextFile) @@ -612,9 +610,9 @@ internal class BatchFileOrchestratorTest { // When Thread.sleep(CLEANUP_FREQUENCY_THRESHOLD_MS + 1) - val start = DefaultTimeProvider().getDeviceTimestamp() + val start = timeProvider.getDeviceTimestamp() val result = testedOrchestrator.getWritableFile() - val end = DefaultTimeProvider().getDeviceTimestamp() + val end = timeProvider.getDeviceTimestamp() // Then checkNotNull(result) @@ -652,7 +650,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + timeProvider ) // When @@ -679,7 +677,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + timeProvider ) // When @@ -707,7 +705,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + timeProvider ) // When @@ -728,12 +726,12 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val oldTimestamp = DefaultTimeProvider().getDeviceTimestamp() - oldFileAge + val oldTimestamp = timeProvider.getDeviceTimestamp() - oldFileAge val oldFile = File(fakeRootDir, oldTimestamp.toString()) oldFile.createNewFile() val oldFileMeta = File("${oldFile.path}_metadata") oldFileMeta.createNewFile() - val youngTimestamp = DefaultTimeProvider().getDeviceTimestamp() - RECENT_DELAY_MS - 1 + val youngTimestamp = timeProvider.getDeviceTimestamp() - RECENT_DELAY_MS - 1 val youngFile = File(fakeRootDir, youngTimestamp.toString()) youngFile.createNewFile() @@ -775,7 +773,7 @@ internal class BatchFileOrchestratorTest { fun `M return file W getReadableFile() {existing old enough file}`() { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val timestamp = DefaultTimeProvider().getDeviceTimestamp() - (RECENT_DELAY_MS * 2) + val timestamp = timeProvider.getDeviceTimestamp() - (RECENT_DELAY_MS * 2) val file = File(fakeRootDir, timestamp.toString()) file.createNewFile() @@ -793,7 +791,7 @@ internal class BatchFileOrchestratorTest { fun `M return null W getReadableFile() {file is too recent}`() { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val timestamp = DefaultTimeProvider().getDeviceTimestamp() - (RECENT_DELAY_MS / 2) + val timestamp = timeProvider.getDeviceTimestamp() - (RECENT_DELAY_MS / 2) val file = File(fakeRootDir, timestamp.toString()) file.createNewFile() @@ -808,7 +806,7 @@ internal class BatchFileOrchestratorTest { fun `M return null W getReadableFile() {file is in exclude list}`() { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val timestamp = DefaultTimeProvider().getDeviceTimestamp() - (RECENT_DELAY_MS * 2) + val timestamp = timeProvider.getDeviceTimestamp() - (RECENT_DELAY_MS * 2) val file = File(fakeRootDir, timestamp.toString()) file.createNewFile() @@ -835,7 +833,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + timeProvider ) // When @@ -862,7 +860,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + timeProvider ) // When @@ -890,7 +888,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + timeProvider ) // When @@ -936,8 +934,8 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val old = DefaultTimeProvider().getDeviceTimestamp() - (RECENT_DELAY_MS * 2) - val new = DefaultTimeProvider().getDeviceTimestamp() - (RECENT_DELAY_MS / 2) + val old = timeProvider.getDeviceTimestamp() - (RECENT_DELAY_MS * 2) + val new = timeProvider.getDeviceTimestamp() - (RECENT_DELAY_MS / 2) val expectedFiles = mutableListOf() for (i in 1..count) { // create both non readable and non writable files @@ -982,8 +980,8 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val old = DefaultTimeProvider().getDeviceTimestamp() - (RECENT_DELAY_MS * 2) - val new = DefaultTimeProvider().getDeviceTimestamp() - (RECENT_DELAY_MS / 2) + val old = timeProvider.getDeviceTimestamp() - (RECENT_DELAY_MS * 2) + val new = timeProvider.getDeviceTimestamp() - (RECENT_DELAY_MS / 2) val expectedFiles = mutableListOf() for (i in 1..count) { // create both non readable and non writable files @@ -1030,7 +1028,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + timeProvider ) // When @@ -1057,7 +1055,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + timeProvider ) // When @@ -1085,7 +1083,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + timeProvider ) // When @@ -1161,7 +1159,7 @@ internal class BatchFileOrchestratorTest { @Test fun `M return metadata file W getMetadataFile()`() { // Given - val fakeFileName = DefaultTimeProvider().getDeviceTimestamp().toString() + val fakeFileName = timeProvider.getDeviceTimestamp().toString() val fakeFile = File(fakeRootDir.path, fakeFileName) // When @@ -1177,7 +1175,7 @@ internal class BatchFileOrchestratorTest { @StringForgery fakeSuffix: String ) { // Given - val fakeFileName = DefaultTimeProvider().getDeviceTimestamp().toString() + val fakeFileName = timeProvider.getDeviceTimestamp().toString() val fakeFile = File("${fakeRootDir.parent}$fakeSuffix", fakeFileName) // When diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt index 1a87c15919..19079ab16c 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt @@ -28,6 +28,9 @@ import java.util.concurrent.TimeUnit ExtendWith(ForgeExtension::class) ) class DefaultAppStartTimeProviderTest { + + private val timeProvider = DefaultTimeProvider() + @Test fun `M return process start time W appStartTime { N+ }`( @IntForgery(min = Build.VERSION_CODES.N) apiVersion: Int @@ -35,7 +38,6 @@ class DefaultAppStartTimeProviderTest { // GIVEN val mockBuildSdkVersionProvider: BuildSdkVersionProvider = mock() whenever(mockBuildSdkVersionProvider.version) doReturn apiVersion - val timeProvider = DefaultTimeProvider() val diffMs = SystemClock.elapsedRealtime() - Process.getStartElapsedRealtime() val startTimeNs = timeProvider.getDeviceElapsedTimeNs() - TimeUnit.MILLISECONDS.toNanos(diffMs) @@ -58,7 +60,6 @@ class DefaultAppStartTimeProviderTest { val startTimeNs = DdRumContentProvider.createTimeNs // WHEN - val timeProvider = DefaultTimeProvider() val defaultAppStartTimeProvider = DefaultAppStartTimeProvider(timeProvider, mockBuildSdkVersionProvider) val providedStartTime = defaultAppStartTimeProvider.appStartTimeNs diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/KronosTimeProviderTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/KronosTimeProviderTest.kt index f4a209dbf4..b6e186e2aa 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/KronosTimeProviderTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/KronosTimeProviderTest.kt @@ -35,6 +35,8 @@ import java.util.concurrent.TimeUnit @ForgeConfiguration(Configurator::class) internal class KronosTimeProviderTest { + private val timeProvider = DefaultTimeProvider() + lateinit var testedTimeProvider: KronosTimeProvider @Mock @@ -46,7 +48,7 @@ internal class KronosTimeProviderTest { @BeforeEach fun `set up`() { whenever(mockClock.getCurrentTimeMs()) doReturn fakeDate.time - testedTimeProvider = KronosTimeProvider(mockClock) + testedTimeProvider = KronosTimeProvider(mockClock, timeProvider) } @Test @@ -58,7 +60,7 @@ internal class KronosTimeProviderTest { @Test fun `returns server time offset in nanoseconds`() { - val now = DefaultTimeProvider().getDeviceTimestamp() + val now = timeProvider.getDeviceTimestamp() val result = testedTimeProvider.getServerOffsetNanos() val expectedOffset = TimeUnit.MILLISECONDS.toNanos(fakeDate.time - now) @@ -70,7 +72,7 @@ internal class KronosTimeProviderTest { @Test fun `returns server time offset in milliseconds`() { - val now = DefaultTimeProvider().getDeviceTimestamp() + val now = timeProvider.getDeviceTimestamp() val result = testedTimeProvider.getServerOffsetMillis() val expectedOffset = fakeDate.time - now @@ -82,7 +84,7 @@ internal class KronosTimeProviderTest { @Test fun `returns device time`() { - val now = DefaultTimeProvider().getDeviceTimestamp() + val now = timeProvider.getDeviceTimestamp() val result = testedTimeProvider.getDeviceTimestamp() assertThat(result).isCloseTo(now, Offset.offset(TEST_OFFSET)) diff --git a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/TimeProvider.kt b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/TimeProvider.kt index f76897c27a..796fdf6446 100644 --- a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/TimeProvider.kt +++ b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/TimeProvider.kt @@ -6,11 +6,12 @@ package com.datadog.android.internal.time -// there is no NoOpImplementation on purpose, we don't want to have 0 values for the -// case when this instance is used. +import com.datadog.tools.annotation.NoOpImplementation + /** * Interface to provide the current time in both device and server time references. */ +@NoOpImplementation interface TimeProvider { /** diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessorTest.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessorTest.kt index 0e0895c107..9465495e26 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessorTest.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessorTest.kt @@ -58,6 +58,8 @@ internal class ExposureEventsProcessorTest { private lateinit var testedProcessor: ExposureEventsProcessor private lateinit var fakeFlag: PrecomputedFlag + private val timeProvider: TimeProvider = DefaultTimeProvider() + @BeforeEach fun `set up`(forge: Forge) { val fakeTimestamp = forge.aLong(min = 1) @@ -227,8 +229,7 @@ internal class ExposureEventsProcessorTest { @Test fun `M generate consistent timestamps W processEvent() { multiple calls }`(forge: Forge) { // Given - val realTimeProvider = DefaultTimeProvider() - whenever(mockTimeProvider.getDeviceTimestamp()).thenAnswer { realTimeProvider.getDeviceTimestamp() } + whenever(mockTimeProvider.getDeviceTimestamp()).thenAnswer { timeProvider.getDeviceTimestamp() } val fakeContext1 = EvaluationContext( targetingKey = forge.anAlphabeticalString(), @@ -239,13 +240,13 @@ internal class ExposureEventsProcessorTest { attributes = mapOf("user_id" to forge.anAlphabeticalString()) ) - val beforeTime = realTimeProvider.getDeviceTimestamp() + val beforeTime = timeProvider.getDeviceTimestamp() // When testedProcessor.processEvent(fakeFlagName, fakeContext1, fakeFlag) testedProcessor.processEvent(fakeFlagName, fakeContext2, fakeFlag) - val afterTime = realTimeProvider.getDeviceTimestamp() + val afterTime = timeProvider.getDeviceTimestamp() // Then val eventCaptor = argumentCaptor() diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateDeserializerTest.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateDeserializerTest.kt index 269a37ef6a..6634bfda10 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateDeserializerTest.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateDeserializerTest.kt @@ -29,6 +29,8 @@ import org.mockito.quality.Strictness @MockitoSettings(strictness = Strictness.LENIENT) internal class FlagsStateDeserializerTest { + private val timeProvider = DefaultTimeProvider() + @Mock lateinit var mockInternalLogger: InternalLogger @@ -46,7 +48,7 @@ internal class FlagsStateDeserializerTest { val stringAttr = forge.anAlphabeticalString() val numberAttr = forge.anInt().toString() val booleanAttr = forge.aBool().toString() - val timestamp = DefaultTimeProvider().getDeviceTimestamp() + val timestamp = timeProvider.getDeviceTimestamp() val json = JSONObject().apply { put( @@ -108,7 +110,7 @@ internal class FlagsStateDeserializerTest { fun `M deserialize empty state W deserialize() { valid JSON with empty data }`(forge: Forge) { // Given val targetingKey = forge.anAlphabeticalString() - val timestamp = DefaultTimeProvider().getDeviceTimestamp() + val timestamp = timeProvider.getDeviceTimestamp() val json = JSONObject().apply { put( @@ -173,7 +175,7 @@ internal class FlagsStateDeserializerTest { fun `M deserialize with empty attributes W deserialize() { missing attributes field }`(forge: Forge) { // Given val targetingKey = forge.anAlphabeticalString() - val timestamp = DefaultTimeProvider().getDeviceTimestamp() + val timestamp = timeProvider.getDeviceTimestamp() val json = JSONObject().apply { put( @@ -201,7 +203,7 @@ internal class FlagsStateDeserializerTest { fun `M skip invalid flag and process others W deserialize() { one invalid flag }`(forge: Forge) { // Given val targetingKey = forge.anAlphabeticalString() - val timestamp = DefaultTimeProvider().getDeviceTimestamp() + val timestamp = timeProvider.getDeviceTimestamp() val json = JSONObject().apply { put( @@ -283,7 +285,7 @@ internal class FlagsStateDeserializerTest { } ) put("flags", JSONObject()) - put("lastUpdateTimestamp", DefaultTimeProvider().getDeviceTimestamp()) + put("lastUpdateTimestamp", timeProvider.getDeviceTimestamp()) } // When diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateSerializerTest.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateSerializerTest.kt index 07f4f76f25..38d85bbc0c 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateSerializerTest.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateSerializerTest.kt @@ -24,6 +24,8 @@ import org.mockito.junit.jupiter.MockitoExtension @ExtendWith(MockitoExtension::class, ForgeExtension::class) internal class FlagsStateSerializerTest { + private val timeProvider = DefaultTimeProvider() + @Mock lateinit var mockInternalLogger: InternalLogger @@ -66,7 +68,7 @@ internal class FlagsStateSerializerTest { ) ) - val timestamp = DefaultTimeProvider().getDeviceTimestamp() + val timestamp = timeProvider.getDeviceTimestamp() val flagsState = FlagsStateEntry(evaluationContext, flags, timestamp) // When diff --git a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/LogsFeatureTest.kt b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/LogsFeatureTest.kt index 0e5fca2239..c74562e2b3 100644 --- a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/LogsFeatureTest.kt +++ b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/LogsFeatureTest.kt @@ -127,11 +127,13 @@ internal class LogsFeatureTest { private var fakeServerTimeOffset: Long = 0L + private val timeProvider = DefaultTimeProvider() + @BeforeEach fun `set up`( forge: Forge ) { - val now = DefaultTimeProvider().getDeviceTimestamp() + val now = timeProvider.getDeviceTimestamp() fakeServerTimeOffset = forge.aLong(min = -now, max = Long.MAX_VALUE - now) whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger diff --git a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/DatadogLogGeneratorTest.kt b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/DatadogLogGeneratorTest.kt index 5e2d4f2f56..6a96a32a16 100644 --- a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/DatadogLogGeneratorTest.kt +++ b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/DatadogLogGeneratorTest.kt @@ -45,6 +45,8 @@ import java.util.UUID @ForgeConfiguration(Configurator::class) internal class DatadogLogGeneratorTest { + private val timeProvider = DefaultTimeProvider() + lateinit var testedLogGenerator: DatadogLogGenerator lateinit var fakeServiceName: String @@ -89,7 +91,7 @@ internal class DatadogLogGeneratorTest { fakeAttributes = forge.aMap { anAlphabeticalString() to anInt() } fakeTags = forge.aList { anAlphabeticalString() }.toSet() fakeThrowable = forge.aThrowable() - fakeTimestamp = DefaultTimeProvider().getDeviceTimestamp() + fakeTimestamp = timeProvider.getDeviceTimestamp() fakeThreadName = forge.anAlphabeticalString() fakeTimeOffset = forge.aLong( min = -fakeTimestamp, diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporterTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporterTest.kt index 168703b189..222f6e626a 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporterTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporterTest.kt @@ -69,6 +69,8 @@ import org.mockito.quality.Strictness @ForgeConfiguration(Configurator::class) internal class DatadogLateCrashReporterTest { + private val timeProvider = DefaultTimeProvider() + private lateinit var testedHandler: LateCrashReporter @Mock @@ -146,7 +148,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( + date = timeProvider.getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ), @@ -247,7 +249,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( + date = timeProvider.getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ), @@ -349,7 +351,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( + date = timeProvider.getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ), @@ -408,7 +410,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( + date = timeProvider.getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ), @@ -493,7 +495,7 @@ internal class DatadogLateCrashReporterTest { ) ) val fakeViewEvent = viewEvent.copy( - date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( + date = timeProvider.getDeviceTimestamp() - forge.aLong( min = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD + 1 ), usr = ViewEvent.Usr( @@ -698,7 +700,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( + date = timeProvider.getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ), @@ -795,7 +797,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( + date = timeProvider.getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ), @@ -879,7 +881,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( + date = timeProvider.getDeviceTimestamp() - forge.aLong( min = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD + 1 ), usr = ViewEvent.Usr( @@ -970,7 +972,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( + date = timeProvider.getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ) @@ -1027,7 +1029,7 @@ internal class DatadogLateCrashReporterTest { ) { // Given val fakeViewEvent = viewEvent.copy( - date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( + date = timeProvider.getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ) @@ -1067,7 +1069,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( + date = timeProvider.getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ) @@ -1104,7 +1106,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( + date = timeProvider.getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ) @@ -1142,7 +1144,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = DefaultTimeProvider().getDeviceTimestamp() - forge.aLong( + date = timeProvider.getDeviceTimestamp() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/TimeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/TimeTest.kt index c4e79c3290..35dc5a0182 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/TimeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/TimeTest.kt @@ -7,6 +7,7 @@ package com.datadog.android.rum.internal.domain import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.utils.forge.Configurator import fr.xgouchet.elmyr.annotation.LongForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration @@ -21,15 +22,17 @@ import java.util.concurrent.TimeUnit @ForgeConfiguration(Configurator::class) internal class TimeTest { + private val timeProvider: TimeProvider = DefaultTimeProvider() + @Test fun `creates Time with current millis and nanos`() { - val startMs = DefaultTimeProvider().getDeviceTimestamp() - val startNs = DefaultTimeProvider().getDeviceElapsedTimeNs() + val startMs = timeProvider.getDeviceTimestamp() + val startNs = timeProvider.getDeviceElapsedTimeNs() val time = Time() - val endNs = DefaultTimeProvider().getDeviceElapsedTimeNs() - val endMs = DefaultTimeProvider().getDeviceTimestamp() + val endNs = timeProvider.getDeviceElapsedTimeNs() + val endMs = timeProvider.getDeviceTimestamp() assertThat(time.timestamp).isBetween(startMs, endMs) assertThat(time.nanoTime).isBetween(startNs, endNs) @@ -39,11 +42,11 @@ internal class TimeTest { fun `M convert timestamp to Time W asTime()`( @LongForgery(1000000000000, 2000000000000) timestamp: Long ) { - val startMs = DefaultTimeProvider().getDeviceTimestamp() - val startNs = DefaultTimeProvider().getDeviceElapsedTimeNs() + val startMs = timeProvider.getDeviceTimestamp() + val startNs = timeProvider.getDeviceElapsedTimeNs() val time = timestamp.asTime() - val endNs = DefaultTimeProvider().getDeviceElapsedTimeNs() - val endMs = DefaultTimeProvider().getDeviceTimestamp() + val endNs = timeProvider.getDeviceElapsedTimeNs() + val endMs = timeProvider.getDeviceTimestamp() assertThat(time.timestamp).isEqualTo(timestamp) val nanoOffset = time.nanoTime - ((startNs + endNs) / 2) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReaderTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReaderTest.kt index c47ebd9a6e..bf6ad2cf8a 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReaderTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReaderTest.kt @@ -21,6 +21,7 @@ import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener import com.datadog.android.api.InternalLogger import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.internal.domain.accessibility.DefaultAccessibilityReader.Companion.CAPTIONING_ENABLED_KEY import com.datadog.android.rum.utils.forge.Configurator import com.datadog.tools.unit.annotations.TestTargetApi @@ -85,6 +86,9 @@ internal class DefaultAccessibilityReaderTest { @Mock lateinit var mockHandler: Handler + @Mock + lateinit var mockTimeProvider: TimeProvider + private lateinit var testedReader: DefaultAccessibilityReader @BeforeEach @@ -107,7 +111,8 @@ internal class DefaultAccessibilityReaderTest { accessibilityManager = mockAccessibilityManager, secureWrapper = mockSecureWrapper, globalWrapper = mockGlobalWrapper, - handler = mockHandler + handler = mockHandler, + timeProvider = mockTimeProvider ) } @@ -163,7 +168,8 @@ internal class DefaultAccessibilityReaderTest { accessibilityManager = null, secureWrapper = mockSecureWrapper, globalWrapper = mockGlobalWrapper, - handler = mockHandler + handler = mockHandler, + timeProvider = mockTimeProvider ) // When @@ -342,7 +348,8 @@ internal class DefaultAccessibilityReaderTest { accessibilityManager = mockAccessibilityManager, secureWrapper = mockSecureWrapper, globalWrapper = mockGlobalWrapper, - handler = mockHandler + handler = mockHandler, + timeProvider = mockTimeProvider ) // When @@ -622,7 +629,8 @@ internal class DefaultAccessibilityReaderTest { accessibilityManager = null, secureWrapper = mockSecureWrapper, globalWrapper = mockGlobalWrapper, - handler = mockHandler + handler = mockHandler, + timeProvider = mockTimeProvider ) testedReader.getState() @@ -757,20 +765,23 @@ internal class DefaultAccessibilityReaderTest { fun `M update lastPollTime W getState { after threshold exceeded }`() { // Given whenever(mockActivityManager.lockTaskModeState) doReturn ActivityManager.LOCK_TASK_MODE_NONE + val realTimeProvider = DefaultTimeProvider() // Initialize + whenever(mockTimeProvider.getDeviceTimestamp()) doReturn realTimeProvider.getDeviceTimestamp() testedReader.getState() val lastPollTimeField = testedReader.javaClass.getDeclaredField("lastPollTime") lastPollTimeField.isAccessible = true val lastPollTime = lastPollTimeField.get(testedReader) as AtomicLong - val oldTime = DefaultTimeProvider().getDeviceTimestamp() - 31_000 + val oldTime = realTimeProvider.getDeviceTimestamp() - 31_000 lastPollTime.set(oldTime) // When - Call after threshold exceeded - val currentTimeBefore = DefaultTimeProvider().getDeviceTimestamp() + val currentTimeBefore = realTimeProvider.getDeviceTimestamp() + whenever(mockTimeProvider.getDeviceTimestamp()) doReturn currentTimeBefore testedReader.getState() - val currentTimeAfter = DefaultTimeProvider().getDeviceTimestamp() + val currentTimeAfter = realTimeProvider.getDeviceTimestamp() // Then - lastPollTime should be updated to current time (within reasonable range) val newPollTime = lastPollTime.get() diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeAttributePropagationTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeAttributePropagationTest.kt index 1452c4dc99..8ccd88e5a9 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeAttributePropagationTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeAttributePropagationTest.kt @@ -129,6 +129,8 @@ internal class RumApplicationScopeAttributePropagationTest { @Mock lateinit var mockSlowFramesListener: SlowFramesListener + private val timeProvider = DefaultTimeProvider() + lateinit var fakeEventTime: Time lateinit var fakeEvent: RumRawEvent @@ -170,8 +172,8 @@ internal class RumApplicationScopeAttributePropagationTest { ) val fakeOffset = -forge.aLong(1000, 50000) - val fakeTimestamp = DefaultTimeProvider().getDeviceTimestamp() + fakeOffset - val fakeNanos = DefaultTimeProvider().getDeviceElapsedTimeNs() + TimeUnit.MILLISECONDS.toNanos(fakeOffset) + val fakeTimestamp = timeProvider.getDeviceTimestamp() + fakeOffset + val fakeNanos = timeProvider.getDeviceElapsedTimeNs() + TimeUnit.MILLISECONDS.toNanos(fakeOffset) val maxLimit = max(Long.MAX_VALUE - fakeTimestamp, Long.MAX_VALUE) val minLimit = min(-fakeTimestamp, maxLimit) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeAttributePropagationTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeAttributePropagationTest.kt index cae78a6a92..d353bb4b8a 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeAttributePropagationTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeAttributePropagationTest.kt @@ -136,6 +136,8 @@ internal class RumViewScopeAttributePropagationTest { @Mock lateinit var mockSlowFramesListener: SlowFramesListener + private val timeProvider = DefaultTimeProvider() + @Forgery lateinit var fakeKey: RumScopeKey @@ -220,8 +222,8 @@ internal class RumViewScopeAttributePropagationTest { fakeParentContext.copy(syntheticsTestId = null, syntheticsResultId = null) val fakeOffset = -forge.aLong(1000, 50000) - val fakeTimestamp = DefaultTimeProvider().getDeviceTimestamp() + fakeOffset - val fakeNanos = DefaultTimeProvider().getDeviceElapsedTimeNs() + TimeUnit.MILLISECONDS.toNanos(fakeOffset) + val fakeTimestamp = timeProvider.getDeviceTimestamp() + fakeOffset + val fakeNanos = timeProvider.getDeviceElapsedTimeNs() + TimeUnit.MILLISECONDS.toNanos(fakeOffset) val maxLimit = max(Long.MAX_VALUE - fakeTimestamp, Long.MAX_VALUE) val minLimit = min(-fakeTimestamp, maxLimit) fakeSampleRate = forge.aFloat(min = 0.0f, max = 100.0f) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/ActionTypeInteractionValidatorTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/ActionTypeInteractionValidatorTest.kt index ed358528cb..524029028f 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/ActionTypeInteractionValidatorTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/ActionTypeInteractionValidatorTest.kt @@ -59,28 +59,30 @@ internal class ActionTypeInteractionValidatorTest { companion object { + private val timeProvider = DefaultTimeProvider() + @JvmStatic fun validTypesContexts(): List { return listOf( InternalInteractionContext( "viewId", ActionEvent.ActionEventActionType.TAP, - DefaultTimeProvider().getDeviceElapsedTimeNs() + timeProvider.getDeviceElapsedTimeNs() ), InternalInteractionContext( "viewId", ActionEvent.ActionEventActionType.CLICK, - DefaultTimeProvider().getDeviceElapsedTimeNs() + timeProvider.getDeviceElapsedTimeNs() ), InternalInteractionContext( "viewId", ActionEvent.ActionEventActionType.SWIPE, - DefaultTimeProvider().getDeviceElapsedTimeNs() + timeProvider.getDeviceElapsedTimeNs() ), InternalInteractionContext( "viewId", ActionEvent.ActionEventActionType.BACK, - DefaultTimeProvider().getDeviceElapsedTimeNs() + timeProvider.getDeviceElapsedTimeNs() ) ) } @@ -91,12 +93,12 @@ internal class ActionTypeInteractionValidatorTest { InternalInteractionContext( "viewId", ActionEvent.ActionEventActionType.CUSTOM, - DefaultTimeProvider().getDeviceElapsedTimeNs() + timeProvider.getDeviceElapsedTimeNs() ), InternalInteractionContext( "viewId", ActionEvent.ActionEventActionType.SCROLL, - DefaultTimeProvider().getDeviceElapsedTimeNs() + timeProvider.getDeviceElapsedTimeNs() ) ) } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/InteractionToNextViewMetricResolverTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/InteractionToNextViewMetricResolverTest.kt index 32545273f7..acc5be6150 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/InteractionToNextViewMetricResolverTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/InteractionToNextViewMetricResolverTest.kt @@ -40,6 +40,9 @@ import java.util.UUID @MockitoSettings(strictness = Strictness.LENIENT) @ForgeConfiguration(Configurator::class) internal class InteractionToNextViewMetricResolverTest { + + private val timeProvider = DefaultTimeProvider() + private lateinit var testedMetric: InteractionToNextViewMetricResolver @Mock @@ -54,7 +57,7 @@ internal class InteractionToNextViewMetricResolverTest { private lateinit var fakeViewId: String private lateinit var fakeFirstViewId: String - private val fakeFirstViewTimestampNs: Long = DefaultTimeProvider().getDeviceElapsedTimeNs() + private val fakeFirstViewTimestampNs: Long = timeProvider.getDeviceElapsedTimeNs() // region setup @@ -136,7 +139,7 @@ internal class InteractionToNextViewMetricResolverTest { @Test fun `M return NO_ACTION W getState { no action was registered on previous view }`() { // When - testedMetric.onViewCreated(fakeViewId, DefaultTimeProvider().getDeviceElapsedTimeNs()) + testedMetric.onViewCreated(fakeViewId, timeProvider.getDeviceElapsedTimeNs()) val result = testedMetric.getState(fakeViewId) // Then @@ -583,7 +586,7 @@ internal class InteractionToNextViewMetricResolverTest { min: Int, max: Int, viewId: String, - startTimestamp: Long = DefaultTimeProvider().getDeviceElapsedTimeNs() + startTimestamp: Long = timeProvider.getDeviceElapsedTimeNs() ): List { return aList(size = anInt(min = min, max = max)) { getForgery() diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/metric/interactiontonextview/TimeBasedInteractionIdentifierTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/metric/interactiontonextview/TimeBasedInteractionIdentifierTest.kt index d78131ce96..cda8afbf92 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/metric/interactiontonextview/TimeBasedInteractionIdentifierTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/metric/interactiontonextview/TimeBasedInteractionIdentifierTest.kt @@ -7,6 +7,7 @@ package com.datadog.android.rum.metric.interactiontonextview import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.utils.forge.Configurator import com.datadog.tools.unit.ObjectTest import fr.xgouchet.elmyr.Forge @@ -33,6 +34,8 @@ internal class TimeBasedInteractionIdentifierTest : ObjectTest().toString() testedMetric.resourceWasStarted(InternalResourceContext(fakeResourceId, startTimestamp)) @@ -197,7 +198,7 @@ internal class NetworkSettledMetricResolverTest { fun `M return null W resolveMetric(){ view not created }`(forge: Forge) { // Given testedMetric = NetworkSettledMetricResolver(mockInitialResourceIdentifier, mockInternalLogger) - val startTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() + val startTimestamp = timeProvider.getDeviceElapsedTimeNs() val stopTimestamp = startTimestamp + forge.aLong(min = 1, max = 1000) val fakeResourceId = forge.getForgery().toString() testedMetric.resourceWasStarted(InternalResourceContext(fakeResourceId, startTimestamp)) @@ -246,7 +247,7 @@ internal class NetworkSettledMetricResolverTest { fun `M pass the viewCreatedTimestamp to validator W resourceWasStarted()`(forge: Forge) { // Given testedMetric = NetworkSettledMetricResolver(mockInitialResourceIdentifier, mockInternalLogger) - val startTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() + val startTimestamp = timeProvider.getDeviceElapsedTimeNs() val expectedViewCreatedTimestamp: Long? if (forge.aBool()) { testedMetric.viewWasCreated(fakeViewStartTime) @@ -415,7 +416,7 @@ internal class NetworkSettledMetricResolverTest { @Test fun `M return null W resolveMetric(){ resource was stopped with a different id }`(forge: Forge) { // Given - val startTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() + val startTimestamp = timeProvider.getDeviceElapsedTimeNs() val stopTimestamp = startTimestamp + forge.aLong(min = 1, max = 1000) val fakeResourceContext = forge.getForgery() val fakeDifferentResourceContext = forge.getForgery() @@ -437,7 +438,7 @@ internal class NetworkSettledMetricResolverTest { @Test fun `M return null W resolveMetric(){ resource was dropped }`(forge: Forge) { // Given - val startTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() + val startTimestamp = timeProvider.getDeviceElapsedTimeNs() val fakeResourceContext = forge.getForgery() testedMetric.resourceWasStarted(InternalResourceContext(fakeResourceContext.resourceId, startTimestamp)) testedMetric.resourceWasDropped(fakeResourceContext.resourceId) @@ -483,7 +484,7 @@ internal class NetworkSettledMetricResolverTest { forge: Forge ) { // Given - val startTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() + val startTimestamp = timeProvider.getDeviceElapsedTimeNs() val stopTimestamp = startTimestamp + forge.aLong(min = 1, max = 1000) val fakeResourceContext = forge.getForgery() val thread1 = Thread { @@ -519,7 +520,7 @@ internal class NetworkSettledMetricResolverTest { forge: Forge ) { // Given - val startTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() + val startTimestamp = timeProvider.getDeviceElapsedTimeNs() val stopTimestamp = startTimestamp + forge.aLong(min = 1, max = 1000) val fakeResourceContext = forge.getForgery() val thread1 = Thread { @@ -556,7 +557,7 @@ internal class NetworkSettledMetricResolverTest { forge: Forge ) { // Given - val startTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() + val startTimestamp = timeProvider.getDeviceElapsedTimeNs() val stopTimestamp = startTimestamp + forge.aLong(min = 1, max = 1000) val fakeResourceContext = forge.getForgery() val thread1 = Thread { @@ -594,7 +595,7 @@ internal class NetworkSettledMetricResolverTest { // region Internal private fun Forge.forgeStartTimestamps(size: Int = anInt(min = 1, max = 10)) = aList(size = size) { - DefaultTimeProvider().getDeviceElapsedTimeNs() + aLong(min = 1, max = 1000) + timeProvider.getDeviceElapsedTimeNs() + aLong(min = 1, max = 1000) } private fun List.mapToSettledIntervals(): List { diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/DefaultRecorderProvider.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/DefaultRecorderProvider.kt index b4eb7ddf8e..9fb43cd2b0 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/DefaultRecorderProvider.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/DefaultRecorderProvider.kt @@ -22,7 +22,6 @@ import android.widget.TextView import androidx.appcompat.widget.ActionBarContainer import androidx.appcompat.widget.SwitchCompat import com.datadog.android.api.feature.FeatureSdkCore -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.utils.ImageViewUtils import com.datadog.android.sessionreplay.ImagePrivacy import com.datadog.android.sessionreplay.MapperTypeWrapper @@ -86,7 +85,7 @@ internal class DefaultRecorderProvider( touchPrivacyManager = touchPrivacyManager, textAndInputPrivacy = textAndInputPrivacy, recordWriter = recordWriter, - timeProvider = DefaultTimeProvider(), + timeProvider = sdkCore.timeProvider, mappers = customMappers + builtInMappers(), customOptionSelectorDetectors = customOptionSelectorDetectors, customDrawableMappers = customDrawableMappers, diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/SessionReplayFeatureTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/SessionReplayFeatureTest.kt index 68c1c0d67c..03db01e97c 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/SessionReplayFeatureTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/SessionReplayFeatureTest.kt @@ -12,6 +12,7 @@ import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.FeatureContextUpdateReceiver import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.core.sampling.Sampler +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.sessionreplay.NoOpSessionReplayInternalCallback import com.datadog.android.sessionreplay.SessionReplayConfiguration import com.datadog.android.sessionreplay.forge.ForgeConfigurator @@ -95,6 +96,9 @@ internal class SessionReplayFeatureTest { @Mock lateinit var mockSampler: Sampler + @Mock + lateinit var mockTimeProvider: TimeProvider + private lateinit var fakeSessionId: String private var fakeSampleRate: Float? = null @@ -105,6 +109,7 @@ internal class SessionReplayFeatureTest { whenever(mockSampler.getSampleRate()).thenReturn(fakeSampleRate) fakeSessionId = UUID.randomUUID().toString() whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger + whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider whenever(mockSdkCore.createSingleThreadExecutorService(any())) doReturn mockExecutorService testedFeature = SessionReplayFeature( diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandlerTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandlerTest.kt index d2d550287b..3238e65418 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandlerTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandlerTest.kt @@ -67,6 +67,9 @@ import java.util.concurrent.TimeUnit @MockitoSettings(strictness = Strictness.LENIENT) @ForgeConfiguration(ForgeConfigurator::class) internal class RecordedDataQueueHandlerTest { + + private val timeProvider = DefaultTimeProvider() + private lateinit var testedHandler: RecordedDataQueueHandler @Mock @@ -291,7 +294,7 @@ internal class RecordedDataQueueHandlerTest { ) { // Given mockSnapshotItem.apply { - val expiredTime = DefaultTimeProvider().getDeviceElapsedTimeNs() - RecordedDataQueueHandler.MAX_DELAY_NS + val expiredTime = timeProvider.getDeviceElapsedTimeNs() - RecordedDataQueueHandler.MAX_DELAY_NS whenever(creationTimeStampInNs).thenReturn(expiredTime) whenever(isValid()).thenReturn(true) whenever(isReady()).thenReturn(true) @@ -533,7 +536,7 @@ internal class RecordedDataQueueHandlerTest { mockSnapshotItem1.apply { whenever(systemInformation).thenReturn(mockSystemInformation) whenever(nodes).thenReturn(fakeNodeData) - whenever(creationTimeStampInNs).thenReturn(DefaultTimeProvider().getDeviceElapsedTimeNs()) + whenever(creationTimeStampInNs).thenReturn(timeProvider.getDeviceElapsedTimeNs()) whenever(isValid()).thenReturn(true) whenever(isReady()).thenReturn(true) } @@ -542,7 +545,7 @@ internal class RecordedDataQueueHandlerTest { mockSnapshotItem2.apply { whenever(systemInformation).thenReturn(mockSystemInformation) whenever(nodes).thenReturn(fakeNodeData) - whenever(creationTimeStampInNs).thenReturn(DefaultTimeProvider().getDeviceElapsedTimeNs()) + whenever(creationTimeStampInNs).thenReturn(timeProvider.getDeviceElapsedTimeNs()) whenever(isValid()).thenReturn(true) whenever(isReady()).thenReturn(false) } @@ -551,7 +554,7 @@ internal class RecordedDataQueueHandlerTest { mockSnapshotItem3.apply { whenever(systemInformation).thenReturn(mockSystemInformation) whenever(nodes).thenReturn(fakeNodeData) - whenever(creationTimeStampInNs).thenReturn(DefaultTimeProvider().getDeviceElapsedTimeNs()) + whenever(creationTimeStampInNs).thenReturn(timeProvider.getDeviceElapsedTimeNs()) whenever(isValid()).thenReturn(true) whenever(isReady()).thenReturn(true) } @@ -733,7 +736,7 @@ internal class RecordedDataQueueHandlerTest { ) mockSnapshotItem.apply { - val expiredTime = DefaultTimeProvider().getDeviceElapsedTimeNs() - RecordedDataQueueHandler.MAX_DELAY_NS + val expiredTime = timeProvider.getDeviceElapsedTimeNs() - RecordedDataQueueHandler.MAX_DELAY_NS whenever(creationTimeStampInNs).thenReturn(expiredTime) whenever(isValid()).thenReturn(true) whenever(isReady()).thenReturn(true) @@ -765,7 +768,7 @@ internal class RecordedDataQueueHandlerTest { private fun addSnapshotItemToQueue(): SnapshotRecordedDataQueueItem { val newRumContext = RecordedQueuedItemContext( - timestamp = DefaultTimeProvider().getDeviceTimestamp(), + timestamp = timeProvider.getDeviceTimestamp(), newRumContext = fakeRecordedQueuedItemContext.newRumContext ) diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/resources/ResourceDataStoreManagerTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/resources/ResourceDataStoreManagerTest.kt index 60bc0dc9f4..227eb7f406 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/resources/ResourceDataStoreManagerTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/resources/ResourceDataStoreManagerTest.kt @@ -49,6 +49,9 @@ import org.mockito.quality.Strictness @MockitoSettings(strictness = Strictness.LENIENT) @ForgeConfiguration(ForgeConfigurator::class) internal class ResourceDataStoreManagerTest { + + private val timeProvider = DefaultTimeProvider() + private lateinit var testedDataStoreManager: ResourceDataStoreManager @Mock @@ -329,9 +332,9 @@ internal class ResourceDataStoreManagerTest { val resourceHashes = forge.aList { aString() }.distinct() val fakeVersionCode = forge.anInt(min = 0) val entryTime = if (isExpired) { - DefaultTimeProvider().getDeviceElapsedTimeNs() - DATASTORE_EXPIRATION_NS + timeProvider.getDeviceElapsedTimeNs() - DATASTORE_EXPIRATION_NS } else { - DefaultTimeProvider().getDeviceElapsedTimeNs() + timeProvider.getDeviceElapsedTimeNs() } val mockResourceHashesEntry: ResourceHashesEntry = mock { diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/api/IdGenerationStrategyTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/api/IdGenerationStrategyTest.kt index dc31fdd6f5..35011d6cfe 100644 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/api/IdGenerationStrategyTest.kt +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/api/IdGenerationStrategyTest.kt @@ -31,13 +31,15 @@ import java.security.SecureRandom @ForgeConfiguration(Configurator::class) internal class IdGenerationStrategyTest { + private val timeProvider = DefaultTimeProvider() + // Please note that these tests were just ported from the Groovy CoreTracer tests in APM java code @ParameterizedTest @ValueSource(strings = ["RANDOM", "SEQUENTIAL", "SECURE_RANDOM"]) fun `M generate id with strategyName and tIdSize bits`(strategyName: String) { val isTid128 = listOf(false, true) - val highOrderLowerBound = (DefaultTimeProvider().getDeviceTimestamp() / 1000).shl(32) + val highOrderLowerBound = (timeProvider.getDeviceTimestamp() / 1000).shl(32) isTid128.forEach { tId128b -> val strategy = IdGenerationStrategy.fromName(strategyName, tId128b) val traceIds = (0..4096).map { strategy.generateTraceId() } @@ -56,7 +58,7 @@ internal class IdGenerationStrategyTest { assertThat(hashCode).isEqualTo(traceId.hashCode()) assertThat(checked).doesNotContain(traceId) if (tId128b && strategyName != "SEQUENTIAL") { - val highOrderUpperBound = (DefaultTimeProvider().getDeviceTimestamp() / 1000).shl(32) + val highOrderUpperBound = (timeProvider.getDeviceTimestamp() / 1000).shl(32) val lowOrderUpperBound = Long.MAX_VALUE assertThat(traceId.toLong()).isLessThanOrEqualTo(lowOrderUpperBound) assertThat(traceId.toHighOrderLong()).isLessThanOrEqualTo(highOrderUpperBound) diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/CoreSpanBuilderTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/CoreSpanBuilderTest.kt index c83c9687f8..91b10e5a31 100644 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/CoreSpanBuilderTest.kt +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/CoreSpanBuilderTest.kt @@ -40,6 +40,8 @@ import java.util.stream.Stream @Timeout(5) internal class CoreSpanBuilderTest : DDCoreSpecification() { + private val timeProvider = DefaultTimeProvider() + lateinit var writer: ListWriter lateinit var tracer: CoreTracer @@ -157,9 +159,9 @@ internal class CoreSpanBuilderTest : DDCoreSpecification() { // When // auto-timestamp in nanoseconds - val start = DefaultTimeProvider().getDeviceTimestamp() + val start = timeProvider.getDeviceTimestamp() val secondSpan = tracer.buildSpan(instrumentationName, expectedName).withServiceName("foo").start() - val stop = DefaultTimeProvider().getDeviceTimestamp() + val stop = timeProvider.getDeviceTimestamp() // Then // Give a range of +/- 1 millis diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanTest.kt index a76a82d615..7e15e24aac 100644 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanTest.kt +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanTest.kt @@ -42,6 +42,9 @@ import java.util.concurrent.TimeUnit import java.util.stream.Stream internal class DDSpanTest : DDCoreSpecification() { + + private val timeProvider = DefaultTimeProvider() + private val sampler = RateByServiceTraceSampler() lateinit var writer: ListWriter lateinit var tracer: CoreTracer @@ -143,18 +146,18 @@ internal class DDSpanTest : DDCoreSpecification() { // Given val mod = TimeUnit.MILLISECONDS.toNanos(1) val builder = tracer.buildSpan(instrumentationName, "test") - val start = DefaultTimeProvider().getDeviceElapsedTimeNs() + val start = timeProvider.getDeviceElapsedTimeNs() val span = builder.start() - val between = DefaultTimeProvider().getDeviceElapsedTimeNs() - val betweenDur = DefaultTimeProvider().getDeviceElapsedTimeNs() - between - val total = DefaultTimeProvider().getDeviceElapsedTimeNs() - start + val between = timeProvider.getDeviceElapsedTimeNs() + val betweenDur = timeProvider.getDeviceElapsedTimeNs() - between + val total = timeProvider.getDeviceElapsedTimeNs() - start // When span.finish() // Then val timeDifference = TimeUnit.NANOSECONDS.toSeconds(span.startTime) - - TimeUnit.MILLISECONDS.toSeconds(DefaultTimeProvider().getDeviceTimestamp()) + TimeUnit.MILLISECONDS.toSeconds(timeProvider.getDeviceTimestamp()) assertThat(timeDifference).isLessThan(5) assertThat(span.durationNano).isGreaterThan(betweenDur) assertThat(span.durationNano).isLessThan(total) @@ -166,10 +169,10 @@ internal class DDSpanTest : DDCoreSpecification() { // Given val mod = TimeUnit.MILLISECONDS.toNanos(1) val builder = tracer.buildSpan(instrumentationName, "test") - val start = DefaultTimeProvider().getDeviceElapsedTimeNs() + val start = timeProvider.getDeviceElapsedTimeNs() val span = builder.start() - val between = DefaultTimeProvider().getDeviceElapsedTimeNs() - val betweenDur = DefaultTimeProvider().getDeviceElapsedTimeNs() - between + val between = timeProvider.getDeviceElapsedTimeNs() + val betweenDur = timeProvider.getDeviceElapsedTimeNs() - between // When span.publish() @@ -180,14 +183,14 @@ internal class DDSpanTest : DDCoreSpecification() { // When var finish = span.phasedFinish() - val total = DefaultTimeProvider().getDeviceElapsedTimeNs() - start + val total = timeProvider.getDeviceElapsedTimeNs() - start // Then assertThat(finish).isTrue() assertThat(writer).isEmpty() val actualDurationNano = span.durationNano and Long.MAX_VALUE val timeDifference = TimeUnit.NANOSECONDS.toSeconds(span.startTime) - - TimeUnit.MILLISECONDS.toSeconds(DefaultTimeProvider().getDeviceTimestamp()) + TimeUnit.MILLISECONDS.toSeconds(timeProvider.getDeviceTimestamp()) assertThat(timeDifference).isLessThan(5) assertThat(actualDurationNano).isGreaterThan(betweenDur) assertThat(actualDurationNano).isLessThan(total) @@ -220,20 +223,20 @@ internal class DDSpanTest : DDCoreSpecification() { fun `starting with a timestamp disables nanotime`() { // Given val mod = TimeUnit.MILLISECONDS.toNanos(1) - val start = DefaultTimeProvider().getDeviceTimestamp() + val start = timeProvider.getDeviceTimestamp() val builder = tracer.buildSpan(instrumentationName, "test") - .withStartTimestamp(TimeUnit.MILLISECONDS.toMicros(DefaultTimeProvider().getDeviceTimestamp())) + .withStartTimestamp(TimeUnit.MILLISECONDS.toMicros(timeProvider.getDeviceTimestamp())) val span = builder.start() - val between = DefaultTimeProvider().getDeviceTimestamp() - val betweenDur = DefaultTimeProvider().getDeviceTimestamp() - between + val between = timeProvider.getDeviceTimestamp() + val betweenDur = timeProvider.getDeviceTimestamp() - between // When span.finish() // Then - val total = Math.max(1, DefaultTimeProvider().getDeviceTimestamp() - start) + val total = Math.max(1, timeProvider.getDeviceTimestamp() - start) val timeDifference = TimeUnit.NANOSECONDS.toSeconds(span.startTime) - - TimeUnit.MILLISECONDS.toSeconds(DefaultTimeProvider().getDeviceTimestamp()) + TimeUnit.MILLISECONDS.toSeconds(timeProvider.getDeviceTimestamp()) assertThat(timeDifference).isLessThan(5) assertThat(span.durationNano).isGreaterThanOrEqualTo(TimeUnit.MILLISECONDS.toNanos(betweenDur)) assertThat(span.durationNano).isLessThanOrEqualTo(TimeUnit.MILLISECONDS.toNanos(total)) @@ -245,16 +248,16 @@ internal class DDSpanTest : DDCoreSpecification() { // Given val mod = TimeUnit.MILLISECONDS.toNanos(1) val builder = tracer.buildSpan(instrumentationName, "test") - val start = DefaultTimeProvider().getDeviceTimestamp() + val start = timeProvider.getDeviceTimestamp() val span = builder.start() - val between = DefaultTimeProvider().getDeviceTimestamp() - val betweenDur = DefaultTimeProvider().getDeviceTimestamp() - between + val between = timeProvider.getDeviceTimestamp() + val betweenDur = timeProvider.getDeviceTimestamp() - between // When - span.finish(TimeUnit.MILLISECONDS.toMicros(DefaultTimeProvider().getDeviceTimestamp() + 1)) - val total = DefaultTimeProvider().getDeviceTimestamp() - start + 1 + span.finish(TimeUnit.MILLISECONDS.toMicros(timeProvider.getDeviceTimestamp() + 1)) + val total = timeProvider.getDeviceTimestamp() - start + 1 val timeDifference = TimeUnit.NANOSECONDS.toSeconds(span.startTime) - - TimeUnit.MILLISECONDS.toSeconds(DefaultTimeProvider().getDeviceTimestamp()) + TimeUnit.MILLISECONDS.toSeconds(timeProvider.getDeviceTimestamp()) // Then assertThat(timeDifference).isLessThan(5) diff --git a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumFeature.kt b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumFeature.kt index 462404c787..536cd301d5 100644 --- a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumFeature.kt +++ b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumFeature.kt @@ -26,7 +26,7 @@ import java.util.concurrent.atomic.AtomicBoolean internal class WebViewRumFeature( private val sdkCore: FeatureSdkCore, override val requestFactory: RequestFactory, - internal val nativeRumViewsCache: NativeRumViewsCache = WebViewNativeRumViewsCache() + internal val nativeRumViewsCache: NativeRumViewsCache = WebViewNativeRumViewsCache(sdkCore.timeProvider) ) : StorageBackedFeature, FeatureContextUpdateReceiver { internal var dataWriter: DataWriter = NoOpDataWriter() diff --git a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCache.kt b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCache.kt index b16f19ee42..f13296f81f 100644 --- a/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCache.kt +++ b/features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCache.kt @@ -11,8 +11,8 @@ import java.util.LinkedList import java.util.concurrent.TimeUnit internal class WebViewNativeRumViewsCache( - private val entriesTtlLimitInMs: Long = DATA_PURGE_TTL_LIMIT_IN_MS, - private val timeProvider: TimeProvider + private val timeProvider: TimeProvider, + private val entriesTtlLimitInMs: Long = DATA_PURGE_TTL_LIMIT_IN_MS ) : NativeRumViewsCache { internal val parentViewsHistoryQueue: LinkedList = LinkedList() diff --git a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/WebViewTrackingTest.kt b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/WebViewTrackingTest.kt index 7153c26b10..7248890b74 100644 --- a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/WebViewTrackingTest.kt +++ b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/WebViewTrackingTest.kt @@ -15,6 +15,7 @@ import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.FeatureScope import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.api.feature.StorageBackedFeature +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.api.net.RequestFactory import com.datadog.android.api.storage.EventBatchWriter import com.datadog.android.api.storage.EventType @@ -103,6 +104,9 @@ internal class WebViewTrackingTest { @Mock lateinit var mockReplayFeatureScope: FeatureScope + @Mock + lateinit var mockTimeProvider: TimeProvider + @BeforeEach fun `set up`() { whenever( @@ -124,6 +128,7 @@ internal class WebViewTrackingTest { mockReplayFeatureScope.unwrap() ) doReturn mockReplayFeature whenever(mockCore.internalLogger) doReturn mockInternalLogger + whenever(mockCore.timeProvider) doReturn mockTimeProvider whenever(mockRumFeature.requestFactory) doReturn mockRumRequestFactory whenever(mockLogsFeature.requestFactory) doReturn mockLogsRequestFactory diff --git a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCacheTest.kt b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCacheTest.kt index 686e2b4716..0e9b535a5d 100644 --- a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCacheTest.kt +++ b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCacheTest.kt @@ -31,6 +31,8 @@ import java.util.concurrent.atomic.AtomicLong @ForgeConfiguration(Configurator::class) internal class WebViewNativeRumViewsCacheTest { + private val timeProvider = DefaultTimeProvider() + private lateinit var fakeIdGenerator: FakeIdGenerator private lateinit var fakeClock: FakeClock private lateinit var testedCache: WebViewNativeRumViewsCache @@ -41,7 +43,7 @@ internal class WebViewNativeRumViewsCacheTest { fun `set up`(forge: Forge) { fakeClock = FakeClock() fakeIdGenerator = FakeIdGenerator(forge) - testedCache = WebViewNativeRumViewsCache() + testedCache = WebViewNativeRumViewsCache(timeProvider) } @Test @@ -165,11 +167,11 @@ internal class WebViewNativeRumViewsCacheTest { ) { // Given val entriesTtlLimitInMs = TimeUnit.SECONDS.toMillis(1) - testedCache = WebViewNativeRumViewsCache(entriesTtlLimitInMs) + testedCache = WebViewNativeRumViewsCache(timeProvider, entriesTtlLimitInMs) val fakeOldEntries = forge.aList(size = forge.anInt(min = 1, max = 10)) { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeIdGenerator.generateId(), - WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to DefaultTimeProvider().getDeviceTimestamp(), + WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to timeProvider.getDeviceTimestamp(), WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY to forge.aBool() ) } @@ -185,7 +187,7 @@ internal class WebViewNativeRumViewsCacheTest { ) { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeIdGenerator.generateId(), - WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to DefaultTimeProvider().getDeviceTimestamp(), + WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to timeProvider.getDeviceTimestamp(), WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY to forge.aBool() ) } @@ -247,7 +249,7 @@ internal class WebViewNativeRumViewsCacheTest { val fakeEntries = forge.aList(size = forge.anInt(min = 1, max = 10)) { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeViewId, - WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to DefaultTimeProvider().getDeviceTimestamp(), + WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to timeProvider.getDeviceTimestamp(), WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY to forge.aBool() ) } diff --git a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTracerProviderTest.kt b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTracerProviderTest.kt index b7bd3c3056..9396763add 100644 --- a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTracerProviderTest.kt +++ b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTracerProviderTest.kt @@ -60,6 +60,7 @@ import kotlin.system.measureNanoTime @MockitoSettings(strictness = Strictness.LENIENT) internal class OtelTracerProviderTest { + private val timeProvider = DefaultTimeProvider() private lateinit var stubSdkCore: StubSDKCore private lateinit var blockingWriterWrapper: BlockingWriterWrapper @@ -449,7 +450,7 @@ internal class OtelTracerProviderTest { forge: Forge ) { // Given - val fakeStartTimestamp = DefaultTimeProvider().getDeviceTimestamp() - forge.aPositiveLong() + val fakeStartTimestamp = timeProvider.getDeviceTimestamp() - forge.aPositiveLong() val testedProvider = OtelTracerProvider.Builder(stubSdkCore).build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() @@ -869,10 +870,10 @@ internal class OtelTracerProviderTest { val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() // When - val startNanos = DefaultTimeProvider().getDeviceElapsedTimeNs() + val startNanos = timeProvider.getDeviceElapsedTimeNs() var spansCounter = 0 while ( - (DefaultTimeProvider().getDeviceElapsedTimeNs() - startNanos) < ONE_SECOND_AS_NANOS && (spansCounter < 200) + (timeProvider.getDeviceElapsedTimeNs() - startNanos) < ONE_SECOND_AS_NANOS && (spansCounter < 200) ) { tracer.spanBuilder(forge.anAlphabeticalString()).startSpan().end() spansCounter++ @@ -910,10 +911,10 @@ internal class OtelTracerProviderTest { val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() // When - val startNanos = DefaultTimeProvider().getDeviceElapsedTimeNs() + val startNanos = timeProvider.getDeviceElapsedTimeNs() var spansCounter = 0 while ( - (DefaultTimeProvider().getDeviceElapsedTimeNs() - startNanos) < ONE_SECOND_AS_NANOS && (spansCounter < 200) + (timeProvider.getDeviceElapsedTimeNs() - startNanos) < ONE_SECOND_AS_NANOS && (spansCounter < 200) ) { tracer.spanBuilder(forge.anAlphabeticalString()).startSpan().end() spansCounter++ @@ -948,10 +949,10 @@ internal class OtelTracerProviderTest { val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() // When - val startNanos = DefaultTimeProvider().getDeviceElapsedTimeNs() + val startNanos = timeProvider.getDeviceElapsedTimeNs() var spansCounter = 0 while ( - (DefaultTimeProvider().getDeviceElapsedTimeNs() - startNanos < (ONE_SECOND_AS_NANOS * 2)) && + (timeProvider.getDeviceElapsedTimeNs() - startNanos < (ONE_SECOND_AS_NANOS * 2)) && (spansCounter < 200) ) { tracer.spanBuilder(forge.anAlphabeticalString()).startSpan().end() diff --git a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/room/RoomDataSource.kt b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/room/RoomDataSource.kt index c2f7b14c54..ef94408885 100644 --- a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/room/RoomDataSource.kt +++ b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/room/RoomDataSource.kt @@ -7,7 +7,6 @@ package com.datadog.android.sample.data.db.room import android.content.Context -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.sample.data.db.DataSource import com.datadog.android.sample.data.model.Log import com.datadog.android.sample.data.model.LogAttributes @@ -20,7 +19,6 @@ import java.util.concurrent.TimeUnit internal class RoomDataSource(val context: Context) : DataSource { private val logDao = LogsDatabase.getInstance(context).logDao() - private val timeProvider = DefaultTimeProvider() // region LocalDataSource @@ -39,7 +37,7 @@ internal class RoomDataSource(val context: Context) : DataSource { // region Internal private fun insertLogs(logs: List) { - val currentTimeInMillis = timeProvider.getDeviceTimestamp() + val currentTimeInMillis = System.currentTimeMillis() val minTtlRequired = currentTimeInMillis - LOGS_EXPIRING_TTL_IN_MS // purge data first logDao.purge(minTtlRequired) @@ -58,7 +56,7 @@ internal class RoomDataSource(val context: Context) : DataSource { private val fetchLogsCallable = Callable> { val minTtlRequired = - timeProvider.getDeviceTimestamp() - LOGS_EXPIRING_TTL_IN_MS + System.currentTimeMillis() - LOGS_EXPIRING_TTL_IN_MS logDao.getAll(minTtlRequired).map { Log( id = it.uid, diff --git a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/sqldelight/SqlDelightDataSource.kt b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/sqldelight/SqlDelightDataSource.kt index 2621394a33..e7fccd8f6b 100644 --- a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/sqldelight/SqlDelightDataSource.kt +++ b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/sqldelight/SqlDelightDataSource.kt @@ -7,7 +7,6 @@ package com.datadog.android.sample.data.db.sqldelight import android.content.Context -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.sample.data.db.DataSource import com.datadog.android.sample.data.model.Log import com.datadog.android.sample.data.model.LogAttributes @@ -22,7 +21,6 @@ import java.util.concurrent.TimeUnit internal class SqlDelightDataSource(val context: Context) : DataSource { private val logsDatabase = Database.getInstance(context) - private val timeProvider = DefaultTimeProvider() // region LocalDataSource @@ -41,7 +39,7 @@ internal class SqlDelightDataSource(val context: Context) : DataSource { // region Internal private fun insertLogs(logs: List) { - val currentTimeInMillis = timeProvider.getDeviceTimestamp() + val currentTimeInMillis = System.currentTimeMillis() val minTtlRequired = currentTimeInMillis - LOGS_EXPIRING_TTL_IN_MS // purge data first logsDatabase.logsQueries.purgeLogs(minTtlRequired) @@ -64,7 +62,7 @@ internal class SqlDelightDataSource(val context: Context) : DataSource { private val fetchLogsCallable = Callable> { val minTtlRequired = - timeProvider.getDeviceTimestamp() - LOGS_EXPIRING_TTL_IN_MS + System.currentTimeMillis() - LOGS_EXPIRING_TTL_IN_MS logsDatabase.logsQueries.getLogs(minTtlRequired).executeAsList().map { Log( it._id, diff --git a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/sqlite/SQLiteDataSource.kt b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/sqlite/SQLiteDataSource.kt index e90e008c51..17ee16a9f8 100644 --- a/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/sqlite/SQLiteDataSource.kt +++ b/sample/kotlin/src/main/kotlin/com/datadog/android/sample/data/db/sqlite/SQLiteDataSource.kt @@ -21,7 +21,6 @@ import java.util.concurrent.Callable import java.util.concurrent.TimeUnit internal class SQLiteDataSource(val context: Context) : DataSource { - private val timeProvider = DefaultTimeProvider() // region LocalDataSource @@ -40,7 +39,7 @@ internal class SQLiteDataSource(val context: Context) : DataSource { // region Internal private fun insertLogs(logs: List) { - val currentTimeInMillis = timeProvider.getDeviceTimestamp() + val currentTimeInMillis = System.currentTimeMillis() val minTtlRequired = currentTimeInMillis - LOGS_EXPIRING_TTL_IN_MS // purge data first purgeData(minTtlRequired) @@ -75,7 +74,7 @@ internal class SQLiteDataSource(val context: Context) : DataSource { ) val whereClause = "${DatadogDbContract.Logs.COLUMN_NAME_TTL} >= ?" val minTtlRequired = - timeProvider.getDeviceTimestamp() - LOGS_EXPIRING_TTL_IN_MS + System.currentTimeMillis() - LOGS_EXPIRING_TTL_IN_MS val whereClauseArg = arrayOf(minTtlRequired.toString()) val cursor = context.contentResolver.query( diff --git a/tools/benchmark/src/main/java/com/datadog/benchmark/internal/reader/FpsVitalReader.kt b/tools/benchmark/src/main/java/com/datadog/benchmark/internal/reader/FpsVitalReader.kt index e519fc643f..05dab653d6 100644 --- a/tools/benchmark/src/main/java/com/datadog/benchmark/internal/reader/FpsVitalReader.kt +++ b/tools/benchmark/src/main/java/com/datadog/benchmark/internal/reader/FpsVitalReader.kt @@ -11,13 +11,14 @@ import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import java.util.concurrent.TimeUnit -internal class FpsVitalReader : VitalReader { +internal class FpsVitalReader( + private val timeProvider: TimeProvider = DefaultTimeProvider() +) : VitalReader { private var currentFps: Double = 0.0 private var frameCount = 0 private var lastFrameTime: Long = 0 private val intervalMs = FPS_SAMPLE_INTERVAL_IN_MS - private val timeProvider: TimeProvider = DefaultTimeProvider() private val frameCallback = object : Choreographer.FrameCallback { override fun doFrame(frameTimeNanos: Long) { From bde228ef4c3a3009df6a8a38c3c2b83ed1f3b98d Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Tue, 18 Nov 2025 20:06:48 +0000 Subject: [PATCH 04/29] RUM-10363: Fix detekt --- .../kotlin/com/datadog/android/core/internal/CoreFeature.kt | 5 +++-- .../com/datadog/android/webview/WebViewTrackingTest.kt | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt index 9236e0ccdb..46c3f9fe31 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt @@ -461,7 +461,7 @@ internal class CoreFeature( } else { appContext } - kronosClock = AndroidClockFactory.createKronosClock( + val clock = AndroidClockFactory.createKronosClock( safeContext, ntpHosts = listOf( DatadogNtpEndpoint.NTP_0, @@ -488,8 +488,9 @@ internal class CoreFeature( } } + kronosClock = clock // Switch to KronosTimeProvider for NTP-synced time - timeProvider = KronosTimeProvider(kronosClock!!, DefaultTimeProvider()) + timeProvider = KronosTimeProvider(clock, DefaultTimeProvider()) } @RequiresApi(Build.VERSION_CODES.N) diff --git a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/WebViewTrackingTest.kt b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/WebViewTrackingTest.kt index 7248890b74..bb869dac6a 100644 --- a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/WebViewTrackingTest.kt +++ b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/WebViewTrackingTest.kt @@ -15,12 +15,12 @@ import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.FeatureScope import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.api.feature.StorageBackedFeature -import com.datadog.android.internal.time.TimeProvider import com.datadog.android.api.net.RequestFactory import com.datadog.android.api.storage.EventBatchWriter import com.datadog.android.api.storage.EventType import com.datadog.android.api.storage.NoOpDataWriter import com.datadog.android.api.storage.RawBatchEvent +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import com.datadog.android.utils.verifyLog import com.datadog.android.webview.internal.DatadogEventBridge From d95c227b943343f5afe81b45adf71a9aa7d63f7c Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Tue, 18 Nov 2025 20:12:53 +0000 Subject: [PATCH 05/29] RUM-10363: Fix documentation --- .../datadog/android/core/stub/StubScheduledExecutorService.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/reliability/stub-core/src/main/kotlin/com/datadog/android/core/stub/StubScheduledExecutorService.kt b/reliability/stub-core/src/main/kotlin/com/datadog/android/core/stub/StubScheduledExecutorService.kt index 4030db2ea8..38d6ee1d91 100644 --- a/reliability/stub-core/src/main/kotlin/com/datadog/android/core/stub/StubScheduledExecutorService.kt +++ b/reliability/stub-core/src/main/kotlin/com/datadog/android/core/stub/StubScheduledExecutorService.kt @@ -151,11 +151,12 @@ class StubScheduledExecutorService(executorContext: String) : ScheduledExecutorS * @param V the type of the task's result * @property delegate the delegate [FutureTask] * @property triggerTimestamp the timestamp at which the task should be triggered + * @property nowProvider a function that provides the current timestamp in milliseconds */ class ScheduledFutureTask( val delegate: FutureTask, val triggerTimestamp: Long, - private val nowProvider: () -> Long + val nowProvider: () -> Long ) : RunnableFuture by delegate, ScheduledFuture { override fun compareTo(other: Delayed?): Int { val delay = getDelay(TimeUnit.MILLISECONDS) From 127a3b88228acde945a9815eee7d5dbd5f56a9c0 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Wed, 19 Nov 2025 11:49:19 +0000 Subject: [PATCH 06/29] RUM-10363: Small improvements --- dd-sdk-android-core/api/dd-sdk-android-core.api | 1 - .../com/datadog/android/core/internal/CoreFeature.kt | 5 +++-- .../android/core/internal/NoOpInternalSdkCore.kt | 8 +++----- .../internal/thread/ObservableLinkedBlockingQueue.kt | 6 +++--- .../com/datadog/android/core/time/SdkTimeProvider.kt | 10 ++++++++-- .../datadog/android/core/internal/CoreFeatureTest.kt | 3 ++- .../core/internal/logger/SdkInternalLoggerTest.kt | 6 +++--- .../api/dd-sdk-android-internal.api | 9 +++++++++ .../android/internal/profiler/DDExecutionTimer.kt | 5 ++++- .../com/datadog/android/internal/time/TimeProvider.kt | 2 +- .../com/datadog/android/rum/DdRumContentProvider.kt | 2 +- .../android/flags/internal/model/FlagsStateEntry.kt | 4 ++-- .../log/internal/domain/DatadogLogGeneratorTest.kt | 5 ++--- .../kotlin/com/datadog/android/ndk/NdkTests.kt | 10 ++++++---- .../android/ndk/internal/NdkCrashReportsFeatureTest.kt | 4 ++-- .../networksettled/NetworkSettledMetricResolverTest.kt | 10 +++++----- 16 files changed, 54 insertions(+), 36 deletions(-) diff --git a/dd-sdk-android-core/api/dd-sdk-android-core.api b/dd-sdk-android-core/api/dd-sdk-android-core.api index 4a580183b1..a6b7066fb2 100644 --- a/dd-sdk-android-core/api/dd-sdk-android-core.api +++ b/dd-sdk-android-core/api/dd-sdk-android-core.api @@ -962,7 +962,6 @@ public final class com/datadog/android/core/time/SdkTimeProvider { public final fun elapsed ()J public final fun getProvider ()Lcom/datadog/android/internal/time/TimeProvider; public final fun now ()J - public final fun setProvider (Lcom/datadog/android/internal/time/TimeProvider;)V } public abstract interface class com/datadog/android/event/EventMapper { diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt index 46c3f9fe31..9d4d1dd98b 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt @@ -73,6 +73,7 @@ import com.datadog.android.core.internal.utils.unboundInternalLogger import com.datadog.android.core.persistence.PersistenceStrategy import com.datadog.android.core.thread.FlushableExecutorService import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.NoOpTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.internal.utils.allowThreadDiskReads import com.datadog.android.ndk.internal.DatadogNdkCrashHandler @@ -154,7 +155,7 @@ internal class CoreFeature( DefaultFirstPartyHostHeaderTypeResolver(emptyMap()) internal var networkInfoProvider: NetworkInfoProvider = NoOpNetworkInfoProvider() internal var systemInfoProvider: SystemInfoProvider = NoOpSystemInfoProvider() - internal var timeProvider: TimeProvider = DefaultTimeProvider() + internal var timeProvider: TimeProvider = NoOpTimeProvider() internal var trackingConsentProvider: ConsentProvider = NoOpConsentProvider() internal var userInfoProvider: MutableUserInfoProvider = NoOpMutableUserInfoProvider() internal var accountInfoProvider: MutableAccountInfoProvider = NoOpMutableAccountInfoProvider() @@ -733,7 +734,7 @@ internal class CoreFeature( firstPartyHostHeaderTypeResolver = DefaultFirstPartyHostHeaderTypeResolver(emptyMap()) networkInfoProvider = NoOpNetworkInfoProvider() systemInfoProvider = NoOpSystemInfoProvider() - timeProvider = DefaultTimeProvider() + timeProvider = NoOpTimeProvider() trackingConsentProvider = NoOpConsentProvider() userInfoProvider = NoOpMutableUserInfoProvider() androidInfoProvider = NoOpAndroidInfoProvider() diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpInternalSdkCore.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpInternalSdkCore.kt index 977be7548d..65da90f455 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpInternalSdkCore.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpInternalSdkCore.kt @@ -19,7 +19,7 @@ import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.internal.logger.SdkInternalLogger import com.datadog.android.core.internal.net.DefaultFirstPartyHostHeaderTypeResolver import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver -import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.NoOpTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.privacy.TrackingConsent import com.google.gson.JsonObject @@ -45,11 +45,9 @@ import java.util.concurrent.TimeUnit */ internal object NoOpInternalSdkCore : InternalSdkCore { - private val internalTimeProvider: TimeProvider = DefaultTimeProvider() - override val name: String = "no-op" - override val time: TimeInfo = with(internalTimeProvider.getDeviceTimestamp()) { + override val time: TimeInfo = with(timeProvider.getDeviceTimestamp()) { TimeInfo( deviceTimeNs = TimeUnit.MILLISECONDS.toNanos(this), serverTimeNs = TimeUnit.MILLISECONDS.toNanos(this), @@ -59,7 +57,7 @@ internal object NoOpInternalSdkCore : InternalSdkCore { } override val timeProvider: TimeProvider - get() = internalTimeProvider + get() = NoOpTimeProvider() override val service: String get() = "" diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/ObservableLinkedBlockingQueue.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/ObservableLinkedBlockingQueue.kt index e170f5bf8c..821fcf09e1 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/ObservableLinkedBlockingQueue.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/ObservableLinkedBlockingQueue.kt @@ -6,8 +6,8 @@ package com.datadog.android.core.internal.thread +import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.internal.thread.NamedExecutionUnit -import com.datadog.android.internal.time.DefaultTimeProvider import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicLong @@ -17,12 +17,12 @@ internal open class ObservableLinkedBlockingQueue : LinkedBlockingQueue private val currentTimeProvider: () -> Long constructor( - currentTimeProvider: () -> Long = { DefaultTimeProvider().getDeviceTimestamp() } + currentTimeProvider: () -> Long = { SdkTimeProvider.now() } ) : this(Int.MAX_VALUE, currentTimeProvider) constructor( capacity: Int, - currentTimeProvider: () -> Long = { DefaultTimeProvider().getDeviceTimestamp() } + currentTimeProvider: () -> Long = { SdkTimeProvider.now() } ) : super(capacity) { this.currentTimeProvider = currentTimeProvider } diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/time/SdkTimeProvider.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/time/SdkTimeProvider.kt index 10d2969308..766ce93d75 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/time/SdkTimeProvider.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/time/SdkTimeProvider.kt @@ -6,6 +6,8 @@ package com.datadog.android.core.time +import com.datadog.android.Datadog +import com.datadog.android.core.internal.DatadogCore import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider @@ -16,9 +18,13 @@ object SdkTimeProvider { /** * Returns the [com.datadog.android.internal.time.TimeProvider] currently used by the SDK. + * Falls back to [DefaultTimeProvider] if the SDK is not initialized. */ - @Volatile - var provider: TimeProvider = DefaultTimeProvider() + val provider: TimeProvider + get() { + val sdkCore = Datadog.getInstance() as? DatadogCore + return sdkCore?.coreFeature?.timeProvider ?: DefaultTimeProvider() + } /** * Returns our best estimate of the current wall-clock time in milliseconds. diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/CoreFeatureTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/CoreFeatureTest.kt index 60d9626b9d..264afde303 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/CoreFeatureTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/CoreFeatureTest.kt @@ -37,6 +37,7 @@ import com.datadog.android.core.internal.user.NoOpMutableUserInfoProvider import com.datadog.android.core.persistence.PersistenceStrategy import com.datadog.android.core.thread.FlushableExecutorService import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.NoOpTimeProvider import com.datadog.android.ndk.internal.DatadogNdkCrashHandler import com.datadog.android.ndk.internal.NoOpNdkCrashHandler import com.datadog.android.privacy.TrackingConsent @@ -1310,7 +1311,7 @@ internal class CoreFeatureTest { assertThat(testedFeature.systemInfoProvider) .isInstanceOf(NoOpSystemInfoProvider::class.java) assertThat(testedFeature.timeProvider) - .isInstanceOf(DefaultTimeProvider::class.java) + .isInstanceOf(NoOpTimeProvider::class.java) assertThat(testedFeature.trackingConsentProvider) .isInstanceOf(NoOpConsentProvider::class.java) assertThat(testedFeature.userInfoProvider) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/logger/SdkInternalLoggerTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/logger/SdkInternalLoggerTest.kt index 9c6df3c809..e921117b95 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/logger/SdkInternalLoggerTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/logger/SdkInternalLoggerTest.kt @@ -14,9 +14,9 @@ import com.datadog.android.api.feature.FeatureScope import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.core.internal.metrics.MethodCalledTelemetry import com.datadog.android.core.metrics.TelemetryMetricType +import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.internal.attributes.LocalAttribute import com.datadog.android.internal.telemetry.InternalTelemetryEvent -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.utils.forge.Configurator import com.datadog.tools.unit.forge.aThrowable import com.datadog.tools.unit.forge.exhaustiveAttributes @@ -767,7 +767,7 @@ internal class SdkInternalLoggerTest { @StringForgery fakeOperation: String ) { // Given - val startNs = DefaultTimeProvider().getDeviceElapsedTimeNs() + val startNs = SdkTimeProvider.elapsed() // When val result = testedInternalLogger.startPerformanceMeasure( @@ -776,7 +776,7 @@ internal class SdkInternalLoggerTest { 100f, fakeOperation ) - val endNs = DefaultTimeProvider().getDeviceElapsedTimeNs() + val endNs = SdkTimeProvider.elapsed() // Then val methodCalledTelemetry = result as? MethodCalledTelemetry diff --git a/dd-sdk-android-internal/api/dd-sdk-android-internal.api b/dd-sdk-android-internal/api/dd-sdk-android-internal.api index e1da327213..f88dee177a 100644 --- a/dd-sdk-android-internal/api/dd-sdk-android-internal.api +++ b/dd-sdk-android-internal/api/dd-sdk-android-internal.api @@ -262,6 +262,15 @@ public final class com/datadog/android/internal/time/DefaultTimeProvider : com/d public fun getServerTimestamp ()J } +public final class com/datadog/android/internal/time/NoOpTimeProvider : com/datadog/android/internal/time/TimeProvider { + public fun ()V + public fun getDeviceElapsedTimeNs ()J + public fun getDeviceTimestamp ()J + public fun getServerOffsetMillis ()J + public fun getServerOffsetNanos ()J + public fun getServerTimestamp ()J +} + public abstract interface class com/datadog/android/internal/time/TimeProvider { public abstract fun getDeviceElapsedTimeNs ()J public abstract fun getDeviceTimestamp ()J diff --git a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/profiler/DDExecutionTimer.kt b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/profiler/DDExecutionTimer.kt index 8d9e36ac78..19d69cda37 100644 --- a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/profiler/DDExecutionTimer.kt +++ b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/profiler/DDExecutionTimer.kt @@ -12,8 +12,11 @@ import com.datadog.android.internal.time.TimeProvider internal class DDExecutionTimer( private val track: String, private val benchmarkSdkUploads: BenchmarkSdkUploads = GlobalBenchmark.getBenchmarkSdkUploads(), - private val timeProvider: TimeProvider = DefaultTimeProvider() + timeProviderFactory: () -> TimeProvider = { DefaultTimeProvider() } ) : ExecutionTimer { + + private val timeProvider: TimeProvider by lazy(timeProviderFactory) + override fun measure(action: () -> T): T { if (track.isEmpty()) { return action() diff --git a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/TimeProvider.kt b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/TimeProvider.kt index 796fdf6446..8374071c15 100644 --- a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/TimeProvider.kt +++ b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/TimeProvider.kt @@ -11,7 +11,7 @@ import com.datadog.tools.annotation.NoOpImplementation /** * Interface to provide the current time in both device and server time references. */ -@NoOpImplementation +@NoOpImplementation(publicNoOpImplementation = true) interface TimeProvider { /** diff --git a/dd-sdk-android-internal/src/main/java/com/datadog/android/rum/DdRumContentProvider.kt b/dd-sdk-android-internal/src/main/java/com/datadog/android/rum/DdRumContentProvider.kt index 09966f75db..cae4c9f320 100644 --- a/dd-sdk-android-internal/src/main/java/com/datadog/android/rum/DdRumContentProvider.kt +++ b/dd-sdk-android-internal/src/main/java/com/datadog/android/rum/DdRumContentProvider.kt @@ -78,6 +78,6 @@ class DdRumContentProvider : ContentProvider() { /** * fallback for APIs below Android N, see [DefaultAppStartTimeProvider]. */ - val createTimeNs: Long = DefaultTimeProvider().getDeviceElapsedTimeNs() + val createTimeNs: Long by lazy { DefaultTimeProvider().getDeviceElapsedTimeNs() } } } diff --git a/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/model/FlagsStateEntry.kt b/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/model/FlagsStateEntry.kt index 66bd49f70d..dd14bae016 100644 --- a/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/model/FlagsStateEntry.kt +++ b/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/model/FlagsStateEntry.kt @@ -6,11 +6,11 @@ package com.datadog.android.flags.internal.model +import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.flags.model.EvaluationContext -import com.datadog.android.internal.time.DefaultTimeProvider internal data class FlagsStateEntry( val evaluationContext: EvaluationContext, val flags: Map, - val lastUpdateTimestamp: Long = DefaultTimeProvider().getDeviceTimestamp() + val lastUpdateTimestamp: Long = SdkTimeProvider.now() ) diff --git a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/DatadogLogGeneratorTest.kt b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/DatadogLogGeneratorTest.kt index 3ab72e717a..3e0bf031f9 100644 --- a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/DatadogLogGeneratorTest.kt +++ b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/DatadogLogGeneratorTest.kt @@ -12,6 +12,7 @@ import com.datadog.android.api.context.NetworkInfo import com.datadog.android.api.context.UserInfo import com.datadog.android.api.feature.Feature import com.datadog.android.core.feature.event.ThreadDump +import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.log.LogAttributes import com.datadog.android.log.assertj.LogEventAssert.Companion.assertThat @@ -44,8 +45,6 @@ import java.util.UUID @ForgeConfiguration(Configurator::class) internal class DatadogLogGeneratorTest { - private val timeProvider = DefaultTimeProvider() - lateinit var testedLogGenerator: DatadogLogGenerator lateinit var fakeServiceName: String @@ -90,7 +89,7 @@ internal class DatadogLogGeneratorTest { fakeAttributes = forge.aMap { anAlphabeticalString() to anInt() } fakeTags = forge.aList { anAlphabeticalString() }.toSet() fakeThrowable = forge.aThrowable() - fakeTimestamp = timeProvider.getDeviceTimestamp() + fakeTimestamp = SdkTimeProvider.now() fakeThreadName = forge.anAlphabeticalString() fakeTimeOffset = forge.aLong( min = -fakeTimestamp, diff --git a/features/dd-sdk-android-ndk/src/androidTest/kotlin/com/datadog/android/ndk/NdkTests.kt b/features/dd-sdk-android-ndk/src/androidTest/kotlin/com/datadog/android/ndk/NdkTests.kt index 950d001cee..d883a93e28 100644 --- a/features/dd-sdk-android-ndk/src/androidTest/kotlin/com/datadog/android/ndk/NdkTests.kt +++ b/features/dd-sdk-android-ndk/src/androidTest/kotlin/com/datadog/android/ndk/NdkTests.kt @@ -7,7 +7,8 @@ package com.datadog.android.ndk import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.core.time.SdkTimeProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.tools.unit.ConditionWatcher import com.datadog.tools.unit.assertj.JsonObjectAssert.Companion.assertThat import com.google.gson.JsonParser @@ -18,7 +19,6 @@ import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder import org.junit.runner.RunWith -import java.lang.RuntimeException import java.nio.charset.Charset import java.util.concurrent.TimeUnit @@ -36,6 +36,8 @@ class NdkTests { System.loadLibrary("datadog-native-lib") System.loadLibrary("datadog-native-lib-test") } + + val timeProvider: TimeProvider = SdkTimeProvider.provider } @Test @@ -62,11 +64,11 @@ class NdkTests { updateTrackingConsent(1) val fakeAppStartTimeMs = forge.aLong( min = 0L, - max = DefaultTimeProvider().getDeviceTimestamp() + max = timeProvider.getDeviceTimestamp() ) updateAppStartTime(fakeAppStartTimeMs) - val expectedTimestamp = DefaultTimeProvider().getDeviceTimestamp() + val expectedTimestamp = timeProvider.getDeviceTimestamp() val expectedTimeSinceAppStartMs = expectedTimestamp - fakeAppStartTimeMs simulateSignalInterception( fakeSignal, diff --git a/features/dd-sdk-android-ndk/src/test/kotlin/com/datadog/android/ndk/internal/NdkCrashReportsFeatureTest.kt b/features/dd-sdk-android-ndk/src/test/kotlin/com/datadog/android/ndk/internal/NdkCrashReportsFeatureTest.kt index dff95f082e..882886bba6 100644 --- a/features/dd-sdk-android-ndk/src/test/kotlin/com/datadog/android/ndk/internal/NdkCrashReportsFeatureTest.kt +++ b/features/dd-sdk-android-ndk/src/test/kotlin/com/datadog/android/ndk/internal/NdkCrashReportsFeatureTest.kt @@ -9,7 +9,7 @@ package com.datadog.android.ndk.internal import android.content.Context import com.datadog.android.api.InternalLogger import com.datadog.android.core.InternalSdkCore -import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.privacy.TrackingConsent import com.datadog.tools.unit.setFieldValue import fr.xgouchet.elmyr.junit5.ForgeExtension @@ -51,7 +51,7 @@ class NdkCrashReportsFeatureTest { @BeforeEach fun `set up`() { - whenever(mockSdkCore.timeProvider) doReturn DefaultTimeProvider() + whenever(mockSdkCore.timeProvider) doReturn SdkTimeProvider.provider testedFeature = NdkCrashReportsFeature(mockSdkCore) } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/metric/networksettled/NetworkSettledMetricResolverTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/metric/networksettled/NetworkSettledMetricResolverTest.kt index 183dde12b1..d165bbe84e 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/metric/networksettled/NetworkSettledMetricResolverTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/metric/networksettled/NetworkSettledMetricResolverTest.kt @@ -7,6 +7,7 @@ package com.datadog.android.rum.metric.networksettled import com.datadog.android.api.InternalLogger +import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.internal.metric.NoValueReason import com.datadog.android.rum.internal.metric.ViewInitializationMetricsConfig @@ -47,14 +48,13 @@ internal class NetworkSettledMetricResolverTest { @Mock lateinit var mockInitialResourceIdentifier: InitialResourceIdentifier - private val timeProvider = DefaultTimeProvider() private var fakeViewStartTime: Long = 0L // region SetUp @BeforeEach fun `set up`() { - fakeViewStartTime = timeProvider.getDeviceElapsedTimeNs() + fakeViewStartTime = SdkTimeProvider.elapsed() testedMetric = NetworkSettledMetricResolver(mockInitialResourceIdentifier, mockInternalLogger) testedMetric.viewWasCreated(fakeViewStartTime) whenever(mockInitialResourceIdentifier.validate(any())).thenReturn(true) @@ -93,7 +93,7 @@ internal class NetworkSettledMetricResolverTest { @Test fun `M return the correct metric W resolveMetric(){ only one resource registered }`(forge: Forge) { // Given - val startTimestamp = timeProvider.getDeviceElapsedTimeNs() + val startTimestamp = SdkTimeProvider.elapsed() val stopTimestamp = startTimestamp + forge.aLong(min = 1, max = 1000) val fakeResourceId = forge.getForgery().toString() testedMetric.resourceWasStarted(InternalResourceContext(fakeResourceId, startTimestamp)) @@ -198,7 +198,7 @@ internal class NetworkSettledMetricResolverTest { fun `M return null W resolveMetric(){ view not created }`(forge: Forge) { // Given testedMetric = NetworkSettledMetricResolver(mockInitialResourceIdentifier, mockInternalLogger) - val startTimestamp = timeProvider.getDeviceElapsedTimeNs() + val startTimestamp = SdkTimeProvider.elapsed() val stopTimestamp = startTimestamp + forge.aLong(min = 1, max = 1000) val fakeResourceId = forge.getForgery().toString() testedMetric.resourceWasStarted(InternalResourceContext(fakeResourceId, startTimestamp)) @@ -247,7 +247,7 @@ internal class NetworkSettledMetricResolverTest { fun `M pass the viewCreatedTimestamp to validator W resourceWasStarted()`(forge: Forge) { // Given testedMetric = NetworkSettledMetricResolver(mockInitialResourceIdentifier, mockInternalLogger) - val startTimestamp = timeProvider.getDeviceElapsedTimeNs() + val startTimestamp = SdkTimeProvider.elapsed() val expectedViewCreatedTimestamp: Long? if (forge.aBool()) { testedMetric.viewWasCreated(fakeViewStartTime) From 4fea2b566dd4cd0b33e64f4c9a815eea19aa2c42 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Wed, 19 Nov 2025 12:24:48 +0000 Subject: [PATCH 07/29] RUM-10363: Remove NoOpTimeProvider --- .../android/core/internal/CoreFeature.kt | 13 +++++------- .../core/internal/NoOpInternalSdkCore.kt | 4 ++-- .../core/internal/time/KronosTimeProvider.kt | 21 +++++++------------ .../datadog/android/core/DatadogCoreTest.kt | 2 +- .../android/core/internal/CoreFeatureTest.kt | 3 +-- .../internal/time/KronosTimeProviderTest.kt | 11 ++++------ .../api/dd-sdk-android-internal.api | 14 +++++-------- .../internal/time/DefaultTimeProvider.kt | 6 ++---- .../android/internal/time/TimeProvider.kt | 15 ++++++++----- 9 files changed, 38 insertions(+), 51 deletions(-) diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt index 9d4d1dd98b..06aa2b35dd 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt @@ -73,7 +73,6 @@ import com.datadog.android.core.internal.utils.unboundInternalLogger import com.datadog.android.core.persistence.PersistenceStrategy import com.datadog.android.core.thread.FlushableExecutorService import com.datadog.android.internal.time.DefaultTimeProvider -import com.datadog.android.internal.time.NoOpTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.internal.utils.allowThreadDiskReads import com.datadog.android.ndk.internal.DatadogNdkCrashHandler @@ -155,7 +154,7 @@ internal class CoreFeature( DefaultFirstPartyHostHeaderTypeResolver(emptyMap()) internal var networkInfoProvider: NetworkInfoProvider = NoOpNetworkInfoProvider() internal var systemInfoProvider: SystemInfoProvider = NoOpSystemInfoProvider() - internal var timeProvider: TimeProvider = NoOpTimeProvider() + internal var timeProvider: TimeProvider = DefaultTimeProvider() internal var trackingConsentProvider: ConsentProvider = NoOpConsentProvider() internal var userInfoProvider: MutableUserInfoProvider = NoOpMutableUserInfoProvider() internal var accountInfoProvider: MutableAccountInfoProvider = NoOpMutableAccountInfoProvider() @@ -462,7 +461,7 @@ internal class CoreFeature( } else { appContext } - val clock = AndroidClockFactory.createKronosClock( + kronosClock = AndroidClockFactory.createKronosClock( safeContext, ntpHosts = listOf( DatadogNtpEndpoint.NTP_0, @@ -487,11 +486,9 @@ internal class CoreFeature( ) } } - } - kronosClock = clock - // Switch to KronosTimeProvider for NTP-synced time - timeProvider = KronosTimeProvider(clock, DefaultTimeProvider()) + timeProvider = KronosTimeProvider(this) + } } @RequiresApi(Build.VERSION_CODES.N) @@ -734,7 +731,7 @@ internal class CoreFeature( firstPartyHostHeaderTypeResolver = DefaultFirstPartyHostHeaderTypeResolver(emptyMap()) networkInfoProvider = NoOpNetworkInfoProvider() systemInfoProvider = NoOpSystemInfoProvider() - timeProvider = NoOpTimeProvider() + timeProvider = DefaultTimeProvider() trackingConsentProvider = NoOpConsentProvider() userInfoProvider = NoOpMutableUserInfoProvider() androidInfoProvider = NoOpAndroidInfoProvider() diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpInternalSdkCore.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpInternalSdkCore.kt index 65da90f455..c1c7ff4327 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpInternalSdkCore.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/NoOpInternalSdkCore.kt @@ -19,7 +19,7 @@ import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.internal.logger.SdkInternalLogger import com.datadog.android.core.internal.net.DefaultFirstPartyHostHeaderTypeResolver import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver -import com.datadog.android.internal.time.NoOpTimeProvider +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.privacy.TrackingConsent import com.google.gson.JsonObject @@ -57,7 +57,7 @@ internal object NoOpInternalSdkCore : InternalSdkCore { } override val timeProvider: TimeProvider - get() = NoOpTimeProvider() + get() = DefaultTimeProvider() override val service: String get() = "" diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/KronosTimeProvider.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/KronosTimeProvider.kt index 22d60365ec..44c3025b15 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/KronosTimeProvider.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/time/KronosTimeProvider.kt @@ -10,28 +10,23 @@ import com.datadog.android.internal.time.TimeProvider import com.lyft.kronos.Clock import java.util.concurrent.TimeUnit +/** + * A [TimeProvider] implementation that uses Kronos NTP for server time synchronization. + * + * Device timestamp and elapsed time are inherited from [TimeProvider] default implementations. + */ internal class KronosTimeProvider( - private val clock: Clock, - private val deviceTimeProvider: TimeProvider + private val clock: Clock ) : TimeProvider { - override fun getDeviceTimestamp(): Long { - return deviceTimeProvider.getDeviceTimestamp() - } - override fun getServerTimestamp(): Long { return clock.getCurrentTimeMs() } - override fun getDeviceElapsedTimeNs(): Long { - return deviceTimeProvider.getDeviceElapsedTimeNs() - } - override fun getServerOffsetMillis(): Long { val server = clock.getCurrentTimeMs() - val device = deviceTimeProvider.getDeviceTimestamp() - val delta = server - device - return delta + val device = getDeviceTimestamp() + return server - device } override fun getServerOffsetNanos(): Long { diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt index 044bff4e8a..4518216232 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt @@ -1015,7 +1015,7 @@ internal class DatadogCoreTest { } @Test - fun `M provide time info without correction W time() {NoOpTimeProvider}`() { + fun `M provide time info without correction W time() {DefaultTimeProvider}`() { // Given testedCore.coreFeature = mock() val timeProvider = DefaultTimeProvider() diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/CoreFeatureTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/CoreFeatureTest.kt index 264afde303..60d9626b9d 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/CoreFeatureTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/CoreFeatureTest.kt @@ -37,7 +37,6 @@ import com.datadog.android.core.internal.user.NoOpMutableUserInfoProvider import com.datadog.android.core.persistence.PersistenceStrategy import com.datadog.android.core.thread.FlushableExecutorService import com.datadog.android.internal.time.DefaultTimeProvider -import com.datadog.android.internal.time.NoOpTimeProvider import com.datadog.android.ndk.internal.DatadogNdkCrashHandler import com.datadog.android.ndk.internal.NoOpNdkCrashHandler import com.datadog.android.privacy.TrackingConsent @@ -1311,7 +1310,7 @@ internal class CoreFeatureTest { assertThat(testedFeature.systemInfoProvider) .isInstanceOf(NoOpSystemInfoProvider::class.java) assertThat(testedFeature.timeProvider) - .isInstanceOf(NoOpTimeProvider::class.java) + .isInstanceOf(DefaultTimeProvider::class.java) assertThat(testedFeature.trackingConsentProvider) .isInstanceOf(NoOpConsentProvider::class.java) assertThat(testedFeature.userInfoProvider) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/KronosTimeProviderTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/KronosTimeProviderTest.kt index b6e186e2aa..4cb7877d3a 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/KronosTimeProviderTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/KronosTimeProviderTest.kt @@ -6,7 +6,6 @@ package com.datadog.android.core.internal.time -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.utils.forge.Configurator import com.lyft.kronos.Clock import fr.xgouchet.elmyr.annotation.Forgery @@ -35,8 +34,6 @@ import java.util.concurrent.TimeUnit @ForgeConfiguration(Configurator::class) internal class KronosTimeProviderTest { - private val timeProvider = DefaultTimeProvider() - lateinit var testedTimeProvider: KronosTimeProvider @Mock @@ -48,7 +45,7 @@ internal class KronosTimeProviderTest { @BeforeEach fun `set up`() { whenever(mockClock.getCurrentTimeMs()) doReturn fakeDate.time - testedTimeProvider = KronosTimeProvider(mockClock, timeProvider) + testedTimeProvider = KronosTimeProvider(mockClock) } @Test @@ -60,7 +57,7 @@ internal class KronosTimeProviderTest { @Test fun `returns server time offset in nanoseconds`() { - val now = timeProvider.getDeviceTimestamp() + val now = testedTimeProvider.getDeviceTimestamp() val result = testedTimeProvider.getServerOffsetNanos() val expectedOffset = TimeUnit.MILLISECONDS.toNanos(fakeDate.time - now) @@ -72,7 +69,7 @@ internal class KronosTimeProviderTest { @Test fun `returns server time offset in milliseconds`() { - val now = timeProvider.getDeviceTimestamp() + val now = testedTimeProvider.getDeviceTimestamp() val result = testedTimeProvider.getServerOffsetMillis() val expectedOffset = fakeDate.time - now @@ -84,7 +81,7 @@ internal class KronosTimeProviderTest { @Test fun `returns device time`() { - val now = timeProvider.getDeviceTimestamp() + val now = testedTimeProvider.getDeviceTimestamp() val result = testedTimeProvider.getDeviceTimestamp() assertThat(result).isCloseTo(now, Offset.offset(TEST_OFFSET)) diff --git a/dd-sdk-android-internal/api/dd-sdk-android-internal.api b/dd-sdk-android-internal/api/dd-sdk-android-internal.api index f88dee177a..5842bbfa25 100644 --- a/dd-sdk-android-internal/api/dd-sdk-android-internal.api +++ b/dd-sdk-android-internal/api/dd-sdk-android-internal.api @@ -262,15 +262,6 @@ public final class com/datadog/android/internal/time/DefaultTimeProvider : com/d public fun getServerTimestamp ()J } -public final class com/datadog/android/internal/time/NoOpTimeProvider : com/datadog/android/internal/time/TimeProvider { - public fun ()V - public fun getDeviceElapsedTimeNs ()J - public fun getDeviceTimestamp ()J - public fun getServerOffsetMillis ()J - public fun getServerOffsetNanos ()J - public fun getServerTimestamp ()J -} - public abstract interface class com/datadog/android/internal/time/TimeProvider { public abstract fun getDeviceElapsedTimeNs ()J public abstract fun getDeviceTimestamp ()J @@ -279,6 +270,11 @@ public abstract interface class com/datadog/android/internal/time/TimeProvider { public abstract fun getServerTimestamp ()J } +public final class com/datadog/android/internal/time/TimeProvider$DefaultImpls { + public static fun getDeviceElapsedTimeNs (Lcom/datadog/android/internal/time/TimeProvider;)J + public static fun getDeviceTimestamp (Lcom/datadog/android/internal/time/TimeProvider;)J +} + public final class com/datadog/android/internal/utils/ByteArrayExtKt { public static final fun toHexString ([B)Ljava/lang/String; } diff --git a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/DefaultTimeProvider.kt b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/DefaultTimeProvider.kt index a21894852f..452b8ead5c 100644 --- a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/DefaultTimeProvider.kt +++ b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/DefaultTimeProvider.kt @@ -9,14 +9,12 @@ package com.datadog.android.internal.time /** * A [TimeProvider] implementation that provides the current device time as both device and server time. * The offsets are always 0. + * + * Device timestamp and elapsed time are inherited from [TimeProvider] default implementations. */ class DefaultTimeProvider : TimeProvider { - override fun getDeviceTimestamp(): Long = System.currentTimeMillis() - override fun getServerTimestamp(): Long = System.currentTimeMillis() - override fun getDeviceElapsedTimeNs(): Long = System.nanoTime() - override fun getServerOffsetNanos(): Long = 0L override fun getServerOffsetMillis(): Long = 0L diff --git a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/TimeProvider.kt b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/TimeProvider.kt index 8374071c15..6467075a60 100644 --- a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/TimeProvider.kt +++ b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/time/TimeProvider.kt @@ -6,18 +6,20 @@ package com.datadog.android.internal.time -import com.datadog.tools.annotation.NoOpImplementation - +// there is no NoOpImplementation on purpose, we don't want to have 0 values for the +// case when this instance is used. /** * Interface to provide the current time in both device and server time references. */ -@NoOpImplementation(publicNoOpImplementation = true) interface TimeProvider { /** * Returns the current device timestamp in milliseconds. + * + * Default implementation returns [System.currentTimeMillis]. + * This should not be overridden as device time always refers to the local system clock. */ - fun getDeviceTimestamp(): Long + fun getDeviceTimestamp(): Long = System.currentTimeMillis() /** * Returns the current server timestamp in milliseconds. @@ -27,8 +29,11 @@ interface TimeProvider { /** * Returns the current device elapsed time in nanoseconds. * This value is monotonic and mirrors [System.nanoTime]. + * + * Default implementation returns [System.nanoTime]. + * This should not be overridden as device elapsed time always refers to the monotonic system clock. */ - fun getDeviceElapsedTimeNs(): Long + fun getDeviceElapsedTimeNs(): Long = System.nanoTime() /** * Returns the offset between the device and server time references in nanoseconds. From a7972b0d7a4515c48645bec04e38e552106d0d93 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Wed, 19 Nov 2025 16:55:23 +0000 Subject: [PATCH 08/29] RUM-10363: Remove SdkTimeProvider.kt, local CI working --- .../api/dd-sdk-android-core.api | 9 +---- .../android/core/internal/CoreFeature.kt | 14 ++++--- .../data/upload/RotatingDnsResolver.kt | 14 ++++--- .../core/internal/logger/SdkInternalLogger.kt | 4 +- .../internal/metrics/MethodCalledTelemetry.kt | 10 +++-- .../file/advanced/ConsentAwareFileMigrator.kt | 13 +++++-- .../file/advanced/FeatureFileOrchestrator.kt | 3 +- .../advanced/MoveDataMigrationOperation.kt | 6 ++- .../advanced/WipeDataMigrationOperation.kt | 6 ++- .../thread/BackPressureExecutorService.kt | 6 ++- .../thread/BackPressuredBlockingQueue.kt | 12 ++++-- .../thread/ObservableLinkedBlockingQueue.kt | 14 +++---- .../internal/thread/ThreadPoolExecutorExt.kt | 12 ++++-- .../android/core/internal/utils/MiscUtils.kt | 14 ++++--- .../core/thread/FlushableExecutorService.kt | 5 ++- .../android/core/time/SdkTimeProvider.kt | 38 ------------------- .../error/internal/DatadogExceptionHandler.kt | 2 +- .../internal/logger/SdkInternalLoggerTest.kt | 5 +-- .../flags/internal/model/FlagsStateEntry.kt | 3 +- .../domain/DatadogLogGeneratorTest.kt | 4 +- .../com/datadog/android/ndk/NdkTests.kt | 11 +----- .../internal/NdkCrashReportsFeatureTest.kt | 2 - .../api/dd-sdk-android-rum.api | 4 +- .../kotlin/com/datadog/android/rum/Rum.kt | 3 +- .../datadog/android/rum/RumConfiguration.kt | 7 +++- .../android/rum/internal/RumFeature.kt | 7 +++- .../android/rum/internal/domain/Time.kt | 28 ++++++-------- .../domain/scope/RumApplicationScope.kt | 13 +++++-- .../internal/domain/scope/RumSessionScope.kt | 9 +++-- .../MainLooperLongTaskStrategy.kt | 7 ++-- .../metric/slowframes/SlowFramesListener.kt | 7 ++-- .../rum/internal/monitor/DatadogRumMonitor.kt | 17 +++++---- .../internal/startup/RumAppStartupDetector.kt | 3 +- .../startup/RumFirstDrawTimeReporter.kt | 3 +- .../rum/resource/RumResourceInputStream.kt | 25 ++++++------ .../internal/domain/scope/RumViewScopeTest.kt | 11 +++--- .../NetworkSettledMetricResolverTest.kt | 23 +++++------ .../async/RecordedDataQueueHandler.kt | 12 ++++-- .../internal/async/RecordedDataQueueItem.kt | 3 +- .../async/ResourceRecordedDataQueueItem.kt | 9 ++++- .../async/SnapshotRecordedDataQueueItem.kt | 7 +++- .../async/TouchEventRecordedDataQueueItem.kt | 7 +++- .../processor/RecordedDataProcessor.kt | 7 ++-- .../internal/recorder/Debouncer.kt | 17 +++++---- .../recorder/DefaultOnDrawListenerProducer.kt | 5 ++- .../recorder/SessionReplayRecorder.kt | 9 +++-- .../callback/RecorderWindowCallback.kt | 13 +++---- .../listener/WindowsOnDrawListener.kt | 5 ++- .../resources/ResourceDataStoreManager.kt | 5 +-- .../rum/integration/ManualTrackingRumTest.kt | 15 ++++---- .../ViewLoadingTimeMetricsTests.kt | 17 ++++----- 51 files changed, 256 insertions(+), 249 deletions(-) delete mode 100644 dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/time/SdkTimeProvider.kt diff --git a/dd-sdk-android-core/api/dd-sdk-android-core.api b/dd-sdk-android-core/api/dd-sdk-android-core.api index a6b7066fb2..e2dcb7817d 100644 --- a/dd-sdk-android-core/api/dd-sdk-android-core.api +++ b/dd-sdk-android-core/api/dd-sdk-android-core.api @@ -954,14 +954,7 @@ public abstract interface class com/datadog/android/core/thread/FlushableExecuto } public abstract interface class com/datadog/android/core/thread/FlushableExecutorService$Factory { - public abstract fun create (Lcom/datadog/android/api/InternalLogger;Ljava/lang/String;Lcom/datadog/android/core/configuration/BackPressureStrategy;)Lcom/datadog/android/core/thread/FlushableExecutorService; -} - -public final class com/datadog/android/core/time/SdkTimeProvider { - public static final field INSTANCE Lcom/datadog/android/core/time/SdkTimeProvider; - public final fun elapsed ()J - public final fun getProvider ()Lcom/datadog/android/internal/time/TimeProvider; - public final fun now ()J + public abstract fun create (Lcom/datadog/android/api/InternalLogger;Ljava/lang/String;Lcom/datadog/android/core/configuration/BackPressureStrategy;Lcom/datadog/android/internal/time/TimeProvider;)Lcom/datadog/android/core/thread/FlushableExecutorService; } public abstract interface class com/datadog/android/event/EventMapper { diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt index 06aa2b35dd..4bb367f32c 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/CoreFeature.kt @@ -135,7 +135,7 @@ internal class CoreFeature( .writeTimeout(NETWORK_TIMEOUT_MS, TimeUnit.MILLISECONDS) .protocols(listOf(Protocol.HTTP_2, Protocol.HTTP_1_1)) .connectionSpecs(listOf(connectionSpec)) - .dns(RotatingDnsResolver()) // NPE cannot happen here + .dns(RotatingDnsResolver(timeProvider = timeProvider)) // NPE cannot happen here .build() } @@ -323,7 +323,7 @@ internal class CoreFeature( } fun createExecutorService(executorContext: String): ExecutorService { - return executorServiceFactory.create(internalLogger, executorContext, backpressureStrategy) + return executorServiceFactory.create(internalLogger, executorContext, backpressureStrategy, timeProvider) } fun createScheduledExecutorService(executorContext: String): ScheduledExecutorService { @@ -645,7 +645,8 @@ internal class CoreFeature( persistenceExecutorService = executorServiceFactory.create( internalLogger = internalLogger, executorContext = "storage", - backPressureStrategy = backpressureStrategy + backPressureStrategy = backpressureStrategy, + timeProvider = timeProvider ) val contextQueue = BackPressuredBlockingQueue( internalLogger, @@ -655,7 +656,8 @@ internal class CoreFeature( // just notify when reached onItemDropped = {}, onThresholdReached = {}, - backpressureMitigation = null + backpressureMitigation = null, + timeProvider = timeProvider ) @Suppress("UnsafeThirdPartyFunctionCall") // all parameters are safe contextExecutorService = ThreadPoolExecutor( @@ -746,8 +748,8 @@ internal class CoreFeature( " process of your application." internal val DEFAULT_FLUSHABLE_EXECUTOR_SERVICE_FACTORY = - FlushableExecutorService.Factory { logger, executorContext, backPressureStrategy -> - BackPressureExecutorService(logger, executorContext, backPressureStrategy) + FlushableExecutorService.Factory { logger, executorContext, backPressureStrategy, timeProvider -> + BackPressureExecutorService(logger, executorContext, backPressureStrategy, timeProvider) } internal val DEFAULT_SCHEDULED_EXECUTOR_SERVICE_FACTORY = diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/data/upload/RotatingDnsResolver.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/data/upload/RotatingDnsResolver.kt index faef621878..dfb8d1551f 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/data/upload/RotatingDnsResolver.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/data/upload/RotatingDnsResolver.kt @@ -6,7 +6,7 @@ package com.datadog.android.core.internal.data.upload -import com.datadog.android.core.time.SdkTimeProvider +import com.datadog.android.internal.time.TimeProvider import okhttp3.Dns import java.net.InetAddress import kotlin.time.Duration @@ -15,17 +15,19 @@ import kotlin.time.Duration.Companion.nanoseconds internal class RotatingDnsResolver( private val delegate: Dns = Dns.SYSTEM, - private val ttl: Duration = TTL_30_MIN + private val ttl: Duration = TTL_30_MIN, + private val timeProvider: TimeProvider ) : Dns { data class ResolvedHost( val hostname: String, - val addresses: MutableList + val addresses: MutableList, + private val timeProvider: TimeProvider ) { - private val resolutionTimestamp: Long = SdkTimeProvider.elapsed() + private val resolutionTimestamp: Long = timeProvider.getDeviceElapsedTimeNs() fun getAge(): Duration { - return (SdkTimeProvider.elapsed() - resolutionTimestamp).nanoseconds + return (timeProvider.getDeviceElapsedTimeNs() - resolutionTimestamp).nanoseconds } fun rotate() { @@ -51,7 +53,7 @@ internal class RotatingDnsResolver( } else { @Suppress("UnsafeThirdPartyFunctionCall") // handled by caller val result = delegate.lookup(hostname) - knownHosts[hostname] = ResolvedHost(hostname, result.toMutableList()) + knownHosts[hostname] = ResolvedHost(hostname, result.toMutableList(), timeProvider) safeCopy(result) } } diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/logger/SdkInternalLogger.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/logger/SdkInternalLogger.kt index 9505a5b00e..85c9b503a9 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/logger/SdkInternalLogger.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/logger/SdkInternalLogger.kt @@ -19,6 +19,7 @@ import com.datadog.android.core.sampling.RateBasedSampler import com.datadog.android.internal.attributes.LocalAttribute import com.datadog.android.internal.attributes.enrichWithNonNullAttribute import com.datadog.android.internal.telemetry.InternalTelemetryEvent +import com.datadog.android.internal.time.DefaultTimeProvider internal class SdkInternalLogger( private val sdkCore: FeatureSdkCore?, @@ -134,7 +135,8 @@ internal class SdkInternalLogger( internalLogger = this, operationName = operationName, callerClass = callerClass, - creationSampleRate = samplingRate + creationSampleRate = samplingRate, + timeProvider = sdkCore?.timeProvider ?: DefaultTimeProvider() ) } } diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetry.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetry.kt index 3efa7ae40b..b5392edd0c 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetry.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetry.kt @@ -10,7 +10,7 @@ import com.datadog.android.api.InternalLogger import com.datadog.android.core.metrics.MethodCallSamplingRate import com.datadog.android.core.metrics.PerformanceMetric import com.datadog.android.core.metrics.PerformanceMetric.Companion.METRIC_TYPE -import com.datadog.android.core.time.SdkTimeProvider +import com.datadog.android.internal.time.TimeProvider /** * Performance metric to measure the execution time for a method. @@ -18,18 +18,20 @@ import com.datadog.android.core.time.SdkTimeProvider * @param operationName the operation name * @param callerClass - the class calling the performance metric. * @param creationSampleRate - sampling frequency used to create the metric - * @param startTime - the time when the metric is instantiated, to be used as the start point for the measurement. + * @param timeProvider - the provider for time measurements. */ internal class MethodCalledTelemetry( internal val internalLogger: InternalLogger, internal val operationName: String, internal val callerClass: String, internal val creationSampleRate: Float, - internal val startTime: Long = SdkTimeProvider.elapsed() + internal val timeProvider: TimeProvider ) : PerformanceMetric { + internal val startTime: Long = timeProvider.getDeviceElapsedTimeNs() + override fun stopAndSend(isSuccessful: Boolean) { - val executionTime = SdkTimeProvider.elapsed() - startTime + val executionTime = timeProvider.getDeviceElapsedTimeNs() - startTime val additionalProperties: MutableMap = mutableMapOf() additionalProperties[EXECUTION_TIME] = executionTime diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/advanced/ConsentAwareFileMigrator.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/advanced/ConsentAwareFileMigrator.kt index a33dc71bee..fa35bbbe9f 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/advanced/ConsentAwareFileMigrator.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/advanced/ConsentAwareFileMigrator.kt @@ -10,11 +10,13 @@ import androidx.annotation.WorkerThread import com.datadog.android.api.InternalLogger import com.datadog.android.core.internal.persistence.file.FileMover import com.datadog.android.core.internal.persistence.file.FileOrchestrator +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.privacy.TrackingConsent internal class ConsentAwareFileMigrator( private val fileMover: FileMover, - private val internalLogger: InternalLogger + private val internalLogger: InternalLogger, + private val timeProvider: TimeProvider ) : DataMigrator { @WorkerThread @@ -47,7 +49,8 @@ internal class ConsentAwareFileMigrator( WipeDataMigrationOperation( previousFileOrchestrator.getRootDir(), fileMover, - internalLogger + internalLogger, + timeProvider ) } @@ -56,7 +59,8 @@ internal class ConsentAwareFileMigrator( WipeDataMigrationOperation( newFileOrchestrator.getRootDir(), fileMover, - internalLogger + internalLogger, + timeProvider ) } @@ -65,7 +69,8 @@ internal class ConsentAwareFileMigrator( previousFileOrchestrator.getRootDir(), newFileOrchestrator.getRootDir(), fileMover, - internalLogger + internalLogger, + timeProvider ) } diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/advanced/FeatureFileOrchestrator.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/advanced/FeatureFileOrchestrator.kt index 43d32d786c..f28efad6b3 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/advanced/FeatureFileOrchestrator.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/advanced/FeatureFileOrchestrator.kt @@ -62,7 +62,8 @@ internal class FeatureFileOrchestrator( ), ConsentAwareFileMigrator( FileMover(internalLogger), - internalLogger + internalLogger, + timeProvider ), executorService, internalLogger diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperation.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperation.kt index e043c35586..db740fb1e8 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperation.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperation.kt @@ -10,6 +10,7 @@ import androidx.annotation.WorkerThread import com.datadog.android.api.InternalLogger import com.datadog.android.core.internal.persistence.file.FileMover import com.datadog.android.core.internal.utils.retryWithDelay +import com.datadog.android.internal.time.TimeProvider import java.io.File import java.util.concurrent.TimeUnit @@ -21,7 +22,8 @@ internal class MoveDataMigrationOperation( internal val fromDir: File?, internal val toDir: File?, internal val fileMover: FileMover, - internal val internalLogger: InternalLogger + internal val internalLogger: InternalLogger, + internal val timeProvider: TimeProvider ) : DataMigrationOperation { @WorkerThread @@ -39,7 +41,7 @@ internal class MoveDataMigrationOperation( { WARN_NULL_DEST_DIR } ) } else { - retryWithDelay(MAX_RETRY, RETRY_DELAY_NS, internalLogger) { + retryWithDelay(MAX_RETRY, RETRY_DELAY_NS, internalLogger, timeProvider) { fileMover.moveFiles(fromDir, toDir) } } diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/advanced/WipeDataMigrationOperation.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/advanced/WipeDataMigrationOperation.kt index cf120e0e14..83c6333b6c 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/advanced/WipeDataMigrationOperation.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/persistence/file/advanced/WipeDataMigrationOperation.kt @@ -10,6 +10,7 @@ import androidx.annotation.WorkerThread import com.datadog.android.api.InternalLogger import com.datadog.android.core.internal.persistence.file.FileMover import com.datadog.android.core.internal.utils.retryWithDelay +import com.datadog.android.internal.time.TimeProvider import java.io.File import java.util.concurrent.TimeUnit @@ -19,7 +20,8 @@ import java.util.concurrent.TimeUnit internal class WipeDataMigrationOperation( internal val targetDir: File?, internal val fileMover: FileMover, - internal val internalLogger: InternalLogger + internal val internalLogger: InternalLogger, + internal val timeProvider: TimeProvider ) : DataMigrationOperation { @WorkerThread @@ -31,7 +33,7 @@ internal class WipeDataMigrationOperation( { WARN_NULL_DIR } ) } else { - retryWithDelay(MAX_RETRY, RETRY_DELAY_NS, internalLogger) { + retryWithDelay(MAX_RETRY, RETRY_DELAY_NS, internalLogger, timeProvider) { fileMover.delete(targetDir) } } diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/BackPressureExecutorService.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/BackPressureExecutorService.kt index a00fcd59a1..729f4d6016 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/BackPressureExecutorService.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/BackPressureExecutorService.kt @@ -9,6 +9,7 @@ package com.datadog.android.core.internal.thread import com.datadog.android.api.InternalLogger import com.datadog.android.core.configuration.BackPressureStrategy import com.datadog.android.core.thread.FlushableExecutorService +import com.datadog.android.internal.time.TimeProvider import java.util.concurrent.ThreadPoolExecutor import java.util.concurrent.TimeUnit @@ -18,13 +19,14 @@ import java.util.concurrent.TimeUnit internal class BackPressureExecutorService( val logger: InternalLogger, executorContext: String, - backpressureStrategy: BackPressureStrategy + backpressureStrategy: BackPressureStrategy, + timeProvider: TimeProvider ) : ThreadPoolExecutor( CORE_POOL_SIZE, CORE_POOL_SIZE, THREAD_POOL_MAX_KEEP_ALIVE_MS, TimeUnit.MILLISECONDS, - BackPressuredBlockingQueue(logger, executorContext, backpressureStrategy), + BackPressuredBlockingQueue(logger, executorContext, backpressureStrategy, timeProvider), DatadogThreadFactory(executorContext) ), FlushableExecutorService { diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/BackPressuredBlockingQueue.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/BackPressuredBlockingQueue.kt index 73f8f464ce..b6c120609f 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/BackPressuredBlockingQueue.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/BackPressuredBlockingQueue.kt @@ -10,6 +10,7 @@ import com.datadog.android.api.InternalLogger import com.datadog.android.core.configuration.BackPressureMitigation import com.datadog.android.core.configuration.BackPressureStrategy import com.datadog.android.internal.thread.NamedExecutionUnit +import com.datadog.android.internal.time.TimeProvider import java.util.concurrent.TimeUnit /** @@ -33,7 +34,8 @@ internal class BackPressuredBlockingQueue : ObservableLinkedBlockingQue constructor( logger: InternalLogger, executorContext: String, - backPressureStrategy: BackPressureStrategy + backPressureStrategy: BackPressureStrategy, + timeProvider: TimeProvider ) : this( logger, executorContext, @@ -41,7 +43,8 @@ internal class BackPressuredBlockingQueue : ObservableLinkedBlockingQue backPressureStrategy.capacity, backPressureStrategy.onThresholdReached, backPressureStrategy.onItemDropped, - backPressureStrategy.backpressureMitigation + backPressureStrategy.backpressureMitigation, + timeProvider ) constructor( @@ -51,8 +54,9 @@ internal class BackPressuredBlockingQueue : ObservableLinkedBlockingQue capacity: Int, onThresholdReached: () -> Unit, onItemDropped: (Any) -> Unit, - backpressureMitigation: BackPressureMitigation? - ) : super(capacity) { + backpressureMitigation: BackPressureMitigation?, + timeProvider: TimeProvider + ) : super(capacity, timeProvider) { this.logger = logger this.executorContext = executorContext this.capacity = capacity diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/ObservableLinkedBlockingQueue.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/ObservableLinkedBlockingQueue.kt index 821fcf09e1..6f08032724 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/ObservableLinkedBlockingQueue.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/ObservableLinkedBlockingQueue.kt @@ -6,31 +6,31 @@ package com.datadog.android.core.internal.thread -import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.internal.thread.NamedExecutionUnit +import com.datadog.android.internal.time.TimeProvider import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicLong internal open class ObservableLinkedBlockingQueue : LinkedBlockingQueue { - private val currentTimeProvider: () -> Long + private val timeProvider: TimeProvider constructor( - currentTimeProvider: () -> Long = { SdkTimeProvider.now() } - ) : this(Int.MAX_VALUE, currentTimeProvider) + timeProvider: TimeProvider + ) : this(Int.MAX_VALUE, timeProvider) constructor( capacity: Int, - currentTimeProvider: () -> Long = { SdkTimeProvider.now() } + timeProvider: TimeProvider ) : super(capacity) { - this.currentTimeProvider = currentTimeProvider + this.timeProvider = timeProvider } private val lastDumpTimestamp: AtomicLong = AtomicLong(0) fun dumpQueue(): Map? { - val currentTime = currentTimeProvider.invoke() + val currentTime = timeProvider.getDeviceTimestamp() val last = lastDumpTimestamp.get() val timeSinceLastDump = currentTime - last return if (timeSinceLastDump > DUMPING_TIME_INTERVAL_IN_MS) { diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/ThreadPoolExecutorExt.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/ThreadPoolExecutorExt.kt index 2654bb9899..c7775b1938 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/ThreadPoolExecutorExt.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/thread/ThreadPoolExecutorExt.kt @@ -7,14 +7,18 @@ package com.datadog.android.core.internal.thread import com.datadog.android.api.InternalLogger -import com.datadog.android.core.time.SdkTimeProvider +import com.datadog.android.internal.time.TimeProvider import java.util.concurrent.ThreadPoolExecutor import java.util.concurrent.TimeUnit internal const val MAX_SLEEP_DURATION_IN_MS = 10L -internal fun ThreadPoolExecutor.waitToIdle(timeoutInMs: Long, internalLogger: InternalLogger): Boolean { - val startTime = SdkTimeProvider.elapsed() +internal fun ThreadPoolExecutor.waitToIdle( + timeoutInMs: Long, + internalLogger: InternalLogger, + timeProvider: TimeProvider +): Boolean { + val startTime = timeProvider.getDeviceElapsedTimeNs() val timeoutInNs = TimeUnit.MILLISECONDS.toNanos(timeoutInMs) val sleepDurationInMs = timeoutInMs.coerceIn(0, MAX_SLEEP_DURATION_IN_MS) var interrupted: Boolean @@ -23,7 +27,7 @@ internal fun ThreadPoolExecutor.waitToIdle(timeoutInMs: Long, internalLogger: In return true } interrupted = sleepSafe(sleepDurationInMs, internalLogger) - } while (((SdkTimeProvider.elapsed() - startTime) < timeoutInNs) && !interrupted) + } while (((timeProvider.getDeviceElapsedTimeNs() - startTime) < timeoutInNs) && !interrupted) return isIdle() } diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/utils/MiscUtils.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/utils/MiscUtils.kt index 8d05258139..878daf7b72 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/utils/MiscUtils.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/utils/MiscUtils.kt @@ -7,7 +7,7 @@ package com.datadog.android.core.internal.utils import com.datadog.android.api.InternalLogger -import com.datadog.android.core.time.SdkTimeProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.internal.utils.NULL_MAP_VALUE import com.datadog.android.lint.InternalApi import com.google.gson.JsonArray @@ -24,9 +24,10 @@ internal fun retryWithDelay( times: Int, retryDelayNs: Long, internalLogger: InternalLogger, + timeProvider: TimeProvider, block: () -> Boolean ): Boolean { - return retryWithDelay(block, times, retryDelayNs, internalLogger) + return retryWithDelay(block, times, retryDelayNs, internalLogger, timeProvider) } @Suppress("TooGenericExceptionCaught") @@ -34,13 +35,14 @@ internal inline fun retryWithDelay( block: () -> Boolean, times: Int, loopsDelayInNanos: Long, - internalLogger: InternalLogger + internalLogger: InternalLogger, + timeProvider: TimeProvider ): Boolean { var retryCounter = 1 var wasSuccessful = false - var loopTimeOrigin = SdkTimeProvider.elapsed() - loopsDelayInNanos + var loopTimeOrigin = timeProvider.getDeviceElapsedTimeNs() - loopsDelayInNanos while (retryCounter <= times && !wasSuccessful) { - if ((SdkTimeProvider.elapsed() - loopTimeOrigin) >= loopsDelayInNanos) { + if ((timeProvider.getDeviceElapsedTimeNs() - loopTimeOrigin) >= loopsDelayInNanos) { wasSuccessful = try { block() } catch (e: Exception) { @@ -55,7 +57,7 @@ internal inline fun retryWithDelay( ) false } - loopTimeOrigin = SdkTimeProvider.elapsed() + loopTimeOrigin = timeProvider.getDeviceElapsedTimeNs() retryCounter++ } } diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/thread/FlushableExecutorService.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/thread/FlushableExecutorService.kt index ce38ef0f05..572195b549 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/thread/FlushableExecutorService.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/thread/FlushableExecutorService.kt @@ -8,6 +8,7 @@ package com.datadog.android.core.thread import com.datadog.android.api.InternalLogger import com.datadog.android.core.configuration.BackPressureStrategy +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.lint.InternalApi import java.util.concurrent.ExecutorService @@ -36,12 +37,14 @@ interface FlushableExecutorService : ExecutorService { * @param internalLogger the internal logger * @param executorContext Context to be used for logging and naming threads running on this executor. * @param backPressureStrategy the strategy to handle back-pressure + * @param timeProvider - the provider for time measurements. * @return the instance */ fun create( internalLogger: InternalLogger, executorContext: String, - backPressureStrategy: BackPressureStrategy + backPressureStrategy: BackPressureStrategy, + timeProvider: TimeProvider ): FlushableExecutorService } } diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/time/SdkTimeProvider.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/time/SdkTimeProvider.kt deleted file mode 100644 index 766ce93d75..0000000000 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/time/SdkTimeProvider.kt +++ /dev/null @@ -1,38 +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.core.time - -import com.datadog.android.Datadog -import com.datadog.android.core.internal.DatadogCore -import com.datadog.android.internal.time.DefaultTimeProvider -import com.datadog.android.internal.time.TimeProvider - -/** - * Global accessor to the [com.datadog.android.internal.time.TimeProvider] configured by the core. - */ -object SdkTimeProvider { - - /** - * Returns the [com.datadog.android.internal.time.TimeProvider] currently used by the SDK. - * Falls back to [DefaultTimeProvider] if the SDK is not initialized. - */ - val provider: TimeProvider - get() { - val sdkCore = Datadog.getInstance() as? DatadogCore - return sdkCore?.coreFeature?.timeProvider ?: DefaultTimeProvider() - } - - /** - * Returns our best estimate of the current wall-clock time in milliseconds. - */ - fun now(): Long = provider.getDeviceTimestamp() - - /** - * Returns the monotonic elapsed time in nanoseconds since boot. - */ - fun elapsed(): Long = provider.getDeviceElapsedTimeNs() -} diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/error/internal/DatadogExceptionHandler.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/error/internal/DatadogExceptionHandler.kt index 51eb001496..b12669a57c 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/error/internal/DatadogExceptionHandler.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/error/internal/DatadogExceptionHandler.kt @@ -57,7 +57,7 @@ internal class DatadogExceptionHandler( // give some time to the persistence executor service to finish its tasks if (sdkCore is InternalSdkCore) { val idled = (sdkCore.getPersistenceExecutorService() as? ThreadPoolExecutor) - ?.waitToIdle(MAX_WAIT_FOR_IDLE_TIME_IN_MS, sdkCore.internalLogger) ?: true + ?.waitToIdle(MAX_WAIT_FOR_IDLE_TIME_IN_MS, sdkCore.internalLogger, sdkCore.timeProvider) ?: true if (!idled) { sdkCore.internalLogger.log( InternalLogger.Level.WARN, diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/logger/SdkInternalLoggerTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/logger/SdkInternalLoggerTest.kt index e921117b95..9f3894f102 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/logger/SdkInternalLoggerTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/logger/SdkInternalLoggerTest.kt @@ -14,7 +14,6 @@ import com.datadog.android.api.feature.FeatureScope import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.core.internal.metrics.MethodCalledTelemetry import com.datadog.android.core.metrics.TelemetryMetricType -import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.internal.attributes.LocalAttribute import com.datadog.android.internal.telemetry.InternalTelemetryEvent import com.datadog.android.utils.forge.Configurator @@ -767,7 +766,7 @@ internal class SdkInternalLoggerTest { @StringForgery fakeOperation: String ) { // Given - val startNs = SdkTimeProvider.elapsed() + val startNs = System.nanoTime() // When val result = testedInternalLogger.startPerformanceMeasure( @@ -776,7 +775,7 @@ internal class SdkInternalLoggerTest { 100f, fakeOperation ) - val endNs = SdkTimeProvider.elapsed() + val endNs = System.nanoTime() // Then val methodCalledTelemetry = result as? MethodCalledTelemetry diff --git a/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/model/FlagsStateEntry.kt b/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/model/FlagsStateEntry.kt index dd14bae016..d61d3f383f 100644 --- a/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/model/FlagsStateEntry.kt +++ b/features/dd-sdk-android-flags/src/main/kotlin/com/datadog/android/flags/internal/model/FlagsStateEntry.kt @@ -6,11 +6,10 @@ package com.datadog.android.flags.internal.model -import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.flags.model.EvaluationContext internal data class FlagsStateEntry( val evaluationContext: EvaluationContext, val flags: Map, - val lastUpdateTimestamp: Long = SdkTimeProvider.now() + val lastUpdateTimestamp: Long ) diff --git a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/DatadogLogGeneratorTest.kt b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/DatadogLogGeneratorTest.kt index 3e0bf031f9..a1b2573edc 100644 --- a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/DatadogLogGeneratorTest.kt +++ b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/domain/DatadogLogGeneratorTest.kt @@ -12,8 +12,6 @@ import com.datadog.android.api.context.NetworkInfo import com.datadog.android.api.context.UserInfo import com.datadog.android.api.feature.Feature import com.datadog.android.core.feature.event.ThreadDump -import com.datadog.android.core.time.SdkTimeProvider -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.log.LogAttributes import com.datadog.android.log.assertj.LogEventAssert.Companion.assertThat import com.datadog.android.log.model.LogEvent @@ -89,7 +87,7 @@ internal class DatadogLogGeneratorTest { fakeAttributes = forge.aMap { anAlphabeticalString() to anInt() } fakeTags = forge.aList { anAlphabeticalString() }.toSet() fakeThrowable = forge.aThrowable() - fakeTimestamp = SdkTimeProvider.now() + fakeTimestamp = System.currentTimeMillis() fakeThreadName = forge.anAlphabeticalString() fakeTimeOffset = forge.aLong( min = -fakeTimestamp, diff --git a/features/dd-sdk-android-ndk/src/androidTest/kotlin/com/datadog/android/ndk/NdkTests.kt b/features/dd-sdk-android-ndk/src/androidTest/kotlin/com/datadog/android/ndk/NdkTests.kt index d883a93e28..57c982e73a 100644 --- a/features/dd-sdk-android-ndk/src/androidTest/kotlin/com/datadog/android/ndk/NdkTests.kt +++ b/features/dd-sdk-android-ndk/src/androidTest/kotlin/com/datadog/android/ndk/NdkTests.kt @@ -7,8 +7,6 @@ package com.datadog.android.ndk import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.datadog.android.core.time.SdkTimeProvider -import com.datadog.android.internal.time.TimeProvider import com.datadog.tools.unit.ConditionWatcher import com.datadog.tools.unit.assertj.JsonObjectAssert.Companion.assertThat import com.google.gson.JsonParser @@ -36,8 +34,6 @@ class NdkTests { System.loadLibrary("datadog-native-lib") System.loadLibrary("datadog-native-lib-test") } - - val timeProvider: TimeProvider = SdkTimeProvider.provider } @Test @@ -62,13 +58,10 @@ class NdkTests { val fakeErrorStack = forge.anAlphabeticalString() initNdkErrorHandler(temporaryFolder.root.absolutePath) updateTrackingConsent(1) - val fakeAppStartTimeMs = forge.aLong( - min = 0L, - max = timeProvider.getDeviceTimestamp() - ) + val fakeAppStartTimeMs = forge.aLong(min = 0L, max = System.currentTimeMillis()) updateAppStartTime(fakeAppStartTimeMs) - val expectedTimestamp = timeProvider.getDeviceTimestamp() + val expectedTimestamp = System.currentTimeMillis() val expectedTimeSinceAppStartMs = expectedTimestamp - fakeAppStartTimeMs simulateSignalInterception( fakeSignal, diff --git a/features/dd-sdk-android-ndk/src/test/kotlin/com/datadog/android/ndk/internal/NdkCrashReportsFeatureTest.kt b/features/dd-sdk-android-ndk/src/test/kotlin/com/datadog/android/ndk/internal/NdkCrashReportsFeatureTest.kt index 882886bba6..330686958c 100644 --- a/features/dd-sdk-android-ndk/src/test/kotlin/com/datadog/android/ndk/internal/NdkCrashReportsFeatureTest.kt +++ b/features/dd-sdk-android-ndk/src/test/kotlin/com/datadog/android/ndk/internal/NdkCrashReportsFeatureTest.kt @@ -9,7 +9,6 @@ package com.datadog.android.ndk.internal import android.content.Context import com.datadog.android.api.InternalLogger import com.datadog.android.core.InternalSdkCore -import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.privacy.TrackingConsent import com.datadog.tools.unit.setFieldValue import fr.xgouchet.elmyr.junit5.ForgeExtension @@ -51,7 +50,6 @@ class NdkCrashReportsFeatureTest { @BeforeEach fun `set up`() { - whenever(mockSdkCore.timeProvider) doReturn SdkTimeProvider.provider testedFeature = NdkCrashReportsFeature(mockSdkCore) } diff --git a/features/dd-sdk-android-rum/api/dd-sdk-android-rum.api b/features/dd-sdk-android-rum/api/dd-sdk-android-rum.api index f5226876f3..044598bb78 100644 --- a/features/dd-sdk-android-rum/api/dd-sdk-android-rum.api +++ b/features/dd-sdk-android-rum/api/dd-sdk-android-rum.api @@ -7753,11 +7753,13 @@ public final class com/datadog/android/rum/resource/ResourceId { public final class com/datadog/android/rum/resource/RumResourceInputStream : java/io/InputStream { public fun (Ljava/io/InputStream;Ljava/lang/String;)V public fun (Ljava/io/InputStream;Ljava/lang/String;Lcom/datadog/android/api/SdkCore;)V - public synthetic fun (Ljava/io/InputStream;Ljava/lang/String;Lcom/datadog/android/api/SdkCore;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/io/InputStream;Ljava/lang/String;Lcom/datadog/android/api/SdkCore;Lcom/datadog/android/internal/time/TimeProvider;)V + public synthetic fun (Ljava/io/InputStream;Ljava/lang/String;Lcom/datadog/android/api/SdkCore;Lcom/datadog/android/internal/time/TimeProvider;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun available ()I public fun close ()V public final fun getDelegate ()Ljava/io/InputStream; public final fun getSdkCore ()Lcom/datadog/android/api/SdkCore; + public final fun getTimeProvider ()Lcom/datadog/android/internal/time/TimeProvider; public final fun getUrl ()Ljava/lang/String; public fun mark (I)V public fun markSupported ()Z diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/Rum.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/Rum.kt index e1ba33d111..ccbaaae94f 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/Rum.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/Rum.kt @@ -145,7 +145,8 @@ object Rum { accessibilitySnapshotManager = rumFeature.accessibilitySnapshotManager, batteryInfoProvider = rumFeature.batteryInfoProvider, displayInfoProvider = rumFeature.displayInfoProvider, - rumAppStartupTelemetryReporter = RumAppStartupTelemetryReporter.create(sdkCore) + rumAppStartupTelemetryReporter = RumAppStartupTelemetryReporter.create(sdkCore), + timeProvider = sdkCore.timeProvider ) } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/RumConfiguration.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/RumConfiguration.kt index 6635c21366..439af31723 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/RumConfiguration.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/RumConfiguration.kt @@ -9,6 +9,7 @@ package com.datadog.android.rum import android.os.Looper import androidx.annotation.FloatRange import com.datadog.android.event.EventMapper +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.configuration.SlowFramesConfiguration import com.datadog.android.rum.configuration.VitalsUpdateFrequency import com.datadog.android.rum.event.ViewEventMapper @@ -139,9 +140,11 @@ data class RumConfiguration internal constructor( * value less than or equal to 0 disables the long task tracking */ @JvmOverloads - fun trackLongTasks(longTaskThresholdMs: Long = RumFeature.DEFAULT_LONG_TASK_THRESHOLD_MS): Builder { + fun trackLongTasks( + longTaskThresholdMs: Long = RumFeature.DEFAULT_LONG_TASK_THRESHOLD_MS + ): Builder { val strategy = if (longTaskThresholdMs > 0) { - MainLooperLongTaskStrategy(longTaskThresholdMs) + MainLooperLongTaskStrategy(longTaskThresholdMs, DefaultTimeProvider()) } else { null } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/RumFeature.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/RumFeature.kt index dbd319d870..c6b6d02661 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/RumFeature.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/RumFeature.kt @@ -36,6 +36,7 @@ import com.datadog.android.event.MapperSerializer import com.datadog.android.event.NoOpEventMapper import com.datadog.android.internal.flags.RumFlagEvaluationMessage import com.datadog.android.internal.telemetry.InternalTelemetryEvent +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.GlobalRumMonitor import com.datadog.android.rum.RumErrorSource import com.datadog.android.rum.RumSessionListener @@ -288,7 +289,8 @@ internal class RumFeature( metricDispatcher = DefaultUISlownessMetricDispatcher( slowFramesConfiguration, sdkCore.internalLogger - ) + ), + sdkCore.timeProvider ) } else { sdkCore.internalLogger.log( @@ -777,7 +779,8 @@ internal class RumFeature( interactionPredicate = NoOpInteractionPredicate(), viewTrackingStrategy = ActivityViewTrackingStrategy(false), longTaskTrackingStrategy = MainLooperLongTaskStrategy( - DEFAULT_LONG_TASK_THRESHOLD_MS + DEFAULT_LONG_TASK_THRESHOLD_MS, + DefaultTimeProvider() ), viewEventMapper = NoOpEventMapper(), errorEventMapper = NoOpEventMapper(), diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/Time.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/Time.kt index 68dce87e0b..a42e16678b 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/Time.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/Time.kt @@ -1,3 +1,4 @@ + /* * 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/). @@ -6,26 +7,19 @@ package com.datadog.android.rum.internal.domain -import com.datadog.android.core.time.SdkTimeProvider import java.util.concurrent.TimeUnit internal data class Time( - val timestamp: Long, - val nanoTime: Long -) { - constructor() : this( - timestamp = SdkTimeProvider.now(), - nanoTime = SdkTimeProvider.elapsed() - ) -} + val timestamp: Long = System.currentTimeMillis(), + val nanoTime: Long = System.nanoTime() +) internal fun Long.asTime(): Time { - val currentTimestamp = SdkTimeProvider.now() - val currentNano = SdkTimeProvider.elapsed() - val offset = this - currentTimestamp - val adjustedNano = currentNano + TimeUnit.MILLISECONDS.toNanos(offset) - return Time( - timestamp = this, - nanoTime = adjustedNano - ) + // Because nanoTime only measures the nanoseconds since the beginning + // of the current JVM lifetime, we need to approximate the nanotime we want. + // We simply convert the delay between the desired and real timestamp and + // apply it to the measured nanotime + val now = Time() + val offset = this - now.timestamp + return Time(this, TimeUnit.MILLISECONDS.toNanos(offset) + now.nanoTime) } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScope.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScope.kt index af6fc9d22f..9204720e81 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScope.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScope.kt @@ -14,6 +14,7 @@ import com.datadog.android.api.feature.EventWriteScope import com.datadog.android.api.storage.DataWriter import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.DdRumContentProvider import com.datadog.android.rum.GlobalRumMonitor import com.datadog.android.rum.RumSessionListener @@ -52,7 +53,8 @@ internal class RumApplicationScope( private val accessibilitySnapshotManager: AccessibilitySnapshotManager, private val batteryInfoProvider: InfoProvider, private val displayInfoProvider: InfoProvider, - private val rumAppStartupTelemetryReporter: RumAppStartupTelemetryReporter + private val rumAppStartupTelemetryReporter: RumAppStartupTelemetryReporter, + private val timeProvider: TimeProvider ) : RumScope, RumViewChangedListener { override val parentScope: RumScope? = null @@ -81,7 +83,8 @@ internal class RumApplicationScope( accessibilitySnapshotManager = accessibilitySnapshotManager, batteryInfoProvider = batteryInfoProvider, displayInfoProvider = displayInfoProvider, - rumAppStartupTelemetryReporter = rumAppStartupTelemetryReporter + rumAppStartupTelemetryReporter = rumAppStartupTelemetryReporter, + timeProvider = timeProvider ) ) @@ -202,14 +205,16 @@ internal class RumApplicationScope( accessibilitySnapshotManager = accessibilitySnapshotManager, batteryInfoProvider = batteryInfoProvider, displayInfoProvider = displayInfoProvider, - rumAppStartupTelemetryReporter = rumAppStartupTelemetryReporter + rumAppStartupTelemetryReporter = rumAppStartupTelemetryReporter, + timeProvider = timeProvider ) childScopes.add(newSession) if (event !is RumRawEvent.StartView) { lastActiveViewInfo?.let { val startViewEvent = RumRawEvent.StartView( key = it.key, - attributes = it.attributes + attributes = it.attributes, + eventTime = Time() ) newSession.handleEvent(startViewEvent, datadogContext, writeScope, writer) } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScope.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScope.kt index e0534cef98..625f425db3 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScope.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScope.kt @@ -14,7 +14,7 @@ import com.datadog.android.api.storage.DataWriter import com.datadog.android.api.storage.NoOpDataWriter import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver -import com.datadog.android.core.time.SdkTimeProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.RumSessionListener import com.datadog.android.rum.RumSessionType import com.datadog.android.rum.internal.domain.InfoProvider @@ -59,14 +59,15 @@ internal class RumSessionScope( private val sessionInactivityNanos: Long = DEFAULT_SESSION_INACTIVITY_NS, private val sessionMaxDurationNanos: Long = DEFAULT_SESSION_MAX_DURATION_NS, rumSessionTypeOverride: RumSessionType?, - private val rumAppStartupTelemetryReporter: RumAppStartupTelemetryReporter + private val rumAppStartupTelemetryReporter: RumAppStartupTelemetryReporter, + private val timeProvider: TimeProvider ) : RumScope { internal var sessionId = RumContext.NULL_UUID internal var sessionState: State = State.NOT_TRACKED private var startReason: StartReason = StartReason.USER_APP_LAUNCH internal var isActive: Boolean = true - private val sessionStartNs = AtomicLong(SdkTimeProvider.elapsed()) + private val sessionStartNs = AtomicLong(timeProvider.getDeviceElapsedTimeNs()) private val lastUserInteractionNs = AtomicLong(0L) @@ -207,7 +208,7 @@ internal class RumSessionScope( @Suppress("ComplexMethod") private fun updateSession(event: RumRawEvent) { - val nanoTime = SdkTimeProvider.elapsed() + val nanoTime = timeProvider.getDeviceElapsedTimeNs() val isNewSession = sessionId == RumContext.NULL_UUID val timeSinceLastInteractionNs = nanoTime - lastUserInteractionNs.get() diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/instrumentation/MainLooperLongTaskStrategy.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/instrumentation/MainLooperLongTaskStrategy.kt index 1468df031b..ba2c12b514 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/instrumentation/MainLooperLongTaskStrategy.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/instrumentation/MainLooperLongTaskStrategy.kt @@ -10,7 +10,7 @@ import android.content.Context import android.os.Looper import android.util.Printer import com.datadog.android.api.SdkCore -import com.datadog.android.core.time.SdkTimeProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.GlobalRumMonitor import com.datadog.android.rum.internal.monitor.AdvancedRumMonitor import com.datadog.android.rum.tracking.TrackingStrategy @@ -19,7 +19,8 @@ import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean internal class MainLooperLongTaskStrategy( - internal val thresholdMs: Long + internal val thresholdMs: Long, + private val timeProvider: TimeProvider ) : Printer, TrackingStrategy { private val thresholdNS = TimeUnit.MILLISECONDS.toNanos(thresholdMs) @@ -80,7 +81,7 @@ internal class MainLooperLongTaskStrategy( // region Internal private fun detectLongTask(message: String) { - val now = SdkTimeProvider.elapsed() + val now = timeProvider.getDeviceElapsedTimeNs() if (message.startsWith(PREFIX_START)) { @Suppress("UnsafeThirdPartyFunctionCall") // substring can't throw IndexOutOfBounds target = message.substring(PREFIX_START_LENGTH) diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/metric/slowframes/SlowFramesListener.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/metric/slowframes/SlowFramesListener.kt index b1676ce3e3..cfc4abfbcf 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/metric/slowframes/SlowFramesListener.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/metric/slowframes/SlowFramesListener.kt @@ -6,7 +6,7 @@ package com.datadog.android.rum.internal.metric.slowframes import androidx.metrics.performance.FrameData -import com.datadog.android.core.time.SdkTimeProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.configuration.SlowFramesConfiguration import com.datadog.android.rum.internal.domain.FrameMetricsData import com.datadog.android.rum.internal.domain.state.SlowFrameRecord @@ -23,14 +23,15 @@ internal interface SlowFramesListener : FrameStateListener { internal class DefaultSlowFramesListener( internal val configuration: SlowFramesConfiguration, - internal val metricDispatcher: UISlownessMetricDispatcher + internal val metricDispatcher: UISlownessMetricDispatcher, + timeProvider: TimeProvider ) : SlowFramesListener { @Volatile private var currentViewId: String? = null @Volatile - private var currentViewStartedTimeStampNs: Long = SdkTimeProvider.elapsed() + private var currentViewStartedTimeStampNs: Long = timeProvider.getDeviceElapsedTimeNs() private val slowFramesRecords = ConcurrentHashMap() diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitor.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitor.kt index f370992cf3..c96edd7701 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitor.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitor.kt @@ -26,6 +26,7 @@ import com.datadog.android.core.metrics.MethodCallSamplingRate import com.datadog.android.internal.telemetry.InternalTelemetryEvent import com.datadog.android.internal.telemetry.InternalTelemetryEvent.ApiUsage.AddOperationStepVital.ActionType import com.datadog.android.internal.thread.NamedCallable +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.DdRumContentProvider import com.datadog.android.rum.ExperimentalRumApi import com.datadog.android.rum.RumActionType @@ -96,7 +97,8 @@ internal class DatadogRumMonitor( accessibilitySnapshotManager: AccessibilitySnapshotManager, batteryInfoProvider: InfoProvider, displayInfoProvider: InfoProvider, - rumAppStartupTelemetryReporter: RumAppStartupTelemetryReporter + rumAppStartupTelemetryReporter: RumAppStartupTelemetryReporter, + timeProvider: TimeProvider ) : RumMonitor, AdvancedRumMonitor { internal var rootScope = RumApplicationScope( @@ -118,7 +120,8 @@ internal class DatadogRumMonitor( accessibilitySnapshotManager = accessibilitySnapshotManager, batteryInfoProvider = batteryInfoProvider, displayInfoProvider = displayInfoProvider, - rumAppStartupTelemetryReporter = rumAppStartupTelemetryReporter + rumAppStartupTelemetryReporter = rumAppStartupTelemetryReporter, + timeProvider = timeProvider ) internal val keepAliveRunnable = Runnable { @@ -589,11 +592,11 @@ internal class DatadogRumMonitor( override fun eventDropped(viewId: String, event: StorageEvent) { when (event) { - is StorageEvent.Action -> handleEvent(RumRawEvent.ActionDropped(viewId)) - is StorageEvent.Resource -> handleEvent(RumRawEvent.ResourceDropped(viewId, event.resourceId)) - is StorageEvent.Error -> handleEvent(RumRawEvent.ErrorDropped(viewId, event.resourceId)) - is StorageEvent.LongTask -> handleEvent(RumRawEvent.LongTaskDropped(viewId, false)) - is StorageEvent.FrozenFrame -> handleEvent(RumRawEvent.LongTaskDropped(viewId, true)) + is StorageEvent.Action -> handleEvent(RumRawEvent.ActionDropped(viewId, eventTime = Time())) + is StorageEvent.Resource -> handleEvent(RumRawEvent.ResourceDropped(viewId, event.resourceId, Time())) + is StorageEvent.Error -> handleEvent(RumRawEvent.ErrorDropped(viewId, event.resourceId, Time())) + is StorageEvent.LongTask -> handleEvent(RumRawEvent.LongTaskDropped(viewId, false, Time())) + is StorageEvent.FrozenFrame -> handleEvent(RumRawEvent.LongTaskDropped(viewId, true, Time())) is StorageEvent.View -> { // Nothing to do } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumAppStartupDetector.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumAppStartupDetector.kt index 1cea3bc692..fcc53890c8 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumAppStartupDetector.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumAppStartupDetector.kt @@ -9,7 +9,6 @@ package com.datadog.android.rum.internal.startup import android.app.Application import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.internal.system.BuildSdkVersionProvider -import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.rum.DdRumContentProvider internal interface RumAppStartupDetector { @@ -30,7 +29,7 @@ internal interface RumAppStartupDetector { buildSdkVersionProvider = BuildSdkVersionProvider.DEFAULT, appStartupTimeProviderNs = { sdkCore.appStartTimeNs }, processImportanceProvider = { DdRumContentProvider.processImportance }, - timeProviderNs = { SdkTimeProvider.elapsed() }, + timeProviderNs = { sdkCore.timeProvider.getDeviceElapsedTimeNs() }, listener ) } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumFirstDrawTimeReporter.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumFirstDrawTimeReporter.kt index 028281b06c..4728065b03 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumFirstDrawTimeReporter.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/startup/RumFirstDrawTimeReporter.kt @@ -10,7 +10,6 @@ import android.app.Activity import android.os.Handler import android.os.Looper import com.datadog.android.core.InternalSdkCore -import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.rum.internal.utils.window.RumWindowCallbacksRegistryImpl internal interface RumFirstDrawTimeReporter { @@ -24,7 +23,7 @@ internal interface RumFirstDrawTimeReporter { fun create(sdkCore: InternalSdkCore): RumFirstDrawTimeReporter { return RumFirstDrawTimeReporterImpl( internalLogger = sdkCore.internalLogger, - timeProviderNs = { SdkTimeProvider.elapsed() }, + timeProviderNs = { sdkCore.timeProvider.getDeviceElapsedTimeNs() }, windowCallbacksRegistry = RumWindowCallbacksRegistryImpl(), handler = Handler(Looper.getMainLooper()) ) diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/resource/RumResourceInputStream.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/resource/RumResourceInputStream.kt index ed312cc8e9..2ceedcf4a5 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/resource/RumResourceInputStream.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/resource/RumResourceInputStream.kt @@ -8,7 +8,8 @@ package com.datadog.android.rum.resource import com.datadog.android.Datadog import com.datadog.android.api.SdkCore -import com.datadog.android.core.time.SdkTimeProvider +import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.GlobalRumMonitor import com.datadog.android.rum.RumErrorSource import com.datadog.android.rum.RumResourceKind @@ -24,6 +25,7 @@ import java.io.InputStream * @param url the URL associated with the underlying resource, as you want it displayed in Datadog * @param sdkCore the [SdkCore] instance to report resources to. If not provided, default * instance will be used. + * @param timeProvider - the provider for time measurements. */ @Suppress("ThrowingInternalException", "TooGenericExceptionCaught") class RumResourceInputStream @@ -31,7 +33,8 @@ class RumResourceInputStream constructor( val delegate: InputStream, val url: String, - val sdkCore: SdkCore = Datadog.getInstance() + val sdkCore: SdkCore = Datadog.getInstance(), + val timeProvider: TimeProvider = DefaultTimeProvider() ) : InputStream() { internal val key: String = delegate.javaClass.simpleName + @@ -47,7 +50,7 @@ constructor( init { val rumMonitor = GlobalRumMonitor.get(sdkCore) rumMonitor.startResource(key, METHOD, url) - callStart = nowNs() + callStart = timeProvider.getDeviceElapsedTimeNs() if (rumMonitor is AdvancedRumMonitor) { rumMonitor.waitForResourceTiming(key) } @@ -57,36 +60,36 @@ constructor( /** @inheritdoc */ override fun read(): Int { - if (firstByte == 0L) firstByte = nowNs() + if (firstByte == 0L) firstByte = timeProvider.getDeviceElapsedTimeNs() return callWithErrorTracking(ERROR_READ) { @Suppress("UnsafeThirdPartyFunctionCall") // caller should handle the exception read().also { if (it >= 0) size++ - lastByte = nowNs() + lastByte = timeProvider.getDeviceElapsedTimeNs() } } } /** @inheritdoc */ override fun read(b: ByteArray): Int { - if (firstByte == 0L) firstByte = nowNs() + if (firstByte == 0L) firstByte = timeProvider.getDeviceElapsedTimeNs() return callWithErrorTracking(ERROR_READ) { @Suppress("UnsafeThirdPartyFunctionCall") // caller should handle the exception read(b).also { if (it >= 0) size += it - lastByte = nowNs() + lastByte = timeProvider.getDeviceElapsedTimeNs() } } } /** @inheritdoc */ override fun read(b: ByteArray, off: Int, len: Int): Int { - if (firstByte == 0L) firstByte = nowNs() + if (firstByte == 0L) firstByte = timeProvider.getDeviceElapsedTimeNs() return callWithErrorTracking(ERROR_READ) { @Suppress("UnsafeThirdPartyFunctionCall") // caller should handle the exception read(b, off, len).also { if (it >= 0) size += it - lastByte = nowNs() + lastByte = timeProvider.getDeviceElapsedTimeNs() } } } @@ -176,10 +179,6 @@ constructor( } } - private fun nowNs(): Long { - return SdkTimeProvider.elapsed() - } - // endregion internal companion object { diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt index c272ea540d..c1b76c37d3 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt @@ -19,7 +19,6 @@ import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.feature.event.ThreadDump import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver import com.datadog.android.internal.telemetry.InternalTelemetryEvent -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.utils.loggableStackTrace import com.datadog.android.rum.RumActionType import com.datadog.android.rum.RumAttributes @@ -331,8 +330,8 @@ internal class RumViewScopeTest { fakeParentContext = fakeParentContext.copy(syntheticsTestId = null, syntheticsResultId = null) val fakeOffset = -forge.aLong(1000, 50000) - val fakeTimestamp = DefaultTimeProvider().getDeviceTimestamp() + fakeOffset - val fakeNanos = DefaultTimeProvider().getDeviceElapsedTimeNs() + TimeUnit.MILLISECONDS.toNanos(fakeOffset) + val fakeTimestamp = System.currentTimeMillis() + fakeOffset + val fakeNanos = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(fakeOffset) val maxLimit = max(Long.MAX_VALUE - fakeTimestamp, Long.MAX_VALUE) val minLimit = min(-fakeTimestamp, maxLimit) val fakeBrightness = forge.aFloat(0f, 255f) @@ -5491,7 +5490,7 @@ internal class RumViewScopeTest { mockEventWriteScope, mockWriter ) - val customTimingEstimatedDuration = DefaultTimeProvider().getDeviceElapsedTimeNs() - fakeEventTime.nanoTime + val customTimingEstimatedDuration = System.nanoTime() - fakeEventTime.nanoTime // Then argumentCaptor { @@ -5568,14 +5567,14 @@ internal class RumViewScopeTest { mockEventWriteScope, mockWriter ) - val customTiming1EstimatedDuration = DefaultTimeProvider().getDeviceElapsedTimeNs() - fakeEventTime.nanoTime + val customTiming1EstimatedDuration = System.nanoTime() - fakeEventTime.nanoTime testedScope.handleEvent( RumRawEvent.AddCustomTiming(fakeTimingKey2), fakeDatadogContext, mockEventWriteScope, mockWriter ) - val customTiming2EstimatedDuration = DefaultTimeProvider().getDeviceElapsedTimeNs() - fakeEventTime.nanoTime + val customTiming2EstimatedDuration = System.nanoTime() - fakeEventTime.nanoTime // Then argumentCaptor { diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/metric/networksettled/NetworkSettledMetricResolverTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/metric/networksettled/NetworkSettledMetricResolverTest.kt index d165bbe84e..d5adf5faa6 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/metric/networksettled/NetworkSettledMetricResolverTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/metric/networksettled/NetworkSettledMetricResolverTest.kt @@ -7,8 +7,6 @@ package com.datadog.android.rum.metric.networksettled import com.datadog.android.api.InternalLogger -import com.datadog.android.core.time.SdkTimeProvider -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.internal.metric.NoValueReason import com.datadog.android.rum.internal.metric.ViewInitializationMetricsConfig import com.datadog.android.rum.internal.metric.networksettled.InternalResourceContext @@ -48,13 +46,12 @@ internal class NetworkSettledMetricResolverTest { @Mock lateinit var mockInitialResourceIdentifier: InitialResourceIdentifier - private var fakeViewStartTime: Long = 0L + private var fakeViewStartTime: Long = System.nanoTime() // region SetUp @BeforeEach fun `set up`() { - fakeViewStartTime = SdkTimeProvider.elapsed() testedMetric = NetworkSettledMetricResolver(mockInitialResourceIdentifier, mockInternalLogger) testedMetric.viewWasCreated(fakeViewStartTime) whenever(mockInitialResourceIdentifier.validate(any())).thenReturn(true) @@ -93,7 +90,7 @@ internal class NetworkSettledMetricResolverTest { @Test fun `M return the correct metric W resolveMetric(){ only one resource registered }`(forge: Forge) { // Given - val startTimestamp = SdkTimeProvider.elapsed() + val startTimestamp = System.nanoTime() val stopTimestamp = startTimestamp + forge.aLong(min = 1, max = 1000) val fakeResourceId = forge.getForgery().toString() testedMetric.resourceWasStarted(InternalResourceContext(fakeResourceId, startTimestamp)) @@ -198,7 +195,7 @@ internal class NetworkSettledMetricResolverTest { fun `M return null W resolveMetric(){ view not created }`(forge: Forge) { // Given testedMetric = NetworkSettledMetricResolver(mockInitialResourceIdentifier, mockInternalLogger) - val startTimestamp = SdkTimeProvider.elapsed() + val startTimestamp = System.nanoTime() val stopTimestamp = startTimestamp + forge.aLong(min = 1, max = 1000) val fakeResourceId = forge.getForgery().toString() testedMetric.resourceWasStarted(InternalResourceContext(fakeResourceId, startTimestamp)) @@ -247,7 +244,7 @@ internal class NetworkSettledMetricResolverTest { fun `M pass the viewCreatedTimestamp to validator W resourceWasStarted()`(forge: Forge) { // Given testedMetric = NetworkSettledMetricResolver(mockInitialResourceIdentifier, mockInternalLogger) - val startTimestamp = SdkTimeProvider.elapsed() + val startTimestamp = System.nanoTime() val expectedViewCreatedTimestamp: Long? if (forge.aBool()) { testedMetric.viewWasCreated(fakeViewStartTime) @@ -416,7 +413,7 @@ internal class NetworkSettledMetricResolverTest { @Test fun `M return null W resolveMetric(){ resource was stopped with a different id }`(forge: Forge) { // Given - val startTimestamp = timeProvider.getDeviceElapsedTimeNs() + val startTimestamp = System.nanoTime() val stopTimestamp = startTimestamp + forge.aLong(min = 1, max = 1000) val fakeResourceContext = forge.getForgery() val fakeDifferentResourceContext = forge.getForgery() @@ -438,7 +435,7 @@ internal class NetworkSettledMetricResolverTest { @Test fun `M return null W resolveMetric(){ resource was dropped }`(forge: Forge) { // Given - val startTimestamp = timeProvider.getDeviceElapsedTimeNs() + val startTimestamp = System.nanoTime() val fakeResourceContext = forge.getForgery() testedMetric.resourceWasStarted(InternalResourceContext(fakeResourceContext.resourceId, startTimestamp)) testedMetric.resourceWasDropped(fakeResourceContext.resourceId) @@ -484,7 +481,7 @@ internal class NetworkSettledMetricResolverTest { forge: Forge ) { // Given - val startTimestamp = timeProvider.getDeviceElapsedTimeNs() + val startTimestamp = System.nanoTime() val stopTimestamp = startTimestamp + forge.aLong(min = 1, max = 1000) val fakeResourceContext = forge.getForgery() val thread1 = Thread { @@ -520,7 +517,7 @@ internal class NetworkSettledMetricResolverTest { forge: Forge ) { // Given - val startTimestamp = timeProvider.getDeviceElapsedTimeNs() + val startTimestamp = System.nanoTime() val stopTimestamp = startTimestamp + forge.aLong(min = 1, max = 1000) val fakeResourceContext = forge.getForgery() val thread1 = Thread { @@ -557,7 +554,7 @@ internal class NetworkSettledMetricResolverTest { forge: Forge ) { // Given - val startTimestamp = timeProvider.getDeviceElapsedTimeNs() + val startTimestamp = System.nanoTime() val stopTimestamp = startTimestamp + forge.aLong(min = 1, max = 1000) val fakeResourceContext = forge.getForgery() val thread1 = Thread { @@ -595,7 +592,7 @@ internal class NetworkSettledMetricResolverTest { // region Internal private fun Forge.forgeStartTimestamps(size: Int = anInt(min = 1, max = 10)) = aList(size = size) { - timeProvider.getDeviceElapsedTimeNs() + aLong(min = 1, max = 1000) + System.nanoTime() + aLong(min = 1, max = 1000) } private fun List.mapToSettledIntervals(): List { diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandler.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandler.kt index acf6425adc..969e3f542a 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandler.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandler.kt @@ -12,7 +12,7 @@ import androidx.annotation.WorkerThread import com.datadog.android.api.InternalLogger import com.datadog.android.core.internal.utils.executeSafe import com.datadog.android.core.sampling.RateBasedSampler -import com.datadog.android.core.time.SdkTimeProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.sessionreplay.internal.processor.RecordedDataProcessor import com.datadog.android.sessionreplay.internal.processor.RumContextDataHandler import com.datadog.android.sessionreplay.model.MobileSegment @@ -33,7 +33,8 @@ internal class RecordedDataQueueHandler( private val executorService: ExecutorService, internal val recordedDataQueue: Queue, private val telemetrySampleRate: Float = TELEMETRY_SAMPLE_RATE_PERCENT, - private val sampler: RateBasedSampler = RateBasedSampler(telemetrySampleRate) + private val sampler: RateBasedSampler = RateBasedSampler(telemetrySampleRate), + private val timeProvider: TimeProvider ) : DataQueueHandler { @Synchronized @@ -55,7 +56,8 @@ internal class RecordedDataQueueHandler( recordedQueuedItemContext = rumContextData, identifier = identifier, resourceData = resourceData, - mimeType + mimeType, + timeProvider = timeProvider ) insertIntoRecordedDataQueue(item) @@ -72,6 +74,7 @@ internal class RecordedDataQueueHandler( val item = TouchEventRecordedDataQueueItem( recordedQueuedItemContext = rumContextData, + timeProvider = timeProvider, touchData = pointerInteractions ) @@ -87,6 +90,7 @@ internal class RecordedDataQueueHandler( val item = SnapshotRecordedDataQueueItem( recordedQueuedItemContext = rumContextData, + timeProvider = timeProvider, systemInformation = systemInformation ) @@ -129,7 +133,7 @@ internal class RecordedDataQueueHandler( val nextItem = recordedDataQueue.peek() if (nextItem != null) { - val nextItemAgeInNs = SdkTimeProvider.elapsed() - nextItem.creationTimeStampInNs + val nextItemAgeInNs = timeProvider.getDeviceElapsedTimeNs() - nextItem.creationTimeStampInNs if (!nextItem.isValid()) { if (sampler.sample(Unit)) { logInvalidQueueItemException(nextItem) diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueItem.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueItem.kt index 879337ba60..a22548ad89 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueItem.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueItem.kt @@ -6,12 +6,11 @@ package com.datadog.android.sessionreplay.internal.async -import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.sessionreplay.internal.processor.RecordedQueuedItemContext internal abstract class RecordedDataQueueItem( internal val recordedQueuedItemContext: RecordedQueuedItemContext, - internal val creationTimeStampInNs: Long = SdkTimeProvider.elapsed() + internal val creationTimeStampInNs: Long ) { internal abstract fun isValid(): Boolean diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/ResourceRecordedDataQueueItem.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/ResourceRecordedDataQueueItem.kt index 4b68fbbb19..2569acd938 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/ResourceRecordedDataQueueItem.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/ResourceRecordedDataQueueItem.kt @@ -6,14 +6,19 @@ package com.datadog.android.sessionreplay.internal.async +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.sessionreplay.internal.processor.RecordedQueuedItemContext internal class ResourceRecordedDataQueueItem( recordedQueuedItemContext: RecordedQueuedItemContext, val identifier: String, val resourceData: ByteArray, - val mimeType: String? = null -) : RecordedDataQueueItem(recordedQueuedItemContext) { + val mimeType: String? = null, + timeProvider: TimeProvider +) : RecordedDataQueueItem( + recordedQueuedItemContext, + timeProvider.getDeviceElapsedTimeNs() +) { override fun isValid(): Boolean { return resourceData.isNotEmpty() diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/SnapshotRecordedDataQueueItem.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/SnapshotRecordedDataQueueItem.kt index 5139dac9ea..172087a969 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/SnapshotRecordedDataQueueItem.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/SnapshotRecordedDataQueueItem.kt @@ -6,6 +6,7 @@ package com.datadog.android.sessionreplay.internal.async +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.sessionreplay.internal.processor.RecordedQueuedItemContext import com.datadog.android.sessionreplay.internal.recorder.Node import com.datadog.android.sessionreplay.recorder.SystemInformation @@ -13,8 +14,12 @@ import java.util.concurrent.atomic.AtomicInteger internal class SnapshotRecordedDataQueueItem( recordedQueuedItemContext: RecordedQueuedItemContext, + timeProvider: TimeProvider, internal val systemInformation: SystemInformation -) : RecordedDataQueueItem(recordedQueuedItemContext) { +) : RecordedDataQueueItem( + recordedQueuedItemContext, + timeProvider.getDeviceElapsedTimeNs() +) { @Volatile internal var nodes = emptyList() @Volatile internal var isFinishedTraversal = false diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/TouchEventRecordedDataQueueItem.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/TouchEventRecordedDataQueueItem.kt index 53090e48b4..53b748722c 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/TouchEventRecordedDataQueueItem.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/TouchEventRecordedDataQueueItem.kt @@ -6,13 +6,18 @@ package com.datadog.android.sessionreplay.internal.async +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.sessionreplay.internal.processor.RecordedQueuedItemContext import com.datadog.android.sessionreplay.model.MobileSegment internal class TouchEventRecordedDataQueueItem( recordedQueuedItemContext: RecordedQueuedItemContext, + timeProvider: TimeProvider, internal val touchData: List = emptyList() -) : RecordedDataQueueItem(recordedQueuedItemContext) { +) : RecordedDataQueueItem( + recordedQueuedItemContext, + timeProvider.getDeviceElapsedTimeNs() +) { override fun isValid(): Boolean { return touchData.isNotEmpty() diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/processor/RecordedDataProcessor.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/processor/RecordedDataProcessor.kt index 051921f0b7..0d6712dc4a 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/processor/RecordedDataProcessor.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/processor/RecordedDataProcessor.kt @@ -8,7 +8,7 @@ package com.datadog.android.sessionreplay.internal.processor import android.content.res.Configuration import androidx.annotation.WorkerThread -import com.datadog.android.core.time.SdkTimeProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.sessionreplay.internal.async.ResourceRecordedDataQueueItem import com.datadog.android.sessionreplay.internal.async.SnapshotRecordedDataQueueItem import com.datadog.android.sessionreplay.internal.async.TouchEventRecordedDataQueueItem @@ -28,6 +28,7 @@ internal class RecordedDataProcessor( private val resourcesWriter: ResourcesWriter, private val writer: RecordWriter, private val mutationResolver: MutationResolver, + private val timeProvider: TimeProvider, private val nodeFlattener: NodeFlattener = NodeFlattener() ) : Processor { private var prevSnapshot: List = emptyList() @@ -163,8 +164,8 @@ internal class RecordedDataProcessor( } private fun isTimeForFullSnapshot(): Boolean { - return if (SdkTimeProvider.elapsed() - lastSnapshotTimestamp >= FULL_SNAPSHOT_INTERVAL_IN_NS) { - lastSnapshotTimestamp = SdkTimeProvider.elapsed() + return if (timeProvider.getDeviceElapsedTimeNs() - lastSnapshotTimestamp >= FULL_SNAPSHOT_INTERVAL_IN_NS) { + lastSnapshotTimestamp = timeProvider.getDeviceElapsedTimeNs() true } else { false diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/Debouncer.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/Debouncer.kt index e2200fb252..1c8e07e713 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/Debouncer.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/Debouncer.kt @@ -10,7 +10,7 @@ import android.os.Handler import android.os.Looper import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.FeatureSdkCore -import com.datadog.android.core.time.SdkTimeProvider +import com.datadog.android.internal.time.TimeProvider import java.util.concurrent.TimeUnit internal class Debouncer( @@ -18,7 +18,8 @@ internal class Debouncer( private val maxRecordDelayInNs: Long = MAX_DELAY_THRESHOLD_NS, private val timeBank: TimeBank = RecordingTimeBank(), private val sdkCore: FeatureSdkCore, - private val dynamicOptimizationEnabled: Boolean + private val dynamicOptimizationEnabled: Boolean, + private val timeProvider: TimeProvider ) { private var lastTimeRecordWasPerformed = 0L @@ -30,11 +31,11 @@ internal class Debouncer( // reason why we are not initializing this in the constructor is that in case the // component was initialized earlier than the first debounce request was requested // it will execute the runnable directly and will not pass through the handler. - lastTimeRecordWasPerformed = SdkTimeProvider.elapsed() + lastTimeRecordWasPerformed = timeProvider.getDeviceElapsedTimeNs() firstRequest = false } handler.removeCallbacksAndMessages(null) - val timePassedSinceLastExecution = SdkTimeProvider.elapsed() - lastTimeRecordWasPerformed + val timePassedSinceLastExecution = timeProvider.getDeviceElapsedTimeNs() - lastTimeRecordWasPerformed if (timePassedSinceLastExecution >= maxRecordDelayInNs) { executeRunnable(runnable) } else { @@ -50,14 +51,14 @@ internal class Debouncer( } else { runnable.run() } - lastTimeRecordWasPerformed = SdkTimeProvider.elapsed() + lastTimeRecordWasPerformed = timeProvider.getDeviceElapsedTimeNs() } private fun runInTimeBalance(block: () -> Unit) { - if (timeBank.updateAndCheck(SdkTimeProvider.elapsed())) { - val startTimeInNano = SdkTimeProvider.elapsed() + if (timeBank.updateAndCheck(timeProvider.getDeviceElapsedTimeNs())) { + val startTimeInNano = timeProvider.getDeviceElapsedTimeNs() block() - val endTimeInNano = SdkTimeProvider.elapsed() + val endTimeInNano = timeProvider.getDeviceElapsedTimeNs() timeBank.consume(endTimeInNano - startTimeInNano) } else { logSkippedFrame() diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/DefaultOnDrawListenerProducer.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/DefaultOnDrawListenerProducer.kt index 92a7010a37..418ea3b222 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/DefaultOnDrawListenerProducer.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/DefaultOnDrawListenerProducer.kt @@ -10,6 +10,7 @@ import android.view.View import android.view.ViewTreeObserver import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.core.metrics.MethodCallSamplingRate +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.sessionreplay.ImagePrivacy import com.datadog.android.sessionreplay.TextAndInputPrivacy import com.datadog.android.sessionreplay.internal.TouchPrivacyManager @@ -20,7 +21,8 @@ internal class DefaultOnDrawListenerProducer( private val snapshotProducer: SnapshotProducer, private val recordedDataQueueHandler: RecordedDataQueueHandler, private val sdkCore: FeatureSdkCore, - private val dynamicOptimizationEnabled: Boolean + private val dynamicOptimizationEnabled: Boolean, + private val timeProvider: TimeProvider ) : OnDrawListenerProducer { override fun create( @@ -38,6 +40,7 @@ internal class DefaultOnDrawListenerProducer( sdkCore = sdkCore, methodCallSamplingRate = MethodCallSamplingRate.LOW.rate, dynamicOptimizationEnabled = dynamicOptimizationEnabled, + timeProvider = timeProvider, touchPrivacyManager = touchPrivacyManager ) } diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/SessionReplayRecorder.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/SessionReplayRecorder.kt index 9fba608ef8..038c241e24 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/SessionReplayRecorder.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/SessionReplayRecorder.kt @@ -100,7 +100,8 @@ internal class SessionReplayRecorder : OnWindowRefreshedCallback, Recorder { resourceDataStoreManager, resourcesWriter, recordWriter, - MutationResolver(internalLogger) + MutationResolver(internalLogger), + timeProvider ) this.appContext = appContext @@ -115,7 +116,8 @@ internal class SessionReplayRecorder : OnWindowRefreshedCallback, Recorder { executorService = sdkCore.createSingleThreadExecutorService( "sr-event-processing" ), - recordedDataQueue = ConcurrentLinkedQueue() + recordedDataQueue = ConcurrentLinkedQueue(), + timeProvider = timeProvider ) val viewIdentifierResolver: ViewIdentifierResolver = DefaultViewIdentifierResolver @@ -181,7 +183,8 @@ internal class SessionReplayRecorder : OnWindowRefreshedCallback, Recorder { ), recordedDataQueueHandler = recordedDataQueueHandler, sdkCore = sdkCore, - dynamicOptimizationEnabled = dynamicOptimizationEnabled + dynamicOptimizationEnabled = dynamicOptimizationEnabled, + timeProvider = timeProvider ), touchPrivacyManager = touchPrivacyManager ) diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/callback/RecorderWindowCallback.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/callback/RecorderWindowCallback.kt index c31c061ad6..31720299a4 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/callback/RecorderWindowCallback.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/callback/RecorderWindowCallback.kt @@ -12,7 +12,6 @@ import android.view.MotionEvent import android.view.Window import androidx.annotation.MainThread import com.datadog.android.api.InternalLogger -import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.internal.utils.densityNormalized import com.datadog.android.sessionreplay.ImagePrivacy @@ -50,7 +49,7 @@ internal class RecorderWindowCallback( private val pixelsDensity = appContext.resources.displayMetrics.density internal val pointerInteractions: MutableList = LinkedList() private var lastOnMoveUpdateTimeInNs: Long = 0L - private var lastPerformedFlushTimeInNs: Long = SdkTimeProvider.elapsed() + private var lastPerformedFlushTimeInNs: Long = timeProvider.getDeviceElapsedTimeNs() private var shouldRecordMotion: Boolean = false // region Window.Callback @@ -100,19 +99,19 @@ internal class RecorderWindowCallback( when (event.action.and(MotionEvent.ACTION_MASK)) { MotionEvent.ACTION_DOWN -> { // reset the flush time to avoid flush in the next event - lastPerformedFlushTimeInNs = SdkTimeProvider.elapsed() + lastPerformedFlushTimeInNs = timeProvider.getDeviceElapsedTimeNs() updatePositions(event, MobileSegment.PointerEventType.DOWN) // reset the on move update time in order to take into account the first move event lastOnMoveUpdateTimeInNs = 0 } MotionEvent.ACTION_MOVE -> { - if (SdkTimeProvider.elapsed() - lastOnMoveUpdateTimeInNs >= motionUpdateThresholdInNs) { + if (timeProvider.getDeviceElapsedTimeNs() - lastOnMoveUpdateTimeInNs >= motionUpdateThresholdInNs) { updatePositions(event, MobileSegment.PointerEventType.MOVE) - lastOnMoveUpdateTimeInNs = SdkTimeProvider.elapsed() + lastOnMoveUpdateTimeInNs = timeProvider.getDeviceElapsedTimeNs() } // make sure we flush from time to time to avoid glitches in the player - if (SdkTimeProvider.elapsed() - lastPerformedFlushTimeInNs >= + if (timeProvider.getDeviceElapsedTimeNs() - lastPerformedFlushTimeInNs >= flushPositionBufferThresholdInNs ) { flushPositions() @@ -162,7 +161,7 @@ internal class RecorderWindowCallback( } pointerInteractions.clear() - lastPerformedFlushTimeInNs = SdkTimeProvider.elapsed() + lastPerformedFlushTimeInNs = timeProvider.getDeviceElapsedTimeNs() } private fun logOrRethrowWrappedCallbackException(e: NullPointerException) { diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/listener/WindowsOnDrawListener.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/listener/WindowsOnDrawListener.kt index 9d75b12fda..594c18c6e6 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/listener/WindowsOnDrawListener.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/recorder/listener/WindowsOnDrawListener.kt @@ -12,6 +12,7 @@ import androidx.annotation.MainThread import androidx.annotation.UiThread import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.api.feature.measureMethodCallPerf +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.sessionreplay.ImagePrivacy import com.datadog.android.sessionreplay.TextAndInputPrivacy import com.datadog.android.sessionreplay.internal.TouchPrivacyManager @@ -32,10 +33,12 @@ internal class WindowsOnDrawListener( private val miscUtils: MiscUtils = MiscUtils, private val sdkCore: FeatureSdkCore, dynamicOptimizationEnabled: Boolean, + timeProvider: TimeProvider, private val touchPrivacyManager: TouchPrivacyManager, private val debouncer: Debouncer = Debouncer( sdkCore = sdkCore, - dynamicOptimizationEnabled = dynamicOptimizationEnabled + dynamicOptimizationEnabled = dynamicOptimizationEnabled, + timeProvider = timeProvider ), private val methodCallSamplingRate: Float ) : ViewTreeObserver.OnDrawListener { diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/resources/ResourceDataStoreManager.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/resources/ResourceDataStoreManager.kt index 5aee7a03c0..d90d4b36c7 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/resources/ResourceDataStoreManager.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/resources/ResourceDataStoreManager.kt @@ -14,7 +14,6 @@ import com.datadog.android.api.storage.datastore.DataStoreWriteCallback import com.datadog.android.core.internal.persistence.Deserializer import com.datadog.android.core.persistence.Serializer import com.datadog.android.core.persistence.datastore.DataStoreContent -import com.datadog.android.core.time.SdkTimeProvider import com.datadog.android.sessionreplay.model.ResourceHashesEntry import java.util.Collections import java.util.concurrent.ConcurrentHashMap @@ -28,7 +27,7 @@ internal class ResourceDataStoreManager( ) { @Suppress("UnsafeThirdPartyFunctionCall") // map is initialized empty private val knownResources = Collections.newSetFromMap(ConcurrentHashMap()) - private val storedLastUpdateDateNs = AtomicLong(SdkTimeProvider.elapsed()) + private val storedLastUpdateDateNs = AtomicLong(featureSdkCore.timeProvider.getDeviceElapsedTimeNs()) private val isInitialized = AtomicBoolean(false) // has init finished executing its async actions init { @@ -130,7 +129,7 @@ internal class ResourceDataStoreManager( ) private fun didDataStoreExpire(lastUpdateDate: Long): Boolean = - SdkTimeProvider.elapsed() - lastUpdateDate > DATASTORE_EXPIRATION_NS + featureSdkCore.timeProvider.getDeviceElapsedTimeNs() - lastUpdateDate > DATASTORE_EXPIRATION_NS // endregion diff --git a/reliability/single-fit/rum/src/test/kotlin/com/datadog/android/rum/integration/ManualTrackingRumTest.kt b/reliability/single-fit/rum/src/test/kotlin/com/datadog/android/rum/integration/ManualTrackingRumTest.kt index 850d54f7ea..1ffd882303 100644 --- a/reliability/single-fit/rum/src/test/kotlin/com/datadog/android/rum/integration/ManualTrackingRumTest.kt +++ b/reliability/single-fit/rum/src/test/kotlin/com/datadog/android/rum/integration/ManualTrackingRumTest.kt @@ -8,7 +8,6 @@ package com.datadog.android.rum.integration import com.datadog.android.api.feature.Feature import com.datadog.android.core.stub.StubSDKCore -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.ExperimentalRumApi import com.datadog.android.rum.GlobalRumMonitor import com.datadog.android.rum.Rum @@ -486,11 +485,11 @@ class ManualTrackingRumTest { ) { // Given val rumMonitor = GlobalRumMonitor.get(stubSdkCore) - val startTime = DefaultTimeProvider().getDeviceElapsedTimeNs() + val startTime = System.nanoTime() rumMonitor.startView(key, name) // When - val endTime = DefaultTimeProvider().getDeviceElapsedTimeNs() + val endTime = System.nanoTime() val expectedViewLoadingTime = endTime - startTime rumMonitor.addViewLoadingTime(overwrite) @@ -579,14 +578,14 @@ class ManualTrackingRumTest { ) { // Given val rumMonitor = GlobalRumMonitor.get(stubSdkCore) - val startTime = DefaultTimeProvider().getDeviceElapsedTimeNs() + val startTime = System.nanoTime() rumMonitor.startView(key, name) - val intermediateTime = DefaultTimeProvider().getDeviceElapsedTimeNs() + val intermediateTime = System.nanoTime() rumMonitor.addViewLoadingTime(overwrite) // When Thread.sleep(100) - val endTime = DefaultTimeProvider().getDeviceElapsedTimeNs() + val endTime = System.nanoTime() rumMonitor.addViewLoadingTime(true) // Then @@ -646,9 +645,9 @@ class ManualTrackingRumTest { ) { // Given val rumMonitor = GlobalRumMonitor.get(stubSdkCore) - val startTime = DefaultTimeProvider().getDeviceElapsedTimeNs() + val startTime = System.nanoTime() rumMonitor.startView(key, name) - val intermediateTime = DefaultTimeProvider().getDeviceElapsedTimeNs() + val intermediateTime = System.nanoTime() rumMonitor.addViewLoadingTime(overwrite) // When diff --git a/reliability/single-fit/rum/src/test/kotlin/com/datadog/android/rum/integration/ViewLoadingTimeMetricsTests.kt b/reliability/single-fit/rum/src/test/kotlin/com/datadog/android/rum/integration/ViewLoadingTimeMetricsTests.kt index fc05369789..35f8fb591c 100644 --- a/reliability/single-fit/rum/src/test/kotlin/com/datadog/android/rum/integration/ViewLoadingTimeMetricsTests.kt +++ b/reliability/single-fit/rum/src/test/kotlin/com/datadog/android/rum/integration/ViewLoadingTimeMetricsTests.kt @@ -8,7 +8,6 @@ package com.datadog.android.rum.integration import com.datadog.android.api.feature.Feature import com.datadog.android.core.stub.StubSDKCore -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.GlobalRumMonitor import com.datadog.android.rum.Rum import com.datadog.android.rum.RumActionType @@ -112,11 +111,11 @@ class ViewLoadingTimeMetricsTests { val monitor = GlobalRumMonitor.get(stubSdkCore) // When - val startViewTime = DefaultTimeProvider().getDeviceElapsedTimeNs() + val startViewTime = System.nanoTime() monitor.startView(viewKey, viewName) monitor.startResource(resourceKey, rumResourceMethod, resourceUrl) Thread.sleep(100) - val stopResourceTime = DefaultTimeProvider().getDeviceElapsedTimeNs() + val stopResourceTime = System.nanoTime() monitor.stopResource(resourceKey, resourceStatus, resourceSize, rumResourceKind) monitor.stopView(viewKey) val appExpectedTtnsTime = (stopResourceTime - startViewTime) @@ -185,11 +184,11 @@ class ViewLoadingTimeMetricsTests { val monitor = GlobalRumMonitor.get(stubSdkCore) // When - val startViewTime = DefaultTimeProvider().getDeviceElapsedTimeNs() + val startViewTime = System.nanoTime() monitor.startView(viewKey, viewName) monitor.startResource(resourceKey, rumResourceMethod, resourceUrl) Thread.sleep(100) - val stopResourceTime = DefaultTimeProvider().getDeviceElapsedTimeNs() + val stopResourceTime = System.nanoTime() monitor.stopResource(resourceKey, resourceStatus, resourceSize, rumResourceKind) monitor.addTiming(forge.anAlphabeticalString()) Thread.sleep(100) @@ -287,11 +286,11 @@ class ViewLoadingTimeMetricsTests { val monitor = GlobalRumMonitor.get(stubSdkCore) // When - val startViewTime = DefaultTimeProvider().getDeviceElapsedTimeNs() + val startViewTime = System.nanoTime() monitor.startView(viewKey, viewName) monitor.startResource(resourceKey, rumResourceMethod, resourceUrl) Thread.sleep(100) - val stopResourceTime = DefaultTimeProvider().getDeviceElapsedTimeNs() + val stopResourceTime = System.nanoTime() monitor.stopResourceWithError(resourceKey, resourceStatus, errorMessage, errorSource, throwable) monitor.stopView(viewKey) val appExpectedTtnsTime = (stopResourceTime - startViewTime) @@ -1391,10 +1390,10 @@ class ViewLoadingTimeMetricsTests { monitor.startView(previousViewKey, previousViewName) monitor.startAction(rumActionType, lastInteractionName) Thread.sleep(100) - val stopActionTime = DefaultTimeProvider().getDeviceElapsedTimeNs() + val stopActionTime = System.nanoTime() monitor.stopAction(rumActionType, lastInteractionName) monitor.stopView(previousViewKey) - val startViewTime = DefaultTimeProvider().getDeviceElapsedTimeNs() + val startViewTime = System.nanoTime() monitor.startView(viewKey, viewName) Thread.sleep(100) monitor.stopView(viewKey) From 7ddfa2f722ecb57526ca01eea0178223ff6d6cc8 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Wed, 19 Nov 2025 16:58:08 +0000 Subject: [PATCH 09/29] RUM-10363: Update api files --- dd-sdk-android-core/api/apiSurface | 6 +----- dd-sdk-android-internal/api/apiSurface | 2 -- features/dd-sdk-android-rum/api/apiSurface | 2 +- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/dd-sdk-android-core/api/apiSurface b/dd-sdk-android-core/api/apiSurface index 5811d9e4bc..2eed5f75d6 100644 --- a/dd-sdk-android-core/api/apiSurface +++ b/dd-sdk-android-core/api/apiSurface @@ -364,11 +364,7 @@ interface com.datadog.android.core.sampling.Sampler interface com.datadog.android.core.thread.FlushableExecutorService : java.util.concurrent.ExecutorService fun drainTo(MutableCollection) interface Factory - fun create(com.datadog.android.api.InternalLogger, String, com.datadog.android.core.configuration.BackPressureStrategy): FlushableExecutorService -object com.datadog.android.core.time.SdkTimeProvider - var provider: com.datadog.android.internal.time.TimeProvider - fun now(): Long - fun elapsed(): Long + fun create(com.datadog.android.api.InternalLogger, String, com.datadog.android.core.configuration.BackPressureStrategy, com.datadog.android.internal.time.TimeProvider): FlushableExecutorService interface com.datadog.android.event.EventMapper fun map(T): T? class com.datadog.android.event.MapperSerializer : com.datadog.android.core.persistence.Serializer diff --git a/dd-sdk-android-internal/api/apiSurface b/dd-sdk-android-internal/api/apiSurface index 8fc31d7ebe..1b25f553ed 100644 --- a/dd-sdk-android-internal/api/apiSurface +++ b/dd-sdk-android-internal/api/apiSurface @@ -92,9 +92,7 @@ class com.datadog.android.internal.thread.NamedRunnable : NamedExecutionUnit, Ru class com.datadog.android.internal.thread.NamedCallable : NamedExecutionUnit, java.util.concurrent.Callable constructor(String, java.util.concurrent.Callable) class com.datadog.android.internal.time.DefaultTimeProvider : TimeProvider - override fun getDeviceTimestamp(): Long override fun getServerTimestamp(): Long - override fun getDeviceElapsedTimeNs(): Long override fun getServerOffsetNanos(): Long override fun getServerOffsetMillis(): Long interface com.datadog.android.internal.time.TimeProvider diff --git a/features/dd-sdk-android-rum/api/apiSurface b/features/dd-sdk-android-rum/api/apiSurface index 9055e1e615..4eab37ad7d 100644 --- a/features/dd-sdk-android-rum/api/apiSurface +++ b/features/dd-sdk-android-rum/api/apiSurface @@ -229,7 +229,7 @@ class com.datadog.android.rum.resource.ResourceId override fun equals(Any?): Boolean override fun hashCode(): Int class com.datadog.android.rum.resource.RumResourceInputStream : java.io.InputStream - constructor(java.io.InputStream, String, com.datadog.android.api.SdkCore = Datadog.getInstance()) + constructor(java.io.InputStream, String, com.datadog.android.api.SdkCore = Datadog.getInstance(), com.datadog.android.internal.time.TimeProvider = DefaultTimeProvider()) override fun read(): Int override fun read(ByteArray): Int override fun read(ByteArray, Int, Int): Int From 82947e8d2fd70e2a4a676cdc5a54ae8cec675e26 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Wed, 19 Nov 2025 17:03:51 +0000 Subject: [PATCH 10/29] RUM-10363: Revert test files changes --- .../datadog/android/core/DatadogCoreTest.kt | 5 +- .../metrics/BatchMetricsDispatcherTest.kt | 3 +- .../metrics/MethodCalledTelemetryTest.kt | 8 +- .../advanced/FeatureFileOrchestratorTest.kt | 13 +- .../file/batch/BatchFileOrchestratorTest.kt | 153 ++++++++---------- .../time/DefaultAppStartTimeProviderTest.kt | 14 +- .../internal/time/KronosTimeProviderTest.kt | 6 +- .../utils/forge/NdkCrashLogForgeryFactory.kt | 3 +- .../internal/ExposureEventsProcessorTest.kt | 21 +-- .../flags/internal/FlagsFeatureTest.kt | 5 - .../persistence/FlagsStateDeserializerTest.kt | 13 +- .../persistence/FlagsStateSerializerTest.kt | 5 +- .../repository/DefaultFlagsRepositoryTest.kt | 5 - .../android/log/internal/LogsFeatureTest.kt | 5 +- .../internal/logger/DatadogLogHandlerTest.kt | 23 +-- .../kotlin/com/datadog/android/rum/RumTest.kt | 5 - .../internal/DatadogLateCrashReporterTest.kt | 34 ++-- .../android/rum/internal/RumFeatureTest.kt | 5 - .../android/rum/internal/domain/TimeTest.kt | 20 +-- .../DefaultAccessibilityReaderTest.kt | 26 +-- .../battery/DefaultBatteryInfoProviderTest.kt | 3 +- ...pplicationScopeAttributePropagationTest.kt | 7 +- .../RumViewScopeAttributePropagationTest.kt | 7 +- .../internal/domain/scope/RumViewScopeTest.kt | 33 +--- .../ActionTypeInteractionValidatorTest.kt | 15 +- ...InteractionToNextViewMetricResolverTest.kt | 26 ++- .../internal/monitor/DatadogRumMonitorTest.kt | 3 +- .../TimeBasedInteractionIdentifierTest.kt | 8 +- .../NetworkSettledMetricResolverTest.kt | 1 + .../internal/SessionReplayFeatureTest.kt | 5 - .../async/RecordedDataQueueHandlerTest.kt | 16 +- .../resources/ResourceDataStoreManagerTest.kt | 8 +- .../trace/api/IdGenerationStrategyTest.kt | 7 +- .../datadog/trace/core/CoreSpanBuilderTest.kt | 7 +- .../com/datadog/trace/core/DDSpanTest.kt | 48 +++--- .../trace/core/PendingTraceTestBase.kt | 3 +- .../trace/internal/DatadogSpanLoggerTest.kt | 7 - .../utils/forge/SpanEventForgeryFactory.kt | 3 +- .../android/webview/WebViewTrackingTest.kt | 5 - .../domain/WebViewNativeRumViewsCacheTest.kt | 15 +- .../integration/tests/InternalSdkCoreTest.kt | 6 +- .../otel/OtelTracerProviderTest.kt | 23 +-- .../java/forge/SpanEventForgeryFactory.kt | 3 +- 43 files changed, 216 insertions(+), 415 deletions(-) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt index 4518216232..8912f4e350 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt @@ -1015,12 +1015,11 @@ internal class DatadogCoreTest { } @Test - fun `M provide time info without correction W time() {DefaultTimeProvider}`() { + fun `M provide time info without correction W time() {NoOpTimeProvider}`() { // Given testedCore.coreFeature = mock() - val timeProvider = DefaultTimeProvider() whenever(testedCore.coreFeature.initialized).thenReturn(AtomicBoolean()) - whenever(testedCore.coreFeature.timeProvider) doReturn timeProvider + whenever(testedCore.coreFeature.timeProvider) doReturn DefaultTimeProvider() // When val time = testedCore.time diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/BatchMetricsDispatcherTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/BatchMetricsDispatcherTest.kt index e6e479fdbe..5e0a9a4b48 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/BatchMetricsDispatcherTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/BatchMetricsDispatcherTest.kt @@ -10,7 +10,6 @@ import com.datadog.android.api.InternalLogger import com.datadog.android.api.feature.Feature import com.datadog.android.core.internal.configuration.DataUploadConfiguration import com.datadog.android.core.internal.persistence.file.FilePersistenceConfig -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import fr.xgouchet.elmyr.Forge @@ -58,7 +57,7 @@ internal class BatchMetricsDispatcherTest { @Mock lateinit var mockDateTimeProvider: TimeProvider - private var currentTimeInMillis: Long = DefaultTimeProvider().getDeviceTimestamp() + private var currentTimeInMillis: Long = System.currentTimeMillis() @Mock lateinit var mockInternalLogger: InternalLogger diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetryTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetryTest.kt index 1c6c6f8102..2485f16e0d 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetryTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetryTest.kt @@ -17,7 +17,6 @@ import com.datadog.android.core.internal.metrics.MethodCalledTelemetry.Companion import com.datadog.android.core.internal.metrics.MethodCalledTelemetry.Companion.METRIC_TYPE_VALUE import com.datadog.android.core.internal.metrics.MethodCalledTelemetry.Companion.OPERATION_NAME import com.datadog.android.core.metrics.PerformanceMetric.Companion.METRIC_TYPE -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.tools.unit.extensions.TestConfigurationExtension import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.FloatForgery @@ -45,9 +44,6 @@ import org.mockito.quality.Strictness ) @MockitoSettings(strictness = Strictness.LENIENT) internal class MethodCalledTelemetryTest { - - private val timeProvider = DefaultTimeProvider() - private lateinit var testedMethodCalledTelemetry: MethodCalledTelemetry @StringForgery @@ -109,7 +105,7 @@ internal class MethodCalledTelemetryTest { whenever(mockDeviceInfo.osVersion).thenReturn(fakeOsVersion) whenever(mockDeviceInfo.deviceBuildId).thenReturn(fakeOsBuild) - fakeStartTime = timeProvider.getDeviceElapsedTimeNs() + fakeStartTime = System.nanoTime() testedMethodCalledTelemetry = MethodCalledTelemetry( internalLogger = mockInternalLogger, operationName = fakeOperationName, @@ -141,7 +137,7 @@ internal class MethodCalledTelemetryTest { verify(mockInternalLogger).logMetric(any(), mapCaptor.capture(), eq(100.0f), eq(fakeCreationSampleRate)) val executionTime = mapCaptor.firstValue[EXECUTION_TIME] as Long - assertThat(executionTime).isLessThan(timeProvider.getDeviceElapsedTimeNs() - fakeStartTime) + assertThat(executionTime).isLessThan(System.nanoTime() - fakeStartTime) } @Test diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/FeatureFileOrchestratorTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/FeatureFileOrchestratorTest.kt index 651b1234ce..27fcecabbf 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/FeatureFileOrchestratorTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/FeatureFileOrchestratorTest.kt @@ -11,7 +11,6 @@ import com.datadog.android.core.internal.metrics.MetricsDispatcher import com.datadog.android.core.internal.persistence.file.FilePersistenceConfig import com.datadog.android.core.internal.persistence.file.batch.BatchFileOrchestrator import com.datadog.android.core.internal.privacy.ConsentProvider -import com.datadog.android.internal.time.TimeProvider import com.datadog.android.privacy.TrackingConsent import com.datadog.android.utils.forge.Configurator import fr.xgouchet.elmyr.annotation.Forgery @@ -65,9 +64,6 @@ internal class FeatureFileOrchestratorTest { @Mock lateinit var mockMetricsDispatcher: MetricsDispatcher - @Mock - lateinit var mockTimeProvider: TimeProvider - @BeforeEach fun `set up`() { whenever(mockConsentProvider.getConsent()) doReturn fakeConsent @@ -85,8 +81,7 @@ internal class FeatureFileOrchestratorTest { mockExecutorService, fakeFilePersistenceConfig, mockInternalLogger, - mockMetricsDispatcher, - mockTimeProvider + mockMetricsDispatcher ) // Then @@ -108,8 +103,7 @@ internal class FeatureFileOrchestratorTest { mockExecutorService, fakeFilePersistenceConfig, mockInternalLogger, - mockMetricsDispatcher, - mockTimeProvider + mockMetricsDispatcher ) // Then @@ -131,8 +125,7 @@ internal class FeatureFileOrchestratorTest { mockExecutorService, fakeFilePersistenceConfig, mockInternalLogger, - mockMetricsDispatcher, - mockTimeProvider + mockMetricsDispatcher ) // Then diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt index 157770d816..8d83ceeee4 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt @@ -12,7 +12,6 @@ import com.datadog.android.core.internal.metrics.MetricsDispatcher import com.datadog.android.core.internal.metrics.RemovalReason import com.datadog.android.core.internal.persistence.file.FileOrchestrator import com.datadog.android.core.internal.persistence.file.FilePersistenceConfig -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.utils.forge.Configurator import com.datadog.android.utils.verifyLog import fr.xgouchet.elmyr.Forge @@ -36,6 +35,7 @@ import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock +import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.verifyNoMoreInteractions @@ -77,8 +77,6 @@ internal class BatchFileOrchestratorTest { @Mock lateinit var mockPendingFiles: AtomicInteger - private val timeProvider = DefaultTimeProvider() - @BeforeEach fun `set up`() { whenever(mockPendingFiles.decrementAndGet()).thenReturn(fakePendingBatches) @@ -90,7 +88,6 @@ internal class BatchFileOrchestratorTest { config = TEST_PERSISTENCE_CONFIG, internalLogger = mockLogger, metricsDispatcher = mockMetricsDispatcher, - timeProvider = timeProvider, pendingFiles = mockPendingFiles ) } @@ -113,9 +110,9 @@ internal class BatchFileOrchestratorTest { @Test fun `M send batch_closed metric W getWritableFile()`() { // Given - val lowerTimestamp = timeProvider.getDeviceTimestamp() + val lowerTimestamp = System.currentTimeMillis() val oldFile = testedOrchestrator.getWritableFile() - val upperTimestamp = timeProvider.getDeviceTimestamp() + val upperTimestamp = System.currentTimeMillis() Thread.sleep(RECENT_DELAY_MS + 1) // When @@ -146,11 +143,10 @@ internal class BatchFileOrchestratorTest { val notADir = File(fakeRootDir, fileName) notADir.createNewFile() testedOrchestrator = BatchFileOrchestrator( - rootDir = notADir, - config = TEST_PERSISTENCE_CONFIG, - internalLogger = mockLogger, - metricsDispatcher = mockMetricsDispatcher, - timeProvider = timeProvider + notADir, + TEST_PERSISTENCE_CONFIG, + mockLogger, + mockMetricsDispatcher ) // When @@ -174,11 +170,10 @@ internal class BatchFileOrchestratorTest { whenever(corruptedDir.mkdirs()).thenReturn(false) whenever(corruptedDir.path) doReturn fakeRootDir.path testedOrchestrator = BatchFileOrchestrator( - rootDir = corruptedDir, - config = TEST_PERSISTENCE_CONFIG, - internalLogger = mockLogger, - metricsDispatcher = mockMetricsDispatcher, - timeProvider = timeProvider + corruptedDir, + TEST_PERSISTENCE_CONFIG, + mockLogger, + mockMetricsDispatcher ) // When @@ -203,11 +198,10 @@ internal class BatchFileOrchestratorTest { whenever(restrictedDir.canWrite()).thenReturn(false) whenever(restrictedDir.path) doReturn fakeRootDir.path testedOrchestrator = BatchFileOrchestrator( - rootDir = restrictedDir, - config = TEST_PERSISTENCE_CONFIG, - internalLogger = mockLogger, - metricsDispatcher = mockMetricsDispatcher, - timeProvider = timeProvider + restrictedDir, + TEST_PERSISTENCE_CONFIG, + mockLogger, + mockMetricsDispatcher ) // When @@ -242,19 +236,19 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val oldTimestamp = timeProvider.getDeviceTimestamp() - oldFileAge + val oldTimestamp = System.currentTimeMillis() - oldFileAge val oldFile = File(fakeRootDir, oldTimestamp.toString()) oldFile.createNewFile() val oldFileMeta = File("${oldFile.path}_metadata") oldFileMeta.createNewFile() - val youngTimestamp = timeProvider.getDeviceTimestamp() - RECENT_DELAY_MS - 1 + val youngTimestamp = System.currentTimeMillis() - RECENT_DELAY_MS - 1 val youngFile = File(fakeRootDir, youngTimestamp.toString()) youngFile.createNewFile() // When - val start = timeProvider.getDeviceTimestamp() + val start = System.currentTimeMillis() val result = testedOrchestrator.getWritableFile() - val end = timeProvider.getDeviceTimestamp() + val end = System.currentTimeMillis() // Then checkNotNull(result) @@ -280,19 +274,19 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val oldTimestamp = timeProvider.getDeviceTimestamp() - oldFileAge + val oldTimestamp = System.currentTimeMillis() - oldFileAge val oldFile = File(fakeRootDir, oldTimestamp.toString()) oldFile.createNewFile() val oldFileMeta = File("${oldFile.path}_metadata") oldFileMeta.createNewFile() - val youngTimestamp = timeProvider.getDeviceTimestamp() - RECENT_DELAY_MS - 1 + val youngTimestamp = System.currentTimeMillis() - RECENT_DELAY_MS - 1 val youngFile = File(fakeRootDir, youngTimestamp.toString()) youngFile.createNewFile() // When - val start = timeProvider.getDeviceTimestamp() + val start = System.currentTimeMillis() val result = testedOrchestrator.getWritableFile() - val end = timeProvider.getDeviceTimestamp() + val end = System.currentTimeMillis() // let's add very old file after the previous cleanup call. If threshold is respected, // cleanup shouldn't be performed during the next getWritableFile call val evenOlderFile = File(fakeRootDir, (oldTimestamp - 1).toString()) @@ -323,16 +317,16 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val oldTimestamp = timeProvider.getDeviceTimestamp() - oldFileAge + val oldTimestamp = System.currentTimeMillis() - oldFileAge val oldFile = File(fakeRootDir, oldTimestamp.toString()) oldFile.createNewFile() val oldFileMeta = File("${oldFile.path}_metadata") oldFileMeta.createNewFile() // When - val start = timeProvider.getDeviceTimestamp() + val start = System.currentTimeMillis() val result = testedOrchestrator.getWritableFile() - val end = timeProvider.getDeviceTimestamp() + val end = System.currentTimeMillis() Thread.sleep(CLEANUP_FREQUENCY_THRESHOLD_MS + 1) val evenOlderFile = File(fakeRootDir, (oldTimestamp - 1).toString()) evenOlderFile.createNewFile() @@ -376,9 +370,9 @@ internal class BatchFileOrchestratorTest { assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) // When - val start = timeProvider.getDeviceTimestamp() + val start = System.currentTimeMillis() val result = testedOrchestrator.getWritableFile() - val end = timeProvider.getDeviceTimestamp() + val end = System.currentTimeMillis() // Then checkNotNull(result) @@ -417,18 +411,18 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val beforeFileCreateTimestamp = timeProvider.getDeviceTimestamp() + val beforeFileCreateTimestamp = System.currentTimeMillis() val previousFile = testedOrchestrator.getWritableFile() - val afterFileCreateTimestamp = timeProvider.getDeviceTimestamp() + val afterFileCreateTimestamp = System.currentTimeMillis() checkNotNull(previousFile) previousFile.writeText(previousData) Thread.sleep(RECENT_DELAY_MS + 1) // When - val start = timeProvider.getDeviceTimestamp() + val start = System.currentTimeMillis() val result = testedOrchestrator.getWritableFile() - val end = timeProvider.getDeviceTimestamp() + val end = System.currentTimeMillis() // Then checkNotNull(result) @@ -453,14 +447,14 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val previousFile = File(fakeRootDir, timeProvider.getDeviceTimestamp().toString()) + val previousFile = File(fakeRootDir, System.currentTimeMillis().toString()) previousFile.writeText(previousData) Thread.sleep(1) // When - val start = timeProvider.getDeviceTimestamp() + val start = System.currentTimeMillis() val result = testedOrchestrator.getWritableFile() - val end = timeProvider.getDeviceTimestamp() + val end = System.currentTimeMillis() // Then checkNotNull(result) @@ -477,18 +471,18 @@ internal class BatchFileOrchestratorTest { fun `M return new File W getWritableFile() {previous file is deleted}`() { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val beforeFileCreateTimestamp = timeProvider.getDeviceTimestamp() + val beforeFileCreateTimestamp = System.currentTimeMillis() val previousFile = testedOrchestrator.getWritableFile() - val afterFileCreateTimestamp = timeProvider.getDeviceTimestamp() + val afterFileCreateTimestamp = System.currentTimeMillis() checkNotNull(previousFile) previousFile.createNewFile() previousFile.delete() Thread.sleep(1) // When - val start = timeProvider.getDeviceTimestamp() + val start = System.currentTimeMillis() val result = testedOrchestrator.getWritableFile() - val end = timeProvider.getDeviceTimestamp() + val end = System.currentTimeMillis() // Then checkNotNull(result) @@ -513,17 +507,17 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val beforeFileCreateTimestamp = timeProvider.getDeviceTimestamp() + val beforeFileCreateTimestamp = System.currentTimeMillis() val previousFile = testedOrchestrator.getWritableFile() - val afterFileCreateTimestamp = timeProvider.getDeviceTimestamp() + val afterFileCreateTimestamp = System.currentTimeMillis() checkNotNull(previousFile) previousFile.writeText(previousData) Thread.sleep(1) // When - val start = timeProvider.getDeviceTimestamp() + val start = System.currentTimeMillis() val result = testedOrchestrator.getWritableFile() - val end = timeProvider.getDeviceTimestamp() + val end = System.currentTimeMillis() // Then checkNotNull(result) @@ -548,7 +542,7 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val beforeFileCreateTimestamp = timeProvider.getDeviceTimestamp() + val beforeFileCreateTimestamp = System.currentTimeMillis() var previousFile = testedOrchestrator.getWritableFile() repeat(4) { @@ -565,12 +559,12 @@ internal class BatchFileOrchestratorTest { assumeTrue(file == previousFile) file?.appendText(previousData[i]) } - val afterLastFileUsageTimestamp = timeProvider.getDeviceTimestamp() + val afterLastFileUsageTimestamp = System.currentTimeMillis() // When - val start = timeProvider.getDeviceTimestamp() + val start = System.currentTimeMillis() val nextFile = testedOrchestrator.getWritableFile() - val end = timeProvider.getDeviceTimestamp() + val end = System.currentTimeMillis() // Then checkNotNull(nextFile) @@ -610,9 +604,9 @@ internal class BatchFileOrchestratorTest { // When Thread.sleep(CLEANUP_FREQUENCY_THRESHOLD_MS + 1) - val start = timeProvider.getDeviceTimestamp() + val start = System.currentTimeMillis() val result = testedOrchestrator.getWritableFile() - val end = timeProvider.getDeviceTimestamp() + val end = System.currentTimeMillis() // Then checkNotNull(result) @@ -649,8 +643,7 @@ internal class BatchFileOrchestratorTest { notADir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher, - timeProvider + mockMetricsDispatcher ) // When @@ -676,8 +669,7 @@ internal class BatchFileOrchestratorTest { corruptedDir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher, - timeProvider + mockMetricsDispatcher ) // When @@ -704,8 +696,7 @@ internal class BatchFileOrchestratorTest { restrictedDir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher, - timeProvider + mockMetricsDispatcher ) // When @@ -726,12 +717,12 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val oldTimestamp = timeProvider.getDeviceTimestamp() - oldFileAge + val oldTimestamp = System.currentTimeMillis() - oldFileAge val oldFile = File(fakeRootDir, oldTimestamp.toString()) oldFile.createNewFile() val oldFileMeta = File("${oldFile.path}_metadata") oldFileMeta.createNewFile() - val youngTimestamp = timeProvider.getDeviceTimestamp() - RECENT_DELAY_MS - 1 + val youngTimestamp = System.currentTimeMillis() - RECENT_DELAY_MS - 1 val youngFile = File(fakeRootDir, youngTimestamp.toString()) youngFile.createNewFile() @@ -773,7 +764,7 @@ internal class BatchFileOrchestratorTest { fun `M return file W getReadableFile() {existing old enough file}`() { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val timestamp = timeProvider.getDeviceTimestamp() - (RECENT_DELAY_MS * 2) + val timestamp = System.currentTimeMillis() - (RECENT_DELAY_MS * 2) val file = File(fakeRootDir, timestamp.toString()) file.createNewFile() @@ -791,7 +782,7 @@ internal class BatchFileOrchestratorTest { fun `M return null W getReadableFile() {file is too recent}`() { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val timestamp = timeProvider.getDeviceTimestamp() - (RECENT_DELAY_MS / 2) + val timestamp = System.currentTimeMillis() - (RECENT_DELAY_MS / 2) val file = File(fakeRootDir, timestamp.toString()) file.createNewFile() @@ -806,7 +797,7 @@ internal class BatchFileOrchestratorTest { fun `M return null W getReadableFile() {file is in exclude list}`() { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val timestamp = timeProvider.getDeviceTimestamp() - (RECENT_DELAY_MS * 2) + val timestamp = System.currentTimeMillis() - (RECENT_DELAY_MS * 2) val file = File(fakeRootDir, timestamp.toString()) file.createNewFile() @@ -832,8 +823,7 @@ internal class BatchFileOrchestratorTest { notADir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher, - timeProvider + mockMetricsDispatcher ) // When @@ -859,8 +849,7 @@ internal class BatchFileOrchestratorTest { corruptedDir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher, - timeProvider + mockMetricsDispatcher ) // When @@ -887,8 +876,7 @@ internal class BatchFileOrchestratorTest { restrictedDir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher, - timeProvider + mockMetricsDispatcher ) // When @@ -934,8 +922,8 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val old = timeProvider.getDeviceTimestamp() - (RECENT_DELAY_MS * 2) - val new = timeProvider.getDeviceTimestamp() - (RECENT_DELAY_MS / 2) + val old = System.currentTimeMillis() - (RECENT_DELAY_MS * 2) + val new = System.currentTimeMillis() - (RECENT_DELAY_MS / 2) val expectedFiles = mutableListOf() for (i in 1..count) { // create both non readable and non writable files @@ -980,8 +968,8 @@ internal class BatchFileOrchestratorTest { ) { // Given assumeTrue(fakeRootDir.listFiles().isNullOrEmpty()) - val old = timeProvider.getDeviceTimestamp() - (RECENT_DELAY_MS * 2) - val new = timeProvider.getDeviceTimestamp() - (RECENT_DELAY_MS / 2) + val old = System.currentTimeMillis() - (RECENT_DELAY_MS * 2) + val new = System.currentTimeMillis() - (RECENT_DELAY_MS / 2) val expectedFiles = mutableListOf() for (i in 1..count) { // create both non readable and non writable files @@ -1027,8 +1015,7 @@ internal class BatchFileOrchestratorTest { notADir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher, - timeProvider + mockMetricsDispatcher ) // When @@ -1054,8 +1041,7 @@ internal class BatchFileOrchestratorTest { corruptedDir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher, - timeProvider + mockMetricsDispatcher ) // When @@ -1082,8 +1068,7 @@ internal class BatchFileOrchestratorTest { restrictedDir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher, - timeProvider + mockMetricsDispatcher ) // When @@ -1159,7 +1144,7 @@ internal class BatchFileOrchestratorTest { @Test fun `M return metadata file W getMetadataFile()`() { // Given - val fakeFileName = timeProvider.getDeviceTimestamp().toString() + val fakeFileName = System.currentTimeMillis().toString() val fakeFile = File(fakeRootDir.path, fakeFileName) // When @@ -1175,7 +1160,7 @@ internal class BatchFileOrchestratorTest { @StringForgery fakeSuffix: String ) { // Given - val fakeFileName = timeProvider.getDeviceTimestamp().toString() + val fakeFileName = System.currentTimeMillis().toString() val fakeFile = File("${fakeRootDir.parent}$fakeSuffix", fakeFileName) // When diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt index 19079ab16c..bb01164101 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt @@ -10,7 +10,6 @@ import android.os.Build import android.os.Process import android.os.SystemClock import com.datadog.android.core.internal.system.BuildSdkVersionProvider -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.DdRumContentProvider import fr.xgouchet.elmyr.annotation.IntForgery import fr.xgouchet.elmyr.junit5.ForgeExtension @@ -28,9 +27,6 @@ import java.util.concurrent.TimeUnit ExtendWith(ForgeExtension::class) ) class DefaultAppStartTimeProviderTest { - - private val timeProvider = DefaultTimeProvider() - @Test fun `M return process start time W appStartTime { N+ }`( @IntForgery(min = Build.VERSION_CODES.N) apiVersion: Int @@ -39,11 +35,11 @@ class DefaultAppStartTimeProviderTest { val mockBuildSdkVersionProvider: BuildSdkVersionProvider = mock() whenever(mockBuildSdkVersionProvider.version) doReturn apiVersion val diffMs = SystemClock.elapsedRealtime() - Process.getStartElapsedRealtime() - val startTimeNs = timeProvider.getDeviceElapsedTimeNs() - TimeUnit.MILLISECONDS.toNanos(diffMs) + val startTimeNs = System.nanoTime() - TimeUnit.MILLISECONDS.toNanos(diffMs) // WHEN - val defaultAppStartTimeProvider = DefaultAppStartTimeProvider(timeProvider, mockBuildSdkVersionProvider) - val providedStartTime = defaultAppStartTimeProvider.appStartTimeNs + val timeProvider = DefaultAppStartTimeProvider(mockBuildSdkVersionProvider) + val providedStartTime = timeProvider.appStartTimeNs // THEN assertThat(providedStartTime) @@ -60,8 +56,8 @@ class DefaultAppStartTimeProviderTest { val startTimeNs = DdRumContentProvider.createTimeNs // WHEN - val defaultAppStartTimeProvider = DefaultAppStartTimeProvider(timeProvider, mockBuildSdkVersionProvider) - val providedStartTime = defaultAppStartTimeProvider.appStartTimeNs + val timeProvider = DefaultAppStartTimeProvider(mockBuildSdkVersionProvider) + val providedStartTime = timeProvider.appStartTimeNs // THEN assertThat(providedStartTime).isEqualTo(startTimeNs) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/KronosTimeProviderTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/KronosTimeProviderTest.kt index 4cb7877d3a..09be35bcfb 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/KronosTimeProviderTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/KronosTimeProviderTest.kt @@ -57,7 +57,7 @@ internal class KronosTimeProviderTest { @Test fun `returns server time offset in nanoseconds`() { - val now = testedTimeProvider.getDeviceTimestamp() + val now = System.currentTimeMillis() val result = testedTimeProvider.getServerOffsetNanos() val expectedOffset = TimeUnit.MILLISECONDS.toNanos(fakeDate.time - now) @@ -69,7 +69,7 @@ internal class KronosTimeProviderTest { @Test fun `returns server time offset in milliseconds`() { - val now = testedTimeProvider.getDeviceTimestamp() + val now = System.currentTimeMillis() val result = testedTimeProvider.getServerOffsetMillis() val expectedOffset = fakeDate.time - now @@ -81,7 +81,7 @@ internal class KronosTimeProviderTest { @Test fun `returns device time`() { - val now = testedTimeProvider.getDeviceTimestamp() + val now = System.currentTimeMillis() val result = testedTimeProvider.getDeviceTimestamp() assertThat(result).isCloseTo(now, Offset.offset(TEST_OFFSET)) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/forge/NdkCrashLogForgeryFactory.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/forge/NdkCrashLogForgeryFactory.kt index 78002701b7..2148b51fb4 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/forge/NdkCrashLogForgeryFactory.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/utils/forge/NdkCrashLogForgeryFactory.kt @@ -6,7 +6,6 @@ package com.datadog.android.utils.forge -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.ndk.internal.NdkCrashLog import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.ForgeryFactory @@ -16,7 +15,7 @@ internal class NdkCrashLogForgeryFactory : override fun getForgery(forge: Forge): NdkCrashLog { return NdkCrashLog( signal = forge.anInt(min = 1), - timestamp = DefaultTimeProvider().getDeviceTimestamp(), + timestamp = System.currentTimeMillis(), timeSinceAppStartMs = forge.aNullable { aPositiveLong() }, signalName = forge.anAlphabeticalString(), message = forge.anAlphabeticalString(), diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessorTest.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessorTest.kt index 9465495e26..602ea1a461 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessorTest.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessorTest.kt @@ -11,8 +11,6 @@ import com.datadog.android.flags.internal.storage.RecordWriter import com.datadog.android.flags.model.EvaluationContext import com.datadog.android.flags.model.ExposureEvent import com.datadog.android.flags.utils.forge.ForgeConfigurator -import com.datadog.android.internal.time.DefaultTimeProvider -import com.datadog.android.internal.time.TimeProvider import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration @@ -28,10 +26,8 @@ import org.mockito.kotlin.any import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.atLeast import org.mockito.kotlin.atMost -import org.mockito.kotlin.doReturn import org.mockito.kotlin.times import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever @ExtendWith(MockitoExtension::class, ForgeExtension::class) @ForgeConfiguration(ForgeConfigurator::class) @@ -40,9 +36,6 @@ internal class ExposureEventsProcessorTest { @Mock lateinit var mockRecordWriter: RecordWriter - @Mock - lateinit var mockTimeProvider: TimeProvider - @StringForgery lateinit var fakeFlagName: String @@ -58,13 +51,9 @@ internal class ExposureEventsProcessorTest { private lateinit var testedProcessor: ExposureEventsProcessor private lateinit var fakeFlag: PrecomputedFlag - private val timeProvider: TimeProvider = DefaultTimeProvider() - @BeforeEach fun `set up`(forge: Forge) { - val fakeTimestamp = forge.aLong(min = 1) - whenever(mockTimeProvider.getDeviceTimestamp()) doReturn fakeTimestamp - testedProcessor = ExposureEventsProcessor(mockRecordWriter, mockTimeProvider) + testedProcessor = ExposureEventsProcessor(mockRecordWriter) fakeFlag = forge.getForgery().copy( allocationKey = fakeAllocationKey, variationKey = fakeVariationKey @@ -229,8 +218,6 @@ internal class ExposureEventsProcessorTest { @Test fun `M generate consistent timestamps W processEvent() { multiple calls }`(forge: Forge) { // Given - whenever(mockTimeProvider.getDeviceTimestamp()).thenAnswer { timeProvider.getDeviceTimestamp() } - val fakeContext1 = EvaluationContext( targetingKey = forge.anAlphabeticalString(), attributes = mapOf("user_id" to forge.anAlphabeticalString()) @@ -240,13 +227,13 @@ internal class ExposureEventsProcessorTest { attributes = mapOf("user_id" to forge.anAlphabeticalString()) ) - val beforeTime = timeProvider.getDeviceTimestamp() + val beforeTime = System.currentTimeMillis() // When testedProcessor.processEvent(fakeFlagName, fakeContext1, fakeFlag) testedProcessor.processEvent(fakeFlagName, fakeContext2, fakeFlag) - val afterTime = timeProvider.getDeviceTimestamp() + val afterTime = System.currentTimeMillis() // Then val eventCaptor = argumentCaptor() @@ -494,7 +481,7 @@ internal class ExposureEventsProcessorTest { Thread { repeat(10) { Thread.sleep(5) - testedProcessor = ExposureEventsProcessor(mockRecordWriter, mockTimeProvider) + testedProcessor = ExposureEventsProcessor(mockRecordWriter) } } } diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/FlagsFeatureTest.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/FlagsFeatureTest.kt index 33d68f3538..74f53391ed 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/FlagsFeatureTest.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/FlagsFeatureTest.kt @@ -15,7 +15,6 @@ import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.flags.FlagsConfiguration import com.datadog.android.flags.internal.storage.ExposureEventRecordWriter import com.datadog.android.flags.internal.storage.NoOpRecordWriter -import com.datadog.android.internal.time.TimeProvider import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeExtension @@ -52,9 +51,6 @@ internal class FlagsFeatureTest { @Mock lateinit var mockInternalLogger: InternalLogger - @Mock - lateinit var mockTimeProvider: TimeProvider - @Mock lateinit var mockContext: Context @@ -66,7 +62,6 @@ internal class FlagsFeatureTest { @BeforeEach fun `set up`() { whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger - whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider whenever(mockSdkCore.createSingleThreadExecutorService(any())) doReturn mockExecutorService // Setup mockContext with default release build (flags = 0) diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateDeserializerTest.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateDeserializerTest.kt index 6634bfda10..957a1ed416 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateDeserializerTest.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateDeserializerTest.kt @@ -7,7 +7,6 @@ package com.datadog.android.flags.internal.persistence import com.datadog.android.api.InternalLogger -import com.datadog.android.internal.time.DefaultTimeProvider import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.junit5.ForgeExtension import org.assertj.core.api.Assertions.assertThat @@ -29,8 +28,6 @@ import org.mockito.quality.Strictness @MockitoSettings(strictness = Strictness.LENIENT) internal class FlagsStateDeserializerTest { - private val timeProvider = DefaultTimeProvider() - @Mock lateinit var mockInternalLogger: InternalLogger @@ -48,7 +45,7 @@ internal class FlagsStateDeserializerTest { val stringAttr = forge.anAlphabeticalString() val numberAttr = forge.anInt().toString() val booleanAttr = forge.aBool().toString() - val timestamp = timeProvider.getDeviceTimestamp() + val timestamp = System.currentTimeMillis() val json = JSONObject().apply { put( @@ -110,7 +107,7 @@ internal class FlagsStateDeserializerTest { fun `M deserialize empty state W deserialize() { valid JSON with empty data }`(forge: Forge) { // Given val targetingKey = forge.anAlphabeticalString() - val timestamp = timeProvider.getDeviceTimestamp() + val timestamp = System.currentTimeMillis() val json = JSONObject().apply { put( @@ -175,7 +172,7 @@ internal class FlagsStateDeserializerTest { fun `M deserialize with empty attributes W deserialize() { missing attributes field }`(forge: Forge) { // Given val targetingKey = forge.anAlphabeticalString() - val timestamp = timeProvider.getDeviceTimestamp() + val timestamp = System.currentTimeMillis() val json = JSONObject().apply { put( @@ -203,7 +200,7 @@ internal class FlagsStateDeserializerTest { fun `M skip invalid flag and process others W deserialize() { one invalid flag }`(forge: Forge) { // Given val targetingKey = forge.anAlphabeticalString() - val timestamp = timeProvider.getDeviceTimestamp() + val timestamp = System.currentTimeMillis() val json = JSONObject().apply { put( @@ -285,7 +282,7 @@ internal class FlagsStateDeserializerTest { } ) put("flags", JSONObject()) - put("lastUpdateTimestamp", timeProvider.getDeviceTimestamp()) + put("lastUpdateTimestamp", System.currentTimeMillis()) } // When diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateSerializerTest.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateSerializerTest.kt index 38d85bbc0c..762aac3651 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateSerializerTest.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateSerializerTest.kt @@ -10,7 +10,6 @@ import com.datadog.android.api.InternalLogger import com.datadog.android.flags.internal.model.FlagsStateEntry import com.datadog.android.flags.internal.model.PrecomputedFlag import com.datadog.android.flags.model.EvaluationContext -import com.datadog.android.internal.time.DefaultTimeProvider import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.junit5.ForgeExtension import org.assertj.core.api.Assertions.assertThat @@ -24,8 +23,6 @@ import org.mockito.junit.jupiter.MockitoExtension @ExtendWith(MockitoExtension::class, ForgeExtension::class) internal class FlagsStateSerializerTest { - private val timeProvider = DefaultTimeProvider() - @Mock lateinit var mockInternalLogger: InternalLogger @@ -68,7 +65,7 @@ internal class FlagsStateSerializerTest { ) ) - val timestamp = timeProvider.getDeviceTimestamp() + val timestamp = System.currentTimeMillis() val flagsState = FlagsStateEntry(evaluationContext, flags, timestamp) // When diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/repository/DefaultFlagsRepositoryTest.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/repository/DefaultFlagsRepositoryTest.kt index dbb28f050b..1f7ae7ad4e 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/repository/DefaultFlagsRepositoryTest.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/repository/DefaultFlagsRepositoryTest.kt @@ -14,7 +14,6 @@ import com.datadog.android.flags.internal.model.FlagsStateEntry import com.datadog.android.flags.internal.model.PrecomputedFlag import com.datadog.android.flags.model.EvaluationContext import com.datadog.android.flags.utils.forge.ForgeConfigurator -import com.datadog.android.internal.time.TimeProvider import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension @@ -51,15 +50,11 @@ internal class DefaultFlagsRepositoryTest { @Mock lateinit var mockInternalLogger: InternalLogger - @Mock - lateinit var mockTimeProvider: TimeProvider - private lateinit var testedRepository: DefaultFlagsRepository @BeforeEach fun `set up`() { whenever(mockFeatureSdkCore.internalLogger) doReturn mockInternalLogger - whenever(mockFeatureSdkCore.timeProvider) doReturn mockTimeProvider whenever( mockDataStore.value( key = any(), diff --git a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/LogsFeatureTest.kt b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/LogsFeatureTest.kt index 77bc044331..119e5bd81e 100644 --- a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/LogsFeatureTest.kt +++ b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/LogsFeatureTest.kt @@ -19,7 +19,6 @@ import com.datadog.android.api.storage.EventType import com.datadog.android.api.storage.FeatureStorageConfiguration import com.datadog.android.event.EventMapper import com.datadog.android.event.MapperSerializer -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.utils.NULL_MAP_VALUE import com.datadog.android.log.LogAttributes import com.datadog.android.log.internal.domain.event.LogEventMapperWrapper @@ -127,13 +126,11 @@ internal class LogsFeatureTest { private var fakeServerTimeOffset: Long = 0L - private val timeProvider = DefaultTimeProvider() - @BeforeEach fun `set up`( forge: Forge ) { - val now = timeProvider.getDeviceTimestamp() + val now = System.currentTimeMillis() fakeServerTimeOffset = forge.aLong(min = -now, max = Long.MAX_VALUE - now) whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger diff --git a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt index beafbe260d..8fae036bb2 100644 --- a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt +++ b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt @@ -15,7 +15,6 @@ import com.datadog.android.api.storage.DataWriter import com.datadog.android.api.storage.EventBatchWriter import com.datadog.android.api.storage.EventType import com.datadog.android.core.sampling.Sampler -import com.datadog.android.internal.time.TimeProvider import com.datadog.android.log.LogAttributes import com.datadog.android.log.assertj.LogEventAssert.Companion.assertThat import com.datadog.android.log.internal.LogsFeature @@ -70,7 +69,6 @@ internal class DatadogLogHandlerTest { private lateinit var fakeTags: Set private lateinit var fakeAttributes: Map private var fakeLevel: Int = 0 - private var fakeCurrentTimestamp: Long = 0 @Forgery lateinit var fakeThrowable: Throwable @@ -114,16 +112,12 @@ internal class DatadogLogHandlerTest { @Mock lateinit var mockSampler: Sampler - @Mock - lateinit var mockTimeProvider: TimeProvider - @BeforeEach fun `set up`(forge: Forge) { fakeServiceName = forge.anAlphabeticalString() fakeLoggerName = forge.anAlphabeticalString() fakeMessage = forge.anAlphabeticalString() fakeLevel = forge.anInt(2, 8) - fakeCurrentTimestamp = forge.aLong(min = 1) fakeAttributes = forge.aMap { anAlphabeticalString() to anInt() } fakeTags = forge.aList { anAlphabeticalString() }.toSet() fakeDatadogContext = fakeDatadogContext.copy( @@ -144,9 +138,6 @@ internal class DatadogLogHandlerTest { } ) - whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider - whenever(mockTimeProvider.getDeviceTimestamp()) doReturn fakeCurrentTimestamp - whenever( mockSdkCore.getFeature(Feature.LOGS_FEATURE_NAME) ) doReturn mockLogsFeatureScope @@ -178,7 +169,7 @@ internal class DatadogLogHandlerTest { @Test fun `forward log to LogWriter`() { // Given - val now = fakeCurrentTimestamp + val now = System.currentTimeMillis() // When testedHandler.handleLog( @@ -260,7 +251,7 @@ internal class DatadogLogHandlerTest { @Test fun `forward log to LogWriter with throwable`() { // Given - val now = fakeCurrentTimestamp + val now = System.currentTimeMillis() // When testedHandler.handleLog( @@ -325,7 +316,7 @@ internal class DatadogLogHandlerTest { @StringForgery errorStack: String ) { // Given - val now = fakeCurrentTimestamp + val now = System.currentTimeMillis() // When testedHandler.handleLog( @@ -641,7 +632,7 @@ internal class DatadogLogHandlerTest { @Test fun `forward log to LogWriter on background thread`(forge: Forge) { // Given - val now = fakeCurrentTimestamp + val now = System.currentTimeMillis() val threadName = forge.anAlphabeticalString() val countDownLatch = CountDownLatch(1) @@ -705,7 +696,7 @@ internal class DatadogLogHandlerTest { @Test fun `forward log to LogWriter without network info`() { // Given - val now = fakeCurrentTimestamp + val now = System.currentTimeMillis() testedHandler = DatadogLogHandler( loggerName = fakeLoggerName, logGenerator = DatadogLogGenerator( @@ -767,7 +758,7 @@ internal class DatadogLogHandlerTest { @Test fun `forward minimal log to LogWriter`() { // Given - val now = fakeCurrentTimestamp + val now = System.currentTimeMillis() fakeDatadogContext = fakeDatadogContext.copy( featuresContext = fakeDatadogContext.featuresContext.toMutableMap().apply { remove(Feature.RUM_FEATURE_NAME) @@ -1079,7 +1070,7 @@ internal class DatadogLogHandlerTest { @Test fun `it will sample in the logs when required`() { // Given - val now = fakeCurrentTimestamp + val now = System.currentTimeMillis() whenever(mockSampler.sample(Unit)).thenReturn(true) testedHandler = DatadogLogHandler( loggerName = fakeLoggerName, diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/RumTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/RumTest.kt index 3b33563d16..564f260bf1 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/RumTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/RumTest.kt @@ -13,7 +13,6 @@ import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.sampling.RateBasedSampler -import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.internal.RumFeature import com.datadog.android.rum.internal.monitor.DatadogRumMonitor import com.datadog.android.rum.internal.monitor.NoOpAdvancedRumMonitor @@ -60,16 +59,12 @@ internal class RumTest { @Mock lateinit var mockSdkCore: InternalSdkCore - @Mock - lateinit var mockTimeProvider: TimeProvider - @BeforeEach fun `set up`() { whenever(mockSdkCore.internalLogger) doReturn mock() whenever(mockSdkCore.firstPartyHostResolver) doReturn mock() whenever(mockSdkCore.createSingleThreadExecutorService(any())) doReturn mock() whenever(mockSdkCore.createScheduledExecutorService(any())) doReturn mock() - whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider } @Test diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporterTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporterTest.kt index 222f6e626a..938a0e8653 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporterTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporterTest.kt @@ -19,8 +19,6 @@ import com.datadog.android.api.storage.EventType import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.feature.event.ThreadDump import com.datadog.android.core.internal.persistence.Deserializer -import com.datadog.android.internal.time.DefaultTimeProvider -import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.RumErrorSource import com.datadog.android.rum.assertj.ErrorEventAssert import com.datadog.android.rum.assertj.ViewEventAssert @@ -69,8 +67,6 @@ import org.mockito.quality.Strictness @ForgeConfiguration(Configurator::class) internal class DatadogLateCrashReporterTest { - private val timeProvider = DefaultTimeProvider() - private lateinit var testedHandler: LateCrashReporter @Mock @@ -97,16 +93,12 @@ internal class DatadogLateCrashReporterTest { @Mock lateinit var mockInternalLogger: InternalLogger - @Mock - lateinit var mockTimeProvider: TimeProvider - @Forgery lateinit var fakeDatadogContext: DatadogContext @BeforeEach fun `set up`() { whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger - whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider whenever(mockSdkCore.getFeature(Feature.RUM_FEATURE_NAME)) doReturn mockRumFeatureScope whenever(mockEventWriteScope.invoke(any())) doAnswer { @@ -148,7 +140,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = timeProvider.getDeviceTimestamp() - forge.aLong( + date = System.currentTimeMillis() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ), @@ -249,7 +241,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = timeProvider.getDeviceTimestamp() - forge.aLong( + date = System.currentTimeMillis() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ), @@ -351,7 +343,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = timeProvider.getDeviceTimestamp() - forge.aLong( + date = System.currentTimeMillis() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ), @@ -410,7 +402,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = timeProvider.getDeviceTimestamp() - forge.aLong( + date = System.currentTimeMillis() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ), @@ -495,7 +487,7 @@ internal class DatadogLateCrashReporterTest { ) ) val fakeViewEvent = viewEvent.copy( - date = timeProvider.getDeviceTimestamp() - forge.aLong( + date = System.currentTimeMillis() - forge.aLong( min = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD + 1 ), usr = ViewEvent.Usr( @@ -700,7 +692,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = timeProvider.getDeviceTimestamp() - forge.aLong( + date = System.currentTimeMillis() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ), @@ -797,7 +789,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = timeProvider.getDeviceTimestamp() - forge.aLong( + date = System.currentTimeMillis() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ), @@ -881,7 +873,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = timeProvider.getDeviceTimestamp() - forge.aLong( + date = System.currentTimeMillis() - forge.aLong( min = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD + 1 ), usr = ViewEvent.Usr( @@ -972,7 +964,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = timeProvider.getDeviceTimestamp() - forge.aLong( + date = System.currentTimeMillis() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ) @@ -1029,7 +1021,7 @@ internal class DatadogLateCrashReporterTest { ) { // Given val fakeViewEvent = viewEvent.copy( - date = timeProvider.getDeviceTimestamp() - forge.aLong( + date = System.currentTimeMillis() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ) @@ -1069,7 +1061,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = timeProvider.getDeviceTimestamp() - forge.aLong( + date = System.currentTimeMillis() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ) @@ -1106,7 +1098,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = timeProvider.getDeviceTimestamp() - forge.aLong( + date = System.currentTimeMillis() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ) @@ -1144,7 +1136,7 @@ internal class DatadogLateCrashReporterTest { ) val fakeViewEvent = viewEvent.copy( - date = timeProvider.getDeviceTimestamp() - forge.aLong( + date = System.currentTimeMillis() - forge.aLong( min = 0L, max = DatadogLateCrashReporter.VIEW_EVENT_AVAILABILITY_TIME_THRESHOLD - 1000 ) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt index f9ca422055..78f5fdf4bc 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt @@ -25,7 +25,6 @@ import com.datadog.android.event.EventMapper import com.datadog.android.event.MapperSerializer import com.datadog.android.internal.flags.RumFlagEvaluationMessage import com.datadog.android.internal.telemetry.InternalTelemetryEvent -import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.GlobalRumMonitor import com.datadog.android.rum.RumErrorSource import com.datadog.android.rum.assertj.RumFeatureAssert @@ -148,14 +147,10 @@ internal class RumFeatureTest { @Mock lateinit var mockScheduledExecutorService: ScheduledExecutorService - @Mock - lateinit var mockTimeProvider: TimeProvider - @BeforeEach fun `set up`() { whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger whenever(mockSdkCore.createScheduledExecutorService(any())) doReturn mockScheduledExecutorService - whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider val mockContentResolver = mock() whenever(appContext.mockInstance.contentResolver) doReturn mockContentResolver diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/TimeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/TimeTest.kt index 35dc5a0182..292d46ba93 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/TimeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/TimeTest.kt @@ -6,8 +6,6 @@ package com.datadog.android.rum.internal.domain -import com.datadog.android.internal.time.DefaultTimeProvider -import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.utils.forge.Configurator import fr.xgouchet.elmyr.annotation.LongForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration @@ -22,17 +20,15 @@ import java.util.concurrent.TimeUnit @ForgeConfiguration(Configurator::class) internal class TimeTest { - private val timeProvider: TimeProvider = DefaultTimeProvider() - @Test fun `creates Time with current millis and nanos`() { - val startMs = timeProvider.getDeviceTimestamp() - val startNs = timeProvider.getDeviceElapsedTimeNs() + val startMs = System.currentTimeMillis() + val startNs = System.nanoTime() val time = Time() - val endNs = timeProvider.getDeviceElapsedTimeNs() - val endMs = timeProvider.getDeviceTimestamp() + val endNs = System.nanoTime() + val endMs = System.currentTimeMillis() assertThat(time.timestamp).isBetween(startMs, endMs) assertThat(time.nanoTime).isBetween(startNs, endNs) @@ -42,11 +38,11 @@ internal class TimeTest { fun `M convert timestamp to Time W asTime()`( @LongForgery(1000000000000, 2000000000000) timestamp: Long ) { - val startMs = timeProvider.getDeviceTimestamp() - val startNs = timeProvider.getDeviceElapsedTimeNs() + val startMs = System.currentTimeMillis() + val startNs = System.nanoTime() val time = timestamp.asTime() - val endNs = timeProvider.getDeviceElapsedTimeNs() - val endMs = timeProvider.getDeviceTimestamp() + val endNs = System.nanoTime() + val endMs = System.currentTimeMillis() assertThat(time.timestamp).isEqualTo(timestamp) val nanoOffset = time.nanoTime - ((startNs + endNs) / 2) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReaderTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReaderTest.kt index bf6ad2cf8a..2517883d08 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReaderTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReaderTest.kt @@ -20,8 +20,6 @@ import android.view.View import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener import com.datadog.android.api.InternalLogger -import com.datadog.android.internal.time.DefaultTimeProvider -import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.internal.domain.accessibility.DefaultAccessibilityReader.Companion.CAPTIONING_ENABLED_KEY import com.datadog.android.rum.utils.forge.Configurator import com.datadog.tools.unit.annotations.TestTargetApi @@ -86,9 +84,6 @@ internal class DefaultAccessibilityReaderTest { @Mock lateinit var mockHandler: Handler - @Mock - lateinit var mockTimeProvider: TimeProvider - private lateinit var testedReader: DefaultAccessibilityReader @BeforeEach @@ -111,8 +106,7 @@ internal class DefaultAccessibilityReaderTest { accessibilityManager = mockAccessibilityManager, secureWrapper = mockSecureWrapper, globalWrapper = mockGlobalWrapper, - handler = mockHandler, - timeProvider = mockTimeProvider + handler = mockHandler ) } @@ -168,8 +162,7 @@ internal class DefaultAccessibilityReaderTest { accessibilityManager = null, secureWrapper = mockSecureWrapper, globalWrapper = mockGlobalWrapper, - handler = mockHandler, - timeProvider = mockTimeProvider + handler = mockHandler ) // When @@ -348,8 +341,7 @@ internal class DefaultAccessibilityReaderTest { accessibilityManager = mockAccessibilityManager, secureWrapper = mockSecureWrapper, globalWrapper = mockGlobalWrapper, - handler = mockHandler, - timeProvider = mockTimeProvider + handler = mockHandler ) // When @@ -629,8 +621,7 @@ internal class DefaultAccessibilityReaderTest { accessibilityManager = null, secureWrapper = mockSecureWrapper, globalWrapper = mockGlobalWrapper, - handler = mockHandler, - timeProvider = mockTimeProvider + handler = mockHandler ) testedReader.getState() @@ -765,23 +756,20 @@ internal class DefaultAccessibilityReaderTest { fun `M update lastPollTime W getState { after threshold exceeded }`() { // Given whenever(mockActivityManager.lockTaskModeState) doReturn ActivityManager.LOCK_TASK_MODE_NONE - val realTimeProvider = DefaultTimeProvider() // Initialize - whenever(mockTimeProvider.getDeviceTimestamp()) doReturn realTimeProvider.getDeviceTimestamp() testedReader.getState() val lastPollTimeField = testedReader.javaClass.getDeclaredField("lastPollTime") lastPollTimeField.isAccessible = true val lastPollTime = lastPollTimeField.get(testedReader) as AtomicLong - val oldTime = realTimeProvider.getDeviceTimestamp() - 31_000 + val oldTime = System.currentTimeMillis() - 31_000 lastPollTime.set(oldTime) // When - Call after threshold exceeded - val currentTimeBefore = realTimeProvider.getDeviceTimestamp() - whenever(mockTimeProvider.getDeviceTimestamp()) doReturn currentTimeBefore + val currentTimeBefore = System.currentTimeMillis() testedReader.getState() - val currentTimeAfter = realTimeProvider.getDeviceTimestamp() + val currentTimeAfter = System.currentTimeMillis() // Then - lastPollTime should be updated to current time (within reasonable range) val newPollTime = lastPollTime.get() diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/battery/DefaultBatteryInfoProviderTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/battery/DefaultBatteryInfoProviderTest.kt index 51cc4105e1..bd52b06454 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/battery/DefaultBatteryInfoProviderTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/battery/DefaultBatteryInfoProviderTest.kt @@ -15,7 +15,6 @@ import android.os.BatteryManager import android.os.BatteryManager.BATTERY_PROPERTY_CAPACITY import android.os.Build import android.os.PowerManager -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.utils.forge.Configurator import com.datadog.tools.unit.annotations.TestTargetApi import com.datadog.tools.unit.extensions.ApiLevelExtension @@ -63,7 +62,7 @@ internal class DefaultBatteryInfoProviderTest { @Mock lateinit var mockBatteryManager: BatteryManager - private val testSuiteStartTime = DefaultTimeProvider().getDeviceTimestamp() + private val testSuiteStartTime = System.currentTimeMillis() private val shortPollingInterval = 200 diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeAttributePropagationTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeAttributePropagationTest.kt index 8ccd88e5a9..df709cb7a9 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeAttributePropagationTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeAttributePropagationTest.kt @@ -14,7 +14,6 @@ import com.datadog.android.api.feature.EventWriteScope import com.datadog.android.api.feature.FeatureScope import com.datadog.android.api.storage.DataWriter import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.RumSessionListener import com.datadog.android.rum.RumSessionType import com.datadog.android.rum.internal.FeaturesContextResolver @@ -129,8 +128,6 @@ internal class RumApplicationScopeAttributePropagationTest { @Mock lateinit var mockSlowFramesListener: SlowFramesListener - private val timeProvider = DefaultTimeProvider() - lateinit var fakeEventTime: Time lateinit var fakeEvent: RumRawEvent @@ -172,8 +169,8 @@ internal class RumApplicationScopeAttributePropagationTest { ) val fakeOffset = -forge.aLong(1000, 50000) - val fakeTimestamp = timeProvider.getDeviceTimestamp() + fakeOffset - val fakeNanos = timeProvider.getDeviceElapsedTimeNs() + TimeUnit.MILLISECONDS.toNanos(fakeOffset) + val fakeTimestamp = System.currentTimeMillis() + fakeOffset + val fakeNanos = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(fakeOffset) val maxLimit = max(Long.MAX_VALUE - fakeTimestamp, Long.MAX_VALUE) val minLimit = min(-fakeTimestamp, maxLimit) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeAttributePropagationTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeAttributePropagationTest.kt index d353bb4b8a..16e77a2794 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeAttributePropagationTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeAttributePropagationTest.kt @@ -18,7 +18,6 @@ import com.datadog.android.api.storage.EventBatchWriter import com.datadog.android.api.storage.EventType import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.RumAttributes import com.datadog.android.rum.RumErrorSource import com.datadog.android.rum.RumSessionType @@ -136,8 +135,6 @@ internal class RumViewScopeAttributePropagationTest { @Mock lateinit var mockSlowFramesListener: SlowFramesListener - private val timeProvider = DefaultTimeProvider() - @Forgery lateinit var fakeKey: RumScopeKey @@ -222,8 +219,8 @@ internal class RumViewScopeAttributePropagationTest { fakeParentContext.copy(syntheticsTestId = null, syntheticsResultId = null) val fakeOffset = -forge.aLong(1000, 50000) - val fakeTimestamp = timeProvider.getDeviceTimestamp() + fakeOffset - val fakeNanos = timeProvider.getDeviceElapsedTimeNs() + TimeUnit.MILLISECONDS.toNanos(fakeOffset) + val fakeTimestamp = System.currentTimeMillis() + fakeOffset + val fakeNanos = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(fakeOffset) val maxLimit = max(Long.MAX_VALUE - fakeTimestamp, Long.MAX_VALUE) val minLimit = min(-fakeTimestamp, maxLimit) fakeSampleRate = forge.aFloat(min = 0.0f, max = 100.0f) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt index fa73744caf..9eb5bc17cc 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumViewScopeTest.kt @@ -8495,10 +8495,7 @@ internal class RumViewScopeTest { fakeEvent = RumRawEvent.StopView( key = testedScope.key, attributes = forge.exhaustiveAttributes(), - eventTime = fakeEventTime.copy( - nanoTime = fakeEventTime.nanoTime + fakeViewDurationNs, - timestamp = fakeEventTime.timestamp + TimeUnit.NANOSECONDS.toMillis(fakeViewDurationNs) - ) + eventTime = Time(nanoTime = fakeEventTime.nanoTime + fakeViewDurationNs) ) // When @@ -8514,12 +8511,7 @@ internal class RumViewScopeTest { ) { // When testedScope.handleEvent( - forge.startViewEvent( - eventTime = fakeEventTime.copy( - nanoTime = fakeEventTime.nanoTime + fakeViewDurationNs, - timestamp = fakeEventTime.timestamp + TimeUnit.NANOSECONDS.toMillis(fakeViewDurationNs) - ) - ), + forge.startViewEvent(eventTime = Time(nanoTime = fakeEventTime.nanoTime + fakeViewDurationNs)), fakeDatadogContext, mockEventWriteScope, mockWriter @@ -8533,12 +8525,7 @@ internal class RumViewScopeTest { fun `M call resolveReport(viewId, true, Long) of slowFramesListener W handleEvent(StopSession)`() { // When testedScope.handleEvent( - RumRawEvent.StopSession( - eventTime = fakeEventTime.copy( - nanoTime = fakeEventTime.nanoTime + fakeViewDurationNs, - timestamp = fakeEventTime.timestamp + TimeUnit.NANOSECONDS.toMillis(fakeViewDurationNs) - ) - ), + RumRawEvent.StopSession(eventTime = Time(nanoTime = fakeEventTime.nanoTime + fakeViewDurationNs)), fakeDatadogContext, mockEventWriteScope, mockWriter @@ -8680,12 +8667,7 @@ internal class RumViewScopeTest { @Test fun `M onDurationResolved W closing scope(StopSession)`(@LongForgery(min = 1) expectedDuration: Long) { // Given - val stopEvent = RumRawEvent.StopSession( - eventTime = fakeEventTime.copy( - nanoTime = fakeEventTime.nanoTime + expectedDuration, - timestamp = fakeEventTime.timestamp + TimeUnit.NANOSECONDS.toMillis(expectedDuration) - ) - ) + val stopEvent = RumRawEvent.StopSession(eventTime = Time(nanoTime = fakeEventTime.nanoTime + expectedDuration)) testedScope = newRumViewScope() // When @@ -8700,10 +8682,7 @@ internal class RumViewScopeTest { // Given val stopEvent = RumRawEvent.AddViewLoadingTime( overwrite = false, - eventTime = fakeEventTime.copy( - nanoTime = fakeEventTime.nanoTime + expectedDuration, - timestamp = fakeEventTime.timestamp + TimeUnit.NANOSECONDS.toMillis(expectedDuration) - ) + eventTime = Time(nanoTime = fakeEventTime.nanoTime + expectedDuration) ) testedScope = newRumViewScope() @@ -8717,7 +8696,7 @@ internal class RumViewScopeTest { @Test fun `M return a new RumViewScope W renew the current one`() { // Given - val expectedTime = fakeEventTime.copy() + val expectedTime = Time(nanoTime = fakeEventTime.nanoTime) // When val newScope = testedScope.renew(expectedTime) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/ActionTypeInteractionValidatorTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/ActionTypeInteractionValidatorTest.kt index 524029028f..7e3e1eb26a 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/ActionTypeInteractionValidatorTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/ActionTypeInteractionValidatorTest.kt @@ -6,7 +6,6 @@ package com.datadog.android.rum.internal.metric.interactiontonextview -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.model.ActionEvent import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach @@ -59,30 +58,28 @@ internal class ActionTypeInteractionValidatorTest { companion object { - private val timeProvider = DefaultTimeProvider() - @JvmStatic fun validTypesContexts(): List { return listOf( InternalInteractionContext( "viewId", ActionEvent.ActionEventActionType.TAP, - timeProvider.getDeviceElapsedTimeNs() + System.nanoTime() ), InternalInteractionContext( "viewId", ActionEvent.ActionEventActionType.CLICK, - timeProvider.getDeviceElapsedTimeNs() + System.nanoTime() ), InternalInteractionContext( "viewId", ActionEvent.ActionEventActionType.SWIPE, - timeProvider.getDeviceElapsedTimeNs() + System.nanoTime() ), InternalInteractionContext( "viewId", ActionEvent.ActionEventActionType.BACK, - timeProvider.getDeviceElapsedTimeNs() + System.nanoTime() ) ) } @@ -93,12 +90,12 @@ internal class ActionTypeInteractionValidatorTest { InternalInteractionContext( "viewId", ActionEvent.ActionEventActionType.CUSTOM, - timeProvider.getDeviceElapsedTimeNs() + System.nanoTime() ), InternalInteractionContext( "viewId", ActionEvent.ActionEventActionType.SCROLL, - timeProvider.getDeviceElapsedTimeNs() + System.nanoTime() ) ) } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/InteractionToNextViewMetricResolverTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/InteractionToNextViewMetricResolverTest.kt index acc5be6150..2d8624b6c6 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/InteractionToNextViewMetricResolverTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/metric/interactiontonextview/InteractionToNextViewMetricResolverTest.kt @@ -7,7 +7,6 @@ package com.datadog.android.rum.internal.metric.interactiontonextview import com.datadog.android.api.InternalLogger -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.internal.metric.NoValueReason import com.datadog.android.rum.internal.metric.ViewInitializationMetricsConfig import com.datadog.android.rum.metric.interactiontonextview.LastInteractionIdentifier @@ -40,9 +39,6 @@ import java.util.UUID @MockitoSettings(strictness = Strictness.LENIENT) @ForgeConfiguration(Configurator::class) internal class InteractionToNextViewMetricResolverTest { - - private val timeProvider = DefaultTimeProvider() - private lateinit var testedMetric: InteractionToNextViewMetricResolver @Mock @@ -57,7 +53,7 @@ internal class InteractionToNextViewMetricResolverTest { private lateinit var fakeViewId: String private lateinit var fakeFirstViewId: String - private val fakeFirstViewTimestampNs: Long = timeProvider.getDeviceElapsedTimeNs() + private val fakeFirstViewTimestampNs: Long = System.nanoTime() // region setup @@ -139,7 +135,7 @@ internal class InteractionToNextViewMetricResolverTest { @Test fun `M return NO_ACTION W getState { no action was registered on previous view }`() { // When - testedMetric.onViewCreated(fakeViewId, timeProvider.getDeviceElapsedTimeNs()) + testedMetric.onViewCreated(fakeViewId, System.nanoTime()) val result = testedMetric.getState(fakeViewId) // Then @@ -153,7 +149,7 @@ internal class InteractionToNextViewMetricResolverTest { ) { // Given val fakePreviousActions = forge.generateFakeActions(1, 10, fakeFirstViewId) - val fakeViewCreatedTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() + val fakeViewCreatedTimestamp = System.nanoTime() whenever(mockInteractionValidator.validate(any())).thenReturn(true) whenever(mockLastInteractionIdentifier.validate(any())).thenReturn(false) fakePreviousActions.forEach { testedMetric.onActionSent(it) } @@ -171,7 +167,7 @@ internal class InteractionToNextViewMetricResolverTest { fun `M return the right initializationTime and null noValueReason W getState`(forge: Forge) { // Given val fakePreviousActions = forge.generateFakeActions(1, 2, fakeFirstViewId) - val fakeCurrentViewCreatedTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() + val fakeCurrentViewCreatedTimestamp = System.nanoTime() fakePreviousActions.mockValidators(fakeCurrentViewCreatedTimestamp) fakePreviousActions.forEach { testedMetric.onActionSent(it) } val expectedMetricValue = fakeCurrentViewCreatedTimestamp - fakePreviousActions.last().eventCreatedAtNanos @@ -219,7 +215,7 @@ internal class InteractionToNextViewMetricResolverTest { fun `M return the right metric W resolveMetric`(forge: Forge) { // Given val fakePreviousActions = forge.generateFakeActions(1, 10, fakeFirstViewId) - val fakeViewCreatedTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() + val fakeViewCreatedTimestamp = System.nanoTime() fakePreviousActions.mockValidators(fakeViewCreatedTimestamp) fakePreviousActions.forEach { testedMetric.onActionSent(it) @@ -239,7 +235,7 @@ internal class InteractionToNextViewMetricResolverTest { forge: Forge ) { // Given - val fakeViewCreatedTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() + val fakeViewCreatedTimestamp = System.nanoTime() val fakePreviousActions = forge.generateFakeActions(1, 10, fakeFirstViewId, fakeViewCreatedTimestamp) fakePreviousActions.mockValidators(fakeViewCreatedTimestamp) fakePreviousActions.forEach { @@ -266,7 +262,7 @@ internal class InteractionToNextViewMetricResolverTest { ) { // Given val fakePreviousActions = forge.generateFakeActions(1, 10, fakeFirstViewId) - val fakeViewCreatedTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() + val fakeViewCreatedTimestamp = System.nanoTime() fakePreviousActions.forEach { whenever(mockInteractionValidator.validate(it)).thenReturn(false) whenever( @@ -295,7 +291,7 @@ internal class InteractionToNextViewMetricResolverTest { ) { // Given val fakePreviousActions = forge.generateFakeActions(1, 10, fakeFirstViewId) - val fakeViewCreatedTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() + val fakeViewCreatedTimestamp = System.nanoTime() fakePreviousActions.forEach { whenever(mockInteractionValidator.validate(it)).thenReturn(true) whenever( @@ -324,7 +320,7 @@ internal class InteractionToNextViewMetricResolverTest { ) { // Given val fakePreviousActions = forge.generateFakeActions(1, 10, forge.aString()) - val fakeViewCreatedTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() + val fakeViewCreatedTimestamp = System.nanoTime() fakePreviousActions.mockValidators(fakeViewCreatedTimestamp) testedMetric.onViewCreated(fakeViewId, fakeViewCreatedTimestamp) @@ -508,7 +504,7 @@ internal class InteractionToNextViewMetricResolverTest { fun `M be thread safe W resolveMetric`(forge: Forge) { // Given val fakePreviousActions = forge.generateFakeActions(1, 10, fakeFirstViewId) - val fakeViewCreatedTimestamp = DefaultTimeProvider().getDeviceElapsedTimeNs() + val fakeViewCreatedTimestamp = System.nanoTime() fakePreviousActions.mockValidators(fakeViewCreatedTimestamp) fakePreviousActions.forEach { Thread { @@ -586,7 +582,7 @@ internal class InteractionToNextViewMetricResolverTest { min: Int, max: Int, viewId: String, - startTimestamp: Long = timeProvider.getDeviceElapsedTimeNs() + startTimestamp: Long = System.nanoTime() ): List { return aList(size = anInt(min = min, max = max)) { getForgery() diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitorTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitorTest.kt index 95b3f61d79..11ac5a5eb2 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitorTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitorTest.kt @@ -20,7 +20,6 @@ import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.feature.event.ThreadDump import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver import com.datadog.android.internal.telemetry.InternalTelemetryEvent -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.DdRumContentProvider import com.datadog.android.rum.ExperimentalRumApi import com.datadog.android.rum.RumActionType @@ -1120,7 +1119,7 @@ internal class DatadogRumMonitorTest { mockRumFeatureScope.getWriteContextSync(setOf(Feature.SESSION_REPLAY_FEATURE_NAME)) ) doReturn (fakeDatadogContext to mockEventWriteScope) testedMonitor.drainExecutorService() - val now = DefaultTimeProvider().getDeviceElapsedTimeNs() + val now = System.nanoTime() val appStartTimeNs = forge.aLong(min = 0L, max = now) whenever(mockSdkCore.appStartTimeNs) doReturn appStartTimeNs diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/metric/interactiontonextview/TimeBasedInteractionIdentifierTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/metric/interactiontonextview/TimeBasedInteractionIdentifierTest.kt index cda8afbf92..2c38b779b1 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/metric/interactiontonextview/TimeBasedInteractionIdentifierTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/metric/interactiontonextview/TimeBasedInteractionIdentifierTest.kt @@ -6,8 +6,6 @@ package com.datadog.android.rum.metric.interactiontonextview -import com.datadog.android.internal.time.DefaultTimeProvider -import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.utils.forge.Configurator import com.datadog.tools.unit.ObjectTest import fr.xgouchet.elmyr.Forge @@ -34,8 +32,6 @@ internal class TimeBasedInteractionIdentifierTest : ObjectTest - @Mock - lateinit var mockTimeProvider: TimeProvider - private lateinit var fakeSessionId: String private var fakeSampleRate: Float? = null @@ -109,7 +105,6 @@ internal class SessionReplayFeatureTest { whenever(mockSampler.getSampleRate()).thenReturn(fakeSampleRate) fakeSessionId = UUID.randomUUID().toString() whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger - whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider whenever(mockSdkCore.createSingleThreadExecutorService(any())) doReturn mockExecutorService testedFeature = SessionReplayFeature( diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandlerTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandlerTest.kt index 3238e65418..0b48454d67 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandlerTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandlerTest.kt @@ -8,7 +8,6 @@ package com.datadog.android.sessionreplay.internal.async import com.datadog.android.api.InternalLogger import com.datadog.android.core.sampling.RateBasedSampler -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.sessionreplay.forge.ForgeConfigurator import com.datadog.android.sessionreplay.internal.async.RecordedDataQueueHandler.Companion.ITEM_DROPPED_EXPIRED_MESSAGE import com.datadog.android.sessionreplay.internal.async.RecordedDataQueueHandler.Companion.ITEM_DROPPED_INVALID_MESSAGE @@ -67,9 +66,6 @@ import java.util.concurrent.TimeUnit @MockitoSettings(strictness = Strictness.LENIENT) @ForgeConfiguration(ForgeConfigurator::class) internal class RecordedDataQueueHandlerTest { - - private val timeProvider = DefaultTimeProvider() - private lateinit var testedHandler: RecordedDataQueueHandler @Mock @@ -294,7 +290,7 @@ internal class RecordedDataQueueHandlerTest { ) { // Given mockSnapshotItem.apply { - val expiredTime = timeProvider.getDeviceElapsedTimeNs() - RecordedDataQueueHandler.MAX_DELAY_NS + val expiredTime = System.nanoTime() - RecordedDataQueueHandler.MAX_DELAY_NS whenever(creationTimeStampInNs).thenReturn(expiredTime) whenever(isValid()).thenReturn(true) whenever(isReady()).thenReturn(true) @@ -536,7 +532,7 @@ internal class RecordedDataQueueHandlerTest { mockSnapshotItem1.apply { whenever(systemInformation).thenReturn(mockSystemInformation) whenever(nodes).thenReturn(fakeNodeData) - whenever(creationTimeStampInNs).thenReturn(timeProvider.getDeviceElapsedTimeNs()) + whenever(creationTimeStampInNs).thenReturn(System.nanoTime()) whenever(isValid()).thenReturn(true) whenever(isReady()).thenReturn(true) } @@ -545,7 +541,7 @@ internal class RecordedDataQueueHandlerTest { mockSnapshotItem2.apply { whenever(systemInformation).thenReturn(mockSystemInformation) whenever(nodes).thenReturn(fakeNodeData) - whenever(creationTimeStampInNs).thenReturn(timeProvider.getDeviceElapsedTimeNs()) + whenever(creationTimeStampInNs).thenReturn(System.nanoTime()) whenever(isValid()).thenReturn(true) whenever(isReady()).thenReturn(false) } @@ -554,7 +550,7 @@ internal class RecordedDataQueueHandlerTest { mockSnapshotItem3.apply { whenever(systemInformation).thenReturn(mockSystemInformation) whenever(nodes).thenReturn(fakeNodeData) - whenever(creationTimeStampInNs).thenReturn(timeProvider.getDeviceElapsedTimeNs()) + whenever(creationTimeStampInNs).thenReturn(System.nanoTime()) whenever(isValid()).thenReturn(true) whenever(isReady()).thenReturn(true) } @@ -736,7 +732,7 @@ internal class RecordedDataQueueHandlerTest { ) mockSnapshotItem.apply { - val expiredTime = timeProvider.getDeviceElapsedTimeNs() - RecordedDataQueueHandler.MAX_DELAY_NS + val expiredTime = System.nanoTime() - RecordedDataQueueHandler.MAX_DELAY_NS whenever(creationTimeStampInNs).thenReturn(expiredTime) whenever(isValid()).thenReturn(true) whenever(isReady()).thenReturn(true) @@ -768,7 +764,7 @@ internal class RecordedDataQueueHandlerTest { private fun addSnapshotItemToQueue(): SnapshotRecordedDataQueueItem { val newRumContext = RecordedQueuedItemContext( - timestamp = timeProvider.getDeviceTimestamp(), + timestamp = System.currentTimeMillis(), newRumContext = fakeRecordedQueuedItemContext.newRumContext ) diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/resources/ResourceDataStoreManagerTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/resources/ResourceDataStoreManagerTest.kt index 227eb7f406..753958c3d9 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/resources/ResourceDataStoreManagerTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/resources/ResourceDataStoreManagerTest.kt @@ -15,7 +15,6 @@ import com.datadog.android.api.storage.datastore.DataStoreWriteCallback import com.datadog.android.core.internal.persistence.Deserializer import com.datadog.android.core.persistence.Serializer import com.datadog.android.core.persistence.datastore.DataStoreContent -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.sessionreplay.forge.ForgeConfigurator import com.datadog.android.sessionreplay.internal.resources.ResourceDataStoreManager.Companion.DATASTORE_EXPIRATION_NS import com.datadog.android.sessionreplay.internal.resources.ResourceDataStoreManager.Companion.DATASTORE_HASHES_ENTRY_NAME @@ -49,9 +48,6 @@ import org.mockito.quality.Strictness @MockitoSettings(strictness = Strictness.LENIENT) @ForgeConfiguration(ForgeConfigurator::class) internal class ResourceDataStoreManagerTest { - - private val timeProvider = DefaultTimeProvider() - private lateinit var testedDataStoreManager: ResourceDataStoreManager @Mock @@ -332,9 +328,9 @@ internal class ResourceDataStoreManagerTest { val resourceHashes = forge.aList { aString() }.distinct() val fakeVersionCode = forge.anInt(min = 0) val entryTime = if (isExpired) { - timeProvider.getDeviceElapsedTimeNs() - DATASTORE_EXPIRATION_NS + System.nanoTime() - DATASTORE_EXPIRATION_NS } else { - timeProvider.getDeviceElapsedTimeNs() + System.nanoTime() } val mockResourceHashesEntry: ResourceHashesEntry = mock { diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/api/IdGenerationStrategyTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/api/IdGenerationStrategyTest.kt index 35011d6cfe..8d302556fa 100644 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/api/IdGenerationStrategyTest.kt +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/api/IdGenerationStrategyTest.kt @@ -6,7 +6,6 @@ package com.datadog.trace.api -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.utils.forge.Configurator import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension @@ -31,15 +30,13 @@ import java.security.SecureRandom @ForgeConfiguration(Configurator::class) internal class IdGenerationStrategyTest { - private val timeProvider = DefaultTimeProvider() - // Please note that these tests were just ported from the Groovy CoreTracer tests in APM java code @ParameterizedTest @ValueSource(strings = ["RANDOM", "SEQUENTIAL", "SECURE_RANDOM"]) fun `M generate id with strategyName and tIdSize bits`(strategyName: String) { val isTid128 = listOf(false, true) - val highOrderLowerBound = (timeProvider.getDeviceTimestamp() / 1000).shl(32) + val highOrderLowerBound = (System.currentTimeMillis() / 1000).shl(32) isTid128.forEach { tId128b -> val strategy = IdGenerationStrategy.fromName(strategyName, tId128b) val traceIds = (0..4096).map { strategy.generateTraceId() } @@ -58,7 +55,7 @@ internal class IdGenerationStrategyTest { assertThat(hashCode).isEqualTo(traceId.hashCode()) assertThat(checked).doesNotContain(traceId) if (tId128b && strategyName != "SEQUENTIAL") { - val highOrderUpperBound = (timeProvider.getDeviceTimestamp() / 1000).shl(32) + val highOrderUpperBound = (System.currentTimeMillis() / 1000).shl(32) val lowOrderUpperBound = Long.MAX_VALUE assertThat(traceId.toLong()).isLessThanOrEqualTo(lowOrderUpperBound) assertThat(traceId.toHighOrderLong()).isLessThanOrEqualTo(highOrderUpperBound) diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/CoreSpanBuilderTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/CoreSpanBuilderTest.kt index 91b10e5a31..00c4080759 100644 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/CoreSpanBuilderTest.kt +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/CoreSpanBuilderTest.kt @@ -6,7 +6,6 @@ package com.datadog.trace.core -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.utils.safeGetThreadId import com.datadog.tools.unit.getFieldValue import com.datadog.trace.api.Config @@ -40,8 +39,6 @@ import java.util.stream.Stream @Timeout(5) internal class CoreSpanBuilderTest : DDCoreSpecification() { - private val timeProvider = DefaultTimeProvider() - lateinit var writer: ListWriter lateinit var tracer: CoreTracer @@ -159,9 +156,9 @@ internal class CoreSpanBuilderTest : DDCoreSpecification() { // When // auto-timestamp in nanoseconds - val start = timeProvider.getDeviceTimestamp() + val start = System.currentTimeMillis() val secondSpan = tracer.buildSpan(instrumentationName, expectedName).withServiceName("foo").start() - val stop = timeProvider.getDeviceTimestamp() + val stop = System.currentTimeMillis() // Then // Give a range of +/- 1 millis diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanTest.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanTest.kt index 7e15e24aac..8bd990d4fb 100644 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanTest.kt +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/DDSpanTest.kt @@ -6,7 +6,6 @@ package com.datadog.trace.core -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.tools.unit.getFieldValue import com.datadog.trace.api.DDSpanId import com.datadog.trace.api.DDTags @@ -42,9 +41,6 @@ import java.util.concurrent.TimeUnit import java.util.stream.Stream internal class DDSpanTest : DDCoreSpecification() { - - private val timeProvider = DefaultTimeProvider() - private val sampler = RateByServiceTraceSampler() lateinit var writer: ListWriter lateinit var tracer: CoreTracer @@ -146,18 +142,18 @@ internal class DDSpanTest : DDCoreSpecification() { // Given val mod = TimeUnit.MILLISECONDS.toNanos(1) val builder = tracer.buildSpan(instrumentationName, "test") - val start = timeProvider.getDeviceElapsedTimeNs() + val start = System.nanoTime() val span = builder.start() - val between = timeProvider.getDeviceElapsedTimeNs() - val betweenDur = timeProvider.getDeviceElapsedTimeNs() - between - val total = timeProvider.getDeviceElapsedTimeNs() - start + val between = System.nanoTime() + val betweenDur = System.nanoTime() - between + val total = System.nanoTime() - start // When span.finish() // Then val timeDifference = TimeUnit.NANOSECONDS.toSeconds(span.startTime) - - TimeUnit.MILLISECONDS.toSeconds(timeProvider.getDeviceTimestamp()) + TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) assertThat(timeDifference).isLessThan(5) assertThat(span.durationNano).isGreaterThan(betweenDur) assertThat(span.durationNano).isLessThan(total) @@ -169,10 +165,10 @@ internal class DDSpanTest : DDCoreSpecification() { // Given val mod = TimeUnit.MILLISECONDS.toNanos(1) val builder = tracer.buildSpan(instrumentationName, "test") - val start = timeProvider.getDeviceElapsedTimeNs() + val start = System.nanoTime() val span = builder.start() - val between = timeProvider.getDeviceElapsedTimeNs() - val betweenDur = timeProvider.getDeviceElapsedTimeNs() - between + val between = System.nanoTime() + val betweenDur = System.nanoTime() - between // When span.publish() @@ -183,14 +179,14 @@ internal class DDSpanTest : DDCoreSpecification() { // When var finish = span.phasedFinish() - val total = timeProvider.getDeviceElapsedTimeNs() - start + val total = System.nanoTime() - start // Then assertThat(finish).isTrue() assertThat(writer).isEmpty() val actualDurationNano = span.durationNano and Long.MAX_VALUE val timeDifference = TimeUnit.NANOSECONDS.toSeconds(span.startTime) - - TimeUnit.MILLISECONDS.toSeconds(timeProvider.getDeviceTimestamp()) + TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) assertThat(timeDifference).isLessThan(5) assertThat(actualDurationNano).isGreaterThan(betweenDur) assertThat(actualDurationNano).isLessThan(total) @@ -223,20 +219,20 @@ internal class DDSpanTest : DDCoreSpecification() { fun `starting with a timestamp disables nanotime`() { // Given val mod = TimeUnit.MILLISECONDS.toNanos(1) - val start = timeProvider.getDeviceTimestamp() + val start = System.currentTimeMillis() val builder = tracer.buildSpan(instrumentationName, "test") - .withStartTimestamp(TimeUnit.MILLISECONDS.toMicros(timeProvider.getDeviceTimestamp())) + .withStartTimestamp(TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis())) val span = builder.start() - val between = timeProvider.getDeviceTimestamp() - val betweenDur = timeProvider.getDeviceTimestamp() - between + val between = System.currentTimeMillis() + val betweenDur = System.currentTimeMillis() - between // When span.finish() // Then - val total = Math.max(1, timeProvider.getDeviceTimestamp() - start) + val total = Math.max(1, System.currentTimeMillis() - start) val timeDifference = TimeUnit.NANOSECONDS.toSeconds(span.startTime) - - TimeUnit.MILLISECONDS.toSeconds(timeProvider.getDeviceTimestamp()) + TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) assertThat(timeDifference).isLessThan(5) assertThat(span.durationNano).isGreaterThanOrEqualTo(TimeUnit.MILLISECONDS.toNanos(betweenDur)) assertThat(span.durationNano).isLessThanOrEqualTo(TimeUnit.MILLISECONDS.toNanos(total)) @@ -248,16 +244,16 @@ internal class DDSpanTest : DDCoreSpecification() { // Given val mod = TimeUnit.MILLISECONDS.toNanos(1) val builder = tracer.buildSpan(instrumentationName, "test") - val start = timeProvider.getDeviceTimestamp() + val start = System.currentTimeMillis() val span = builder.start() - val between = timeProvider.getDeviceTimestamp() - val betweenDur = timeProvider.getDeviceTimestamp() - between + val between = System.currentTimeMillis() + val betweenDur = System.currentTimeMillis() - between // When - span.finish(TimeUnit.MILLISECONDS.toMicros(timeProvider.getDeviceTimestamp() + 1)) - val total = timeProvider.getDeviceTimestamp() - start + 1 + span.finish(TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis() + 1)) + val total = System.currentTimeMillis() - start + 1 val timeDifference = TimeUnit.NANOSECONDS.toSeconds(span.startTime) - - TimeUnit.MILLISECONDS.toSeconds(timeProvider.getDeviceTimestamp()) + TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) // Then assertThat(timeDifference).isLessThan(5) diff --git a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/PendingTraceTestBase.kt b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/PendingTraceTestBase.kt index 2d8138f3f9..ff18570dd6 100644 --- a/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/PendingTraceTestBase.kt +++ b/features/dd-sdk-android-trace-internal/src/test/kotlin/com/datadog/trace/core/PendingTraceTestBase.kt @@ -6,7 +6,6 @@ package com.datadog.trace.core -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.tools.unit.getFieldValue import com.datadog.trace.common.writer.ListWriter import org.assertj.core.api.Assertions.assertThat @@ -137,7 +136,7 @@ internal abstract class PendingTraceTestBase : DDCoreSpecification() { assertThat( Math.abs( TimeUnit.NANOSECONDS.toSeconds(trace.currentTimeNano) - - TimeUnit.MILLISECONDS.toSeconds(DefaultTimeProvider().getDeviceTimestamp()) + TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) ) ).isLessThan(5) } diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanLoggerTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanLoggerTest.kt index 3c1421a2ac..bb9bba0d65 100644 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanLoggerTest.kt +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanLoggerTest.kt @@ -9,7 +9,6 @@ import android.util.Log import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.FeatureScope import com.datadog.android.api.feature.FeatureSdkCore -import com.datadog.android.internal.time.TimeProvider import com.datadog.android.internal.utils.loggableStackTrace import com.datadog.android.log.LogAttributes import com.datadog.android.trace.api.DatadogTracingConstants @@ -34,7 +33,6 @@ import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever import org.mockito.quality.Strictness @Extensions( @@ -62,18 +60,13 @@ class DatadogSpanLoggerTest { @Mock lateinit var mockLogFeatureScope: FeatureScope - @Mock - lateinit var mockTimeProvider: TimeProvider - private lateinit var testedLogger: DatadogSpanLogger @BeforeEach fun `set up`() { mockSdkCore = mock { on { getFeature(Feature.LOGS_FEATURE_NAME) } doReturn mockLogFeatureScope - on { timeProvider } doReturn mockTimeProvider } - whenever(mockTimeProvider.getDeviceTimestamp()) doReturn 0L testedLogger = DatadogSpanLogger(mockSdkCore) } diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/SpanEventForgeryFactory.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/SpanEventForgeryFactory.kt index 3ee26f399f..cbeb9fb782 100644 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/SpanEventForgeryFactory.kt +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/utils/forge/SpanEventForgeryFactory.kt @@ -11,7 +11,6 @@ import com.datadog.android.api.context.DeviceInfo import com.datadog.android.api.context.DeviceType import com.datadog.android.api.context.NetworkInfo import com.datadog.android.api.context.UserInfo -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.utils.toHexString import com.datadog.android.trace.model.SpanEvent import fr.xgouchet.elmyr.Forge @@ -32,7 +31,7 @@ internal class SpanEventForgeryFactory : ForgeryFactory { val spanId = forge.aLong(min = 1).toHexString() val parentId = forge.aLong(min = 1).toHexString() val duration = forge.aLong(min = 0) - val startTime = TimeUnit.SECONDS.toNanos(DefaultTimeProvider().getDeviceTimestamp()) + val startTime = TimeUnit.SECONDS.toNanos(System.currentTimeMillis()) val appPackageVersion = forge.aStringMatching("[0-9]\\.[0-9]\\.[0-9]") val tracerVersion = forge.aStringMatching("[0-9]\\.[0-9]\\.[0-9]") val userInfo = forge.aNullable() diff --git a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/WebViewTrackingTest.kt b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/WebViewTrackingTest.kt index bb869dac6a..7153c26b10 100644 --- a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/WebViewTrackingTest.kt +++ b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/WebViewTrackingTest.kt @@ -20,7 +20,6 @@ import com.datadog.android.api.storage.EventBatchWriter import com.datadog.android.api.storage.EventType import com.datadog.android.api.storage.NoOpDataWriter import com.datadog.android.api.storage.RawBatchEvent -import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import com.datadog.android.utils.verifyLog import com.datadog.android.webview.internal.DatadogEventBridge @@ -104,9 +103,6 @@ internal class WebViewTrackingTest { @Mock lateinit var mockReplayFeatureScope: FeatureScope - @Mock - lateinit var mockTimeProvider: TimeProvider - @BeforeEach fun `set up`() { whenever( @@ -128,7 +124,6 @@ internal class WebViewTrackingTest { mockReplayFeatureScope.unwrap() ) doReturn mockReplayFeature whenever(mockCore.internalLogger) doReturn mockInternalLogger - whenever(mockCore.timeProvider) doReturn mockTimeProvider whenever(mockRumFeature.requestFactory) doReturn mockRumRequestFactory whenever(mockLogsFeature.requestFactory) doReturn mockLogsRequestFactory diff --git a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCacheTest.kt b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCacheTest.kt index 0e9b535a5d..0375ddebf2 100644 --- a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCacheTest.kt +++ b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCacheTest.kt @@ -6,7 +6,6 @@ package com.datadog.android.webview.internal.rum.domain -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.utils.forge.Configurator import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.junit5.ForgeConfiguration @@ -31,8 +30,6 @@ import java.util.concurrent.atomic.AtomicLong @ForgeConfiguration(Configurator::class) internal class WebViewNativeRumViewsCacheTest { - private val timeProvider = DefaultTimeProvider() - private lateinit var fakeIdGenerator: FakeIdGenerator private lateinit var fakeClock: FakeClock private lateinit var testedCache: WebViewNativeRumViewsCache @@ -43,7 +40,7 @@ internal class WebViewNativeRumViewsCacheTest { fun `set up`(forge: Forge) { fakeClock = FakeClock() fakeIdGenerator = FakeIdGenerator(forge) - testedCache = WebViewNativeRumViewsCache(timeProvider) + testedCache = WebViewNativeRumViewsCache() } @Test @@ -167,11 +164,11 @@ internal class WebViewNativeRumViewsCacheTest { ) { // Given val entriesTtlLimitInMs = TimeUnit.SECONDS.toMillis(1) - testedCache = WebViewNativeRumViewsCache(timeProvider, entriesTtlLimitInMs) + testedCache = WebViewNativeRumViewsCache(entriesTtlLimitInMs) val fakeOldEntries = forge.aList(size = forge.anInt(min = 1, max = 10)) { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeIdGenerator.generateId(), - WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to timeProvider.getDeviceTimestamp(), + WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to System.currentTimeMillis(), WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY to forge.aBool() ) } @@ -187,7 +184,7 @@ internal class WebViewNativeRumViewsCacheTest { ) { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeIdGenerator.generateId(), - WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to timeProvider.getDeviceTimestamp(), + WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to System.currentTimeMillis(), WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY to forge.aBool() ) } @@ -249,7 +246,7 @@ internal class WebViewNativeRumViewsCacheTest { val fakeEntries = forge.aList(size = forge.anInt(min = 1, max = 10)) { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeViewId, - WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to timeProvider.getDeviceTimestamp(), + WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to System.currentTimeMillis(), WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY to forge.aBool() ) } @@ -318,7 +315,7 @@ internal class WebViewNativeRumViewsCacheTest { private class FakeClock { - val initialTimeMillis = AtomicLong(DefaultTimeProvider().getDeviceTimestamp()) + val initialTimeMillis = AtomicLong(System.currentTimeMillis()) fun nextCurrentTimeMillis(): Long { return initialTimeMillis.incrementAndGet() diff --git a/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/InternalSdkCoreTest.kt b/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/InternalSdkCoreTest.kt index 8069b20d95..c04e81970f 100644 --- a/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/InternalSdkCoreTest.kt +++ b/reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/InternalSdkCoreTest.kt @@ -36,7 +36,6 @@ 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.internal.time.DefaultTimeProvider import com.datadog.android.privacy.TrackingConsent import com.datadog.android.trace.TracingHeaderType import com.datadog.tools.unit.forge.exhaustiveAttributes @@ -102,7 +101,7 @@ class InternalSdkCoreTest : MockServerTest() { @Before fun setUp() { - currentDeviceTimeInMs = DefaultTimeProvider().getDeviceTimestamp() + currentDeviceTimeInMs = System.currentTimeMillis() stubFeature = StubStorageBackedFeature( forge, fakeFeatureName, @@ -612,8 +611,7 @@ class InternalSdkCoreTest : MockServerTest() { // endregion companion object { - private val APPLICATION_START_TIME_UPPER_BOUND_NS = - DefaultTimeProvider().getDeviceElapsedTimeNs() + private val APPLICATION_START_TIME_UPPER_BOUND_NS = System.nanoTime() private val DEVICE_TIME_OFFSET_NS = TimeUnit.SECONDS.toNanos(6) private const val ANDROID_SOURCE = "android" private const val BUILD_ID = "core_it_build_id" diff --git a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTracerProviderTest.kt b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTracerProviderTest.kt index 9396763add..ddea228f7d 100644 --- a/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTracerProviderTest.kt +++ b/reliability/single-fit/trace/src/test/kotlin/com/datadog/android/trace/integration/otel/OtelTracerProviderTest.kt @@ -9,7 +9,6 @@ package com.datadog.android.trace.integration.otel import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.SdkFeatureMock import com.datadog.android.core.stub.StubSDKCore -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.tests.ktx.getInt import com.datadog.android.trace.Trace import com.datadog.android.trace.TraceConfiguration @@ -60,7 +59,6 @@ import kotlin.system.measureNanoTime @MockitoSettings(strictness = Strictness.LENIENT) internal class OtelTracerProviderTest { - private val timeProvider = DefaultTimeProvider() private lateinit var stubSdkCore: StubSDKCore private lateinit var blockingWriterWrapper: BlockingWriterWrapper @@ -450,7 +448,7 @@ internal class OtelTracerProviderTest { forge: Forge ) { // Given - val fakeStartTimestamp = timeProvider.getDeviceTimestamp() - forge.aPositiveLong() + val fakeStartTimestamp = System.currentTimeMillis() - forge.aPositiveLong() val testedProvider = OtelTracerProvider.Builder(stubSdkCore).build() val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() @@ -870,11 +868,9 @@ internal class OtelTracerProviderTest { val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() // When - val startNanos = timeProvider.getDeviceElapsedTimeNs() + val startNanos = System.nanoTime() var spansCounter = 0 - while ( - (timeProvider.getDeviceElapsedTimeNs() - startNanos) < ONE_SECOND_AS_NANOS && (spansCounter < 200) - ) { + while ((System.nanoTime() - startNanos) < ONE_SECOND_AS_NANOS && (spansCounter < 200)) { tracer.spanBuilder(forge.anAlphabeticalString()).startSpan().end() spansCounter++ } @@ -911,11 +907,9 @@ internal class OtelTracerProviderTest { val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() // When - val startNanos = timeProvider.getDeviceElapsedTimeNs() + val startNanos = System.nanoTime() var spansCounter = 0 - while ( - (timeProvider.getDeviceElapsedTimeNs() - startNanos) < ONE_SECOND_AS_NANOS && (spansCounter < 200) - ) { + while ((System.nanoTime() - startNanos) < ONE_SECOND_AS_NANOS && (spansCounter < 200)) { tracer.spanBuilder(forge.anAlphabeticalString()).startSpan().end() spansCounter++ } @@ -949,12 +943,9 @@ internal class OtelTracerProviderTest { val tracer = testedProvider.tracerBuilder(fakeInstrumentationName).build() // When - val startNanos = timeProvider.getDeviceElapsedTimeNs() + val startNanos = System.nanoTime() var spansCounter = 0 - while ( - (timeProvider.getDeviceElapsedTimeNs() - startNanos < (ONE_SECOND_AS_NANOS * 2)) && - (spansCounter < 200) - ) { + while (((System.nanoTime() - startNanos) < (ONE_SECOND_AS_NANOS * 2)) && (spansCounter < 200)) { tracer.spanBuilder(forge.anAlphabeticalString()).startSpan().end() spansCounter++ } diff --git a/tools/benchmark/src/test/java/forge/SpanEventForgeryFactory.kt b/tools/benchmark/src/test/java/forge/SpanEventForgeryFactory.kt index 6dbf314235..e533e81d6d 100644 --- a/tools/benchmark/src/test/java/forge/SpanEventForgeryFactory.kt +++ b/tools/benchmark/src/test/java/forge/SpanEventForgeryFactory.kt @@ -8,7 +8,6 @@ package forge import com.datadog.android.api.context.NetworkInfo import com.datadog.android.api.context.UserInfo -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.utils.toHexString import com.datadog.benchmark.internal.model.SpanEvent import fr.xgouchet.elmyr.Forge @@ -29,7 +28,7 @@ internal class SpanEventForgeryFactory : ForgeryFactory { val spanId = forge.aLong(min = 1).toHexString() val parentId = forge.aLong(min = 1).toHexString() val duration = forge.aLong(min = 0) - val startTime = TimeUnit.SECONDS.toNanos(DefaultTimeProvider().getDeviceTimestamp()) + val startTime = TimeUnit.SECONDS.toNanos(System.currentTimeMillis()) val appPackageVersion = forge.aStringMatching("[0-9]\\.[0-9]\\.[0-9]") val tracerVersion = forge.aStringMatching("[0-9]\\.[0-9]\\.[0-9]") val userInfo: UserInfo? = forge.aNullable() From 14fe852c77c3ba90cbf3c5fb21e72ef712689b44 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Thu, 20 Nov 2025 11:31:58 +0000 Subject: [PATCH 11/29] RUM-10363: Continue improving tests --- .../internal/logger/DatadogLogHandlerTest.kt | 37 +++++++++++-------- .../internal/NdkCrashReportsFeatureTest.kt | 9 ++++- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt index 8fae036bb2..9685371f48 100644 --- a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt +++ b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt @@ -15,6 +15,7 @@ import com.datadog.android.api.storage.DataWriter import com.datadog.android.api.storage.EventBatchWriter import com.datadog.android.api.storage.EventType import com.datadog.android.core.sampling.Sampler +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.log.LogAttributes import com.datadog.android.log.assertj.LogEventAssert.Companion.assertThat import com.datadog.android.log.internal.LogsFeature @@ -70,6 +71,8 @@ internal class DatadogLogHandlerTest { private lateinit var fakeAttributes: Map private var fakeLevel: Int = 0 + private var fakeTimestamp: Long = 0L + @Forgery lateinit var fakeThrowable: Throwable @@ -91,6 +94,9 @@ internal class DatadogLogHandlerTest { @Mock lateinit var mockSdkCore: FeatureSdkCore + @Mock + lateinit var mockTimeProvider: TimeProvider + @Mock lateinit var mockLogsFeatureScope: FeatureScope @@ -118,6 +124,7 @@ internal class DatadogLogHandlerTest { fakeLoggerName = forge.anAlphabeticalString() fakeMessage = forge.anAlphabeticalString() fakeLevel = forge.anInt(2, 8) + fakeTimestamp = forge.aLong(min = 0L) fakeAttributes = forge.aMap { anAlphabeticalString() to anInt() } fakeTags = forge.aList { anAlphabeticalString() }.toSet() fakeDatadogContext = fakeDatadogContext.copy( @@ -155,6 +162,9 @@ internal class DatadogLogHandlerTest { mockSdkCore.getFeature(Feature.RUM_FEATURE_NAME) ) doReturn mockRumFeature + whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider + whenever(mockTimeProvider.getDeviceTimestamp()) doReturn fakeTimestamp + testedHandler = DatadogLogHandler( loggerName = fakeLoggerName, logGenerator = DatadogLogGenerator( @@ -168,8 +178,6 @@ internal class DatadogLogHandlerTest { @Test fun `forward log to LogWriter`() { - // Given - val now = System.currentTimeMillis() // When testedHandler.handleLog( @@ -194,7 +202,7 @@ internal class DatadogLogHandlerTest { .hasThreadName(Thread.currentThread().name) .hasStatus(fakeLevel.asLogStatus()) .hasMessage(fakeMessage) - .hasDateAround(now) + .hasDateAround(fakeTimestamp) .hasNetworkInfo(fakeDatadogContext.networkInfo) .hasUserInfo(fakeDatadogContext.userInfo) .hasBuildId(fakeDatadogContext.appBuildId) @@ -250,8 +258,6 @@ internal class DatadogLogHandlerTest { @Test fun `forward log to LogWriter with throwable`() { - // Given - val now = System.currentTimeMillis() // When testedHandler.handleLog( @@ -276,7 +282,7 @@ internal class DatadogLogHandlerTest { .hasThreadName(Thread.currentThread().name) .hasStatus(fakeLevel.asLogStatus()) .hasMessage(fakeMessage) - .hasDateAround(now) + .hasDateAround(fakeTimestamp) .hasNetworkInfo(fakeDatadogContext.networkInfo) .hasUserInfo(fakeDatadogContext.userInfo) .hasAccountInfo(fakeDatadogContext.accountInfo) @@ -316,7 +322,7 @@ internal class DatadogLogHandlerTest { @StringForgery errorStack: String ) { // Given - val now = System.currentTimeMillis() + // When testedHandler.handleLog( @@ -343,7 +349,7 @@ internal class DatadogLogHandlerTest { .hasThreadName(Thread.currentThread().name) .hasStatus(fakeLevel.asLogStatus()) .hasMessage(fakeMessage) - .hasDateAround(now) + .hasDateAround(fakeTimestamp) .hasNetworkInfo(fakeDatadogContext.networkInfo) .hasUserInfo(fakeDatadogContext.userInfo) .hasAccountInfo(fakeDatadogContext.accountInfo) @@ -632,7 +638,7 @@ internal class DatadogLogHandlerTest { @Test fun `forward log to LogWriter on background thread`(forge: Forge) { // Given - val now = System.currentTimeMillis() + val threadName = forge.anAlphabeticalString() val countDownLatch = CountDownLatch(1) @@ -668,7 +674,7 @@ internal class DatadogLogHandlerTest { .hasThreadName(threadName) .hasStatus(fakeLevel.asLogStatus()) .hasMessage(fakeMessage) - .hasDateAround(now) + .hasDateAround(fakeTimestamp) .hasNetworkInfo(fakeDatadogContext.networkInfo) .hasUserInfo(fakeDatadogContext.userInfo) .hasAccountInfo(fakeDatadogContext.accountInfo) @@ -696,7 +702,7 @@ internal class DatadogLogHandlerTest { @Test fun `forward log to LogWriter without network info`() { // Given - val now = System.currentTimeMillis() + testedHandler = DatadogLogHandler( loggerName = fakeLoggerName, logGenerator = DatadogLogGenerator( @@ -730,7 +736,7 @@ internal class DatadogLogHandlerTest { .hasThreadName(Thread.currentThread().name) .hasStatus(fakeLevel.asLogStatus()) .hasMessage(fakeMessage) - .hasDateAround(now) + .hasDateAround(fakeTimestamp) .doesNotHaveNetworkInfo() .hasUserInfo(fakeDatadogContext.userInfo) .hasAccountInfo(fakeDatadogContext.accountInfo) @@ -758,7 +764,7 @@ internal class DatadogLogHandlerTest { @Test fun `forward minimal log to LogWriter`() { // Given - val now = System.currentTimeMillis() + fakeDatadogContext = fakeDatadogContext.copy( featuresContext = fakeDatadogContext.featuresContext.toMutableMap().apply { remove(Feature.RUM_FEATURE_NAME) @@ -797,7 +803,7 @@ internal class DatadogLogHandlerTest { .hasThreadName(Thread.currentThread().name) .hasStatus(fakeLevel.asLogStatus()) .hasMessage(fakeMessage) - .hasDateAround(now) + .hasDateAround(fakeTimestamp) .doesNotHaveNetworkInfo() .hasUserInfo(fakeDatadogContext.userInfo) .hasAccountInfo(fakeDatadogContext.accountInfo) @@ -1070,7 +1076,6 @@ internal class DatadogLogHandlerTest { @Test fun `it will sample in the logs when required`() { // Given - val now = System.currentTimeMillis() whenever(mockSampler.sample(Unit)).thenReturn(true) testedHandler = DatadogLogHandler( loggerName = fakeLoggerName, @@ -1102,7 +1107,7 @@ internal class DatadogLogHandlerTest { .hasLoggerName(fakeLoggerName) .hasStatus(fakeLevel.asLogStatus()) .hasMessage(fakeMessage) - .hasDateAround(now) + .hasDateAround(fakeTimestamp) .hasNetworkInfo(fakeDatadogContext.networkInfo) .hasUserInfo(fakeDatadogContext.userInfo) .hasAccountInfo(fakeDatadogContext.accountInfo) diff --git a/features/dd-sdk-android-ndk/src/test/kotlin/com/datadog/android/ndk/internal/NdkCrashReportsFeatureTest.kt b/features/dd-sdk-android-ndk/src/test/kotlin/com/datadog/android/ndk/internal/NdkCrashReportsFeatureTest.kt index 330686958c..3f990a6722 100644 --- a/features/dd-sdk-android-ndk/src/test/kotlin/com/datadog/android/ndk/internal/NdkCrashReportsFeatureTest.kt +++ b/features/dd-sdk-android-ndk/src/test/kotlin/com/datadog/android/ndk/internal/NdkCrashReportsFeatureTest.kt @@ -9,6 +9,7 @@ package com.datadog.android.ndk.internal import android.content.Context import com.datadog.android.api.InternalLogger import com.datadog.android.core.InternalSdkCore +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.privacy.TrackingConsent import com.datadog.tools.unit.setFieldValue import fr.xgouchet.elmyr.junit5.ForgeExtension @@ -45,11 +46,17 @@ class NdkCrashReportsFeatureTest { @Mock lateinit var mockSdkCore: InternalSdkCore + @Mock + lateinit var mockTimeProvider: TimeProvider + @TempDir lateinit var tempDir: File @BeforeEach fun `set up`() { + whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider + whenever(mockTimeProvider.getDeviceTimestamp()) doReturn 1234567890000L + whenever(mockTimeProvider.getDeviceElapsedTimeNs()) doReturn 987654321000L testedFeature = NdkCrashReportsFeature(mockSdkCore) } @@ -100,7 +107,7 @@ class NdkCrashReportsFeatureTest { @ParameterizedTest @EnumSource(TrackingConsent::class) - fun `M do nothing W register { nativeLibrary not loaded }`( + fun `M do nothing W register { nativeLibrary not loaded }`( trackingConsent: TrackingConsent ) { // GIVEN From 779a6cf3bc43a2d304932db7990379d86987b6c4 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Thu, 20 Nov 2025 13:50:29 +0000 Subject: [PATCH 12/29] RUM-10363: Compilation errors fixed on tests --- .../core/DatadogCoreInitializationTest.kt | 36 +++++++-------- .../datadog/android/core/DatadogCoreTest.kt | 2 +- .../android/core/internal/CoreFeatureTest.kt | 2 +- .../data/upload/RotatingDnsResolverTest.kt | 6 ++- .../metrics/MethodCalledTelemetryTest.kt | 8 +++- .../advanced/ConsentAwareFileMigratorTest.kt | 7 ++- .../advanced/FeatureFileOrchestratorTest.kt | 13 ++++-- .../MoveDataMigrationOperationTest.kt | 13 ++++-- .../WipeDataMigrationOperationTest.kt | 10 ++++- .../file/batch/BatchFileOrchestratorTest.kt | 44 +++++++++++++------ .../thread/AbstractExecutorServiceTest.kt | 12 ++++- .../thread/BackPressureExecutorServiceTest.kt | 7 ++- ...ropOldestBackPressuredBlockingQueueTest.kt | 7 ++- ...oreNewestBackPressuredBlockingQueueTest.kt | 7 ++- .../LoggingScheduledThreadPoolExecutorTest.kt | 4 +- ...UnboundedBackPressuredBlockingQueueTest.kt | 7 ++- ...t => ObservableLinkedBlockingQueueTest.kt} | 21 ++++++--- .../thread/ThreadPoolExecutorExtTest.kt | 19 ++++---- .../time/DefaultAppStartTimeProviderTest.kt | 7 ++- .../core/internal/utils/MiscUtilsTest.kt | 15 ++++--- .../internal/DatadogExceptionHandlerTest.kt | 8 +++- .../internal/ExposureEventsProcessorTest.kt | 8 +++- .../persistence/FlagsStateSerializerTest.kt | 4 +- .../internal/logger/DatadogLogHandlerTest.kt | 9 ++-- .../DefaultAccessibilityReader.kt | 4 +- .../rum/RumConfigurationBuilderTest.kt | 8 +++- .../DefaultAccessibilityReaderTest.kt | 8 ++++ ...pplicationScopeAttributePropagationTest.kt | 7 ++- .../domain/scope/RumApplicationScopeTest.kt | 7 ++- ...RumSessionScopeAttributePropagationTest.kt | 7 ++- .../domain/scope/RumSessionScopeTest.kt | 7 ++- .../MainLooperLongTaskStrategyTest.kt | 12 +++-- .../DefaultSlowFramesListenerTest.kt | 9 +++- .../internal/monitor/DatadogRumMonitorTest.kt | 28 ++++++++---- .../async/RecordedDataQueueHandler.kt | 4 +- .../async/ResourceRecordedDataQueueItem.kt | 4 +- .../async/SnapshotRecordedDataQueueItem.kt | 4 +- ...urceRecordedDataQueueItemForgeryFactory.kt | 3 +- ...shotRecordedDataQueueItemForgeryFactory.kt | 3 +- ...ventRecordedDataQueueItemForgeryFactory.kt | 3 +- .../async/RecordedDataQueueHandlerTest.kt | 16 +++++-- .../ResourceRecordedDataQueueItemTest.kt | 3 +- .../SnapshotRecordedDataQueueItemTest.kt | 7 ++- .../TouchEventRecordedDataQueueItemTest.kt | 6 +++ .../processor/RecordedDataProcessorTest.kt | 19 +++++--- .../internal/recorder/DebouncerTest.kt | 13 ++++-- .../listener/WindowsOnDrawListenerTest.kt | 6 +++ 47 files changed, 330 insertions(+), 134 deletions(-) rename dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/{ObservableBlockingQueueTest.kt => ObservableLinkedBlockingQueueTest.kt} (85%) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreInitializationTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreInitializationTest.kt index 3e922b5cfc..5bccc9ba1a 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreInitializationTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreInitializationTest.kt @@ -111,7 +111,7 @@ internal class DatadogCoreInitializationTest { appContext.mockInstance, fakeInstanceId, fakeInstanceName, - executorServiceFactory = { _, _, _ -> mockPersistenceExecutorService } + executorServiceFactory = { _, _, _, _ -> mockPersistenceExecutorService } ).apply { initialize(fakeConfiguration.copy(crashReportsEnabled = crashReportsEnabled)) } @@ -187,7 +187,7 @@ internal class DatadogCoreInitializationTest { appContext.mockInstance, fakeInstanceId, fakeInstanceName, - executorServiceFactory = { _, _, _ -> mockPersistenceExecutorService } + executorServiceFactory = { _, _, _, _ -> mockPersistenceExecutorService } ).apply { initialize(fakeConfiguration) } @@ -210,7 +210,7 @@ internal class DatadogCoreInitializationTest { appContext.mockInstance, fakeInstanceId, fakeInstanceName, - executorServiceFactory = { _, _, _ -> mockPersistenceExecutorService } + executorServiceFactory = { _, _, _, _ -> mockPersistenceExecutorService } ).apply { initialize( fakeConfiguration.copy( @@ -238,7 +238,7 @@ internal class DatadogCoreInitializationTest { appContext.mockInstance, fakeInstanceId, fakeInstanceName, - executorServiceFactory = { _, _, _ -> mockPersistenceExecutorService } + executorServiceFactory = { _, _, _, _ -> mockPersistenceExecutorService } ).apply { initialize( fakeConfiguration.copy( @@ -267,7 +267,7 @@ internal class DatadogCoreInitializationTest { appContext.mockInstance, fakeInstanceId, fakeInstanceName, - executorServiceFactory = { _, _, _ -> mockPersistenceExecutorService } + executorServiceFactory = { _, _, _, _ -> mockPersistenceExecutorService } ).apply { initialize( fakeConfiguration.copy( @@ -295,7 +295,7 @@ internal class DatadogCoreInitializationTest { appContext.mockInstance, fakeInstanceId, fakeInstanceName, - executorServiceFactory = { _, _, _ -> mockPersistenceExecutorService } + executorServiceFactory = { _, _, _, _ -> mockPersistenceExecutorService } ).apply { initialize( fakeConfiguration.copy( @@ -354,7 +354,7 @@ internal class DatadogCoreInitializationTest { appContext.mockInstance, fakeInstanceId, fakeInstanceName, - executorServiceFactory = { _, _, _ -> mockPersistenceExecutorService } + executorServiceFactory = { _, _, _, _ -> mockPersistenceExecutorService } ).apply { initialize(configuration) } @@ -402,7 +402,7 @@ internal class DatadogCoreInitializationTest { appContext.mockInstance, fakeInstanceId, fakeInstanceName, - executorServiceFactory = { _, _, _ -> mockPersistenceExecutorService } + executorServiceFactory = { _, _, _, _ -> mockPersistenceExecutorService } ).apply { initialize(fakeConfiguration.copy(additionalConfig = mapOf(Datadog.DD_SOURCE_TAG to source))) } @@ -420,7 +420,7 @@ internal class DatadogCoreInitializationTest { appContext.mockInstance, fakeInstanceId, fakeInstanceName, - executorServiceFactory = { _, _, _ -> mockPersistenceExecutorService } + executorServiceFactory = { _, _, _, _ -> mockPersistenceExecutorService } ).apply { initialize( fakeConfiguration.copy(additionalConfig = mapOf(Datadog.DD_SOURCE_TAG to source)) @@ -440,7 +440,7 @@ internal class DatadogCoreInitializationTest { appContext.mockInstance, fakeInstanceId, fakeInstanceName, - executorServiceFactory = { _, _, _ -> mockPersistenceExecutorService } + executorServiceFactory = { _, _, _, _ -> mockPersistenceExecutorService } ).apply { initialize( fakeConfiguration.copy(additionalConfig = mapOf(Datadog.DD_SOURCE_TAG to source)) @@ -460,7 +460,7 @@ internal class DatadogCoreInitializationTest { appContext.mockInstance, fakeInstanceId, fakeInstanceName, - executorServiceFactory = { _, _, _ -> mockPersistenceExecutorService } + executorServiceFactory = { _, _, _, _ -> mockPersistenceExecutorService } ).apply { initialize(fakeConfiguration.copy(additionalConfig = customAttributes.nonNullData)) } @@ -478,7 +478,7 @@ internal class DatadogCoreInitializationTest { appContext.mockInstance, fakeInstanceId, fakeInstanceName, - executorServiceFactory = { _, _, _ -> mockPersistenceExecutorService } + executorServiceFactory = { _, _, _, _ -> mockPersistenceExecutorService } ).apply { initialize( fakeConfiguration.copy( @@ -500,7 +500,7 @@ internal class DatadogCoreInitializationTest { appContext.mockInstance, fakeInstanceId, fakeInstanceName, - executorServiceFactory = { _, _, _ -> mockPersistenceExecutorService } + executorServiceFactory = { _, _, _, _ -> mockPersistenceExecutorService } ).apply { initialize( fakeConfiguration.copy( @@ -522,7 +522,7 @@ internal class DatadogCoreInitializationTest { appContext.mockInstance, fakeInstanceId, fakeInstanceName, - executorServiceFactory = { _, _, _ -> mockPersistenceExecutorService } + executorServiceFactory = { _, _, _, _ -> mockPersistenceExecutorService } ).apply { initialize( fakeConfiguration.copy( @@ -544,7 +544,7 @@ internal class DatadogCoreInitializationTest { appContext.mockInstance, fakeInstanceId, fakeInstanceName, - executorServiceFactory = { _, _, _ -> mockPersistenceExecutorService } + executorServiceFactory = { _, _, _, _ -> mockPersistenceExecutorService } ).apply { initialize(fakeConfiguration.copy(additionalConfig = customAttributes.nonNullData)) } @@ -562,7 +562,7 @@ internal class DatadogCoreInitializationTest { appContext.mockInstance, fakeInstanceId, fakeInstanceName, - executorServiceFactory = { _, _, _ -> mockPersistenceExecutorService } + executorServiceFactory = { _, _, _, _ -> mockPersistenceExecutorService } ).apply { initialize( fakeConfiguration.copy( @@ -584,7 +584,7 @@ internal class DatadogCoreInitializationTest { appContext.mockInstance, fakeInstanceId, fakeInstanceName, - executorServiceFactory = { _, _, _ -> mockPersistenceExecutorService } + executorServiceFactory = { _, _, _, _ -> mockPersistenceExecutorService } ).apply { initialize( fakeConfiguration.copy( @@ -608,7 +608,7 @@ internal class DatadogCoreInitializationTest { appContext.mockInstance, fakeInstanceId, fakeInstanceName, - executorServiceFactory = { _, _, _ -> mockPersistenceExecutorService } + executorServiceFactory = { _, _, _, _ -> mockPersistenceExecutorService } ).apply { initialize( fakeConfiguration.copy( diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt index 8912f4e350..aa754973fa 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt @@ -151,7 +151,7 @@ internal class DatadogCoreTest { fakeInstanceId, fakeInstanceName, internalLoggerProvider = { mockInternalLogger }, - executorServiceFactory = { _, _, _ -> mockPersistenceExecutorService }, + executorServiceFactory = { _, _, _, _ -> mockPersistenceExecutorService }, buildSdkVersionProvider = mockBuildSdkVersionProvider ).apply { initialize(fakeConfiguration) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/CoreFeatureTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/CoreFeatureTest.kt index 60d9626b9d..7cd00c575f 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/CoreFeatureTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/CoreFeatureTest.kt @@ -147,7 +147,7 @@ internal class CoreFeatureTest { testedFeature = CoreFeature( mockInternalLogger, mockAppStartTimeProvider, - executorServiceFactory = { _, _, _ -> mockPersistenceExecutorService }, + executorServiceFactory = { _, _, _, _ -> mockPersistenceExecutorService }, scheduledExecutorServiceFactory = { _, _, _ -> mockScheduledExecutorService } ) whenever(appContext.mockInstance.getSystemService(Context.CONNECTIVITY_SERVICE)) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/data/upload/RotatingDnsResolverTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/data/upload/RotatingDnsResolverTest.kt index d25b9fe7b7..ebc7330e6a 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/data/upload/RotatingDnsResolverTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/data/upload/RotatingDnsResolverTest.kt @@ -6,6 +6,7 @@ package com.datadog.android.core.internal.data.upload +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.StringForgery @@ -41,6 +42,9 @@ internal class RotatingDnsResolverTest { @Mock lateinit var mockDelegate: Dns + @Mock + lateinit var mockTimeProvider: TimeProvider + @StringForgery lateinit var fakeHostname: String @@ -50,7 +54,7 @@ internal class RotatingDnsResolverTest { fun `set up`(forge: Forge) { fakeInetAddresses = forge.aList { mock() } - testedDns = RotatingDnsResolver(mockDelegate, TEST_TTL_MS) + testedDns = RotatingDnsResolver(mockDelegate, TEST_TTL_MS, mockTimeProvider) } @Test diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetryTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetryTest.kt index 2485f16e0d..45ff1eff77 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetryTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetryTest.kt @@ -17,6 +17,7 @@ import com.datadog.android.core.internal.metrics.MethodCalledTelemetry.Companion import com.datadog.android.core.internal.metrics.MethodCalledTelemetry.Companion.METRIC_TYPE_VALUE import com.datadog.android.core.internal.metrics.MethodCalledTelemetry.Companion.OPERATION_NAME import com.datadog.android.core.metrics.PerformanceMetric.Companion.METRIC_TYPE +import com.datadog.android.internal.time.TimeProvider import com.datadog.tools.unit.extensions.TestConfigurationExtension import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.FloatForgery @@ -61,6 +62,9 @@ internal class MethodCalledTelemetryTest { @Mock lateinit var mockDeviceInfo: DeviceInfo + @Mock + lateinit var mockTimeProvider: TimeProvider + @StringForgery lateinit var fakeDeviceModel: String @@ -110,8 +114,8 @@ internal class MethodCalledTelemetryTest { internalLogger = mockInternalLogger, operationName = fakeOperationName, callerClass = fakeCallerClass, - startTime = fakeStartTime, - creationSampleRate = fakeCreationSampleRate + creationSampleRate = fakeCreationSampleRate, + timeProvider = mockTimeProvider ) } diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/ConsentAwareFileMigratorTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/ConsentAwareFileMigratorTest.kt index a2697700e7..62d146af0a 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/ConsentAwareFileMigratorTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/ConsentAwareFileMigratorTest.kt @@ -9,6 +9,7 @@ package com.datadog.android.core.internal.persistence.file.advanced import com.datadog.android.api.InternalLogger import com.datadog.android.core.internal.persistence.file.FileMover import com.datadog.android.core.internal.persistence.file.FileOrchestrator +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.privacy.TrackingConsent import com.datadog.android.utils.forge.Configurator import fr.xgouchet.elmyr.annotation.Forgery @@ -51,11 +52,15 @@ internal class ConsentAwareFileMigratorTest { @Mock lateinit var mockInternalLogger: InternalLogger + @Mock + lateinit var mockTimeProvider: TimeProvider + @BeforeEach fun `set up`() { testedMigrator = ConsentAwareFileMigrator( mockFileMover, - mockInternalLogger + mockInternalLogger, + mockTimeProvider ) } diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/FeatureFileOrchestratorTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/FeatureFileOrchestratorTest.kt index 27fcecabbf..651b1234ce 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/FeatureFileOrchestratorTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/FeatureFileOrchestratorTest.kt @@ -11,6 +11,7 @@ import com.datadog.android.core.internal.metrics.MetricsDispatcher import com.datadog.android.core.internal.persistence.file.FilePersistenceConfig import com.datadog.android.core.internal.persistence.file.batch.BatchFileOrchestrator import com.datadog.android.core.internal.privacy.ConsentProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.privacy.TrackingConsent import com.datadog.android.utils.forge.Configurator import fr.xgouchet.elmyr.annotation.Forgery @@ -64,6 +65,9 @@ internal class FeatureFileOrchestratorTest { @Mock lateinit var mockMetricsDispatcher: MetricsDispatcher + @Mock + lateinit var mockTimeProvider: TimeProvider + @BeforeEach fun `set up`() { whenever(mockConsentProvider.getConsent()) doReturn fakeConsent @@ -81,7 +85,8 @@ internal class FeatureFileOrchestratorTest { mockExecutorService, fakeFilePersistenceConfig, mockInternalLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + mockTimeProvider ) // Then @@ -103,7 +108,8 @@ internal class FeatureFileOrchestratorTest { mockExecutorService, fakeFilePersistenceConfig, mockInternalLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + mockTimeProvider ) // Then @@ -125,7 +131,8 @@ internal class FeatureFileOrchestratorTest { mockExecutorService, fakeFilePersistenceConfig, mockInternalLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + mockTimeProvider ) // Then diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperationTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperationTest.kt index 4f93549804..4450bc1f1a 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperationTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperationTest.kt @@ -8,6 +8,7 @@ package com.datadog.android.core.internal.persistence.file.advanced import com.datadog.android.api.InternalLogger import com.datadog.android.core.internal.persistence.file.FileMover +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import com.datadog.android.utils.verifyLog import fr.xgouchet.elmyr.junit5.ForgeConfiguration @@ -51,13 +52,17 @@ internal class MoveDataMigrationOperationTest { @Mock lateinit var mockInternalLogger: InternalLogger + @Mock + lateinit var mockTimeProvider: TimeProvider + @BeforeEach fun `set up`() { testedOperation = MoveDataMigrationOperation( fakeFromDirectory, fakeToDirectory, mockFileMover, - mockInternalLogger + mockInternalLogger, + mockTimeProvider ) } @@ -68,7 +73,8 @@ internal class MoveDataMigrationOperationTest { null, fakeToDirectory, mockFileMover, - mockInternalLogger + mockInternalLogger, + mockTimeProvider ) // When @@ -90,7 +96,8 @@ internal class MoveDataMigrationOperationTest { fakeFromDirectory, null, mockFileMover, - mockInternalLogger + mockInternalLogger, + mockTimeProvider ) whenever(mockFileMover.delete(fakeFromDirectory)) doReturn true diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/WipeDataMigrationOperationTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/WipeDataMigrationOperationTest.kt index f6a9dbab4b..9118b81ef6 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/WipeDataMigrationOperationTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/WipeDataMigrationOperationTest.kt @@ -8,6 +8,7 @@ package com.datadog.android.core.internal.persistence.file.advanced import com.datadog.android.api.InternalLogger import com.datadog.android.core.internal.persistence.file.FileMover +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import com.datadog.android.utils.verifyLog import fr.xgouchet.elmyr.junit5.ForgeConfiguration @@ -49,12 +50,16 @@ internal class WipeDataMigrationOperationTest { @Mock lateinit var mockInternalLogger: InternalLogger + @Mock + lateinit var mockTimeProvider: TimeProvider + @BeforeEach fun `set up`() { testedOperation = WipeDataMigrationOperation( fakeTargetDirectory, mockFileMover, - mockInternalLogger + mockInternalLogger, + mockTimeProvider ) } @@ -64,7 +69,8 @@ internal class WipeDataMigrationOperationTest { testedOperation = WipeDataMigrationOperation( null, mockFileMover, - mockInternalLogger + mockInternalLogger, + mockTimeProvider ) // When diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt index 8d83ceeee4..9c454f983e 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt @@ -12,6 +12,7 @@ import com.datadog.android.core.internal.metrics.MetricsDispatcher import com.datadog.android.core.internal.metrics.RemovalReason import com.datadog.android.core.internal.persistence.file.FileOrchestrator import com.datadog.android.core.internal.persistence.file.FilePersistenceConfig +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import com.datadog.android.utils.verifyLog import fr.xgouchet.elmyr.Forge @@ -35,7 +36,6 @@ import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.mock -import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.verifyNoMoreInteractions @@ -77,6 +77,9 @@ internal class BatchFileOrchestratorTest { @Mock lateinit var mockPendingFiles: AtomicInteger + @Mock + lateinit var mockTimeProvider: TimeProvider + @BeforeEach fun `set up`() { whenever(mockPendingFiles.decrementAndGet()).thenReturn(fakePendingBatches) @@ -88,7 +91,8 @@ internal class BatchFileOrchestratorTest { config = TEST_PERSISTENCE_CONFIG, internalLogger = mockLogger, metricsDispatcher = mockMetricsDispatcher, - pendingFiles = mockPendingFiles + pendingFiles = mockPendingFiles, + timeProvider = mockTimeProvider ) } @@ -146,7 +150,8 @@ internal class BatchFileOrchestratorTest { notADir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + mockTimeProvider ) // When @@ -173,7 +178,8 @@ internal class BatchFileOrchestratorTest { corruptedDir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + mockTimeProvider ) // When @@ -201,7 +207,8 @@ internal class BatchFileOrchestratorTest { restrictedDir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + mockTimeProvider ) // When @@ -643,7 +650,8 @@ internal class BatchFileOrchestratorTest { notADir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + mockTimeProvider ) // When @@ -669,7 +677,8 @@ internal class BatchFileOrchestratorTest { corruptedDir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + mockTimeProvider ) // When @@ -696,7 +705,8 @@ internal class BatchFileOrchestratorTest { restrictedDir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + mockTimeProvider ) // When @@ -823,7 +833,8 @@ internal class BatchFileOrchestratorTest { notADir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + mockTimeProvider ) // When @@ -849,7 +860,8 @@ internal class BatchFileOrchestratorTest { corruptedDir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + mockTimeProvider ) // When @@ -876,7 +888,8 @@ internal class BatchFileOrchestratorTest { restrictedDir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + mockTimeProvider ) // When @@ -1015,7 +1028,8 @@ internal class BatchFileOrchestratorTest { notADir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + mockTimeProvider ) // When @@ -1041,7 +1055,8 @@ internal class BatchFileOrchestratorTest { corruptedDir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + mockTimeProvider ) // When @@ -1068,7 +1083,8 @@ internal class BatchFileOrchestratorTest { restrictedDir, TEST_PERSISTENCE_CONFIG, mockLogger, - mockMetricsDispatcher + mockMetricsDispatcher, + mockTimeProvider ) // When diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/AbstractExecutorServiceTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/AbstractExecutorServiceTest.kt index 904015772c..e83205146c 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/AbstractExecutorServiceTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/AbstractExecutorServiceTest.kt @@ -9,6 +9,7 @@ package com.datadog.android.core.internal.thread import com.datadog.android.api.InternalLogger import com.datadog.android.core.configuration.BackPressureMitigation import com.datadog.android.core.configuration.BackPressureStrategy +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import com.datadog.android.utils.verifyLog import com.datadog.tools.unit.forge.aThrowable @@ -50,6 +51,9 @@ internal abstract class AbstractExecutorServiceTest { @Mock lateinit var mockOnItemDropped: (Any) -> Unit + @Mock + lateinit var mockTimeProvider: TimeProvider + @IntForgery(8, 128) var fakeBackPressureCapacity: Int = 0 @@ -66,7 +70,7 @@ internal abstract class AbstractExecutorServiceTest { mockOnItemDropped, fakeBackPressureMitigation ) - testedExecutor = createTestedExecutorService(forge, fakeBackpressureStrategy) + testedExecutor = createTestedExecutorService(forge, fakeBackpressureStrategy, mockTimeProvider) } @AfterEach @@ -74,7 +78,11 @@ internal abstract class AbstractExecutorServiceTest { testedExecutor.shutdownNow() } - abstract fun createTestedExecutorService(forge: Forge, backPressureStrategy: BackPressureStrategy): T + abstract fun createTestedExecutorService( + forge: Forge, + backPressureStrategy: BackPressureStrategy, + timeProvider: TimeProvider + ): T // region execute diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/BackPressureExecutorServiceTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/BackPressureExecutorServiceTest.kt index 93b01d2159..f08e004f0d 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/BackPressureExecutorServiceTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/BackPressureExecutorServiceTest.kt @@ -7,6 +7,7 @@ package com.datadog.android.core.internal.thread import com.datadog.android.core.configuration.BackPressureStrategy +import com.datadog.android.internal.time.TimeProvider import fr.xgouchet.elmyr.Forge import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -16,12 +17,14 @@ internal class BackPressureExecutorServiceTest : override fun createTestedExecutorService( forge: Forge, - backPressureStrategy: BackPressureStrategy + backPressureStrategy: BackPressureStrategy, + timeProvider: TimeProvider ): BackPressureExecutorService { return BackPressureExecutorService( mockInternalLogger, forge.anAlphabeticalString(), - backPressureStrategy + backPressureStrategy, + timeProvider ) } diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/DropOldestBackPressuredBlockingQueueTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/DropOldestBackPressuredBlockingQueueTest.kt index 313662a56f..8edfe8fce0 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/DropOldestBackPressuredBlockingQueueTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/DropOldestBackPressuredBlockingQueueTest.kt @@ -9,6 +9,7 @@ package com.datadog.android.core.internal.thread import com.datadog.android.api.InternalLogger import com.datadog.android.core.configuration.BackPressureMitigation import com.datadog.android.core.configuration.BackPressureStrategy +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.IntForgery @@ -51,6 +52,9 @@ class DropOldestBackPressuredBlockingQueueTest { @Mock lateinit var mockOnItemsDropped: (Any) -> Unit + @Mock + lateinit var mockTimeProvider: TimeProvider + @IntForgery(8, 16) var fakeBackPressureThreshold: Int = 0 @@ -64,7 +68,8 @@ class DropOldestBackPressuredBlockingQueueTest { mockOnThresholdReached, mockOnItemsDropped, BackPressureMitigation.DROP_OLDEST - ) + ), + mockTimeProvider ) } diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/IgnoreNewestBackPressuredBlockingQueueTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/IgnoreNewestBackPressuredBlockingQueueTest.kt index bafa652a60..77da591485 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/IgnoreNewestBackPressuredBlockingQueueTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/IgnoreNewestBackPressuredBlockingQueueTest.kt @@ -9,6 +9,7 @@ package com.datadog.android.core.internal.thread import com.datadog.android.api.InternalLogger import com.datadog.android.core.configuration.BackPressureMitigation import com.datadog.android.core.configuration.BackPressureStrategy +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.IntForgery @@ -51,6 +52,9 @@ class IgnoreNewestBackPressuredBlockingQueueTest { @Mock lateinit var mockOnItemsDropped: (Any) -> Unit + @Mock + lateinit var mockTimeProvider: TimeProvider + @IntForgery(8, 16) var fakeBackPressureThreshold: Int = 0 @@ -64,7 +68,8 @@ class IgnoreNewestBackPressuredBlockingQueueTest { mockOnThresholdReached, mockOnItemsDropped, BackPressureMitigation.IGNORE_NEWEST - ) + ), + mockTimeProvider ) } diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/LoggingScheduledThreadPoolExecutorTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/LoggingScheduledThreadPoolExecutorTest.kt index 003bb357bd..8431900ff9 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/LoggingScheduledThreadPoolExecutorTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/LoggingScheduledThreadPoolExecutorTest.kt @@ -8,6 +8,7 @@ package com.datadog.android.core.internal.thread import com.datadog.android.api.InternalLogger import com.datadog.android.core.configuration.BackPressureStrategy +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.verifyLog import com.datadog.tools.unit.forge.aThrowable import fr.xgouchet.elmyr.Forge @@ -23,7 +24,8 @@ internal class LoggingScheduledThreadPoolExecutorTest : override fun createTestedExecutorService( forge: Forge, - backPressureStrategy: BackPressureStrategy + backPressureStrategy: BackPressureStrategy, + timeProvider: TimeProvider ): ScheduledThreadPoolExecutor { return LoggingScheduledThreadPoolExecutor( 1, diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/NotifyOnlyUnboundedBackPressuredBlockingQueueTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/NotifyOnlyUnboundedBackPressuredBlockingQueueTest.kt index 8612705920..359343fd20 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/NotifyOnlyUnboundedBackPressuredBlockingQueueTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/NotifyOnlyUnboundedBackPressuredBlockingQueueTest.kt @@ -7,6 +7,7 @@ package com.datadog.android.core.internal.thread import com.datadog.android.api.InternalLogger +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.IntForgery @@ -49,6 +50,9 @@ class NotifyOnlyUnboundedBackPressuredBlockingQueueTest { @Mock lateinit var mockOnItemsDropped: (Any) -> Unit + @Mock + lateinit var mockTimeProvider: TimeProvider + @IntForgery(8, 16) var fakeNotifyThreshold: Int = 0 @@ -61,7 +65,8 @@ class NotifyOnlyUnboundedBackPressuredBlockingQueueTest { capacity = Int.MAX_VALUE, onThresholdReached = mockOnThresholdReached, onItemDropped = mockOnItemsDropped, - backpressureMitigation = null + backpressureMitigation = null, + timeProvider = mockTimeProvider ) } diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/ObservableBlockingQueueTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/ObservableLinkedBlockingQueueTest.kt similarity index 85% rename from dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/ObservableBlockingQueueTest.kt rename to dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/ObservableLinkedBlockingQueueTest.kt index 73942b7de3..3c4a029ae1 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/ObservableBlockingQueueTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/ObservableLinkedBlockingQueueTest.kt @@ -7,6 +7,7 @@ package com.datadog.android.core.internal.thread import com.datadog.android.internal.thread.NamedRunnable +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.junit5.ForgeConfiguration @@ -18,6 +19,8 @@ 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.kotlin.doReturn +import org.mockito.kotlin.whenever import org.mockito.quality.Strictness @Extensions( @@ -26,7 +29,7 @@ import org.mockito.quality.Strictness ) @MockitoSettings(strictness = Strictness.LENIENT) @ForgeConfiguration(Configurator::class) -class ObservableBlockingQueueTest { +class ObservableLinkedBlockingQueueTest { @Test fun `M return only once non-null map W try to dump multiple times in dump interval`( @@ -39,9 +42,10 @@ class ObservableBlockingQueueTest { val fakeThirdTimestamp = fakeSecondTimestamp + forge.aLong(max = 1000) val fakeTimestamps = listOf(fakeFirstTimestamp, fakeSecondTimestamp, fakeThirdTimestamp).iterator() - val fakeTimeProvider: () -> Long = { fakeTimestamps.next() } + val mockTimeProvider = mock() + whenever(mockTimeProvider.getDeviceTimestamp()).thenAnswer { fakeTimestamps.next() } val testedObservableLinkedBlockingQueue = - ObservableLinkedBlockingQueue(fakeItemCount + 1, fakeTimeProvider) + ObservableLinkedBlockingQueue(fakeItemCount + 1, mockTimeProvider) repeat(fakeItemCount) { val mockItem = mock() testedObservableLinkedBlockingQueue.offer(mockItem) @@ -67,9 +71,10 @@ class ObservableBlockingQueueTest { val fakeFirstTimestamp = forge.aLong(min = 10000) val fakeSecondTimestamp = fakeFirstTimestamp + forge.aLong(min = 5000) val fakeTimestamps = listOf(fakeFirstTimestamp, fakeSecondTimestamp).iterator() - val fakeTimeProvider: () -> Long = { fakeTimestamps.next() } + val mockTimeProvider = mock() + whenever(mockTimeProvider.getDeviceTimestamp()).thenAnswer { fakeTimestamps.next() } val testedObservableLinkedBlockingQueue = - ObservableLinkedBlockingQueue(fakeItemCount + 1, fakeTimeProvider) + ObservableLinkedBlockingQueue(fakeItemCount + 1, mockTimeProvider) repeat(fakeItemCount) { val mockItem = mock() testedObservableLinkedBlockingQueue.offer(mockItem) @@ -90,10 +95,12 @@ class ObservableBlockingQueueTest { ) { // Given val expectedMap = mutableMapOf() - val fakeTimeProvider: () -> Long = { forge.aLong(min = 10000L) } + val fakeTimestamp = forge.aLong(min = 10000L) + val mockTimeProvider = mock() + whenever(mockTimeProvider.getDeviceTimestamp()) doReturn fakeTimestamp val fakeRunnableCount = forge.anInt(min = 5, max = 100) val testedObservableLinkedBlockingQueue = - ObservableLinkedBlockingQueue(fakeRunnableCount + 1, fakeTimeProvider) + ObservableLinkedBlockingQueue(fakeRunnableCount + 1, mockTimeProvider) val fakeRunnableTypeCount = forge.anInt(min = 1, max = fakeRunnableCount) val fakeRunnableTypes = mutableListOf() repeat(fakeRunnableTypeCount) { diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/ThreadPoolExecutorExtTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/ThreadPoolExecutorExtTest.kt index f25b6dfe1b..38d151cafb 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/ThreadPoolExecutorExtTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/ThreadPoolExecutorExtTest.kt @@ -7,6 +7,7 @@ package com.datadog.android.core.internal.thread import com.datadog.android.api.InternalLogger +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.LongForgery @@ -20,7 +21,6 @@ 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.kotlin.mock import org.mockito.kotlin.whenever import org.mockito.quality.Strictness import java.util.concurrent.ThreadPoolExecutor @@ -40,6 +40,9 @@ internal class ThreadPoolExecutorExtTest { @Mock lateinit var mockInternalLogger: InternalLogger + @Mock + lateinit var mockTimeProvider: TimeProvider + @Test fun `M return false W waitToIdle { timeout reached }`( @LongForgery(min = 0, max = 500) fakeTimeout: Long, @@ -52,7 +55,7 @@ internal class ThreadPoolExecutorExtTest { whenever(testedMockExecutor.completedTaskCount).thenReturn(fakeCompletedCount) // WHEN - val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger) + val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, mockTimeProvider) testedMockExecutor.isIdle() // THEN @@ -72,7 +75,7 @@ internal class ThreadPoolExecutorExtTest { // WHEN val duration = measureTimeMillis { - testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger) + testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, mockTimeProvider) } // THEN @@ -91,7 +94,7 @@ internal class ThreadPoolExecutorExtTest { .thenReturn(fakeTaskCount) // WHEN - val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger) + val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, mockTimeProvider) // THEN assertThat(isIdled).isTrue() @@ -112,7 +115,7 @@ internal class ThreadPoolExecutorExtTest { .thenReturn(fakeTaskCount / 2).thenReturn(fakeTaskCount) // WHEN - val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, internalLogger = mock()) + val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, mockTimeProvider) // THEN assertThat(isIdled).isTrue() @@ -128,7 +131,7 @@ internal class ThreadPoolExecutorExtTest { val fakeCompletedCount = forge.aLong(min = 0, max = fakeTaskCount - 1) whenever(testedMockExecutor.taskCount).thenReturn(fakeTaskCount) whenever(testedMockExecutor.completedTaskCount).thenReturn(fakeCompletedCount) - val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger) + val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, mockTimeProvider) // THEN assertThat(isIdled).isFalse() @@ -145,7 +148,7 @@ internal class ThreadPoolExecutorExtTest { .thenReturn(fakeTaskCount) // WHEN - val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger) + val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, mockTimeProvider) // THEN assertThat(isIdled).isTrue() @@ -168,7 +171,7 @@ internal class ThreadPoolExecutorExtTest { .thenReturn(fakeTaskCount + 2) // WHEN - val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger) + val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, mockTimeProvider) // THEN assertThat(isIdled).isTrue() diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt index bb01164101..9a974e9bb8 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt @@ -10,6 +10,7 @@ import android.os.Build import android.os.Process import android.os.SystemClock import com.datadog.android.core.internal.system.BuildSdkVersionProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.DdRumContentProvider import fr.xgouchet.elmyr.annotation.IntForgery import fr.xgouchet.elmyr.junit5.ForgeExtension @@ -32,13 +33,14 @@ class DefaultAppStartTimeProviderTest { @IntForgery(min = Build.VERSION_CODES.N) apiVersion: Int ) { // GIVEN + val mockTimeProvider: TimeProvider = mock() val mockBuildSdkVersionProvider: BuildSdkVersionProvider = mock() whenever(mockBuildSdkVersionProvider.version) doReturn apiVersion val diffMs = SystemClock.elapsedRealtime() - Process.getStartElapsedRealtime() val startTimeNs = System.nanoTime() - TimeUnit.MILLISECONDS.toNanos(diffMs) // WHEN - val timeProvider = DefaultAppStartTimeProvider(mockBuildSdkVersionProvider) + val timeProvider = DefaultAppStartTimeProvider(mockTimeProvider, mockBuildSdkVersionProvider) val providedStartTime = timeProvider.appStartTimeNs // THEN @@ -51,12 +53,13 @@ class DefaultAppStartTimeProviderTest { @IntForgery(min = Build.VERSION_CODES.M, max = Build.VERSION_CODES.N) apiVersion: Int ) { // GIVEN + val mockTimeProvider: TimeProvider = mock() val mockBuildSdkVersionProvider: BuildSdkVersionProvider = mock() whenever(mockBuildSdkVersionProvider.version) doReturn apiVersion val startTimeNs = DdRumContentProvider.createTimeNs // WHEN - val timeProvider = DefaultAppStartTimeProvider(mockBuildSdkVersionProvider) + val timeProvider = DefaultAppStartTimeProvider(mockTimeProvider, mockBuildSdkVersionProvider) val providedStartTime = timeProvider.appStartTimeNs // THEN diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/MiscUtilsTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/MiscUtilsTest.kt index d00c1ee8ee..3a198eb3ba 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/MiscUtilsTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/MiscUtilsTest.kt @@ -9,6 +9,7 @@ package com.datadog.android.core.internal.utils import com.datadog.android.api.InternalLogger import com.datadog.android.core.internal.utils.JsonSerializer.ITEM_SERIALIZATION_ERROR import com.datadog.android.core.internal.utils.JsonSerializer.safeMapValuesToJson +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.internal.utils.NULL_MAP_VALUE import com.datadog.android.utils.forge.Configurator import com.datadog.android.utils.verifyLog @@ -62,6 +63,9 @@ internal class MiscUtilsTest { @Mock lateinit var mockInternalLogger: InternalLogger + @Mock + lateinit var mockTimeProvider: TimeProvider + @Test fun `M repeat max N times W retryWithDelay { success = false }`(forge: Forge) { // Given @@ -71,7 +75,7 @@ internal class MiscUtilsTest { whenever(mockedBlock.invoke()).thenReturn(false) // When - val wasSuccessful = retryWithDelay(mockedBlock, fakeTimes, fakeDelay, mockInternalLogger) + val wasSuccessful = retryWithDelay(mockedBlock, fakeTimes, fakeDelay, mockInternalLogger, mockTimeProvider) // Then assertThat(wasSuccessful).isFalse() @@ -87,7 +91,8 @@ internal class MiscUtilsTest { whenever(mockedBlock.invoke()).thenReturn(false) // When - val executionTime = measureNanoTime { retryWithDelay(mockedBlock, fakeTimes, fakeDelay, mockInternalLogger) } + val executionTime = + measureNanoTime { retryWithDelay(mockedBlock, fakeTimes, fakeDelay, mockInternalLogger, mockTimeProvider) } // Then assertThat(executionTime).isCloseTo( @@ -103,7 +108,7 @@ internal class MiscUtilsTest { val mockedBlock: () -> Boolean = mock() // When - retryWithDelay(mockedBlock, forge.anInt(Int.MIN_VALUE, 1), fakeDelay, mockInternalLogger) + retryWithDelay(mockedBlock, forge.anInt(Int.MIN_VALUE, 1), fakeDelay, mockInternalLogger, mockTimeProvider) // Then verifyNoInteractions(mockedBlock) @@ -117,7 +122,7 @@ internal class MiscUtilsTest { whenever(mockedBlock.invoke()).thenReturn(false).thenReturn(true) // When - val wasSuccessful = retryWithDelay(mockedBlock, 3, fakeDelay, mockInternalLogger) + val wasSuccessful = retryWithDelay(mockedBlock, 3, fakeDelay, mockInternalLogger, mockTimeProvider) // Then assertThat(wasSuccessful).isTrue() @@ -135,7 +140,7 @@ internal class MiscUtilsTest { whenever(mockedBlock.invoke()).thenThrow(exception).thenReturn(true) // When - val wasSuccessful = retryWithDelay(mockedBlock, 3, fakeDelay, mockInternalLogger) + val wasSuccessful = retryWithDelay(mockedBlock, 3, fakeDelay, mockInternalLogger, mockTimeProvider) // Then assertThat(wasSuccessful).isTrue() diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/error/internal/DatadogExceptionHandlerTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/error/internal/DatadogExceptionHandlerTest.kt index 5450f205ca..fc6d1f0bb1 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/error/internal/DatadogExceptionHandlerTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/error/internal/DatadogExceptionHandlerTest.kt @@ -21,6 +21,7 @@ import com.datadog.android.core.internal.CoreFeature import com.datadog.android.core.internal.thread.waitToIdle import com.datadog.android.core.internal.utils.TAG_DATADOG_UPLOAD import com.datadog.android.core.internal.utils.UPLOAD_WORKER_NAME +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.internal.utils.loggableStackTrace import com.datadog.android.utils.config.ApplicationContextTestConfiguration import com.datadog.android.utils.forge.Configurator @@ -81,6 +82,9 @@ internal class DatadogExceptionHandlerTest { @Mock lateinit var mockInternalLogger: InternalLogger + @Mock + lateinit var mockTimeProvider: TimeProvider + @Mock lateinit var mockLogsFeatureScope: FeatureScope @@ -102,6 +106,8 @@ internal class DatadogExceptionHandlerTest { whenever(mockSdkCore.getFeature(Feature.RUM_FEATURE_NAME)) doReturn mockRumFeatureScope whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger whenever(mockSdkCore.name) doReturn fakeInstanceName + whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider + whenever(mockTimeProvider.getDeviceElapsedTimeNs()) doReturn 0L CoreFeature.disableKronosBackgroundSync = true @@ -164,7 +170,7 @@ internal class DatadogExceptionHandlerTest { // Then verify(mockScheduledThreadExecutor) - .waitToIdle(DatadogExceptionHandler.MAX_WAIT_FOR_IDLE_TIME_IN_MS, mockInternalLogger) + .waitToIdle(DatadogExceptionHandler.MAX_WAIT_FOR_IDLE_TIME_IN_MS, mockInternalLogger, mockTimeProvider) mockInternalLogger.verifyLog( InternalLogger.Level.WARN, InternalLogger.Target.USER, diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessorTest.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessorTest.kt index 602ea1a461..c52c4546b3 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessorTest.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessorTest.kt @@ -11,6 +11,7 @@ import com.datadog.android.flags.internal.storage.RecordWriter import com.datadog.android.flags.model.EvaluationContext import com.datadog.android.flags.model.ExposureEvent import com.datadog.android.flags.utils.forge.ForgeConfigurator +import com.datadog.android.internal.time.TimeProvider import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration @@ -36,6 +37,9 @@ internal class ExposureEventsProcessorTest { @Mock lateinit var mockRecordWriter: RecordWriter + @Mock + lateinit var mockTimeProvider: TimeProvider + @StringForgery lateinit var fakeFlagName: String @@ -53,7 +57,7 @@ internal class ExposureEventsProcessorTest { @BeforeEach fun `set up`(forge: Forge) { - testedProcessor = ExposureEventsProcessor(mockRecordWriter) + testedProcessor = ExposureEventsProcessor(mockRecordWriter, mockTimeProvider) fakeFlag = forge.getForgery().copy( allocationKey = fakeAllocationKey, variationKey = fakeVariationKey @@ -481,7 +485,7 @@ internal class ExposureEventsProcessorTest { Thread { repeat(10) { Thread.sleep(5) - testedProcessor = ExposureEventsProcessor(mockRecordWriter) + testedProcessor = ExposureEventsProcessor(mockRecordWriter, mockTimeProvider) } } } diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateSerializerTest.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateSerializerTest.kt index 762aac3651..2217a75740 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateSerializerTest.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/persistence/FlagsStateSerializerTest.kt @@ -86,7 +86,7 @@ internal class FlagsStateSerializerTest { // Given val targetingKey = forge.anAlphabeticalString() val evaluationContext = EvaluationContext(targetingKey, emptyMap()) - val flagsState = FlagsStateEntry(evaluationContext, emptyMap()) + val flagsState = FlagsStateEntry(evaluationContext, emptyMap(), System.currentTimeMillis()) // When val serialized = testedSerializer.serialize(flagsState) @@ -103,7 +103,7 @@ internal class FlagsStateSerializerTest { // Given // Create a state that could potentially cause serialization issues val evaluationContext = EvaluationContext("key", emptyMap()) - val flagsState = FlagsStateEntry(evaluationContext, emptyMap()) + val flagsState = FlagsStateEntry(evaluationContext, emptyMap(), System.currentTimeMillis()) // When val serialized = testedSerializer.serialize(flagsState) diff --git a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt index 9685371f48..3cb208a629 100644 --- a/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt +++ b/features/dd-sdk-android-logs/src/test/kotlin/com/datadog/android/log/internal/logger/DatadogLogHandlerTest.kt @@ -178,7 +178,6 @@ internal class DatadogLogHandlerTest { @Test fun `forward log to LogWriter`() { - // When testedHandler.handleLog( fakeLevel, @@ -258,7 +257,6 @@ internal class DatadogLogHandlerTest { @Test fun `forward log to LogWriter with throwable`() { - // When testedHandler.handleLog( fakeLevel, @@ -322,7 +320,6 @@ internal class DatadogLogHandlerTest { @StringForgery errorStack: String ) { // Given - // When testedHandler.handleLog( @@ -638,7 +635,7 @@ internal class DatadogLogHandlerTest { @Test fun `forward log to LogWriter on background thread`(forge: Forge) { // Given - + val threadName = forge.anAlphabeticalString() val countDownLatch = CountDownLatch(1) @@ -702,7 +699,7 @@ internal class DatadogLogHandlerTest { @Test fun `forward log to LogWriter without network info`() { // Given - + testedHandler = DatadogLogHandler( loggerName = fakeLoggerName, logGenerator = DatadogLogGenerator( @@ -764,7 +761,7 @@ internal class DatadogLogHandlerTest { @Test fun `forward minimal log to LogWriter`() { // Given - + fakeDatadogContext = fakeDatadogContext.copy( featuresContext = fakeDatadogContext.featuresContext.toMutableMap().apply { remove(Feature.RUM_FEATURE_NAME) diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReader.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReader.kt index cd9f3bd353..01659dfe12 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReader.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReader.kt @@ -29,6 +29,7 @@ import java.util.concurrent.atomic.AtomicLong internal class DefaultAccessibilityReader( private val internalLogger: InternalLogger, private val applicationContext: Context, + private val timeProvider: TimeProvider, private val resources: Resources = applicationContext.resources, private val activityManager: ActivityManager? = applicationContext.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager, @@ -36,8 +37,7 @@ internal class DefaultAccessibilityReader( applicationContext.getSystemService(Context.ACCESSIBILITY_SERVICE) as? AccessibilityManager, private val secureWrapper: SecureWrapper = SecureWrapper(), private val globalWrapper: GlobalWrapper = GlobalWrapper(), - private val handler: Handler = Handler(Looper.getMainLooper()), - private val timeProvider: TimeProvider + private val handler: Handler = Handler(Looper.getMainLooper()) ) : InfoProvider, ComponentCallbacks { private val displayInversionListener = object : ContentObserver(handler) { diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/RumConfigurationBuilderTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/RumConfigurationBuilderTest.kt index 9b068a7e5f..5bf1d32c8d 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/RumConfigurationBuilderTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/RumConfigurationBuilderTest.kt @@ -9,6 +9,7 @@ package com.datadog.android.rum import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.event.EventMapper import com.datadog.android.event.NoOpEventMapper +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.assertj.ConfigurationRumAssert import com.datadog.android.rum.configuration.SlowFramesConfiguration import com.datadog.android.rum.configuration.VitalsUpdateFrequency @@ -69,6 +70,9 @@ internal class RumConfigurationBuilderTest { @Mock lateinit var mockSdkCore: FeatureSdkCore + @Mock + lateinit var mockTimeProvider: TimeProvider + @Forgery lateinit var fakeApplicationId: UUID @@ -103,7 +107,7 @@ internal class RumConfigurationBuilderTest { assertThat(telemetryConfigurationMapper) .isEqualTo(NoOpEventMapper()) assertThat(longTaskTrackingStrategy) - .isEqualTo(MainLooperLongTaskStrategy(100L)) + .isEqualTo(MainLooperLongTaskStrategy(100L, mockTimeProvider)) assertThat(backgroundEventTracking).isFalse() assertThat(trackFrustrations).isTrue() // on Android R+ this should be false, but since default value is static property @@ -231,7 +235,7 @@ internal class RumConfigurationBuilderTest { // Then assertThat(rumConfiguration.featureConfiguration).isEqualTo( RumFeature.DEFAULT_RUM_CONFIG.copy( - longTaskTrackingStrategy = MainLooperLongTaskStrategy(durationMs) + longTaskTrackingStrategy = MainLooperLongTaskStrategy(durationMs, mockTimeProvider) ) ) } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReaderTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReaderTest.kt index 2517883d08..33effd9f93 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReaderTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReaderTest.kt @@ -20,6 +20,7 @@ import android.view.View import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener import com.datadog.android.api.InternalLogger +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.internal.domain.accessibility.DefaultAccessibilityReader.Companion.CAPTIONING_ENABLED_KEY import com.datadog.android.rum.utils.forge.Configurator import com.datadog.tools.unit.annotations.TestTargetApi @@ -84,6 +85,9 @@ internal class DefaultAccessibilityReaderTest { @Mock lateinit var mockHandler: Handler + @Mock + lateinit var mockTimeProvider: TimeProvider + private lateinit var testedReader: DefaultAccessibilityReader @BeforeEach @@ -101,6 +105,7 @@ internal class DefaultAccessibilityReaderTest { return DefaultAccessibilityReader( internalLogger = mockInternalLogger, applicationContext = mockContext, + timeProvider = mockTimeProvider, resources = mockResources, activityManager = mockActivityManager, accessibilityManager = mockAccessibilityManager, @@ -157,6 +162,7 @@ internal class DefaultAccessibilityReaderTest { testedReader = DefaultAccessibilityReader( internalLogger = mockInternalLogger, applicationContext = mockContext, + timeProvider = mockTimeProvider, resources = mockResources, activityManager = mockActivityManager, accessibilityManager = null, @@ -336,6 +342,7 @@ internal class DefaultAccessibilityReaderTest { testedReader = DefaultAccessibilityReader( internalLogger = mockInternalLogger, applicationContext = mockContext, + timeProvider = mockTimeProvider, resources = mockResources, activityManager = null, accessibilityManager = mockAccessibilityManager, @@ -616,6 +623,7 @@ internal class DefaultAccessibilityReaderTest { testedReader = DefaultAccessibilityReader( internalLogger = mockInternalLogger, applicationContext = mockContext, + timeProvider = mockTimeProvider, resources = mockResources, activityManager = mockActivityManager, accessibilityManager = null, diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeAttributePropagationTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeAttributePropagationTest.kt index df709cb7a9..2845bd6487 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeAttributePropagationTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeAttributePropagationTest.kt @@ -14,6 +14,7 @@ import com.datadog.android.api.feature.EventWriteScope import com.datadog.android.api.feature.FeatureScope import com.datadog.android.api.storage.DataWriter import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.RumSessionListener import com.datadog.android.rum.RumSessionType import com.datadog.android.rum.internal.FeaturesContextResolver @@ -125,6 +126,9 @@ internal class RumApplicationScopeAttributePropagationTest { @Mock lateinit var mockRumAppStartupTelemetryReporter: RumAppStartupTelemetryReporter + @Mock + lateinit var mockTimeProvider: TimeProvider + @Mock lateinit var mockSlowFramesListener: SlowFramesListener @@ -207,7 +211,8 @@ internal class RumApplicationScopeAttributePropagationTest { accessibilitySnapshotManager = mockAccessibilitySnapshotManager, batteryInfoProvider = mockBatteryInfoProvider, displayInfoProvider = mockDisplayInfoProvider, - rumAppStartupTelemetryReporter = mockRumAppStartupTelemetryReporter + rumAppStartupTelemetryReporter = mockRumAppStartupTelemetryReporter, + timeProvider = mockTimeProvider ) } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeTest.kt index 18b73af121..0021d08e47 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScopeTest.kt @@ -16,6 +16,7 @@ import com.datadog.android.api.feature.FeatureScope import com.datadog.android.api.storage.DataWriter import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.DdRumContentProvider import com.datadog.android.rum.RumActionType import com.datadog.android.rum.RumSessionListener @@ -131,6 +132,9 @@ internal class RumApplicationScopeTest { @Mock lateinit var mockInternalLogger: InternalLogger + @Mock + lateinit var mockTimeProvider: TimeProvider + @StringForgery(regex = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}") lateinit var fakeApplicationId: String @@ -183,7 +187,8 @@ internal class RumApplicationScopeTest { accessibilitySnapshotManager = mockAccessibilitySnapshotManager, batteryInfoProvider = mockBatteryInfoProvider, displayInfoProvider = mockDisplayInfoProvider, - rumAppStartupTelemetryReporter = mockRumAppStartupTelemetryReporter + rumAppStartupTelemetryReporter = mockRumAppStartupTelemetryReporter, + timeProvider = mockTimeProvider ) } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeAttributePropagationTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeAttributePropagationTest.kt index 5d8d1be210..ceeb07f737 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeAttributePropagationTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeAttributePropagationTest.kt @@ -14,6 +14,7 @@ import com.datadog.android.api.storage.DataWriter import com.datadog.android.api.storage.EventBatchWriter import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.RumSessionListener import com.datadog.android.rum.RumSessionType import com.datadog.android.rum.internal.FeaturesContextResolver @@ -124,6 +125,9 @@ internal class RumSessionScopeAttributePropagationTest { @Mock lateinit var mockSlowFramesListener: SlowFramesListener + @Mock + lateinit var mockTimeProvider: TimeProvider + lateinit var fakeParentAttributes: Map @Forgery @@ -182,7 +186,8 @@ internal class RumSessionScopeAttributePropagationTest { accessibilitySnapshotManager = mockAccessibilitySnapshotManager, batteryInfoProvider = mockBatteryInfoProvider, displayInfoProvider = mockDisplayInfoProvider, - rumAppStartupTelemetryReporter = mockRumAppStartupTelemetryReporter + rumAppStartupTelemetryReporter = mockRumAppStartupTelemetryReporter, + timeProvider = mockTimeProvider ) } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeTest.kt index 628691e80e..276fd455cd 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeTest.kt @@ -16,6 +16,7 @@ import com.datadog.android.api.storage.DataWriter import com.datadog.android.api.storage.NoOpDataWriter import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.RumSessionListener import com.datadog.android.rum.RumSessionType import com.datadog.android.rum.internal.domain.InfoProvider @@ -141,6 +142,9 @@ internal class RumSessionScopeTest { @Mock lateinit var mockEventWriteScope: EventWriteScope + @Mock + lateinit var mockTimeProvider: TimeProvider + @Forgery lateinit var fakeParentContext: RumContext @@ -1552,7 +1556,8 @@ internal class RumSessionScopeTest { accessibilitySnapshotManager = mockAccessibilitySnapshotManager, batteryInfoProvider = mockBatteryInfoProvider, displayInfoProvider = mockDisplayInfoProvider, - rumAppStartupTelemetryReporter = mockRumAppStartupTelemetryReporter + rumAppStartupTelemetryReporter = mockRumAppStartupTelemetryReporter, + timeProvider = mockTimeProvider ) if (withMockChildScope) { diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/instrumentation/MainLooperLongTaskStrategyTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/instrumentation/MainLooperLongTaskStrategyTest.kt index bd8bbe8bb5..549c63465a 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/instrumentation/MainLooperLongTaskStrategyTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/instrumentation/MainLooperLongTaskStrategyTest.kt @@ -7,6 +7,7 @@ package com.datadog.android.rum.internal.instrumentation import android.os.Looper +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.internal.monitor.AdvancedRumMonitor import com.datadog.android.rum.utils.config.GlobalRumMonitorTestConfiguration import com.datadog.android.rum.utils.forge.Configurator @@ -54,11 +55,14 @@ internal class MainLooperLongTaskStrategyTest : ObjectTest Unit>() @@ -415,7 +422,8 @@ internal class DatadogRumMonitorTest { accessibilitySnapshotManager = mockAccessibilitySnapshotManager, batteryInfoProvider = mockBatteryInfoProvider, displayInfoProvider = mockDisplayInfoProvider, - rumAppStartupTelemetryReporter = mockRumAppStartupTelemetryReporter + rumAppStartupTelemetryReporter = mockRumAppStartupTelemetryReporter, + timeProvider = mockTimeProvider ) testedMonitor.start() val mockCallback = mock<(String?) -> Unit>() @@ -2032,7 +2040,8 @@ internal class DatadogRumMonitorTest { accessibilitySnapshotManager = mockAccessibilitySnapshotManager, batteryInfoProvider = mockBatteryInfoProvider, displayInfoProvider = mockDisplayInfoProvider, - rumAppStartupTelemetryReporter = mockRumAppStartupTelemetryReporter + rumAppStartupTelemetryReporter = mockRumAppStartupTelemetryReporter, + timeProvider = mockTimeProvider ) // When @@ -2071,7 +2080,8 @@ internal class DatadogRumMonitorTest { accessibilitySnapshotManager = mockAccessibilitySnapshotManager, batteryInfoProvider = mockBatteryInfoProvider, displayInfoProvider = mockDisplayInfoProvider, - rumAppStartupTelemetryReporter = mockRumAppStartupTelemetryReporter + rumAppStartupTelemetryReporter = mockRumAppStartupTelemetryReporter, + timeProvider = mockTimeProvider ) // When @@ -2111,7 +2121,8 @@ internal class DatadogRumMonitorTest { batteryInfoProvider = mockBatteryInfoProvider, displayInfoProvider = mockDisplayInfoProvider, rumSessionTypeOverride = null, - rumAppStartupTelemetryReporter = mockRumAppStartupTelemetryReporter + rumAppStartupTelemetryReporter = mockRumAppStartupTelemetryReporter, + timeProvider = mockTimeProvider ) whenever(mockExecutorService.isShutdown).thenReturn(true) @@ -2283,7 +2294,8 @@ internal class DatadogRumMonitorTest { accessibilitySnapshotManager = mockAccessibilitySnapshotManager, batteryInfoProvider = mockBatteryInfoProvider, displayInfoProvider = mockDisplayInfoProvider, - rumAppStartupTelemetryReporter = mockRumAppStartupTelemetryReporter + rumAppStartupTelemetryReporter = mockRumAppStartupTelemetryReporter, + timeProvider = mockTimeProvider ) testedMonitor.startView(key, name, attributes) // When diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandler.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandler.kt index 969e3f542a..6f2b3bd27c 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandler.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/RecordedDataQueueHandler.kt @@ -56,8 +56,8 @@ internal class RecordedDataQueueHandler( recordedQueuedItemContext = rumContextData, identifier = identifier, resourceData = resourceData, - mimeType, - timeProvider = timeProvider + timeProvider = timeProvider, + mimeType = mimeType ) insertIntoRecordedDataQueue(item) diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/ResourceRecordedDataQueueItem.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/ResourceRecordedDataQueueItem.kt index 2569acd938..44e12006d6 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/ResourceRecordedDataQueueItem.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/ResourceRecordedDataQueueItem.kt @@ -13,8 +13,8 @@ internal class ResourceRecordedDataQueueItem( recordedQueuedItemContext: RecordedQueuedItemContext, val identifier: String, val resourceData: ByteArray, - val mimeType: String? = null, - timeProvider: TimeProvider + val timeProvider: TimeProvider, + val mimeType: String? = null ) : RecordedDataQueueItem( recordedQueuedItemContext, timeProvider.getDeviceElapsedTimeNs() diff --git a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/SnapshotRecordedDataQueueItem.kt b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/SnapshotRecordedDataQueueItem.kt index 172087a969..d1b210d3df 100644 --- a/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/SnapshotRecordedDataQueueItem.kt +++ b/features/dd-sdk-android-session-replay/src/main/kotlin/com/datadog/android/sessionreplay/internal/async/SnapshotRecordedDataQueueItem.kt @@ -14,8 +14,8 @@ import java.util.concurrent.atomic.AtomicInteger internal class SnapshotRecordedDataQueueItem( recordedQueuedItemContext: RecordedQueuedItemContext, - timeProvider: TimeProvider, - internal val systemInformation: SystemInformation + internal val systemInformation: SystemInformation, + timeProvider: TimeProvider ) : RecordedDataQueueItem( recordedQueuedItemContext, timeProvider.getDeviceElapsedTimeNs() diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/ResourceRecordedDataQueueItemForgeryFactory.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/ResourceRecordedDataQueueItemForgeryFactory.kt index dce9507019..04a0de5f60 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/ResourceRecordedDataQueueItemForgeryFactory.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/ResourceRecordedDataQueueItemForgeryFactory.kt @@ -16,7 +16,8 @@ internal class ResourceRecordedDataQueueItemForgeryFactory : ForgeryFactory().toString(), - resourceData = forge.aString().toByteArray() + resourceData = forge.aString().toByteArray(), + timeProvider = forge.getForgery() ) } } diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/SnapshotRecordedDataQueueItemForgeryFactory.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/SnapshotRecordedDataQueueItemForgeryFactory.kt index 6d9cb9546a..d76e5aee0b 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/SnapshotRecordedDataQueueItemForgeryFactory.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/SnapshotRecordedDataQueueItemForgeryFactory.kt @@ -14,7 +14,8 @@ internal class SnapshotRecordedDataQueueItemForgeryFactory : ForgeryFactory + @Mock + lateinit var mockTimeProvider: TimeProvider + @Forgery lateinit var fakeRecordedQueuedItemContext: RecordedQueuedItemContext @@ -135,7 +139,8 @@ internal class RecordedDataQueueHandlerTest { internalLogger = mockInternalLogger, recordedDataQueue = fakeRecordedDataQueue, telemetrySampleRate = 1f, - sampler = mockRateBasedSampler + sampler = mockRateBasedSampler, + timeProvider = mockTimeProvider ) } @@ -159,7 +164,8 @@ internal class RecordedDataQueueHandlerTest { internalLogger = mockInternalLogger, recordedDataQueue = mockQueue, telemetrySampleRate = 1f, - sampler = mockRateBasedSampler + sampler = mockRateBasedSampler, + timeProvider = mockTimeProvider ) testedHandler.recordedDataQueue.add(fakeSnapshotQueueItem) @@ -686,7 +692,8 @@ internal class RecordedDataQueueHandlerTest { internalLogger = mockInternalLogger, recordedDataQueue = fakeRecordedDataQueue, telemetrySampleRate = 0f, - sampler = mockRateBasedSampler + sampler = mockRateBasedSampler, + timeProvider = mockTimeProvider ) whenever(mockSnapshotItem.nodes).thenReturn(emptyList()) @@ -728,7 +735,8 @@ internal class RecordedDataQueueHandlerTest { internalLogger = mockInternalLogger, recordedDataQueue = fakeRecordedDataQueue, telemetrySampleRate = 0f, - sampler = mockRateBasedSampler + sampler = mockRateBasedSampler, + timeProvider = mockTimeProvider ) mockSnapshotItem.apply { diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/ResourceRecordedDataQueueItemTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/ResourceRecordedDataQueueItemTest.kt index 172a5e83d6..f33f7b864e 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/ResourceRecordedDataQueueItemTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/ResourceRecordedDataQueueItemTest.kt @@ -33,7 +33,8 @@ internal class ResourceRecordedDataQueueItemTest { val testedRecordedDataQueueItem = ResourceRecordedDataQueueItem( recordedQueuedItemContext = fakeResourceRecordedDataQueueItem.recordedQueuedItemContext, identifier = fakeResourceRecordedDataQueueItem.identifier, - resourceData = ByteArray(0) + resourceData = ByteArray(0), + timeProvider = fakeResourceRecordedDataQueueItem.timeProvider ) // Then diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/SnapshotRecordedDataQueueItemTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/SnapshotRecordedDataQueueItemTest.kt index c53537477d..ec8e4925c0 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/SnapshotRecordedDataQueueItemTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/SnapshotRecordedDataQueueItemTest.kt @@ -6,6 +6,7 @@ package com.datadog.android.sessionreplay.internal.async +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.sessionreplay.forge.ForgeConfigurator import com.datadog.android.sessionreplay.internal.processor.RecordedQueuedItemContext import com.datadog.android.sessionreplay.recorder.SystemInformation @@ -39,6 +40,9 @@ internal class SnapshotRecordedDataQueueItemTest { @Mock lateinit var mockSystemInformation: SystemInformation + @Mock + lateinit var mockTimeProvider: TimeProvider + private lateinit var testedItem: SnapshotRecordedDataQueueItem @BeforeEach @@ -67,7 +71,8 @@ internal class SnapshotRecordedDataQueueItemTest { // Given testedItem = SnapshotRecordedDataQueueItem( fakeRecordedQueuedItemContext, - mockSystemInformation + mockSystemInformation, + mockTimeProvider ) // Then diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/TouchEventRecordedDataQueueItemTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/TouchEventRecordedDataQueueItemTest.kt index ee5044187c..88810d5921 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/TouchEventRecordedDataQueueItemTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/async/TouchEventRecordedDataQueueItemTest.kt @@ -6,6 +6,7 @@ package com.datadog.android.sessionreplay.internal.async +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.sessionreplay.forge.ForgeConfigurator import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration @@ -15,6 +16,7 @@ import org.junit.jupiter.api.BeforeEach 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 @@ -30,6 +32,9 @@ internal class TouchEventRecordedDataQueueItemTest { @Forgery lateinit var fakeTouchEventRecordedDataQueueItem: TouchEventRecordedDataQueueItem + @Mock + lateinit var mockTimeProvider: TimeProvider + lateinit var testedItem: TouchEventRecordedDataQueueItem @BeforeEach @@ -42,6 +47,7 @@ internal class TouchEventRecordedDataQueueItemTest { // Given testedItem = TouchEventRecordedDataQueueItem( recordedQueuedItemContext = fakeTouchEventRecordedDataQueueItem.recordedQueuedItemContext, + timeProvider = mockTimeProvider, touchData = emptyList() ) diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/processor/RecordedDataProcessorTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/processor/RecordedDataProcessorTest.kt index 6adf01fe30..a82b506a3c 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/processor/RecordedDataProcessorTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/processor/RecordedDataProcessorTest.kt @@ -155,11 +155,12 @@ internal class RecordedDataProcessorTest { .thenReturn(forge.getForgery()) whenever(mockTimeProvider.getDeviceTimestamp()).thenReturn(fakeTimestamp) testedProcessor = RecordedDataProcessor( + resourceDataStoreManager = mockResourceDataStoreManager, resourcesWriter = mockResourcesWriter, writer = mockWriter, mutationResolver = mockMutationResolver, - nodeFlattener = mockNodeFlattener, - resourceDataStoreManager = mockResourceDataStoreManager + timeProvider = mockTimeProvider, + nodeFlattener = mockNodeFlattener ) } @@ -746,6 +747,7 @@ internal class RecordedDataProcessorTest { val item = TouchEventRecordedDataQueueItem( recordedQueuedItemContext = rumContextData, + timeProvider = mockTimeProvider, touchData = fakeTouchRecords ) @@ -1315,8 +1317,9 @@ internal class RecordedDataProcessorTest { usedContext: RecordedQueuedItemContext = currentRecordedQueuedItemContext ): ResourceRecordedDataQueueItem = ResourceRecordedDataQueueItem( recordedQueuedItemContext = usedContext, - resourceData = resourceData, - identifier = fakeIdentifier + identifier = fakeIdentifier, + timeProvider = mockTimeProvider, + resourceData = resourceData ) private fun createSnapshotItem( @@ -1324,8 +1327,9 @@ internal class RecordedDataProcessorTest { systemInformation: SystemInformation = fakeSystemInformation, usedContext: RecordedQueuedItemContext = currentRecordedQueuedItemContext ): SnapshotRecordedDataQueueItem = SnapshotRecordedDataQueueItem( - usedContext, - systemInformation = systemInformation + recordedQueuedItemContext = usedContext, + systemInformation = systemInformation, + timeProvider = mockTimeProvider ).apply { this.nodes = snapshot } @@ -1335,7 +1339,8 @@ internal class RecordedDataProcessorTest { usedContext: RecordedQueuedItemContext = currentRecordedQueuedItemContext ): TouchEventRecordedDataQueueItem = TouchEventRecordedDataQueueItem( - usedContext, + recordedQueuedItemContext = usedContext, + timeProvider = mockTimeProvider, touchData = touchEvent ) diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/DebouncerTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/DebouncerTest.kt index a78f612670..a91da2b698 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/DebouncerTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/DebouncerTest.kt @@ -10,6 +10,7 @@ import android.os.Handler import com.datadog.android.api.feature.Feature.Companion.RUM_FEATURE_NAME import com.datadog.android.api.feature.FeatureScope import com.datadog.android.api.feature.FeatureSdkCore +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.sessionreplay.forge.ForgeConfigurator import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.BoolForgery @@ -51,6 +52,9 @@ internal class DebouncerTest { @Mock lateinit var mockRumFeature: FeatureScope + @Mock + lateinit var mockTimeProvider: TimeProvider + @BoolForgery var fakeDynamicOptimizationEnabled: Boolean = false @@ -65,7 +69,8 @@ internal class DebouncerTest { TEST_MAX_DELAY_THRESHOLD_IN_NS, sdkCore = mockSdkCore, timeBank = mockTimeBank, - dynamicOptimizationEnabled = fakeDynamicOptimizationEnabled + dynamicOptimizationEnabled = fakeDynamicOptimizationEnabled, + timeProvider = mockTimeProvider ) } @@ -78,7 +83,8 @@ internal class DebouncerTest { TEST_MAX_DELAY_THRESHOLD_IN_NS, sdkCore = mockSdkCore, dynamicOptimizationEnabled = false, - timeBank = mockTimeBank + timeBank = mockTimeBank, + timeProvider = mockTimeProvider ) val fakeRunnable = TestRunnable() val fakeSecondRunnable = TestRunnable() @@ -99,7 +105,8 @@ internal class DebouncerTest { TEST_MAX_DELAY_THRESHOLD_IN_NS, sdkCore = mockSdkCore, timeBank = mockTimeBank, - dynamicOptimizationEnabled = true + dynamicOptimizationEnabled = true, + timeProvider = mockTimeProvider ) whenever(mockTimeBank.updateAndCheck(any())).thenReturn(false) val fakeRunnable = TestRunnable() diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/listener/WindowsOnDrawListenerTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/listener/WindowsOnDrawListenerTest.kt index cb9b30eefe..8ab83ad795 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/listener/WindowsOnDrawListenerTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/listener/WindowsOnDrawListenerTest.kt @@ -15,6 +15,7 @@ import com.datadog.android.api.InternalLogger import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.core.metrics.PerformanceMetric import com.datadog.android.core.metrics.TelemetryMetricType +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.sessionreplay.ImagePrivacy import com.datadog.android.sessionreplay.TextAndInputPrivacy import com.datadog.android.sessionreplay.forge.ForgeConfigurator @@ -91,6 +92,9 @@ internal class WindowsOnDrawListenerTest { @Mock lateinit var mockPerformanceMetric: PerformanceMetric + @Mock + lateinit var mockTimeProvider: TimeProvider + @IntForgery(min = 0) var fakeDecorWidth: Int = 0 @@ -178,6 +182,7 @@ internal class WindowsOnDrawListenerTest { sdkCore = mockSdkCore, methodCallSamplingRate = fakeMethodCallSamplingRate, dynamicOptimizationEnabled = fakeDynamicOptimizationEnabled, + timeProvider = mockTimeProvider, touchPrivacyManager = mockTouchPrivacyManager ) } @@ -232,6 +237,7 @@ internal class WindowsOnDrawListenerTest { sdkCore = mockSdkCore, methodCallSamplingRate = fakeMethodCallSamplingRate, dynamicOptimizationEnabled = fakeDynamicOptimizationEnabled, + timeProvider = mockTimeProvider, touchPrivacyManager = mockTouchPrivacyManager ) testedListener.onDraw() From 01a9fa611deea24e42593db2f173423da79d6881 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Thu, 20 Nov 2025 14:27:30 +0000 Subject: [PATCH 13/29] RUM-10363: Fix test --- .../data/upload/RotatingDnsResolverTest.kt | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/data/upload/RotatingDnsResolverTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/data/upload/RotatingDnsResolverTest.kt index ebc7330e6a..0b9d2783be 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/data/upload/RotatingDnsResolverTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/data/upload/RotatingDnsResolverTest.kt @@ -9,6 +9,7 @@ package com.datadog.android.core.internal.data.upload import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import fr.xgouchet.elmyr.Forge +import fr.xgouchet.elmyr.annotation.LongForgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension @@ -48,12 +49,17 @@ internal class RotatingDnsResolverTest { @StringForgery lateinit var fakeHostname: String + @LongForgery(min = 0L) + var fakeInitialTimeNs: Long = 0L + lateinit var fakeInetAddresses: List @BeforeEach fun `set up`(forge: Forge) { fakeInetAddresses = forge.aList { mock() } + whenever(mockTimeProvider.getDeviceElapsedTimeNs()) doReturn fakeInitialTimeNs + testedDns = RotatingDnsResolver(mockDelegate, TEST_TTL_MS, mockTimeProvider) } @@ -94,7 +100,8 @@ internal class RotatingDnsResolverTest { // When val result = testedDns.lookup(fakeHostname) - Thread.sleep(TEST_TTL_MS.inWholeMilliseconds) + val fakeExpiredTime = fakeInitialTimeNs + TEST_TTL_MS.inWholeNanoseconds + whenever(mockTimeProvider.getDeviceElapsedTimeNs()) doReturn fakeExpiredTime val result2 = testedDns.lookup(fakeHostname) // Then @@ -123,11 +130,15 @@ internal class RotatingDnsResolverTest { // the real use case where we have a small number of addresses to rotate fakeInetAddresses = forge.aList(size = forge.anInt(min = 1, max = 3)) { mock() } whenever(mockDelegate.lookup(fakeHostname)) doReturn fakeInetAddresses - // just wait the TTL time to make sure all threads are concurrently accessing the lookup - Thread.sleep(TEST_TTL_MS.inWholeMilliseconds) + + // Mock time progression: start at initial time, then advance past TTL to force cache expiration + val fakeExpiredTime = fakeInitialTimeNs + TEST_TTL_MS.inWholeNanoseconds + + forge.aLong(min = 1L, max = 1000000L) + whenever(mockTimeProvider.getDeviceElapsedTimeNs()).doReturn(fakeInitialTimeNs, fakeExpiredTime) + var exceptionThrown: Exception? = null - // When + // When - concurrent access from multiple threads List(100) { Thread { Thread.sleep(forge.aLong(min = 0, max = 100)) From 8fc31c55f62b8d85d4cb0c61799995873271e16d Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Thu, 20 Nov 2025 16:21:59 +0000 Subject: [PATCH 14/29] RUM-10363: Continue fixing tests --- .../data/upload/RotatingDnsResolverTest.kt | 20 +++++----- .../MoveDataMigrationOperationTest.kt | 2 + .../internal/recorder/DebouncerTest.kt | 37 +++++++++++++------ 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/data/upload/RotatingDnsResolverTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/data/upload/RotatingDnsResolverTest.kt index 0b9d2783be..c04d02f21e 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/data/upload/RotatingDnsResolverTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/data/upload/RotatingDnsResolverTest.kt @@ -25,6 +25,8 @@ import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoSettings import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock +import org.mockito.kotlin.times +import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import org.mockito.quality.Strictness import java.net.InetAddress @@ -81,13 +83,15 @@ internal class RotatingDnsResolverTest { whenever(mockDelegate.lookup(fakeHostname)) doReturn fakeInetAddresses val result = mutableListOf() - // When - fakeInetAddresses.forEach { + // When - perform multiple lookups within TTL + fakeInetAddresses.forEach { _ -> result.add(testedDns.lookup(fakeHostname).first()) } - // Then + // Then - should return rotated addresses (each call rotates) assertThat(result).containsExactlyElementsOf(fakeInetAddresses) + // Verify delegate was called only once (subsequent calls use cache) + verify(mockDelegate, times(1)).lookup(fakeHostname) } @Test @@ -107,6 +111,7 @@ internal class RotatingDnsResolverTest { // Then assertThat(result).containsExactlyElementsOf(fakeInetAddresses) assertThat(result2).containsExactlyElementsOf(fakeInetAddresses2) + verify(mockDelegate, times(2)).lookup(fakeHostname) } @Test @@ -129,19 +134,16 @@ internal class RotatingDnsResolverTest { // we need to keep the list of addresses low as it can only be reproduced with low number and it reflects // the real use case where we have a small number of addresses to rotate fakeInetAddresses = forge.aList(size = forge.anInt(min = 1, max = 3)) { mock() } - whenever(mockDelegate.lookup(fakeHostname)) doReturn fakeInetAddresses + whenever(mockDelegate.lookup(fakeHostname)).doReturn(fakeInetAddresses) - // Mock time progression: start at initial time, then advance past TTL to force cache expiration - val fakeExpiredTime = fakeInitialTimeNs + TEST_TTL_MS.inWholeNanoseconds + - forge.aLong(min = 1L, max = 1000000L) + val fakeExpiredTime = fakeInitialTimeNs + TEST_TTL_MS.inWholeNanoseconds whenever(mockTimeProvider.getDeviceElapsedTimeNs()).doReturn(fakeInitialTimeNs, fakeExpiredTime) var exceptionThrown: Exception? = null - // When - concurrent access from multiple threads + // When List(100) { Thread { - Thread.sleep(forge.aLong(min = 0, max = 100)) try { testedDns.lookup(fakeHostname) } catch (e: Exception) { diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperationTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperationTest.kt index 4450bc1f1a..855edc1a52 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperationTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperationTest.kt @@ -64,6 +64,8 @@ internal class MoveDataMigrationOperationTest { mockInternalLogger, mockTimeProvider ) + + whenever(mockTimeProvider.getDeviceElapsedTimeNs()) doReturn 0L } @Test diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/DebouncerTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/DebouncerTest.kt index a91da2b698..0be8ae7b93 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/DebouncerTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/DebouncerTest.kt @@ -14,6 +14,7 @@ import com.datadog.android.internal.time.TimeProvider import com.datadog.android.sessionreplay.forge.ForgeConfigurator import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.BoolForgery +import fr.xgouchet.elmyr.annotation.LongForgery import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension import org.assertj.core.api.Assertions.assertThat @@ -25,6 +26,7 @@ import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoSettings import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn import org.mockito.kotlin.eq import org.mockito.kotlin.times import org.mockito.kotlin.verify @@ -55,6 +57,9 @@ internal class DebouncerTest { @Mock lateinit var mockTimeProvider: TimeProvider + @LongForgery(min = 0L) + var fakeInitialTimeNs: Long = 0L + @BoolForgery var fakeDynamicOptimizationEnabled: Boolean = false @@ -64,6 +69,7 @@ internal class DebouncerTest { fun `set up`() { whenever(mockTimeBank.updateAndCheck(any())).thenReturn(true) whenever(mockSdkCore.getFeature(RUM_FEATURE_NAME)).thenReturn(mockRumFeature) + whenever(mockTimeProvider.getDeviceElapsedTimeNs()) doReturn fakeInitialTimeNs testedDebouncer = Debouncer( mockHandler, TEST_MAX_DELAY_THRESHOLD_IN_NS, @@ -91,9 +97,12 @@ internal class DebouncerTest { // When testedDebouncer.debounce(fakeRunnable) - Thread.sleep(TimeUnit.NANOSECONDS.toMillis(TEST_MAX_DELAY_THRESHOLD_IN_NS)) + + val fakeExpiredTime = fakeInitialTimeNs + TEST_MAX_DELAY_THRESHOLD_IN_NS + whenever(mockTimeProvider.getDeviceElapsedTimeNs()) doReturn fakeExpiredTime testedDebouncer.debounce(fakeSecondRunnable) + // Then assertThat(fakeSecondRunnable.wasExecuted).isTrue() } @@ -114,7 +123,8 @@ internal class DebouncerTest { // When testedDebouncer.debounce(fakeRunnable) - Thread.sleep(TimeUnit.NANOSECONDS.toMillis(TEST_MAX_DELAY_THRESHOLD_IN_NS)) + val fakeExpiredTime = fakeInitialTimeNs + TEST_MAX_DELAY_THRESHOLD_IN_NS + whenever(mockTimeProvider.getDeviceElapsedTimeNs()) doReturn fakeExpiredTime testedDebouncer.debounce(fakeSecondRunnable) // Then @@ -144,7 +154,8 @@ internal class DebouncerTest { val fakeRunnable = TestRunnable() val fakeSecondRunnable = TestRunnable() testedDebouncer.debounce(fakeRunnable) - Thread.sleep(TimeUnit.NANOSECONDS.toMillis(TEST_MAX_DELAY_THRESHOLD_IN_NS)) + val fakeExpiredTime = fakeInitialTimeNs + TEST_MAX_DELAY_THRESHOLD_IN_NS + whenever(mockTimeProvider.getDeviceElapsedTimeNs()) doReturn fakeExpiredTime // When testedDebouncer.debounce(fakeSecondRunnable) @@ -167,18 +178,18 @@ internal class DebouncerTest { val fakeDelayedRunnables = forge.aList(size = forge.anInt(min = 1, max = 10)) { TestRunnable() } - // we remove 1ms just to make sure the threshold is not reached before the next debounce is - // called val delayInterval = (TEST_MAX_DELAY_THRESHOLD_IN_NS / fakeDelayedRunnables.size) - 1 val fakeExecutedRunnable = TestRunnable() + var currentTime = fakeInitialTimeNs + whenever(mockTimeProvider.getDeviceElapsedTimeNs()).thenAnswer { currentTime } + fakeDelayedRunnables.forEach { testedDebouncer.debounce(it) - Thread.sleep(TimeUnit.NANOSECONDS.toMillis(delayInterval)) + currentTime += delayInterval } // When - // wait for the removed 1ms - Thread.sleep(1L * fakeDelayedRunnables.size) + currentTime = fakeInitialTimeNs + TEST_MAX_DELAY_THRESHOLD_IN_NS testedDebouncer.debounce(fakeExecutedRunnable) // Then @@ -205,15 +216,19 @@ internal class DebouncerTest { // called val delayInterval = (TEST_MAX_DELAY_THRESHOLD_IN_NS / fakeDelayedRunnablesPack1.size) - 1 val fakeExecutedRunnable = TestRunnable() + var currentTime = fakeInitialTimeNs + whenever(mockTimeProvider.getDeviceElapsedTimeNs()).thenAnswer { currentTime } + fakeDelayedRunnablesPack1.forEach { testedDebouncer.debounce(it) - Thread.sleep(TimeUnit.NANOSECONDS.toMillis(delayInterval)) + currentTime += delayInterval } - // wait for the removed 1ms - Thread.sleep(1L * fakeDelayedRunnablesPack1.size) + + currentTime = fakeInitialTimeNs + TEST_MAX_DELAY_THRESHOLD_IN_NS testedDebouncer.debounce(fakeExecutedRunnable) // When + currentTime += (1L * fakeDelayedRunnablesPack1.size) fakeDelayedRunnablesPack2.forEach { testedDebouncer.debounce(it) } From f5c25e237558bdda3ebf67e54a861ea4ab39e17e Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Thu, 20 Nov 2025 18:34:06 +0000 Subject: [PATCH 15/29] RUM-10363: Continue fixing tests --- .../MoveDataMigrationOperationTest.kt | 14 ++--- .../WipeDataMigrationOperationTest.kt | 10 ++-- .../file/batch/BatchFileOrchestratorTest.kt | 31 +++++------ .../thread/ThreadPoolExecutorExtTest.kt | 18 +++---- .../time/DefaultAppStartTimeProviderTest.kt | 51 ++++++++++--------- .../flags/internal/FlagsFeatureTest.kt | 5 ++ .../repository/DefaultFlagsRepositoryTest.kt | 5 ++ .../android/rum/utils/forge/Configurator.kt | 2 + ...urceRecordedDataQueueItemForgeryFactory.kt | 3 +- ...shotRecordedDataQueueItemForgeryFactory.kt | 3 +- ...ventRecordedDataQueueItemForgeryFactory.kt | 3 +- .../internal/SessionReplayFeatureTest.kt | 5 ++ .../async/RecordedDataQueueHandlerTest.kt | 18 +++++-- .../resources/ResourceDataStoreManagerTest.kt | 29 +++++++---- .../trace/internal/DatadogSpanLoggerTest.kt | 12 +++++ 15 files changed, 134 insertions(+), 75 deletions(-) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperationTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperationTest.kt index 855edc1a52..24d5bee17b 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperationTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperationTest.kt @@ -8,6 +8,7 @@ package com.datadog.android.core.internal.persistence.file.advanced import com.datadog.android.api.InternalLogger import com.datadog.android.core.internal.persistence.file.FileMover +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import com.datadog.android.utils.verifyLog @@ -52,20 +53,19 @@ internal class MoveDataMigrationOperationTest { @Mock lateinit var mockInternalLogger: InternalLogger - @Mock - lateinit var mockTimeProvider: TimeProvider + lateinit var fakeTimeProvider: TimeProvider @BeforeEach fun `set up`() { + + fakeTimeProvider = DefaultTimeProvider() testedOperation = MoveDataMigrationOperation( fakeFromDirectory, fakeToDirectory, mockFileMover, mockInternalLogger, - mockTimeProvider + fakeTimeProvider ) - - whenever(mockTimeProvider.getDeviceElapsedTimeNs()) doReturn 0L } @Test @@ -76,7 +76,7 @@ internal class MoveDataMigrationOperationTest { fakeToDirectory, mockFileMover, mockInternalLogger, - mockTimeProvider + fakeTimeProvider ) // When @@ -99,7 +99,7 @@ internal class MoveDataMigrationOperationTest { null, mockFileMover, mockInternalLogger, - mockTimeProvider + fakeTimeProvider ) whenever(mockFileMover.delete(fakeFromDirectory)) doReturn true diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/WipeDataMigrationOperationTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/WipeDataMigrationOperationTest.kt index 9118b81ef6..bdfaf66308 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/WipeDataMigrationOperationTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/WipeDataMigrationOperationTest.kt @@ -8,6 +8,7 @@ package com.datadog.android.core.internal.persistence.file.advanced import com.datadog.android.api.InternalLogger import com.datadog.android.core.internal.persistence.file.FileMover +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import com.datadog.android.utils.verifyLog @@ -50,16 +51,17 @@ internal class WipeDataMigrationOperationTest { @Mock lateinit var mockInternalLogger: InternalLogger - @Mock - lateinit var mockTimeProvider: TimeProvider + lateinit var fakeTimeProvider: TimeProvider @BeforeEach fun `set up`() { + + fakeTimeProvider = DefaultTimeProvider() testedOperation = WipeDataMigrationOperation( fakeTargetDirectory, mockFileMover, mockInternalLogger, - mockTimeProvider + fakeTimeProvider ) } @@ -70,7 +72,7 @@ internal class WipeDataMigrationOperationTest { null, mockFileMover, mockInternalLogger, - mockTimeProvider + fakeTimeProvider ) // When diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt index 9c454f983e..0ba5093d79 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt @@ -12,6 +12,7 @@ import com.datadog.android.core.internal.metrics.MetricsDispatcher import com.datadog.android.core.internal.metrics.RemovalReason import com.datadog.android.core.internal.persistence.file.FileOrchestrator import com.datadog.android.core.internal.persistence.file.FilePersistenceConfig +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import com.datadog.android.utils.verifyLog @@ -77,8 +78,7 @@ internal class BatchFileOrchestratorTest { @Mock lateinit var mockPendingFiles: AtomicInteger - @Mock - lateinit var mockTimeProvider: TimeProvider + lateinit var fakeTimeProvider: TimeProvider @BeforeEach fun `set up`() { @@ -86,13 +86,14 @@ internal class BatchFileOrchestratorTest { whenever(mockPendingFiles.incrementAndGet()).thenReturn(fakePendingBatches) fakeRootDir = File(tempDir, fakeRootDirName) fakeRootDir.mkdirs() + fakeTimeProvider = DefaultTimeProvider() testedOrchestrator = BatchFileOrchestrator( rootDir = fakeRootDir, config = TEST_PERSISTENCE_CONFIG, internalLogger = mockLogger, metricsDispatcher = mockMetricsDispatcher, pendingFiles = mockPendingFiles, - timeProvider = mockTimeProvider + timeProvider = fakeTimeProvider ) } @@ -151,7 +152,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - mockTimeProvider + fakeTimeProvider ) // When @@ -179,7 +180,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - mockTimeProvider + fakeTimeProvider ) // When @@ -208,7 +209,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - mockTimeProvider + fakeTimeProvider ) // When @@ -651,7 +652,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - mockTimeProvider + fakeTimeProvider ) // When @@ -678,7 +679,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - mockTimeProvider + fakeTimeProvider ) // When @@ -706,7 +707,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - mockTimeProvider + fakeTimeProvider ) // When @@ -834,7 +835,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - mockTimeProvider + fakeTimeProvider ) // When @@ -861,7 +862,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - mockTimeProvider + fakeTimeProvider ) // When @@ -889,7 +890,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - mockTimeProvider + fakeTimeProvider ) // When @@ -1029,7 +1030,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - mockTimeProvider + fakeTimeProvider ) // When @@ -1056,7 +1057,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - mockTimeProvider + fakeTimeProvider ) // When @@ -1084,7 +1085,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - mockTimeProvider + fakeTimeProvider ) // When diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/ThreadPoolExecutorExtTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/ThreadPoolExecutorExtTest.kt index 38d151cafb..9d5cc51d16 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/ThreadPoolExecutorExtTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/ThreadPoolExecutorExtTest.kt @@ -7,6 +7,7 @@ package com.datadog.android.core.internal.thread import com.datadog.android.api.InternalLogger +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import fr.xgouchet.elmyr.Forge @@ -40,8 +41,7 @@ internal class ThreadPoolExecutorExtTest { @Mock lateinit var mockInternalLogger: InternalLogger - @Mock - lateinit var mockTimeProvider: TimeProvider + var fakeTimeProvider: TimeProvider = DefaultTimeProvider() @Test fun `M return false W waitToIdle { timeout reached }`( @@ -55,7 +55,7 @@ internal class ThreadPoolExecutorExtTest { whenever(testedMockExecutor.completedTaskCount).thenReturn(fakeCompletedCount) // WHEN - val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, mockTimeProvider) + val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, fakeTimeProvider) testedMockExecutor.isIdle() // THEN @@ -75,7 +75,7 @@ internal class ThreadPoolExecutorExtTest { // WHEN val duration = measureTimeMillis { - testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, mockTimeProvider) + testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, fakeTimeProvider) } // THEN @@ -94,7 +94,7 @@ internal class ThreadPoolExecutorExtTest { .thenReturn(fakeTaskCount) // WHEN - val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, mockTimeProvider) + val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, fakeTimeProvider) // THEN assertThat(isIdled).isTrue() @@ -115,7 +115,7 @@ internal class ThreadPoolExecutorExtTest { .thenReturn(fakeTaskCount / 2).thenReturn(fakeTaskCount) // WHEN - val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, mockTimeProvider) + val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, fakeTimeProvider) // THEN assertThat(isIdled).isTrue() @@ -131,7 +131,7 @@ internal class ThreadPoolExecutorExtTest { val fakeCompletedCount = forge.aLong(min = 0, max = fakeTaskCount - 1) whenever(testedMockExecutor.taskCount).thenReturn(fakeTaskCount) whenever(testedMockExecutor.completedTaskCount).thenReturn(fakeCompletedCount) - val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, mockTimeProvider) + val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, fakeTimeProvider) // THEN assertThat(isIdled).isFalse() @@ -148,7 +148,7 @@ internal class ThreadPoolExecutorExtTest { .thenReturn(fakeTaskCount) // WHEN - val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, mockTimeProvider) + val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, fakeTimeProvider) // THEN assertThat(isIdled).isTrue() @@ -171,7 +171,7 @@ internal class ThreadPoolExecutorExtTest { .thenReturn(fakeTaskCount + 2) // WHEN - val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, mockTimeProvider) + val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, fakeTimeProvider) // THEN assertThat(isIdled).isTrue() diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt index 9a974e9bb8..70077e42b8 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/time/DefaultAppStartTimeProviderTest.kt @@ -13,56 +13,61 @@ import com.datadog.android.core.internal.system.BuildSdkVersionProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.DdRumContentProvider import fr.xgouchet.elmyr.annotation.IntForgery +import fr.xgouchet.elmyr.annotation.LongForgery import fr.xgouchet.elmyr.junit5.ForgeExtension import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.data.Offset import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.api.extension.Extensions -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.mock +import org.mockito.Mock +import org.mockito.junit.jupiter.MockitoExtension import org.mockito.kotlin.whenever import java.util.concurrent.TimeUnit @Extensions( + ExtendWith(MockitoExtension::class), ExtendWith(ForgeExtension::class) ) class DefaultAppStartTimeProviderTest { + + @Mock + private lateinit var mockTimeProvider: TimeProvider + + @Mock + private lateinit var mockBuildSdkVersionProvider: BuildSdkVersionProvider + @Test fun `M return process start time W appStartTime { N+ }`( - @IntForgery(min = Build.VERSION_CODES.N) apiVersion: Int + @IntForgery(min = Build.VERSION_CODES.N) apiVersion: Int, + @LongForgery(min = 0L) fakeCurrentTimeNs: Long ) { - // GIVEN - val mockTimeProvider: TimeProvider = mock() - val mockBuildSdkVersionProvider: BuildSdkVersionProvider = mock() - whenever(mockBuildSdkVersionProvider.version) doReturn apiVersion + whenever(mockBuildSdkVersionProvider.version).thenReturn(apiVersion) + whenever(mockTimeProvider.getDeviceElapsedTimeNs()).thenReturn(fakeCurrentTimeNs) val diffMs = SystemClock.elapsedRealtime() - Process.getStartElapsedRealtime() - val startTimeNs = System.nanoTime() - TimeUnit.MILLISECONDS.toNanos(diffMs) + val expectedStartTimeNs = fakeCurrentTimeNs - TimeUnit.MILLISECONDS.toNanos(diffMs) - // WHEN - val timeProvider = DefaultAppStartTimeProvider(mockTimeProvider, mockBuildSdkVersionProvider) - val providedStartTime = timeProvider.appStartTimeNs + val testedAppStartTimeProvider = DefaultAppStartTimeProvider( + mockTimeProvider, + mockBuildSdkVersionProvider + ) + val providedStartTime = testedAppStartTimeProvider.appStartTimeNs - // THEN - assertThat(providedStartTime) - .isCloseTo(startTimeNs, Offset.offset(TimeUnit.MILLISECONDS.toNanos(100))) + assertThat(providedStartTime).isEqualTo(expectedStartTimeNs) } @Test fun `M return content provider load time W appStartTime { Legacy }`( @IntForgery(min = Build.VERSION_CODES.M, max = Build.VERSION_CODES.N) apiVersion: Int ) { - // GIVEN - val mockTimeProvider: TimeProvider = mock() - val mockBuildSdkVersionProvider: BuildSdkVersionProvider = mock() - whenever(mockBuildSdkVersionProvider.version) doReturn apiVersion + whenever(mockBuildSdkVersionProvider.version).thenReturn(apiVersion) val startTimeNs = DdRumContentProvider.createTimeNs - // WHEN - val timeProvider = DefaultAppStartTimeProvider(mockTimeProvider, mockBuildSdkVersionProvider) - val providedStartTime = timeProvider.appStartTimeNs + val testedAppStartTimeProvider = DefaultAppStartTimeProvider( + mockTimeProvider, + mockBuildSdkVersionProvider + ) + val providedStartTime = testedAppStartTimeProvider.appStartTimeNs - // THEN assertThat(providedStartTime).isEqualTo(startTimeNs) } } diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/FlagsFeatureTest.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/FlagsFeatureTest.kt index 74f53391ed..338939f98f 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/FlagsFeatureTest.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/FlagsFeatureTest.kt @@ -15,6 +15,7 @@ import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.flags.FlagsConfiguration import com.datadog.android.flags.internal.storage.ExposureEventRecordWriter import com.datadog.android.flags.internal.storage.NoOpRecordWriter +import com.datadog.android.internal.time.TimeProvider import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeExtension @@ -54,6 +55,9 @@ internal class FlagsFeatureTest { @Mock lateinit var mockContext: Context + @Mock + lateinit var mockTimeProvider: TimeProvider + @StringForgery lateinit var fakeApplicationId: String @@ -62,6 +66,7 @@ internal class FlagsFeatureTest { @BeforeEach fun `set up`() { whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger + whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider whenever(mockSdkCore.createSingleThreadExecutorService(any())) doReturn mockExecutorService // Setup mockContext with default release build (flags = 0) diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/repository/DefaultFlagsRepositoryTest.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/repository/DefaultFlagsRepositoryTest.kt index 1f7ae7ad4e..dbb28f050b 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/repository/DefaultFlagsRepositoryTest.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/repository/DefaultFlagsRepositoryTest.kt @@ -14,6 +14,7 @@ import com.datadog.android.flags.internal.model.FlagsStateEntry import com.datadog.android.flags.internal.model.PrecomputedFlag import com.datadog.android.flags.model.EvaluationContext import com.datadog.android.flags.utils.forge.ForgeConfigurator +import com.datadog.android.internal.time.TimeProvider import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension @@ -50,11 +51,15 @@ internal class DefaultFlagsRepositoryTest { @Mock lateinit var mockInternalLogger: InternalLogger + @Mock + lateinit var mockTimeProvider: TimeProvider + private lateinit var testedRepository: DefaultFlagsRepository @BeforeEach fun `set up`() { whenever(mockFeatureSdkCore.internalLogger) doReturn mockInternalLogger + whenever(mockFeatureSdkCore.timeProvider) doReturn mockTimeProvider whenever( mockDataStore.value( key = any(), diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/utils/forge/Configurator.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/utils/forge/Configurator.kt index 4fa0d4c474..b9b2990842 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/utils/forge/Configurator.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/utils/forge/Configurator.kt @@ -12,6 +12,7 @@ import com.datadog.android.internal.tests.elmyr.InternalTelemetryDebugLogForgery import com.datadog.android.internal.tests.elmyr.InternalTelemetryErrorLogForgeryFactory import com.datadog.android.internal.tests.elmyr.InternalTelemetryEventForgeryFactory import com.datadog.android.internal.tests.elmyr.InternalTelemetryMetricForgeryFactory +import com.datadog.android.internal.tests.elmyr.TimeProviderForgeryFactory import com.datadog.android.internal.tests.elmyr.TracingHeaderTypesSetForgeryFactory import com.datadog.android.rum.tests.elmyr.ResourceIdForgeryFactory import com.datadog.android.rum.tests.elmyr.RumScopeKeyForgeryFactory @@ -65,6 +66,7 @@ internal class Configurator : BaseConfigurator() { forge.addFactory(InternalTelemetryConfigurationForgeryFactory()) forge.addFactory(InternalTelemetryApiUsageForgeryFactory()) forge.addFactory(TracingHeaderTypesSetForgeryFactory()) + forge.addFactory(TimeProviderForgeryFactory()) // RumRawEvent forge.addFactory(ActionSentForgeryFactory()) diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/ResourceRecordedDataQueueItemForgeryFactory.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/ResourceRecordedDataQueueItemForgeryFactory.kt index 04a0de5f60..b658f3623e 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/ResourceRecordedDataQueueItemForgeryFactory.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/ResourceRecordedDataQueueItemForgeryFactory.kt @@ -6,6 +6,7 @@ package com.datadog.android.sessionreplay.forge +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.sessionreplay.internal.async.ResourceRecordedDataQueueItem import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.ForgeryFactory @@ -17,7 +18,7 @@ internal class ResourceRecordedDataQueueItemForgeryFactory : ForgeryFactory().toString(), resourceData = forge.aString().toByteArray(), - timeProvider = forge.getForgery() + timeProvider = DefaultTimeProvider() ) } } diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/SnapshotRecordedDataQueueItemForgeryFactory.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/SnapshotRecordedDataQueueItemForgeryFactory.kt index d76e5aee0b..83f69ed174 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/SnapshotRecordedDataQueueItemForgeryFactory.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/forge/SnapshotRecordedDataQueueItemForgeryFactory.kt @@ -6,6 +6,7 @@ package com.datadog.android.sessionreplay.forge +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.sessionreplay.internal.async.SnapshotRecordedDataQueueItem import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.ForgeryFactory @@ -15,7 +16,7 @@ internal class SnapshotRecordedDataQueueItemForgeryFactory : ForgeryFactory { val resourceHashes = forge.aList { aString() }.distinct() val fakeVersionCode = forge.anInt(min = 0) val entryTime = if (isExpired) { - System.nanoTime() - DATASTORE_EXPIRATION_NS + currentTime - DATASTORE_EXPIRATION_NS - 1 } else { - System.nanoTime() + currentTime } val mockResourceHashesEntry: ResourceHashesEntry = mock { diff --git a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanLoggerTest.kt b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanLoggerTest.kt index bb9bba0d65..8d115d9681 100644 --- a/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanLoggerTest.kt +++ b/features/dd-sdk-android-trace/src/test/kotlin/com/datadog/android/trace/internal/DatadogSpanLoggerTest.kt @@ -9,6 +9,7 @@ import android.util.Log import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.FeatureScope import com.datadog.android.api.feature.FeatureSdkCore +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.internal.utils.loggableStackTrace import com.datadog.android.log.LogAttributes import com.datadog.android.trace.api.DatadogTracingConstants @@ -18,6 +19,7 @@ import com.datadog.android.trace.internal.DatadogSpanLogger.Companion.TRACE_LOGG import com.datadog.android.utils.forge.Configurator 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 @@ -33,6 +35,7 @@ import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever import org.mockito.quality.Strictness @Extensions( @@ -57,9 +60,15 @@ class DatadogSpanLoggerTest { @Forgery lateinit var fakeSpan: DatadogSpan + @LongForgery(min = 0L) + var fakeTimestamp: Long = 0L + @Mock lateinit var mockLogFeatureScope: FeatureScope + @Mock + lateinit var mockTimeProvider: TimeProvider + private lateinit var testedLogger: DatadogSpanLogger @BeforeEach @@ -68,6 +77,9 @@ class DatadogSpanLoggerTest { on { getFeature(Feature.LOGS_FEATURE_NAME) } doReturn mockLogFeatureScope } + whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider + whenever(mockTimeProvider.getDeviceTimestamp()) doReturn fakeTimestamp + testedLogger = DatadogSpanLogger(mockSdkCore) } From 4ceba2d49bf56ae850296f974abaa126c6ab5bb3 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Thu, 20 Nov 2025 19:50:50 +0000 Subject: [PATCH 16/29] RUM-10363: Continue fixing tests --- .../MoveDataMigrationOperationTest.kt | 1 - .../WipeDataMigrationOperationTest.kt | 1 - .../core/internal/utils/MiscUtilsTest.kt | 16 ++++----- .../internal/DatadogExceptionHandlerTest.kt | 5 ++- .../internal/ExposureEventsProcessorTest.kt | 19 +++++++---- .../android/rum/utils/forge/Configurator.kt | 2 -- .../callback/RecorderWindowCallbackTest.kt | 33 +++++++++++++++---- 7 files changed, 48 insertions(+), 29 deletions(-) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperationTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperationTest.kt index 24d5bee17b..f3a36a3fe8 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperationTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperationTest.kt @@ -57,7 +57,6 @@ internal class MoveDataMigrationOperationTest { @BeforeEach fun `set up`() { - fakeTimeProvider = DefaultTimeProvider() testedOperation = MoveDataMigrationOperation( fakeFromDirectory, diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/WipeDataMigrationOperationTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/WipeDataMigrationOperationTest.kt index bdfaf66308..be9f497716 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/WipeDataMigrationOperationTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/WipeDataMigrationOperationTest.kt @@ -55,7 +55,6 @@ internal class WipeDataMigrationOperationTest { @BeforeEach fun `set up`() { - fakeTimeProvider = DefaultTimeProvider() testedOperation = WipeDataMigrationOperation( fakeTargetDirectory, diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/MiscUtilsTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/MiscUtilsTest.kt index 3a198eb3ba..16b5cfb5f9 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/MiscUtilsTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/MiscUtilsTest.kt @@ -9,6 +9,7 @@ package com.datadog.android.core.internal.utils import com.datadog.android.api.InternalLogger import com.datadog.android.core.internal.utils.JsonSerializer.ITEM_SERIALIZATION_ERROR import com.datadog.android.core.internal.utils.JsonSerializer.safeMapValuesToJson +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.internal.utils.NULL_MAP_VALUE import com.datadog.android.utils.forge.Configurator @@ -62,9 +63,8 @@ internal class MiscUtilsTest { @Mock lateinit var mockInternalLogger: InternalLogger - - @Mock - lateinit var mockTimeProvider: TimeProvider + + var fakeTimeProvider: TimeProvider = DefaultTimeProvider() @Test fun `M repeat max N times W retryWithDelay { success = false }`(forge: Forge) { @@ -75,7 +75,7 @@ internal class MiscUtilsTest { whenever(mockedBlock.invoke()).thenReturn(false) // When - val wasSuccessful = retryWithDelay(mockedBlock, fakeTimes, fakeDelay, mockInternalLogger, mockTimeProvider) + val wasSuccessful = retryWithDelay(mockedBlock, fakeTimes, fakeDelay, mockInternalLogger, fakeTimeProvider) // Then assertThat(wasSuccessful).isFalse() @@ -92,7 +92,7 @@ internal class MiscUtilsTest { // When val executionTime = - measureNanoTime { retryWithDelay(mockedBlock, fakeTimes, fakeDelay, mockInternalLogger, mockTimeProvider) } + measureNanoTime { retryWithDelay(mockedBlock, fakeTimes, fakeDelay, mockInternalLogger, fakeTimeProvider) } // Then assertThat(executionTime).isCloseTo( @@ -108,7 +108,7 @@ internal class MiscUtilsTest { val mockedBlock: () -> Boolean = mock() // When - retryWithDelay(mockedBlock, forge.anInt(Int.MIN_VALUE, 1), fakeDelay, mockInternalLogger, mockTimeProvider) + retryWithDelay(mockedBlock, forge.anInt(Int.MIN_VALUE, 1), fakeDelay, mockInternalLogger, fakeTimeProvider) // Then verifyNoInteractions(mockedBlock) @@ -122,7 +122,7 @@ internal class MiscUtilsTest { whenever(mockedBlock.invoke()).thenReturn(false).thenReturn(true) // When - val wasSuccessful = retryWithDelay(mockedBlock, 3, fakeDelay, mockInternalLogger, mockTimeProvider) + val wasSuccessful = retryWithDelay(mockedBlock, 3, fakeDelay, mockInternalLogger, fakeTimeProvider) // Then assertThat(wasSuccessful).isTrue() @@ -140,7 +140,7 @@ internal class MiscUtilsTest { whenever(mockedBlock.invoke()).thenThrow(exception).thenReturn(true) // When - val wasSuccessful = retryWithDelay(mockedBlock, 3, fakeDelay, mockInternalLogger, mockTimeProvider) + val wasSuccessful = retryWithDelay(mockedBlock, 3, fakeDelay, mockInternalLogger, fakeTimeProvider) // Then assertThat(wasSuccessful).isTrue() diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/error/internal/DatadogExceptionHandlerTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/error/internal/DatadogExceptionHandlerTest.kt index fc6d1f0bb1..dbf96af272 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/error/internal/DatadogExceptionHandlerTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/error/internal/DatadogExceptionHandlerTest.kt @@ -21,6 +21,7 @@ import com.datadog.android.core.internal.CoreFeature import com.datadog.android.core.internal.thread.waitToIdle import com.datadog.android.core.internal.utils.TAG_DATADOG_UPLOAD import com.datadog.android.core.internal.utils.UPLOAD_WORKER_NAME +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.internal.utils.loggableStackTrace import com.datadog.android.utils.config.ApplicationContextTestConfiguration @@ -82,8 +83,7 @@ internal class DatadogExceptionHandlerTest { @Mock lateinit var mockInternalLogger: InternalLogger - @Mock - lateinit var mockTimeProvider: TimeProvider + var mockTimeProvider: TimeProvider = DefaultTimeProvider() @Mock lateinit var mockLogsFeatureScope: FeatureScope @@ -107,7 +107,6 @@ internal class DatadogExceptionHandlerTest { whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger whenever(mockSdkCore.name) doReturn fakeInstanceName whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider - whenever(mockTimeProvider.getDeviceElapsedTimeNs()) doReturn 0L CoreFeature.disableKronosBackgroundSync = true diff --git a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessorTest.kt b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessorTest.kt index c52c4546b3..5eb1f61869 100644 --- a/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessorTest.kt +++ b/features/dd-sdk-android-flags/src/test/kotlin/com/datadog/android/flags/internal/ExposureEventsProcessorTest.kt @@ -27,8 +27,10 @@ import org.mockito.kotlin.any import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.atLeast import org.mockito.kotlin.atMost +import org.mockito.kotlin.doReturn import org.mockito.kotlin.times import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @ExtendWith(MockitoExtension::class, ForgeExtension::class) @ForgeConfiguration(ForgeConfigurator::class) @@ -77,8 +79,10 @@ internal class ExposureEventsProcessorTest { "age" to forge.anInt(min = 18, max = 99).toString() ) ) + val fakeTimestamp = forge.aLong(min = 1) // When + whenever(mockTimeProvider.getDeviceTimestamp()) doReturn fakeTimestamp testedProcessor.processEvent(fakeFlagName, fakeContext, fakeFlag) // Then @@ -92,7 +96,7 @@ internal class ExposureEventsProcessorTest { assertThat(capturedEvent.subject.id).isEqualTo(fakeTargetingKey) assertThat(capturedEvent.subject.attributes.additionalProperties).containsKeys("user_id", "plan", "age") assertThat(capturedEvent.subject.attributes.additionalProperties).hasSize(3) - assertThat(capturedEvent.timestamp).isGreaterThan(0) + assertThat(capturedEvent.timestamp).isEqualTo(fakeTimestamp) } @Test @@ -231,22 +235,23 @@ internal class ExposureEventsProcessorTest { attributes = mapOf("user_id" to forge.anAlphabeticalString()) ) - val beforeTime = System.currentTimeMillis() + val fakeTimestamp1 = forge.aLong(min = 1000000L) + val fakeTimestamp2 = forge.aLong(min = 1000000L) + whenever(mockTimeProvider.getDeviceTimestamp()) + .thenReturn(fakeTimestamp1) + .thenReturn(fakeTimestamp2) // When testedProcessor.processEvent(fakeFlagName, fakeContext1, fakeFlag) testedProcessor.processEvent(fakeFlagName, fakeContext2, fakeFlag) - val afterTime = System.currentTimeMillis() - // Then val eventCaptor = argumentCaptor() verify(mockRecordWriter, times(2)).write(eventCaptor.capture()) val capturedEvents = eventCaptor.allValues - capturedEvents.forEach { event -> - assertThat(event.timestamp).isBetween(beforeTime, afterTime) - } + assertThat(capturedEvents[0].timestamp).isEqualTo(fakeTimestamp1) + assertThat(capturedEvents[1].timestamp).isEqualTo(fakeTimestamp2) } // endregion diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/utils/forge/Configurator.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/utils/forge/Configurator.kt index b9b2990842..4fa0d4c474 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/utils/forge/Configurator.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/utils/forge/Configurator.kt @@ -12,7 +12,6 @@ import com.datadog.android.internal.tests.elmyr.InternalTelemetryDebugLogForgery import com.datadog.android.internal.tests.elmyr.InternalTelemetryErrorLogForgeryFactory import com.datadog.android.internal.tests.elmyr.InternalTelemetryEventForgeryFactory import com.datadog.android.internal.tests.elmyr.InternalTelemetryMetricForgeryFactory -import com.datadog.android.internal.tests.elmyr.TimeProviderForgeryFactory import com.datadog.android.internal.tests.elmyr.TracingHeaderTypesSetForgeryFactory import com.datadog.android.rum.tests.elmyr.ResourceIdForgeryFactory import com.datadog.android.rum.tests.elmyr.RumScopeKeyForgeryFactory @@ -66,7 +65,6 @@ internal class Configurator : BaseConfigurator() { forge.addFactory(InternalTelemetryConfigurationForgeryFactory()) forge.addFactory(InternalTelemetryApiUsageForgeryFactory()) forge.addFactory(TracingHeaderTypesSetForgeryFactory()) - forge.addFactory(TimeProviderForgeryFactory()) // RumRawEvent forge.addFactory(ActionSentForgeryFactory()) diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/callback/RecorderWindowCallbackTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/callback/RecorderWindowCallbackTest.kt index 88665c524a..9dbb2da0fd 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/callback/RecorderWindowCallbackTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/recorder/callback/RecorderWindowCallbackTest.kt @@ -115,8 +115,11 @@ internal class RecorderWindowCallbackTest { @Forgery lateinit var fakeRumContext: SessionReplayRumContext + private var currentElapsedTimeNs: Long = 0L + @BeforeEach fun `set up`() { + currentElapsedTimeNs = 0L val mockResources = mock { val displayMetrics = DisplayMetrics().apply { density = fakeDensity.toFloat() } whenever(it.displayMetrics).thenReturn(displayMetrics) @@ -125,6 +128,9 @@ internal class RecorderWindowCallbackTest { .thenReturn(fakeTouchEventRecordedDataQueueItem) whenever(mockContext.resources).thenReturn(mockResources) whenever(mockTimeProvider.getDeviceTimestamp()).thenReturn(fakeTimestamp) + whenever(mockTimeProvider.getDeviceElapsedTimeNs()).thenAnswer { + currentElapsedTimeNs + } whenever(mockRumContextProvider.getRumContext()).thenReturn(fakeRumContext) whenever(mockTouchPrivacyManager.shouldRecordTouch(any())) .thenReturn(true) @@ -280,11 +286,14 @@ internal class RecorderWindowCallbackTest { fakeEvent5Records // When + currentElapsedTimeNs = 0L testedWindowCallback.dispatchTouchEvent(relatedMotionEvent1) + currentElapsedTimeNs += TEST_MOTION_UPDATE_DELAY_THRESHOLD_NS testedWindowCallback.dispatchTouchEvent(relatedMotionEvent2) // must skip 3 as the motion update delay was not reached + currentElapsedTimeNs += TEST_MOTION_UPDATE_DELAY_THRESHOLD_NS / 2 testedWindowCallback.dispatchTouchEvent(relatedMotionEvent3) - Thread.sleep(TimeUnit.NANOSECONDS.toMillis(TEST_MOTION_UPDATE_DELAY_THRESHOLD_NS)) + currentElapsedTimeNs += TEST_MOTION_UPDATE_DELAY_THRESHOLD_NS testedWindowCallback.dispatchTouchEvent(relatedMotionEvent4) testedWindowCallback.dispatchTouchEvent(relatedMotionEvent5) @@ -313,18 +322,19 @@ internal class RecorderWindowCallbackTest { val expectedRecords2 = fakeMoveRecordsAfterFlush.flatten() + fakeUpEventRecords // When + currentElapsedTimeNs = 0L testedWindowCallback.dispatchTouchEvent(fakeDownEvent) fakeMoveEventsBeforeFlush.forEachIndexed { index, event -> - if (index == fakeMoveEventsBeforeFlush.size - 1) { - Thread.sleep(TimeUnit.NANOSECONDS.toMillis(TEST_FLUSH_BUFFER_THRESHOLD_NS)) + currentElapsedTimeNs += if (index == fakeMoveEventsBeforeFlush.size - 1) { + TEST_FLUSH_BUFFER_THRESHOLD_NS } else { - Thread.sleep(TimeUnit.NANOSECONDS.toMillis(TEST_MOTION_UPDATE_DELAY_THRESHOLD_NS)) + TEST_MOTION_UPDATE_DELAY_THRESHOLD_NS } testedWindowCallback.dispatchTouchEvent(event) } fakeMoveEventsAfterFlush.forEach { - Thread.sleep(TimeUnit.NANOSECONDS.toMillis(TEST_MOTION_UPDATE_DELAY_THRESHOLD_NS)) + currentElapsedTimeNs += TEST_MOTION_UPDATE_DELAY_THRESHOLD_NS testedWindowCallback.dispatchTouchEvent(it) } testedWindowCallback.dispatchTouchEvent(fakeUpEvent) @@ -363,10 +373,14 @@ internal class RecorderWindowCallbackTest { fakeGesture2UpRecords // When + currentElapsedTimeNs = 0L testedWindowCallback.dispatchTouchEvent(fakeGesture1DownEvent) + currentElapsedTimeNs += TEST_MOTION_UPDATE_DELAY_THRESHOLD_NS testedWindowCallback.dispatchTouchEvent(fakeGesture1MoveEvent) testedWindowCallback.dispatchTouchEvent(fakeGesture1UpEvent) + currentElapsedTimeNs += TEST_MOTION_UPDATE_DELAY_THRESHOLD_NS testedWindowCallback.dispatchTouchEvent(fakeGesture2DownEvent) + currentElapsedTimeNs += TEST_MOTION_UPDATE_DELAY_THRESHOLD_NS testedWindowCallback.dispatchTouchEvent(fakeGesture2MoveEvent) testedWindowCallback.dispatchTouchEvent(fakeGesture2UpEvent) @@ -392,10 +406,11 @@ internal class RecorderWindowCallbackTest { val expectedTouchRecords1 = fakeDownRecords + fakeEvent1MoveRecords // When + currentElapsedTimeNs = 0L testedWindowCallback.dispatchTouchEvent(fakeDownEvent) - Thread.sleep(TimeUnit.NANOSECONDS.toMillis(TEST_FLUSH_BUFFER_THRESHOLD_NS)) + currentElapsedTimeNs += TEST_FLUSH_BUFFER_THRESHOLD_NS testedWindowCallback.dispatchTouchEvent(fakeMotion1Event) - Thread.sleep(TimeUnit.NANOSECONDS.toMillis(TEST_FLUSH_BUFFER_THRESHOLD_NS)) + currentElapsedTimeNs += TEST_FLUSH_BUFFER_THRESHOLD_NS testedWindowCallback.dispatchTouchEvent(fakeMotion2Event) // Then @@ -417,7 +432,9 @@ internal class RecorderWindowCallbackTest { val relatedMotionEvent3 = fakeEvent3Records.asMotionEvent() // When + currentElapsedTimeNs = 0L testedWindowCallback.dispatchTouchEvent(relatedMotionEvent1) + currentElapsedTimeNs += TEST_MOTION_UPDATE_DELAY_THRESHOLD_NS testedWindowCallback.dispatchTouchEvent(relatedMotionEvent2) testedWindowCallback.dispatchTouchEvent(relatedMotionEvent3) @@ -497,7 +514,9 @@ internal class RecorderWindowCallbackTest { ) // When + currentElapsedTimeNs = 0L testedWindowCallback.dispatchTouchEvent(relatedMotionEvent1) + currentElapsedTimeNs += TEST_MOTION_UPDATE_DELAY_THRESHOLD_NS testedWindowCallback.dispatchTouchEvent(relatedMotionEvent2) testedWindowCallback.dispatchTouchEvent(relatedMotionEvent3) From 3b37f8a90bc714b2537442ea391af0e656429573 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Thu, 20 Nov 2025 19:55:05 +0000 Subject: [PATCH 17/29] RUM-10363: Fix ktlint --- .../com/datadog/android/core/internal/utils/MiscUtilsTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/MiscUtilsTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/MiscUtilsTest.kt index 16b5cfb5f9..425bd67cd3 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/MiscUtilsTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/MiscUtilsTest.kt @@ -63,7 +63,7 @@ internal class MiscUtilsTest { @Mock lateinit var mockInternalLogger: InternalLogger - + var fakeTimeProvider: TimeProvider = DefaultTimeProvider() @Test From 38e08a8b1740d22ba689d6324b640ca5c39ed608 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Thu, 20 Nov 2025 22:32:00 +0000 Subject: [PATCH 18/29] RUM-10363: Continue fixing tests --- .../android/rum/internal/DatadogLateCrashReporterTest.kt | 1 + .../com/datadog/android/rum/internal/RumFeatureTest.kt | 5 +++++ .../instrumentation/MainLooperLongTaskStrategyTest.kt | 6 +++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporterTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporterTest.kt index 938a0e8653..3ccd590226 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporterTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/DatadogLateCrashReporterTest.kt @@ -99,6 +99,7 @@ internal class DatadogLateCrashReporterTest { @BeforeEach fun `set up`() { whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger + whenever(mockSdkCore.timeProvider) doReturn mock() whenever(mockSdkCore.getFeature(Feature.RUM_FEATURE_NAME)) doReturn mockRumFeatureScope whenever(mockEventWriteScope.invoke(any())) doAnswer { diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt index 78f5fdf4bc..44ba3404a6 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt @@ -25,6 +25,7 @@ import com.datadog.android.event.EventMapper import com.datadog.android.event.MapperSerializer import com.datadog.android.internal.flags.RumFlagEvaluationMessage import com.datadog.android.internal.telemetry.InternalTelemetryEvent +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.GlobalRumMonitor import com.datadog.android.rum.RumErrorSource import com.datadog.android.rum.assertj.RumFeatureAssert @@ -147,9 +148,13 @@ internal class RumFeatureTest { @Mock lateinit var mockScheduledExecutorService: ScheduledExecutorService + @Mock + lateinit var mockTimeProvider: TimeProvider + @BeforeEach fun `set up`() { whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger + whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider whenever(mockSdkCore.createScheduledExecutorService(any())) doReturn mockScheduledExecutorService val mockContentResolver = mock() diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/instrumentation/MainLooperLongTaskStrategyTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/instrumentation/MainLooperLongTaskStrategyTest.kt index 549c63465a..83fd73b777 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/instrumentation/MainLooperLongTaskStrategyTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/instrumentation/MainLooperLongTaskStrategyTest.kt @@ -38,6 +38,7 @@ import org.mockito.kotlin.isA import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions +import org.mockito.kotlin.whenever import org.mockito.quality.Strictness import java.util.concurrent.TimeUnit @@ -58,6 +59,9 @@ internal class MainLooperLongTaskStrategyTest : ObjectTest>>>> Dispatching to $target $callback: $what") - Thread.sleep(duration / 4) testedPrinter.println("<<<<< Finished to $target $callback") // Then From 5fa41d5b7293c7dbde66e147adc771db18c0f24b Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Fri, 21 Nov 2025 11:59:46 +0000 Subject: [PATCH 19/29] RUM-10363: Tests pass --- .../MoveDataMigrationOperationTest.kt | 10 +++---- .../WipeDataMigrationOperationTest.kt | 8 ++--- .../file/batch/BatchFileOrchestratorTest.kt | 30 +++++++++---------- .../thread/ThreadPoolExecutorExtTest.kt | 16 +++++----- .../core/internal/utils/MiscUtilsTest.kt | 12 ++++---- .../domain/scope/RumApplicationScope.kt | 3 +- .../rum/internal/monitor/DatadogRumMonitor.kt | 10 +++---- .../kotlin/com/datadog/android/rum/RumTest.kt | 2 ++ .../android/rum/internal/RumFeatureTest.kt | 10 ++++--- .../DefaultAccessibilityReaderTest.kt | 12 ++++---- .../domain/scope/RumSessionScopeTest.kt | 13 ++++---- .../MainLooperLongTaskStrategyTest.kt | 19 ++++++------ .../processor/RecordedDataProcessorTest.kt | 18 +++++------ .../android/webview/WebViewTrackingTest.kt | 5 ++++ .../domain/WebViewNativeRumViewsCacheTest.kt | 9 ++++-- 15 files changed, 95 insertions(+), 82 deletions(-) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperationTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperationTest.kt index f3a36a3fe8..ce0a61e990 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperationTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/MoveDataMigrationOperationTest.kt @@ -53,17 +53,17 @@ internal class MoveDataMigrationOperationTest { @Mock lateinit var mockInternalLogger: InternalLogger - lateinit var fakeTimeProvider: TimeProvider + lateinit var realTimeProvider: TimeProvider @BeforeEach fun `set up`() { - fakeTimeProvider = DefaultTimeProvider() + realTimeProvider = DefaultTimeProvider() testedOperation = MoveDataMigrationOperation( fakeFromDirectory, fakeToDirectory, mockFileMover, mockInternalLogger, - fakeTimeProvider + realTimeProvider ) } @@ -75,7 +75,7 @@ internal class MoveDataMigrationOperationTest { fakeToDirectory, mockFileMover, mockInternalLogger, - fakeTimeProvider + realTimeProvider ) // When @@ -98,7 +98,7 @@ internal class MoveDataMigrationOperationTest { null, mockFileMover, mockInternalLogger, - fakeTimeProvider + realTimeProvider ) whenever(mockFileMover.delete(fakeFromDirectory)) doReturn true diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/WipeDataMigrationOperationTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/WipeDataMigrationOperationTest.kt index be9f497716..e897822343 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/WipeDataMigrationOperationTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/advanced/WipeDataMigrationOperationTest.kt @@ -51,16 +51,16 @@ internal class WipeDataMigrationOperationTest { @Mock lateinit var mockInternalLogger: InternalLogger - lateinit var fakeTimeProvider: TimeProvider + lateinit var realTimeProvider: TimeProvider @BeforeEach fun `set up`() { - fakeTimeProvider = DefaultTimeProvider() + realTimeProvider = DefaultTimeProvider() testedOperation = WipeDataMigrationOperation( fakeTargetDirectory, mockFileMover, mockInternalLogger, - fakeTimeProvider + realTimeProvider ) } @@ -71,7 +71,7 @@ internal class WipeDataMigrationOperationTest { null, mockFileMover, mockInternalLogger, - fakeTimeProvider + realTimeProvider ) // When diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt index 0ba5093d79..eb44e38de1 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/persistence/file/batch/BatchFileOrchestratorTest.kt @@ -78,7 +78,7 @@ internal class BatchFileOrchestratorTest { @Mock lateinit var mockPendingFiles: AtomicInteger - lateinit var fakeTimeProvider: TimeProvider + lateinit var realTimeProvider: TimeProvider @BeforeEach fun `set up`() { @@ -86,14 +86,14 @@ internal class BatchFileOrchestratorTest { whenever(mockPendingFiles.incrementAndGet()).thenReturn(fakePendingBatches) fakeRootDir = File(tempDir, fakeRootDirName) fakeRootDir.mkdirs() - fakeTimeProvider = DefaultTimeProvider() + realTimeProvider = DefaultTimeProvider() testedOrchestrator = BatchFileOrchestrator( rootDir = fakeRootDir, config = TEST_PERSISTENCE_CONFIG, internalLogger = mockLogger, metricsDispatcher = mockMetricsDispatcher, pendingFiles = mockPendingFiles, - timeProvider = fakeTimeProvider + timeProvider = realTimeProvider ) } @@ -152,7 +152,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + realTimeProvider ) // When @@ -180,7 +180,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + realTimeProvider ) // When @@ -209,7 +209,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + realTimeProvider ) // When @@ -652,7 +652,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + realTimeProvider ) // When @@ -679,7 +679,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + realTimeProvider ) // When @@ -707,7 +707,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + realTimeProvider ) // When @@ -835,7 +835,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + realTimeProvider ) // When @@ -862,7 +862,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + realTimeProvider ) // When @@ -890,7 +890,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + realTimeProvider ) // When @@ -1030,7 +1030,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + realTimeProvider ) // When @@ -1057,7 +1057,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + realTimeProvider ) // When @@ -1085,7 +1085,7 @@ internal class BatchFileOrchestratorTest { TEST_PERSISTENCE_CONFIG, mockLogger, mockMetricsDispatcher, - fakeTimeProvider + realTimeProvider ) // When diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/ThreadPoolExecutorExtTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/ThreadPoolExecutorExtTest.kt index 9d5cc51d16..c16f75e77a 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/ThreadPoolExecutorExtTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/thread/ThreadPoolExecutorExtTest.kt @@ -41,7 +41,7 @@ internal class ThreadPoolExecutorExtTest { @Mock lateinit var mockInternalLogger: InternalLogger - var fakeTimeProvider: TimeProvider = DefaultTimeProvider() + var realTimeProvider: TimeProvider = DefaultTimeProvider() @Test fun `M return false W waitToIdle { timeout reached }`( @@ -55,7 +55,7 @@ internal class ThreadPoolExecutorExtTest { whenever(testedMockExecutor.completedTaskCount).thenReturn(fakeCompletedCount) // WHEN - val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, fakeTimeProvider) + val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, realTimeProvider) testedMockExecutor.isIdle() // THEN @@ -75,7 +75,7 @@ internal class ThreadPoolExecutorExtTest { // WHEN val duration = measureTimeMillis { - testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, fakeTimeProvider) + testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, realTimeProvider) } // THEN @@ -94,7 +94,7 @@ internal class ThreadPoolExecutorExtTest { .thenReturn(fakeTaskCount) // WHEN - val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, fakeTimeProvider) + val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, realTimeProvider) // THEN assertThat(isIdled).isTrue() @@ -115,7 +115,7 @@ internal class ThreadPoolExecutorExtTest { .thenReturn(fakeTaskCount / 2).thenReturn(fakeTaskCount) // WHEN - val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, fakeTimeProvider) + val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, realTimeProvider) // THEN assertThat(isIdled).isTrue() @@ -131,7 +131,7 @@ internal class ThreadPoolExecutorExtTest { val fakeCompletedCount = forge.aLong(min = 0, max = fakeTaskCount - 1) whenever(testedMockExecutor.taskCount).thenReturn(fakeTaskCount) whenever(testedMockExecutor.completedTaskCount).thenReturn(fakeCompletedCount) - val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, fakeTimeProvider) + val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, realTimeProvider) // THEN assertThat(isIdled).isFalse() @@ -148,7 +148,7 @@ internal class ThreadPoolExecutorExtTest { .thenReturn(fakeTaskCount) // WHEN - val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, fakeTimeProvider) + val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, realTimeProvider) // THEN assertThat(isIdled).isTrue() @@ -171,7 +171,7 @@ internal class ThreadPoolExecutorExtTest { .thenReturn(fakeTaskCount + 2) // WHEN - val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, fakeTimeProvider) + val isIdled = testedMockExecutor.waitToIdle(fakeTimeout, mockInternalLogger, realTimeProvider) // THEN assertThat(isIdled).isTrue() diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/MiscUtilsTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/MiscUtilsTest.kt index 425bd67cd3..64df4ca4cb 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/MiscUtilsTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/MiscUtilsTest.kt @@ -64,7 +64,7 @@ internal class MiscUtilsTest { @Mock lateinit var mockInternalLogger: InternalLogger - var fakeTimeProvider: TimeProvider = DefaultTimeProvider() + var realTimeProvider: TimeProvider = DefaultTimeProvider() @Test fun `M repeat max N times W retryWithDelay { success = false }`(forge: Forge) { @@ -75,7 +75,7 @@ internal class MiscUtilsTest { whenever(mockedBlock.invoke()).thenReturn(false) // When - val wasSuccessful = retryWithDelay(mockedBlock, fakeTimes, fakeDelay, mockInternalLogger, fakeTimeProvider) + val wasSuccessful = retryWithDelay(mockedBlock, fakeTimes, fakeDelay, mockInternalLogger, realTimeProvider) // Then assertThat(wasSuccessful).isFalse() @@ -92,7 +92,7 @@ internal class MiscUtilsTest { // When val executionTime = - measureNanoTime { retryWithDelay(mockedBlock, fakeTimes, fakeDelay, mockInternalLogger, fakeTimeProvider) } + measureNanoTime { retryWithDelay(mockedBlock, fakeTimes, fakeDelay, mockInternalLogger, realTimeProvider) } // Then assertThat(executionTime).isCloseTo( @@ -108,7 +108,7 @@ internal class MiscUtilsTest { val mockedBlock: () -> Boolean = mock() // When - retryWithDelay(mockedBlock, forge.anInt(Int.MIN_VALUE, 1), fakeDelay, mockInternalLogger, fakeTimeProvider) + retryWithDelay(mockedBlock, forge.anInt(Int.MIN_VALUE, 1), fakeDelay, mockInternalLogger, realTimeProvider) // Then verifyNoInteractions(mockedBlock) @@ -122,7 +122,7 @@ internal class MiscUtilsTest { whenever(mockedBlock.invoke()).thenReturn(false).thenReturn(true) // When - val wasSuccessful = retryWithDelay(mockedBlock, 3, fakeDelay, mockInternalLogger, fakeTimeProvider) + val wasSuccessful = retryWithDelay(mockedBlock, 3, fakeDelay, mockInternalLogger, realTimeProvider) // Then assertThat(wasSuccessful).isTrue() @@ -140,7 +140,7 @@ internal class MiscUtilsTest { whenever(mockedBlock.invoke()).thenThrow(exception).thenReturn(true) // When - val wasSuccessful = retryWithDelay(mockedBlock, 3, fakeDelay, mockInternalLogger, fakeTimeProvider) + val wasSuccessful = retryWithDelay(mockedBlock, 3, fakeDelay, mockInternalLogger, realTimeProvider) // Then assertThat(wasSuccessful).isTrue() diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScope.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScope.kt index 9204720e81..dfe24accc0 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScope.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/scope/RumApplicationScope.kt @@ -213,8 +213,7 @@ internal class RumApplicationScope( lastActiveViewInfo?.let { val startViewEvent = RumRawEvent.StartView( key = it.key, - attributes = it.attributes, - eventTime = Time() + attributes = it.attributes ) newSession.handleEvent(startViewEvent, datadogContext, writeScope, writer) } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitor.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitor.kt index c96edd7701..6cc39b3581 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitor.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/monitor/DatadogRumMonitor.kt @@ -592,11 +592,11 @@ internal class DatadogRumMonitor( override fun eventDropped(viewId: String, event: StorageEvent) { when (event) { - is StorageEvent.Action -> handleEvent(RumRawEvent.ActionDropped(viewId, eventTime = Time())) - is StorageEvent.Resource -> handleEvent(RumRawEvent.ResourceDropped(viewId, event.resourceId, Time())) - is StorageEvent.Error -> handleEvent(RumRawEvent.ErrorDropped(viewId, event.resourceId, Time())) - is StorageEvent.LongTask -> handleEvent(RumRawEvent.LongTaskDropped(viewId, false, Time())) - is StorageEvent.FrozenFrame -> handleEvent(RumRawEvent.LongTaskDropped(viewId, true, Time())) + is StorageEvent.Action -> handleEvent(RumRawEvent.ActionDropped(viewId)) + is StorageEvent.Resource -> handleEvent(RumRawEvent.ResourceDropped(viewId, event.resourceId)) + is StorageEvent.Error -> handleEvent(RumRawEvent.ErrorDropped(viewId, event.resourceId)) + is StorageEvent.LongTask -> handleEvent(RumRawEvent.LongTaskDropped(viewId, false)) + is StorageEvent.FrozenFrame -> handleEvent(RumRawEvent.LongTaskDropped(viewId, true)) is StorageEvent.View -> { // Nothing to do } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/RumTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/RumTest.kt index 564f260bf1..53cdf66ae1 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/RumTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/RumTest.kt @@ -13,6 +13,7 @@ import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.sampling.RateBasedSampler +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.rum.internal.RumFeature import com.datadog.android.rum.internal.monitor.DatadogRumMonitor import com.datadog.android.rum.internal.monitor.NoOpAdvancedRumMonitor @@ -62,6 +63,7 @@ internal class RumTest { @BeforeEach fun `set up`() { whenever(mockSdkCore.internalLogger) doReturn mock() + whenever(mockSdkCore.timeProvider) doReturn DefaultTimeProvider() whenever(mockSdkCore.firstPartyHostResolver) doReturn mock() whenever(mockSdkCore.createSingleThreadExecutorService(any())) doReturn mock() whenever(mockSdkCore.createScheduledExecutorService(any())) doReturn mock() diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt index 44ba3404a6..2e1e641c91 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt @@ -25,6 +25,7 @@ import com.datadog.android.event.EventMapper import com.datadog.android.event.MapperSerializer import com.datadog.android.internal.flags.RumFlagEvaluationMessage import com.datadog.android.internal.telemetry.InternalTelemetryEvent +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.GlobalRumMonitor import com.datadog.android.rum.RumErrorSource @@ -148,13 +149,13 @@ internal class RumFeatureTest { @Mock lateinit var mockScheduledExecutorService: ScheduledExecutorService - @Mock - lateinit var mockTimeProvider: TimeProvider + lateinit var realTimeProvider: TimeProvider @BeforeEach fun `set up`() { + realTimeProvider = DefaultTimeProvider() whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger - whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider + whenever(mockSdkCore.timeProvider) doReturn realTimeProvider whenever(mockSdkCore.createScheduledExecutorService(any())) doReturn mockScheduledExecutorService val mockContentResolver = mock() @@ -817,6 +818,7 @@ internal class RumFeatureTest { verify(mockLongTaskTrackingStrategy).unregister(appContext.mockInstance) } + @Test fun `M clean up all RUM context update receivers W onStop()`() { // Given testedFeature.onInitialize(appContext.mockInstance) @@ -827,7 +829,7 @@ internal class RumFeatureTest { // Then rumContextUpdateReceivers.forEach { - verify(mockSdkCore.removeContextUpdateReceiver(it)) + verify(mockSdkCore).removeContextUpdateReceiver(it) } assertThat(testedFeature.rumContextUpdateReceivers).isEmpty() } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReaderTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReaderTest.kt index 33effd9f93..dd84a6509c 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReaderTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/accessibility/DefaultAccessibilityReaderTest.kt @@ -20,6 +20,7 @@ import android.view.View import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener import com.datadog.android.api.InternalLogger +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.internal.domain.accessibility.DefaultAccessibilityReader.Companion.CAPTIONING_ENABLED_KEY import com.datadog.android.rum.utils.forge.Configurator @@ -85,8 +86,7 @@ internal class DefaultAccessibilityReaderTest { @Mock lateinit var mockHandler: Handler - @Mock - lateinit var mockTimeProvider: TimeProvider + val realTimeProvider: TimeProvider = DefaultTimeProvider() private lateinit var testedReader: DefaultAccessibilityReader @@ -105,7 +105,7 @@ internal class DefaultAccessibilityReaderTest { return DefaultAccessibilityReader( internalLogger = mockInternalLogger, applicationContext = mockContext, - timeProvider = mockTimeProvider, + timeProvider = realTimeProvider, resources = mockResources, activityManager = mockActivityManager, accessibilityManager = mockAccessibilityManager, @@ -162,7 +162,7 @@ internal class DefaultAccessibilityReaderTest { testedReader = DefaultAccessibilityReader( internalLogger = mockInternalLogger, applicationContext = mockContext, - timeProvider = mockTimeProvider, + timeProvider = realTimeProvider, resources = mockResources, activityManager = mockActivityManager, accessibilityManager = null, @@ -342,7 +342,7 @@ internal class DefaultAccessibilityReaderTest { testedReader = DefaultAccessibilityReader( internalLogger = mockInternalLogger, applicationContext = mockContext, - timeProvider = mockTimeProvider, + timeProvider = realTimeProvider, resources = mockResources, activityManager = null, accessibilityManager = mockAccessibilityManager, @@ -623,7 +623,7 @@ internal class DefaultAccessibilityReaderTest { testedReader = DefaultAccessibilityReader( internalLogger = mockInternalLogger, applicationContext = mockContext, - timeProvider = mockTimeProvider, + timeProvider = realTimeProvider, resources = mockResources, activityManager = mockActivityManager, accessibilityManager = null, diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeTest.kt index 276fd455cd..f50d92d98c 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeTest.kt @@ -16,6 +16,7 @@ import com.datadog.android.api.storage.DataWriter import com.datadog.android.api.storage.NoOpDataWriter import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.RumSessionListener import com.datadog.android.rum.RumSessionType @@ -142,9 +143,6 @@ internal class RumSessionScopeTest { @Mock lateinit var mockEventWriteScope: EventWriteScope - @Mock - lateinit var mockTimeProvider: TimeProvider - @Forgery lateinit var fakeParentContext: RumContext @@ -167,10 +165,14 @@ internal class RumSessionScopeTest { private var fakeRumSessionType: RumSessionType? = null + lateinit var realTimeProvider: TimeProvider + @BeforeEach fun `set up`(forge: Forge) { fakeInitialViewEvent = forge.startViewEvent() + realTimeProvider = DefaultTimeProvider() + whenever(mockParentScope.getRumContext()) doReturn fakeParentContext whenever(mockChildScope.handleEvent(any(), any(), any(), any())) doReturn mockChildScope whenever(mockSdkCore.getFeature(Feature.SESSION_REPLAY_FEATURE_NAME)) doReturn @@ -1531,7 +1533,8 @@ internal class RumSessionScopeTest { private fun initializeTestedScope( sampleRate: Float = 100f, withMockChildScope: Boolean = true, - backgroundTrackingEnabled: Boolean? = null + backgroundTrackingEnabled: Boolean? = null, + timeProvider: TimeProvider = DefaultTimeProvider() ) { testedScope = RumSessionScope( parentScope = mockParentScope, @@ -1557,7 +1560,7 @@ internal class RumSessionScopeTest { batteryInfoProvider = mockBatteryInfoProvider, displayInfoProvider = mockDisplayInfoProvider, rumAppStartupTelemetryReporter = mockRumAppStartupTelemetryReporter, - timeProvider = mockTimeProvider + timeProvider = timeProvider ) if (withMockChildScope) { diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/instrumentation/MainLooperLongTaskStrategyTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/instrumentation/MainLooperLongTaskStrategyTest.kt index 83fd73b777..01f007024b 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/instrumentation/MainLooperLongTaskStrategyTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/instrumentation/MainLooperLongTaskStrategyTest.kt @@ -7,6 +7,7 @@ package com.datadog.android.rum.internal.instrumentation import android.os.Looper +import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.internal.monitor.AdvancedRumMonitor import com.datadog.android.rum.utils.config.GlobalRumMonitorTestConfiguration @@ -38,7 +39,6 @@ import org.mockito.kotlin.isA import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions -import org.mockito.kotlin.whenever import org.mockito.quality.Strictness import java.util.concurrent.TimeUnit @@ -56,8 +56,7 @@ internal class MainLooperLongTaskStrategyTest : ObjectTest>>>> Dispatching to $target $callback: $what") + Thread.sleep(duration / 4) testedPrinter.println("<<<<< Finished to $target $callback") // Then @@ -152,21 +151,21 @@ internal class MainLooperLongTaskStrategyTest : ObjectTest() ) doReturn mockReplayFeature whenever(mockCore.internalLogger) doReturn mockInternalLogger + whenever(mockCore.timeProvider) doReturn mockTimeProvider whenever(mockRumFeature.requestFactory) doReturn mockRumRequestFactory whenever(mockLogsFeature.requestFactory) doReturn mockLogsRequestFactory diff --git a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCacheTest.kt b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCacheTest.kt index 0375ddebf2..2bfe2f1fc9 100644 --- a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCacheTest.kt +++ b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCacheTest.kt @@ -6,6 +6,8 @@ package com.datadog.android.webview.internal.rum.domain +import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.junit5.ForgeConfiguration @@ -32,6 +34,8 @@ internal class WebViewNativeRumViewsCacheTest { private lateinit var fakeIdGenerator: FakeIdGenerator private lateinit var fakeClock: FakeClock + + private lateinit var realTimeProvider: TimeProvider private lateinit var testedCache: WebViewNativeRumViewsCache // region Unit Tests @@ -40,7 +44,8 @@ internal class WebViewNativeRumViewsCacheTest { fun `set up`(forge: Forge) { fakeClock = FakeClock() fakeIdGenerator = FakeIdGenerator(forge) - testedCache = WebViewNativeRumViewsCache() + realTimeProvider = DefaultTimeProvider() + testedCache = WebViewNativeRumViewsCache(realTimeProvider) } @Test @@ -164,7 +169,7 @@ internal class WebViewNativeRumViewsCacheTest { ) { // Given val entriesTtlLimitInMs = TimeUnit.SECONDS.toMillis(1) - testedCache = WebViewNativeRumViewsCache(entriesTtlLimitInMs) + testedCache = WebViewNativeRumViewsCache(realTimeProvider, entriesTtlLimitInMs) val fakeOldEntries = forge.aList(size = forge.anInt(min = 1, max = 10)) { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeIdGenerator.generateId(), From ec0936054ce20ab1b13659bd2f0f370bb9d84787 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Fri, 21 Nov 2025 12:04:24 +0000 Subject: [PATCH 20/29] RUM-10363: Fix ktlint --- .../internal/processor/RecordedDataProcessorTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/processor/RecordedDataProcessorTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/processor/RecordedDataProcessorTest.kt index 5d1de6a5b5..04f3e3fd3a 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/processor/RecordedDataProcessorTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/processor/RecordedDataProcessorTest.kt @@ -92,7 +92,7 @@ internal class RecordedDataProcessorTest { lateinit var mockResourceDataStoreManager: ResourceDataStoreManager private val invalidRumContext = SessionReplayRumContext() - + private val realTimeProvider = DefaultTimeProvider() private lateinit var invalidRecordedQueuedItemContext: RecordedQueuedItemContext From 50978b7b4603f940e998b44305e986e942b8de17 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Fri, 21 Nov 2025 17:22:11 +0000 Subject: [PATCH 21/29] RUM-10363: Small improvements --- .../kotlin/com/datadog/android/core/internal/SdkFeature.kt | 3 ++- .../datadog/android/internal/profiler/DDExecutionTimer.kt | 7 ++----- .../datadog/android/internal/profiler/GlobalBenchmark.kt | 7 +++++-- .../datadog/android/rum/resource/RumResourceInputStream.kt | 7 ++++--- .../datadog/benchmark/internal/reader/FpsVitalReader.kt | 6 +++--- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/SdkFeature.kt b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/SdkFeature.kt index b0d736e45e..8bf85da834 100644 --- a/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/SdkFeature.kt +++ b/dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/SdkFeature.kt @@ -399,7 +399,8 @@ internal class SdkFeature( sdkVersion = coreFeature.sdkVersion, androidInfoProvider = coreFeature.androidInfoProvider, executionTimer = GlobalBenchmark.createExecutionTimer( - track = wrappedFeature.name + track = wrappedFeature.name, + timeProvider = coreFeature.timeProvider ) ) } diff --git a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/profiler/DDExecutionTimer.kt b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/profiler/DDExecutionTimer.kt index 19d69cda37..b1366b2101 100644 --- a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/profiler/DDExecutionTimer.kt +++ b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/profiler/DDExecutionTimer.kt @@ -6,17 +6,14 @@ package com.datadog.android.internal.profiler -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider internal class DDExecutionTimer( private val track: String, - private val benchmarkSdkUploads: BenchmarkSdkUploads = GlobalBenchmark.getBenchmarkSdkUploads(), - timeProviderFactory: () -> TimeProvider = { DefaultTimeProvider() } + private val timeProvider: TimeProvider, + private val benchmarkSdkUploads: BenchmarkSdkUploads = GlobalBenchmark.getBenchmarkSdkUploads() ) : ExecutionTimer { - private val timeProvider: TimeProvider by lazy(timeProviderFactory) - override fun measure(action: () -> T): T { if (track.isEmpty()) { return action() diff --git a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/profiler/GlobalBenchmark.kt b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/profiler/GlobalBenchmark.kt index f80a5f7d45..eba7a6230b 100644 --- a/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/profiler/GlobalBenchmark.kt +++ b/dd-sdk-android-internal/src/main/java/com/datadog/android/internal/profiler/GlobalBenchmark.kt @@ -6,6 +6,8 @@ package com.datadog.android.internal.profiler +import com.datadog.android.internal.time.TimeProvider + /** * A global holder of [BenchmarkProfiler] * allowing registration and retrieval of [BenchmarkProfiler] and [BenchmarkMeter] implementations. @@ -48,13 +50,14 @@ object GlobalBenchmark { /** * Creates the appropriate [ExecutionTimer]. */ - fun createExecutionTimer(track: String): ExecutionTimer { + fun createExecutionTimer(track: String, timeProvider: TimeProvider): ExecutionTimer { if (benchmarkSdkUploads is NoOpBenchmarkSdkUploads) { return NoOpExecutionTimer() } return DDExecutionTimer( - track = track + track = track, + timeProvider = timeProvider ) } } diff --git a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/resource/RumResourceInputStream.kt b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/resource/RumResourceInputStream.kt index 2ceedcf4a5..34c9a16177 100644 --- a/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/resource/RumResourceInputStream.kt +++ b/features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/resource/RumResourceInputStream.kt @@ -8,6 +8,7 @@ package com.datadog.android.rum.resource import com.datadog.android.Datadog import com.datadog.android.api.SdkCore +import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.GlobalRumMonitor @@ -25,7 +26,6 @@ import java.io.InputStream * @param url the URL associated with the underlying resource, as you want it displayed in Datadog * @param sdkCore the [SdkCore] instance to report resources to. If not provided, default * instance will be used. - * @param timeProvider - the provider for time measurements. */ @Suppress("ThrowingInternalException", "TooGenericExceptionCaught") class RumResourceInputStream @@ -33,8 +33,7 @@ class RumResourceInputStream constructor( val delegate: InputStream, val url: String, - val sdkCore: SdkCore = Datadog.getInstance(), - val timeProvider: TimeProvider = DefaultTimeProvider() + val sdkCore: SdkCore = Datadog.getInstance() ) : InputStream() { internal val key: String = delegate.javaClass.simpleName + @@ -47,6 +46,8 @@ constructor( private var firstByte: Long = 0L private var lastByte: Long = 0L + private val timeProvider: TimeProvider = (sdkCore as? FeatureSdkCore)?.timeProvider ?: DefaultTimeProvider() + init { val rumMonitor = GlobalRumMonitor.get(sdkCore) rumMonitor.startResource(key, METHOD, url) diff --git a/tools/benchmark/src/main/java/com/datadog/benchmark/internal/reader/FpsVitalReader.kt b/tools/benchmark/src/main/java/com/datadog/benchmark/internal/reader/FpsVitalReader.kt index 05dab653d6..2891600d59 100644 --- a/tools/benchmark/src/main/java/com/datadog/benchmark/internal/reader/FpsVitalReader.kt +++ b/tools/benchmark/src/main/java/com/datadog/benchmark/internal/reader/FpsVitalReader.kt @@ -11,15 +11,15 @@ import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import java.util.concurrent.TimeUnit -internal class FpsVitalReader( - private val timeProvider: TimeProvider = DefaultTimeProvider() -) : VitalReader { +internal class FpsVitalReader() : VitalReader { private var currentFps: Double = 0.0 private var frameCount = 0 private var lastFrameTime: Long = 0 private val intervalMs = FPS_SAMPLE_INTERVAL_IN_MS + private val timeProvider: TimeProvider = DefaultTimeProvider() + private val frameCallback = object : Choreographer.FrameCallback { override fun doFrame(frameTimeNanos: Long) { if (lastFrameTime == 0L) { From 8db5d89b3b548c32e126ccbf125881e2a46eb509 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Fri, 21 Nov 2025 17:31:34 +0000 Subject: [PATCH 22/29] RUM-10363: Update api files --- dd-sdk-android-internal/api/apiSurface | 2 +- dd-sdk-android-internal/api/dd-sdk-android-internal.api | 2 +- features/dd-sdk-android-rum/api/apiSurface | 2 +- features/dd-sdk-android-rum/api/dd-sdk-android-rum.api | 4 +--- .../com/datadog/benchmark/internal/reader/FpsVitalReader.kt | 2 +- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/dd-sdk-android-internal/api/apiSurface b/dd-sdk-android-internal/api/apiSurface index 1b25f553ed..abee9b5876 100644 --- a/dd-sdk-android-internal/api/apiSurface +++ b/dd-sdk-android-internal/api/apiSurface @@ -53,7 +53,7 @@ object com.datadog.android.internal.profiler.GlobalBenchmark fun register(BenchmarkSdkUploads) fun getProfiler(): BenchmarkProfiler fun getBenchmarkSdkUploads(): BenchmarkSdkUploads - fun createExecutionTimer(String): ExecutionTimer + fun createExecutionTimer(String, com.datadog.android.internal.time.TimeProvider): ExecutionTimer sealed class com.datadog.android.internal.telemetry.InternalTelemetryEvent sealed class Log : InternalTelemetryEvent constructor(String, Map?) diff --git a/dd-sdk-android-internal/api/dd-sdk-android-internal.api b/dd-sdk-android-internal/api/dd-sdk-android-internal.api index 5842bbfa25..9d05bf4b82 100644 --- a/dd-sdk-android-internal/api/dd-sdk-android-internal.api +++ b/dd-sdk-android-internal/api/dd-sdk-android-internal.api @@ -121,7 +121,7 @@ public abstract interface class com/datadog/android/internal/profiler/ExecutionT public final class com/datadog/android/internal/profiler/GlobalBenchmark { public static final field INSTANCE Lcom/datadog/android/internal/profiler/GlobalBenchmark; - public final fun createExecutionTimer (Ljava/lang/String;)Lcom/datadog/android/internal/profiler/ExecutionTimer; + public final fun createExecutionTimer (Ljava/lang/String;Lcom/datadog/android/internal/time/TimeProvider;)Lcom/datadog/android/internal/profiler/ExecutionTimer; public final fun getBenchmarkSdkUploads ()Lcom/datadog/android/internal/profiler/BenchmarkSdkUploads; public final fun getProfiler ()Lcom/datadog/android/internal/profiler/BenchmarkProfiler; public final fun register (Lcom/datadog/android/internal/profiler/BenchmarkProfiler;)V diff --git a/features/dd-sdk-android-rum/api/apiSurface b/features/dd-sdk-android-rum/api/apiSurface index 4eab37ad7d..9055e1e615 100644 --- a/features/dd-sdk-android-rum/api/apiSurface +++ b/features/dd-sdk-android-rum/api/apiSurface @@ -229,7 +229,7 @@ class com.datadog.android.rum.resource.ResourceId override fun equals(Any?): Boolean override fun hashCode(): Int class com.datadog.android.rum.resource.RumResourceInputStream : java.io.InputStream - constructor(java.io.InputStream, String, com.datadog.android.api.SdkCore = Datadog.getInstance(), com.datadog.android.internal.time.TimeProvider = DefaultTimeProvider()) + constructor(java.io.InputStream, String, com.datadog.android.api.SdkCore = Datadog.getInstance()) override fun read(): Int override fun read(ByteArray): Int override fun read(ByteArray, Int, Int): Int diff --git a/features/dd-sdk-android-rum/api/dd-sdk-android-rum.api b/features/dd-sdk-android-rum/api/dd-sdk-android-rum.api index 044598bb78..f5226876f3 100644 --- a/features/dd-sdk-android-rum/api/dd-sdk-android-rum.api +++ b/features/dd-sdk-android-rum/api/dd-sdk-android-rum.api @@ -7753,13 +7753,11 @@ public final class com/datadog/android/rum/resource/ResourceId { public final class com/datadog/android/rum/resource/RumResourceInputStream : java/io/InputStream { public fun (Ljava/io/InputStream;Ljava/lang/String;)V public fun (Ljava/io/InputStream;Ljava/lang/String;Lcom/datadog/android/api/SdkCore;)V - public fun (Ljava/io/InputStream;Ljava/lang/String;Lcom/datadog/android/api/SdkCore;Lcom/datadog/android/internal/time/TimeProvider;)V - public synthetic fun (Ljava/io/InputStream;Ljava/lang/String;Lcom/datadog/android/api/SdkCore;Lcom/datadog/android/internal/time/TimeProvider;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Ljava/io/InputStream;Ljava/lang/String;Lcom/datadog/android/api/SdkCore;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun available ()I public fun close ()V public final fun getDelegate ()Ljava/io/InputStream; public final fun getSdkCore ()Lcom/datadog/android/api/SdkCore; - public final fun getTimeProvider ()Lcom/datadog/android/internal/time/TimeProvider; public final fun getUrl ()Ljava/lang/String; public fun mark (I)V public fun markSupported ()Z diff --git a/tools/benchmark/src/main/java/com/datadog/benchmark/internal/reader/FpsVitalReader.kt b/tools/benchmark/src/main/java/com/datadog/benchmark/internal/reader/FpsVitalReader.kt index 2891600d59..fab80d487c 100644 --- a/tools/benchmark/src/main/java/com/datadog/benchmark/internal/reader/FpsVitalReader.kt +++ b/tools/benchmark/src/main/java/com/datadog/benchmark/internal/reader/FpsVitalReader.kt @@ -11,7 +11,7 @@ import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import java.util.concurrent.TimeUnit -internal class FpsVitalReader() : VitalReader { +internal class FpsVitalReader : VitalReader { private var currentFps: Double = 0.0 private var frameCount = 0 From 4676a543fb8fec42130ac15e5f4a351320016634 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Sun, 23 Nov 2025 22:47:20 +0000 Subject: [PATCH 23/29] RUM-10363: Add timeProvider to StubSDKCore.kt --- .../main/kotlin/com/datadog/android/core/stub/StubSDKCore.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/reliability/stub-core/src/main/kotlin/com/datadog/android/core/stub/StubSDKCore.kt b/reliability/stub-core/src/main/kotlin/com/datadog/android/core/stub/StubSDKCore.kt index 61976d7a5c..e1fd2bfad3 100644 --- a/reliability/stub-core/src/main/kotlin/com/datadog/android/core/stub/StubSDKCore.kt +++ b/reliability/stub-core/src/main/kotlin/com/datadog/android/core/stub/StubSDKCore.kt @@ -20,6 +20,8 @@ import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.FeatureScope import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver +import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.TimeProvider import fr.xgouchet.elmyr.Forge import org.mockito.Mockito.mock import org.mockito.kotlin.doReturn @@ -159,6 +161,8 @@ class StubSDKCore( override val internalLogger: InternalLogger = StubInternalLogger() + override val timeProvider: TimeProvider = DefaultTimeProvider() + override fun registerFeature(feature: Feature) { stubFeatureScope(feature, StubFeatureScope(feature, { datadogContext })) } From 3a43bca67aee10e24a1070fb462e7e470b32f872 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Mon, 24 Nov 2025 00:09:47 +0000 Subject: [PATCH 24/29] RUM-10363: Test fix for min API integration test --- .../main/java/com/datadog/android/rum/DdRumContentProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-sdk-android-internal/src/main/java/com/datadog/android/rum/DdRumContentProvider.kt b/dd-sdk-android-internal/src/main/java/com/datadog/android/rum/DdRumContentProvider.kt index cae4c9f320..09966f75db 100644 --- a/dd-sdk-android-internal/src/main/java/com/datadog/android/rum/DdRumContentProvider.kt +++ b/dd-sdk-android-internal/src/main/java/com/datadog/android/rum/DdRumContentProvider.kt @@ -78,6 +78,6 @@ class DdRumContentProvider : ContentProvider() { /** * fallback for APIs below Android N, see [DefaultAppStartTimeProvider]. */ - val createTimeNs: Long by lazy { DefaultTimeProvider().getDeviceElapsedTimeNs() } + val createTimeNs: Long = DefaultTimeProvider().getDeviceElapsedTimeNs() } } From f806072d341f6c65ba6319ae515e07afa1a96dd2 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Mon, 24 Nov 2025 17:09:52 +0000 Subject: [PATCH 25/29] RUM-10363: Small improvements --- .../com/datadog/android/core/stub/StubSDKCore.kt | 2 +- .../android/core/stub/StubScheduledExecutorService.kt | 8 +++++--- .../java/com/datadog/benchmark/DatadogVitalsMeter.kt | 10 +++++++--- .../benchmark/internal/reader/FpsVitalReader.kt | 5 +---- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/reliability/stub-core/src/main/kotlin/com/datadog/android/core/stub/StubSDKCore.kt b/reliability/stub-core/src/main/kotlin/com/datadog/android/core/stub/StubSDKCore.kt index e1fd2bfad3..0355c8aaff 100644 --- a/reliability/stub-core/src/main/kotlin/com/datadog/android/core/stub/StubSDKCore.kt +++ b/reliability/stub-core/src/main/kotlin/com/datadog/android/core/stub/StubSDKCore.kt @@ -191,7 +191,7 @@ class StubSDKCore( } override fun createScheduledExecutorService(executorContext: String): ScheduledExecutorService { - return StubScheduledExecutorService(executorContext) + return StubScheduledExecutorService(executorContext, timeProvider) } override fun createSingleThreadExecutorService(executorContext: String): ExecutorService { diff --git a/reliability/stub-core/src/main/kotlin/com/datadog/android/core/stub/StubScheduledExecutorService.kt b/reliability/stub-core/src/main/kotlin/com/datadog/android/core/stub/StubScheduledExecutorService.kt index 38d6ee1d91..d83864a175 100644 --- a/reliability/stub-core/src/main/kotlin/com/datadog/android/core/stub/StubScheduledExecutorService.kt +++ b/reliability/stub-core/src/main/kotlin/com/datadog/android/core/stub/StubScheduledExecutorService.kt @@ -6,7 +6,7 @@ package com.datadog.android.core.stub -import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.TimeProvider import java.util.concurrent.Callable import java.util.concurrent.Delayed import java.util.concurrent.Future @@ -21,11 +21,13 @@ import kotlin.math.sign * Stubbed version of a [ScheduledExecutorService]. */ @Suppress("UndocumentedPublicFunction") -class StubScheduledExecutorService(executorContext: String) : ScheduledExecutorService { +class StubScheduledExecutorService( + executorContext: String, + private val timeProvider: TimeProvider +) : ScheduledExecutorService { private var isShutdown = false private var isTerminated = false - private val timeProvider = DefaultTimeProvider() init { println("Stubbing a ScheduledExecutorService with context: $executorContext") diff --git a/tools/benchmark/src/main/java/com/datadog/benchmark/DatadogVitalsMeter.kt b/tools/benchmark/src/main/java/com/datadog/benchmark/DatadogVitalsMeter.kt index 5985a8a65b..cedeafb996 100644 --- a/tools/benchmark/src/main/java/com/datadog/benchmark/DatadogVitalsMeter.kt +++ b/tools/benchmark/src/main/java/com/datadog/benchmark/DatadogVitalsMeter.kt @@ -6,6 +6,8 @@ package com.datadog.benchmark +import com.datadog.android.Datadog +import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.internal.profiler.GlobalBenchmark import com.datadog.benchmark.exporter.DatadogMetricExporter import com.datadog.benchmark.exporter.DatadogSpanExporter @@ -31,11 +33,13 @@ import java.util.concurrent.TimeUnit * It provides functionalities to start and stop monitoring these metrics, which will be uploaded to * Datadog metric API. */ -class DatadogVitalsMeter private constructor(private val meter: Meter) : DatadogBaseMeter { +class DatadogVitalsMeter private constructor( + private val meter: Meter, + private val fpsVitalReader: FpsVitalReader +) : DatadogBaseMeter { private val cpuVitalReader: CPUVitalReader = CPUVitalReader() private val memoryVitalReader: MemoryVitalReader = MemoryVitalReader() - private val fpsVitalReader: FpsVitalReader = FpsVitalReader() private val gaugesByMetricName: MutableMap = mutableMapOf() @@ -109,7 +113,7 @@ class DatadogVitalsMeter private constructor(private val meter: Meter) : Datadog GlobalOpenTelemetry.set(openTelemetry) GlobalBenchmark.register(DDBenchmarkProfiler()) val meter = openTelemetry.getMeter(METER_INSTRUMENTATION_SCOPE_NAME) - return DatadogVitalsMeter(meter) + return DatadogVitalsMeter(meter, FpsVitalReader((Datadog.getInstance() as FeatureSdkCore).timeProvider)) } private const val METER_INSTRUMENTATION_SCOPE_NAME = "datadog.open-telemetry" diff --git a/tools/benchmark/src/main/java/com/datadog/benchmark/internal/reader/FpsVitalReader.kt b/tools/benchmark/src/main/java/com/datadog/benchmark/internal/reader/FpsVitalReader.kt index fab80d487c..58efc6881e 100644 --- a/tools/benchmark/src/main/java/com/datadog/benchmark/internal/reader/FpsVitalReader.kt +++ b/tools/benchmark/src/main/java/com/datadog/benchmark/internal/reader/FpsVitalReader.kt @@ -7,19 +7,16 @@ package com.datadog.benchmark.internal.reader import android.view.Choreographer -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import java.util.concurrent.TimeUnit -internal class FpsVitalReader : VitalReader { +internal class FpsVitalReader(timeProvider: TimeProvider) : VitalReader { private var currentFps: Double = 0.0 private var frameCount = 0 private var lastFrameTime: Long = 0 private val intervalMs = FPS_SAMPLE_INTERVAL_IN_MS - private val timeProvider: TimeProvider = DefaultTimeProvider() - private val frameCallback = object : Choreographer.FrameCallback { override fun doFrame(frameTimeNanos: Long) { if (lastFrameTime == 0L) { From ec53cffd8600eaf1f62183fb4bef2b1b00da4615 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Wed, 26 Nov 2025 00:19:28 +0000 Subject: [PATCH 26/29] RUM-10363: Continue improving tests --- .../processor/RecordedDataProcessorTest.kt | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/processor/RecordedDataProcessorTest.kt b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/processor/RecordedDataProcessorTest.kt index 04f3e3fd3a..900fd46a21 100644 --- a/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/processor/RecordedDataProcessorTest.kt +++ b/features/dd-sdk-android-session-replay/src/test/kotlin/com/datadog/android/sessionreplay/internal/processor/RecordedDataProcessorTest.kt @@ -7,7 +7,7 @@ package com.datadog.android.sessionreplay.internal.processor import android.content.res.Configuration -import com.datadog.android.internal.time.DefaultTimeProvider +import com.datadog.android.internal.time.TimeProvider import com.datadog.android.sessionreplay.forge.ForgeConfigurator import com.datadog.android.sessionreplay.internal.ResourcesFeature import com.datadog.android.sessionreplay.internal.async.ResourceRecordedDataQueueItem @@ -46,7 +46,6 @@ import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.whenever import org.mockito.quality.Strictness -import java.util.concurrent.TimeUnit @Extensions( ExtendWith(MockitoExtension::class), @@ -93,7 +92,8 @@ internal class RecordedDataProcessorTest { private val invalidRumContext = SessionReplayRumContext() - private val realTimeProvider = DefaultTimeProvider() + @Mock + lateinit var mockTimeProvider: TimeProvider private lateinit var invalidRecordedQueuedItemContext: RecordedQueuedItemContext private lateinit var initialRecordedQueuedItemContext: RecordedQueuedItemContext @@ -157,7 +157,7 @@ internal class RecordedDataProcessorTest { resourcesWriter = mockResourcesWriter, writer = mockWriter, mutationResolver = mockMutationResolver, - timeProvider = realTimeProvider, + timeProvider = mockTimeProvider, nodeFlattener = mockNodeFlattener ) } @@ -279,6 +279,22 @@ internal class RecordedDataProcessorTest { forge: Forge ) { // Given + val fakeInitialElapsedTimeNs = forge.aLong( + min = RecordedDataProcessor.FULL_SNAPSHOT_INTERVAL_IN_NS, + max = Long.MAX_VALUE / 2 + ) + val fakeElapsedTimeAfterIntervalNs = + fakeInitialElapsedTimeNs + RecordedDataProcessor.FULL_SNAPSHOT_INTERVAL_IN_NS + + whenever(mockTimeProvider.getDeviceElapsedTimeNs()).thenReturn( + fakeInitialElapsedTimeNs, + fakeInitialElapsedTimeNs, + fakeInitialElapsedTimeNs, + fakeElapsedTimeAfterIntervalNs, + fakeElapsedTimeAfterIntervalNs, + fakeElapsedTimeAfterIntervalNs + ) + fakeSnapshot1.map { val fakeFlattenedSnapshot = forge.aList { getForgery(MobileSegment.Wireframe::class.java) @@ -299,7 +315,6 @@ internal class RecordedDataProcessorTest { fakeSnapshotItem1 = createSnapshotItem(fakeSnapshot1) testedProcessor.processScreenSnapshots(fakeSnapshotItem1) - Thread.sleep(TimeUnit.NANOSECONDS.toMillis(RecordedDataProcessor.FULL_SNAPSHOT_INTERVAL_IN_NS)) currentRecordedQueuedItemContext = mockRumContextDataHandler.createRumContextData() ?: fail("RumContextData is null") @@ -745,7 +760,7 @@ internal class RecordedDataProcessorTest { val item = TouchEventRecordedDataQueueItem( recordedQueuedItemContext = rumContextData, - timeProvider = realTimeProvider, + timeProvider = mockTimeProvider, touchData = fakeTouchRecords ) @@ -1316,7 +1331,7 @@ internal class RecordedDataProcessorTest { ): ResourceRecordedDataQueueItem = ResourceRecordedDataQueueItem( recordedQueuedItemContext = usedContext, identifier = fakeIdentifier, - timeProvider = realTimeProvider, + timeProvider = mockTimeProvider, resourceData = resourceData ) @@ -1327,7 +1342,7 @@ internal class RecordedDataProcessorTest { ): SnapshotRecordedDataQueueItem = SnapshotRecordedDataQueueItem( recordedQueuedItemContext = usedContext, systemInformation = systemInformation, - timeProvider = realTimeProvider + timeProvider = mockTimeProvider ).apply { this.nodes = snapshot } @@ -1338,7 +1353,7 @@ internal class RecordedDataProcessorTest { ): TouchEventRecordedDataQueueItem = TouchEventRecordedDataQueueItem( recordedQueuedItemContext = usedContext, - timeProvider = realTimeProvider, + timeProvider = mockTimeProvider, touchData = touchEvent ) From e6e4195cfeec0092982464d1f1d9253dba456b05 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Wed, 26 Nov 2025 14:29:43 +0000 Subject: [PATCH 27/29] RUM-10363: Improve tests --- .../domain/WebViewNativeRumViewsCacheTest.kt | 73 ++++++++++--------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCacheTest.kt b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCacheTest.kt index 2bfe2f1fc9..cdfbc4468e 100644 --- a/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCacheTest.kt +++ b/features/dd-sdk-android-webview/src/test/kotlin/com/datadog/android/webview/internal/rum/domain/WebViewNativeRumViewsCacheTest.kt @@ -6,7 +6,6 @@ package com.datadog.android.webview.internal.rum.domain -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.utils.forge.Configurator import fr.xgouchet.elmyr.Forge @@ -18,11 +17,12 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertDoesNotThrow 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.kotlin.whenever import org.mockito.quality.Strictness import java.util.concurrent.TimeUnit -import java.util.concurrent.atomic.AtomicLong @Extensions( ExtendWith(MockitoExtension::class), @@ -33,19 +33,27 @@ import java.util.concurrent.atomic.AtomicLong internal class WebViewNativeRumViewsCacheTest { private lateinit var fakeIdGenerator: FakeIdGenerator - private lateinit var fakeClock: FakeClock - private lateinit var realTimeProvider: TimeProvider + private var fakeCurrentTimeMs: Long = 0L + + @Mock + private lateinit var mockTimeProvider: TimeProvider private lateinit var testedCache: WebViewNativeRumViewsCache // region Unit Tests @BeforeEach fun `set up`(forge: Forge) { - fakeClock = FakeClock() + fakeCurrentTimeMs = forge.aPositiveLong() fakeIdGenerator = FakeIdGenerator(forge) - realTimeProvider = DefaultTimeProvider() - testedCache = WebViewNativeRumViewsCache(realTimeProvider) + whenever(mockTimeProvider.getDeviceTimestamp()).thenReturn(fakeCurrentTimeMs) + testedCache = WebViewNativeRumViewsCache(mockTimeProvider) + } + + private fun nextTimestamp(): Long { + fakeCurrentTimeMs++ + whenever(mockTimeProvider.getDeviceTimestamp()).thenReturn(fakeCurrentTimeMs) + return fakeCurrentTimeMs } @Test @@ -56,18 +64,18 @@ internal class WebViewNativeRumViewsCacheTest { val fakeEntries = forge.aList(size = forge.anInt(min = 1, max = 10)) { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeIdGenerator.generateId(), - WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to fakeClock.nextCurrentTimeMillis(), + WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to nextTimestamp(), WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY to true ) } val fakeNoReplayEntries = forge.aList(size = forge.anInt(min = 1, max = 10)) { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeIdGenerator.generateId(), - WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to fakeClock.nextCurrentTimeMillis(), + WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to nextTimestamp(), WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY to false ) } - val fakeBrowserEventTimestampInMs = fakeClock.nextCurrentTimeMillis() + val fakeBrowserEventTimestampInMs = nextTimestamp() fakeEntries.forEach { testedCache.addToCache(it) } @@ -90,14 +98,14 @@ internal class WebViewNativeRumViewsCacheTest { val fakeEntries = forge.aList { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeIdGenerator.generateId(), - WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to fakeClock.nextCurrentTimeMillis(), + WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to nextTimestamp(), WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY to false ) } fakeEntries.forEach { testedCache.addToCache(it) } - val fakeBrowserEventTimestampInMs = fakeClock.nextCurrentTimeMillis() + val fakeBrowserEventTimestampInMs = nextTimestamp() // When val resolvedParentId = testedCache.resolveLastParentIdForBrowserEvent(fakeBrowserEventTimestampInMs) @@ -111,11 +119,11 @@ internal class WebViewNativeRumViewsCacheTest { forge: Forge ) { // Given - val fakeBrowserEventTimestampInMs = fakeClock.nextCurrentTimeMillis() + val fakeBrowserEventTimestampInMs = nextTimestamp() val fakeEntries = forge.aList { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeIdGenerator.generateId(), - WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to fakeClock.nextCurrentTimeMillis(), + WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to nextTimestamp(), WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY to forge.aBool() ) } @@ -133,7 +141,7 @@ internal class WebViewNativeRumViewsCacheTest { @Test fun `M return null W resolveLastParentIdForBrowserEvent() { no data in cache }`() { // Given - val fakeBrowserEventTimestampInMs = fakeClock.nextCurrentTimeMillis() + val fakeBrowserEventTimestampInMs = nextTimestamp() // When val resolvedParentId = testedCache.resolveLastParentIdForBrowserEvent(fakeBrowserEventTimestampInMs) @@ -150,14 +158,14 @@ internal class WebViewNativeRumViewsCacheTest { val fakeEntries = forge.aList { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeIdGenerator.generateId(), - WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to fakeClock.nextCurrentTimeMillis(), + WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to nextTimestamp(), WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY to forge.aBool() ) } fakeEntries.forEach { Thread { testedCache.addToCache(it) }.apply { start() }.join(5000) } - val fakeBrowserEventTimestampInMs = fakeClock.nextCurrentTimeMillis() + val fakeBrowserEventTimestampInMs = nextTimestamp() // Then assertDoesNotThrow { testedCache.resolveLastParentIdForBrowserEvent(fakeBrowserEventTimestampInMs) } @@ -169,18 +177,22 @@ internal class WebViewNativeRumViewsCacheTest { ) { // Given val entriesTtlLimitInMs = TimeUnit.SECONDS.toMillis(1) - testedCache = WebViewNativeRumViewsCache(realTimeProvider, entriesTtlLimitInMs) + val oldEntriesTimestamp = fakeCurrentTimeMs + testedCache = WebViewNativeRumViewsCache(mockTimeProvider, entriesTtlLimitInMs) + var oldEntryTimestamp = oldEntriesTimestamp val fakeOldEntries = forge.aList(size = forge.anInt(min = 1, max = 10)) { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeIdGenerator.generateId(), - WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to System.currentTimeMillis(), + WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to ++oldEntryTimestamp, WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY to forge.aBool() ) } + whenever(mockTimeProvider.getDeviceTimestamp()).thenReturn(oldEntriesTimestamp) fakeOldEntries.forEach { testedCache.addToCache(it) } // When - Thread.sleep(entriesTtlLimitInMs) + val newEntriesTimestamp = oldEntryTimestamp + entriesTtlLimitInMs + 1 + var newEntryTimestamp = newEntriesTimestamp val fakeNewEntries = forge.aList( size = forge.anInt( min = 1, @@ -189,7 +201,7 @@ internal class WebViewNativeRumViewsCacheTest { ) { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeIdGenerator.generateId(), - WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to System.currentTimeMillis(), + WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to ++newEntryTimestamp, WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY to forge.aBool() ) } @@ -200,6 +212,8 @@ internal class WebViewNativeRumViewsCacheTest { it[WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY] as Boolean ) } + // Simulate time passing: device time is beyond TTL for old entries + whenever(mockTimeProvider.getDeviceTimestamp()).thenReturn(newEntriesTimestamp) fakeNewEntries.forEach { testedCache.addToCache(it) } // Then @@ -220,7 +234,7 @@ internal class WebViewNativeRumViewsCacheTest { ) { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeIdGenerator.generateId() + index++, - WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to fakeClock.nextCurrentTimeMillis(), + WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to nextTimestamp(), WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY to forge.aBool() ) } @@ -251,7 +265,7 @@ internal class WebViewNativeRumViewsCacheTest { val fakeEntries = forge.aList(size = forge.anInt(min = 1, max = 10)) { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeViewId, - WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to System.currentTimeMillis(), + WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to nextTimestamp(), WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY to forge.aBool() ) } @@ -279,7 +293,7 @@ internal class WebViewNativeRumViewsCacheTest { ) { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeIdGenerator.generateId(), - WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to fakeClock.nextCurrentTimeMillis(), + WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to nextTimestamp(), WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY to forge.aBool() ) } @@ -291,7 +305,7 @@ internal class WebViewNativeRumViewsCacheTest { ) { mapOf( WebViewNativeRumViewsCache.VIEW_ID_KEY to fakeIdGenerator.generateId(), - WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to fakeClock.nextCurrentTimeMillis(), + WebViewNativeRumViewsCache.VIEW_TIMESTAMP_KEY to nextTimestamp(), WebViewNativeRumViewsCache.VIEW_HAS_REPLAY_KEY to forge.aBool() ) } @@ -318,15 +332,6 @@ internal class WebViewNativeRumViewsCacheTest { // region Utils - private class FakeClock { - - val initialTimeMillis = AtomicLong(System.currentTimeMillis()) - - fun nextCurrentTimeMillis(): Long { - return initialTimeMillis.incrementAndGet() - } - } - private class FakeIdGenerator(private val forge: Forge) { var index = 0 fun generateId(): String { From 093816cbb0cadc1f3df933bdc73e2c54f77bf0c2 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Fri, 28 Nov 2025 14:23:25 +0000 Subject: [PATCH 28/29] RUM-12059: Continue improving tests --- .../core/internal/utils/MiscUtilsTest.kt | 62 +++++++++---------- .../android/rum/internal/RumFeatureTest.kt | 6 +- 2 files changed, 31 insertions(+), 37 deletions(-) diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/MiscUtilsTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/MiscUtilsTest.kt index 64df4ca4cb..8d16387e45 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/MiscUtilsTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/utils/MiscUtilsTest.kt @@ -9,7 +9,6 @@ package com.datadog.android.core.internal.utils import com.datadog.android.api.InternalLogger import com.datadog.android.core.internal.utils.JsonSerializer.ITEM_SERIALIZATION_ERROR import com.datadog.android.core.internal.utils.JsonSerializer.safeMapValuesToJson -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.internal.utils.NULL_MAP_VALUE import com.datadog.android.utils.forge.Configurator @@ -27,13 +26,13 @@ import fr.xgouchet.elmyr.annotation.BoolForgery import fr.xgouchet.elmyr.annotation.FloatForgery import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.annotation.IntForgery +import fr.xgouchet.elmyr.annotation.LongForgery import fr.xgouchet.elmyr.annotation.MapForgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.annotation.StringForgeryType import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.data.Offset import org.json.JSONArray import org.json.JSONObject import org.junit.jupiter.api.Test @@ -51,7 +50,6 @@ import org.mockito.quality.Strictness import java.util.Date import java.util.Locale import java.util.concurrent.TimeUnit -import kotlin.system.measureNanoTime @Extensions( ExtendWith(MockitoExtension::class), @@ -64,65 +62,52 @@ internal class MiscUtilsTest { @Mock lateinit var mockInternalLogger: InternalLogger - var realTimeProvider: TimeProvider = DefaultTimeProvider() + @Mock + lateinit var mockTimeProvider: TimeProvider + + @LongForgery(min = 0L) + var fakeCurrentTimeNs = 0L + + @LongForgery(min = 0L, max = 2L) + var fakeDelaySeconds = 0L @Test fun `M repeat max N times W retryWithDelay { success = false }`(forge: Forge) { // Given val fakeTimes = forge.anInt(min = 1, max = 10) - val fakeDelay = TimeUnit.SECONDS.toNanos(forge.aLong(min = 0, max = 2)) val mockedBlock: () -> Boolean = mock() whenever(mockedBlock.invoke()).thenReturn(false) + stubTimeProviderWithDelay() // When - val wasSuccessful = retryWithDelay(mockedBlock, fakeTimes, fakeDelay, mockInternalLogger, realTimeProvider) + val wasSuccessful = retryWithDelay(mockedBlock, fakeTimes, fakeDelayNs, mockInternalLogger, mockTimeProvider) // Then assertThat(wasSuccessful).isFalse() verify(mockedBlock, times(fakeTimes)).invoke() } - @Test - fun `M execute the block in a delayed loop W retryWithDelay`(forge: Forge) { - // Given - val fakeTimes = forge.anInt(min = 1, max = 4) - val fakeDelay = TimeUnit.SECONDS.toNanos(forge.aLong(min = 0, max = 2)) - val mockedBlock: () -> Boolean = mock() - whenever(mockedBlock.invoke()).thenReturn(false) - - // When - val executionTime = - measureNanoTime { retryWithDelay(mockedBlock, fakeTimes, fakeDelay, mockInternalLogger, realTimeProvider) } - - // Then - assertThat(executionTime).isCloseTo( - fakeTimes * fakeDelay, - Offset.offset(TimeUnit.SECONDS.toNanos(1)) - ) - } - @Test fun `M do nothing W retryWithDelay { times less or equal than 0 }`(forge: Forge) { // Given - val fakeDelay = TimeUnit.SECONDS.toNanos(forge.aLong(min = 0, max = 2)) val mockedBlock: () -> Boolean = mock() // When - retryWithDelay(mockedBlock, forge.anInt(Int.MIN_VALUE, 1), fakeDelay, mockInternalLogger, realTimeProvider) + retryWithDelay(mockedBlock, forge.anInt(Int.MIN_VALUE, 1), fakeDelayNs, mockInternalLogger, mockTimeProvider) // Then verifyNoInteractions(mockedBlock) } @Test - fun `M repeat until success W retryWithDelay { result false }`(forge: Forge) { + fun `M repeat until success W retryWithDelay { result false }`() { // Given - val fakeDelay = TimeUnit.SECONDS.toNanos(forge.aLong(min = 0, max = 2)) val mockedBlock: () -> Boolean = mock() whenever(mockedBlock.invoke()).thenReturn(false).thenReturn(true) + stubTimeProviderWithDelay() // When - val wasSuccessful = retryWithDelay(mockedBlock, 3, fakeDelay, mockInternalLogger, realTimeProvider) + val wasSuccessful = retryWithDelay(mockedBlock, 3, fakeDelayNs, mockInternalLogger, mockTimeProvider) // Then assertThat(wasSuccessful).isTrue() @@ -131,16 +116,15 @@ internal class MiscUtilsTest { @Test fun `M repeat until success W retryWithDelay { exception }`( - @Forgery exception: Exception, - forge: Forge + @Forgery exception: Exception ) { // Given - val fakeDelay = TimeUnit.SECONDS.toNanos(forge.aLong(min = 0, max = 2)) val mockedBlock: () -> Boolean = mock() whenever(mockedBlock.invoke()).thenThrow(exception).thenReturn(true) + stubTimeProviderWithDelay() // When - val wasSuccessful = retryWithDelay(mockedBlock, 3, fakeDelay, mockInternalLogger, realTimeProvider) + val wasSuccessful = retryWithDelay(mockedBlock, 3, fakeDelayNs, mockInternalLogger, mockTimeProvider) // Then assertThat(wasSuccessful).isTrue() @@ -320,5 +304,15 @@ internal class MiscUtilsTest { } } + private val fakeDelayNs: Long + get() = TimeUnit.SECONDS.toNanos(fakeDelaySeconds) + + private fun stubTimeProviderWithDelay() { + whenever(mockTimeProvider.getDeviceElapsedTimeNs()).thenAnswer { + fakeCurrentTimeNs += fakeDelayNs + fakeCurrentTimeNs + } + } + // endregion } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt index 2e1e641c91..ff7c37e082 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt @@ -149,13 +149,13 @@ internal class RumFeatureTest { @Mock lateinit var mockScheduledExecutorService: ScheduledExecutorService - lateinit var realTimeProvider: TimeProvider + @Mock + lateinit var mockTimeProvider: TimeProvider @BeforeEach fun `set up`() { - realTimeProvider = DefaultTimeProvider() whenever(mockSdkCore.internalLogger) doReturn mockInternalLogger - whenever(mockSdkCore.timeProvider) doReturn realTimeProvider + whenever(mockSdkCore.timeProvider) doReturn mockTimeProvider whenever(mockSdkCore.createScheduledExecutorService(any())) doReturn mockScheduledExecutorService val mockContentResolver = mock() From 1dac1ccad3002c65462894eb56b44c49781c6433 Mon Sep 17 00:00:00 2001 From: Francisco Veiga Date: Fri, 28 Nov 2025 17:10:03 +0000 Subject: [PATCH 29/29] RUM-10363: Add StubTimeProvider --- .../datadog/android/core/DatadogCoreTest.kt | 31 +++-- .../metrics/MethodCalledTelemetryTest.kt | 14 +- .../android/rum/internal/RumFeatureTest.kt | 1 - .../internal/domain/scope/RumRawEventExt.kt | 8 +- .../domain/scope/RumSessionScopeTest.kt | 129 ++++++++++++------ tools/unit/build.gradle.kts | 1 + .../tools/unit/stub/StubTimeProvider.kt | 37 +++++ 7 files changed, 161 insertions(+), 60 deletions(-) create mode 100644 tools/unit/src/main/kotlin/com/datadog/tools/unit/stub/StubTimeProvider.kt diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt index aa754973fa..7cf0dfb5c9 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt @@ -28,7 +28,6 @@ import com.datadog.android.core.internal.privacy.ConsentProvider import com.datadog.android.core.internal.system.BuildSdkVersionProvider import com.datadog.android.core.internal.user.MutableUserInfoProvider import com.datadog.android.core.thread.FlushableExecutorService -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.ndk.internal.NdkCrashHandler import com.datadog.android.privacy.TrackingConsent @@ -40,6 +39,7 @@ import com.datadog.tools.unit.extensions.TestConfigurationExtension import com.datadog.tools.unit.extensions.config.TestConfiguration import com.datadog.tools.unit.forge.aThrowable import com.datadog.tools.unit.forge.exhaustiveAttributes +import com.datadog.tools.unit.stub.StubTimeProvider import com.google.gson.JsonObject import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.AdvancedForgery @@ -53,7 +53,6 @@ import fr.xgouchet.elmyr.annotation.StringForgeryType import fr.xgouchet.elmyr.junit5.ForgeConfiguration import fr.xgouchet.elmyr.junit5.ForgeExtension import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.data.Offset import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.AssertionFailureBuilder import org.junit.jupiter.api.BeforeEach @@ -1015,22 +1014,31 @@ internal class DatadogCoreTest { } @Test - fun `M provide time info without correction W time() {NoOpTimeProvider}`() { + fun `M provide time info without correction W time() {NoOpTimeProvider}`( + @LongForgery(min = 0L) fakeDeviceTimestampMs: Long, + @LongForgery(min = 0L) fakeServerTimestampMs: Long, + @LongForgery(min = 0L) fakeServerOffsetNs: Long, + @LongForgery(min = 0L) fakeServerOffsetMs: Long + ) { // Given testedCore.coreFeature = mock() whenever(testedCore.coreFeature.initialized).thenReturn(AtomicBoolean()) - whenever(testedCore.coreFeature.timeProvider) doReturn DefaultTimeProvider() + val stubTimeProvider = StubTimeProvider( + deviceTimestampMs = fakeDeviceTimestampMs, + serverTimestampMs = fakeServerTimestampMs, + serverOffsetNs = fakeServerOffsetNs, + serverOffsetMs = fakeServerOffsetMs + ) + whenever(testedCore.coreFeature.timeProvider) doReturn stubTimeProvider // When val time = testedCore.time // Then - // We do keep a margin of 1ms delay as this test can sometimes be flaky. - // the DatadogCore.time implementation computes server and device time independently and it can sometimes - // happen that those computations land on successive ms, leading to a 1second offset - assertThat(time.deviceTimeNs).isCloseTo(time.serverTimeNs, Offset.offset(msToNs)) - assertThat(time.serverTimeOffsetMs).isLessThanOrEqualTo(1) - assertThat(time.serverTimeOffsetNs).isLessThanOrEqualTo(msToNs) + assertThat(time.deviceTimeNs).isEqualTo(TimeUnit.MILLISECONDS.toNanos(fakeDeviceTimestampMs)) + assertThat(time.serverTimeNs).isEqualTo(TimeUnit.MILLISECONDS.toNanos(fakeServerTimestampMs)) + assertThat(time.serverTimeOffsetMs).isEqualTo(fakeServerOffsetMs) + assertThat(time.serverTimeOffsetNs).isEqualTo(fakeServerOffsetNs) } @Test @@ -1494,9 +1502,6 @@ internal class DatadogCoreTest { companion object { - val msToNs = TimeUnit.MILLISECONDS.toNanos(1) - val secondsToNs = TimeUnit.SECONDS.toNanos(1) - val appContext = ApplicationContextTestConfiguration(Application::class.java) @TestConfigurationsProvider diff --git a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetryTest.kt b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetryTest.kt index 45ff1eff77..b0ee3395f1 100644 --- a/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetryTest.kt +++ b/dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/internal/metrics/MethodCalledTelemetryTest.kt @@ -21,6 +21,7 @@ import com.datadog.android.internal.time.TimeProvider import com.datadog.tools.unit.extensions.TestConfigurationExtension import fr.xgouchet.elmyr.Forge import fr.xgouchet.elmyr.annotation.FloatForgery +import fr.xgouchet.elmyr.annotation.LongForgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.junit5.ForgeExtension import org.assertj.core.api.Assertions.assertThat @@ -89,7 +90,11 @@ internal class MethodCalledTelemetryTest { @FloatForgery(min = 0.1f, max = 100f) private var fakeCreationSampleRate: Float = 0.1f - private var fakeStartTime: Long = 0 + @LongForgery(min = 0L) + private var fakeStartTimeNs: Long = 0L + + @LongForgery(min = 0L) + private var fakeElapsedTimeNs: Long = 0L private var fakeStatus: Boolean = false @@ -109,7 +114,10 @@ internal class MethodCalledTelemetryTest { whenever(mockDeviceInfo.osVersion).thenReturn(fakeOsVersion) whenever(mockDeviceInfo.deviceBuildId).thenReturn(fakeOsBuild) - fakeStartTime = System.nanoTime() + whenever(mockTimeProvider.getDeviceElapsedTimeNs()) + .thenReturn(fakeStartTimeNs) + .thenReturn(fakeStartTimeNs + fakeElapsedTimeNs) + testedMethodCalledTelemetry = MethodCalledTelemetry( internalLogger = mockInternalLogger, operationName = fakeOperationName, @@ -141,7 +149,7 @@ internal class MethodCalledTelemetryTest { verify(mockInternalLogger).logMetric(any(), mapCaptor.capture(), eq(100.0f), eq(fakeCreationSampleRate)) val executionTime = mapCaptor.firstValue[EXECUTION_TIME] as Long - assertThat(executionTime).isLessThan(System.nanoTime() - fakeStartTime) + assertThat(executionTime).isEqualTo(fakeElapsedTimeNs) } @Test diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt index ff7c37e082..544370a11c 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/RumFeatureTest.kt @@ -25,7 +25,6 @@ import com.datadog.android.event.EventMapper import com.datadog.android.event.MapperSerializer import com.datadog.android.internal.flags.RumFlagEvaluationMessage import com.datadog.android.internal.telemetry.InternalTelemetryEvent -import com.datadog.android.internal.time.DefaultTimeProvider import com.datadog.android.internal.time.TimeProvider import com.datadog.android.rum.GlobalRumMonitor import com.datadog.android.rum.RumErrorSource diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumRawEventExt.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumRawEventExt.kt index d2b430bae5..6efbdaecb0 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumRawEventExt.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumRawEventExt.kt @@ -42,12 +42,16 @@ internal fun Forge.stopViewEvent(): RumRawEvent.StopView { ) } -internal fun Forge.startActionEvent(continuous: Boolean? = null): RumRawEvent.StartAction { +internal fun Forge.startActionEvent( + continuous: Boolean? = null, + eventTime: Time = Time() +): RumRawEvent.StartAction { return RumRawEvent.StartAction( type = aValueFrom(RumActionType::class.java), name = anAlphabeticalString(), waitForStop = continuous ?: aBool(), - attributes = exhaustiveAttributes() + attributes = exhaustiveAttributes(), + eventTime = eventTime ) } diff --git a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeTest.kt b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeTest.kt index f50d92d98c..c07a0715ee 100644 --- a/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeTest.kt +++ b/features/dd-sdk-android-rum/src/test/kotlin/com/datadog/android/rum/internal/domain/scope/RumSessionScopeTest.kt @@ -16,12 +16,12 @@ import com.datadog.android.api.storage.DataWriter import com.datadog.android.api.storage.NoOpDataWriter import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.internal.net.FirstPartyHostHeaderTypeResolver -import com.datadog.android.internal.time.DefaultTimeProvider -import com.datadog.android.internal.time.TimeProvider +import com.datadog.tools.unit.stub.StubTimeProvider import com.datadog.android.rum.RumSessionListener import com.datadog.android.rum.RumSessionType import com.datadog.android.rum.internal.domain.InfoProvider import com.datadog.android.rum.internal.domain.RumContext +import com.datadog.android.rum.internal.domain.Time import com.datadog.android.rum.internal.domain.accessibility.AccessibilitySnapshotManager import com.datadog.android.rum.internal.domain.battery.BatteryInfo import com.datadog.android.rum.internal.domain.display.DisplayInfo @@ -165,14 +165,13 @@ internal class RumSessionScopeTest { private var fakeRumSessionType: RumSessionType? = null - lateinit var realTimeProvider: TimeProvider + private lateinit var stubTimeProvider: StubTimeProvider @BeforeEach fun `set up`(forge: Forge) { + stubTimeProvider = StubTimeProvider(elapsedTimeNs = TEST_INACTIVITY_NS + 1) fakeInitialViewEvent = forge.startViewEvent() - realTimeProvider = DefaultTimeProvider() - whenever(mockParentScope.getRumContext()) doReturn fakeParentContext whenever(mockChildScope.handleEvent(any(), any(), any(), any())) doReturn mockChildScope whenever(mockSdkCore.getFeature(Feature.SESSION_REPLAY_FEATURE_NAME)) doReturn @@ -591,7 +590,7 @@ internal class RumSessionScopeTest { val initialContext = testedScope.getRumContext() // When - Thread.sleep(TEST_INACTIVITY_MS) + advanceTimeByMs(TEST_INACTIVITY_MS) val result = testedScope.handleEvent(mock(), fakeDatadogContext, mockEventWriteScope, mockWriter) val context = testedScope.getRumContext() @@ -612,7 +611,7 @@ internal class RumSessionScopeTest { val initialContext = testedScope.getRumContext() // When - Thread.sleep(TEST_INACTIVITY_MS) + advanceTimeByMs(TEST_INACTIVITY_MS) val result = testedScope.handleEvent(mock(), fakeDatadogContext, mockEventWriteScope, mockWriter) val context = testedScope.getRumContext() @@ -632,7 +631,7 @@ internal class RumSessionScopeTest { val initialContext = testedScope.getRumContext() // When - Thread.sleep(TEST_INACTIVITY_MS) + advanceTimeByMs(TEST_INACTIVITY_MS) val result = testedScope.handleEvent(forge.startActionEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) val context = testedScope.getRumContext() @@ -655,7 +654,7 @@ internal class RumSessionScopeTest { val initialContext = testedScope.getRumContext() // When - Thread.sleep(TEST_INACTIVITY_MS) + advanceTimeByMs(TEST_INACTIVITY_MS) val result = testedScope.handleEvent(forge.startViewEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) val context = testedScope.getRumContext() @@ -679,7 +678,7 @@ internal class RumSessionScopeTest { val initialContext = testedScope.getRumContext() // When - Thread.sleep(TEST_INACTIVITY_MS) + advanceTimeByMs(TEST_INACTIVITY_MS) val result = testedScope.handleEvent(forge.startResourceEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) val context = testedScope.getRumContext() @@ -703,7 +702,7 @@ internal class RumSessionScopeTest { val initialContext = testedScope.getRumContext() // When - Thread.sleep(TEST_INACTIVITY_MS) + advanceTimeByMs(TEST_INACTIVITY_MS) val result = testedScope.handleEvent(forge.addErrorEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) val context = testedScope.getRumContext() @@ -726,7 +725,7 @@ internal class RumSessionScopeTest { val initialContext = testedScope.getRumContext() // When - Thread.sleep(TEST_INACTIVITY_MS) + advanceTimeByMs(TEST_INACTIVITY_MS) val result = testedScope.handleEvent(forge.startResourceEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) val context = testedScope.getRumContext() @@ -748,7 +747,7 @@ internal class RumSessionScopeTest { val initialContext = testedScope.getRumContext() // When - Thread.sleep(TEST_INACTIVITY_MS) + advanceTimeByMs(TEST_INACTIVITY_MS) val result = testedScope.handleEvent(forge.addErrorEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) val context = testedScope.getRumContext() @@ -764,16 +763,26 @@ internal class RumSessionScopeTest { forge: Forge ) { // Given - testedScope.handleEvent(forge.startViewEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) + testedScope.handleEvent( + forge.startViewEvent(eventTime = currentFakeTime()), + fakeDatadogContext, + mockEventWriteScope, + mockWriter + ) val initialContext = testedScope.getRumContext() val repeatCount = (TEST_MAX_DURATION_MS / TEST_SLEEP_MS) + 1 repeat(repeatCount.toInt()) { - Thread.sleep(TEST_SLEEP_MS) - testedScope.handleEvent(forge.startActionEvent(false), fakeDatadogContext, mockEventWriteScope, mockWriter) + advanceTimeByMs(TEST_SLEEP_MS) + testedScope.handleEvent( + forge.startActionEvent(continuous = false, eventTime = currentFakeTime()), + fakeDatadogContext, + mockEventWriteScope, + mockWriter + ) } // When - Thread.sleep(TEST_SLEEP_MS) + advanceTimeByMs(TEST_SLEEP_MS) val result = testedScope.handleEvent(mock(), fakeDatadogContext, mockEventWriteScope, mockWriter) val context = testedScope.getRumContext() @@ -791,18 +800,32 @@ internal class RumSessionScopeTest { forge: Forge ) { // Given - testedScope.handleEvent(forge.startViewEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) + testedScope.handleEvent( + forge.startViewEvent(eventTime = currentFakeTime()), + fakeDatadogContext, + mockEventWriteScope, + mockWriter + ) val initialContext = testedScope.getRumContext() val repeatCount = (TEST_MAX_DURATION_MS / TEST_SLEEP_MS) + 1 repeat(repeatCount.toInt()) { - Thread.sleep(TEST_SLEEP_MS) - testedScope.handleEvent(forge.startActionEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) + advanceTimeByMs(TEST_SLEEP_MS) + testedScope.handleEvent( + forge.startActionEvent(eventTime = currentFakeTime()), + fakeDatadogContext, + mockEventWriteScope, + mockWriter + ) } // When - Thread.sleep(TEST_SLEEP_MS) - val result = - testedScope.handleEvent(forge.startActionEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) + advanceTimeByMs(TEST_SLEEP_MS) + val result = testedScope.handleEvent( + forge.startActionEvent(eventTime = currentFakeTime()), + fakeDatadogContext, + mockEventWriteScope, + mockWriter + ) val context = testedScope.getRumContext() // Then @@ -819,18 +842,32 @@ internal class RumSessionScopeTest { forge: Forge ) { // Given - testedScope.handleEvent(forge.startViewEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) + testedScope.handleEvent( + forge.startViewEvent(eventTime = currentFakeTime()), + fakeDatadogContext, + mockEventWriteScope, + mockWriter + ) val initialContext = testedScope.getRumContext() val repeatCount = (TEST_MAX_DURATION_MS / TEST_SLEEP_MS) + 1 repeat(repeatCount.toInt()) { - Thread.sleep(TEST_SLEEP_MS) - testedScope.handleEvent(forge.startActionEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) + advanceTimeByMs(TEST_SLEEP_MS) + testedScope.handleEvent( + forge.startActionEvent(eventTime = currentFakeTime()), + fakeDatadogContext, + mockEventWriteScope, + mockWriter + ) } // When - Thread.sleep(TEST_SLEEP_MS) - val result = - testedScope.handleEvent(forge.startViewEvent(), fakeDatadogContext, mockEventWriteScope, mockWriter) + advanceTimeByMs(TEST_SLEEP_MS) + val result = testedScope.handleEvent( + forge.startViewEvent(eventTime = currentFakeTime()), + fakeDatadogContext, + mockEventWriteScope, + mockWriter + ) val context = testedScope.getRumContext() // Then @@ -934,12 +971,12 @@ internal class RumSessionScopeTest { val initialContext = testedScope.getRumContext() val repeatCount = (TEST_MAX_DURATION_MS / TEST_SLEEP_MS) + 1 repeat(repeatCount.toInt()) { - Thread.sleep(TEST_SLEEP_MS) + advanceTimeByMs(TEST_SLEEP_MS) testedScope.handleEvent(forge.startActionEvent(false), fakeDatadogContext, mockEventWriteScope, mockWriter) } // When - Thread.sleep(TEST_MAX_DURATION_MS) + advanceTimeByMs(TEST_MAX_DURATION_MS) val newEvent = forge.startViewEvent() val result = testedScope.handleEvent(newEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) val context = testedScope.getRumContext() @@ -963,7 +1000,7 @@ internal class RumSessionScopeTest { val initialContext = testedScope.getRumContext() // When - Thread.sleep(TEST_INACTIVITY_MS) + advanceTimeByMs(TEST_INACTIVITY_MS) val newEvent = forge.startViewEvent() val result = testedScope.handleEvent(newEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) val context = testedScope.getRumContext() @@ -1012,12 +1049,12 @@ internal class RumSessionScopeTest { val initialContext = testedScope.getRumContext() val repeatCount = (TEST_MAX_DURATION_MS / TEST_SLEEP_MS) + 1 repeat(repeatCount.toInt()) { - Thread.sleep(TEST_SLEEP_MS) + advanceTimeByMs(TEST_SLEEP_MS) testedScope.handleEvent(forge.startActionEvent(false), fakeDatadogContext, mockEventWriteScope, mockWriter) } // When - Thread.sleep(TEST_MAX_DURATION_MS) + advanceTimeByMs(TEST_MAX_DURATION_MS) val newEvent = forge.startViewEvent() val result = testedScope.handleEvent(newEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) val context = testedScope.getRumContext() @@ -1042,7 +1079,7 @@ internal class RumSessionScopeTest { val initialContext = testedScope.getRumContext() // When - Thread.sleep(TEST_INACTIVITY_MS) + advanceTimeByMs(TEST_INACTIVITY_MS) val newEvent = forge.startViewEvent() val result = testedScope.handleEvent(newEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) val context = testedScope.getRumContext() @@ -1173,7 +1210,7 @@ internal class RumSessionScopeTest { val firstSessionId = testedScope.getRumContext().sessionId // When - Thread.sleep(TEST_MAX_DURATION_MS) + advanceTimeByMs(TEST_MAX_DURATION_MS) val newEvent = forge.startViewEvent() testedScope.handleEvent(newEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) val secondSessionId = testedScope.getRumContext().sessionId @@ -1210,7 +1247,7 @@ internal class RumSessionScopeTest { val firstSessionId = testedScope.getRumContext().sessionId // When - Thread.sleep(TEST_INACTIVITY_MS) + advanceTimeByMs(TEST_INACTIVITY_MS) val newEvent = forge.startViewEvent() testedScope.handleEvent(newEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) val secondSessionId = testedScope.getRumContext().sessionId @@ -1299,7 +1336,7 @@ internal class RumSessionScopeTest { val firstSessionId = testedScope.getRumContext().sessionId // When - Thread.sleep(TEST_MAX_DURATION_MS) + advanceTimeByMs(TEST_MAX_DURATION_MS) val newEvent = forge.startViewEvent() testedScope.handleEvent(newEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) val secondSessionId = testedScope.getRumContext().sessionId @@ -1338,7 +1375,7 @@ internal class RumSessionScopeTest { val firstSessionId = testedScope.getRumContext().sessionId // When - Thread.sleep(TEST_INACTIVITY_MS) + advanceTimeByMs(TEST_INACTIVITY_MS) val newEvent = forge.startViewEvent() testedScope.handleEvent(newEvent, fakeDatadogContext, mockEventWriteScope, mockWriter) val secondSessionId = testedScope.getRumContext().sessionId @@ -1530,11 +1567,21 @@ internal class RumSessionScopeTest { // region Internal + private fun advanceTimeByMs(ms: Long) { + stubTimeProvider.elapsedTimeNs += TimeUnit.MILLISECONDS.toNanos(ms) + } + + private fun currentFakeTime(): Time { + return Time( + timestamp = stubTimeProvider.deviceTimestampMs, + nanoTime = stubTimeProvider.elapsedTimeNs + ) + } + private fun initializeTestedScope( sampleRate: Float = 100f, withMockChildScope: Boolean = true, backgroundTrackingEnabled: Boolean? = null, - timeProvider: TimeProvider = DefaultTimeProvider() ) { testedScope = RumSessionScope( parentScope = mockParentScope, @@ -1560,7 +1607,7 @@ internal class RumSessionScopeTest { batteryInfoProvider = mockBatteryInfoProvider, displayInfoProvider = mockDisplayInfoProvider, rumAppStartupTelemetryReporter = mockRumAppStartupTelemetryReporter, - timeProvider = timeProvider + timeProvider = stubTimeProvider ) if (withMockChildScope) { diff --git a/tools/unit/build.gradle.kts b/tools/unit/build.gradle.kts index bab94aa56e..a14039a77b 100644 --- a/tools/unit/build.gradle.kts +++ b/tools/unit/build.gradle.kts @@ -68,6 +68,7 @@ dependencies { implementation(libs.bundles.testTools) implementation(libs.gson) implementation(libs.mockitoKotlin) + implementation(project(":dd-sdk-android-internal")) testImplementation(libs.bundles.jUnit5) testImplementation(libs.bundles.testTools) diff --git a/tools/unit/src/main/kotlin/com/datadog/tools/unit/stub/StubTimeProvider.kt b/tools/unit/src/main/kotlin/com/datadog/tools/unit/stub/StubTimeProvider.kt new file mode 100644 index 0000000000..98bab52385 --- /dev/null +++ b/tools/unit/src/main/kotlin/com/datadog/tools/unit/stub/StubTimeProvider.kt @@ -0,0 +1,37 @@ +/* + * 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.tools.unit.stub + +import com.datadog.android.internal.time.TimeProvider + +/** + * A stub implementation of [TimeProvider] for testing purposes. + * + * @property elapsedTimeNs The elapsed time in nanoseconds (like `System.nanoTime`). Defaults to `0`. + * @property deviceTimestampMs The device wall clock timestamp in milliseconds. Defaults to `0`. + * @property serverTimestampMs The server wall clock timestamp in milliseconds. Defaults to `0`. + * @property serverOffsetNs The server time offset in nanoseconds. Defaults to `0`. + * @property serverOffsetMs The server time offset in milliseconds. Defaults to `0`. + */ +class StubTimeProvider( + var elapsedTimeNs: Long = 0L, + var deviceTimestampMs: Long = 0L, + var serverTimestampMs: Long = 0L, + var serverOffsetNs: Long = 0L, + var serverOffsetMs: Long = 0L +) : TimeProvider { + + override fun getDeviceTimestamp(): Long = deviceTimestampMs + + override fun getServerTimestamp(): Long = serverTimestampMs + + override fun getDeviceElapsedTimeNs(): Long = elapsedTimeNs + + override fun getServerOffsetNanos(): Long = serverOffsetNs + + override fun getServerOffsetMillis(): Long = serverOffsetMs +}