mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 23:21:59 +01:00
address the bulk download review
- Cancelling a list while its download loop was still enqueueing missed every song queued after the cancel looked. The loop now holds the lock for the whole queue, so a cancel waits for it to finish and then removes all of it. That needed the enqueue helper split into a locked entry point and a body the loop can call while already holding the lock — calling the locked one from inside the lock deadlocked, which is what hung the tests. - An empty resolved list now says so instead of doing nothing. - One song failing to enqueue no longer aborts the rest of the batch. - The artist actions resolve through the albums the artist page lists, matching what is on screen, rather than through songs.artistId, which for a guest on a compilation enqueued nothing. - Bulk actions are one `applyAction` on the repository, so the three view models and both screens stop repeating the same dispatch. - Tests: the playlist and artist resolve paths, a Running row counted as downloading, the action dispatch, and the action rule moved to a plain unit test.
This commit is contained in:
@@ -12,6 +12,7 @@ import com.subtracks.data.download.ArtworkStore
|
||||
import com.subtracks.data.download.FakeDownloadEngine
|
||||
import com.subtracks.data.model.Album
|
||||
import com.subtracks.data.model.Artist
|
||||
import com.subtracks.data.model.BulkDownloadAction
|
||||
import com.subtracks.data.model.DownloadList
|
||||
import com.subtracks.data.model.DownloadStatus
|
||||
import com.subtracks.data.model.ListDownloadStatus
|
||||
@@ -490,6 +491,10 @@ class DownloadRepositoryTest {
|
||||
db.downloadDao().upsert(SongDownload(1, "s2", DownloadStatus.Queued, engineId = 22))
|
||||
|
||||
assertEquals(ListDownloadStatus(total = 2, downloaded = 1, downloading = 1), statusOf(DownloadList.Album, "al1"))
|
||||
|
||||
db.downloadDao().upsert(SongDownload(1, "s2", DownloadStatus.Running, engineId = 22))
|
||||
|
||||
assertEquals(ListDownloadStatus(total = 2, downloaded = 1, downloading = 1), statusOf(DownloadList.Album, "al1"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -514,6 +519,72 @@ class DownloadRepositoryTest {
|
||||
refId: String,
|
||||
): ListDownloadStatus = runBlocking { repository.status(1, list, refId).first() }
|
||||
|
||||
@Test
|
||||
fun downloadingAPlaylistQueuesEverySong() =
|
||||
runTest {
|
||||
seedLibrary()
|
||||
runBlocking {
|
||||
db.libraryDao().upsertPlaylists(listOf(Playlist(1, "pl1", "Playlist", null, null, 2, 0)))
|
||||
db.libraryDao().upsertPlaylistSongs(
|
||||
listOf(
|
||||
PlaylistSong(1, "pl1", "s1", 0),
|
||||
PlaylistSong(1, "pl1", "s2", 1),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
runBlocking { repository.downloadAll(1, DownloadList.Playlist, "pl1") }
|
||||
|
||||
assertEquals(listOf("s1", "s2"), requestedSongIds())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun downloadingAnArtistQueuesTheSongsOfItsAlbums() =
|
||||
runTest {
|
||||
seedLibrary()
|
||||
// s2 is a guest on the album, so its own artistId differs: the artist page lists the
|
||||
// album, and the download follows what the page shows.
|
||||
runBlocking {
|
||||
db.libraryDao().upsertSongs(
|
||||
listOf(
|
||||
Song(
|
||||
sourceId = 1,
|
||||
id = "s2",
|
||||
albumId = "al1",
|
||||
artistId = "ar2",
|
||||
title = "Song 2",
|
||||
album = "Album",
|
||||
artist = "Guest",
|
||||
duration = 100,
|
||||
track = 2,
|
||||
disc = 1,
|
||||
starred = null,
|
||||
genre = null,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
runBlocking { repository.downloadAll(1, DownloadList.Artist, "ar1") }
|
||||
|
||||
assertEquals(listOf("s1", "s2"), requestedSongIds())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun applyActionRoutesToTheMatchingBulkAction() =
|
||||
runTest {
|
||||
seedLibrary()
|
||||
|
||||
runBlocking { repository.applyAction(1, DownloadList.Album, "al1", BulkDownloadAction.Download) }
|
||||
assertEquals(2, engine.requests.size)
|
||||
|
||||
runBlocking { repository.applyAction(1, DownloadList.Album, "al1", BulkDownloadAction.Cancel) }
|
||||
|
||||
assertEquals(ListDownloadStatus(total = 2), statusOf(DownloadList.Album, "al1"))
|
||||
}
|
||||
|
||||
private fun requestedSongIds(): List<String> = engine.requests.map { it.second.path.substringAfterLast('/') }
|
||||
|
||||
private fun album(coverArt: String?) =
|
||||
Album(
|
||||
sourceId = 1,
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.subtracks.ui.components
|
||||
|
||||
import com.subtracks.data.model.BulkDownloadAction
|
||||
import com.subtracks.data.model.ListDownloadStatus
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class BulkDownloadActionTest {
|
||||
@Test
|
||||
fun anUndownloadedListOffersDownload() {
|
||||
assertEquals(BulkDownloadAction.Download, ListDownloadStatus(total = 3).action())
|
||||
assertEquals(BulkDownloadAction.Download, ListDownloadStatus().action())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun aPartiallyDownloadedListStillOffersDownload() {
|
||||
assertEquals(BulkDownloadAction.Download, ListDownloadStatus(total = 3, downloaded = 1).action())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun aListWithDownloadsInFlightOffersCancel() {
|
||||
assertEquals(BulkDownloadAction.Cancel, ListDownloadStatus(total = 3, downloaded = 1, downloading = 1).action())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun aFullyDownloadedListOffersDelete() {
|
||||
assertEquals(BulkDownloadAction.Delete, ListDownloadStatus(total = 3, downloaded = 3).action())
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.subtracks.data.model.BulkDownloadAction
|
||||
import com.subtracks.data.model.ListDownloadStatus
|
||||
import com.subtracks.ui.theme.SubtracksTheme
|
||||
import org.junit.Assert.assertEquals
|
||||
@@ -21,27 +22,6 @@ class HeroDownloadTest {
|
||||
@get:Rule
|
||||
val composeRule = createAndroidComposeRule<ComponentActivity>()
|
||||
|
||||
@Test
|
||||
fun anUndownloadedListOffersDownload() {
|
||||
assertEquals(BulkDownloadAction.Download, ListDownloadStatus(total = 3).action())
|
||||
assertEquals(BulkDownloadAction.Download, ListDownloadStatus().action())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun aPartiallyDownloadedListStillOffersDownload() {
|
||||
assertEquals(BulkDownloadAction.Download, ListDownloadStatus(total = 3, downloaded = 1).action())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun aListWithDownloadsInFlightOffersCancel() {
|
||||
assertEquals(BulkDownloadAction.Cancel, ListDownloadStatus(total = 3, downloaded = 1, downloading = 1).action())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun aFullyDownloadedListOffersDelete() {
|
||||
assertEquals(BulkDownloadAction.Delete, ListDownloadStatus(total = 3, downloaded = 3).action())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun tappingTheButtonReportsTheActionItsStateOffers() {
|
||||
val actions = mutableListOf<BulkDownloadAction>()
|
||||
|
||||
Reference in New Issue
Block a user