add a one-step undo and harden queue edits

This commit is contained in:
austinried
2026-10-04 00:29:51 +00:00
parent e87d176155
commit 7fc142ce6f
11 changed files with 283 additions and 82 deletions
@@ -42,7 +42,7 @@ class QueuePagingSourceTest {
runTest {
seedSongs(130)
repository.replace(listOf(repository.songsEntry(1)))
val source = repository.pagingSource()
val source = repository.pagingSource(initialPosition = 0)
val first = load(source, key = null)
assertEquals((0L until 60L).toList(), first.data.map { it.position })
@@ -60,10 +60,24 @@ class QueuePagingSourceTest {
assertEquals(1L, third.prevKey)
}
@Test
fun theFirstPageStartsAtTheInitialPosition() =
runTest {
seedSongs(130)
repository.replace(listOf(repository.songsEntry(1)))
val source = repository.pagingSource(initialPosition = 100)
val first = load(source, key = null)
assertEquals((60L until 120L).toList(), first.data.map { it.position })
assertEquals(2L, first.nextKey)
assertEquals(0L, first.prevKey)
}
@Test
fun anEmptyQueueLoadsNothing() =
runTest {
val result = load(repository.pagingSource(), key = null)
val result = load(repository.pagingSource(0), key = null)
assertEquals(emptyList<QueueWindowItem>(), result.data)
assertEquals(null, result.nextKey)
@@ -15,6 +15,8 @@ import com.subtracks.data.model.Source
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -299,6 +301,17 @@ class QueueRepositoryTest {
assertEquals(listOf("s2", "s3", "s4", "s1"), resolveAll(repository.snapshot()))
}
@Test
fun movingFromOutsideTheQueueChangesNothing() =
runTest {
seedLibrary()
repository.snapshotAfter(listOf(repository.albumEntry(1, "al1")))
assertFalse(repository.move(from = 5, to = 0))
assertTrue(repository.move(from = 0, to = 2))
assertEquals(listOf("s2", "s3", "s1"), resolveAll(repository.snapshot()))
}
private suspend fun QueueRepository.snapshotAfter(entries: List<QueueEntry>): QueueSnapshot {
replace(entries)
return snapshot()
@@ -370,7 +370,7 @@ class PlaybackControllerTest {
}
assertEquals(0L, controller.state.value.position)
assertTrue(handle.operations.contains("removeAt(0)"))
assertTrue(handle.operations.any { it.startsWith("setWindow") })
assertFalse(controller.state.value.hasPrevious)
}
@@ -437,6 +437,79 @@ class PlaybackControllerTest {
)
}
@Test
fun undoRestoresARemovedTrack() {
seedAlbum(5, sourceId = 1)
controller.playAlbum(1, "al1", 2)
await {
controller.state.value.item
?.id == "s3"
}
runBlocking { controller.removeAt(3) }
await { handle.itemCount == 4 }
runBlocking { controller.undo() }
await { handle.itemCount == 5 }
assertEquals(listOf("s1", "s2", "s3", "s4", "s5"), handle.items.map { it.id })
assertEquals(
"s3",
controller.state.value.item
?.id,
)
assertEquals(2L, controller.state.value.position)
}
@Test
fun undoRestoresAReorderedQueue() {
seedAlbum(4, sourceId = 1)
controller.playAlbum(1, "al1", 0)
await {
controller.state.value.item
?.id == "s1"
}
runBlocking { controller.move(0, 2) }
await { handle.items.map { it.id } == listOf("s2", "s3", "s1", "s4") }
runBlocking { controller.undo() }
await { handle.items.map { it.id } == listOf("s1", "s2", "s3", "s4") }
assertEquals(
"s1",
controller.state.value.item
?.id,
)
assertEquals(0L, controller.state.value.position)
}
@Test
fun startingANewQueueClearsTheUndo() {
seedAlbum(3, sourceId = 1)
controller.playAlbum(1, "al1", 0)
await {
controller.state.value.item
?.id == "s1"
}
runBlocking { controller.removeAt(1) }
await { handle.itemCount == 2 }
controller.playAlbum(1, "al1", 2)
await {
controller.state.value.item
?.id == "s3"
}
runBlocking { controller.undo() }
assertEquals(listOf("s1", "s2", "s3"), handle.items.map { it.id })
}
private fun seedAlbum(
count: Int,
sourceId: Long,
@@ -237,6 +237,7 @@ class AppScreenshotTest {
onPlay = {},
onRemove = {},
onMove = { _, _ -> },
onUndo = {},
modifier = Modifier.fillMaxSize(),
)
}