mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 23:21:59 +01:00
address network quality review findings
- fall back to the legacy max_bitrate/stream_format keys so existing settings survive the upgrade - keep the last network mode when there is no active network instead of reporting mobile, so a connectivity gap does not flip modes and cause spurious reloads - skip the quality reload when the player has ended or advanced mid-read - keep the reload collector alive across transient failures - edit stream quality from a local dialog draft so a fast second tap is not lost - cover the mode mapping, migration, paused reload and equal-quality no-op with tests
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
package com.subtracks.data.net
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class NetworkModeTest {
|
||||
@Test
|
||||
fun meteredIsMobileAndUnmeteredIsWifi() {
|
||||
assertEquals(NetworkMode.Mobile, networkModeFor(true, NetworkMode.Wifi))
|
||||
assertEquals(NetworkMode.Wifi, networkModeFor(false, NetworkMode.Mobile))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun noActiveNetworkKeepsTheLastMode() {
|
||||
assertEquals(NetworkMode.Wifi, networkModeFor(null, NetworkMode.Wifi))
|
||||
assertEquals(NetworkMode.Mobile, networkModeFor(null, NetworkMode.Mobile))
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.subtracks.data.prefs
|
||||
|
||||
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.intPreferencesKey
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.subtracks.data.net.NetworkMode
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -55,6 +58,28 @@ class UserPreferencesTest {
|
||||
assertEquals(StreamQuality(320, null), prefs.streamQuality(NetworkMode.Wifi).first())
|
||||
assertEquals(StreamQuality(96, "opus"), prefs.streamQuality(NetworkMode.Mobile).first())
|
||||
|
||||
file.delete()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun streamQualityFallsBackToTheLegacySingleValue() =
|
||||
runTest {
|
||||
val file = File.createTempFile("user-prefs", ".preferences_pb").apply { delete() }
|
||||
val store = PreferenceDataStoreFactory.create(scope = backgroundScope) { file }
|
||||
store.edit { prefs ->
|
||||
prefs[intPreferencesKey("max_bitrate")] = 192
|
||||
prefs[stringPreferencesKey("stream_format")] = "mp3"
|
||||
}
|
||||
val prefs = UserPreferences(store)
|
||||
|
||||
assertEquals(StreamQuality(192, "mp3"), prefs.streamQuality(NetworkMode.Wifi).first())
|
||||
assertEquals(StreamQuality(192, "mp3"), prefs.streamQuality(NetworkMode.Mobile).first())
|
||||
|
||||
prefs.setStreamQuality(NetworkMode.Mobile, StreamQuality(64, null))
|
||||
|
||||
assertEquals(StreamQuality(192, "mp3"), prefs.streamQuality(NetworkMode.Wifi).first())
|
||||
assertEquals(StreamQuality(64, null), prefs.streamQuality(NetworkMode.Mobile).first())
|
||||
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.subtracks.data.repo.SourceRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.asCoroutineDispatcher
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.OkHttpClient
|
||||
@@ -39,6 +40,7 @@ class PlaybackControllerTest {
|
||||
|
||||
private lateinit var db: SubtracksDatabase
|
||||
private lateinit var prefs: UserPreferences
|
||||
private lateinit var networkMode: MutableStateFlow<NetworkMode>
|
||||
private lateinit var sources: SourceRepository
|
||||
private lateinit var queues: QueueRepository
|
||||
private lateinit var handle: FakePlayerHandle
|
||||
@@ -53,7 +55,8 @@ class PlaybackControllerTest {
|
||||
.setDriver(BundledSQLiteDriver())
|
||||
.build()
|
||||
prefs = fakeUserPreferences()
|
||||
sources = SourceRepository(db, OkHttpClient(), prefs)
|
||||
networkMode = MutableStateFlow(NetworkMode.Wifi)
|
||||
sources = SourceRepository(db, OkHttpClient(), prefs, networkMode = networkMode)
|
||||
queues = QueueRepository(db)
|
||||
handle = FakePlayerHandle()
|
||||
controller = PlaybackController(sources, queues, FakePlayerConnection(handle), dispatcher = dispatcher)
|
||||
@@ -1039,6 +1042,69 @@ class PlaybackControllerTest {
|
||||
assertEquals(listOf("prepare", "play"), handle.operations.takeLast(2))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun changingStreamQualityWhilePausedReloadsWithoutResuming() {
|
||||
seedAlbum(60, sourceId = 1)
|
||||
runBlocking { prefs.setStreamQuality(NetworkMode.Wifi, StreamQuality(64, null)) }
|
||||
await { sources.quality.value == StreamQuality(64, null) }
|
||||
|
||||
controller.playAlbum(1, "al1", 10)
|
||||
await { handle.playWhenReady }
|
||||
controller.togglePlayPause()
|
||||
await { !handle.playWhenReady }
|
||||
handle.positionMs = 33_000L
|
||||
val before = handle.operations.size
|
||||
val windows = handle.operations.count { it.startsWith("setWindow") }
|
||||
|
||||
runBlocking { prefs.setStreamQuality(NetworkMode.Wifi, StreamQuality(128, "opus")) }
|
||||
await { handle.operations.count { it.startsWith("setWindow") } > windows }
|
||||
|
||||
assertEquals(33_000L, handle.positionMs)
|
||||
assertFalse(handle.playWhenReady)
|
||||
assertTrue(handle.operations.drop(before).none { it.startsWith("prepare") || it.startsWith("play") })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun switchingToTheOtherModeReloadsWithItsQuality() {
|
||||
seedAlbum(60, sourceId = 1)
|
||||
runBlocking {
|
||||
prefs.setStreamQuality(NetworkMode.Wifi, StreamQuality(320, null))
|
||||
prefs.setStreamQuality(NetworkMode.Mobile, StreamQuality(96, "opus"))
|
||||
}
|
||||
await { sources.quality.value == StreamQuality(320, null) }
|
||||
|
||||
controller.playAlbum(1, "al1", 10)
|
||||
await { handle.operations.contains("play") }
|
||||
handle.positionMs = 12_000L
|
||||
val windows = handle.operations.count { it.startsWith("setWindow") }
|
||||
|
||||
networkMode.value = NetworkMode.Mobile
|
||||
await { sources.quality.value == StreamQuality(96, "opus") }
|
||||
await { handle.operations.count { it.startsWith("setWindow") } > windows }
|
||||
|
||||
assertEquals("s11", handle.currentItem?.id)
|
||||
assertEquals(12_000L, handle.positionMs)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun switchingToTheOtherModeWithEqualQualityDoesNotReload() {
|
||||
seedAlbum(60, sourceId = 1)
|
||||
runBlocking {
|
||||
prefs.setStreamQuality(NetworkMode.Wifi, StreamQuality(96, "opus"))
|
||||
prefs.setStreamQuality(NetworkMode.Mobile, StreamQuality(96, "opus"))
|
||||
}
|
||||
await { sources.quality.value == StreamQuality(96, "opus") }
|
||||
|
||||
controller.playAlbum(1, "al1", 10)
|
||||
await { handle.operations.contains("play") }
|
||||
val windows = handle.operations.count { it.startsWith("setWindow") }
|
||||
|
||||
networkMode.value = NetworkMode.Mobile
|
||||
runBlocking { delay(300) }
|
||||
|
||||
assertEquals(windows, handle.operations.count { it.startsWith("setWindow") })
|
||||
}
|
||||
|
||||
private fun seedAlbum(
|
||||
count: Int,
|
||||
sourceId: Long,
|
||||
|
||||
Reference in New Issue
Block a user