Skip to content

Commit 641dae4

Browse files
authored
605 bad commandersact stop position after removing item (#606)
1 parent 5876f46 commit 641dae4

File tree

5 files changed

+155
-56
lines changed

5 files changed

+155
-56
lines changed

pillarbox-core-business/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ dependencies {
4242
implementation(libs.okhttp.logging.interceptor)
4343
api(libs.tagcommander.core)
4444

45+
testImplementation(project(":pillarbox-player-testutils"))
4546
testImplementation(libs.androidx.media3.test.utils)
4647
testImplementation(libs.androidx.media3.test.utils.robolectric)
4748
testImplementation(libs.androidx.test.core)

pillarbox-core-business/src/test/java/ch/srgssr/pillarbox/core/business/tracker/commandersact/CommandersActTrackerIntegrationTest.kt

Lines changed: 127 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import ch.srgssr.pillarbox.core.business.integrationlayer.service.HttpMediaCompo
3131
import ch.srgssr.pillarbox.core.business.integrationlayer.service.MediaCompositionService
3232
import ch.srgssr.pillarbox.core.business.tracker.DefaultMediaItemTrackerRepository
3333
import ch.srgssr.pillarbox.core.business.tracker.comscore.ComScoreTracker
34+
import ch.srgssr.pillarbox.player.test.utils.TestPillarboxRunHelper
3435
import ch.srgssr.pillarbox.player.tracker.MediaItemTrackerRepository
3536
import io.mockk.Called
3637
import io.mockk.confirmVerified
@@ -141,17 +142,23 @@ class CommandersActTrackerIntegrationTest {
141142

142143
assertEquals(3, tcMediaEvents.size)
143144

144-
assertEquals(Play, tcMediaEvents[0].eventType)
145-
assertTrue(tcMediaEvents[0].assets.isNotEmpty())
146-
assertNull(tcMediaEvents[0].sourceId)
145+
tcMediaEvents[0].let {
146+
assertEquals(Play, it.eventType)
147+
assertTrue(it.assets.isNotEmpty())
148+
assertNull(it.sourceId)
149+
}
147150

148-
assertEquals(Stop, tcMediaEvents[1].eventType)
149-
assertTrue(tcMediaEvents[1].assets.isNotEmpty())
150-
assertNull(tcMediaEvents[1].sourceId)
151+
tcMediaEvents[1].let {
152+
assertEquals(Stop, it.eventType)
153+
assertTrue(it.assets.isNotEmpty())
154+
assertNull(it.sourceId)
155+
}
151156

152-
assertEquals(Play, tcMediaEvents[2].eventType)
153-
assertTrue(tcMediaEvents[2].assets.isNotEmpty())
154-
assertNull(tcMediaEvents[2].sourceId)
157+
tcMediaEvents[2].let {
158+
assertEquals(Play, it.eventType)
159+
assertTrue(it.assets.isNotEmpty())
160+
assertNull(it.sourceId)
161+
}
155162
}
156163

157164
@Test
@@ -309,13 +316,16 @@ class CommandersActTrackerIntegrationTest {
309316

310317
assertEquals(2, tcMediaEvents.size)
311318

312-
assertEquals(Pause, tcMediaEvents[0].eventType)
313-
assertTrue(tcMediaEvents[0].assets.isNotEmpty())
314-
assertNull(tcMediaEvents[0].sourceId)
315-
316-
assertEquals(Play, tcMediaEvents[1].eventType)
317-
assertTrue(tcMediaEvents[1].assets.isNotEmpty())
318-
assertNull(tcMediaEvents[1].sourceId)
319+
tcMediaEvents[0].let {
320+
assertEquals(Pause, it.eventType)
321+
assertTrue(it.assets.isNotEmpty())
322+
assertNull(it.sourceId)
323+
}
324+
tcMediaEvents[1].let {
325+
assertEquals(Play, it.eventType)
326+
assertTrue(it.assets.isNotEmpty())
327+
assertNull(it.sourceId)
328+
}
319329
}
320330

321331
@Test
@@ -351,33 +361,39 @@ class CommandersActTrackerIntegrationTest {
351361

352362
assertEquals(3, tcMediaEvents.size)
353363

354-
assertEquals(Play, tcMediaEvents[0].eventType)
355-
assertTrue(tcMediaEvents[0].assets.isNotEmpty())
356-
assertNull(tcMediaEvents[0].sourceId)
357-
358-
assertEquals(Pause, tcMediaEvents[1].eventType)
359-
assertTrue(tcMediaEvents[1].assets.isNotEmpty())
360-
assertNull(tcMediaEvents[1].sourceId)
361-
362-
assertEquals(Play, tcMediaEvents[2].eventType)
363-
assertTrue(tcMediaEvents[2].assets.isNotEmpty())
364-
assertNull(tcMediaEvents[2].sourceId)
364+
tcMediaEvents[0].let {
365+
assertEquals(Play, it.eventType)
366+
assertTrue(it.assets.isNotEmpty())
367+
assertNull(it.sourceId)
368+
}
369+
tcMediaEvents[1].let {
370+
assertEquals(Pause, it.eventType)
371+
assertTrue(it.assets.isNotEmpty())
372+
assertNull(it.sourceId)
373+
}
374+
tcMediaEvents[2].let {
375+
assertEquals(Play, it.eventType)
376+
assertTrue(it.assets.isNotEmpty())
377+
assertNull(it.sourceId)
378+
}
365379
}
366380

367381
@Test
368382
fun `player prepared, playing and stopped`() {
369383
val tcMediaEvents = mutableListOf<TCMediaEvent>()
370384

371-
player.setMediaItem(SRGMediaItemBuilder(URN_LIVE_VIDEO).build())
385+
player.setMediaItem(SRGMediaItemBuilder(URN_NOT_LIVE_VIDEO).build())
372386
player.prepare()
373387
player.playWhenReady = true
374388

375389
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_READY)
376390
TestPlayerRunHelper.runUntilPendingCommandsAreFullyHandled(player)
377391

378-
clock.advanceTime(2.minutes.inWholeMilliseconds)
392+
val position = 2.minutes
393+
TestPillarboxRunHelper.runUntilPosition(player, position = position, clock = clock)
379394
player.stop()
380395

396+
TestPlayerRunHelper.runUntilPendingCommandsAreFullyHandled(player)
381397
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_IDLE)
382398

383399
verifyOrder {
@@ -389,13 +405,59 @@ class CommandersActTrackerIntegrationTest {
389405

390406
assertEquals(2, tcMediaEvents.size)
391407

392-
assertEquals(Stop, tcMediaEvents[0].eventType)
393-
assertTrue(tcMediaEvents[0].assets.isNotEmpty())
394-
assertNull(tcMediaEvents[0].sourceId)
408+
tcMediaEvents[0].let {
409+
assertEquals(Stop, it.eventType)
410+
assertTrue(it.assets.isNotEmpty())
411+
assertNull(it.sourceId)
412+
assertEquals(position, it.mediaPosition)
413+
}
395414

396-
assertEquals(Play, tcMediaEvents[1].eventType)
397-
assertTrue(tcMediaEvents[1].assets.isNotEmpty())
398-
assertNull(tcMediaEvents[1].sourceId)
415+
tcMediaEvents[1].let {
416+
assertEquals(Play, it.eventType)
417+
assertTrue(it.assets.isNotEmpty())
418+
assertNull(it.sourceId)
419+
}
420+
}
421+
422+
@Test
423+
fun `player prepared, playing and remove last item`() {
424+
val tcMediaEvents = mutableListOf<TCMediaEvent>()
425+
426+
player.setMediaItem(SRGMediaItemBuilder(URN_NOT_LIVE_VIDEO).build())
427+
player.prepare()
428+
player.playWhenReady = true
429+
430+
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_READY)
431+
TestPlayerRunHelper.runUntilPendingCommandsAreFullyHandled(player)
432+
433+
val position = 2.minutes
434+
TestPillarboxRunHelper.runUntilPosition(player, position = position, clock = clock)
435+
player.removeMediaItem(0)
436+
437+
TestPlayerRunHelper.runUntilPendingCommandsAreFullyHandled(player)
438+
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_ENDED)
439+
440+
verifyOrder {
441+
commandersAct.enableRunningInBackground()
442+
commandersAct.sendTcMediaEvent(capture(tcMediaEvents))
443+
commandersAct.sendTcMediaEvent(capture(tcMediaEvents))
444+
}
445+
confirmVerified(commandersAct)
446+
447+
assertEquals(2, tcMediaEvents.size)
448+
449+
tcMediaEvents[0].let {
450+
assertEquals(Stop, it.eventType)
451+
assertTrue(it.assets.isNotEmpty())
452+
assertNull(it.sourceId)
453+
assertEquals(position, it.mediaPosition)
454+
}
455+
456+
tcMediaEvents[1].let {
457+
assertEquals(Play, it.eventType)
458+
assertTrue(it.assets.isNotEmpty())
459+
assertNull(it.sourceId)
460+
}
399461
}
400462

