Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

412 update player tests with turbine #418

Merged
merged 9 commits into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ okhttp = "4.12.0"
srg-data-provider = "0.8.0"
tag-commander-core = "5.4.2"
tag-commander-server-side = "5.5.2"
turbine = "1.0.0"

[libraries]
accompanist-navigation-material = { module = "com.google.accompanist:accompanist-navigation-material", version.ref = "accompanist" }
Expand Down Expand Up @@ -118,6 +119,7 @@ androidx-compose-runtime-saveable = { module = "androidx.compose.runtime:runtime
leanback = { group = "androidx.leanback", name = "leanback", version.ref = "androidx-leanback" }
androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" }
guava = { module = "com.google.guava:guava", version.ref = "guava" }
turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" }

[plugins]
android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" }
Expand Down
1 change: 1 addition & 0 deletions pillarbox-player/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ dependencies {
testImplementation(libs.kotlinx.coroutines.test)
testImplementation(libs.mockk)
testImplementation(libs.mockk.dsl)
testImplementation(libs.turbine)

androidTestImplementation(project(":pillarbox-player-testutils"))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.transformLatest
Expand Down Expand Up @@ -149,6 +152,7 @@ fun Player.mediaItemCountAsFlow(): Flow<Int> = callbackFlow {

/**
* Ticker emits event every [interval] when [Player.isPlaying] is true.
* Emit a value once at least once.
*/
@OptIn(ExperimentalCoroutinesApi::class)
fun Player.tickerWhilePlayingAsFlow(
Expand All @@ -162,9 +166,11 @@ fun Player.tickerWhilePlayingAsFlow(

/**
* Current position of the player update every [updateInterval] when it is playing.
* Send current position once if not playing.
*/
fun Player.currentPositionAsFlow(updateInterval: Duration = DefaultUpdateInterval): Flow<Long> =
merge(
if (isPlaying) emptyFlow() else flowOf(currentPosition),
tickerWhilePlayingAsFlow(updateInterval).map {
currentPosition
},
Expand All @@ -178,14 +184,11 @@ private fun Player.positionChangedFlow(): Flow<Long> = callbackFlow {
newPosition: Player.PositionInfo,
reason: Int
) {
if (reason == Player.DISCONTINUITY_REASON_SEEK) {
trySend(currentPosition)
}
trySend(newPosition.positionMs)
}
}
trySend(currentPosition)
addPlayerListener(player = this@positionChangedFlow, listener)
}
}.distinctUntilChanged()

/**
* Current buffered percentage as flow [Player.getBufferedPercentage]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@ class TestCurrentMediaItemTracker {

@Test
fun testAreEqualsDifferentMediaItem() {
val mediaItem = createMediaItem("M1")
val mediaItem2 = createMediaItem("M2")
val mediaItem = createMediaItemWithMediaId("M1")
val mediaItem2 = createMediaItemWithMediaId("M2")
Assert.assertFalse(CurrentMediaItemTracker.areEqual(mediaItem, mediaItem2))
}

@Test
fun testAreEqualsSameMediaId() {
val mediaItem = createMediaItem("M1")
val mediaItem2 = createMediaItem("M1")
val mediaItem = createMediaItemWithMediaId("M1")
val mediaItem2 = createMediaItemWithMediaId("M1")
Assert.assertTrue(CurrentMediaItemTracker.areEqual(mediaItem, mediaItem2))
}

Expand Down Expand Up @@ -95,7 +95,7 @@ class TestCurrentMediaItemTracker {

@Test
fun testStartEnd() = runTest {
val mediaItem = createMediaItem("M1")
val mediaItem = createMediaItemWithMediaId("M1")
val expected = listOf(EventState.IDLE, EventState.START, EventState.EOF)
analyticsCommander.simulateItemStart(mediaItem)
analyticsCommander.simulateItemEnd(mediaItem)
Expand All @@ -105,7 +105,7 @@ class TestCurrentMediaItemTracker {
@Test
fun testStartAsyncLoadEnd() = runTest {
val mediaItemEmpty = MediaItem.Builder().setMediaId("M1").build()
val mediaItemLoaded = createMediaItem("M1")
val mediaItemLoaded = createMediaItemWithMediaId("M1")
val expected = listOf(EventState.IDLE, EventState.START, EventState.EOF)
analyticsCommander.simulateItemStart(mediaItemEmpty)
analyticsCommander.simulateItemLoaded(mediaItemLoaded)
Expand All @@ -116,7 +116,7 @@ class TestCurrentMediaItemTracker {
@Test
fun testStartAsyncLoadRelease() = runTest {
val mediaItemEmpty = MediaItem.Builder().setMediaId("M1").build()
val mediaItemLoaded = createMediaItem("M1")
val mediaItemLoaded = createMediaItemWithMediaId("M1")
val expected = listOf(EventState.IDLE, EventState.START, EventState.END)
analyticsCommander.simulateItemStart(mediaItemEmpty)
analyticsCommander.simulateItemLoaded(mediaItemLoaded)
Expand All @@ -126,7 +126,7 @@ class TestCurrentMediaItemTracker {

@Test
fun testStartReleased() = runTest {
val mediaItem = createMediaItem("M1")
val mediaItem = createMediaItemWithMediaId("M1")
val expected = listOf(EventState.IDLE, EventState.START, EventState.END)
analyticsCommander.simulateItemStart(mediaItem)
analyticsCommander.simulateRelease(mediaItem)
Expand All @@ -135,15 +135,15 @@ class TestCurrentMediaItemTracker {

@Test
fun testRelease() = runTest {
val mediaItem = createMediaItem("M1")
val mediaItem = createMediaItemWithMediaId("M1")
val expected = listOf(EventState.IDLE)
analyticsCommander.simulateRelease(mediaItem)
Assert.assertEquals(expected, tracker.stateList)
}

@Test
fun testRestartAfterEnd() = runTest {
val mediaItem = createMediaItem("M1")
val mediaItem = createMediaItemWithMediaId("M1")
val expected = listOf(EventState.IDLE, EventState.START, EventState.EOF, EventState.START, EventState.EOF)
analyticsCommander.simulateItemStart(mediaItem)
analyticsCommander.simulateItemEnd(mediaItem)
Expand All @@ -157,15 +157,15 @@ class TestCurrentMediaItemTracker {
@Test
fun testMediaTransitionSeekToNext() = runTest {
val expectedStates = listOf(EventState.IDLE, EventState.START, EventState.END, EventState.START, EventState.EOF)
val mediaItem = createMediaItem("M1")
val mediaItem2 = createMediaItem("M2")
val mediaItem = createMediaItemWithMediaId("M1")
val mediaItem2 = createMediaItemWithMediaId("M2")
analyticsCommander.simulateItemStart(mediaItem)
analyticsCommander.simulateItemTransitionSeek(mediaItem, mediaItem2)
analyticsCommander.simulateItemEnd(mediaItem2)
Assert.assertEquals("Different Item", expectedStates, tracker.stateList)
tracker.clear()

val mediaItem3 = createMediaItem("M1")
val mediaItem3 = createMediaItemWithMediaId("M1")
analyticsCommander.simulateItemStart(mediaItem)
analyticsCommander.simulateItemTransitionSeek(mediaItem, mediaItem3)
analyticsCommander.simulateItemEnd(mediaItem3)
Expand All @@ -175,9 +175,9 @@ class TestCurrentMediaItemTracker {
@Test
fun testMediaItemTransitionWithAsyncItem() = runTest {
val expectedStates = listOf(EventState.IDLE, EventState.START, EventState.END, EventState.START, EventState.END)
val mediaItem = createMediaItem("M1")
val mediaItem = createMediaItemWithMediaId("M1")
val mediaItem2 = MediaItem.Builder().setMediaId("M2").build()
val mediaItem2Loaded = createMediaItem("M2")
val mediaItem2Loaded = createMediaItemWithMediaId("M2")
analyticsCommander.simulateItemStart(mediaItem)
analyticsCommander.simulateItemTransitionSeek(mediaItem, mediaItem2)
Assert.assertEquals(listOf(EventState.IDLE, EventState.START, EventState.END), tracker.stateList)
Expand All @@ -203,15 +203,15 @@ class TestCurrentMediaItemTracker {
@Test
fun testMediaTransitionSameItemAuto() = runTest {
val expectedStates = listOf(EventState.IDLE, EventState.START, EventState.EOF, EventState.START, EventState.EOF)
val mediaItem = createMediaItem("M1")
val mediaItem2 = createMediaItem("M2")
val mediaItem = createMediaItemWithMediaId("M1")
val mediaItem2 = createMediaItemWithMediaId("M2")
analyticsCommander.simulateItemStart(mediaItem)
analyticsCommander.simulateItemTransitionAuto(mediaItem, mediaItem2)
analyticsCommander.simulateItemEnd(mediaItem2)
Assert.assertEquals("Different Item", expectedStates, tracker.stateList)
tracker.clear()

val mediaItem3 = createMediaItem("M1")
val mediaItem3 = createMediaItemWithMediaId("M1")
analyticsCommander.simulateItemStart(mediaItem)
analyticsCommander.simulateItemTransitionAuto(mediaItem, mediaItem3)
analyticsCommander.simulateItemEnd(mediaItem3)
Expand All @@ -221,7 +221,7 @@ class TestCurrentMediaItemTracker {
@Test
fun testMediaTransitionRepeat() = runTest {
val expectedStates = listOf(EventState.IDLE, EventState.START, EventState.EOF, EventState.START)
val mediaItem = createMediaItem("M1")
val mediaItem = createMediaItemWithMediaId("M1")

analyticsCommander.simulateItemStart(mediaItem)
analyticsCommander.simulateItemTransitionRepeat(mediaItem)
Expand All @@ -231,7 +231,7 @@ class TestCurrentMediaItemTracker {

@Test
fun testMultipleStop() = runTest {
val mediaItem = createMediaItem("M1")
val mediaItem = createMediaItemWithMediaId("M1")
analyticsCommander.simulateItemStart(mediaItem)
analyticsCommander.simulateItemEnd(mediaItem)
analyticsCommander.simulateRelease(mediaItem)
Expand All @@ -242,7 +242,7 @@ class TestCurrentMediaItemTracker {

@Test
fun testStartEndDisableAtStartAnalytics() = runTest {
val mediaItem = createMediaItem("M1")
val mediaItem = createMediaItemWithMediaId("M1")
val expected = listOf(EventState.IDLE)
currentItemTracker.enabled = false
analyticsCommander.simulateItemStart(mediaItem)
Expand All @@ -252,7 +252,7 @@ class TestCurrentMediaItemTracker {

@Test
fun testStartEndToggleAnalytics() = runTest {
val mediaItem = createMediaItem("M1")
val mediaItem = createMediaItemWithMediaId("M1")
val expected = listOf(EventState.IDLE, EventState.START, EventState.END, EventState.START, EventState.EOF)
currentItemTracker.enabled = true
analyticsCommander.simulateItemStart(mediaItem)
Expand All @@ -266,7 +266,7 @@ class TestCurrentMediaItemTracker {
@Test
fun testStartAsyncLoadEndToggleAnalytics() = runTest {
val mediaItemEmpty = MediaItem.Builder().setMediaId("M1").build()
val mediaItemLoaded = createMediaItem("M1")
val mediaItemLoaded = createMediaItemWithMediaId("M1")
val expected = listOf(EventState.IDLE, EventState.START, EventState.END, EventState.START, EventState.EOF)
currentItemTracker.enabled = true
analyticsCommander.simulateItemStart(mediaItemEmpty)
Expand All @@ -281,7 +281,7 @@ class TestCurrentMediaItemTracker {
@Test
fun testStartAsyncLoadEndDisableAtEnd() = runTest {
val mediaItemEmpty = MediaItem.Builder().setMediaId("M1").build()
val mediaItemLoaded = createMediaItem("M1")
val mediaItemLoaded = createMediaItemWithMediaId("M1")
val expected = listOf(EventState.IDLE, EventState.START, EventState.EOF)
currentItemTracker.enabled = true
analyticsCommander.simulateItemStart(mediaItemEmpty)
Expand All @@ -293,7 +293,7 @@ class TestCurrentMediaItemTracker {

@Test
fun testStartRemoveItem() = runTest {
val mediaItem = createMediaItem("M1")
val mediaItem = createMediaItemWithMediaId("M1")
val expected = listOf(EventState.IDLE, EventState.START, EventState.END)
analyticsCommander.simulateItemStart(mediaItem)
analyticsCommander.simulateItemRemoved(mediaItem)
Expand All @@ -303,9 +303,9 @@ class TestCurrentMediaItemTracker {
companion object {
private val uri: Uri = mockk(relaxed = true)

fun createMediaItem(mediaId: String): MediaItem {
fun createMediaItemWithMediaId(mediaId: String): MediaItem {
every { uri.toString() } returns "https://host/media.mp4"
every { uri.equals(Any()) } returns true
every { uri == Any() } returns true
return MediaItem.Builder()
.setUri(uri)
.setMediaId(mediaId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,30 +89,21 @@ class TestMediaItemTrackerList {
Assert.assertNull(trackerC)
}

private class ItemTrackerA : MediaItemTracker {
private open class EmptyItemTracker : MediaItemTracker {
override fun start(player: ExoPlayer, initialData: Any?) {
// Nothing
}

override fun stop(player: ExoPlayer, reason: MediaItemTracker.StopReason, positionMs: Long) {
// Nothing
}

}

private class ItemTrackerB : MediaItemTracker {
override fun start(player: ExoPlayer, initialData: Any?) {
}

override fun stop(player: ExoPlayer, reason: MediaItemTracker.StopReason, positionMs: Long) {
}
}
private class ItemTrackerA : EmptyItemTracker()

private open class ItemTrackerC : MediaItemTracker {
override fun start(player: ExoPlayer, initialData: Any?) {
}
private class ItemTrackerB : EmptyItemTracker()

override fun stop(player: ExoPlayer, reason: MediaItemTracker.StopReason, positionMs: Long) {
}
}
private open class ItemTrackerC : EmptyItemTracker()

private class ItemTrackerD : ItemTrackerC()
}
Loading
Loading