Refactored the remaining test cases of ZimManageViewModel to follow the new approach. Additionally, added new unit test cases to cover new functionalities such as pagination, fetching new content when changing language, searching, etc.

* Improved some unit test cases that sometimes failed on CI due to coroutine IO threading issues.
This commit is contained in:
MohitMaliFtechiz 2025-07-11 04:23:08 +05:30
parent 136052e0bb
commit badddc8aab
7 changed files with 165 additions and 243 deletions

View File

@ -60,14 +60,6 @@ class OnlineLibraryManager @Inject constructor(
it.printStackTrace() it.printStackTrace()
}.getOrNull() }.getOrNull()
suspend fun getOnlineBooksLanguage(): List<String> {
return runCatching {
library.booksLanguages.distinct()
}.onFailure {
it.printStackTrace()
}.getOrDefault(emptyList())
}
/** /**
* Builds the URL for fetching the OPDS library entries with pagination and optional filters. * Builds the URL for fetching the OPDS library entries with pagination and optional filters.
* *

View File

@ -163,6 +163,8 @@ class ZimManageViewModel @Inject constructor(
val books: List<LibkiwixBook> val books: List<LibkiwixBook>
) )
@Suppress("InjectDispatcher")
private var ioDispatcher: CoroutineDispatcher = Dispatchers.IO
private var isUnitTestCase: Boolean = false private var isUnitTestCase: Boolean = false
val sideEffects: MutableSharedFlow<SideEffect<*>> = MutableSharedFlow() val sideEffects: MutableSharedFlow<SideEffect<*>> = MutableSharedFlow()
private val _libraryItems = MutableStateFlow<List<LibraryListItem>>(emptyList()) private val _libraryItems = MutableStateFlow<List<LibraryListItem>>(emptyList())
@ -313,11 +315,11 @@ class ZimManageViewModel @Inject constructor(
onCleared() onCleared()
} }
private fun observeCoroutineFlows(dispatcher: CoroutineDispatcher = Dispatchers.IO) { private fun observeCoroutineFlows() {
val downloads = downloadDao.downloads() val downloads = downloadDao.downloads()
val booksFromDao = books() val booksFromDao = books()
coroutineJobs.apply { coroutineJobs.apply {
add(scanBooksFromStorage(dispatcher)) add(scanBooksFromStorage())
add(updateBookItems()) add(updateBookItems())
add(fileSelectActions()) add(fileSelectActions())
add(updateLibraryItems(booksFromDao, downloads, networkLibrary)) add(updateLibraryItems(booksFromDao, downloads, networkLibrary))
@ -340,7 +342,7 @@ class ZimManageViewModel @Inject constructor(
} }
@OptIn(FlowPreview::class) @OptIn(FlowPreview::class)
private fun observeSearch(dispatcher: CoroutineDispatcher = Dispatchers.IO) = private fun observeSearch() =
requestFiltering requestFiltering
.onEach { .onEach {
libraryListIsRefreshing.postValue(true) libraryListIsRefreshing.postValue(true)
@ -349,17 +351,17 @@ class ZimManageViewModel @Inject constructor(
) )
} }
.debounce(500) .debounce(500)
.flowOn(dispatcher) .flowOn(ioDispatcher)
.launchIn(viewModelScope) .launchIn(viewModelScope)
private fun observeLanguageChanges(dispatcher: CoroutineDispatcher = Dispatchers.IO) = private fun observeLanguageChanges() =
sharedPreferenceUtil.onlineContentLanguage sharedPreferenceUtil.onlineContentLanguage
.onEach { .onEach {
updateOnlineLibraryFilters( updateOnlineLibraryFilters(
OnlineLibraryRequest(lang = it, page = ZERO, isLoadMoreItem = false) OnlineLibraryRequest(lang = it, page = ZERO, isLoadMoreItem = false)
) )
} }
.flowOn(dispatcher) .flowOn(ioDispatcher)
.launchIn(viewModelScope) .launchIn(viewModelScope)
fun updateOnlineLibraryFilters(newRequest: OnlineLibraryRequest) { fun updateOnlineLibraryFilters(newRequest: OnlineLibraryRequest) {
@ -381,11 +383,11 @@ class ZimManageViewModel @Inject constructor(
} }
.launchIn(viewModelScope) .launchIn(viewModelScope)
private fun scanBooksFromStorage(dispatcher: CoroutineDispatcher = Dispatchers.IO) = private fun scanBooksFromStorage() =
checkFileSystemForBooksOnRequest(books()) checkFileSystemForBooksOnRequest(books())
.catch { it.printStackTrace() } .catch { it.printStackTrace() }
.onEach { books -> libkiwixBookOnDisk.insert(books) } .onEach { books -> libkiwixBookOnDisk.insert(books) }
.flowOn(dispatcher) .flowOn(ioDispatcher)
.launchIn(viewModelScope) .launchIn(viewModelScope)
private fun fileSelectActions() = private fun fileSelectActions() =
@ -472,14 +474,11 @@ class ZimManageViewModel @Inject constructor(
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
private fun requestsAndConnectivityChangesToLibraryRequests( private fun requestsAndConnectivityChangesToLibraryRequests(
library: MutableStateFlow<List<LibkiwixBook>>, library: MutableStateFlow<List<LibkiwixBook>>
dispatcher: CoroutineDispatcher = Dispatchers.IO
) = requestDownloadLibrary.onEach { onlineLibraryRequest -> ) = requestDownloadLibrary.onEach { onlineLibraryRequest ->
// Cancel any previous ongoing job
onlineLibraryFetchingJob?.cancel() onlineLibraryFetchingJob?.cancel()
// Launch new job onlineLibraryFetchingJob = viewModelScope.launch(ioDispatcher) {
onlineLibraryFetchingJob = viewModelScope.launch(dispatcher) {
connectivityBroadcastReceiver.networkStates connectivityBroadcastReceiver.networkStates
.filter { it == CONNECTED } .filter { it == CONNECTED }
.take(1) .take(1)
@ -499,15 +498,17 @@ class ZimManageViewModel @Inject constructor(
} }
} }
.collect { result -> .collect { result ->
library.value = if (result.onlineLibraryRequest.isLoadMoreItem) { library.emit(
if (result.onlineLibraryRequest.isLoadMoreItem) {
library.value + result.books library.value + result.books
} else { } else {
result.books result.books
} }
)
resetDownloadState() resetDownloadState()
} }
} }
}.flowOn(dispatcher) }.flowOn(ioDispatcher)
.launchIn(viewModelScope) .launchIn(viewModelScope)
private fun shouldProceedWithDownload(onlineLibraryRequest: OnlineLibraryRequest): Flow<KiwixService> { private fun shouldProceedWithDownload(onlineLibraryRequest: OnlineLibraryRequest): Flow<KiwixService> {
@ -606,9 +607,8 @@ class ZimManageViewModel @Inject constructor(
private fun updateLibraryItems( private fun updateLibraryItems(
localBooksFromLibkiwix: Flow<List<Book>>, localBooksFromLibkiwix: Flow<List<Book>>,
downloads: Flow<List<DownloadModel>>, downloads: Flow<List<DownloadModel>>,
library: MutableStateFlow<List<LibkiwixBook>>, library: MutableStateFlow<List<LibkiwixBook>>
dispatcher: CoroutineDispatcher = Dispatchers.IO ) = viewModelScope.launch(ioDispatcher) {
) = viewModelScope.launch(dispatcher) {
combine( combine(
localBooksFromLibkiwix, localBooksFromLibkiwix,
downloads, downloads,

View File

@ -50,6 +50,7 @@ import org.kiwix.kiwixmobile.language.viewmodel.Action.Select
import org.kiwix.kiwixmobile.language.viewmodel.Action.UpdateLanguages import org.kiwix.kiwixmobile.language.viewmodel.Action.UpdateLanguages
import org.kiwix.kiwixmobile.language.viewmodel.State.Content import org.kiwix.kiwixmobile.language.viewmodel.State.Content
import org.kiwix.kiwixmobile.language.viewmodel.State.Loading import org.kiwix.kiwixmobile.language.viewmodel.State.Loading
import org.kiwix.kiwixmobile.zimManager.TURBINE_TIMEOUT
import org.kiwix.kiwixmobile.zimManager.testFlow import org.kiwix.kiwixmobile.zimManager.testFlow
import org.kiwix.sharedFunctions.InstantExecutorExtension import org.kiwix.sharedFunctions.InstantExecutorExtension
import org.kiwix.sharedFunctions.language import org.kiwix.sharedFunctions.language
@ -199,7 +200,8 @@ class LanguageViewModelTest {
assert = { assert = {
assertThat(awaitItem()).isEqualTo(Loading) assertThat(awaitItem()).isEqualTo(Loading)
assertThat(awaitItem()).isEqualTo(Content(listOf())) assertThat(awaitItem()).isEqualTo(Content(listOf()))
} },
TURBINE_TIMEOUT
) )
} }

View File

@ -45,6 +45,8 @@ import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain import kotlinx.coroutines.test.setMain
import okhttp3.HttpUrl
import okhttp3.Response
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
@ -56,13 +58,13 @@ import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao
import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk
import org.kiwix.kiwixmobile.core.data.DataSource import org.kiwix.kiwixmobile.core.data.DataSource
import org.kiwix.kiwixmobile.core.data.remote.KiwixService import org.kiwix.kiwixmobile.core.data.remote.KiwixService
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
import org.kiwix.kiwixmobile.core.downloader.model.DownloadModel import org.kiwix.kiwixmobile.core.downloader.model.DownloadModel
import org.kiwix.kiwixmobile.core.entity.LibkiwixBook import org.kiwix.kiwixmobile.core.ui.components.ONE
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower
import org.kiwix.kiwixmobile.core.utils.files.ScanningProgressListener import org.kiwix.kiwixmobile.core.utils.files.ScanningProgressListener
import org.kiwix.kiwixmobile.core.zim_manager.ConnectivityBroadcastReceiver import org.kiwix.kiwixmobile.core.zim_manager.ConnectivityBroadcastReceiver
import org.kiwix.kiwixmobile.core.zim_manager.Language
import org.kiwix.kiwixmobile.core.zim_manager.NetworkState import org.kiwix.kiwixmobile.core.zim_manager.NetworkState
import org.kiwix.kiwixmobile.core.zim_manager.NetworkState.CONNECTED import org.kiwix.kiwixmobile.core.zim_manager.NetworkState.CONNECTED
import org.kiwix.kiwixmobile.core.zim_manager.NetworkState.NOT_CONNECTED import org.kiwix.kiwixmobile.core.zim_manager.NetworkState.NOT_CONNECTED
@ -87,9 +89,9 @@ import org.kiwix.kiwixmobile.zimManager.fileselectView.effects.StartMultiSelecti
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem
import org.kiwix.libkiwix.Book import org.kiwix.libkiwix.Book
import org.kiwix.sharedFunctions.InstantExecutorExtension import org.kiwix.sharedFunctions.InstantExecutorExtension
import org.kiwix.sharedFunctions.MOCK_BASE_URL
import org.kiwix.sharedFunctions.bookOnDisk import org.kiwix.sharedFunctions.bookOnDisk
import org.kiwix.sharedFunctions.downloadModel import org.kiwix.sharedFunctions.downloadModel
import org.kiwix.sharedFunctions.language
import org.kiwix.sharedFunctions.libkiwixBook import org.kiwix.sharedFunctions.libkiwixBook
import java.util.Locale import java.util.Locale
import kotlin.time.Duration import kotlin.time.Duration
@ -118,7 +120,6 @@ class ZimManageViewModelTest {
private val downloads = MutableStateFlow<List<DownloadModel>>(emptyList()) private val downloads = MutableStateFlow<List<DownloadModel>>(emptyList())
private val booksOnFileSystem = MutableStateFlow<List<Book>>(emptyList()) private val booksOnFileSystem = MutableStateFlow<List<Book>>(emptyList())
private val books = MutableStateFlow<List<BookOnDisk>>(emptyList()) private val books = MutableStateFlow<List<BookOnDisk>>(emptyList())
private val languages = MutableStateFlow<List<Language>>(emptyList())
private val onlineContentLanguage = MutableStateFlow("") private val onlineContentLanguage = MutableStateFlow("")
private val fileSystemStates = private val fileSystemStates =
MutableStateFlow<FileSystemState>(FileSystemState.DetectingFileSystem) MutableStateFlow<FileSystemState>(FileSystemState.DetectingFileSystem)
@ -159,16 +160,35 @@ class ZimManageViewModelTest {
} returns networkCapabilities } returns networkCapabilities
every { networkCapabilities.hasTransport(TRANSPORT_WIFI) } returns true every { networkCapabilities.hasTransport(TRANSPORT_WIFI) } returns true
every { sharedPreferenceUtil.prefWifiOnly } returns true every { sharedPreferenceUtil.prefWifiOnly } returns true
every { sharedPreferenceUtil.getCachedLanguageList() } returns languages.value
coEvery { onlineLibraryManager.getOnlineBooksLanguage() } returns listOf("eng")
every { sharedPreferenceUtil.onlineContentLanguage } returns onlineContentLanguage every { sharedPreferenceUtil.onlineContentLanguage } returns onlineContentLanguage
every { sharedPreferenceUtil.selectedOnlineContentLanguage } returns ""
every { onlineLibraryManager.getStartOffset(any(), any()) } returns ONE
every {
onlineLibraryManager.buildLibraryUrl(
any(),
any(),
any(),
any(),
any(),
any()
)
} returns MOCK_BASE_URL
val response = mockk<retrofit2.Response<String>>()
val rawResponse = mockk<Response>()
every { response.raw() } returns rawResponse
val httpsUrl = mockk<HttpUrl>()
every { httpsUrl.host } returns ""
every { httpsUrl.scheme } returns ""
every { rawResponse.networkResponse?.request?.url } returns httpsUrl
coEvery { kiwixService.getLibraryPage(any()) } returns response
every { response.body() } returns ""
downloads.value = emptyList() downloads.value = emptyList()
booksOnFileSystem.value = emptyList() booksOnFileSystem.value = emptyList()
books.value = emptyList() books.value = emptyList()
languages.value = emptyList()
fileSystemStates.value = FileSystemState.DetectingFileSystem fileSystemStates.value = FileSystemState.DetectingFileSystem
booksOnDiskListItems.value = emptyList() booksOnDiskListItems.value = emptyList()
networkStates.value = NOT_CONNECTED networkStates.value = NOT_CONNECTED
onlineContentLanguage.value = ""
viewModel = viewModel =
ZimManageViewModel( ZimManageViewModel(
downloadRoomDao, downloadRoomDao,
@ -258,136 +278,17 @@ class ZimManageViewModelTest {
@Nested @Nested
inner class Languages { inner class Languages {
@Test @Test
fun `network no result & empty language db activates the default locale`() = runTest { fun `changing language updates the filter and do the network request`() = runTest {
val expectedLanguage =
Language(
active = true,
occurencesOfLanguage = 1,
language = "eng",
languageLocalized = "englocal",
languageCode = "ENG",
languageCodeISO2 = "en"
)
expectNetworkDbAndDefault(
listOf(),
listOf(),
expectedLanguage
)
advanceUntilIdle()
// verify { languageRoomDao.insert(listOf(expectedLanguage)) }
}
@Test
fun `network no result & a language db result triggers nothing`() = runTest {
expectNetworkDbAndDefault(
listOf(),
listOf(
Language(
active = true,
occurencesOfLanguage = 1,
language = "eng",
languageLocalized = "englocal",
languageCode = "ENG",
languageCodeISO2 = "en"
)
),
language(isActive = true, occurencesOfLanguage = 1)
)
// verify { languageRoomDao.insert(any()) }
}
@Test
fun `network result & empty language db triggers combined result of default + network`() =
runTest {
val defaultLanguage =
Language(
active = true,
occurencesOfLanguage = 1,
language = "English",
languageLocalized = "English",
languageCode = "eng",
languageCodeISO2 = "eng"
)
expectNetworkDbAndDefault(
listOf(
libkiwixBook(language = "eng"),
libkiwixBook(language = "eng"),
libkiwixBook(language = "fra")
),
listOf(),
defaultLanguage
)
verify {
// languageRoomDao.insert(
// listOf(
// defaultLanguage.copy(occurencesOfLanguage = 2),
// Language(
// active = false,
// occurencesOfLanguage = 1,
// language = "fra",
// languageLocalized = "",
// languageCode = "",
// languageCodeISO2 = ""
// )
// )
// )
}
}
@Test
fun `network result & language db results activates a combined network + db result`() =
runTest {
val dbLanguage =
Language(
active = true,
occurencesOfLanguage = 1,
language = "English",
languageLocalized = "English",
languageCode = "eng",
languageCodeISO2 = "eng"
)
expectNetworkDbAndDefault(
listOf(
libkiwixBook(language = "eng"),
libkiwixBook(language = "eng"),
libkiwixBook(language = "fra")
),
listOf(dbLanguage),
language(isActive = true, occurencesOfLanguage = 1)
)
advanceUntilIdle()
verify(timeout = MOCKK_TIMEOUT_FOR_VERIFICATION) {
// languageRoomDao.insert(
// listOf(
// dbLanguage.copy(occurencesOfLanguage = 2),
// Language(
// active = false,
// occurencesOfLanguage = 1,
// language = "fra",
// languageLocalized = "fra",
// languageCode = "fra",
// languageCodeISO2 = "fra"
// )
// )
// )
}
}
private suspend fun TestScope.expectNetworkDbAndDefault(
networkBooks: List<LibkiwixBook>,
dbBooks: List<Language>,
defaultLanguage: Language
) {
every { application.getString(any()) } returns "" every { application.getString(any()) } returns ""
every { application.getString(any(), any()) } returns "" every { application.getString(any(), any()) } returns ""
// every { defaultLanguageProvider.provide() } returns defaultLanguage viewModel.onlineLibraryRequest.test {
coEvery { onlineLibraryManager.getOnlineBooksLanguage() } returns networkBooks.map { it.language } skipItems(1)
viewModel.networkLibrary.emit(networkBooks) onlineContentLanguage.emit("eng")
advanceUntilIdle() val onlineLibraryRequest = awaitItem()
languages.value = dbBooks assertThat(onlineLibraryRequest.lang).isEqualTo("eng")
advanceUntilIdle() assertThat(onlineLibraryRequest.page).isEqualTo(ZERO)
networkStates.value = CONNECTED assertThat(onlineLibraryRequest.isLoadMoreItem).isEqualTo(false)
advanceUntilIdle() }
} }
} }
@ -399,6 +300,32 @@ class ZimManageViewModelTest {
.assertValue(NOT_CONNECTED) .assertValue(NOT_CONNECTED)
} }
@Test
fun `updateOnlineLibraryFilters updates onlineLibraryRequest`() = runTest {
val newRequest = ZimManageViewModel.OnlineLibraryRequest(
query = "test",
category = "cat",
lang = "en",
page = 2,
isLoadMoreItem = true
)
viewModel.onlineLibraryRequest.test {
skipItems(1)
viewModel.updateOnlineLibraryFilters(newRequest)
assertThat(awaitItem()).isEqualTo(newRequest)
}
}
@Test
fun `search triggers downloading online content`() = runTest {
viewModel.onlineLibraryRequest.test {
skipItems(1)
viewModel.requestFiltering.emit("test")
advanceUntilIdle()
assertThat(awaitItem().query).isEqualTo("test")
}
}
@Test @Test
fun `library update removes from sources and maps to list items`() = runTest { fun `library update removes from sources and maps to list items`() = runTest {
val book = BookTestWrapper("0") val book = BookTestWrapper("0")
@ -406,54 +333,31 @@ class ZimManageViewModelTest {
libkiwixBook(id = "0", url = "", language = Locale.ENGLISH.language, nativeBook = book) libkiwixBook(id = "0", url = "", language = Locale.ENGLISH.language, nativeBook = book)
val bookDownloading = libkiwixBook(id = "1", url = "") val bookDownloading = libkiwixBook(id = "1", url = "")
val bookWithActiveLanguage = libkiwixBook(id = "3", language = "activeLanguage", url = "") val bookWithActiveLanguage = libkiwixBook(id = "3", language = "activeLanguage", url = "")
val bookWithInactiveLanguage = libkiwixBook(id = "4", language = "inactiveLanguage", url = "") viewModel.libraryItems.test {
testFlow(
flow = viewModel.libraryItems,
triggerAction = {
every { application.getString(any()) } returns "" every { application.getString(any()) } returns ""
every { application.getString(any(), any()) } returns "" every { application.getString(any(), any()) } returns ""
coEvery {
onlineLibraryManager.parseOPDSStreamAndGetBooks(any(), any())
} returns arrayListOf(bookWithActiveLanguage)
networkStates.value = CONNECTED networkStates.value = CONNECTED
downloads.value = listOf(downloadModel(book = bookDownloading)) downloads.value = listOf(downloadModel(book = bookDownloading))
books.value = listOf(bookOnDisk(book = bookAlreadyOnDisk)) books.value = listOf(bookOnDisk(book = bookAlreadyOnDisk))
languages.value =
listOf(
language(isActive = true, occurencesOfLanguage = 1, languageCode = "activeLanguage"),
language(isActive = false, occurencesOfLanguage = 1, languageCode = "inactiveLanguage")
)
fileSystemStates.value = CanWrite4GbFile fileSystemStates.value = CanWrite4GbFile
runBlocking {
viewModel.networkLibrary.emit(
listOf(
bookAlreadyOnDisk,
bookDownloading,
bookWithActiveLanguage,
bookWithInactiveLanguage
)
)
}
advanceUntilIdle() advanceUntilIdle()
},
assert = {
while (true) {
val items = awaitItem() val items = awaitItem()
val bookItems = items.filterIsInstance<LibraryListItem.BookItem>() val bookItems = items.filterIsInstance<LibraryListItem.BookItem>()
if (bookItems.size >= 2 && bookItems[0].fileSystemState == CanWrite4GbFile) { if (bookItems.size >= 2 && bookItems[0].fileSystemState == CanWrite4GbFile) {
// assertThat(items).isEqualTo( assertThat(items).isEqualTo(
// listOf( listOf(
// LibraryListItem.DividerItem(Long.MAX_VALUE, R.string.downloading), LibraryListItem.DividerItem(Long.MAX_VALUE, "Downloading:"),
// LibraryListItem.LibraryDownloadItem(downloadModel(book = bookDownloading)), LibraryListItem.LibraryDownloadItem(downloadModel(book = bookDownloading)),
// LibraryListItem.DividerItem(Long.MAX_VALUE - 1, R.string.your_languages), LibraryListItem.DividerItem(Long.MAX_VALUE - 1, "All languages"),
// LibraryListItem.BookItem(bookWithActiveLanguage, CanWrite4GbFile), LibraryListItem.BookItem(bookWithActiveLanguage, CanWrite4GbFile),
// LibraryListItem.DividerItem(Long.MIN_VALUE, R.string.other_languages),
// LibraryListItem.BookItem(bookWithInactiveLanguage, CanWrite4GbFile)
// )
// )
break
}
}
},
timeout = TURBINE_TIMEOUT
) )
)
}
}
} }
@Test @Test
@ -464,39 +368,60 @@ class ZimManageViewModelTest {
url = "", url = "",
size = "${Fat32Checker.FOUR_GIGABYTES_IN_KILOBYTES + 1}" size = "${Fat32Checker.FOUR_GIGABYTES_IN_KILOBYTES + 1}"
) )
every { application.getString(any()) } returns "" every { application.getString(any()) } answers { "" }
every { application.getString(any(), any()) } returns "" every { application.getString(any(), any()) } answers { "" }
testFlow( every { application.getString(any(), *anyVararg()) } answers { "" }
viewModel.libraryItems,
triggerAction = { // test libraryItems fetches for all language.
networkStates.tryEmit(CONNECTED) viewModel.libraryItems.test {
downloads.tryEmit(listOf()) coEvery {
books.tryEmit(listOf()) onlineLibraryManager.parseOPDSStreamAndGetBooks(any(), any())
languages.tryEmit( } returns arrayListOf(bookOver4Gb)
listOf( networkStates.value = CONNECTED
language(isActive = true, occurencesOfLanguage = 1, languageCode = "activeLanguage") downloads.value = listOf()
) books.value = listOf()
) onlineContentLanguage.value = ""
fileSystemStates.tryEmit(CannotWrite4GbFile) fileSystemStates.emit(FileSystemState.DetectingFileSystem)
viewModel.networkLibrary.emit(listOf(bookOver4Gb)) fileSystemStates.emit(CannotWrite4GbFile)
}, advanceUntilIdle()
assert = {
while (true) {
val item = awaitItem() val item = awaitItem()
val bookItem = item.filterIsInstance<LibraryListItem.BookItem>().firstOrNull() val bookItem = item.filterIsInstance<LibraryListItem.BookItem>().firstOrNull()
if (bookItem?.fileSystemState == CannotWrite4GbFile) { if (bookItem?.fileSystemState == CannotWrite4GbFile) {
assertThat(item).isEqualTo( assertThat(item).isEqualTo(
listOf( listOf(
// LibraryListItem.DividerItem(Long.MIN_VALUE, R.string.other_languages), LibraryListItem.DividerItem(Long.MIN_VALUE, "All languages"),
LibraryListItem.BookItem(bookOver4Gb, CannotWrite4GbFile) LibraryListItem.BookItem(bookOver4Gb, CannotWrite4GbFile)
) )
) )
break
} }
} }
},
timeout = TURBINE_TIMEOUT // test library items fetches for a particular language
viewModel.libraryItems.test {
coEvery {
onlineLibraryManager.parseOPDSStreamAndGetBooks(any(), any())
} returns arrayListOf(bookOver4Gb)
every { application.getString(any(), any()) } answers { "Selected language: English" }
networkStates.value = CONNECTED
downloads.value = listOf()
books.value = listOf()
onlineContentLanguage.value = "eng"
fileSystemStates.emit(FileSystemState.DetectingFileSystem)
fileSystemStates.emit(CannotWrite4GbFile)
advanceUntilIdle()
val item = awaitItem()
val bookItem = item.filterIsInstance<LibraryListItem.BookItem>().firstOrNull()
if (bookItem?.fileSystemState == CannotWrite4GbFile) {
assertThat(item).isEqualTo(
listOf(
LibraryListItem.DividerItem(Long.MIN_VALUE, "Selected language: English"),
LibraryListItem.BookItem(bookOver4Gb, CannotWrite4GbFile)
) )
)
}
}
} }
@Nested @Nested
@ -620,6 +545,7 @@ suspend fun <T> TestScope.testFlow(
triggerAction() triggerAction()
assert() assert()
cancelAndIgnoreRemainingEvents() cancelAndIgnoreRemainingEvents()
ensureAllEventsConsumed()
} }
} }
job.join() job.join()

View File

@ -58,6 +58,6 @@ interface KiwixService {
companion object { companion object {
const val OPDS_LIBRARY_ENDPOINT = "v2/entries" const val OPDS_LIBRARY_ENDPOINT = "v2/entries"
const val ITEMS_PER_PAGE = 50 const val ITEMS_PER_PAGE = 25
} }
} }

View File

@ -183,6 +183,7 @@ suspend fun <T> TestScope.testFlow(
triggerAction() triggerAction()
assert() assert()
cancelAndIgnoreRemainingEvents() cancelAndIgnoreRemainingEvents()
ensureAllEventsConsumed()
} }
} }
job.join() job.join()

View File

@ -241,6 +241,7 @@ suspend fun <T> TestScope.testFlow(
triggerAction() triggerAction()
assert() assert()
cancelAndIgnoreRemainingEvents() cancelAndIgnoreRemainingEvents()
ensureAllEventsConsumed()
} }
} }
job.join() job.join()