401463
@Test
@@ -424,17 +486,21 @@ class CommandersActTrackerIntegrationTest {
424486

425487
assertEquals(3, tcMediaEvents.size)
426488

427-
assertEquals(Play, tcMediaEvents[0].eventType)
428-
assertTrue(tcMediaEvents[0].assets.isNotEmpty())
429-
assertNull(tcMediaEvents[0].sourceId)
430-
431-
assertEquals(Seek, tcMediaEvents[1].eventType)
432-
assertTrue(tcMediaEvents[1].assets.isNotEmpty())
433-
assertNull(tcMediaEvents[1].sourceId)
434-
435-
assertEquals(Play, tcMediaEvents[2].eventType)
436-
assertTrue(tcMediaEvents[2].assets.isNotEmpty())
437-
assertNull(tcMediaEvents[2].sourceId)
489+
tcMediaEvents[0].let {
490+
assertEquals(Play, it.eventType)
491+
assertTrue(it.assets.isNotEmpty())
492+
assertNull(it.sourceId)
493+
}
494+
tcMediaEvents[1].let {
495+
assertEquals(Seek, it.eventType)
496+
assertTrue(it.assets.isNotEmpty())
497+
assertNull(it.sourceId)
498+
}
499+
tcMediaEvents[2].let {
500+
assertEquals(Play, it.eventType)
501+
assertTrue(it.assets.isNotEmpty())
502+
assertNull(it.sourceId)
503+
}
438504
}
439505

