mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 23:21:59 +01:00
address review: re-anchoring and consume edge cases
- jumpTo no longer returns early on a stale current position after a re-anchor, which could leave the player and snapshot unsynchronised. - playNext from inside the block inserts after the entry holding the current track, not always at index 1. - expose a layout version in PlaybackState so the queue view reloads when a re-anchor shifts the combined positions under it. - removeAt recomputes the current track's combined position from the refreshed snapshot instead of assuming a one-down shift. - restore keeps the saved resume position when the anchor moves. - next on the last queued track with repeat off no longer consumes it and leaves it playing; repeat wraps instead. - drop a redundant sort in range; revert a rejected cross-section drag by reloading.
This commit is contained in:
@@ -429,6 +429,189 @@ class PlaybackControllerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun skippingPastTheBlockAfterConsumingKeepsGoing() {
|
||||
seedAlbum(3, sourceId = 1)
|
||||
seedSong("x1", "al2")
|
||||
seedSong("x2", "al2")
|
||||
|
||||
controller.playAlbum(1, "al1", 0)
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "s1"
|
||||
}
|
||||
controller.addToQueue(1, QueueKind.Song, "x1")
|
||||
controller.addToQueue(1, QueueKind.Song, "x2")
|
||||
|
||||
controller.playAt(2)
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "x2"
|
||||
}
|
||||
controller.next()
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "s2"
|
||||
}
|
||||
|
||||
controller.next()
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "s3"
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun playNextFromInsideTheBlockGoesAfterTheCurrentTrack() {
|
||||
seedAlbum(3, sourceId = 1)
|
||||
seedSong("x1", "al2")
|
||||
seedSong("x2", "al2")
|
||||
seedSong("x3", "al2")
|
||||
|
||||
controller.playAlbum(1, "al1", 0)
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "s1"
|
||||
}
|
||||
controller.addToQueue(1, QueueKind.Song, "x1")
|
||||
controller.addToQueue(1, QueueKind.Song, "x2")
|
||||
controller.playAt(2)
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "x2"
|
||||
}
|
||||
|
||||
controller.playNext(1, QueueKind.Song, "x3")
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "x2"
|
||||
}
|
||||
await { runBlocking { controller.upcomingItem()?.id } == "x3" }
|
||||
|
||||
controller.next()
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "x3"
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removingAContextTrackUnderTheBlockKeepsTheCursorOnThePlayingTrack() {
|
||||
seedAlbum(3, sourceId = 1)
|
||||
seedSong("x1", "al2")
|
||||
|
||||
controller.playAlbum(1, "al1", 0)
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "s1"
|
||||
}
|
||||
controller.addToQueue(1, QueueKind.Song, "x1")
|
||||
await { runBlocking { controller.upcomingItem()?.id } == "x1" }
|
||||
controller.next()
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "x1"
|
||||
}
|
||||
|
||||
runBlocking { controller.removeAt(0) }
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "x1"
|
||||
}
|
||||
|
||||
assertEquals(1L, runBlocking { queues.cursor() })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun restoringKeepsTheSavedPositionWhenTheAnchorMoves() {
|
||||
seedAlbum(3, sourceId = 1)
|
||||
seedSong("x1", "al2")
|
||||
seedSong("x2", "al2")
|
||||
|
||||
controller.playAlbum(1, "al1", 0)
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "s1"
|
||||
}
|
||||
controller.addToQueue(1, QueueKind.Song, "x1")
|
||||
controller.addToQueue(1, QueueKind.Song, "x2")
|
||||
controller.playAt(2)
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "x2"
|
||||
}
|
||||
controller.next()
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "s2"
|
||||
}
|
||||
|
||||
runBlocking { queues.setPosition(12_000L) }
|
||||
|
||||
val restoredHandle = FakePlayerHandle()
|
||||
val restored = PlaybackController(sources, queues, FakePlayerConnection(restoredHandle), dispatcher = dispatcher)
|
||||
restored.connect()
|
||||
await {
|
||||
restored.state.value.item
|
||||
?.id == "s2"
|
||||
}
|
||||
|
||||
assertEquals(12_000L, restored.positionMs.value)
|
||||
restored.close()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nextOnTheLastQueuedTrackDoesNotStrandIt() {
|
||||
seedAlbum(3, sourceId = 1)
|
||||
seedSong("x1", "al2")
|
||||
|
||||
controller.playAlbum(1, "al1", 2)
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "s3"
|
||||
}
|
||||
controller.addToQueue(1, QueueKind.Song, "x1")
|
||||
await { runBlocking { controller.upcomingItem()?.id } == "x1" }
|
||||
controller.next()
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "x1"
|
||||
}
|
||||
|
||||
controller.next()
|
||||
Thread.sleep(200)
|
||||
|
||||
assertFalse(runBlocking { queues.snapshot().upNext }.isEmpty())
|
||||
assertEquals(
|
||||
"x1",
|
||||
controller.state.value.item
|
||||
?.id,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun reanchoringBumpsTheLayoutVersion() {
|
||||
seedAlbum(4, sourceId = 1)
|
||||
seedSong("x1", "al2")
|
||||
|
||||
controller.playAlbum(1, "al1", 1)
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "s2"
|
||||
}
|
||||
controller.addToQueue(1, QueueKind.Song, "x1")
|
||||
await { runBlocking { controller.upcomingItem()?.id } == "x1" }
|
||||
val before = controller.state.value.layout
|
||||
|
||||
controller.playAt(4)
|
||||
await {
|
||||
controller.state.value.item
|
||||
?.id == "s4"
|
||||
}
|
||||
|
||||
assertTrue(controller.state.value.layout > before)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun theDurationUsesTheNextTracksMetadataWhileItLoads() {
|
||||
seedAlbum(3, sourceId = 1)
|
||||
|
||||
Reference in New Issue
Block a user