test(PlaybackState): add state machine transition tests
- Idle → Connecting, Connecting → Playing/Paused/Reconnecting/Idle - Playing → Paused/Reconnecting/Idle - Paused → Connecting - Reconnecting → Connecting/Idle - Stop from any state returns to Idle Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,163 @@
|
|||||||
|
package xyz.cottongin.radio247.service
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import xyz.cottongin.radio247.data.model.Station
|
||||||
|
|
||||||
|
class PlaybackStateMachineTest {
|
||||||
|
|
||||||
|
private lateinit var stateFlow: MutableStateFlow<PlaybackState>
|
||||||
|
private val testStation = Station(
|
||||||
|
id = 1L,
|
||||||
|
name = "Test Station",
|
||||||
|
url = "http://example.com/stream"
|
||||||
|
)
|
||||||
|
private val testUrls = listOf(
|
||||||
|
"http://example.com/stream1",
|
||||||
|
"http://example.com/stream2"
|
||||||
|
)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
stateFlow = MutableStateFlow(PlaybackState.Idle)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `idle to connecting on play`() {
|
||||||
|
val newState = PlaybackState.Connecting(
|
||||||
|
station = testStation,
|
||||||
|
urls = testUrls,
|
||||||
|
currentUrlIndex = 0
|
||||||
|
)
|
||||||
|
stateFlow.value = newState
|
||||||
|
assertTrue(stateFlow.value is PlaybackState.Connecting)
|
||||||
|
assertEquals(0, (stateFlow.value as PlaybackState.Connecting).currentUrlIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `connecting to playing on engine started`() {
|
||||||
|
stateFlow.value = PlaybackState.Connecting(station = testStation, urls = testUrls)
|
||||||
|
val connectionStartedAt = System.currentTimeMillis()
|
||||||
|
stateFlow.value = PlaybackState.Playing(
|
||||||
|
station = testStation,
|
||||||
|
sessionStartedAt = connectionStartedAt,
|
||||||
|
connectionStartedAt = connectionStartedAt
|
||||||
|
)
|
||||||
|
assertTrue(stateFlow.value is PlaybackState.Playing)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `connecting advances URL index on connection failure`() {
|
||||||
|
stateFlow.value = PlaybackState.Connecting(
|
||||||
|
station = testStation, urls = testUrls, currentUrlIndex = 0
|
||||||
|
)
|
||||||
|
stateFlow.value = PlaybackState.Connecting(
|
||||||
|
station = testStation, urls = testUrls, currentUrlIndex = 1
|
||||||
|
)
|
||||||
|
assertEquals(1, (stateFlow.value as PlaybackState.Connecting).currentUrlIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `connecting to reconnecting when all URLs exhausted and stayConnected`() {
|
||||||
|
stateFlow.value = PlaybackState.Connecting(
|
||||||
|
station = testStation, urls = testUrls, currentUrlIndex = 1
|
||||||
|
)
|
||||||
|
stateFlow.value = PlaybackState.Reconnecting(
|
||||||
|
station = testStation, attempt = 1, sessionStartedAt = 0L
|
||||||
|
)
|
||||||
|
assertTrue(stateFlow.value is PlaybackState.Reconnecting)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `connecting to idle when all URLs exhausted and not stayConnected`() {
|
||||||
|
stateFlow.value = PlaybackState.Connecting(
|
||||||
|
station = testStation, urls = testUrls, currentUrlIndex = 1
|
||||||
|
)
|
||||||
|
stateFlow.value = PlaybackState.Idle
|
||||||
|
assertTrue(stateFlow.value is PlaybackState.Idle)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `playing to paused on user pause`() {
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
stateFlow.value = PlaybackState.Playing(
|
||||||
|
station = testStation, sessionStartedAt = now, connectionStartedAt = now
|
||||||
|
)
|
||||||
|
stateFlow.value = PlaybackState.Paused(
|
||||||
|
station = testStation, sessionStartedAt = now
|
||||||
|
)
|
||||||
|
assertTrue(stateFlow.value is PlaybackState.Paused)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `playing to reconnecting on connection lost with stayConnected`() {
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
stateFlow.value = PlaybackState.Playing(
|
||||||
|
station = testStation, sessionStartedAt = now, connectionStartedAt = now
|
||||||
|
)
|
||||||
|
stateFlow.value = PlaybackState.Reconnecting(
|
||||||
|
station = testStation, sessionStartedAt = now, attempt = 1
|
||||||
|
)
|
||||||
|
assertTrue(stateFlow.value is PlaybackState.Reconnecting)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `playing to idle on connection lost without stayConnected`() {
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
stateFlow.value = PlaybackState.Playing(
|
||||||
|
station = testStation, sessionStartedAt = now, connectionStartedAt = now
|
||||||
|
)
|
||||||
|
stateFlow.value = PlaybackState.Idle
|
||||||
|
assertTrue(stateFlow.value is PlaybackState.Idle)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `paused to connecting on resume`() {
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
stateFlow.value = PlaybackState.Paused(
|
||||||
|
station = testStation, sessionStartedAt = now
|
||||||
|
)
|
||||||
|
stateFlow.value = PlaybackState.Connecting(
|
||||||
|
station = testStation, urls = testUrls, currentUrlIndex = 0
|
||||||
|
)
|
||||||
|
assertTrue(stateFlow.value is PlaybackState.Connecting)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `reconnecting to connecting on retry`() {
|
||||||
|
stateFlow.value = PlaybackState.Reconnecting(
|
||||||
|
station = testStation, sessionStartedAt = 0L, attempt = 3
|
||||||
|
)
|
||||||
|
stateFlow.value = PlaybackState.Connecting(
|
||||||
|
station = testStation, urls = testUrls, currentUrlIndex = 0
|
||||||
|
)
|
||||||
|
assertTrue(stateFlow.value is PlaybackState.Connecting)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `reconnecting to idle on user stop`() {
|
||||||
|
stateFlow.value = PlaybackState.Reconnecting(
|
||||||
|
station = testStation, sessionStartedAt = 0L, attempt = 2
|
||||||
|
)
|
||||||
|
stateFlow.value = PlaybackState.Idle
|
||||||
|
assertTrue(stateFlow.value is PlaybackState.Idle)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `stop from any state returns to idle`() {
|
||||||
|
val states = listOf(
|
||||||
|
PlaybackState.Connecting(station = testStation, urls = testUrls),
|
||||||
|
PlaybackState.Playing(station = testStation, sessionStartedAt = 0L, connectionStartedAt = 0L),
|
||||||
|
PlaybackState.Paused(station = testStation, sessionStartedAt = 0L),
|
||||||
|
PlaybackState.Reconnecting(station = testStation, sessionStartedAt = 0L, attempt = 1)
|
||||||
|
)
|
||||||
|
for (state in states) {
|
||||||
|
stateFlow.value = state
|
||||||
|
stateFlow.value = PlaybackState.Idle
|
||||||
|
assertTrue("Should be Idle after stop from $state", stateFlow.value is PlaybackState.Idle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user