@@ -23,6 +23,7 @@ import ch.srgssr.pillarbox.player.asset.timeRange.Chapter
2323import ch.srgssr.pillarbox.player.source.PillarboxMediaSourceFactory
2424import io.mockk.clearAllMocks
2525import io.mockk.spyk
26+ import io.mockk.verify
2627import io.mockk.verifyOrder
2728import org.junit.runner.RunWith
2829import kotlin.test.AfterTest
@@ -67,15 +68,13 @@ class ChapterTrackerTest {
6768 player.addMediaItem(ChapterAssetLoader .MEDIA_ITEM )
6869 TestPlayerRunHelper .runUntilPlaybackState(player, Player .STATE_ENDED )
6970
70- val expectedChapters = listOf (ChapterAssetLoader .CHAPTER_1 , ChapterAssetLoader .CHAPTER_2 )
71- val receivedChapters = mutableListOf<Chapter >()
72- verifyOrder {
73- listener.onChapterChanged(capture(receivedChapters))
74- listener.onChapterChanged(null )
75- listener.onChapterChanged(capture(receivedChapters))
76- listener.onChapterChanged(null )
71+ val expectedChapters = listOf (ChapterAssetLoader .CHAPTER_1 , null , ChapterAssetLoader .CHAPTER_2 , null )
72+ val receivedChapters = mutableListOf<Chapter ?>()
73+ verify {
74+ listener.onChapterChanged(captureNullable(receivedChapters))
7775 }
78- assertEquals(expectedChapters, receivedChapters.reversed())
76+ assertEquals(expectedChapters.size, receivedChapters.size)
77+ assertEquals(expectedChapters, receivedChapters)
7978 }
8079
8180 @Test
@@ -97,6 +96,38 @@ class ChapterTrackerTest {
9796 }
9897 assertEquals(expectedChapters, receivedChapters.reversed())
9998 }
99+
100+ @Test
101+ fun `chapter transition after seek back` () {
102+ player.addMediaItem(ChapterAssetLoader .MEDIA_ITEM_WITH_CHAPTER )
103+ TestPlayerRunHelper .playUntilPosition(player, 0 , ChapterAssetLoader .CHAPTER_3 .start + 1_000L )
104+ player.seekBack()
105+ TestPlayerRunHelper .runUntilPlaybackState(player, Player .STATE_ENDED )
106+
107+ val expectedChapters = listOf (ChapterAssetLoader .CHAPTER_3 , null , ChapterAssetLoader .CHAPTER_3 , null , ChapterAssetLoader .CHAPTER_4 , null )
108+ val receivedChapters = mutableListOf<Chapter ?>()
109+
110+ verify {
111+ listener.onChapterChanged(captureNullable(receivedChapters))
112+ }
113+ assertEquals(expectedChapters, receivedChapters)
114+ }
115+
116+ @Test
117+ fun `chapter transition skip next` () {
118+ player.addMediaItems(listOf (ChapterAssetLoader .MEDIA_ITEM_WITH_CHAPTER , ChapterAssetLoader .NO_CHAPTER_MEDIA_ITEM ))
119+ TestPlayerRunHelper .playUntilPosition(player, 0 , ChapterAssetLoader .CHAPTER_3 .start + 1_000L )
120+ player.seekToNext()
121+ TestPlayerRunHelper .runUntilPlaybackState(player, Player .STATE_ENDED )
122+
123+ val expectedChapters = listOf (ChapterAssetLoader .CHAPTER_3 , null )
124+ val receivedChapters = mutableListOf<Chapter ?>()
125+
126+ verify {
127+ listener.onChapterChanged(captureNullable(receivedChapters))
128+ }
129+ assertEquals(expectedChapters, receivedChapters)
130+ }
100131}
101132
102133private class ChapterAssetLoader (context : Context ) : AssetLoader(DefaultMediaSourceFactory (context)) {
@@ -107,20 +138,49 @@ private class ChapterAssetLoader(context: Context) : AssetLoader(DefaultMediaSou
107138
108139 override suspend fun loadAsset (mediaItem : MediaItem ): Asset {
109140 val itemBuilder = mediaItem.buildUpon()
110- return Asset (
111- mediaSource = mediaSourceFactory.createMediaSource(itemBuilder.build()),
112- mediaMetadata = mediaItem.mediaMetadata,
113- chapters = listOf (CHAPTER_1 , CHAPTER_2 )
114- )
141+ val mediaSource = mediaSourceFactory.createMediaSource(itemBuilder.build())
142+ return when (mediaItem.mediaId) {
143+ ID_START_WITH_CHAPTER -> {
144+ Asset (
145+ mediaSource = mediaSource,
146+ mediaMetadata = mediaItem.mediaMetadata,
147+ chapters = listOf (CHAPTER_1 , CHAPTER_2 )
148+ )
149+ }
150+
151+ ID_WITH_CHAPTER -> {
152+ Asset (
153+ mediaSource = mediaSource,
154+ mediaMetadata = mediaItem.mediaMetadata,
155+ chapters = listOf (CHAPTER_3 , CHAPTER_4 )
156+ )
157+ }
158+
159+ else -> {
160+ Asset (
161+ mediaSource = mediaSource,
162+ mediaMetadata = mediaItem.mediaMetadata,
163+ chapters = emptyList()
164+ )
165+ }
166+ }
115167 }
116168
117169 companion object {
118170 private const val URL = " https://rts-vod-amd.akamaized.net/ww/13317145/f1d49f18-f302-37ce-866c-1c1c9b76a824/master.m3u8"
119- val MEDIA_ITEM = MediaItem .fromUri(URL )
171+ const val ID_START_WITH_CHAPTER = " ID_START_WITH_CHAPTER"
172+ const val ID_WITH_CHAPTER = " ID_WITH_CHAPTER"
173+
174+ val MEDIA_ITEM = MediaItem .Builder ().setMediaId(ID_START_WITH_CHAPTER ).setUri(URL ).build()
175+ val MEDIA_ITEM_WITH_CHAPTER = MediaItem .Builder ().setMediaId(ID_WITH_CHAPTER ).setUri(URL ).build()
176+ val NO_CHAPTER_MEDIA_ITEM = MediaItem .Builder ().setMediaId(" NoChapter" ).setUri(URL ).build()
120177
121178 const val NEAR_END_POSITION_MS = 15_000L // the video has 17 sec duration
122179
123- val CHAPTER_1 = Chapter (id = " Chapter1" , 0 , 5 , MediaMetadata .EMPTY )
124- val CHAPTER_2 = Chapter (id = " Chapter1" , 5 , NEAR_END_POSITION_MS , MediaMetadata .EMPTY )
180+ val CHAPTER_1 = Chapter (id = " Chapter1" , 0 , 5_000L , MediaMetadata .EMPTY )
181+ val CHAPTER_2 = Chapter (id = " Chapter2" , 5_000L , NEAR_END_POSITION_MS , MediaMetadata .EMPTY )
182+
183+ val CHAPTER_3 = Chapter (id = " Chapter3" , 2_000L , 5_000L , MediaMetadata .EMPTY )
184+ val CHAPTER_4 = Chapter (id = " Chapter4" , 10_000L , NEAR_END_POSITION_MS , MediaMetadata .EMPTY )
125185 }
126186}
0 commit comments