440506
@Test
@@ -508,17 +574,23 @@ class CommandersActTrackerIntegrationTest {
508574

509575
assertEquals(3, tcMediaEvents.size)
510576

511-
assertEquals(Pause, tcMediaEvents[0].eventType)
512-
assertTrue(tcMediaEvents[0].assets.isNotEmpty())
513-
assertNull(tcMediaEvents[0].sourceId)
577+
tcMediaEvents[0].let {
578+
assertEquals(Pause, it.eventType)
579+
assertTrue(it.assets.isNotEmpty())
580+
assertNull(it.sourceId)
581+
}
514582

515-
assertEquals(Pos, tcMediaEvents[1].eventType)
516-
assertTrue(tcMediaEvents[1].assets.isNotEmpty())
517-
assertNull(tcMediaEvents[1].sourceId)
583+
tcMediaEvents[1].let {
584+
assertEquals(Pos, it.eventType)
585+
assertTrue(it.assets.isNotEmpty())
586+
assertNull(it.sourceId)
587+
}
518588

519-
assertEquals(Play, tcMediaEvents[2].eventType)
520-
assertTrue(tcMediaEvents[2].assets.isNotEmpty())
521-
assertNull(tcMediaEvents[2].sourceId)
589+
tcMediaEvents[2].let {
590+
assertEquals(Play, it.eventType)
591+
assertTrue(it.assets.isNotEmpty())
592+
assertNull(it.sourceId)
593+
}
522594
}
523595

524596
@Test

pillarbox-player-testutils/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ plugins {
1111
dependencies {
1212
api(libs.androidx.media3.common)
1313
compileOnly(libs.androidx.media3.exoplayer)
14+
implementation(libs.androidx.media3.test.utils)
1415
implementation(libs.androidx.media3.test.utils.robolectric)
1516
implementation(libs.guava)
1617
runtimeOnly(libs.kotlinx.coroutines.android)

pillarbox-player-testutils/src/main/java/ch/srgssr/pillarbox/player/test/utils/TestPillarboxRunHelper.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ import androidx.media3.common.Player
1010
import androidx.media3.common.util.Assertions
1111
import androidx.media3.common.util.Clock
1212
import androidx.media3.exoplayer.ExoPlayer
13+
import androidx.media3.test.utils.FakeClock
1314
import androidx.media3.test.utils.robolectric.RobolectricUtil
1415
import java.util.concurrent.TimeoutException
1516
import java.util.concurrent.atomic.AtomicBoolean
17+
import kotlin.time.Duration
1618

1719
object TestPillarboxRunHelper {
1820
private fun verifyMainTestThread(player: Player) {
@@ -82,4 +84,23 @@ object TestPillarboxRunHelper {
8284
throw IllegalStateException(player.playerError)
8385
}
8486
}
87+
88+
/**
89+
* Run and wait until [position] is reached
90+
*
91+
* @param player The [Player].
92+
* @param position The position to wait for.
93+
* @param clock The [FakeClock].
94+
*/
95+
@Throws(TimeoutException::class)
96+
fun runUntilPosition(player: Player, position: Duration, clock: FakeClock) {
97+
verifyMainTestThread(player)
98+
if (player is ExoPlayer) {
99+
verifyPlaybackThreadIsAlive(player)
100+
}
101+
clock.advanceTime(position.inWholeMilliseconds)
102+
RobolectricUtil.runMainLooperUntil {
103+
player.currentPosition >= position.inWholeMilliseconds
104+
}
105+
}
85106
}

pillarbox-player/src/main/java/ch/srgssr/pillarbox/player/tracker/CurrentMediaItemPillarboxDataTracker.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ internal class CurrentMediaItemPillarboxDataTracker(private val player: ExoPlaye
7979
}
8080

8181
private inner class CurrentMediaItemListener : Player.Listener {
82+
8283
override fun onMediaItemTransition(
8384
mediaItem: MediaItem?,
8485
@Player.MediaItemTransitionReason reason: Int,
@@ -92,7 +93,10 @@ internal class CurrentMediaItemPillarboxDataTracker(private val player: ExoPlaye
9293
timeline: Timeline,
9394
@Player.TimelineChangeReason reason: Int,
9495
) {
95-
notifyPillarboxDataChange(player.currentMediaItem)
96+
// PillarboxData are loaded when event TIMELINE_CHANGE_REASON_SOURCE_UPDATE is send.
97+
if (reason == Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE) {
98+
notifyPillarboxDataChange(player.currentMediaItem)
99+
}
96100
}
97101
}
98102
}

0 commit comments

Comments
 (0)