diff --git a/eventproducer/build.gradle.kts b/eventproducer/build.gradle.kts index 5915f1d9..a7f4426f 100644 --- a/eventproducer/build.gradle.kts +++ b/eventproducer/build.gradle.kts @@ -27,7 +27,7 @@ dependencies { implementation(libs.tickaroo.annotation) implementation(libs.tickaroo.core) implementation(libs.tickaroo.retrofitConverter) - implementation(libs.truetime) + implementation(libs.tidal.networktime.singletons) ksp(libs.dagger.compiler) kapt(libs.room.compiler) diff --git a/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/EventProducer.kt b/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/EventProducer.kt index 8bea9464..ac5e02cd 100644 --- a/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/EventProducer.kt +++ b/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/EventProducer.kt @@ -1,6 +1,7 @@ package com.tidal.sdk.eventproducer import android.content.Context +import com.tidal.networktime.SNTPClient import com.tidal.sdk.auth.CredentialsProvider import com.tidal.sdk.eventproducer.di.DaggerEventsComponent import com.tidal.sdk.eventproducer.model.EventsConfig @@ -43,6 +44,9 @@ class EventProducer private constructor(coroutineScope: CoroutineScope) { @Inject internal lateinit var monitoringScheduler: MonitoringScheduler + @Inject + internal lateinit var sntpClient: SNTPClient + internal fun startOutage() { _outageState.value = OutageState.Outage() } @@ -91,6 +95,7 @@ class EventProducer private constructor(coroutineScope: CoroutineScope) { ) EventProducer(coroutineScope).also { eventsComponent.inject(it) + it.sntpClient.enableSynchronization() coroutineScope.launch(Dispatchers.IO) { it.scheduler.scheduleBatchAndSend() } coroutineScope.launch(Dispatchers.IO) { it.monitoringScheduler.scheduleSendMonitoringInfo() diff --git a/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/Submitter.kt b/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/Submitter.kt index a8ead6bf..fca0d88c 100644 --- a/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/Submitter.kt +++ b/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/Submitter.kt @@ -8,7 +8,7 @@ import com.tidal.sdk.eventproducer.model.MonitoringEventType import com.tidal.sdk.eventproducer.repository.EventsRepository import com.tidal.sdk.eventproducer.utils.EventSizeValidator import com.tidal.sdk.eventproducer.utils.HeadersUtils -import java.util.* +import java.util.UUID import javax.inject.Inject import javax.inject.Singleton @@ -20,7 +20,7 @@ internal class Submitter @Inject constructor( private val headersUtils: HeadersUtils, ) { - fun sendEvent( + suspend fun sendEvent( eventName: String, consentCategory: ConsentCategory, payload: String, @@ -50,7 +50,7 @@ internal class Submitter @Inject constructor( ) } - private fun createEvent( + private suspend fun createEvent( eventName: String, consentCategory: ConsentCategory, payload: String, diff --git a/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/di/UtilsModule.kt b/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/di/UtilsModule.kt index 90367db3..64ebe579 100644 --- a/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/di/UtilsModule.kt +++ b/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/di/UtilsModule.kt @@ -1,9 +1,11 @@ package com.tidal.sdk.eventproducer.di +import com.tidal.networktime.NTPServer +import com.tidal.networktime.SNTPClient +import com.tidal.networktime.singletons.singleton import com.tidal.sdk.auth.CredentialsProvider import com.tidal.sdk.eventproducer.model.EventsConfigProvider import com.tidal.sdk.eventproducer.utils.HeadersUtils -import com.tidal.sdk.eventproducer.utils.TrueTimeWrapper import dagger.Module import dagger.Provides import dagger.Reusable @@ -15,10 +17,10 @@ internal class UtilsModule { fun provideHeadersUtils( configProvider: EventsConfigProvider, credentialsProvider: CredentialsProvider, - trueTimeWrapper: TrueTimeWrapper, - ) = HeadersUtils(configProvider.config.appVersion, credentialsProvider, trueTimeWrapper) + sntpClient: SNTPClient, + ) = HeadersUtils(configProvider.config.appVersion, credentialsProvider, sntpClient) @Provides @Reusable - fun provideTrueTimeWrapper() = TrueTimeWrapper() + fun provideSntpClient() = SNTPClient(NTPServer("time.google.com")).singleton } diff --git a/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/repository/RepositoryHelper.kt b/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/repository/RepositoryHelper.kt index 0f193ded..dc7ee8e9 100644 --- a/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/repository/RepositoryHelper.kt +++ b/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/repository/RepositoryHelper.kt @@ -28,7 +28,7 @@ internal class RepositoryHelper @Inject constructor( return credentials?.level == Credentials.Level.USER } - fun getMonitoringEvent(monitoringInfo: MonitoringInfo): Event { + suspend fun getMonitoringEvent(monitoringInfo: MonitoringInfo): Event { val monitoringEventPayload = getMonitoringEventPayload(monitoringInfo) val headers = headersUtils.getDefaultHeaders(ConsentCategory.NECESSARY, true) return Event( diff --git a/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/utils/HeadersUtils.kt b/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/utils/HeadersUtils.kt index 0424e162..c141e910 100644 --- a/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/utils/HeadersUtils.kt +++ b/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/utils/HeadersUtils.kt @@ -1,6 +1,7 @@ package com.tidal.sdk.eventproducer.utils import android.os.Build +import com.tidal.networktime.SNTPClient import com.tidal.sdk.auth.CredentialsProvider import com.tidal.sdk.eventproducer.model.ConsentCategory import javax.inject.Inject @@ -19,20 +20,20 @@ internal const val DEVICE_VENDOR_KEY = "device-vendor" internal class HeadersUtils @Inject constructor( private val appVersion: String, private val credentialsProvider: CredentialsProvider, - private val trueTimeWrapper: TrueTimeWrapper, + private val sntpClient: SNTPClient, ) { fun getEventHeaders( defaultHeaders: Map, suppliedHeaders: Map, ): Map = defaultHeaders + suppliedHeaders - fun getDefaultHeaders( + suspend fun getDefaultHeaders( consentCategory: ConsentCategory, isMonitoringEvent: Boolean, ): Map { val deviceModel = Build.MODEL val deviceVendor = Build.MANUFACTURER - val sentTimestamp = trueTimeWrapper.currentTimeMillis.toString() + val sentTimestamp = sntpClient.blockingEpochTime().inWholeMilliseconds.toString() val osName = "Android" val osVersion = Build.VERSION.SDK_INT.toString() val headers = mutableMapOf() diff --git a/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/utils/TrueTimeWrapper.kt b/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/utils/TrueTimeWrapper.kt deleted file mode 100644 index 9fd6a5d7..00000000 --- a/eventproducer/src/main/kotlin/com/tidal/sdk/eventproducer/utils/TrueTimeWrapper.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.tidal.sdk.eventproducer.utils - -import com.instacart.library.truetime.TrueTime - -class TrueTimeWrapper { - - val currentTimeMillis: Long - get() = try { - TrueTime.now().time - } catch (_: Throwable) { - System.currentTimeMillis() - } -} diff --git a/eventproducer/src/test/kotlin/com/tidal/eventproducer/repository/EventsRepositoryTest.kt b/eventproducer/src/test/kotlin/com/tidal/eventproducer/repository/EventsRepositoryTest.kt index 1a5dab7b..ed7ce1b1 100644 --- a/eventproducer/src/test/kotlin/com/tidal/eventproducer/repository/EventsRepositoryTest.kt +++ b/eventproducer/src/test/kotlin/com/tidal/eventproducer/repository/EventsRepositoryTest.kt @@ -24,6 +24,7 @@ import io.mockk.mockkObject import io.mockk.unmockkObject import io.mockk.verify import java.util.concurrent.TimeoutException +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -290,7 +291,9 @@ class EventsRepositoryTest { SendMessageBatchResult(null), ) every { monitoringDataSource.getMonitoringInfo() } returns monitoringInfo - every { repositoryHelper.getMonitoringEvent(monitoringInfo) } returns monitoringEvent + every { + runBlocking { repositoryHelper.getMonitoringEvent(monitoringInfo) } + } returns monitoringEvent every { repositoryHelper.isUserLoggedIn() } returns true every { sqsParametersConverter.getSendEventsParameters(listOf(monitoringEvent)) diff --git a/eventproducer/src/test/kotlin/com/tidal/eventproducer/utils/HeadersUtilsTest.kt b/eventproducer/src/test/kotlin/com/tidal/eventproducer/utils/HeadersUtilsTest.kt index cb473c2f..189ed264 100644 --- a/eventproducer/src/test/kotlin/com/tidal/eventproducer/utils/HeadersUtilsTest.kt +++ b/eventproducer/src/test/kotlin/com/tidal/eventproducer/utils/HeadersUtilsTest.kt @@ -3,11 +3,11 @@ package com.tidal.eventproducer.utils import assertk.assertThat import assertk.assertions.containsAll import com.tidal.eventproducer.fakes.FakeCredentialsProvider +import com.tidal.networktime.SNTPClient import com.tidal.sdk.eventproducer.utils.APP_VERSION_KEY import com.tidal.sdk.eventproducer.utils.CLIENT_ID_KEY import com.tidal.sdk.eventproducer.utils.HeadersUtils import com.tidal.sdk.eventproducer.utils.OS_NAME_KEY -import com.tidal.sdk.eventproducer.utils.TrueTimeWrapper import io.mockk.mockk import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test @@ -17,7 +17,7 @@ class HeadersUtilsTest { private val headerUtils = HeadersUtils( "", FakeCredentialsProvider(), - mockk(), + mockk(), ) @Test diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2da8135c..570944f1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,6 +21,7 @@ slf4j-api = "2.0.12" tickaroo = "0.8.13" tidal-androidx-media = "1.1.1.2" plugins-tidal = "unspecified" +tidal-networktime = "1.1.0" [libraries] plugin-kotlin-android = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } @@ -55,7 +56,6 @@ gson = "com.google.code.gson:gson:2.9.0" kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } kotlin-logging = { module = "io.github.oshai:kotlin-logging-jvm", version.ref = "kotlin-logging" } slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j-api" } -truetime = "com.github.instacart.truetime-android:library-extension-rx:3.5" # Moshi moshi = { module = "com.squareup.moshi:moshi", version.ref ="moshi"} @@ -112,6 +112,9 @@ tidal-exoPlayer-hls = { module = "com.tidal.androidx.media3:media3-exoplayer-hls tidal-exoPlayer-extension-flac = { module = "com.tidal.androidx.media3:media3-flac", version.ref = "tidal-androidx-media" } tidal-exoPlayer-extension-okhttp = { module = "com.tidal.androidx.media3:media3-datasource-okhttp", version.ref = "tidal-androidx-media" } +tidal-networktime = { module = "com.tidal.networktime:networktime", version.ref = "tidal-networktime" } +tidal-networktime-singletons = { module = "com.tidal.networktime:networktime-singletons", version.ref = "tidal-networktime" } + # Security androidx-security-crypto = { module = "androidx.security:security-crypto", version = "1.0.0" } diff --git a/player/README.md b/player/README.md index 282c9d52..56b0f194 100644 --- a/player/README.md +++ b/player/README.md @@ -19,14 +19,7 @@ The Player module encapsulates the playback functionality of TIDAL media product ### Installation -1. We are using the [TrueTime library](https://github.com/instacart/truetime-android) internally, so you need to add the following to your repositories list: -```kotlin -maven { - url = uri("https://jitpack.io") -} -``` - -2. Add the dependency to your `build.gradle.kts` file. +1. Add the dependency to your `build.gradle.kts` file. ```kotlin dependencies { implementation("com.tidal.sdk:player:") @@ -37,7 +30,7 @@ The Player depends on the [Auth](https://github.com/tidal-music/tidal-sdk-androi Here's how to setup the Player and play a TIDAL track: -1. Initialise the Player which depends on a `CredentialsProvider` from the Auth module and an `EventSender` from the EventProducer module. +2. Initialise the Player which depends on a `CredentialsProvider` from the Auth module and an `EventSender` from the EventProducer module. ```kotlin val player = Player( application = application, @@ -46,7 +39,7 @@ val player = Player( ) ``` -2. Load and play a `MediaProduct` track. +3. Load and play a `MediaProduct` track. ```kotlin val mediaProduct = MediaProduct(ProductType.TRACK, "PRODUCT_ID") @@ -54,7 +47,7 @@ player.playbackEngine.load(mediaProduct) player.playbackEngine.play() ``` -3. _(Optional)_ Listen to [player events](https://github.com/tidal-music/tidal-sdk-android/blob/main/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/model/Event.kt). +4. _(Optional)_ Listen to [player events](https://github.com/tidal-music/tidal-sdk-android/blob/main/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/model/Event.kt). ```kotlin player.playbackEngine.events.onEach { Log.d(TAG, "Event=$it") diff --git a/player/apps/demo/build.gradle.kts b/player/apps/demo/build.gradle.kts index e7448ac3..de874fb4 100644 --- a/player/apps/demo/build.gradle.kts +++ b/player/apps/demo/build.gradle.kts @@ -35,5 +35,5 @@ dependencies { implementation(platform(libs.androidx.compose.bom)) implementation(libs.androidx.activity.compose) implementation(libs.androidx.compose.material3) - implementation(libs.truetime) + implementation(libs.tidal.networktime.singletons) } diff --git a/player/apps/demo/src/main/AndroidManifest.xml b/player/apps/demo/src/main/AndroidManifest.xml index 506fe124..c3175d41 100644 --- a/player/apps/demo/src/main/AndroidManifest.xml +++ b/player/apps/demo/src/main/AndroidManifest.xml @@ -3,7 +3,6 @@ package="com.tidal.sdk.player"> = AudioDownloadStatisticsEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -78,13 +78,13 @@ internal object EventFactoryModule { @IntoMap @EventFactoryKey(VideoDownloadStatistics.Payload::class) fun videoDownloadStatisticsEventFactory( - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, uuidWrapper: UUIDWrapper, userSupplier: UserSupplier, clientSupplier: ClientSupplier, videoDownloadStatisticsFactory: VideoDownloadStatistics.Factory, ): EventFactory = VideoDownloadStatisticsEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -96,13 +96,13 @@ internal object EventFactoryModule { @IntoMap @EventFactoryKey(DrmLicenseFetch.Payload::class) fun drmLicenseFetchEventFactory( - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, uuidWrapper: UUIDWrapper, userSupplier: UserSupplier, clientSupplier: ClientSupplier, drmLicenseFetchFactory: DrmLicenseFetch.Factory, ): EventFactory = DrmLicenseFetchEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -149,14 +149,14 @@ internal object EventFactoryModule { @IntoMap @EventFactoryKey(StreamingSessionStart.Payload::class) fun streamingSessionStartEventFactory( - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, uuidWrapper: UUIDWrapper, userSupplier: UserSupplier, clientSupplier: ClientSupplier, streamingSessionStartPayloadDecorator: StreamingSessionStartPayloadDecorator, streamingSessionStartFactory: StreamingSessionStart.Factory, ): EventFactory = StreamingSessionStartEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -169,13 +169,13 @@ internal object EventFactoryModule { @IntoMap @EventFactoryKey(StreamingSessionEnd.Payload::class) fun streamingSessionEndEventFactory( - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, uuidWrapper: UUIDWrapper, userSupplier: UserSupplier, clientSupplier: ClientSupplier, streamingSessionEndFactory: StreamingSessionEnd.Factory, ): EventFactory = StreamingSessionEndEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -187,13 +187,13 @@ internal object EventFactoryModule { @IntoMap @EventFactoryKey(PlaybackInfoFetch.Payload::class) fun playbackInfoFetchEventFactory( - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, uuidWrapper: UUIDWrapper, userSupplier: UserSupplier, clientSupplier: ClientSupplier, playbackInfoFetchFactory: PlaybackInfoFetch.Factory, ): EventFactory = PlaybackInfoFetchEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -205,13 +205,13 @@ internal object EventFactoryModule { @IntoMap @EventFactoryKey(UCPlaybackSession.Payload::class) fun ucPlaybackSessionEventFactory( - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, uuidWrapper: UUIDWrapper, userSupplier: UserSupplier, clientSupplier: ClientSupplier, ucPlaybackSessionFactory: UCPlaybackSession.Factory, ): EventFactory = UCPlaybackSessionEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -223,13 +223,13 @@ internal object EventFactoryModule { @IntoMap @EventFactoryKey(AudioPlaybackSession.Payload::class) fun audioPlaybackSessionEventFactory( - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, uuidWrapper: UUIDWrapper, userSupplier: UserSupplier, clientSupplier: ClientSupplier, audioPlaybackSessionFactory: AudioPlaybackSession.Factory, ): EventFactory = AudioPlaybackSessionEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -241,13 +241,13 @@ internal object EventFactoryModule { @IntoMap @EventFactoryKey(BroadcastPlaybackSession.Payload::class) fun broadcastPlaybackSessionEventFactory( - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, uuidWrapper: UUIDWrapper, userSupplier: UserSupplier, clientSupplier: ClientSupplier, broadcastPlaybackSessionFactory: BroadcastPlaybackSession.Factory, ): EventFactory = BroadcastPlaybackSessionEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -259,13 +259,13 @@ internal object EventFactoryModule { @IntoMap @EventFactoryKey(UCPlaybackStatistics.Payload::class) fun ucPlaybackStatisticsEventFactory( - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, uuidWrapper: UUIDWrapper, userSupplier: UserSupplier, clientSupplier: ClientSupplier, ucPlaybackStatisticsFactory: UCPlaybackStatistics.Factory, ): EventFactory = UCPlaybackStatisticsEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -277,13 +277,13 @@ internal object EventFactoryModule { @IntoMap @EventFactoryKey(VideoPlaybackSession.Payload::class) fun videoPlaybackSessionEventFactory( - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, uuidWrapper: UUIDWrapper, userSupplier: UserSupplier, clientSupplier: ClientSupplier, videoPlaybackSessionFactory: VideoPlaybackSession.Factory, ): EventFactory = VideoPlaybackSessionEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -295,13 +295,13 @@ internal object EventFactoryModule { @IntoMap @EventFactoryKey(AudioPlaybackStatistics.Payload::class) fun audioPlaybackStatisticsEventFactory( - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, uuidWrapper: UUIDWrapper, userSupplier: UserSupplier, clientSupplier: ClientSupplier, audioPlaybackStatisticsFactory: AudioPlaybackStatistics.Factory, ): EventFactory = AudioPlaybackStatisticsEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -313,13 +313,13 @@ internal object EventFactoryModule { @IntoMap @EventFactoryKey(BroadcastPlaybackStatistics.Payload::class) fun broadcastPlaybackStatisticsEventFactory( - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, uuidWrapper: UUIDWrapper, userSupplier: UserSupplier, clientSupplier: ClientSupplier, broadcastPlaybackStatisticsFactory: BroadcastPlaybackStatistics.Factory, ): EventFactory = BroadcastPlaybackStatisticsEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -331,13 +331,13 @@ internal object EventFactoryModule { @IntoMap @EventFactoryKey(VideoPlaybackStatistics.Payload::class) fun videoPlaybackStatisticsEventFactory( - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, uuidWrapper: UUIDWrapper, userSupplier: UserSupplier, clientSupplier: ClientSupplier, videoPlaybackStatisticsFactory: VideoPlaybackStatistics.Factory, ): EventFactory = VideoPlaybackStatisticsEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -349,13 +349,13 @@ internal object EventFactoryModule { @IntoMap @EventFactoryKey(NotStartedPlaybackStatistics.Payload::class) fun errorPlaybackStatisticsEventFactory( - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, uuidWrapper: UUIDWrapper, userSupplier: UserSupplier, clientSupplier: ClientSupplier, notStartedPlaybackStatisticsFactory: NotStartedPlaybackStatistics.Factory, ): EventFactory = NotStartedPlaybackStatisticsEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, diff --git a/player/events/src/test/kotlin/com/tidal/sdk/player/events/EventReporterModuleRootTest.kt b/player/events/src/test/kotlin/com/tidal/sdk/player/events/EventReporterModuleRootTest.kt index 2059a6fd..d109aff8 100644 --- a/player/events/src/test/kotlin/com/tidal/sdk/player/events/EventReporterModuleRootTest.kt +++ b/player/events/src/test/kotlin/com/tidal/sdk/player/events/EventReporterModuleRootTest.kt @@ -5,10 +5,10 @@ import android.net.ConnectivityManager import assertk.assertThat import assertk.assertions.isSameAs import com.google.gson.Gson +import com.tidal.networktime.SNTPClient import com.tidal.sdk.eventproducer.EventSender import com.tidal.sdk.player.common.Configuration import com.tidal.sdk.player.common.UUIDWrapper -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.di.DefaultEventReporterComponent import kotlinx.coroutines.CoroutineScope import okhttp3.OkHttpClient @@ -29,7 +29,7 @@ internal class EventReporterModuleRootTest { private val okHttpClient = mock() private val gson = mock() private val uuidWrapper = mock() - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val eventSender = mock() private val configuration = mock() private val coroutineScope = mock() @@ -42,7 +42,7 @@ internal class EventReporterModuleRootTest { okHttpClient, gson, uuidWrapper, - trueTimeWrapper, + sntpClient, eventSender, coroutineScope ) @@ -69,7 +69,7 @@ internal class EventReporterModuleRootTest { okHttpClient, gson, uuidWrapper, - trueTimeWrapper, + sntpClient, eventSender, configuration, ) @@ -91,7 +91,7 @@ internal class EventReporterModuleRootTest { okHttpClient, gson, uuidWrapper, - trueTimeWrapper, + sntpClient, eventSender, coroutineScope, ) @@ -109,7 +109,7 @@ internal class EventReporterModuleRootTest { okHttpClient, gson, uuidWrapper, - trueTimeWrapper, + sntpClient, eventSender, coroutineScope, ) diff --git a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/AudioDownloadStatisticsEventFactoryTest.kt b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/AudioDownloadStatisticsEventFactoryTest.kt index 3a695016..18525daa 100644 --- a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/AudioDownloadStatisticsEventFactoryTest.kt +++ b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/AudioDownloadStatisticsEventFactoryTest.kt @@ -2,17 +2,19 @@ package com.tidal.sdk.player.events.converter import assertk.assertThat import assertk.assertions.isSameAs +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.UUIDWrapper -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.ClientSupplier import com.tidal.sdk.player.events.UserSupplier import com.tidal.sdk.player.events.model.AudioDownloadStatistics import com.tidal.sdk.player.events.model.Client import com.tidal.sdk.player.events.model.User import java.util.UUID +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions @@ -20,13 +22,13 @@ import org.mockito.kotlin.whenever internal class AudioDownloadStatisticsEventFactoryTest { - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val uuidWrapper = mock() private val userSupplier = mock() private val clientSupplier = mock() private val audioDownloadStatisticsFactory = mock() private val audioDownloadStatisticsEventFactory = AudioDownloadStatisticsEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -35,7 +37,7 @@ internal class AudioDownloadStatisticsEventFactoryTest { @AfterEach fun afterEach() = verifyNoMoreInteractions( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -44,8 +46,8 @@ internal class AudioDownloadStatisticsEventFactoryTest { @Test fun invoke() = runBlocking { - val currentTimeMillis = -3L - whenever(trueTimeWrapper.currentTimeMillis).thenReturn(currentTimeMillis) + val currentTime = 3.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime val randomUUID = mock() whenever(uuidWrapper.randomUUID).thenReturn(randomUUID) val user = mock() @@ -55,17 +57,23 @@ internal class AudioDownloadStatisticsEventFactoryTest { val src = mock() val expected = mock() whenever( - audioDownloadStatisticsFactory.create(currentTimeMillis, randomUUID, user, client, src), + audioDownloadStatisticsFactory.create( + currentTime.inWholeMilliseconds, + randomUUID, + user, + client, + src, + ), ).thenReturn(expected) val actual = audioDownloadStatisticsEventFactory(src) - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() verify(uuidWrapper).randomUUID verify(userSupplier)() verify(clientSupplier)() verify(audioDownloadStatisticsFactory).create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, diff --git a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/AudioPlaybackSessionEventFactoryTest.kt b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/AudioPlaybackSessionEventFactoryTest.kt index 3fad30bd..092378b0 100644 --- a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/AudioPlaybackSessionEventFactoryTest.kt +++ b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/AudioPlaybackSessionEventFactoryTest.kt @@ -2,17 +2,19 @@ package com.tidal.sdk.player.events.converter import assertk.assertThat import assertk.assertions.isSameAs +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.UUIDWrapper -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.ClientSupplier import com.tidal.sdk.player.events.UserSupplier import com.tidal.sdk.player.events.model.AudioPlaybackSession import com.tidal.sdk.player.events.model.Client import com.tidal.sdk.player.events.model.User import java.util.UUID +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions @@ -20,13 +22,13 @@ import org.mockito.kotlin.whenever internal class AudioPlaybackSessionEventFactoryTest { - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val uuidWrapper = mock() private val userSupplier = mock() private val clientSupplier = mock() private val audioPlaybackSessionFactory = mock() private val audioPlaybackSessionEventFactory = AudioPlaybackSessionEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -35,7 +37,7 @@ internal class AudioPlaybackSessionEventFactoryTest { @AfterEach fun afterEach() = verifyNoMoreInteractions( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -44,8 +46,8 @@ internal class AudioPlaybackSessionEventFactoryTest { @Test fun invoke() = runBlocking { - val currentTimeMillis = -3L - whenever(trueTimeWrapper.currentTimeMillis).thenReturn(currentTimeMillis) + val currentTime = 3.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime val randomUUID = mock() whenever(uuidWrapper.randomUUID).thenReturn(randomUUID) val user = mock() @@ -56,7 +58,7 @@ internal class AudioPlaybackSessionEventFactoryTest { val expected = mock() whenever( audioPlaybackSessionFactory.create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, @@ -66,12 +68,12 @@ internal class AudioPlaybackSessionEventFactoryTest { val actual = audioPlaybackSessionEventFactory(payload) - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() verify(uuidWrapper).randomUUID verify(userSupplier)() verify(clientSupplier)() verify(audioPlaybackSessionFactory).create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, diff --git a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/AudioPlaybackStatisticsEventFactoryTest.kt b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/AudioPlaybackStatisticsEventFactoryTest.kt index 4986f5da..0621f9c4 100644 --- a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/AudioPlaybackStatisticsEventFactoryTest.kt +++ b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/AudioPlaybackStatisticsEventFactoryTest.kt @@ -2,17 +2,19 @@ package com.tidal.sdk.player.events.converter import assertk.assertThat import assertk.assertions.isSameAs +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.UUIDWrapper -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.ClientSupplier import com.tidal.sdk.player.events.UserSupplier import com.tidal.sdk.player.events.model.AudioPlaybackStatistics import com.tidal.sdk.player.events.model.Client import com.tidal.sdk.player.events.model.User import java.util.UUID +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions @@ -20,13 +22,13 @@ import org.mockito.kotlin.whenever internal class AudioPlaybackStatisticsEventFactoryTest { - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val uuidWrapper = mock() private val userSupplier = mock() private val clientSupplier = mock() private val audioPlaybackStatisticsFactory = mock() private val audioPlaybackStatisticsEventFactory = AudioPlaybackStatisticsEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -35,7 +37,7 @@ internal class AudioPlaybackStatisticsEventFactoryTest { @AfterEach fun afterEach() = verifyNoMoreInteractions( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -44,8 +46,8 @@ internal class AudioPlaybackStatisticsEventFactoryTest { @Test fun invoke() = runBlocking { - val currentTimeMillis = -3L - whenever(trueTimeWrapper.currentTimeMillis).thenReturn(currentTimeMillis) + val currentTime = 3.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime val randomUUID = mock() whenever(uuidWrapper.randomUUID).thenReturn(randomUUID) val user = mock() @@ -56,7 +58,7 @@ internal class AudioPlaybackStatisticsEventFactoryTest { val expected = mock() whenever( audioPlaybackStatisticsFactory.create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, @@ -66,12 +68,12 @@ internal class AudioPlaybackStatisticsEventFactoryTest { val actual = audioPlaybackStatisticsEventFactory(payload) - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() verify(uuidWrapper).randomUUID verify(userSupplier)() verify(clientSupplier)() verify(audioPlaybackStatisticsFactory).create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, diff --git a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/BroadcastPlaybackSessionEventFactoryTest.kt b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/BroadcastPlaybackSessionEventFactoryTest.kt index 6fce69c6..1588a34b 100644 --- a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/BroadcastPlaybackSessionEventFactoryTest.kt +++ b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/BroadcastPlaybackSessionEventFactoryTest.kt @@ -2,17 +2,19 @@ package com.tidal.sdk.player.events.converter import assertk.assertThat import assertk.assertions.isSameAs +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.UUIDWrapper -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.ClientSupplier import com.tidal.sdk.player.events.UserSupplier import com.tidal.sdk.player.events.model.BroadcastPlaybackSession import com.tidal.sdk.player.events.model.Client import com.tidal.sdk.player.events.model.User import java.util.UUID +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions @@ -20,13 +22,13 @@ import org.mockito.kotlin.whenever internal class BroadcastPlaybackSessionEventFactoryTest { - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val uuidWrapper = mock() private val userSupplier = mock() private val clientSupplier = mock() private val broadcastPlaybackSessionFactory = mock() private val broadcastPlaybackSessionEventFactory = BroadcastPlaybackSessionEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -35,7 +37,7 @@ internal class BroadcastPlaybackSessionEventFactoryTest { @AfterEach fun afterEach() = verifyNoMoreInteractions( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -44,8 +46,8 @@ internal class BroadcastPlaybackSessionEventFactoryTest { @Test fun invoke() = runBlocking { - val currentTimeMillis = -3L - whenever(trueTimeWrapper.currentTimeMillis).thenReturn(currentTimeMillis) + val currentTime = 3.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime val randomUUID = mock() whenever(uuidWrapper.randomUUID).thenReturn(randomUUID) val user = mock() @@ -56,7 +58,7 @@ internal class BroadcastPlaybackSessionEventFactoryTest { val expected = mock() whenever( broadcastPlaybackSessionFactory.create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, @@ -66,12 +68,12 @@ internal class BroadcastPlaybackSessionEventFactoryTest { val actual = broadcastPlaybackSessionEventFactory(payload) - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() verify(uuidWrapper).randomUUID verify(userSupplier)() verify(clientSupplier)() verify(broadcastPlaybackSessionFactory).create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, diff --git a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/BroadcastPlaybackStatisticsEventFactoryTest.kt b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/BroadcastPlaybackStatisticsEventFactoryTest.kt index 506c437a..752228ef 100644 --- a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/BroadcastPlaybackStatisticsEventFactoryTest.kt +++ b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/BroadcastPlaybackStatisticsEventFactoryTest.kt @@ -2,17 +2,19 @@ package com.tidal.sdk.player.events.converter import assertk.assertThat import assertk.assertions.isSameAs +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.UUIDWrapper -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.ClientSupplier import com.tidal.sdk.player.events.UserSupplier import com.tidal.sdk.player.events.model.BroadcastPlaybackStatistics import com.tidal.sdk.player.events.model.Client import com.tidal.sdk.player.events.model.User import java.util.UUID +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions @@ -20,7 +22,7 @@ import org.mockito.kotlin.whenever internal class BroadcastPlaybackStatisticsEventFactoryTest { - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val uuidWrapper = mock() private val userSupplier = mock() private val clientSupplier = mock() @@ -28,7 +30,7 @@ internal class BroadcastPlaybackStatisticsEventFactoryTest { mock() private val broadcastPlaybackStatisticsEventFactory = BroadcastPlaybackStatisticsEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -37,7 +39,7 @@ internal class BroadcastPlaybackStatisticsEventFactoryTest { @AfterEach fun afterEach() = verifyNoMoreInteractions( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -46,8 +48,8 @@ internal class BroadcastPlaybackStatisticsEventFactoryTest { @Test fun invoke() = runBlocking { - val currentTimeMillis = -3L - whenever(trueTimeWrapper.currentTimeMillis).thenReturn(currentTimeMillis) + val currentTime = 3.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime val randomUUID = mock() whenever(uuidWrapper.randomUUID).thenReturn(randomUUID) val user = mock() @@ -58,7 +60,7 @@ internal class BroadcastPlaybackStatisticsEventFactoryTest { val expected = mock() whenever( broadcastPlaybackStatisticsFactory.create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, @@ -68,12 +70,12 @@ internal class BroadcastPlaybackStatisticsEventFactoryTest { val actual = broadcastPlaybackStatisticsEventFactory(payload) - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() verify(uuidWrapper).randomUUID verify(userSupplier)() verify(clientSupplier)() verify(broadcastPlaybackStatisticsFactory).create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, diff --git a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/DrmLicenseFetchEventFactoryTest.kt b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/DrmLicenseFetchEventFactoryTest.kt index 6031c107..6758d839 100644 --- a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/DrmLicenseFetchEventFactoryTest.kt +++ b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/DrmLicenseFetchEventFactoryTest.kt @@ -2,17 +2,19 @@ package com.tidal.sdk.player.events.converter import assertk.assertThat import assertk.assertions.isSameAs +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.UUIDWrapper -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.ClientSupplier import com.tidal.sdk.player.events.UserSupplier import com.tidal.sdk.player.events.model.Client import com.tidal.sdk.player.events.model.DrmLicenseFetch import com.tidal.sdk.player.events.model.User import java.util.UUID +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions @@ -20,13 +22,13 @@ import org.mockito.kotlin.whenever internal class DrmLicenseFetchEventFactoryTest { - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val uuidWrapper = mock() private val userSupplier = mock() private val clientSupplier = mock() private val drmLicenseFetchFactory = mock() private val drmLicenseFetchEventFactory = DrmLicenseFetchEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -35,7 +37,7 @@ internal class DrmLicenseFetchEventFactoryTest { @AfterEach fun afterEach() = verifyNoMoreInteractions( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -44,8 +46,8 @@ internal class DrmLicenseFetchEventFactoryTest { @Test fun invoke() = runBlocking { - val currentTimeMillis = -3L - whenever(trueTimeWrapper.currentTimeMillis).thenReturn(currentTimeMillis) + val currentTime = 3.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime val randomUUID = mock() whenever(uuidWrapper.randomUUID).thenReturn(randomUUID) val user = mock() @@ -55,17 +57,23 @@ internal class DrmLicenseFetchEventFactoryTest { val payload = mock() val expected = mock() whenever( - drmLicenseFetchFactory.create(currentTimeMillis, randomUUID, user, client, payload), + drmLicenseFetchFactory.create( + currentTime.inWholeMilliseconds, + randomUUID, + user, + client, + payload, + ), ).thenReturn(expected) val actual = drmLicenseFetchEventFactory(payload) - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() verify(uuidWrapper).randomUUID verify(userSupplier)() verify(clientSupplier)() verify(drmLicenseFetchFactory).create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, diff --git a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/NotStartedPlaybackStatisticsEventFactoryTest.kt b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/NotStartedPlaybackStatisticsEventFactoryTest.kt index 59c14ba4..78fd3cf7 100644 --- a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/NotStartedPlaybackStatisticsEventFactoryTest.kt +++ b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/NotStartedPlaybackStatisticsEventFactoryTest.kt @@ -2,17 +2,19 @@ package com.tidal.sdk.player.events.converter import assertk.assertThat import assertk.assertions.isSameAs +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.UUIDWrapper -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.ClientSupplier import com.tidal.sdk.player.events.UserSupplier import com.tidal.sdk.player.events.model.Client import com.tidal.sdk.player.events.model.NotStartedPlaybackStatistics import com.tidal.sdk.player.events.model.User import java.util.UUID +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions @@ -20,13 +22,13 @@ import org.mockito.kotlin.whenever internal class NotStartedPlaybackStatisticsEventFactoryTest { - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val uuidWrapper = mock() private val userSupplier = mock() private val clientSupplier = mock() private val notStartedPlaybackStatisticsFactory = mock() private val notStartedPlaybackStatisticsEventFactory = NotStartedPlaybackStatisticsEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -35,7 +37,7 @@ internal class NotStartedPlaybackStatisticsEventFactoryTest { @AfterEach fun afterEach() = verifyNoMoreInteractions( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -44,8 +46,8 @@ internal class NotStartedPlaybackStatisticsEventFactoryTest { @Test fun invoke() = runBlocking { - val currentTimeMillis = -3L - whenever(trueTimeWrapper.currentTimeMillis).thenReturn(currentTimeMillis) + val currentTime = 3.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime val randomUUID = mock() whenever(uuidWrapper.randomUUID).thenReturn(randomUUID) val user = mock() @@ -56,7 +58,7 @@ internal class NotStartedPlaybackStatisticsEventFactoryTest { val expected = mock() whenever( notStartedPlaybackStatisticsFactory.create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, @@ -66,12 +68,12 @@ internal class NotStartedPlaybackStatisticsEventFactoryTest { val actual = notStartedPlaybackStatisticsEventFactory(payload) - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() verify(uuidWrapper).randomUUID verify(userSupplier)() verify(clientSupplier)() verify(notStartedPlaybackStatisticsFactory).create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, diff --git a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/PlaybackInfoFetchEventFactoryTest.kt b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/PlaybackInfoFetchEventFactoryTest.kt index 8ab72731..77a9eec3 100644 --- a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/PlaybackInfoFetchEventFactoryTest.kt +++ b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/PlaybackInfoFetchEventFactoryTest.kt @@ -2,17 +2,19 @@ package com.tidal.sdk.player.events.converter import assertk.assertThat import assertk.assertions.isSameAs +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.UUIDWrapper -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.ClientSupplier import com.tidal.sdk.player.events.UserSupplier import com.tidal.sdk.player.events.model.Client import com.tidal.sdk.player.events.model.PlaybackInfoFetch import com.tidal.sdk.player.events.model.User import java.util.UUID +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions @@ -20,13 +22,13 @@ import org.mockito.kotlin.whenever internal class PlaybackInfoFetchEventFactoryTest { - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val uuidWrapper = mock() private val userSupplier = mock() private val clientSupplier = mock() private val playbackInfoFetchFactory = mock() private val playbackInfoFetchEventFactory = PlaybackInfoFetchEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -35,7 +37,7 @@ internal class PlaybackInfoFetchEventFactoryTest { @AfterEach fun afterEach() = verifyNoMoreInteractions( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -44,8 +46,8 @@ internal class PlaybackInfoFetchEventFactoryTest { @Test fun invoke() = runBlocking { - val currentTimeMillis = -3L - whenever(trueTimeWrapper.currentTimeMillis).thenReturn(currentTimeMillis) + val currentTime = 3.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime val randomUUID = mock() whenever(uuidWrapper.randomUUID).thenReturn(randomUUID) val user = mock() @@ -55,17 +57,23 @@ internal class PlaybackInfoFetchEventFactoryTest { val payload = mock() val expected = mock() whenever( - playbackInfoFetchFactory.create(currentTimeMillis, randomUUID, user, client, payload), + playbackInfoFetchFactory.create( + currentTime.inWholeMilliseconds, + randomUUID, + user, + client, + payload, + ), ).thenReturn(expected) val actual = playbackInfoFetchEventFactory(payload) - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() verify(uuidWrapper).randomUUID verify(userSupplier)() verify(clientSupplier)() verify(playbackInfoFetchFactory).create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, diff --git a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/StreamingSessionEndEventFactoryTest.kt b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/StreamingSessionEndEventFactoryTest.kt index df33cf26..b9ca6bfa 100644 --- a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/StreamingSessionEndEventFactoryTest.kt +++ b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/StreamingSessionEndEventFactoryTest.kt @@ -2,17 +2,19 @@ package com.tidal.sdk.player.events.converter import assertk.assertThat import assertk.assertions.isSameAs +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.UUIDWrapper -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.ClientSupplier import com.tidal.sdk.player.events.UserSupplier import com.tidal.sdk.player.events.model.Client import com.tidal.sdk.player.events.model.StreamingSessionEnd import com.tidal.sdk.player.events.model.User import java.util.UUID +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions @@ -20,13 +22,13 @@ import org.mockito.kotlin.whenever internal class StreamingSessionEndEventFactoryTest { - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val uuidWrapper = mock() private val userSupplier = mock() private val clientSupplier = mock() private val streamingSessionEndFactory = mock() private val streamingSessionEndEventFactory = StreamingSessionEndEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -35,7 +37,7 @@ internal class StreamingSessionEndEventFactoryTest { @AfterEach fun afterEach() = verifyNoMoreInteractions( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -44,8 +46,8 @@ internal class StreamingSessionEndEventFactoryTest { @Test fun invoke() = runBlocking { - val currentTimeMillis = -3L - whenever(trueTimeWrapper.currentTimeMillis).thenReturn(currentTimeMillis) + val currentTime = 3.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime val randomUUID = mock() whenever(uuidWrapper.randomUUID).thenReturn(randomUUID) val user = mock() @@ -55,17 +57,23 @@ internal class StreamingSessionEndEventFactoryTest { val payload = mock() val expected = mock() whenever( - streamingSessionEndFactory.create(currentTimeMillis, randomUUID, user, client, payload), + streamingSessionEndFactory.create( + currentTime.inWholeMilliseconds, + randomUUID, + user, + client, + payload, + ), ).thenReturn(expected) val actual = streamingSessionEndEventFactory(payload) - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() verify(uuidWrapper).randomUUID verify(userSupplier)() verify(clientSupplier)() verify(streamingSessionEndFactory).create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, diff --git a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/StreamingSessionStartEventFactoryTest.kt b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/StreamingSessionStartEventFactoryTest.kt index 60457e09..b4c85491 100644 --- a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/StreamingSessionStartEventFactoryTest.kt +++ b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/StreamingSessionStartEventFactoryTest.kt @@ -2,17 +2,19 @@ package com.tidal.sdk.player.events.converter import assertk.assertThat import assertk.assertions.isSameAs +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.UUIDWrapper -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.ClientSupplier import com.tidal.sdk.player.events.UserSupplier import com.tidal.sdk.player.events.model.Client import com.tidal.sdk.player.events.model.StreamingSessionStart import com.tidal.sdk.player.events.model.User import java.util.UUID +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions @@ -20,7 +22,7 @@ import org.mockito.kotlin.whenever internal class StreamingSessionStartEventFactoryTest { - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val uuidWrapper = mock() private val userSupplier = mock() private val clientSupplier = mock() @@ -28,7 +30,7 @@ internal class StreamingSessionStartEventFactoryTest { mock() private val streamingSessionStartFactory = mock() private val streamingSessionStartEventFactory = StreamingSessionStartEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -38,7 +40,7 @@ internal class StreamingSessionStartEventFactoryTest { @AfterEach fun afterEach() = verifyNoMoreInteractions( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -48,8 +50,8 @@ internal class StreamingSessionStartEventFactoryTest { @Test fun invoke() = runBlocking { - val currentTimeMillis = -3L - whenever(trueTimeWrapper.currentTimeMillis).thenReturn(currentTimeMillis) + val currentTime = 3.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime val randomUUID = mock() whenever(uuidWrapper.randomUUID).thenReturn(randomUUID) val user = mock() @@ -63,18 +65,24 @@ internal class StreamingSessionStartEventFactoryTest { val expected = mock() whenever( streamingSessionStartFactory - .create(currentTimeMillis, randomUUID, user, client, decoratedPayload), + .create( + currentTime.inWholeMilliseconds, + randomUUID, + user, + client, + decoratedPayload, + ), ).thenReturn(expected) val actual = streamingSessionStartEventFactory(payload) - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() verify(uuidWrapper).randomUUID verify(userSupplier)() verify(clientSupplier)() verify(streamingSessionStartPayloadDecorator).decorate(payload) verify(streamingSessionStartFactory).create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, diff --git a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/UCPlaybackSessionEventFactoryTest.kt b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/UCPlaybackSessionEventFactoryTest.kt index 97ac7040..dd9477da 100644 --- a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/UCPlaybackSessionEventFactoryTest.kt +++ b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/UCPlaybackSessionEventFactoryTest.kt @@ -2,17 +2,19 @@ package com.tidal.sdk.player.events.converter import assertk.assertThat import assertk.assertions.isSameAs +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.UUIDWrapper -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.ClientSupplier import com.tidal.sdk.player.events.UserSupplier import com.tidal.sdk.player.events.model.Client import com.tidal.sdk.player.events.model.UCPlaybackSession import com.tidal.sdk.player.events.model.User import java.util.UUID +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions @@ -20,13 +22,13 @@ import org.mockito.kotlin.whenever internal class UCPlaybackSessionEventFactoryTest { - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val uuidWrapper = mock() private val userSupplier = mock() private val clientSupplier = mock() private val ucPlaybackSessionFactory = mock() private val ucPlaybackSessionEventFactory = UCPlaybackSessionEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -35,7 +37,7 @@ internal class UCPlaybackSessionEventFactoryTest { @AfterEach fun afterEach() = verifyNoMoreInteractions( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -44,8 +46,8 @@ internal class UCPlaybackSessionEventFactoryTest { @Test fun invoke() = runBlocking { - val currentTimeMillis = -3L - whenever(trueTimeWrapper.currentTimeMillis).thenReturn(currentTimeMillis) + val currentTime = 3.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime val randomUUID = mock() whenever(uuidWrapper.randomUUID).thenReturn(randomUUID) val user = mock() @@ -56,7 +58,7 @@ internal class UCPlaybackSessionEventFactoryTest { val expected = mock() whenever( ucPlaybackSessionFactory.create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, @@ -66,12 +68,12 @@ internal class UCPlaybackSessionEventFactoryTest { val actual = ucPlaybackSessionEventFactory(payload) - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() verify(uuidWrapper).randomUUID verify(userSupplier)() verify(clientSupplier)() verify(ucPlaybackSessionFactory).create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, diff --git a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/UCPlaybackStatisticsEventFactoryTest.kt b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/UCPlaybackStatisticsEventFactoryTest.kt index 27a10c96..4ce4b91e 100644 --- a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/UCPlaybackStatisticsEventFactoryTest.kt +++ b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/UCPlaybackStatisticsEventFactoryTest.kt @@ -2,17 +2,19 @@ package com.tidal.sdk.player.events.converter import assertk.assertThat import assertk.assertions.isSameAs +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.UUIDWrapper -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.ClientSupplier import com.tidal.sdk.player.events.UserSupplier import com.tidal.sdk.player.events.model.Client import com.tidal.sdk.player.events.model.UCPlaybackStatistics import com.tidal.sdk.player.events.model.User import java.util.UUID +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions @@ -20,7 +22,7 @@ import org.mockito.kotlin.whenever internal class UCPlaybackStatisticsEventFactoryTest { - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val uuidWrapper = mock() private val userSupplier = mock() private val clientSupplier = mock() @@ -28,7 +30,7 @@ internal class UCPlaybackStatisticsEventFactoryTest { mock() private val ucPlaybackStatisticsEventFactory = UCPlaybackStatisticsEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -37,7 +39,7 @@ internal class UCPlaybackStatisticsEventFactoryTest { @AfterEach fun afterEach() = verifyNoMoreInteractions( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -46,8 +48,8 @@ internal class UCPlaybackStatisticsEventFactoryTest { @Test fun invoke() = runBlocking { - val currentTimeMillis = -3L - whenever(trueTimeWrapper.currentTimeMillis).thenReturn(currentTimeMillis) + val currentTime = 3.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime val randomUUID = mock() whenever(uuidWrapper.randomUUID).thenReturn(randomUUID) val user = mock() @@ -58,7 +60,7 @@ internal class UCPlaybackStatisticsEventFactoryTest { val expected = mock() whenever( ucPlaybackStatisticsFactory.create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, @@ -68,12 +70,12 @@ internal class UCPlaybackStatisticsEventFactoryTest { val actual = ucPlaybackStatisticsEventFactory(payload) - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() verify(uuidWrapper).randomUUID verify(userSupplier)() verify(clientSupplier)() verify(ucPlaybackStatisticsFactory).create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, diff --git a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/VideoDownloadStatisticsEventFactoryTest.kt b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/VideoDownloadStatisticsEventFactoryTest.kt index 615af5dd..39b77628 100644 --- a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/VideoDownloadStatisticsEventFactoryTest.kt +++ b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/VideoDownloadStatisticsEventFactoryTest.kt @@ -2,17 +2,19 @@ package com.tidal.sdk.player.events.converter import assertk.assertThat import assertk.assertions.isSameAs +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.UUIDWrapper -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.ClientSupplier import com.tidal.sdk.player.events.UserSupplier import com.tidal.sdk.player.events.model.Client import com.tidal.sdk.player.events.model.User import com.tidal.sdk.player.events.model.VideoDownloadStatistics import java.util.UUID +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions @@ -20,13 +22,13 @@ import org.mockito.kotlin.whenever internal class VideoDownloadStatisticsEventFactoryTest { - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val uuidWrapper = mock() private val userSupplier = mock() private val clientSupplier = mock() private val videoDownloadStatisticsFactory = mock() private val videoDownloadStatisticsEventFactory = VideoDownloadStatisticsEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -35,7 +37,7 @@ internal class VideoDownloadStatisticsEventFactoryTest { @AfterEach fun afterEach() = verifyNoMoreInteractions( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -44,8 +46,8 @@ internal class VideoDownloadStatisticsEventFactoryTest { @Test fun invoke() = runBlocking { - val currentTimeMillis = -3L - whenever(trueTimeWrapper.currentTimeMillis).thenReturn(currentTimeMillis) + val currentTime = 3.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime val randomUUID = mock() whenever(uuidWrapper.randomUUID).thenReturn(randomUUID) val user = mock() @@ -55,17 +57,23 @@ internal class VideoDownloadStatisticsEventFactoryTest { val src = mock() val expected = mock() whenever( - videoDownloadStatisticsFactory.create(currentTimeMillis, randomUUID, user, client, src), + videoDownloadStatisticsFactory.create( + currentTime.inWholeMilliseconds, + randomUUID, + user, + client, + src, + ), ).thenReturn(expected) val actual = videoDownloadStatisticsEventFactory(src) - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() verify(uuidWrapper).randomUUID verify(userSupplier)() verify(clientSupplier)() verify(videoDownloadStatisticsFactory).create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, diff --git a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/VideoPlaybackSessionEventFactoryTest.kt b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/VideoPlaybackSessionEventFactoryTest.kt index 1d21b0de..da573f52 100644 --- a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/VideoPlaybackSessionEventFactoryTest.kt +++ b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/VideoPlaybackSessionEventFactoryTest.kt @@ -2,17 +2,19 @@ package com.tidal.sdk.player.events.converter import assertk.assertThat import assertk.assertions.isSameAs +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.UUIDWrapper -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.ClientSupplier import com.tidal.sdk.player.events.UserSupplier import com.tidal.sdk.player.events.model.Client import com.tidal.sdk.player.events.model.User import com.tidal.sdk.player.events.model.VideoPlaybackSession import java.util.UUID +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions @@ -20,13 +22,13 @@ import org.mockito.kotlin.whenever internal class VideoPlaybackSessionEventFactoryTest { - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val uuidWrapper = mock() private val userSupplier = mock() private val clientSupplier = mock() private val videoPlaybackSessionFactory = mock() private val videoPlaybackSessionEventFactory = VideoPlaybackSessionEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -35,7 +37,7 @@ internal class VideoPlaybackSessionEventFactoryTest { @AfterEach fun afterEach() = verifyNoMoreInteractions( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -44,8 +46,8 @@ internal class VideoPlaybackSessionEventFactoryTest { @Test fun invoke() = runBlocking { - val currentTimeMillis = -3L - whenever(trueTimeWrapper.currentTimeMillis).thenReturn(currentTimeMillis) + val currentTime = 3.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime val randomUUID = mock() whenever(uuidWrapper.randomUUID).thenReturn(randomUUID) val user = mock() @@ -56,7 +58,7 @@ internal class VideoPlaybackSessionEventFactoryTest { val expected = mock() whenever( videoPlaybackSessionFactory.create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, @@ -66,12 +68,12 @@ internal class VideoPlaybackSessionEventFactoryTest { val actual = videoPlaybackSessionEventFactory(payload) - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() verify(uuidWrapper).randomUUID verify(userSupplier)() verify(clientSupplier)() verify(videoPlaybackSessionFactory).create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, diff --git a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/VideoPlaybackStatisticsEventFactoryTest.kt b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/VideoPlaybackStatisticsEventFactoryTest.kt index a5b5d0ee..1dda3650 100644 --- a/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/VideoPlaybackStatisticsEventFactoryTest.kt +++ b/player/events/src/test/kotlin/com/tidal/sdk/player/events/converter/VideoPlaybackStatisticsEventFactoryTest.kt @@ -2,17 +2,19 @@ package com.tidal.sdk.player.events.converter import assertk.assertThat import assertk.assertions.isSameAs +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.UUIDWrapper -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.ClientSupplier import com.tidal.sdk.player.events.UserSupplier import com.tidal.sdk.player.events.model.Client import com.tidal.sdk.player.events.model.User import com.tidal.sdk.player.events.model.VideoPlaybackStatistics import java.util.UUID +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions @@ -20,13 +22,13 @@ import org.mockito.kotlin.whenever internal class VideoPlaybackStatisticsEventFactoryTest { - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val uuidWrapper = mock() private val userSupplier = mock() private val clientSupplier = mock() private val videoPlaybackStatisticsFactory = mock() private val videoPlaybackStatisticsEventFactory = VideoPlaybackStatisticsEventFactory( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -35,7 +37,7 @@ internal class VideoPlaybackStatisticsEventFactoryTest { @AfterEach fun afterEach() = verifyNoMoreInteractions( - trueTimeWrapper, + sntpClient, uuidWrapper, userSupplier, clientSupplier, @@ -44,8 +46,8 @@ internal class VideoPlaybackStatisticsEventFactoryTest { @Test fun invoke() = runBlocking { - val currentTimeMillis = -3L - whenever(trueTimeWrapper.currentTimeMillis).thenReturn(currentTimeMillis) + val currentTime = 3.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime val randomUUID = mock() whenever(uuidWrapper.randomUUID).thenReturn(randomUUID) val user = mock() @@ -56,7 +58,7 @@ internal class VideoPlaybackStatisticsEventFactoryTest { val expected = mock() whenever( videoPlaybackStatisticsFactory.create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, @@ -66,12 +68,12 @@ internal class VideoPlaybackStatisticsEventFactoryTest { val actual = videoPlaybackStatisticsEventFactory(payload) - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() verify(uuidWrapper).randomUUID verify(userSupplier)() verify(clientSupplier)() verify(videoPlaybackStatisticsFactory).create( - currentTimeMillis, + currentTime.inWholeMilliseconds, randomUUID, user, client, diff --git a/player/playback-engine/build.gradle.kts b/player/playback-engine/build.gradle.kts index 701f688d..6eb56d68 100644 --- a/player/playback-engine/build.gradle.kts +++ b/player/playback-engine/build.gradle.kts @@ -34,6 +34,7 @@ dependencies { implementation(libs.tidal.exoPlayer.extension.flac) implementation(libs.tidal.exoPlayer.extension.okhttp) implementation(libs.dagger) + implementation(libs.tidal.networktime) testImplementation(libs.test.assertk) testImplementation(libs.test.junit5Api) @@ -41,6 +42,7 @@ dependencies { testRuntimeOnly(libs.test.junit5Engine) testImplementation(libs.test.junit5Params) testImplementation(project(":player:testutil")) + testImplementation(libs.kotlinx.coroutines.test) androidTestRuntimeOnly(libs.test.mockito.android) androidTestImplementation(libs.test.mockito.kotlin) diff --git a/player/playback-engine/src/androidTest/kotlin/com/tidal/sdk/player/playbackengine/ExoPlayerPlaybackEngineLooperTest.kt b/player/playback-engine/src/androidTest/kotlin/com/tidal/sdk/player/playbackengine/ExoPlayerPlaybackEngineLooperTest.kt index 889297ac..65cb9bc8 100644 --- a/player/playback-engine/src/androidTest/kotlin/com/tidal/sdk/player/playbackengine/ExoPlayerPlaybackEngineLooperTest.kt +++ b/player/playback-engine/src/androidTest/kotlin/com/tidal/sdk/player/playbackengine/ExoPlayerPlaybackEngineLooperTest.kt @@ -4,10 +4,11 @@ import androidx.test.platform.app.InstrumentationRegistry import assertk.assertThat import assertk.assertions.isSameAs import com.google.gson.Gson +import com.tidal.networktime.NTPServer +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.Configuration import com.tidal.sdk.player.common.UUIDWrapper import com.tidal.sdk.player.commonandroid.Base64Codec -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.playbackengine.model.AssetTimeoutConfig import com.tidal.sdk.player.playbackengine.model.BufferConfiguration import com.tidal.sdk.player.playbackengine.player.CacheProvider @@ -37,7 +38,7 @@ internal class ExoPlayerPlaybackEngineLooperTest { mock(), mock(), UUIDWrapper(), - TrueTimeWrapper(), + SNTPClient(NTPServer("time.google.com")), mock(), mock(), mock(), diff --git a/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/ExoPlayerPlaybackEngine.kt b/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/ExoPlayerPlaybackEngine.kt index d94d61ce..731cd267 100644 --- a/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/ExoPlayerPlaybackEngine.kt +++ b/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/ExoPlayerPlaybackEngine.kt @@ -14,12 +14,12 @@ import androidx.media3.exoplayer.analytics.AnalyticsListener import androidx.media3.exoplayer.analytics.AnalyticsListener.EventTime import androidx.media3.exoplayer.hls.HlsManifest import androidx.media3.exoplayer.source.ForwardingTimeline +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.ForwardingMediaProduct import com.tidal.sdk.player.common.model.AudioQuality import com.tidal.sdk.player.common.model.LoudnessNormalizationMode import com.tidal.sdk.player.common.model.MediaProduct import com.tidal.sdk.player.common.model.ProductType -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.EventReporter import com.tidal.sdk.player.events.model.AudioPlaybackSession import com.tidal.sdk.player.events.model.AudioPlaybackStatistics @@ -67,6 +67,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.transformWhile import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking private const val MS_IN_SECOND = 1000L @@ -85,7 +86,7 @@ internal class ExoPlayerPlaybackEngine( private val audioQualityRepository: AudioQualityRepository, private val audioModeRepository: AudioModeRepository, private val volumeHelper: VolumeHelper, - private val trueTimeWrapper: TrueTimeWrapper, + private val sntpClient: SNTPClient, private val eventReporter: EventReporter, private val errorHandler: ErrorHandler, private val djSessionManager: DjSessionManager, @@ -295,7 +296,7 @@ internal class ExoPlayerPlaybackEngine( extendedExoPlayer.currentStreamingSession!! .createUndeterminedPlaybackStatistics( PlaybackStatistics.IdealStartTimestampMs.Known( - trueTimeWrapper.currentTimeMillis, + runBlocking { sntpClient.blockingEpochTime() }.inWholeMilliseconds, ), ) } else if ( @@ -305,7 +306,9 @@ internal class ExoPlayerPlaybackEngine( ) { currentPlaybackStatistics = readCurrentPlaybackStatistics.copy( idealStartTimestampMs = - PlaybackStatistics.IdealStartTimestampMs.Known(trueTimeWrapper.currentTimeMillis), + PlaybackStatistics.IdealStartTimestampMs.Known( + runBlocking { sntpClient.blockingEpochTime() }.inWholeMilliseconds + ), ) } extendedExoPlayer.play() @@ -462,7 +465,7 @@ internal class ExoPlayerPlaybackEngine( } currentPlaybackSession?.actions?.add( Action( - trueTimeWrapper.currentTimeMillis, + runBlocking { sntpClient.blockingEpochTime() }.inWholeMilliseconds, positionInSeconds, actionType, ), @@ -493,7 +496,8 @@ internal class ExoPlayerPlaybackEngine( playbackState = PlaybackState.STALLED } else if (state == Player.STATE_IDLE || state == Player.STATE_ENDED) { if (state == Player.STATE_ENDED) { - val currentTimeMillis = trueTimeWrapper.currentTimeMillis + val currentTimeMillis = runBlocking { sntpClient.blockingEpochTime() } + .inWholeMilliseconds val positionSeconds = if (forwardingMediaProduct?.productType == ProductType.BROADCAST) { extendedExoPlayer.currentPositionSinceEpochMs @@ -547,7 +551,7 @@ internal class ExoPlayerPlaybackEngine( newPosition.positionMs } - val invokedAtMillis = trueTimeWrapper.currentTimeMillis + val invokedAtMillis = runBlocking { sntpClient.blockingEpochTime() }.inWholeMilliseconds val oldPositionSeconds = oldPositionMs.toDouble() / MS_IN_SECOND val newPositionSeconds = newPositionMs.toDouble() / MS_IN_SECOND if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) { @@ -676,7 +680,7 @@ internal class ExoPlayerPlaybackEngine( }.toDouble() / MS_IN_SECOND currentPlaybackSession?.actions?.add( Action( - trueTimeWrapper.currentTimeMillis, + runBlocking { sntpClient.blockingEpochTime() }.inWholeMilliseconds, positionInSeconds, if (playWhenReady) Action.Type.PLAYBACK_START else Action.Type.PLAYBACK_STOP, ), @@ -782,7 +786,7 @@ internal class ExoPlayerPlaybackEngine( } val readCurrentPlaybackStatistics = currentPlaybackStatistics if (readCurrentPlaybackStatistics !is PlaybackStatistics.Success.Started) { - val startTimestamp = trueTimeWrapper.currentTimeMillis + val startTimestamp = runBlocking { sntpClient.blockingEpochTime() }.inWholeMilliseconds if (readCurrentPlaybackStatistics is PlaybackStatistics.Success.Prepared) { currentPlaybackStatistics = readCurrentPlaybackStatistics.toStarted(startTimestamp) currentPlaybackSession!!.apply { @@ -806,7 +810,11 @@ internal class ExoPlayerPlaybackEngine( } else { eventTime.currentPlaybackPositionMs }.toDouble() / MS_IN_SECOND - startStall(Stall.Reason.UNEXPECTED, positionInSeconds, trueTimeWrapper.currentTimeMillis) + startStall( + Stall.Reason.UNEXPECTED, + positionInSeconds, + runBlocking { sntpClient.blockingEpochTime() }.inWholeMilliseconds + ) } @Suppress("LongMethod", "ComplexMethod") @@ -885,7 +893,7 @@ internal class ExoPlayerPlaybackEngine( }.toDouble() / MS_IN_SECOND playbackStatistics + Adaptation( positionInSeconds, - trueTimeWrapper.currentTimeMillis, + runBlocking { sntpClient.blockingEpochTime() }.inWholeMilliseconds, format.sampleMimeType ?: "", format.codecs ?: "", format.bitrate, @@ -972,7 +980,7 @@ internal class ExoPlayerPlaybackEngine( reason, assetPositionSeconds, startTimestamp, - trueTimeWrapper.currentTimeMillis, + runBlocking { sntpClient.blockingEpochTime() }.inWholeMilliseconds, ) } } @@ -1168,7 +1176,7 @@ internal class ExoPlayerPlaybackEngine( errorMessage: String? = null, errorCode: String? = null, endPositionSeconds: Double, - endTimestamp: Long = trueTimeWrapper.currentTimeMillis, + endTimestamp: Long = runBlocking { sntpClient.blockingEpochTime() }.inWholeMilliseconds, ) { reportCurrentPlaybackStatistics(endReason, errorMessage, errorCode, endTimestamp) reportCurrentPlaybackSession(endTimestamp, endPositionSeconds) @@ -1205,7 +1213,7 @@ internal class ExoPlayerPlaybackEngine( streamingSessionId.toString(), idealStartTimestampMs.timestamp, requestedMediaProduct.productType, - trueTimeWrapper.currentTimeMillis, + runBlocking { sntpClient.blockingEpochTime() }.inWholeMilliseconds, errorMessage, errorCode, when (this@report) { diff --git a/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/PlaybackEngineModuleRoot.kt b/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/PlaybackEngineModuleRoot.kt index 9982f25b..bcc9a0fd 100644 --- a/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/PlaybackEngineModuleRoot.kt +++ b/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/PlaybackEngineModuleRoot.kt @@ -3,10 +3,10 @@ package com.tidal.sdk.player.playbackengine import android.content.Context import android.net.ConnectivityManager import com.google.gson.Gson +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.Configuration import com.tidal.sdk.player.common.UUIDWrapper import com.tidal.sdk.player.commonandroid.Base64Codec -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.EventReporter import com.tidal.sdk.player.playbackengine.di.DaggerExoPlayerPlaybackEngineComponent import com.tidal.sdk.player.playbackengine.model.AssetTimeoutConfig @@ -41,7 +41,7 @@ class PlaybackEngineModuleRoot( eventReporter: EventReporter, streamingPrivileges: StreamingPrivileges, uuidWrapper: UUIDWrapper, - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, playbackPrivilegeProvider: PlaybackPrivilegeProvider, offlineCacheProvider: OfflineCacheProvider?, encryption: Encryption?, @@ -67,7 +67,7 @@ class PlaybackEngineModuleRoot( eventReporter, streamingPrivileges, uuidWrapper, - trueTimeWrapper, + sntpClient, playbackPrivilegeProvider, offlineCacheProvider, encryption, diff --git a/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/StreamingApiRepository.kt b/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/StreamingApiRepository.kt index d0652d86..ffbbadca 100644 --- a/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/StreamingApiRepository.kt +++ b/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/StreamingApiRepository.kt @@ -1,11 +1,11 @@ package com.tidal.sdk.player.playbackengine import androidx.media3.exoplayer.drm.MediaDrmCallbackException +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.ForwardingMediaProduct import com.tidal.sdk.player.common.model.AudioQuality import com.tidal.sdk.player.common.model.ProductType import com.tidal.sdk.player.common.model.VideoQuality -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.EventReporter import com.tidal.sdk.player.events.model.DrmLicenseFetch import com.tidal.sdk.player.events.model.EndReason @@ -33,7 +33,7 @@ import java.io.IOException * [AudioQuality] to be used in the request. * @param[videoQualityRepository] An instance of [VideoQualityRepository] which decides the * [VideoQuality] to be used in the request. - * @param[trueTimeWrapper] An instance of [TrueTimeWrapper] to get the time for event reporting. + * @param[sntpClient] An instance of [SNTPClient] to get the time for event reporting. * @param[mediaDrmCallbackExceptionFactory] An instance of [MediaDrmCallbackExceptionFactory] to * create a [MediaDrmCallbackException] for when the drm fails. * @param[eventReporter] An instance of [EventReporter] to report events to. @@ -44,7 +44,7 @@ internal class StreamingApiRepository( private val audioQualityRepository: AudioQualityRepository, private val videoQualityRepository: VideoQualityRepository, private val audioModeRepository: AudioModeRepository, - private val trueTimeWrapper: TrueTimeWrapper, + private val sntpClient: SNTPClient, private val mediaDrmCallbackExceptionFactory: MediaDrmCallbackExceptionFactory, private val eventReporter: EventReporter, private val errorHandler: ErrorHandler, @@ -55,7 +55,7 @@ internal class StreamingApiRepository( */ @SuppressWarnings("TooGenericExceptionCaught") // We rethrow it, so no issue suspend fun getDrmLicense(drmLicenseRequest: DrmLicenseRequest): DrmLicense { - val startTimestamp = trueTimeWrapper.currentTimeMillis + val startTimestamp = sntpClient.blockingEpochTime().inWholeMilliseconds var errorMessage: String? = null var errorCode: String? = null lateinit var endReason: EndReason @@ -73,7 +73,7 @@ internal class StreamingApiRepository( DrmLicenseFetch.Payload( drmLicenseRequest.streamingSessionId, startTimestamp, - trueTimeWrapper.currentTimeMillis, + sntpClient.blockingEpochTime().inWholeMilliseconds, endReason, errorMessage, errorCode, @@ -90,7 +90,7 @@ internal class StreamingApiRepository( streamingSessionId: String, forwardingMediaProduct: ForwardingMediaProduct<*>, ): PlaybackInfo { - val startTimestamp = trueTimeWrapper.currentTimeMillis + val startTimestamp = sntpClient.blockingEpochTime().inWholeMilliseconds var errorMessage: String? = null var errorCode: String? = null lateinit var endReason: EndReason @@ -135,7 +135,7 @@ internal class StreamingApiRepository( PlaybackInfoFetch.Payload( streamingSessionId, startTimestamp, - trueTimeWrapper.currentTimeMillis, + sntpClient.blockingEpochTime().inWholeMilliseconds, endReason, errorMessage, errorCode, diff --git a/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/di/ExoPlayerPlaybackEngineComponent.kt b/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/di/ExoPlayerPlaybackEngineComponent.kt index eeb5706a..55c66731 100644 --- a/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/di/ExoPlayerPlaybackEngineComponent.kt +++ b/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/di/ExoPlayerPlaybackEngineComponent.kt @@ -3,10 +3,10 @@ package com.tidal.sdk.player.playbackengine.di import android.content.Context import android.net.ConnectivityManager import com.google.gson.Gson +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.Configuration import com.tidal.sdk.player.common.UUIDWrapper import com.tidal.sdk.player.commonandroid.Base64Codec -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.EventReporter import com.tidal.sdk.player.playbackengine.Encryption import com.tidal.sdk.player.playbackengine.PlaybackEngine @@ -53,7 +53,7 @@ interface ExoPlayerPlaybackEngineComponent { @BindsInstance eventReporter: EventReporter, @BindsInstance streamingPrivileges: StreamingPrivileges, @BindsInstance uuidWrapper: UUIDWrapper, - @BindsInstance trueTimeWrapper: TrueTimeWrapper, + @BindsInstance sntpClient: SNTPClient, @BindsInstance playbackPrivilegeProvider: PlaybackPrivilegeProvider, @BindsInstance offlineCacheProvider: OfflineCacheProvider?, @BindsInstance encryption: Encryption?, diff --git a/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/di/ExoPlayerPlaybackEngineModule.kt b/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/di/ExoPlayerPlaybackEngineModule.kt index 56773c68..d057edbe 100644 --- a/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/di/ExoPlayerPlaybackEngineModule.kt +++ b/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/di/ExoPlayerPlaybackEngineModule.kt @@ -8,7 +8,7 @@ import android.os.HandlerThread import android.os.Looper import androidx.media3.exoplayer.drm.ExoMediaDrm import androidx.media3.exoplayer.drm.FrameworkMediaDrm -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.events.EventReporter import com.tidal.sdk.player.playbackengine.ExoPlayerPlaybackEngine import com.tidal.sdk.player.playbackengine.PlaybackContextFactory @@ -158,7 +158,7 @@ internal object ExoPlayerPlaybackEngineModule { audioQualityRepository: AudioQualityRepository, audioModeRepository: AudioModeRepository, volumeHelper: VolumeHelper, - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, eventReporter: EventReporter, errorHandler: ErrorHandler, djSessionManager: DjSessionManager, @@ -175,7 +175,7 @@ internal object ExoPlayerPlaybackEngineModule { audioQualityRepository, audioModeRepository, volumeHelper, - trueTimeWrapper, + sntpClient, eventReporter, errorHandler, djSessionManager, diff --git a/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/mediasource/MediaSourcerer.kt b/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/mediasource/MediaSourcerer.kt index 7192e099..cac6e251 100644 --- a/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/mediasource/MediaSourcerer.kt +++ b/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/mediasource/MediaSourcerer.kt @@ -2,13 +2,14 @@ package com.tidal.sdk.player.playbackengine.mediasource import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.source.ConcatenatingMediaSource +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.ForwardingMediaProduct import com.tidal.sdk.player.common.model.MediaProduct -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.EventReporter import com.tidal.sdk.player.events.model.StreamingSessionEnd import com.tidal.sdk.player.playbackengine.mediasource.streamingsession.StreamingSession import kotlin.properties.Delegates +import kotlinx.coroutines.runBlocking /** * Manages [MediaSource]s we use for continuous playback of media into the provided [ExoPlayer] @@ -26,7 +27,7 @@ internal class MediaSourcerer( private val explicitStreamingSessionCreator: StreamingSession.Creator.Explicit, private val implicitStreamingSessionCreator: StreamingSession.Creator.Implicit, private val eventReporter: EventReporter, - private val trueTimeWrapper: TrueTimeWrapper, + private val sntpClient: SNTPClient, ) { var currentStreamingSession: StreamingSession? by Delegates.observable(null) { _, oldValue, _ -> @@ -120,6 +121,9 @@ internal class MediaSourcerer( } private fun StreamingSession.reportEnd() = eventReporter.report( - StreamingSessionEnd.Payload(id.toString(), trueTimeWrapper.currentTimeMillis), + StreamingSessionEnd.Payload( + id.toString(), + runBlocking { sntpClient.blockingEpochTime() }.inWholeMilliseconds, + ), ) } diff --git a/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/mediasource/streamingsession/StreamingSession.kt b/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/mediasource/streamingsession/StreamingSession.kt index c88e1a03..a51fc45f 100644 --- a/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/mediasource/streamingsession/StreamingSession.kt +++ b/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/mediasource/streamingsession/StreamingSession.kt @@ -1,16 +1,17 @@ package com.tidal.sdk.player.playbackengine.mediasource.streamingsession +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.Configuration import com.tidal.sdk.player.common.ForwardingMediaProduct import com.tidal.sdk.player.common.UUIDWrapper import com.tidal.sdk.player.common.model.ProductType -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.EventReporter import com.tidal.sdk.player.events.model.StreamingSessionStart import com.tidal.sdk.player.playbackengine.mediasource.streamingsession.StreamingSession.Explicit import com.tidal.sdk.player.playbackengine.mediasource.streamingsession.StreamingSession.Implicit import com.tidal.sdk.player.streamingapi.playbackinfo.model.PlaybackInfo import java.util.UUID +import kotlinx.coroutines.runBlocking internal sealed class StreamingSession private constructor( val id: UUID, @@ -102,7 +103,7 @@ internal sealed class StreamingSession private constructor( sealed class Creator private constructor( private val factory: T, - private val trueTimeWrapper: TrueTimeWrapper, + private val sntpClient: SNTPClient, private val eventReporter: EventReporter, ) { @@ -115,7 +116,7 @@ internal sealed class StreamingSession private constructor( eventReporter.report( StreamingSessionStart.Payload( it.id.toString(), - trueTimeWrapper.currentTimeMillis, + runBlocking { sntpClient.blockingEpochTime() }.inWholeMilliseconds, startReason, it.configuration.isOfflineMode, sessionProductType, @@ -126,18 +127,18 @@ internal sealed class StreamingSession private constructor( class Explicit( factory: Factory.Explicit, - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, eventReporter: EventReporter, - ) : Creator(factory, trueTimeWrapper, eventReporter) { + ) : Creator(factory, sntpClient, eventReporter) { override val startReason = StreamingSessionStart.StartReason.EXPLICIT } class Implicit( factory: Factory.Implicit, - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, eventReporter: EventReporter, - ) : Creator(factory, trueTimeWrapper, eventReporter) { + ) : Creator(factory, sntpClient, eventReporter) { override val startReason = StreamingSessionStart.StartReason.IMPLICIT } diff --git a/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/player/di/MediaSourcererModule.kt b/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/player/di/MediaSourcererModule.kt index ae50235f..ec0cf779 100644 --- a/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/player/di/MediaSourcererModule.kt +++ b/player/playback-engine/src/main/kotlin/com/tidal/sdk/player/playbackengine/player/di/MediaSourcererModule.kt @@ -23,10 +23,10 @@ import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy import androidx.media3.exoplayer.upstream.ParsingLoadable import androidx.media3.extractor.ExtractorsFactory import com.google.gson.Gson +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.Configuration import com.tidal.sdk.player.common.UUIDWrapper import com.tidal.sdk.player.commonandroid.Base64Codec -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.EventReporter import com.tidal.sdk.player.playbackengine.Encryption import com.tidal.sdk.player.playbackengine.PlayerLoadErrorHandlingPolicy @@ -392,7 +392,7 @@ internal object MediaSourcererModule { audioQualityRepository: AudioQualityRepository, videoQualityRepository: VideoQualityRepository, audioModeRepository: AudioModeRepository, - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, mediaDrmCallbackExceptionFactory: MediaDrmCallbackExceptionFactory, eventReporter: EventReporter, errorHandler: ErrorHandler, @@ -401,7 +401,7 @@ internal object MediaSourcererModule { audioQualityRepository, videoQualityRepository, audioModeRepository, - trueTimeWrapper, + sntpClient, mediaDrmCallbackExceptionFactory, eventReporter, errorHandler, @@ -501,11 +501,11 @@ internal object MediaSourcererModule { @Reusable fun explicitStreamingSessionCreator( streamingSessionFactoryExplicit: StreamingSession.Factory.Explicit, - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, eventReporter: EventReporter, ) = StreamingSession.Creator.Explicit( streamingSessionFactoryExplicit, - trueTimeWrapper, + sntpClient, eventReporter, ) @@ -521,11 +521,11 @@ internal object MediaSourcererModule { @Reusable fun implicitStreamingSessionCreator( streamingSessionFactoryImplicit: StreamingSession.Factory.Implicit, - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, eventReporter: EventReporter, ) = StreamingSession.Creator.Implicit( streamingSessionFactoryImplicit, - trueTimeWrapper, + sntpClient, eventReporter, ) @@ -537,13 +537,13 @@ internal object MediaSourcererModule { explicitStreamingSessionFactory: StreamingSession.Creator.Explicit, implicitStreamingSessionFactory: StreamingSession.Creator.Implicit, eventReporter: EventReporter, - trueTimeWrapper: TrueTimeWrapper, + sntpClient: SNTPClient, ) = MediaSourcerer( exoPlayer, playbackInfoMediaSourceFactory, explicitStreamingSessionFactory, implicitStreamingSessionFactory, eventReporter, - trueTimeWrapper, + sntpClient, ) } diff --git a/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/ExoPlayerPlaybackEngineTest.kt b/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/ExoPlayerPlaybackEngineTest.kt index 3ba1ac7a..3a3c8e22 100644 --- a/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/ExoPlayerPlaybackEngineTest.kt +++ b/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/ExoPlayerPlaybackEngineTest.kt @@ -19,6 +19,7 @@ import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.isNull import assertk.assertions.isSameAs +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.ForwardingMediaProduct import com.tidal.sdk.player.common.model.AssetPresentation import com.tidal.sdk.player.common.model.AudioMode @@ -27,7 +28,6 @@ import com.tidal.sdk.player.common.model.LoudnessNormalizationMode import com.tidal.sdk.player.common.model.MediaProduct import com.tidal.sdk.player.common.model.ProductType import com.tidal.sdk.player.common.model.VideoQuality -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.EventReporter import com.tidal.sdk.player.events.model.PlaybackSession.Payload.Action import com.tidal.sdk.player.events.model.PlaybackStatistics.Payload.Adaptation @@ -68,6 +68,7 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import kotlinx.coroutines.withTimeout import org.junit.jupiter.api.Assertions.assertSame import org.junit.jupiter.api.Assumptions.assumeFalse @@ -111,7 +112,7 @@ internal class ExoPlayerPlaybackEngineTest { private val audioQualityRepository = mock() private val audioModeRepository = mock() private val volumeHelper = mock() - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val eventReporter = mock() private val errorHandler = mock() private val djSessionManager = mock() @@ -140,7 +141,7 @@ internal class ExoPlayerPlaybackEngineTest { audioQualityRepository, audioModeRepository, volumeHelper, - trueTimeWrapper, + sntpClient, eventReporter, errorHandler, djSessionManager, @@ -319,19 +320,23 @@ internal class ExoPlayerPlaybackEngineTest { } @Test - fun playShouldSucceedIfPlaybackStateIsNotIdle() { + fun playShouldSucceedIfPlaybackStateIsNotIdle() = runTest { + whenever(sntpClient.blockingEpochTime()) doReturn 0.milliseconds + playbackEngine.load(forwardingMediaProduct.delegate) val streamingSession = mock() whenever(initialExtendedExoPlayer.currentStreamingSession) doReturn streamingSession - val idealStartTimestampMs = 8L - whenever(trueTimeWrapper.currentTimeMillis) doReturn idealStartTimestampMs + val idealStartTimestampMs = 8.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn idealStartTimestampMs playbackEngine.play() verify(initialExtendedExoPlayer).currentStreamingSession - verify(trueTimeWrapper, atLeastOnce()).currentTimeMillis + verify(sntpClient, atLeastOnce()).blockingEpochTime() verify(streamingSession).createUndeterminedPlaybackStatistics( - PlaybackStatistics.IdealStartTimestampMs.Known(idealStartTimestampMs), + PlaybackStatistics.IdealStartTimestampMs.Known( + idealStartTimestampMs.inWholeMilliseconds, + ), ) verify(initialExtendedExoPlayer).play() } @@ -344,7 +349,8 @@ internal class ExoPlayerPlaybackEngineTest { } @Test - fun pauseShouldSucceedIfPlaybackStateIsNotIdle() { + fun pauseShouldSucceedIfPlaybackStateIsNotIdle() = runTest { + whenever(sntpClient.blockingEpochTime()) doReturn 0.milliseconds playbackEngine.load(forwardingMediaProduct.delegate) playbackEngine.pause() @@ -360,7 +366,9 @@ internal class ExoPlayerPlaybackEngineTest { } @Test - fun seekShouldSucceedIfPlaybackStateIsNotIdle() { + fun seekShouldSucceedIfPlaybackStateIsNotIdle() = runTest { + whenever(sntpClient.blockingEpochTime()) doReturn 0.milliseconds + playbackEngine.load(forwardingMediaProduct.delegate) val seekToMs = 10f @@ -370,7 +378,9 @@ internal class ExoPlayerPlaybackEngineTest { } @Test - fun resetShouldResetThePlaybackEngine() { + fun resetShouldResetThePlaybackEngine() = runTest { + whenever(sntpClient.blockingEpochTime()) doReturn 0.milliseconds + playbackEngine.reset() verify(internalHandler).removeCallbacksAndMessages(null) @@ -386,7 +396,9 @@ internal class ExoPlayerPlaybackEngineTest { } @Test - fun resetShouldResetThePlaybackEngineIfLoadIsCalled() { + fun resetShouldResetThePlaybackEngineIfLoadIsCalled() = runTest { + whenever(sntpClient.blockingEpochTime()) doReturn 0.milliseconds + playbackEngine.load(forwardingMediaProduct.delegate) playbackEngine.reset() @@ -482,10 +494,11 @@ internal class ExoPlayerPlaybackEngineTest { assertSame(initialExtendedExoPlayer, playbackEngine.reflectionExtendedExoPlayer) @Test - fun resetReleasesCurrentExoPlayerAndCreatesANewOne() { + fun resetReleasesCurrentExoPlayerAndCreatesANewOne() = runTest { val newExtendedExoPlayer = mock() whenever(extendedExoPlayerFactory.create(playbackEngine, playbackEngine)) .thenReturn(newExtendedExoPlayer) + whenever(sntpClient.blockingEpochTime()) doReturn 0.milliseconds playbackEngine.reset() @@ -819,8 +832,9 @@ internal class ExoPlayerPlaybackEngineTest { Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS, ], ) - fun onPlaybackSuppressionReasonChangedShouldSetActionForTrack(reason: Int) = + fun onPlaybackSuppressionReasonChangedShouldSetActionForTrack(reason: Int) = runTest { testOnPlaybackSuppressionReasonChanged(reason, ProductType.TRACK) + } @ParameterizedTest @ValueSource( @@ -829,8 +843,9 @@ internal class ExoPlayerPlaybackEngineTest { Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS, ], ) - fun onPlaybackSuppressionReasonChangedShouldSetActionForVideo(reason: Int) = + fun onPlaybackSuppressionReasonChangedShouldSetActionForVideo(reason: Int) = runTest { testOnPlaybackSuppressionReasonChanged(reason, ProductType.VIDEO) + } @ParameterizedTest @ValueSource( @@ -841,10 +856,13 @@ internal class ExoPlayerPlaybackEngineTest { ) fun onPlaybackSuppressionReasonChangedShouldSetActionAndPotentiallySeekForBroadcast( reason: Int, - ) = testOnPlaybackSuppressionReasonChanged(reason, ProductType.BROADCAST) + ) = runTest { testOnPlaybackSuppressionReasonChanged(reason, ProductType.BROADCAST) } @Suppress("LongMethod") - private fun testOnPlaybackSuppressionReasonChanged(reason: Int, productType: ProductType) { + private suspend fun testOnPlaybackSuppressionReasonChanged( + reason: Int, + productType: ProductType, + ) { val forwardingMediaProduct = mock> { on { it.productType } doReturn productType } @@ -904,8 +922,8 @@ internal class ExoPlayerPlaybackEngineTest { on { it.actions } doReturn actions } playbackEngine.reflectionCurrentPlaybackSession = currentPlaybackSession - val currentTimeMills = -1L - whenever(trueTimeWrapper.currentTimeMillis) doReturn currentTimeMills + val currentTime = 1.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime val actionType = if (reason == Player.PLAYBACK_SUPPRESSION_REASON_NONE) { Action.Type.PLAYBACK_START } else { @@ -926,10 +944,10 @@ internal class ExoPlayerPlaybackEngineTest { verify(initialExtendedExoPlayer).currentPositionSinceEpochMs } verify(currentPlaybackSession).actions - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() verify(actions).add( Action( - currentTimeMills, + currentTime.inWholeMilliseconds, positionSeconds, actionType, ), @@ -1151,7 +1169,7 @@ internal class ExoPlayerPlaybackEngineTest { @Suppress("LongMethod") @Test - fun onPlaybackStateChangedToEndedResetsToIdleAndReports() { + fun onPlaybackStateChangedToEndedResetsToIdleAndReports() = runTest { playbackEngine.testMediaSource = mediaSource val playbackContext = mock() playbackEngine.reflectionPlaybackContext = playbackContext @@ -1178,8 +1196,8 @@ internal class ExoPlayerPlaybackEngineTest { on { it.actions } doReturn actions } playbackEngine.reflectionCurrentPlaybackSession = currentPlaybackSession - val currentTimeMillis = 12345L - whenever(trueTimeWrapper.currentTimeMillis) doReturn currentTimeMillis + val currentTimeMillis = 12345.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTimeMillis val currentPlaybackPositionMs = -12314351234 val mediaItem = MediaItem.Builder() @@ -1258,7 +1276,7 @@ internal class ExoPlayerPlaybackEngineTest { sourceType, sourceId, actions, - currentTimeMillis, + currentTimeMillis.inWholeMilliseconds, currentPlaybackPositionMs.toDouble() / 1_000, ), ) @@ -1280,47 +1298,49 @@ internal class ExoPlayerPlaybackEngineTest { Player.DISCONTINUITY_REASON_INTERNAL, ], ) - fun onPositionDiscontinuityShouldOnlyUpdatePosWhenNotAutoTransitionOrSeeking(reason: Int) { - val forwardingMediaProduct = mock>() - val mediaSource = mock { - on { it.forwardingMediaProduct } doReturn forwardingMediaProduct - } - val nextMediaProduct = mock() - val nextForwardingMediaProduct = mock> { - on { it.delegate } doReturn nextMediaProduct - } - val nextMediaSource = mock { - on { it.forwardingMediaProduct } doReturn nextForwardingMediaProduct - } - val playbackContext = mock() - val nextPlaybackContext = mock() - playbackEngine.testMediaSource = mediaSource - playbackEngine.testNextMediaSource = nextMediaSource - playbackEngine.reflectionPlaybackContext = playbackContext - playbackEngine.reflectionNextPlaybackContext = nextPlaybackContext - val positionMs = 87L - val newPositionInfo = spy( - Player.PositionInfo( - null, - -1, - null, - -1, - positionMs, - -1, - -1, - -1, - ), - ) + fun onPositionDiscontinuityShouldOnlyUpdatePosWhenNotAutoTransitionOrSeeking(reason: Int) = + runTest { + val forwardingMediaProduct = mock>() + val mediaSource = mock { + on { it.forwardingMediaProduct } doReturn forwardingMediaProduct + } + val nextMediaProduct = mock() + val nextForwardingMediaProduct = mock> { + on { it.delegate } doReturn nextMediaProduct + } + val nextMediaSource = mock { + on { it.forwardingMediaProduct } doReturn nextForwardingMediaProduct + } + val playbackContext = mock() + val nextPlaybackContext = mock() + playbackEngine.testMediaSource = mediaSource + playbackEngine.testNextMediaSource = nextMediaSource + playbackEngine.reflectionPlaybackContext = playbackContext + playbackEngine.reflectionNextPlaybackContext = nextPlaybackContext + val positionMs = 87L + val newPositionInfo = spy( + Player.PositionInfo( + null, + -1, + null, + -1, + positionMs, + -1, + -1, + -1, + ), + ) + whenever(sntpClient.blockingEpochTime()) doReturn 0.milliseconds - playbackEngine.onPositionDiscontinuity(mock(), mock(), newPositionInfo, reason) + playbackEngine.onPositionDiscontinuity(mock(), mock(), newPositionInfo, reason) - verify(initialExtendedExoPlayer).updatePosition(positionMs) - assertThat(playbackEngine.mediaProduct).isSameAs(forwardingMediaProduct.delegate) - assertThat(playbackEngine.testNextMediaSource).isSameAs(nextMediaSource) - assertThat(playbackEngine.playbackContext).isSameAs(playbackContext) - assertThat(playbackEngine.reflectionNextPlaybackContext).isSameAs(nextPlaybackContext) - verifyNoMoreInteractions(initialExtendedExoPlayer, newPositionInfo) - } + verify(initialExtendedExoPlayer).updatePosition(positionMs) + assertThat(playbackEngine.mediaProduct).isSameAs(forwardingMediaProduct.delegate) + assertThat(playbackEngine.testNextMediaSource).isSameAs(nextMediaSource) + assertThat(playbackEngine.playbackContext).isSameAs(playbackContext) + assertThat(playbackEngine.reflectionNextPlaybackContext).isSameAs(nextPlaybackContext) + verifyNoMoreInteractions(initialExtendedExoPlayer, newPositionInfo) + } @Suppress("LongMethod") @ParameterizedTest @@ -1357,11 +1377,13 @@ internal class ExoPlayerPlaybackEngineTest { playbackEngine.testNextMediaSource = nextMediaSource playbackEngine.reflectionPlaybackContext = mock() playbackEngine.reflectionNextPlaybackContext = nextPlaybackContext - val startTimestampMs = -1L - whenever(trueTimeWrapper.currentTimeMillis) doReturn startTimestampMs + val startTimestampMs = 1.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn startTimestampMs val startedPlaybackStatistics = mock() val preparedPlaybackStatistics = mock { - on { it.toStarted(startTimestampMs) } doReturn startedPlaybackStatistics + on { + it.toStarted(startTimestampMs.inWholeMilliseconds) + } doReturn startedPlaybackStatistics } val undeterminedPlaybackStatisticsWithIdealStartTimestampMs = mock() @@ -1369,7 +1391,9 @@ internal class ExoPlayerPlaybackEngineTest { on { copy( idealStartTimestampMs = - PlaybackStatistics.IdealStartTimestampMs.Known(startTimestampMs), + PlaybackStatistics.IdealStartTimestampMs.Known( + startTimestampMs.inWholeMilliseconds, + ), ) }.thenReturn(undeterminedPlaybackStatisticsWithIdealStartTimestampMs) } @@ -1478,11 +1502,11 @@ internal class ExoPlayerPlaybackEngineTest { verify(initialExtendedExoPlayer).onCurrentItemFinished() verify(volumeHelper).getVolume(playbackInfo) verify(initialExtendedExoPlayer).volume = 1.0F - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() verify(undeterminedPlaybackSessionResolver) .invoke(undeterminedPlaybackStatisticsWithIdealStartTimestampMs, playbackInfo) - verify(preparedPlaybackStatistics).toStarted(startTimestampMs) - verify(nextPlaybackSession).startTimestamp = startTimestampMs + verify(preparedPlaybackStatistics).toStarted(startTimestampMs.inWholeMilliseconds) + verify(nextPlaybackSession).startTimestamp = startTimestampMs.inWholeMilliseconds verify(nextPlaybackSession).startAssetPosition = positionMs.toDouble() / 1_000 verify(timeline).getWindow(eq(windowIndex), any()) verifyNoMoreInteractions( @@ -1501,7 +1525,7 @@ internal class ExoPlayerPlaybackEngineTest { @EnumSource(ProductType::class) fun onPositionDiscontinuityWhenAutoTransitionShouldEmitMediaProductTransitionAndUpdateEventInfoForRepeatModeOne( productType: ProductType, - ) { + ) = runTest { val duration = 24000L val productId = "123" val sourceType = "sourceType" @@ -1534,8 +1558,8 @@ internal class ExoPlayerPlaybackEngineTest { ) playbackEngine.testMediaSource = mediaSource playbackEngine.reflectionPlaybackContext = currentPlaybackContext - val startTimestampMs = -1L - whenever(trueTimeWrapper.currentTimeMillis) doReturn startTimestampMs + val startTimestampMs = 1.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn startTimestampMs val eventPlaybackPositionMs = Long.MAX_VALUE val eventTime = EventTime( -1, @@ -1585,7 +1609,9 @@ internal class ExoPlayerPlaybackEngineTest { val newStreamingSessionId = mock() val startedPlaybackStatistics = mock() val preparedPlaybackStatistics = mock { - on { it.toStarted(startTimestampMs) } doReturn startedPlaybackStatistics + on { + it.toStarted(startTimestampMs.inWholeMilliseconds) + } doReturn startedPlaybackStatistics } val undeterminedPlaybackStatistics = mock() whenever(undeterminedPlaybackSessionResolver(undeterminedPlaybackStatistics, playbackInfo)) @@ -1597,7 +1623,9 @@ internal class ExoPlayerPlaybackEngineTest { } doReturn currentPlaybackSession on { createUndeterminedPlaybackStatistics( - PlaybackStatistics.IdealStartTimestampMs.Known(startTimestampMs), + PlaybackStatistics.IdealStartTimestampMs.Known( + startTimestampMs.inWholeMilliseconds, + ), ) }.thenReturn(undeterminedPlaybackStatistics) } @@ -1638,9 +1666,9 @@ internal class ExoPlayerPlaybackEngineTest { verify(initialExtendedExoPlayer).updatePosition(positionMs) verify(initialExtendedExoPlayer).onRepeatOne(currentForwardingMediaProduct) verify(volumeHelper).getVolume(playbackInfo) - verify(trueTimeWrapper).currentTimeMillis - verify(preparedPlaybackStatistics).toStarted(startTimestampMs) - verify(currentPlaybackSession).startTimestamp = startTimestampMs + verify(sntpClient).blockingEpochTime() + verify(preparedPlaybackStatistics).toStarted(startTimestampMs.inWholeMilliseconds) + verify(currentPlaybackSession).startTimestamp = startTimestampMs.inWholeMilliseconds verify(currentPlaybackSession).startAssetPosition = positionMs.toDouble() / 1_000 verifyNoMoreInteractions( startedPlaybackStatistics, @@ -1653,8 +1681,9 @@ internal class ExoPlayerPlaybackEngineTest { } @Test - fun onPositionDiscontinuityWhenAutoTransitionShouldThrowExceptionForRepeatModeAll() { + fun onPositionDiscontinuityWhenAutoTransitionShouldThrowExceptionForRepeatModeAll() = runTest { whenever(initialExtendedExoPlayer.repeatMode).thenReturn(Player.REPEAT_MODE_ALL) + whenever(sntpClient.blockingEpochTime()) doReturn 0.milliseconds runBlocking { launch { @@ -1674,178 +1703,180 @@ internal class ExoPlayerPlaybackEngineTest { @Suppress("LongMethod") @Test - fun onPositionDiscontinuityDueToSeekShouldCreateStallIfShouldNotStartPlaybackAfterUserAction() { - val currentPlaybackPositionMs = 5L - val eventTime = EventTime( - -1, - Timeline.EMPTY, - -1, - null, - -1, - Timeline.EMPTY, - -1, - null, - currentPlaybackPositionMs, - -1, - ) - val oldPositionMs = 345678L - val oldPosition = spy( - Player.PositionInfo( - null, - 0, - null, - -1, - oldPositionMs, - -1L, + fun onPositionDiscontinuityDueToSeekShouldCreateStallIfShouldNotStartPlaybackAfterUserAction() = + runTest { + val currentPlaybackPositionMs = 5L + val eventTime = EventTime( -1, + Timeline.EMPTY, -1, - ), - ) - val newPositionMs = 44444L - val newPosition = spy( - Player.PositionInfo( - null, - 0, null, -1, - newPositionMs, - -1L, + Timeline.EMPTY, -1, + null, + currentPlaybackPositionMs, -1, - ), - ) - val extendedExoPlayer = mock() - playbackEngine.reflectionExtendedExoPlayer = extendedExoPlayer - whenever(extendedExoPlayer.shouldStartPlaybackAfterUserAction()) doReturn false - val currentTimeMills = -80L - whenever(trueTimeWrapper.currentTimeMillis) doReturn currentTimeMills - val actions = mock>() - val currentPlaybackSession = mock { - on { it.actions } doReturn actions - } - playbackEngine.reflectionCurrentPlaybackSession = currentPlaybackSession + ) + val oldPositionMs = 345678L + val oldPosition = spy( + Player.PositionInfo( + null, + 0, + null, + -1, + oldPositionMs, + -1L, + -1, + -1, + ), + ) + val newPositionMs = 44444L + val newPosition = spy( + Player.PositionInfo( + null, + 0, + null, + -1, + newPositionMs, + -1L, + -1, + -1, + ), + ) + val extendedExoPlayer = mock() + playbackEngine.reflectionExtendedExoPlayer = extendedExoPlayer + whenever(extendedExoPlayer.shouldStartPlaybackAfterUserAction()) doReturn false + val currentTimeMillis = 80.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTimeMillis + val actions = mock>() + val currentPlaybackSession = mock { + on { it.actions } doReturn actions + } + playbackEngine.reflectionCurrentPlaybackSession = currentPlaybackSession - playbackEngine.onPositionDiscontinuity( - eventTime, - oldPosition, - newPosition, - Player.DISCONTINUITY_REASON_SEEK, - ) + playbackEngine.onPositionDiscontinuity( + eventTime, + oldPosition, + newPosition, + Player.DISCONTINUITY_REASON_SEEK, + ) - verify(extendedExoPlayer).updatePosition(newPositionMs) - verify(extendedExoPlayer).shouldStartPlaybackAfterUserAction() - verify(trueTimeWrapper).currentTimeMillis - verify(currentPlaybackSession).actions - inOrder(actions).apply { - verify(actions).add( - Action( - currentTimeMills, - oldPositionMs.toDouble() / 1_000, - Action.Type.PLAYBACK_STOP, + verify(extendedExoPlayer).updatePosition(newPositionMs) + verify(extendedExoPlayer).shouldStartPlaybackAfterUserAction() + verify(sntpClient).blockingEpochTime() + verify(currentPlaybackSession).actions + inOrder(actions).apply { + verify(actions).add( + Action( + currentTimeMillis.inWholeMilliseconds, + oldPositionMs.toDouble() / 1_000, + Action.Type.PLAYBACK_STOP, + ), + ) + verify(actions).add( + Action( + currentTimeMillis.inWholeMilliseconds, + newPositionMs.toDouble() / 1_000, + Action.Type.PLAYBACK_START, + ), + ) + } + assertThat(playbackEngine.reflectionCurrentStall).isEqualTo( + StartedStall( + Reason.SEEK, + currentPlaybackPositionMs.toDouble() / 1_000, + currentTimeMillis.inWholeMilliseconds, ), ) - verify(actions).add( - Action( - currentTimeMills, - newPositionMs.toDouble() / 1_000, - Action.Type.PLAYBACK_START, - ), + verifyNoMoreInteractions( + oldPosition, + newPosition, + extendedExoPlayer, + actions, + currentPlaybackSession, ) } - assertThat(playbackEngine.reflectionCurrentStall).isEqualTo( - StartedStall( - Reason.SEEK, - currentPlaybackPositionMs.toDouble() / 1_000, - currentTimeMills, - ), - ) - verifyNoMoreInteractions( - oldPosition, - newPosition, - extendedExoPlayer, - actions, - currentPlaybackSession, - ) - } @Suppress("LongMethod") @Test - fun onPositionDiscontinuityDueToSeekShouldNotCreateStallIfShouldStartPlaybackAfterUserAction() { - val eventTime = mock() - val oldPositionMs = 345678L - val oldPosition = spy( - Player.PositionInfo( - null, - 0, - null, - -1, - oldPositionMs, - -1L, - -1, - -1, - ), - ) - val newPositionMs = 44444L - val newPosition = spy( - Player.PositionInfo( - null, - 0, - null, - -1, - newPositionMs, - -1L, - -1, - -1, - ), - ) - val extendedExoPlayer = mock() - playbackEngine.reflectionExtendedExoPlayer = extendedExoPlayer - whenever(extendedExoPlayer.shouldStartPlaybackAfterUserAction()) doReturn true - val currentTimeMills = -80L - whenever(trueTimeWrapper.currentTimeMillis) doReturn currentTimeMills - val actions = mock>() - val currentPlaybackSession = mock { - on { it.actions } doReturn actions - } - playbackEngine.reflectionCurrentPlaybackSession = currentPlaybackSession - - playbackEngine.onPositionDiscontinuity( - eventTime, - oldPosition, - newPosition, - Player.DISCONTINUITY_REASON_SEEK, - ) - - verify(extendedExoPlayer).updatePosition(newPositionMs) - verify(extendedExoPlayer).shouldStartPlaybackAfterUserAction() - verify(trueTimeWrapper).currentTimeMillis - verify(currentPlaybackSession).actions - inOrder(actions).apply { - verify(actions).add( - Action( - currentTimeMills, - oldPositionMs.toDouble() / 1_000, - Action.Type.PLAYBACK_STOP, + fun onPositionDiscontinuityDueToSeekShouldNotCreateStallIfShouldStartPlaybackAfterUserAction() = + runTest { + val eventTime = mock() + val oldPositionMs = 345678L + val oldPosition = spy( + Player.PositionInfo( + null, + 0, + null, + -1, + oldPositionMs, + -1L, + -1, + -1, ), ) - verify(actions).add( - Action( - currentTimeMills, - newPositionMs.toDouble() / 1_000, - Action.Type.PLAYBACK_START, + val newPositionMs = 44444L + val newPosition = spy( + Player.PositionInfo( + null, + 0, + null, + -1, + newPositionMs, + -1L, + -1, + -1, ), ) + val extendedExoPlayer = mock() + playbackEngine.reflectionExtendedExoPlayer = extendedExoPlayer + whenever(extendedExoPlayer.shouldStartPlaybackAfterUserAction()) doReturn true + val currentTime = 80.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime + val actions = mock>() + val currentPlaybackSession = mock { + on { it.actions } doReturn actions + } + playbackEngine.reflectionCurrentPlaybackSession = currentPlaybackSession + + playbackEngine.onPositionDiscontinuity( + eventTime, + oldPosition, + newPosition, + Player.DISCONTINUITY_REASON_SEEK, + ) + + verify(extendedExoPlayer).updatePosition(newPositionMs) + verify(extendedExoPlayer).shouldStartPlaybackAfterUserAction() + verify(sntpClient).blockingEpochTime() + verify(currentPlaybackSession).actions + inOrder(actions).apply { + verify(actions).add( + Action( + currentTime.inWholeMilliseconds, + oldPositionMs.toDouble() / 1_000, + Action.Type.PLAYBACK_STOP, + ), + ) + verify(actions).add( + Action( + currentTime.inWholeMilliseconds, + newPositionMs.toDouble() / 1_000, + Action.Type.PLAYBACK_START, + ), + ) + } + assertThat(playbackEngine.reflectionCurrentStall).isNull() + verifyNoMoreInteractions( + eventTime, + oldPosition, + newPosition, + extendedExoPlayer, + actions, + currentPlaybackSession, + ) } - assertThat(playbackEngine.reflectionCurrentStall).isNull() - verifyNoMoreInteractions( - eventTime, - oldPosition, - newPosition, - extendedExoPlayer, - actions, - currentPlaybackSession, - ) - } @Test fun onTimelineChangedShouldDoNothingIfDurationIsUnset() { @@ -2154,71 +2185,72 @@ internal class ExoPlayerPlaybackEngineTest { @Suppress("LongMethod") @Test - fun onAudioPositionAdvancingShouldRecordStartTimestampIfCurrentPlaybackStatisticsArePrepared() { - val forwardingMediaProduct = mock>() - val mediaSource = mock { - on { it.forwardingMediaProduct } doReturn forwardingMediaProduct - } - playbackEngine.testMediaSource = mediaSource - val mediaItem = MediaItem.Builder() - .setMediaId(forwardingMediaProduct.hashCode().toString()) - .build() - val windowZero = Timeline.Window() - .set( - Unit, - mediaItem, + fun onAudioPositionAdvancingShouldRecordStartTimestampIfCurrentPlaybackStatisticsArePrepared() = + runTest { + val forwardingMediaProduct = mock>() + val mediaSource = mock { + on { it.forwardingMediaProduct } doReturn forwardingMediaProduct + } + playbackEngine.testMediaSource = mediaSource + val mediaItem = MediaItem.Builder() + .setMediaId(forwardingMediaProduct.hashCode().toString()) + .build() + val windowZero = Timeline.Window() + .set( + Unit, + mediaItem, + null, + -1L, + -1L, + -1L, + false, + false, + null, + -1L, + -1L, + -1, + -1, + -1L, + ) + val timeline = mock { + on { it.windowCount } doReturn 1 + on { it.getWindow(eq(0), any()) } doReturn windowZero + } + val eventTime = EventTime( + -1, + timeline, + 0, null, - -1L, - -1L, - -1L, - false, - false, + -123, + Timeline.EMPTY, + -1, null, - -1L, - -1L, -1, -1, - -1L, ) - val timeline = mock { - on { it.windowCount } doReturn 1 - on { it.getWindow(eq(0), any()) } doReturn windowZero - } - val eventTime = EventTime( - -1, - timeline, - 0, - null, - -123, - Timeline.EMPTY, - -1, - null, - -1, - -1, - ) - val currentTimeMillis = 5L - whenever(trueTimeWrapper.currentTimeMillis) doReturn currentTimeMillis - val startedPlaybackStatistics = mock() - val preparedPlaybackStatistics = mock { - on { toStarted(currentTimeMillis) } doReturn startedPlaybackStatistics + val currentTime = 5.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime + val startedPlaybackStatistics = mock() + val preparedPlaybackStatistics = mock { + on { toStarted(currentTime.inWholeMilliseconds) } doReturn startedPlaybackStatistics + } + playbackEngine.reflectionCurrentPlaybackStatistics = preparedPlaybackStatistics + val currentPlaybackSession = mock() + playbackEngine.reflectionCurrentPlaybackSession = currentPlaybackSession + + playbackEngine.onAudioPositionAdvancing(eventTime, Long.MAX_VALUE) + + verify(sntpClient).blockingEpochTime() + verify(preparedPlaybackStatistics).toStarted(currentTime.inWholeMilliseconds) + verify(currentPlaybackSession).startTimestamp = currentTime.inWholeMilliseconds + assertThat(playbackEngine.reflectionCurrentPlaybackStatistics) + .isSameAs(startedPlaybackStatistics) + verifyNoMoreInteractions( + startedPlaybackStatistics, + preparedPlaybackStatistics, + currentPlaybackSession, + ) } - playbackEngine.reflectionCurrentPlaybackStatistics = preparedPlaybackStatistics - val currentPlaybackSession = mock() - playbackEngine.reflectionCurrentPlaybackSession = currentPlaybackSession - - playbackEngine.onAudioPositionAdvancing(eventTime, Long.MAX_VALUE) - - verify(trueTimeWrapper).currentTimeMillis - verify(preparedPlaybackStatistics).toStarted(currentTimeMillis) - verify(currentPlaybackSession).startTimestamp = currentTimeMillis - assertThat(playbackEngine.reflectionCurrentPlaybackStatistics) - .isSameAs(startedPlaybackStatistics) - verifyNoMoreInteractions( - startedPlaybackStatistics, - preparedPlaybackStatistics, - currentPlaybackSession, - ) - } @ParameterizedTest @ValueSource(ints = [0, 1]) @@ -2248,7 +2280,7 @@ internal class ExoPlayerPlaybackEngineTest { private fun testTrackAdaptationOnValidWindowIndex( targetWindowIndex: Int, call: ExoPlayerPlaybackEngine.(EventTime, Format) -> Unit, - ) { + ) = runTest { val eventPlaybackPositionMs = 0L val eventTime = EventTime( -1, @@ -2262,8 +2294,8 @@ internal class ExoPlayerPlaybackEngineTest { -1, -1, ) - val currentTimeMillis = -38L - whenever(trueTimeWrapper.currentTimeMillis) doReturn currentTimeMillis + val currentTime = 38.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime val sampleMimeType = "sampleMimeType" val codecs = "codecs" val bitrate = Int.MAX_VALUE @@ -2289,7 +2321,7 @@ internal class ExoPlayerPlaybackEngineTest { } val targetAdaptation = Adaptation( eventPlaybackPositionMs.toDouble() / 1_000, - currentTimeMillis, + currentTime.inWholeMilliseconds, sampleMimeType, codecs, bitrate, @@ -2305,7 +2337,7 @@ internal class ExoPlayerPlaybackEngineTest { playbackEngine.call(eventTime, format) - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() verify(targetPlaybackStatistics) + targetAdaptation assertThat( when (targetWindowIndex) { @@ -2438,7 +2470,7 @@ internal class ExoPlayerPlaybackEngineTest { } @Test - fun setCurrentStallCompletesPreviousCurrentStallIfItWasNotNull() { + fun setCurrentStallCompletesPreviousCurrentStallIfItWasNotNull() = runTest { val currentPlaybackStatistics = mock() playbackEngine.reflectionCurrentPlaybackStatistics = currentPlaybackStatistics val reason = mock() @@ -2450,13 +2482,13 @@ internal class ExoPlayerPlaybackEngineTest { on { it.startTimestamp } doReturn startTimestamp } playbackEngine.reflectionCurrentStall = initialCurrentStartedStall - val currentTimeMillis = 2L - whenever(trueTimeWrapper.currentTimeMillis) doReturn currentTimeMillis + val currentTime = 2.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime val completedStall = Stall( reason, assetPositionSeconds, startTimestamp, - currentTimeMillis, + currentTime.inWholeMilliseconds, ) val updatedPlaybackStatistics = mock() whenever(currentPlaybackStatistics + completedStall) doReturn updatedPlaybackStatistics @@ -2466,7 +2498,7 @@ internal class ExoPlayerPlaybackEngineTest { verify(initialCurrentStartedStall).reason verify(initialCurrentStartedStall).assetPositionSeconds verify(initialCurrentStartedStall).startTimestamp - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() verify(currentPlaybackStatistics) + completedStall assertThat(playbackEngine.reflectionCurrentStall).isNull() assertThat(playbackEngine.reflectionCurrentPlaybackStatistics) @@ -2490,7 +2522,7 @@ internal class ExoPlayerPlaybackEngineTest { } @Test - fun onAudioUnderrunShouldCreateUnexpectedStall() { + fun onAudioUnderrunShouldCreateUnexpectedStall() = runTest { val currentPlaybackPositionMs = 5123132515 val eventTime = EventTime( -1, @@ -2504,8 +2536,8 @@ internal class ExoPlayerPlaybackEngineTest { currentPlaybackPositionMs, -1, ) - val currentTimeMillis = Long.MAX_VALUE - whenever(trueTimeWrapper.currentTimeMillis) doReturn currentTimeMillis + val currentTime = Long.MAX_VALUE.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn currentTime playbackEngine.onAudioUnderrun(eventTime, -1, 0, Long.MIN_VALUE) @@ -2513,7 +2545,7 @@ internal class ExoPlayerPlaybackEngineTest { StartedStall( Reason.UNEXPECTED, currentPlaybackPositionMs.toDouble() / 1_000, - currentTimeMillis, + currentTime.inWholeMilliseconds, ), ) } diff --git a/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/StreamingApiRepositoryTest.kt b/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/StreamingApiRepositoryTest.kt index f2b9991f..ec4f7429 100644 --- a/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/StreamingApiRepositoryTest.kt +++ b/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/StreamingApiRepositoryTest.kt @@ -6,12 +6,12 @@ import assertk.assertThat import assertk.assertions.hasCause import assertk.assertions.isInstanceOf import assertk.assertions.isSameAs +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.ForwardingMediaProduct import com.tidal.sdk.player.common.model.AudioQuality import com.tidal.sdk.player.common.model.ProductQuality import com.tidal.sdk.player.common.model.ProductType import com.tidal.sdk.player.common.model.VideoQuality -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.EventReporter import com.tidal.sdk.player.events.model.DrmLicenseFetch import com.tidal.sdk.player.events.model.EndReason @@ -28,6 +28,7 @@ import com.tidal.sdk.player.streamingapi.drm.model.DrmLicenseRequest import com.tidal.sdk.player.streamingapi.playbackinfo.model.PlaybackInfo import com.tidal.sdk.player.streamingapi.playbackinfo.model.PlaybackMode import java.io.IOException +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest @@ -45,7 +46,7 @@ internal class StreamingApiRepositoryTest { private val audioQualityRepository = mock() private val videoQualityRepository = mock() private val audioModeRepository = mock() - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val mediaDrmCallbackExceptionFactory = mock() private val eventReporter = mock() private val errorHandler = mock() @@ -54,7 +55,7 @@ internal class StreamingApiRepositoryTest { audioQualityRepository, videoQualityRepository, audioModeRepository, - trueTimeWrapper, + sntpClient, mediaDrmCallbackExceptionFactory, eventReporter, errorHandler, @@ -62,14 +63,14 @@ internal class StreamingApiRepositoryTest { @Test fun getDrmLicenseOnSuccessCallReturnsAndReports() = runBlocking { - val startTimestamp = 1L - val endTimestamp = -2L + val startTimestamp = 1.milliseconds + val endTimestamp = 2.milliseconds val streamingSessionId = "streamingSessionId" val drmLicenseRequest = mock { on { it.streamingSessionId } doReturn streamingSessionId } val expectedDrmLicense = mock() - whenever(trueTimeWrapper.currentTimeMillis).thenReturn(startTimestamp, endTimestamp) + whenever(sntpClient.blockingEpochTime()).thenReturn(startTimestamp, endTimestamp) whenever(streamingApi.getDrmLicense(drmLicenseRequest)).thenReturn(expectedDrmLicense) val actual = streamingApiRepository.getDrmLicense(drmLicenseRequest) @@ -78,8 +79,8 @@ internal class StreamingApiRepositoryTest { verify(eventReporter).report( DrmLicenseFetch.Payload( streamingSessionId, - startTimestamp, - endTimestamp, + startTimestamp.inWholeMilliseconds, + endTimestamp.inWholeMilliseconds, EndReason.COMPLETE, null, null, @@ -89,8 +90,8 @@ internal class StreamingApiRepositoryTest { @Test fun getDrmLicenseOnFailureThrowsAndReports() = runBlocking { - val startTimestamp = 1L - val endTimestamp = -2L + val startTimestamp = 1.milliseconds + val endTimestamp = 2.milliseconds val streamingSessionId = "streamingSessionId" val drmLicenseRequest = mock { on { it.streamingSessionId } doReturn streamingSessionId @@ -101,7 +102,7 @@ internal class StreamingApiRepositoryTest { } val errorCode = "errorCode" val mediaDrmCallbackException = mock() - whenever(trueTimeWrapper.currentTimeMillis).thenReturn(startTimestamp, endTimestamp) + whenever(sntpClient.blockingEpochTime()).thenReturn(startTimestamp, endTimestamp) whenever(streamingApi.getDrmLicense(drmLicenseRequest)) doThrow runtimeException whenever(mediaDrmCallbackExceptionFactory.create(runtimeException)) .thenReturn(mediaDrmCallbackException) @@ -118,8 +119,8 @@ internal class StreamingApiRepositoryTest { verify(eventReporter).report( DrmLicenseFetch.Payload( streamingSessionId, - startTimestamp, - endTimestamp, + startTimestamp.inWholeMilliseconds, + endTimestamp.inWholeMilliseconds, EndReason.ERROR, errorMessage, errorCode, @@ -132,8 +133,8 @@ internal class StreamingApiRepositoryTest { @EnumSource(ProductType::class) fun testGetPlaybackInfoForStreamingOnSuccessCallReturnsAndReports(productType: ProductType) = runBlocking { - val startTimestamp = 5L - val endTimestamp = 0L + val startTimestamp = 5.milliseconds + val endTimestamp = 0.milliseconds val streamingSessionId = "streamingSessionId" val productId = "33" val mediaProduct = mock> { @@ -194,7 +195,7 @@ internal class StreamingApiRepositoryTest { ).thenReturn(expected) } } - whenever(trueTimeWrapper.currentTimeMillis).thenReturn(startTimestamp, endTimestamp) + whenever(sntpClient.blockingEpochTime()).thenReturn(startTimestamp, endTimestamp) val actual = streamingApiRepository.getPlaybackInfoForStreaming( streamingSessionId, @@ -205,8 +206,8 @@ internal class StreamingApiRepositoryTest { verify(eventReporter).report( PlaybackInfoFetch.Payload( streamingSessionId, - startTimestamp, - endTimestamp, + startTimestamp.inWholeMilliseconds, + endTimestamp.inWholeMilliseconds, EndReason.COMPLETE, null, null, @@ -235,11 +236,11 @@ internal class StreamingApiRepositoryTest { @EnumSource(ProductType::class) fun testGetPlaybackInfoForStreamingOnFailureCallReturnsAndReports(productType: ProductType) = runBlocking { - val startTimestamp = -9L - val endTimestamp = -4L + val startTimestamp = 9.milliseconds + val endTimestamp = 4.milliseconds val errorMessage = "errorMessage" val errorCode = "errorCode" - whenever(trueTimeWrapper.currentTimeMillis).thenReturn(startTimestamp, endTimestamp) + whenever(sntpClient.blockingEpochTime()).thenReturn(startTimestamp, endTimestamp) val streamingSessionId = "streamingSessionId" val productId = "33" val mediaProduct = mock> { @@ -321,8 +322,8 @@ internal class StreamingApiRepositoryTest { verify(eventReporter).report( PlaybackInfoFetch.Payload( streamingSessionId, - startTimestamp, - endTimestamp, + startTimestamp.inWholeMilliseconds, + endTimestamp.inWholeMilliseconds, EndReason.ERROR, errorMessage, errorCode, diff --git a/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/mediasource/MediaSourcererTest.kt b/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/mediasource/MediaSourcererTest.kt index c76881fc..83c49919 100644 --- a/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/mediasource/MediaSourcererTest.kt +++ b/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/mediasource/MediaSourcererTest.kt @@ -4,14 +4,16 @@ import androidx.media3.exoplayer.ExoPlayer import assertk.assertThat import assertk.assertions.isNull import assertk.assertions.isSameAs +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.ForwardingMediaProduct import com.tidal.sdk.player.common.model.MediaProduct import com.tidal.sdk.player.common.model.ProductType -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.EventReporter import com.tidal.sdk.player.events.model.StreamingSessionEnd import com.tidal.sdk.player.playbackengine.mediasource.streamingsession.StreamingSession import java.util.UUID +import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test import org.mockito.kotlin.doReturn @@ -28,14 +30,14 @@ internal class MediaSourcererTest { private val explicitStreamingSessionCreator = mock() private val implicitStreamingSessionCreator = mock() private val eventReporter = mock() - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val mediaSourcerer = MediaSourcerer( exoPlayer, playbackInfoMediaSourceFactory, explicitStreamingSessionCreator, implicitStreamingSessionCreator, eventReporter, - trueTimeWrapper, + sntpClient, ) @AfterEach @@ -46,7 +48,7 @@ internal class MediaSourcererTest { explicitStreamingSessionCreator, implicitStreamingSessionCreator, eventReporter, - trueTimeWrapper, + sntpClient, ) @Test @@ -157,7 +159,7 @@ internal class MediaSourcererTest { } @Test - fun onCurrentItemFinished() { + fun onCurrentItemFinished() = runTest { val currentStreamingSessionId = mock() val currentStreamingSessionIdString = currentStreamingSessionId.toString() val currentStreamingSession = mock { @@ -167,16 +169,20 @@ internal class MediaSourcererTest { val nextStreamingSession = mock().apply { mediaSourcerer.reflectionNextStreamingSession = this } - val endTimeMillis = -7L - whenever(trueTimeWrapper.currentTimeMillis) doReturn endTimeMillis + val endTime = 7.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn endTime mediaSourcerer.onCurrentItemFinished() verify(exoPlayer).removeMediaItem(0) verify(currentStreamingSession).id - verify(trueTimeWrapper).currentTimeMillis - verify(eventReporter) - .report(StreamingSessionEnd.Payload(currentStreamingSessionIdString, endTimeMillis)) + verify(sntpClient).blockingEpochTime() + verify(eventReporter).report( + StreamingSessionEnd.Payload( + currentStreamingSessionIdString, + endTime.inWholeMilliseconds, + ), + ) verifyNoMoreInteractions( currentStreamingSessionId, currentStreamingSession, @@ -185,7 +191,7 @@ internal class MediaSourcererTest { } @Test - fun onRepeatOne() { + fun onRepeatOne() = runTest { val currentStreamingSessionId = mock() val currentStreamingSession = mock { on { id } doReturn currentStreamingSessionId @@ -200,8 +206,8 @@ internal class MediaSourcererTest { val expectedNewStreamingSession = mock() whenever(implicitStreamingSessionCreator.createAndReportStart(productType, productId)) .thenReturn(expectedNewStreamingSession) - val endTimeMillis = 123L - whenever(trueTimeWrapper.currentTimeMillis).thenReturn(endTimeMillis) + val endTime = 123.milliseconds + whenever(sntpClient.blockingEpochTime()).thenReturn(endTime) val mediaProduct = mock> { on { it.productType } doReturn productType on { it.productId } doReturn productId @@ -214,14 +220,14 @@ internal class MediaSourcererTest { .report( StreamingSessionEnd.Payload( currentStreamingSessionId.toString(), - endTimeMillis, + endTime.inWholeMilliseconds, ), ) - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() } @Test - fun release() { + fun release() = runTest { val currentStreamingSessionId = mock() val currentStreamingSessionIdString = currentStreamingSessionId.toString() val currentStreamingSession = mock { @@ -234,9 +240,9 @@ internal class MediaSourcererTest { on { id } doReturn nextStreamingSessionId } mediaSourcerer.reflectionNextStreamingSession = nextStreamingSession - val endTimeMillis0 = -7L - val endTimeMillis1 = Long.MIN_VALUE - whenever(trueTimeWrapper.currentTimeMillis).doReturn(endTimeMillis0, endTimeMillis1) + val endTime0 = 7.milliseconds + val endTime1 = Long.MIN_VALUE.milliseconds + whenever(sntpClient.blockingEpochTime()).doReturn(endTime0, endTime1) mediaSourcerer.release() @@ -245,11 +251,19 @@ internal class MediaSourcererTest { assertThat(mediaSourcerer.reflectionNextStreamingSession).isNull() verify(currentStreamingSession).id verify(nextStreamingSession).id - verify(trueTimeWrapper, times(2)).currentTimeMillis - verify(eventReporter) - .report(StreamingSessionEnd.Payload(currentStreamingSessionIdString, endTimeMillis0)) - verify(eventReporter) - .report(StreamingSessionEnd.Payload(nextStreamingSessionIdString, endTimeMillis1)) + verify(sntpClient, times(2)).blockingEpochTime() + verify(eventReporter).report( + StreamingSessionEnd.Payload( + currentStreamingSessionIdString, + endTime0.inWholeMilliseconds, + ), + ) + verify(eventReporter).report( + StreamingSessionEnd.Payload( + nextStreamingSessionIdString, + endTime1.inWholeMilliseconds, + ), + ) verifyNoMoreInteractions( currentStreamingSessionId, currentStreamingSession, diff --git a/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/mediasource/streamingsession/ExplicitStreamingSessionCreatorTest.kt b/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/mediasource/streamingsession/ExplicitStreamingSessionCreatorTest.kt index a7ace0ae..044dd449 100644 --- a/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/mediasource/streamingsession/ExplicitStreamingSessionCreatorTest.kt +++ b/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/mediasource/streamingsession/ExplicitStreamingSessionCreatorTest.kt @@ -10,7 +10,7 @@ internal class ExplicitStreamingSessionCreatorTest : override val factory = mock() override val streamingSessionCreator = StreamingSession.Creator.Explicit( factory, - trueTimeWrapper, + sntpClient, eventReporter, ) } diff --git a/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/mediasource/streamingsession/ImplicitStreamingSessionCreatorTest.kt b/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/mediasource/streamingsession/ImplicitStreamingSessionCreatorTest.kt index 835de8b1..46ad2ecf 100644 --- a/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/mediasource/streamingsession/ImplicitStreamingSessionCreatorTest.kt +++ b/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/mediasource/streamingsession/ImplicitStreamingSessionCreatorTest.kt @@ -10,7 +10,7 @@ internal class ImplicitStreamingSessionCreatorTest : override val factory = mock() override val streamingSessionCreator = StreamingSession.Creator.Implicit( factory, - trueTimeWrapper, + sntpClient, eventReporter, ) } diff --git a/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/mediasource/streamingsession/StreamingSessionCreatorTest.kt b/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/mediasource/streamingsession/StreamingSessionCreatorTest.kt index cbb53d1e..f46df5ba 100644 --- a/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/mediasource/streamingsession/StreamingSessionCreatorTest.kt +++ b/player/playback-engine/src/test/kotlin/com/tidal/sdk/player/playbackengine/mediasource/streamingsession/StreamingSessionCreatorTest.kt @@ -1,11 +1,13 @@ package com.tidal.sdk.player.playbackengine.mediasource.streamingsession +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.common.Configuration import com.tidal.sdk.player.common.model.ProductType -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper import com.tidal.sdk.player.events.EventReporter import com.tidal.sdk.player.events.model.StreamingSessionStart import java.util.UUID +import kotlin.time.Duration.Companion.milliseconds +import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Test import org.mockito.kotlin.doReturn @@ -18,15 +20,15 @@ internal abstract class StreamingSessionCreatorTest() + protected val sntpClient = mock() protected val eventReporter = mock() abstract val streamingSessionCreator: StreamingSession.Creator @AfterEach - fun afterEach() = verifyNoMoreInteractions(factory, trueTimeWrapper, eventReporter) + fun afterEach() = verifyNoMoreInteractions(factory, sntpClient, eventReporter) @Test - fun createAndReportStartCreatesAndReportsStart() { + fun createAndReportStartCreatesAndReportsStart() = runTest { val id = mock() val isOfflineModeStart = true val configuration = mock { @@ -37,22 +39,22 @@ internal abstract class StreamingSessionCreatorTest() private val releaseRunnable = mock() private val acquireRunnableFactory = mock() - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val mutableState = mock() private val streamingPrivilegesDefault = StreamingPrivilegesDefault( networkInteractionsHandler, @@ -31,7 +33,7 @@ internal class StreamingPrivilegesDefaultTest { setStreamingPrivilegesListenerRunnableFactory, releaseRunnable, acquireRunnableFactory, - trueTimeWrapper, + sntpClient, mutableState, ) @@ -42,7 +44,7 @@ internal class StreamingPrivilegesDefaultTest { setStreamingPrivilegesListenerRunnableFactory, releaseRunnable, acquireRunnableFactory, - trueTimeWrapper, + sntpClient, mutableState, ) @@ -81,20 +83,25 @@ internal class StreamingPrivilegesDefaultTest { } @Test - fun acquirePostsRunnableHappyPath() { - val startedAtMillis = 7L - whenever(trueTimeWrapper.currentTimeMillis) doReturn startedAtMillis + fun acquirePostsRunnableHappyPath() = runTest { + val startedAt = 7.milliseconds + whenever(sntpClient.blockingEpochTime()) doReturn startedAt val connectionMutableState = mock() whenever(mutableState.connectionMutableState) doReturn connectionMutableState val acquireRunnable = mock() - whenever(acquireRunnableFactory.create(startedAtMillis, connectionMutableState)) + whenever( + acquireRunnableFactory.create( + startedAt.inWholeMilliseconds, + connectionMutableState, + ), + ) .thenReturn(acquireRunnable) streamingPrivilegesDefault.acquireStreamingPrivileges() - verify(trueTimeWrapper).currentTimeMillis + verify(sntpClient).blockingEpochTime() verify(mutableState).connectionMutableState - verify(acquireRunnableFactory).create(startedAtMillis, connectionMutableState) + verify(acquireRunnableFactory).create(startedAt.inWholeMilliseconds, connectionMutableState) verify(networkInteractionsHandler).post(acquireRunnable) verifyNoInteractions(connectionMutableState, acquireRunnable) } diff --git a/player/streaming-privileges/src/test/kotlin/com/tidal/sdk/player/streamingprivileges/StreamingPrivilegesModuleRootTest.kt b/player/streaming-privileges/src/test/kotlin/com/tidal/sdk/player/streamingprivileges/StreamingPrivilegesModuleRootTest.kt index a4b9e773..a8fcb0cd 100644 --- a/player/streaming-privileges/src/test/kotlin/com/tidal/sdk/player/streamingprivileges/StreamingPrivilegesModuleRootTest.kt +++ b/player/streaming-privileges/src/test/kotlin/com/tidal/sdk/player/streamingprivileges/StreamingPrivilegesModuleRootTest.kt @@ -4,7 +4,7 @@ import android.net.ConnectivityManager import assertk.assertThat import assertk.assertions.isSameAs import com.google.gson.Gson -import com.tidal.sdk.player.commonandroid.TrueTimeWrapper +import com.tidal.networktime.SNTPClient import com.tidal.sdk.player.streamingprivileges.di.StreamingPrivilegesComponent import okhttp3.OkHttpClient import org.junit.jupiter.api.AfterEach @@ -20,13 +20,13 @@ internal class StreamingPrivilegesModuleRootTest { private val connectivityManager = mock() private val okHttpClient = mock() private val gson = mock() - private val trueTimeWrapper = mock() + private val sntpClient = mock() private val streamingPrivilegesModuleRoot by lazy { StreamingPrivilegesModuleRoot( connectivityManager, okHttpClient, gson, - trueTimeWrapper, + sntpClient, ) } @@ -58,7 +58,7 @@ internal class StreamingPrivilegesModuleRootTest { connectivityManager, okHttpClient, gson, - trueTimeWrapper, + sntpClient, ) } doReturn component } @@ -70,7 +70,7 @@ internal class StreamingPrivilegesModuleRootTest { connectivityManager, okHttpClient, gson, - trueTimeWrapper, + sntpClient, ) verify(component).streamingPrivileges verifyNoMoreInteractions(expected, component, componentFactory) diff --git a/settings.gradle.kts b/settings.gradle.kts index 54035d3a..912342c7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,9 +12,6 @@ dependencyResolutionManagement { repositories { google() mavenCentral() - maven { - url = uri("https://jitpack.io") - } } }