From 5f1dd6d84901d42b401f989ce16cc934fa3e3048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Tue, 30 Jan 2024 17:48:37 +0100 Subject: [PATCH] Add tests to `pillarbox-player` (#430) --- .../business/tracker/TotalPlaytimeCounter.kt | 8 +- pillarbox-player/build.gradle.kts | 1 + .../player/extension/PlayerCommandsTest.kt | 150 +++++++++++ .../player/extension/VideoSizeTest.kt | 30 +++ .../pillarbox/player/extension/Format.kt | 4 +- .../pillarbox/player/extension/Player.kt | 28 +- .../player/extension/PlayerCommands.kt | 2 - .../pillarbox/player/TestAspectRatio.kt | 85 ------ .../pillarbox/player/TestFormatExtension.kt | 54 ---- .../pillarbox/player/TestTracksExtension.kt | 115 -------- .../pillarbox/player/TestUnsupportedTracks.kt | 123 --------- .../player/extension/ExoPlayerBuilderTest.kt | 36 +++ .../pillarbox/player/extension/FormatTest.kt | 251 ++++++++++++++++++ .../player/extension/MediaItemTest.kt | 49 ++++ .../pillarbox/player/extension/PlayerTest.kt | 85 ++++++ .../pillarbox/player/extension/TracksTest.kt | 195 ++++++++++++++ .../player/extension/VideoSizeTest.kt | 75 ++++++ 17 files changed, 884 insertions(+), 407 deletions(-) create mode 100644 pillarbox-player/src/androidTest/java/ch/srgssr/pillarbox/player/extension/PlayerCommandsTest.kt create mode 100644 pillarbox-player/src/androidTest/java/ch/srgssr/pillarbox/player/extension/VideoSizeTest.kt delete mode 100644 pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/TestAspectRatio.kt delete mode 100644 pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/TestFormatExtension.kt delete mode 100644 pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/TestTracksExtension.kt delete mode 100644 pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/TestUnsupportedTracks.kt create mode 100644 pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/ExoPlayerBuilderTest.kt create mode 100644 pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/FormatTest.kt create mode 100644 pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/MediaItemTest.kt create mode 100644 pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/PlayerTest.kt create mode 100644 pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/TracksTest.kt create mode 100644 pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/VideoSizeTest.kt diff --git a/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/tracker/TotalPlaytimeCounter.kt b/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/tracker/TotalPlaytimeCounter.kt index d52962ea0..47d4f741d 100644 --- a/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/tracker/TotalPlaytimeCounter.kt +++ b/pillarbox-core-business/src/main/java/ch/srgssr/pillarbox/core/business/tracker/TotalPlaytimeCounter.kt @@ -12,12 +12,16 @@ import kotlin.time.Duration.Companion.milliseconds * * @param timeProvider A callback invoked whenever the current time is needed. */ -class TotalPlaytimeCounter( - private val timeProvider: () -> Long = { System.currentTimeMillis() }, +class TotalPlaytimeCounter internal constructor( + private val timeProvider: () -> Long, ) { private var totalPlayTime: Duration = Duration.ZERO private var lastPlayTime = 0L + constructor() : this( + timeProvider = { System.currentTimeMillis() }, + ) + /** * Play * Calling twice play after sometime will compute totalPlaytime diff --git a/pillarbox-player/build.gradle.kts b/pillarbox-player/build.gradle.kts index f200a3ae2..e55e37643 100644 --- a/pillarbox-player/build.gradle.kts +++ b/pillarbox-player/build.gradle.kts @@ -80,6 +80,7 @@ dependencies { testImplementation(project(":pillarbox-player-testutils")) testImplementation(libs.junit) + testImplementation(libs.kotlin.test) testImplementation(libs.kotlinx.coroutines.test) testImplementation(libs.mockk) testImplementation(libs.mockk.dsl) diff --git a/pillarbox-player/src/androidTest/java/ch/srgssr/pillarbox/player/extension/PlayerCommandsTest.kt b/pillarbox-player/src/androidTest/java/ch/srgssr/pillarbox/player/extension/PlayerCommandsTest.kt new file mode 100644 index 000000000..96b8d3fe5 --- /dev/null +++ b/pillarbox-player/src/androidTest/java/ch/srgssr/pillarbox/player/extension/PlayerCommandsTest.kt @@ -0,0 +1,150 @@ +/* + * Copyright (c) SRG SSR. All rights reserved. + * License information is available from the LICENSE file. + */ +package ch.srgssr.pillarbox.player.extension + +import androidx.media3.common.Player +import androidx.media3.common.Player.Commands +import io.mockk.every +import io.mockk.mockk +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +class PlayerCommandsTest { + @Test + fun canSeekToNext() { + val player = mockk { + every { availableCommands } returnsMany listOf( + Commands.Builder().build(), + Commands.Builder().addAll(Player.COMMAND_STOP, Player.COMMAND_GET_TEXT).build(), + Commands.Builder().addAll(Player.COMMAND_STOP, Player.COMMAND_SEEK_TO_NEXT, Player.COMMAND_GET_TEXT).build() + ) + } + + assertFalse(player.availableCommands.canSeekToNext()) + assertFalse(player.availableCommands.canSeekToNext()) + assertTrue(player.availableCommands.canSeekToNext()) + } + + @Test + fun canSeekToPrevious() { + val player = mockk { + every { availableCommands } returnsMany listOf( + Commands.Builder().build(), + Commands.Builder().addAll(Player.COMMAND_STOP, Player.COMMAND_GET_TEXT).build(), + Commands.Builder().addAll(Player.COMMAND_STOP, Player.COMMAND_SEEK_TO_PREVIOUS, Player.COMMAND_GET_TEXT).build() + ) + } + + assertFalse(player.availableCommands.canSeekToPrevious()) + assertFalse(player.availableCommands.canSeekToPrevious()) + assertTrue(player.availableCommands.canSeekToPrevious()) + } + + @Test + fun canSeekForward() { + val player = mockk { + every { availableCommands } returnsMany listOf( + Commands.Builder().build(), + Commands.Builder().addAll(Player.COMMAND_STOP, Player.COMMAND_GET_TEXT).build(), + Commands.Builder().addAll(Player.COMMAND_STOP, Player.COMMAND_SEEK_FORWARD, Player.COMMAND_GET_TEXT).build() + ) + } + + assertFalse(player.availableCommands.canSeekForward()) + assertFalse(player.availableCommands.canSeekForward()) + assertTrue(player.availableCommands.canSeekForward()) + } + + @Test + fun canSeekBack() { + val player = mockk { + every { availableCommands } returnsMany listOf( + Commands.Builder().build(), + Commands.Builder().addAll(Player.COMMAND_STOP, Player.COMMAND_GET_TEXT).build(), + Commands.Builder().addAll(Player.COMMAND_STOP, Player.COMMAND_SEEK_BACK, Player.COMMAND_GET_TEXT).build() + ) + } + + assertFalse(player.availableCommands.canSeekBack()) + assertFalse(player.availableCommands.canSeekBack()) + assertTrue(player.availableCommands.canSeekBack()) + } + + @Test + fun canSeek() { + val player = mockk { + every { availableCommands } returnsMany listOf( + Commands.Builder().build(), + Commands.Builder().addAll(Player.COMMAND_STOP, Player.COMMAND_GET_TEXT).build(), + Commands.Builder().addAll(Player.COMMAND_STOP, Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, Player.COMMAND_GET_TEXT).build() + ) + } + + assertFalse(player.availableCommands.canSeek()) + assertFalse(player.availableCommands.canSeek()) + assertTrue(player.availableCommands.canSeek()) + } + + @Test + fun canPlayPause() { + val player = mockk { + every { availableCommands } returnsMany listOf( + Commands.Builder().build(), + Commands.Builder().addAll(Player.COMMAND_STOP, Player.COMMAND_GET_TEXT).build(), + Commands.Builder().addAll(Player.COMMAND_STOP, Player.COMMAND_PLAY_PAUSE, Player.COMMAND_GET_TEXT).build() + ) + } + + assertFalse(player.availableCommands.canPlayPause()) + assertFalse(player.availableCommands.canPlayPause()) + assertTrue(player.availableCommands.canPlayPause()) + } + + @Test + fun canGetTracks() { + val player = mockk { + every { availableCommands } returnsMany listOf( + Commands.Builder().build(), + Commands.Builder().addAll(Player.COMMAND_STOP, Player.COMMAND_GET_TEXT).build(), + Commands.Builder().addAll(Player.COMMAND_STOP, Player.COMMAND_GET_TRACKS, Player.COMMAND_GET_TEXT).build() + ) + } + + assertFalse(player.availableCommands.canGetTracks()) + assertFalse(player.availableCommands.canGetTracks()) + assertTrue(player.availableCommands.canGetTracks()) + } + + @Test + fun canSetTrackSelectionParameters() { + val player = mockk { + every { availableCommands } returnsMany listOf( + Commands.Builder().build(), + Commands.Builder().addAll(Player.COMMAND_STOP, Player.COMMAND_GET_TEXT).build(), + Commands.Builder().addAll(Player.COMMAND_STOP, Player.COMMAND_SET_TRACK_SELECTION_PARAMETERS, Player.COMMAND_GET_TEXT).build() + ) + } + + assertFalse(player.availableCommands.canSetTrackSelectionParameters()) + assertFalse(player.availableCommands.canSetTrackSelectionParameters()) + assertTrue(player.availableCommands.canSetTrackSelectionParameters()) + } + + @Test + fun canSpeedAndPitch() { + val player = mockk { + every { availableCommands } returnsMany listOf( + Commands.Builder().build(), + Commands.Builder().addAll(Player.COMMAND_STOP, Player.COMMAND_GET_TEXT).build(), + Commands.Builder().addAll(Player.COMMAND_STOP, Player.COMMAND_SET_SPEED_AND_PITCH, Player.COMMAND_GET_TEXT).build() + ) + } + + assertFalse(player.availableCommands.canSpeedAndPitch()) + assertFalse(player.availableCommands.canSpeedAndPitch()) + assertTrue(player.availableCommands.canSpeedAndPitch()) + } +} diff --git a/pillarbox-player/src/androidTest/java/ch/srgssr/pillarbox/player/extension/VideoSizeTest.kt b/pillarbox-player/src/androidTest/java/ch/srgssr/pillarbox/player/extension/VideoSizeTest.kt new file mode 100644 index 000000000..ea522bd56 --- /dev/null +++ b/pillarbox-player/src/androidTest/java/ch/srgssr/pillarbox/player/extension/VideoSizeTest.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) SRG SSR. All rights reserved. + * License information is available from the LICENSE file. + */ +package ch.srgssr.pillarbox.player.extension + +import android.util.Rational +import androidx.media3.common.VideoSize +import org.junit.Assert.assertEquals +import org.junit.Test + +class VideoSizeTest { + @Test + fun toRational_unknown_video_size() { + val input = VideoSize.UNKNOWN + assertEquals(RATIONAL_ONE, input.toRational()) + } + + @Test + fun toRational_with_a_16_9_aspect_ratio() { + val input = VideoSize(1920, 1080) + assertEquals(Rational(1920, 1080), input.toRational()) + } + + @Test + fun toRational_with_a_square_aspect_ratio() { + val input = VideoSize(500, 500) + assertEquals(Rational(500, 500), input.toRational()) + } +} diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Format.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Format.kt index 313d4ff1e..b6ca4ece2 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Format.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Format.kt @@ -51,7 +51,7 @@ val Format.videoSize: VideoSize * Selection flags as string */ fun Format.selectionString(): String { - val selectionFlags: MutableList = ArrayList() + val selectionFlags = mutableListOf() if (hasSelection(C.SELECTION_FLAG_AUTOSELECT)) { selectionFlags.add("auto") } @@ -69,7 +69,7 @@ fun Format.selectionString(): String { */ @Suppress("CyclomaticComplexMethod") fun Format.roleString(): String { - val roleFlags: MutableList = ArrayList() + val roleFlags = mutableListOf() if (hasRole(C.ROLE_FLAG_MAIN)) { roleFlags.add("main") } diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Player.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Player.kt index a8ce77c8b..12dab3d92 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Player.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/Player.kt @@ -7,36 +7,16 @@ package ch.srgssr.pillarbox.player.extension import androidx.media3.common.MediaItem import androidx.media3.common.Player -/** - * Resume playback - */ -fun Player.startPlayback() { - when (playbackState) { - Player.STATE_IDLE -> { - prepare() - } - - Player.STATE_ENDED -> { - seekToDefaultPosition() - } - - else -> { - // Nothing - } - } - play() -} - /** * Get a snapshot of the current media items */ fun Player.getCurrentMediaItems(): List { - if (mediaItemCount == 0) { + val count = mediaItemCount + if (count == 0) { return emptyList() } - val count = mediaItemCount - return ArrayList(count).apply { - for (i in 0 until count) { + return buildList(count) { + repeat(count) { i -> add(getMediaItemAt(i)) } } diff --git a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/PlayerCommands.kt b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/PlayerCommands.kt index 07054f3c0..63dec56ab 100644 --- a/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/PlayerCommands.kt +++ b/pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/extension/PlayerCommands.kt @@ -2,8 +2,6 @@ * Copyright (c) SRG SSR. All rights reserved. * License information is available from the LICENSE file. */ -@file:Suppress("unused") - package ch.srgssr.pillarbox.player.extension import androidx.media3.common.Player diff --git a/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/TestAspectRatio.kt b/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/TestAspectRatio.kt deleted file mode 100644 index dfd196812..000000000 --- a/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/TestAspectRatio.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) SRG SSR. All rights reserved. - * License information is available from the LICENSE file. - */ -package ch.srgssr.pillarbox.player - -import androidx.media3.common.VideoSize -import ch.srgssr.pillarbox.player.extension.computeAspectRatio -import org.junit.Assert -import org.junit.Test - -class TestAspectRatio { - - @Test - fun testUnknownVideoSize() { - val input = VideoSize.UNKNOWN - val expectedAspectRatio = 0.0f - val unknownAspectRatio = 0.0f - Assert.assertEquals(input.computeAspectRatio(unknownAspectRatio), expectedAspectRatio) - } - - @Test - fun testUnknownVideoSizeDefaultAspectRatio() { - val input = VideoSize.UNKNOWN - val expectedAspectRatio = 1.0f - val unknownAspectRatio = 1.0f - Assert.assertEquals(input.computeAspectRatio(unknownAspectRatio), expectedAspectRatio) - } - - @Test - fun testHeightZeroVideoSize() { - val input = VideoSize(0, 100) - val expectedAspectRatio = 0.0f - val unknownAspectRatio = 0.0f - Assert.assertEquals(input.computeAspectRatio(unknownAspectRatio), expectedAspectRatio) - } - - @Test - fun testWidthZeroVideoSize() { - val input = VideoSize(240, 0) - val expectedAspectRatio = 0.0f - val unknownAspectRatio = 0.0f - Assert.assertEquals(input.computeAspectRatio(unknownAspectRatio), expectedAspectRatio) - } - - @Test - fun testWidthAndHeightZeroVideoSize() { - val input = VideoSize(0, 0) - val expectedAspectRatio = 0.0f - val unknownAspectRatio = 0.0f - Assert.assertEquals(input.computeAspectRatio(unknownAspectRatio), expectedAspectRatio) - } - - @Test - fun testAspectRatio16_9() { - val input = VideoSize(1920, 1080) - val expectedAspectRatio = 16 / 9f - val unknownAspectRatio = 1.0f - Assert.assertEquals(input.computeAspectRatio(unknownAspectRatio), expectedAspectRatio) - } - - @Test - fun testAspectRatio9_16() { - val input = VideoSize(1080, 1920) - val expectedAspectRatio = 9 / 16f - val unknownAspectRatio = 1.0f - Assert.assertEquals(input.computeAspectRatio(unknownAspectRatio), expectedAspectRatio) - } - - @Test - fun testAspectRatio4_3() { - val input = VideoSize(800, 600) - val expectedAspectRatio = 4 / 3f - val unknownAspectRatio = 1.0f - Assert.assertEquals(input.computeAspectRatio(unknownAspectRatio), expectedAspectRatio) - } - - @Test - fun testAspectRatioSquare() { - val input = VideoSize(500, 500) - val expectedAspectRatio = 1.0f - val unknownAspectRatio = 0.0f - Assert.assertEquals(input.computeAspectRatio(unknownAspectRatio), expectedAspectRatio) - } -} diff --git a/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/TestFormatExtension.kt b/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/TestFormatExtension.kt deleted file mode 100644 index 2136bb0a8..000000000 --- a/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/TestFormatExtension.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) SRG SSR. All rights reserved. - * License information is available from the LICENSE file. - */ -package ch.srgssr.pillarbox.player - -import androidx.media3.common.C -import androidx.media3.common.Format -import ch.srgssr.pillarbox.player.extension.hasRole -import ch.srgssr.pillarbox.player.extension.hasSelection -import org.junit.Assert -import org.junit.Test - -class TestFormatExtension { - - @Test - fun testHasRoleEmpty() { - val format = Format.Builder().build() - Assert.assertFalse(format.hasRole(C.ROLE_FLAG_DESCRIBES_VIDEO)) - Assert.assertFalse(format.hasRole(C.ROLE_FLAG_ALTERNATE)) - Assert.assertFalse(format.hasRole(C.ROLE_FLAG_ALTERNATE or C.ROLE_FLAG_DESCRIBES_VIDEO)) - Assert.assertFalse(format.hasRole(C.ROLE_FLAG_SIGN)) - } - - @Test - fun testHasRole() { - val roleFlags = C.ROLE_FLAG_ALTERNATE or C.ROLE_FLAG_DESCRIBES_VIDEO - val format = Format.Builder().setRoleFlags(roleFlags).build() - - Assert.assertTrue(format.hasRole(C.ROLE_FLAG_DESCRIBES_VIDEO)) - Assert.assertTrue(format.hasRole(C.ROLE_FLAG_ALTERNATE)) - Assert.assertTrue(format.hasRole(C.ROLE_FLAG_ALTERNATE or C.ROLE_FLAG_DESCRIBES_VIDEO)) - Assert.assertFalse(format.hasRole(C.ROLE_FLAG_SIGN)) - } - - @Test - fun testSelectionEmptyFlags() { - val format = Format.Builder().setSelectionFlags(0).build() - Assert.assertFalse(format.hasSelection(C.SELECTION_FLAG_DEFAULT)) - Assert.assertFalse(format.hasSelection(C.SELECTION_FLAG_AUTOSELECT)) - Assert.assertFalse(format.hasSelection(C.SELECTION_FLAG_AUTOSELECT or C.SELECTION_FLAG_DEFAULT)) - Assert.assertFalse(format.hasSelection(C.SELECTION_FLAG_FORCED)) - } - - @Test - fun testSelectionFlags() { - val selectionFlags = C.SELECTION_FLAG_AUTOSELECT or C.SELECTION_FLAG_DEFAULT - val format = Format.Builder().setSelectionFlags(selectionFlags).build() - Assert.assertTrue(format.hasSelection(C.SELECTION_FLAG_DEFAULT)) - Assert.assertTrue(format.hasSelection(C.SELECTION_FLAG_AUTOSELECT)) - Assert.assertTrue(format.hasSelection(C.SELECTION_FLAG_AUTOSELECT or C.SELECTION_FLAG_DEFAULT)) - Assert.assertFalse(format.hasSelection(C.SELECTION_FLAG_FORCED)) - } -} diff --git a/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/TestTracksExtension.kt b/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/TestTracksExtension.kt deleted file mode 100644 index 991f520bb..000000000 --- a/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/TestTracksExtension.kt +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) SRG SSR. All rights reserved. - * License information is available from the LICENSE file. - */ -package ch.srgssr.pillarbox.player - -import androidx.media3.common.C -import androidx.media3.common.C.RoleFlags -import androidx.media3.common.C.SelectionFlags -import androidx.media3.common.Format -import androidx.media3.common.MimeTypes -import androidx.media3.common.TrackGroup -import androidx.media3.common.Tracks -import ch.srgssr.pillarbox.player.extension.audio -import ch.srgssr.pillarbox.player.extension.text -import ch.srgssr.pillarbox.player.extension.video -import org.junit.Assert -import org.junit.Test - -class TestTracksExtension { - private val listTextTracks = listOf( - createTrackGroup( - listOf( - createFormatTrackFormat("t1", mimeType = TEXT_MIME_TYPE), - createFormatTrackFormat("t2", mimeType = TEXT_MIME_TYPE, selectionFlags = C.SELECTION_FLAG_AUTOSELECT or C.SELECTION_FLAG_DEFAULT), - ), - ), - createTrackGroup( - listOf( - createFormatTrackFormat("t1-sdh", mimeType = TEXT_MIME_TYPE, roleFlags = C.ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND), - ), - ), - ) - private val listForcedSubtitles = listOf( - createTrackGroup( - listOf( - createFormatTrackFormat("t1-forced", mimeType = TEXT_MIME_TYPE, selectionFlags = C.SELECTION_FLAG_FORCED), - ), - ), - ) - private val listAudios = listOf( - createTrackGroup( - listOf( - createFormatTrackFormat("a1", mimeType = AUDIO_MIME_TYPE), - ), - ), - createTrackGroup( - listOf( - createFormatTrackFormat("a1-ad", mimeType = AUDIO_MIME_TYPE, roleFlags = C.ROLE_FLAG_DESCRIBES_VIDEO), - ), - ), - ) - - private val listVideos = listOf( - createTrackGroup( - listOf( - createFormatTrackFormat("v1", mimeType = VIDEO_MIME_TYPE), - createFormatTrackFormat("v2", mimeType = VIDEO_MIME_TYPE), - createFormatTrackFormat("v3", mimeType = VIDEO_MIME_TYPE), - ), - ), - ) - private val tracks = Tracks(listAudios + listTextTracks + listVideos + listForcedSubtitles) - - @Test - fun testTextTracks() { - val textTracks = tracks.text - Assert.assertEquals(listTextTracks, textTracks) - } - - @Test - fun testAudioTracks() { - val audioTracks = tracks.audio - Assert.assertEquals(listAudios, audioTracks) - } - - @Test - fun testVideoTracks() { - val videoTracks = tracks.video - Assert.assertEquals(listVideos, videoTracks) - } - - companion object { - - private const val TEXT_MIME_TYPE = MimeTypes.APPLICATION_TTML - private const val VIDEO_MIME_TYPE = MimeTypes.VIDEO_H265 - private const val AUDIO_MIME_TYPE = MimeTypes.AUDIO_MP4 - - fun createFormatTrackFormat( - label: String? = null, - mimeType: String, - roleFlags: @RoleFlags Int = 0, - selectionFlags: @SelectionFlags Int = C.SELECTION_FLAG_AUTOSELECT - ): - Format { - return Format.Builder() - .setId("id:$label") - .setLabel(label) - .setLanguage("fr") - .setSelectionFlags(selectionFlags) - .setRoleFlags(roleFlags) - .setContainerMimeType(mimeType) - .build() - } - - fun createTrackGroup(listFormat: List): Tracks.Group { - val trackGroup = TrackGroup(*listFormat.toTypedArray()) - val trackSupport = IntArray(listFormat.size) { - C.FORMAT_HANDLED - } - val selected = BooleanArray(listFormat.size) - return Tracks.Group(trackGroup, false, trackSupport, selected) - } - } -} diff --git a/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/TestUnsupportedTracks.kt b/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/TestUnsupportedTracks.kt deleted file mode 100644 index 6b36ef486..000000000 --- a/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/TestUnsupportedTracks.kt +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) SRG SSR. All rights reserved. - * License information is available from the LICENSE file. - */ -package ch.srgssr.pillarbox.player - -import androidx.media3.common.C -import androidx.media3.common.C.FormatSupport -import androidx.media3.common.Format -import androidx.media3.common.MimeTypes -import androidx.media3.common.TrackGroup -import androidx.media3.common.Tracks -import ch.srgssr.pillarbox.player.extension.filterUnsupported -import org.junit.Assert -import org.junit.Test - -class TestUnsupportedTracks { - - @Test - fun testOneSupportedTrack() { - val format = createSampleFormat("Unsupported") - val listFormat = listOf(format) - val trackGroup = TrackGroup(*listFormat.toTypedArray()) - val selected = BooleanArray(listFormat.size) - val trackSupport = createAllSupportArray(listFormat.size, C.FORMAT_HANDLED) - val tracks = Tracks(listOf(Tracks.Group(trackGroup, false, trackSupport, selected))) - val expectedSize = 1 - val expectedTracksGroups = listOf( - Tracks.Group( - TrackGroup(*listOf(format).toTypedArray()), - false, - createAllSupportArray(expectedSize, C.FORMAT_HANDLED), - BooleanArray(expectedSize) - ) - ) - Assert.assertEquals(expectedTracksGroups, tracks.groups.mapNotNull { it.filterUnsupported() }) - } - - @Test - fun testOnlySupportedTracks() { - val format1 = createSampleFormat("F1") - val format2 = createSampleFormat("F2") - val listFormat = listOf(format1, format2) - val trackGroup = TrackGroup(*listFormat.toTypedArray()) - val selected = BooleanArray(listFormat.size) - val trackSupport = createAllSupportArray(listFormat.size, C.FORMAT_HANDLED) - val tracks = Tracks(listOf(Tracks.Group(trackGroup, false, trackSupport, selected))) - val expectedSize = 2 - val expectedTracksGroups = listOf( - Tracks.Group( - TrackGroup(*listOf(format1, format2).toTypedArray()), - false, - createAllSupportArray(expectedSize, C.FORMAT_HANDLED), - BooleanArray(expectedSize) - ) - ) - Assert.assertEquals(expectedTracksGroups, tracks.groups.mapNotNull { it.filterUnsupported() }) - } - - @Test - fun testMultiTrackOneUnsupported() { - val format1 = createSampleFormat("F1") - val format2 = createSampleFormat("F2") - val formatUnsupportedTrack = createSampleFormat("Unsupported") - val listFormat = listOf(format1, format2, formatUnsupportedTrack) - val trackGroup = TrackGroup(*listFormat.toTypedArray()) - val selected = BooleanArray(listFormat.size) - val trackSupport = createAllSupportArray(listFormat.size, C.FORMAT_HANDLED) - trackSupport[2] = C.FORMAT_UNSUPPORTED_SUBTYPE - val tracks = Tracks(listOf(Tracks.Group(trackGroup, false, trackSupport, selected))) - val expectedSize = 2 - val expectedTracksGroups = listOf( - Tracks.Group( - TrackGroup(*listOf(format1, format2).toTypedArray()), - false, - createAllSupportArray(expectedSize, C.FORMAT_HANDLED), - BooleanArray(expectedSize) - ) - ) - Assert.assertEquals(expectedTracksGroups, tracks.groups.mapNotNull { it.filterUnsupported() }) - } - - @Test - fun testMultipleUnsupportedTracks() { - val format1 = createSampleFormat("F1") - val format2 = createSampleFormat("F2") - val formatUnsupportedTrack = createSampleFormat("Unsupported") - val listFormat = listOf(format1, format2, formatUnsupportedTrack) - val trackGroup = TrackGroup(*listFormat.toTypedArray()) - val selected = BooleanArray(listFormat.size) - val trackSupport = createAllSupportArray(listFormat.size, C.FORMAT_UNSUPPORTED_SUBTYPE) - val tracks = Tracks(listOf(Tracks.Group(trackGroup, false, trackSupport, selected))) - Assert.assertTrue(tracks.groups.mapNotNull { it.filterUnsupported() }.isEmpty()) - } - - @Test - fun testOneUnsupportedTrack() { - val formatUnsupportedTrack = createSampleFormat("Unsupported") - val listFormat = listOf(formatUnsupportedTrack) - val trackGroup = TrackGroup(*listFormat.toTypedArray()) - val selected = BooleanArray(listFormat.size) - val trackSupport = createAllSupportArray(listFormat.size, C.FORMAT_UNSUPPORTED_SUBTYPE) - val tracks = Tracks(listOf(Tracks.Group(trackGroup, false, trackSupport, selected))) - Assert.assertTrue(tracks.groups.mapNotNull { it.filterUnsupported() }.isEmpty()) - } - - companion object { - - private fun createSampleFormat(label: String): Format { - return Format.Builder() - .setId("id:$label") - .setLabel(label) - .setContainerMimeType(MimeTypes.AUDIO_MP4) - .build() - } - - private fun createAllSupportArray(size: Int, support: @FormatSupport Int): IntArray { - return IntArray(size) { - support - } - } - } -} diff --git a/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/ExoPlayerBuilderTest.kt b/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/ExoPlayerBuilderTest.kt new file mode 100644 index 000000000..1c7c7a80f --- /dev/null +++ b/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/ExoPlayerBuilderTest.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) SRG SSR. All rights reserved. + * License information is available from the LICENSE file. + */ +package ch.srgssr.pillarbox.player.extension + +import androidx.media3.exoplayer.ExoPlayer +import ch.srgssr.pillarbox.player.SeekIncrement +import io.mockk.confirmVerified +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import kotlin.test.Test +import kotlin.time.Duration.Companion.seconds + +class ExoPlayerBuilderTest { + @Test + fun `set seek increments`() { + val exoPlayerBuilder = mockk { + every { setSeekBackIncrementMs(any()) } returns this + every { setSeekForwardIncrementMs(any()) } returns this + } + val seekIncrement = SeekIncrement( + backward = 45.seconds, + forward = 180.seconds, + ) + + exoPlayerBuilder.setSeekIncrements(seekIncrement) + + verify(exactly = 1) { + exoPlayerBuilder.setSeekBackIncrementMs(seekIncrement.backward.inWholeMilliseconds) + exoPlayerBuilder.setSeekForwardIncrementMs(seekIncrement.forward.inWholeMilliseconds) + } + confirmVerified(exoPlayerBuilder) + } +} diff --git a/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/FormatTest.kt b/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/FormatTest.kt new file mode 100644 index 000000000..2096c4b10 --- /dev/null +++ b/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/FormatTest.kt @@ -0,0 +1,251 @@ +/* + * Copyright (c) SRG SSR. All rights reserved. + * License information is available from the LICENSE file. + */ +package ch.srgssr.pillarbox.player.extension + +import androidx.media3.common.C +import androidx.media3.common.Format +import androidx.media3.common.VideoSize +import java.util.Locale +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNull +import kotlin.test.assertTrue + +class FormatTest { + @Test + fun `hasRole with empty roles`() { + val format = Format.Builder().build() + + assertFalse(format.hasRole(C.ROLE_FLAG_DESCRIBES_VIDEO)) + assertFalse(format.hasRole(C.ROLE_FLAG_ALTERNATE)) + assertFalse(format.hasRole(C.ROLE_FLAG_ALTERNATE or C.ROLE_FLAG_DESCRIBES_VIDEO)) + assertFalse(format.hasRole(C.ROLE_FLAG_SIGN)) + } + + @Test + fun `hasRole with roles`() { + val roleFlags = C.ROLE_FLAG_ALTERNATE or C.ROLE_FLAG_DESCRIBES_VIDEO + val format = Format.Builder().setRoleFlags(roleFlags).build() + + assertTrue(format.hasRole(C.ROLE_FLAG_DESCRIBES_VIDEO)) + assertTrue(format.hasRole(C.ROLE_FLAG_ALTERNATE)) + assertTrue(format.hasRole(C.ROLE_FLAG_ALTERNATE or C.ROLE_FLAG_DESCRIBES_VIDEO)) + assertFalse(format.hasRole(C.ROLE_FLAG_SIGN)) + } + + @Test + fun `hasSelection with empty selections`() { + val format = Format.Builder().build() + + assertFalse(format.hasSelection(C.SELECTION_FLAG_DEFAULT)) + assertFalse(format.hasSelection(C.SELECTION_FLAG_AUTOSELECT)) + assertFalse(format.hasSelection(C.SELECTION_FLAG_AUTOSELECT or C.SELECTION_FLAG_DEFAULT)) + assertFalse(format.hasSelection(C.SELECTION_FLAG_FORCED)) + } + + @Test + fun `hasSelection with selections`() { + val selectionFlags = C.SELECTION_FLAG_AUTOSELECT or C.SELECTION_FLAG_DEFAULT + val format = Format.Builder().setSelectionFlags(selectionFlags).build() + + assertTrue(format.hasSelection(C.SELECTION_FLAG_DEFAULT)) + assertTrue(format.hasSelection(C.SELECTION_FLAG_AUTOSELECT)) + assertTrue(format.hasSelection(C.SELECTION_FLAG_AUTOSELECT or C.SELECTION_FLAG_DEFAULT)) + assertFalse(format.hasSelection(C.SELECTION_FLAG_FORCED)) + } + + @Test + fun `is forced, selection not set`() { + val format = Format.Builder().build() + + assertFalse(format.isForced()) + } + + @Test + fun `is forced, selection set`() { + val selectionFlags = C.SELECTION_FLAG_AUTOSELECT or C.SELECTION_FLAG_FORCED + val format = Format.Builder().setSelectionFlags(selectionFlags).build() + + assertTrue(format.isForced()) + } + + @Test + fun `videoSize with no dimension set`() { + val format = Format.Builder().build() + + assertEquals(VideoSize.UNKNOWN, format.videoSize) + } + + @Test + fun `videoSize with width only`() { + val format = Format.Builder().setWidth(1440).build() + + assertEquals(VideoSize.UNKNOWN, format.videoSize) + } + + @Test + fun `videoSize with height only`() { + val format = Format.Builder().setHeight(900).build() + + assertEquals(VideoSize.UNKNOWN, format.videoSize) + } + + @Test + fun `videoSize with both dimensions set`() { + val width = 1440 + val height = 900 + val format = Format.Builder().setWidth(width).setHeight(height).build() + + assertEquals(VideoSize(width, height), format.videoSize) + } + + @Test + fun `selectionString with empty selections`() { + val format = Format.Builder().build() + + assertEquals("", format.selectionString()) + } + + @Test + fun `selectionString with some selections`() { + val format = Format.Builder() + .setSelectionFlags(C.SELECTION_FLAG_FORCED or C.SELECTION_FLAG_DEFAULT) + .build() + + assertEquals("default,forced", format.selectionString()) + } + + @Test + fun `selectionString with all selections`() { + val format = Format.Builder() + .setSelectionFlags(C.SELECTION_FLAG_FORCED or C.SELECTION_FLAG_AUTOSELECT or C.SELECTION_FLAG_DEFAULT) + .build() + + assertEquals("auto,default,forced", format.selectionString()) + } + + @Test + fun `roleString with empty roles`() { + val format = Format.Builder().build() + + assertEquals("", format.roleString()) + } + + @Test + fun `roleString with some roles`() { + val format = Format.Builder() + .setRoleFlags(C.ROLE_FLAG_EMERGENCY or C.ROLE_FLAG_MAIN or C.ROLE_FLAG_TRICK_PLAY) + .build() + + assertEquals("main,emergency,trick-play", format.roleString()) + } + + @Test + fun `roleString with all roles`() { + val format = Format.Builder() + .setRoleFlags( + C.ROLE_FLAG_MAIN or + C.ROLE_FLAG_ALTERNATE or + C.ROLE_FLAG_SUPPLEMENTARY or + C.ROLE_FLAG_COMMENTARY or + C.ROLE_FLAG_DUB or + C.ROLE_FLAG_EMERGENCY or + C.ROLE_FLAG_CAPTION or + C.ROLE_FLAG_SUBTITLE or + C.ROLE_FLAG_SIGN or + C.ROLE_FLAG_DESCRIBES_VIDEO or + C.ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND or + C.ROLE_FLAG_ENHANCED_DIALOG_INTELLIGIBILITY or + C.ROLE_FLAG_TRANSCRIBES_DIALOG or + C.ROLE_FLAG_EASY_TO_READ or + C.ROLE_FLAG_TRICK_PLAY + ) + .build() + + assertEquals( + "main,alt,supplementary,commentary,dub,emergency,caption,subtitle,sign,describes-video,describes-music," + + "enhanced-intelligibility,transcribes-dialog,easy-read,trick-play", + format.roleString() + ) + } + + @Test + fun `hasAccessibilityRoles with empty roles`() { + val format = Format.Builder().build() + + assertFalse(format.hasAccessibilityRoles()) + } + + @Test + fun `hasAccessibilityRoles without accessibility roles`() { + val format = Format.Builder() + .setRoleFlags(C.ROLE_FLAG_EMERGENCY or C.ROLE_FLAG_MAIN) + .build() + + assertFalse(format.hasAccessibilityRoles()) + } + + @Test + fun `hasAccessibilityRoles with accessibility roles`() { + val format = Format.Builder() + .setRoleFlags(C.ROLE_FLAG_EMERGENCY or C.ROLE_FLAG_DESCRIBES_VIDEO) + .build() + + assertTrue(format.hasAccessibilityRoles()) + } + + @Test + fun `getLocale without language`() { + val format = Format.Builder().build() + + assertNull(format.getLocale()) + } + + @Test + fun `getLocale with language`() { + val format = Format.Builder().setLanguage("fr_CH").build() + + assertEquals(Locale("fr", "CH"), format.getLocale()) + } + + @Test + fun `getLocale with invalid language`() { + val format = Format.Builder().setLanguage("xx_YY").build() + + assertEquals(Locale("xx", "YY"), format.getLocale()) + } + + @Test + fun `displayName without language, without label`() { + val format = Format.Builder().build() + + assertEquals(C.LANGUAGE_UNDETERMINED, format.displayName) + } + + @Test + fun `displayName with language, without label`() { + val format = Format.Builder().setLanguage("fr").build() + + assertEquals("French", format.displayName) + } + + @Test + fun `displayName without language, with label`() { + val format = Format.Builder().setLabel("Format label").build() + + assertEquals("Format label", format.displayName) + } + + @Test + fun `displayName with language, with label`() { + val format = Format.Builder() + .setLanguage("fr") + .setLabel("Format label") + .build() + + assertEquals("French", format.displayName) + } +} diff --git a/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/MediaItemTest.kt b/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/MediaItemTest.kt new file mode 100644 index 000000000..1fee2cbe6 --- /dev/null +++ b/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/MediaItemTest.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) SRG SSR. All rights reserved. + * License information is available from the LICENSE file. + */ +package ch.srgssr.pillarbox.player.extension + +import android.net.Uri +import androidx.media3.common.MediaItem +import ch.srgssr.pillarbox.player.tracker.MediaItemTracker +import ch.srgssr.pillarbox.player.tracker.MediaItemTrackerData +import io.mockk.mockk +import kotlin.test.Test +import kotlin.test.assertNull +import kotlin.test.assertSame +import kotlin.test.assertTrue + +class MediaItemTest { + @Test + fun `getMediaItemTrackerData with no tag set`() { + val mediaItem = MediaItem.Builder().build() + + assertNull(mediaItem.getMediaItemTrackerDataOrNull()) + assertTrue(mediaItem.getMediaItemTrackerData().trackers.isEmpty()) + } + + @Test + fun `getMediaItemTrackerData with tag set with wrong type`() { + val mediaItem = MediaItem.Builder() + .setTag("Hello, World!") + .build() + + assertNull(mediaItem.getMediaItemTrackerDataOrNull()) + assertTrue(mediaItem.getMediaItemTrackerData().trackers.isEmpty()) + } + + @Test + fun `getMediaItemTrackerData with tag set`() { + val mediaItemTrackerData = MediaItemTrackerData() + mediaItemTrackerData.putData(MediaItemTracker::class.java) + + val mediaItem = MediaItem.Builder() + .setUri(mockk()) + .setTrackerData(mediaItemTrackerData) + .build() + + assertSame(mediaItemTrackerData, mediaItem.getMediaItemTrackerDataOrNull()) + assertSame(mediaItemTrackerData, mediaItem.getMediaItemTrackerData()) + } +} diff --git a/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/PlayerTest.kt b/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/PlayerTest.kt new file mode 100644 index 000000000..5ed3eddea --- /dev/null +++ b/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/PlayerTest.kt @@ -0,0 +1,85 @@ +/* + * Copyright (c) SRG SSR. All rights reserved. + * License information is available from the LICENSE file. + */ +package ch.srgssr.pillarbox.player.extension + +import androidx.media3.common.MediaItem +import androidx.media3.common.PlaybackParameters +import androidx.media3.common.Player +import io.mockk.every +import io.mockk.mockk +import kotlin.test.Test +import kotlin.test.assertEquals + +class PlayerTest { + @Test + fun `getCurrentMediaItems without any items`() { + val player = mockk { + every { mediaItemCount } returns 0 + } + + assertEquals(0, player.getCurrentMediaItems().size) + } + + @Test + fun `getCurrentMediaItems with some items`() { + val mediaItemsCount = 3 + val mediaItems = buildList(mediaItemsCount) { + repeat(mediaItemsCount) { + add(mockk()) + } + } + val player = mockk { + every { mediaItemCount } returns mediaItemsCount + every { getMediaItemAt(any()) } answers { mediaItems[invocation.args[0] as Int] } + } + + assertEquals(mediaItems, player.getCurrentMediaItems()) + } + + @Test + fun getPlaybackSpeed() { + val player = mockk { + every { playbackParameters } returns PlaybackParameters(5f) + } + + assertEquals(5f, player.getPlaybackSpeed()) + } + + @Test + fun `currentPositionPercentage with negative duration`() { + val player = mockk { + every { currentPosition } returnsMany listOf(-5L, 0L, 5L) + every { duration } returns -4L + } + + assertEquals(-5f, player.currentPositionPercentage()) + assertEquals(0f, player.currentPositionPercentage()) + assertEquals(5f, player.currentPositionPercentage()) + } + + @Test + fun `currentPositionPercentage with duration equals to 0`() { + val player = mockk { + every { currentPosition } returnsMany listOf(-5L, 0L, 5L) + every { duration } returns 0L + } + + assertEquals(-5f, player.currentPositionPercentage()) + assertEquals(0f, player.currentPositionPercentage()) + assertEquals(5f, player.currentPositionPercentage()) + } + + @Test + fun `currentPositionPercentage with positive duration`() { + val player = mockk { + every { currentPosition } returnsMany listOf(-5L, 0L, 5L) + every { duration } returns 4L + } + + assertEquals(-1.25f, player.currentPositionPercentage()) + assertEquals(0f, player.currentPositionPercentage()) + assertEquals(1.25f, player.currentPositionPercentage()) + } +} diff --git a/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/TracksTest.kt b/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/TracksTest.kt new file mode 100644 index 000000000..4f03cdb21 --- /dev/null +++ b/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/TracksTest.kt @@ -0,0 +1,195 @@ +/* + * Copyright (c) SRG SSR. All rights reserved. + * License information is available from the LICENSE file. + */ +package ch.srgssr.pillarbox.player.extension + +import androidx.media3.common.C +import androidx.media3.common.C.RoleFlags +import androidx.media3.common.C.SelectionFlags +import androidx.media3.common.Format +import androidx.media3.common.MimeTypes +import androidx.media3.common.TrackGroup +import androidx.media3.common.Tracks +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class TracksTest { + private val textTracks = listOf( + createTrackGroup( + createFormatTrackFormat("t1", mimeType = TEXT_MIME_TYPE), + createFormatTrackFormat("t2", mimeType = TEXT_MIME_TYPE, selectionFlags = C.SELECTION_FLAG_AUTOSELECT or C.SELECTION_FLAG_DEFAULT), + ), + createTrackGroup( + createFormatTrackFormat("t1-sdh", mimeType = TEXT_MIME_TYPE, roleFlags = C.ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND), + ), + ) + private val forcedSubtitleTracks = listOf( + createTrackGroup( + createFormatTrackFormat("t1-forced", mimeType = TEXT_MIME_TYPE, selectionFlags = C.SELECTION_FLAG_FORCED), + ), + ) + private val audioTracks = listOf( + createTrackGroup( + createFormatTrackFormat("a1", mimeType = AUDIO_MIME_TYPE), + ), + createTrackGroup( + createFormatTrackFormat("a1-ad", mimeType = AUDIO_MIME_TYPE, roleFlags = C.ROLE_FLAG_DESCRIBES_VIDEO), + ), + ) + private val videoTracks = listOf( + createTrackGroup( + createFormatTrackFormat("v1", mimeType = VIDEO_MIME_TYPE), + createFormatTrackFormat("v2", mimeType = VIDEO_MIME_TYPE), + createFormatTrackFormat("v3", mimeType = VIDEO_MIME_TYPE), + ), + ) + private val tracks = Tracks(audioTracks + textTracks + videoTracks + forcedSubtitleTracks) + + @Test + fun `text with text tracks`() { + val textTracks = tracks.text + assertEquals(this.textTracks, textTracks) + } + + @Test + fun `text with empty tracks`() { + val textTracks = Tracks(emptyList()).text + assertEquals(emptyList(), textTracks) + } + + @Test + fun `audio with audio tracks`() { + val audioTracks = tracks.audio + assertEquals(this.audioTracks, audioTracks) + } + + @Test + fun `audio with empty tracks`() { + val audioTracks = Tracks(emptyList()).audio + assertEquals(emptyList(), audioTracks) + } + + @Test + fun `video with video tracks`() { + val videoTracks = tracks.video + assertEquals(this.videoTracks, videoTracks) + } + + @Test + fun `video with empty tracks`() { + val videoTracks = Tracks(emptyList()).video + assertEquals(emptyList(), videoTracks) + } + + @Test + fun `filterUnsupported one supported track`() { + val format = createFormatTrackFormat("Unsupported", mimeType = AUDIO_MIME_TYPE) + val trackGroup = TrackGroup(format) + val selected = booleanArrayOf(false) + val trackSupport = intArrayOf(C.FORMAT_HANDLED) + val tracks = Tracks(listOf(Tracks.Group(trackGroup, false, trackSupport, selected))) + val expectedTracksGroups = listOf( + Tracks.Group( + trackGroup, + false, + trackSupport, + selected, + ) + ) + assertEquals(expectedTracksGroups, tracks.groups.mapNotNull { it.filterUnsupported() }) + } + + @Test + fun `filterUnsupported only supported track`() { + val format1 = createFormatTrackFormat("F1", mimeType = AUDIO_MIME_TYPE) + val format2 = createFormatTrackFormat("F2", mimeType = AUDIO_MIME_TYPE) + val trackGroup = TrackGroup(format1, format2) + val selected = booleanArrayOf(false, false) + val trackSupport = intArrayOf(C.FORMAT_HANDLED, C.FORMAT_HANDLED) + val tracks = Tracks(listOf(Tracks.Group(trackGroup, false, trackSupport, selected))) + val expectedTracksGroups = listOf( + Tracks.Group( + trackGroup, + false, + trackSupport, + selected, + ) + ) + assertEquals(expectedTracksGroups, tracks.groups.mapNotNull { it.filterUnsupported() }) + } + + @Test + fun `filterUnsupported multiple tracks, one unsupported`() { + val format1 = createFormatTrackFormat("F1", mimeType = AUDIO_MIME_TYPE) + val format2 = createFormatTrackFormat("F2", mimeType = AUDIO_MIME_TYPE) + val formatUnsupportedTrack = createFormatTrackFormat("Unsupported", mimeType = AUDIO_MIME_TYPE) + val trackGroup = TrackGroup(format1, format2, formatUnsupportedTrack) + val selected = booleanArrayOf(false, false, false) + val trackSupport = intArrayOf(C.FORMAT_HANDLED, C.FORMAT_HANDLED, C.FORMAT_UNSUPPORTED_SUBTYPE) + val tracks = Tracks(listOf(Tracks.Group(trackGroup, false, trackSupport, selected))) + val expectedTracksGroups = listOf( + Tracks.Group( + TrackGroup(format1, format2), + false, + intArrayOf(C.FORMAT_HANDLED, C.FORMAT_HANDLED), + booleanArrayOf(false, false), + ) + ) + assertEquals(expectedTracksGroups, tracks.groups.mapNotNull { it.filterUnsupported() }) + } + + @Test + fun `filterUnsupported multiple unsupported tracks`() { + val format1 = createFormatTrackFormat("F1", mimeType = AUDIO_MIME_TYPE) + val format2 = createFormatTrackFormat("F2", mimeType = AUDIO_MIME_TYPE) + val formatUnsupportedTrack = createFormatTrackFormat("Unsupported", mimeType = AUDIO_MIME_TYPE) + val trackGroup = TrackGroup(format1, format2, formatUnsupportedTrack) + val selected = booleanArrayOf(false, false, false) + val trackSupport = intArrayOf(C.FORMAT_UNSUPPORTED_SUBTYPE, C.FORMAT_UNSUPPORTED_SUBTYPE, C.FORMAT_UNSUPPORTED_SUBTYPE) + val tracks = Tracks(listOf(Tracks.Group(trackGroup, false, trackSupport, selected))) + assertTrue(tracks.groups.mapNotNull { it.filterUnsupported() }.isEmpty()) + } + + @Test + fun `filterUnsupported one unsupported track`() { + val formatUnsupportedTrack = createFormatTrackFormat("Unsupported", mimeType = AUDIO_MIME_TYPE) + val trackGroup = TrackGroup(formatUnsupportedTrack) + val selected = booleanArrayOf(false) + val trackSupport = intArrayOf(C.FORMAT_UNSUPPORTED_SUBTYPE) + val tracks = Tracks(listOf(Tracks.Group(trackGroup, false, trackSupport, selected))) + assertTrue(tracks.groups.mapNotNull { it.filterUnsupported() }.isEmpty()) + } + + private companion object { + private const val TEXT_MIME_TYPE = MimeTypes.APPLICATION_TTML + private const val VIDEO_MIME_TYPE = MimeTypes.VIDEO_H265 + private const val AUDIO_MIME_TYPE = MimeTypes.AUDIO_MP4 + + private fun createFormatTrackFormat( + label: String, + mimeType: String, + roleFlags: @RoleFlags Int = 0, + selectionFlags: @SelectionFlags Int = C.SELECTION_FLAG_AUTOSELECT + ): Format { + return Format.Builder() + .setId("id:$label") + .setLabel(label) + .setLanguage("fr") + .setSelectionFlags(selectionFlags) + .setRoleFlags(roleFlags) + .setContainerMimeType(mimeType) + .build() + } + + private fun createTrackGroup(vararg formats: Format): Tracks.Group { + val trackGroup = TrackGroup(*formats) + val trackSupport = IntArray(formats.size) { + C.FORMAT_HANDLED + } + val selected = BooleanArray(formats.size) + return Tracks.Group(trackGroup, false, trackSupport, selected) + } + } +} diff --git a/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/VideoSizeTest.kt b/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/VideoSizeTest.kt new file mode 100644 index 000000000..37ce0da5e --- /dev/null +++ b/pillarbox-player/src/test/java/ch/srgssr/pillarbox/player/extension/VideoSizeTest.kt @@ -0,0 +1,75 @@ +/* + * Copyright (c) SRG SSR. All rights reserved. + * License information is available from the LICENSE file. + */ +package ch.srgssr.pillarbox.player.extension + +import androidx.media3.common.VideoSize +import kotlin.test.Test +import kotlin.test.assertEquals + +class VideoSizeTest { + @Test + fun `computeAspectRatio unknown video size`() { + val input = VideoSize.UNKNOWN + val expectedAspectRatio = 16f / 9 + val unknownAspectRatio = 16f / 9 + assertEquals(expectedAspectRatio, input.computeAspectRatio(unknownAspectRatio)) + } + + @Test + fun `computeAspectRatio with height set to 0`() { + val input = VideoSize(0, 100) + val expectedAspectRatio = 16f / 9 + val unknownAspectRatio = 16f / 9 + assertEquals(expectedAspectRatio, input.computeAspectRatio(unknownAspectRatio)) + } + + @Test + fun `computeAspectRatio with width set to 0`() { + val input = VideoSize(240, 0) + val expectedAspectRatio = 16f / 9 + val unknownAspectRatio = 16f / 9 + assertEquals(expectedAspectRatio, input.computeAspectRatio(unknownAspectRatio)) + } + + @Test + fun `computeAspectRatio with width and height set to 0`() { + val input = VideoSize(0, 0) + val expectedAspectRatio = 16f / 9 + val unknownAspectRatio = 16f / 9 + assertEquals(expectedAspectRatio, input.computeAspectRatio(unknownAspectRatio)) + } + + @Test + fun `computeAspectRatio with a 16-9 aspect ratio`() { + val input = VideoSize(1920, 1080) + val expectedAspectRatio = 16f / 9 + val unknownAspectRatio = 1f + assertEquals(expectedAspectRatio, input.computeAspectRatio(unknownAspectRatio)) + } + + @Test + fun `computeAspectRatio with a 9-16 aspect ratio`() { + val input = VideoSize(1080, 1920) + val expectedAspectRatio = 9f / 16 + val unknownAspectRatio = 1f + assertEquals(expectedAspectRatio, input.computeAspectRatio(unknownAspectRatio)) + } + + @Test + fun `computeAspectRatio with a 4-3 aspect ratio`() { + val input = VideoSize(800, 600) + val expectedAspectRatio = 4f / 3 + val unknownAspectRatio = 1f + assertEquals(expectedAspectRatio, input.computeAspectRatio(unknownAspectRatio)) + } + + @Test + fun `computeAspectRatio with a square aspect ratio`() { + val input = VideoSize(500, 500) + val expectedAspectRatio = 1f + val unknownAspectRatio = 0f + assertEquals(expectedAspectRatio, input.computeAspectRatio(unknownAspectRatio)) + } +}