mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 15:11:58 +01:00
read queue view rows on the main dispatcher in the view model tests
`QueueViewModelTest` read `viewModel.rows` (a `mutableStateListOf` mutated on the main dispatcher) from the test thread, racing the view model: CI saw `ConcurrentModificationException` in `removingWhileShuffledReconcilesTheRows` and a torn read in `removingATrackUnderTheBlockReloadsRows`. Copy the rows on the dispatcher via a `rowsSnapshot()` helper before asserting.
This commit is contained in:
@@ -24,6 +24,7 @@ import kotlinx.coroutines.asCoroutineDispatcher
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.test.resetMain
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.OkHttpClient
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
@@ -146,8 +147,9 @@ class QueueViewModelTest {
|
||||
|
||||
await {
|
||||
val snapshot = runBlocking { queues.snapshot() }
|
||||
viewModel.rows.isNotEmpty() &&
|
||||
viewModel.rows.all { runBlocking { queues.itemAt(snapshot, it.position)?.song?.id } == it.song.song.id }
|
||||
val rows = rowsSnapshot()
|
||||
rows.isNotEmpty() &&
|
||||
runBlocking { rows.all { queues.itemAt(snapshot, it.position)?.song?.id == it.song.song.id } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,17 +312,17 @@ class QueueViewModelTest {
|
||||
|
||||
viewModel.remove(2)
|
||||
await {
|
||||
val positions = viewModel.rows.map { it.position }
|
||||
val positions = rowsSnapshot().map { it.position }
|
||||
positions.isNotEmpty() && positions == (positions.first()..positions.last()).toList()
|
||||
}
|
||||
|
||||
val snapshot = runBlocking { queues.snapshot() }
|
||||
val rows = rowsSnapshot()
|
||||
val expected =
|
||||
runBlocking {
|
||||
(viewModel.rows.first().position..viewModel.rows.last().position)
|
||||
.mapNotNull { queues.itemAt(snapshot, it)?.song?.id }
|
||||
(rows.first().position..rows.last().position).mapNotNull { queues.itemAt(snapshot, it)?.song?.id }
|
||||
}
|
||||
assertEquals(expected, viewModel.rows.map { it.song.song.id })
|
||||
assertEquals(expected, rows.map { it.song.song.id })
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -440,6 +442,10 @@ class QueueViewModelTest {
|
||||
)
|
||||
}
|
||||
|
||||
// `rows` is a snapshot list mutated on the main dispatcher, so read it there rather than from
|
||||
// the test thread (otherwise iterating it races the view model and throws or reads torn state).
|
||||
private fun rowsSnapshot(): List<QueueRow> = runBlocking { withContext(dispatcher) { viewModel.rows.toList() } }
|
||||
|
||||
private fun await(predicate: () -> Boolean) {
|
||||
repeat(500) {
|
||||
if (predicate()) return
|
||||
|
||||
Reference in New Issue
Block a user