mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-11 07:29:14 +01:00
close the test gaps a QA pass found, and de-flake the flaky ones
Verify the Subsonic token hash and salt freshness, and fall back to per-album songs when the empty-search probe fails with an I/O error instead of aborting the whole sync. Add a real-server sync test that mirrors navidrome and gonic into Room and reads the rows back, plus SyncManager coverage for the success, coalescing and mid-sync-failure paths. Cover the sort mapping and delete/ping ViewModels, the download engine's request flags, the queue helpers, download prefs and migration data preservation. Drop the leaked collectors in the ViewModel/queue tests, document the screenshot tests as render smoke tests, bind a real search term in the trigram plan test, and assert a positive in-flight invariant alongside the concurrency timing checks.
This commit is contained in:
@@ -14,6 +14,7 @@ import com.subtracks.data.repo.DownloadRepository
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
@@ -42,6 +43,10 @@ class DownloadNotifier(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun close() {
|
||||
scope.cancel()
|
||||
}
|
||||
|
||||
private fun build(content: DownloadNotification): Notification =
|
||||
NotificationCompat
|
||||
.Builder(context, CHANNEL_ID)
|
||||
|
||||
@@ -35,6 +35,7 @@ import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -78,7 +79,9 @@ class DownloadRepository(
|
||||
}
|
||||
|
||||
fun close() {
|
||||
val job = scope.coroutineContext[Job]
|
||||
scope.cancel()
|
||||
runBlocking { job?.join() }
|
||||
}
|
||||
|
||||
fun states(): StateFlow<Map<String, SongDownload>> = statesFlow
|
||||
|
||||
@@ -27,6 +27,7 @@ import kotlinx.coroutines.sync.Semaphore
|
||||
import kotlinx.coroutines.sync.withPermit
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.xml.sax.Attributes
|
||||
import java.io.IOException
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class SubsonicSource(
|
||||
@@ -272,6 +273,8 @@ class SubsonicSource(
|
||||
) { input -> SubsonicXml.containsEntity(input, "song") }
|
||||
} catch (_: SubsonicException) {
|
||||
false
|
||||
} catch (_: IOException) {
|
||||
false
|
||||
}
|
||||
emptyQuerySearchSupported = supported
|
||||
return supported
|
||||
|
||||
@@ -159,8 +159,8 @@ class LibraryViewModel(
|
||||
fun sync() = syncManager.requestSync()
|
||||
}
|
||||
|
||||
private fun ListQuery.albumSort(): AlbumSort = AlbumSort.entries.firstOrNull { it.name == sort } ?: AlbumSort.Name
|
||||
internal fun ListQuery.albumSort(): AlbumSort = AlbumSort.entries.firstOrNull { it.name == sort } ?: AlbumSort.Name
|
||||
|
||||
private fun ListQuery.artistSort(): ArtistSort = ArtistSort.entries.firstOrNull { it.name == sort } ?: ArtistSort.Name
|
||||
internal fun ListQuery.artistSort(): ArtistSort = ArtistSort.entries.firstOrNull { it.name == sort } ?: ArtistSort.Name
|
||||
|
||||
private fun ListQuery.playlistSort(): PlaylistSort = PlaylistSort.entries.firstOrNull { it.name == sort } ?: PlaylistSort.Name
|
||||
internal fun ListQuery.playlistSort(): PlaylistSort = PlaylistSort.entries.firstOrNull { it.name == sort } ?: PlaylistSort.Name
|
||||
|
||||
Reference in New Issue
Block a user