mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-09-23 12:42:56 -04:00
Merge pull request #4396 from kiwix/remove_objectbox_code_from_project
Removed ObjectBox code from the project (except migration-related code).
This commit is contained in:
commit
b827cfe542
@ -17,7 +17,6 @@
|
||||
<ID>NestedBlockDepth:ReceiverHandShake.kt$ReceiverHandShake$override fun exchangeFileTransferMetadata(inputStream: InputStream, outputStream: OutputStream)</ID>
|
||||
<ID>PackageNaming:AvailableSpaceCalculator.kt$package
|
||||
org.kiwix.kiwixmobile.zimManager.libraryView</ID>
|
||||
<ID>PackageNaming:DefaultLanguageProvider.kt$package org.kiwix.kiwixmobile.zimManager</ID>
|
||||
<ID>PackageNaming:DeleteFiles.kt$package
|
||||
org.kiwix.kiwixmobile.zimManager.fileselectView.effects</ID>
|
||||
<ID>PackageNaming:Fat32Checker.kt$package org.kiwix.kiwixmobile.zimManager</ID>
|
||||
|
@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2020 Kiwix <android.kiwix.org>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.kiwix.kiwixmobile.core.dao
|
||||
|
||||
import io.objectbox.query.Query
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.channels.trySendBlocking
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
class FlowBuilder @Inject constructor() {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun <T> buildCallbackFlow(query: Query<T>) =
|
||||
callbackFlow<List<T>> {
|
||||
val subscription =
|
||||
query.subscribe()
|
||||
.observer { trySendBlocking(it) }
|
||||
awaitClose(subscription::cancel)
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2019 Kiwix <android.kiwix.org>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package org.kiwix.kiwixmobile.core.dao
|
||||
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.kotlin.query
|
||||
import io.objectbox.query.QueryBuilder
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.HistoryEntity
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.HistoryEntity_
|
||||
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
||||
import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.HistoryItem
|
||||
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource.Companion.fromDatabaseValue
|
||||
import javax.inject.Inject
|
||||
|
||||
class HistoryDao @Inject constructor(val box: Box<HistoryEntity>) : PageDao {
|
||||
fun history(): Flow<List<Page>> =
|
||||
box.asFlow(
|
||||
box.query {
|
||||
orderDesc(HistoryEntity_.timeStamp)
|
||||
}
|
||||
).map {
|
||||
it.map { historyEntity ->
|
||||
historyEntity.zimFilePath?.let { filePath ->
|
||||
// set zimReaderSource for previously saved history items
|
||||
fromDatabaseValue(filePath)?.let { zimReaderSource ->
|
||||
historyEntity.zimReaderSource = zimReaderSource
|
||||
}
|
||||
}
|
||||
HistoryItem(historyEntity)
|
||||
}
|
||||
}
|
||||
|
||||
override fun pages(): Flow<List<Page>> = history()
|
||||
override fun deletePages(pagesToDelete: List<Page>) =
|
||||
deleteHistory(pagesToDelete as List<HistoryItem>)
|
||||
|
||||
fun saveHistory(historyItem: HistoryItem) {
|
||||
box.store.callInTx {
|
||||
box
|
||||
.query {
|
||||
equal(
|
||||
HistoryEntity_.historyUrl,
|
||||
historyItem.historyUrl,
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
).and()
|
||||
.equal(
|
||||
HistoryEntity_.dateString,
|
||||
historyItem.dateString,
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
}
|
||||
.remove()
|
||||
box.put(HistoryEntity(historyItem))
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteHistory(historyList: List<HistoryItem>) {
|
||||
box.remove(historyList.map(::HistoryEntity))
|
||||
}
|
||||
|
||||
fun deleteAllHistory() {
|
||||
box.removeAll()
|
||||
}
|
||||
}
|
@ -212,7 +212,6 @@ class LibkiwixBookOnDisk @Inject constructor(
|
||||
}
|
||||
}.onFailure { it.printStackTrace() }
|
||||
writeBookMarksAndSaveLibraryToFile()
|
||||
// TODO test when getting books it will not goes to circular dependencies mode.
|
||||
updateLocalBooksFlow()
|
||||
}
|
||||
|
||||
|
@ -1,159 +0,0 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2019 Kiwix <android.kiwix.org>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package org.kiwix.kiwixmobile.core.dao
|
||||
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.kotlin.inValues
|
||||
import io.objectbox.kotlin.query
|
||||
import io.objectbox.query.QueryBuilder
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.BookOnDiskEntity
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.BookOnDiskEntity_
|
||||
import org.kiwix.kiwixmobile.core.entity.LibkiwixBook
|
||||
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
|
||||
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.BooksOnDiskListItem.BookOnDisk
|
||||
import javax.inject.Inject
|
||||
|
||||
class NewBookDao @Inject constructor(private val box: Box<BookOnDiskEntity>) {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Suppress("Deprecation")
|
||||
fun books(dispatcher: CoroutineDispatcher = Dispatchers.IO) =
|
||||
box.asFlow()
|
||||
.mapLatest { booksList ->
|
||||
val updatedBooks = booksList.onEach { bookOnDiskEntity ->
|
||||
val file = bookOnDiskEntity.file
|
||||
val zimReaderSource = ZimReaderSource(file)
|
||||
try {
|
||||
if (zimReaderSource.canOpenInLibkiwix()) {
|
||||
bookOnDiskEntity.zimReaderSource = zimReaderSource
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
// Do nothing simply return the bookOnDiskEntity.
|
||||
}
|
||||
bookOnDiskEntity
|
||||
}
|
||||
removeBooksThatAreInTrashFolder(updatedBooks)
|
||||
removeBooksThatDoNotExist(updatedBooks.toMutableList())
|
||||
|
||||
updatedBooks.mapNotNull { book ->
|
||||
try {
|
||||
if (book.zimReaderSource.exists() &&
|
||||
!isInTrashFolder(book.zimReaderSource.toDatabase())
|
||||
) {
|
||||
book
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
.map { it.map(::BookOnDisk) }
|
||||
.flowOn(dispatcher)
|
||||
|
||||
@Suppress("Deprecation")
|
||||
suspend fun getBooks() =
|
||||
box.all.map { bookOnDiskEntity ->
|
||||
bookOnDiskEntity.file.let { file ->
|
||||
// set zimReaderSource for previously saved books
|
||||
val zimReaderSource = ZimReaderSource(file)
|
||||
if (zimReaderSource.canOpenInLibkiwix()) {
|
||||
bookOnDiskEntity.zimReaderSource = zimReaderSource
|
||||
}
|
||||
}
|
||||
BookOnDisk(bookOnDiskEntity)
|
||||
}
|
||||
|
||||
fun insert(booksOnDisk: List<BookOnDisk>) {
|
||||
box.store.callInTx {
|
||||
val uniqueBooks = uniqueBooksByFile(booksOnDisk)
|
||||
removeEntriesWithMatchingIds(uniqueBooks)
|
||||
box.put(uniqueBooks.distinctBy { it.book.id }.map(::BookOnDiskEntity))
|
||||
}
|
||||
}
|
||||
|
||||
private fun uniqueBooksByFile(booksOnDisk: List<BookOnDisk>): List<BookOnDisk> {
|
||||
val booksWithSameFilePath = booksWithSameFilePath(booksOnDisk)
|
||||
return booksOnDisk.filter { bookOnDisk: BookOnDisk ->
|
||||
booksWithSameFilePath.none { it.zimReaderSource == bookOnDisk.zimReaderSource }
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("Deprecation")
|
||||
private fun booksWithSameFilePath(booksOnDisk: List<BookOnDisk>) =
|
||||
box.query {
|
||||
inValues(
|
||||
BookOnDiskEntity_.zimReaderSource,
|
||||
booksOnDisk.map { it.zimReaderSource.toDatabase() }.toTypedArray(),
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
}.find()
|
||||
.map(::BookOnDisk)
|
||||
|
||||
private fun removeEntriesWithMatchingIds(uniqueBooks: List<BookOnDisk>) {
|
||||
box.query {
|
||||
inValues(
|
||||
BookOnDiskEntity_.bookId,
|
||||
uniqueBooks.map { it.book.id }.toTypedArray(),
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
}
|
||||
.remove()
|
||||
}
|
||||
|
||||
fun delete(databaseId: Long) {
|
||||
box.remove(databaseId)
|
||||
}
|
||||
|
||||
@Suppress("UnsafeCallOnNullableType")
|
||||
fun migrationInsert(books: List<LibkiwixBook>) {
|
||||
insert(books.map { BookOnDisk(book = it, zimReaderSource = ZimReaderSource(it.file!!)) })
|
||||
}
|
||||
|
||||
private suspend fun removeBooksThatDoNotExist(books: MutableList<BookOnDiskEntity>) {
|
||||
delete(books.filterNot { it.zimReaderSource.exists() })
|
||||
}
|
||||
|
||||
// Remove the existing books from database which are showing on the library screen.
|
||||
private fun removeBooksThatAreInTrashFolder(books: List<BookOnDiskEntity>) {
|
||||
delete(books.filter { isInTrashFolder(it.zimReaderSource.toDatabase()) })
|
||||
}
|
||||
|
||||
// Check if any existing ZIM file showing on the library screen which is inside the trash folder.
|
||||
private fun isInTrashFolder(filePath: String) =
|
||||
Regex("/\\.Trash/").containsMatchIn(filePath)
|
||||
|
||||
private fun delete(books: List<BookOnDiskEntity>) {
|
||||
box.remove(books)
|
||||
}
|
||||
|
||||
fun bookMatching(downloadTitle: String) =
|
||||
box.query {
|
||||
endsWith(
|
||||
BookOnDiskEntity_.zimReaderSource,
|
||||
downloadTitle,
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
}.findFirst()
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2019 Kiwix <android.kiwix.org>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package org.kiwix.kiwixmobile.core.dao
|
||||
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.kotlin.query
|
||||
import io.objectbox.query.QueryBuilder
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity_
|
||||
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
||||
import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem
|
||||
import org.kiwix.kiwixmobile.core.reader.ZimFileReader
|
||||
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource.Companion.fromDatabaseValue
|
||||
import javax.inject.Inject
|
||||
|
||||
class NewBookmarksDao @Inject constructor(val box: Box<BookmarkEntity>) : PageDao {
|
||||
fun bookmarks(): Flow<List<Page>> =
|
||||
box.asFlow(
|
||||
box.query {
|
||||
order(BookmarkEntity_.bookmarkTitle)
|
||||
}
|
||||
).map {
|
||||
it.map { bookmarkEntity ->
|
||||
bookmarkEntity.zimFilePath?.let { filePath ->
|
||||
// set zimReaderSource for previously saved bookmarks
|
||||
fromDatabaseValue(filePath)?.let { zimReaderSource ->
|
||||
bookmarkEntity.zimReaderSource = zimReaderSource
|
||||
}
|
||||
}
|
||||
BookmarkItem(bookmarkEntity)
|
||||
}
|
||||
}
|
||||
|
||||
override fun pages(): Flow<List<Page>> = bookmarks()
|
||||
override fun deletePages(pagesToDelete: List<Page>) =
|
||||
deleteBookmarks(pagesToDelete as List<BookmarkItem>)
|
||||
|
||||
fun getCurrentZimBookmarksUrl(zimFileReader: ZimFileReader?) =
|
||||
box.query {
|
||||
equal(
|
||||
BookmarkEntity_.zimId,
|
||||
zimFileReader?.id.orEmpty(),
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
.or()
|
||||
.equal(
|
||||
BookmarkEntity_.zimName,
|
||||
zimFileReader?.name.orEmpty(),
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
order(BookmarkEntity_.bookmarkTitle)
|
||||
}.property(BookmarkEntity_.bookmarkUrl)
|
||||
.findStrings()
|
||||
.toList()
|
||||
.distinct()
|
||||
|
||||
fun bookmarkUrlsForCurrentBook(
|
||||
zimFileReader: ZimFileReader?,
|
||||
dispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||
): Flow<List<String>> =
|
||||
box.asFlow(
|
||||
box.query {
|
||||
equal(
|
||||
BookmarkEntity_.zimId,
|
||||
zimFileReader?.id.orEmpty(),
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
.or()
|
||||
.equal(
|
||||
BookmarkEntity_.zimName,
|
||||
zimFileReader?.name.orEmpty(),
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
order(BookmarkEntity_.bookmarkTitle)
|
||||
}
|
||||
).map { it.map(BookmarkEntity::bookmarkUrl) }
|
||||
.flowOn(dispatcher)
|
||||
|
||||
fun saveBookmark(bookmarkItem: BookmarkItem) {
|
||||
box.put(BookmarkEntity(bookmarkItem))
|
||||
}
|
||||
|
||||
fun deleteBookmarks(bookmarks: List<BookmarkItem>) {
|
||||
box.remove(bookmarks.map(::BookmarkEntity))
|
||||
}
|
||||
|
||||
fun deleteBookmark(bookmarkUrl: String) {
|
||||
box.query {
|
||||
equal(
|
||||
BookmarkEntity_.bookmarkUrl,
|
||||
bookmarkUrl,
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
}.remove()
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2019 Kiwix <android.kiwix.org>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package org.kiwix.kiwixmobile.core.dao
|
||||
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.kotlin.flow
|
||||
import io.objectbox.kotlin.query
|
||||
import io.objectbox.query.Query
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.LanguageEntity
|
||||
import org.kiwix.kiwixmobile.core.zim_manager.Language
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class NewLanguagesDao @Inject constructor(private val box: Box<LanguageEntity>) {
|
||||
fun languages() =
|
||||
box.asFlow()
|
||||
.map { it.map(LanguageEntity::toLanguageModel) }
|
||||
|
||||
fun insert(languages: List<Language>) {
|
||||
box.store.callInTx {
|
||||
box.removeAll()
|
||||
box.put(languages.map(::LanguageEntity))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun <T> Box<T>.asFlow(query: Query<T> = query {}): Flow<List<T>> {
|
||||
return query.flow()
|
||||
.map { it.toList() }
|
||||
.distinctUntilChanged()
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2020 Kiwix <android.kiwix.org>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.kiwix.kiwixmobile.core.dao
|
||||
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.kotlin.query
|
||||
import io.objectbox.query.QueryBuilder
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.NotesEntity
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.NotesEntity_
|
||||
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
||||
import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem
|
||||
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource.Companion.fromDatabaseValue
|
||||
import javax.inject.Inject
|
||||
|
||||
class NewNoteDao @Inject constructor(val box: Box<NotesEntity>) : PageDao {
|
||||
fun notes(): Flow<List<Page>> =
|
||||
box.asFlow(
|
||||
box.query {
|
||||
order(NotesEntity_.noteTitle)
|
||||
}
|
||||
).map {
|
||||
it.map { notesEntity ->
|
||||
notesEntity.zimFilePath?.let { filePath ->
|
||||
// set zimReaderSource for previously saved notes
|
||||
fromDatabaseValue(filePath)?.let { zimReaderSource ->
|
||||
notesEntity.zimReaderSource = zimReaderSource
|
||||
}
|
||||
}
|
||||
NoteListItem(notesEntity)
|
||||
}
|
||||
}
|
||||
|
||||
override fun pages(): Flow<List<Page>> = notes()
|
||||
|
||||
override fun deletePages(pagesToDelete: List<Page>) =
|
||||
deleteNotes(pagesToDelete as List<NoteListItem>)
|
||||
|
||||
fun saveNote(noteItem: NoteListItem) {
|
||||
box.store.callInTx {
|
||||
if (doesNotAlreadyExist(noteItem)) {
|
||||
box.put(NotesEntity(noteItem))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteNotes(noteList: List<NoteListItem>) {
|
||||
box.remove(noteList.map(::NotesEntity))
|
||||
}
|
||||
|
||||
fun deleteNote(noteUniqueKey: String) {
|
||||
box.query {
|
||||
equal(
|
||||
NotesEntity_.noteTitle,
|
||||
noteUniqueKey,
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
}.remove()
|
||||
}
|
||||
|
||||
private fun doesNotAlreadyExist(noteItem: NoteListItem) =
|
||||
box.query {
|
||||
equal(NotesEntity_.noteTitle, noteItem.title, QueryBuilder.StringOrder.CASE_INSENSITIVE)
|
||||
}.count() == 0L
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2019 Kiwix <android.kiwix.org>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package org.kiwix.kiwixmobile.core.dao
|
||||
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.kotlin.query
|
||||
import io.objectbox.query.QueryBuilder
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.RecentSearchEntity
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.RecentSearchEntity_
|
||||
import org.kiwix.kiwixmobile.core.search.SearchListItem.RecentSearchListItem
|
||||
import javax.inject.Inject
|
||||
|
||||
class NewRecentSearchDao @Inject constructor(
|
||||
private val box: Box<RecentSearchEntity>,
|
||||
private val flowBuilder: FlowBuilder
|
||||
) {
|
||||
fun recentSearches(zimId: String?) =
|
||||
flowBuilder.buildCallbackFlow(
|
||||
box.query {
|
||||
equal(
|
||||
RecentSearchEntity_.zimId,
|
||||
zimId.orEmpty(),
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
orderDesc(RecentSearchEntity_.id)
|
||||
}
|
||||
).map { searchEntities ->
|
||||
searchEntities.distinctBy(RecentSearchEntity::searchTerm)
|
||||
.take(NUM_RECENT_RESULTS)
|
||||
.map { searchEntity -> RecentSearchListItem(searchEntity.searchTerm, searchEntity.url) }
|
||||
}
|
||||
|
||||
fun saveSearch(title: String, id: String, url: String?) {
|
||||
box.put(RecentSearchEntity(searchTerm = title, zimId = id, url = url))
|
||||
}
|
||||
|
||||
fun deleteSearchString(searchTerm: String) {
|
||||
box
|
||||
.query {
|
||||
equal(
|
||||
RecentSearchEntity_.searchTerm,
|
||||
searchTerm,
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
}
|
||||
.remove()
|
||||
}
|
||||
|
||||
fun deleteSearchHistory() {
|
||||
box.removeAll()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val NUM_RECENT_RESULTS = 100
|
||||
}
|
||||
}
|
@ -20,11 +20,11 @@ package org.kiwix.kiwixmobile.core.dao.entities
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.TypeConverter
|
||||
import androidx.room.TypeConverters
|
||||
import com.tonyodev.fetch2.Download
|
||||
import com.tonyodev.fetch2.Error
|
||||
import com.tonyodev.fetch2.Status
|
||||
import io.objectbox.annotation.Convert
|
||||
import io.objectbox.converter.PropertyConverter
|
||||
import org.kiwix.kiwixmobile.core.entity.LibkiwixBook
|
||||
|
||||
@Entity
|
||||
@ -36,9 +36,9 @@ data class DownloadRoomEntity(
|
||||
val etaInMilliSeconds: Long = -1L,
|
||||
val bytesDownloaded: Long = -1L,
|
||||
val totalSizeOfDownload: Long = -1L,
|
||||
@Convert(converter = StatusConverter::class, dbType = Int::class)
|
||||
@TypeConverters(StatusConverter::class)
|
||||
val status: Status = Status.NONE,
|
||||
@Convert(converter = ErrorConverter::class, dbType = Int::class)
|
||||
@TypeConverters(ErrorConverter::class)
|
||||
val error: Error = Error.NONE,
|
||||
val progress: Int = -1,
|
||||
val bookId: String,
|
||||
@ -104,14 +104,18 @@ data class DownloadRoomEntity(
|
||||
)
|
||||
}
|
||||
|
||||
class StatusConverter : EnumConverter<Status>() {
|
||||
override fun convertToEntityProperty(databaseValue: Int) = Status.valueOf(databaseValue)
|
||||
class StatusConverter {
|
||||
@TypeConverter
|
||||
fun convertToEntityProperty(databaseValue: Int): Status = Status.valueOf(databaseValue)
|
||||
|
||||
@TypeConverter
|
||||
fun convertToDatabaseValue(status: Status): Int = status.ordinal
|
||||
}
|
||||
|
||||
class ErrorConverter : EnumConverter<Error>() {
|
||||
override fun convertToEntityProperty(databaseValue: Int) = Error.valueOf(databaseValue)
|
||||
}
|
||||
class ErrorConverter {
|
||||
@TypeConverter
|
||||
fun convertToEntityProperty(databaseValue: Int) = Error.valueOf(databaseValue)
|
||||
|
||||
abstract class EnumConverter<E : Enum<E>> : PropertyConverter<E, Int> {
|
||||
override fun convertToDatabaseValue(entityProperty: E): Int = entityProperty.ordinal
|
||||
@TypeConverter
|
||||
fun convertToDatabaseValue(error: Error): Int = error.ordinal
|
||||
}
|
||||
|
@ -33,9 +33,11 @@ import org.kiwix.kiwixmobile.core.dao.RecentSearchRoomDao
|
||||
import org.kiwix.kiwixmobile.core.dao.WebViewHistoryRoomDao
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.BundleRoomConverter
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.DownloadRoomEntity
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.ErrorConverter
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.HistoryRoomEntity
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.NotesRoomEntity
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.RecentSearchRoomEntity
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.StatusConverter
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.WebViewHistoryEntity
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.ZimSourceRoomConverter
|
||||
|
||||
@ -48,13 +50,15 @@ import org.kiwix.kiwixmobile.core.dao.entities.ZimSourceRoomConverter
|
||||
DownloadRoomEntity::class,
|
||||
WebViewHistoryEntity::class
|
||||
],
|
||||
version = 8,
|
||||
version = 9,
|
||||
exportSchema = false
|
||||
)
|
||||
@TypeConverters(
|
||||
HistoryRoomDaoCoverts::class,
|
||||
ZimSourceRoomConverter::class,
|
||||
BundleRoomConverter::class
|
||||
BundleRoomConverter::class,
|
||||
StatusConverter::class,
|
||||
ErrorConverter::class
|
||||
)
|
||||
abstract class KiwixRoomDatabase : RoomDatabase() {
|
||||
abstract fun recentSearchRoomDao(): RecentSearchRoomDao
|
||||
@ -78,7 +82,8 @@ abstract class KiwixRoomDatabase : RoomDatabase() {
|
||||
MIGRATION_4_5,
|
||||
MIGRATION_5_6,
|
||||
MIGRATION_6_7,
|
||||
MIGRATION_7_8
|
||||
MIGRATION_7_8,
|
||||
MIGRATION_8_9
|
||||
)
|
||||
.build().also { db = it }
|
||||
}
|
||||
@ -305,6 +310,60 @@ abstract class KiwixRoomDatabase : RoomDatabase() {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private val MIGRATION_8_9 =
|
||||
object : Migration(8, 9) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS DownloadRoomEntity_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
downloadId INTEGER NOT NULL,
|
||||
file TEXT,
|
||||
etaInMilliSeconds INTEGER NOT NULL DEFAULT -1,
|
||||
bytesDownloaded INTEGER NOT NULL DEFAULT -1,
|
||||
totalSizeOfDownload INTEGER NOT NULL DEFAULT -1,
|
||||
status INTEGER NOT NULL DEFAULT 0,
|
||||
error INTEGER NOT NULL DEFAULT 0,
|
||||
progress INTEGER NOT NULL DEFAULT -1,
|
||||
bookId TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
language TEXT NOT NULL,
|
||||
creator TEXT NOT NULL,
|
||||
publisher TEXT NOT NULL,
|
||||
date TEXT NOT NULL,
|
||||
url TEXT,
|
||||
articleCount TEXT,
|
||||
mediaCount TEXT,
|
||||
size TEXT NOT NULL,
|
||||
name TEXT,
|
||||
favIcon TEXT NOT NULL,
|
||||
tags TEXT
|
||||
)
|
||||
""".trimIndent()
|
||||
)
|
||||
db.execSQL(
|
||||
"""
|
||||
INSERT INTO DownloadRoomEntity_new (
|
||||
id, downloadId, file, etaInMilliSeconds, bytesDownloaded,
|
||||
totalSizeOfDownload, status, error, progress, bookId, title,
|
||||
description, language, creator, publisher, date, url,
|
||||
articleCount, mediaCount, size, name, favIcon, tags
|
||||
)
|
||||
SELECT id, downloadId, file, etaInMilliSeconds, bytesDownloaded,
|
||||
totalSizeOfDownload, status, error, progress, bookId, title, description,
|
||||
language, creator, publisher, date, url, articleCount,
|
||||
mediaCount, size, name, favIcon, tags
|
||||
FROM DownloadRoomEntity
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
db.execSQL("DROP TABLE DownloadRoomEntity")
|
||||
db.execSQL("ALTER TABLE DownloadRoomEntity_new RENAME TO DownloadRoomEntity")
|
||||
}
|
||||
}
|
||||
|
||||
fun destroyInstance() {
|
||||
db = null
|
||||
}
|
||||
|
@ -28,15 +28,9 @@ import org.kiwix.kiwixmobile.core.CoreApp
|
||||
import org.kiwix.kiwixmobile.core.LibkiwixBookFactory
|
||||
import org.kiwix.kiwixmobile.core.StorageObserver
|
||||
import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao
|
||||
import org.kiwix.kiwixmobile.core.dao.HistoryDao
|
||||
import org.kiwix.kiwixmobile.core.dao.HistoryRoomDao
|
||||
import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk
|
||||
import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks
|
||||
import org.kiwix.kiwixmobile.core.dao.NewBookDao
|
||||
import org.kiwix.kiwixmobile.core.dao.NewBookmarksDao
|
||||
import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao
|
||||
import org.kiwix.kiwixmobile.core.dao.NewNoteDao
|
||||
import org.kiwix.kiwixmobile.core.dao.NewRecentSearchDao
|
||||
import org.kiwix.kiwixmobile.core.dao.NotesRoomDao
|
||||
import org.kiwix.kiwixmobile.core.dao.RecentSearchRoomDao
|
||||
import org.kiwix.kiwixmobile.core.dao.WebViewHistoryRoomDao
|
||||
@ -92,13 +86,7 @@ interface CoreComponent {
|
||||
fun application(): Application
|
||||
fun bookUtils(): BookUtils
|
||||
fun dataSource(): DataSource
|
||||
fun newBookDao(): NewBookDao
|
||||
fun historyDao(): HistoryDao
|
||||
fun noteDao(): NewNoteDao
|
||||
fun newLanguagesDao(): NewLanguagesDao
|
||||
fun recentSearchDao(): NewRecentSearchDao
|
||||
fun downloadRoomDao(): DownloadRoomDao
|
||||
fun newBookmarksDao(): NewBookmarksDao
|
||||
fun connectivityManager(): ConnectivityManager
|
||||
fun objectBoxToLibkiwixMigrator(): ObjectBoxToLibkiwixMigrator
|
||||
fun libkiwixBookmarks(): LibkiwixBookmarks
|
||||
|
@ -21,15 +21,7 @@ import android.content.Context
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.objectbox.BoxStore
|
||||
import io.objectbox.kotlin.boxFor
|
||||
import org.kiwix.kiwixmobile.core.dao.FlowBuilder
|
||||
import org.kiwix.kiwixmobile.core.dao.HistoryDao
|
||||
import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk
|
||||
import org.kiwix.kiwixmobile.core.dao.NewBookDao
|
||||
import org.kiwix.kiwixmobile.core.dao.NewBookmarksDao
|
||||
import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao
|
||||
import org.kiwix.kiwixmobile.core.dao.NewNoteDao
|
||||
import org.kiwix.kiwixmobile.core.dao.NewRecentSearchDao
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.MyObjectBox
|
||||
import org.kiwix.kiwixmobile.core.data.KiwixRoomDatabase
|
||||
import javax.inject.Singleton
|
||||
@ -49,26 +41,6 @@ open class DatabaseModule {
|
||||
return boxStore!!
|
||||
}
|
||||
|
||||
@Provides @Singleton fun providesNewBookDao(boxStore: BoxStore): NewBookDao =
|
||||
NewBookDao(boxStore.boxFor())
|
||||
|
||||
@Provides @Singleton fun providesNewLanguagesDao(boxStore: BoxStore): NewLanguagesDao =
|
||||
NewLanguagesDao(boxStore.boxFor())
|
||||
|
||||
@Provides @Singleton fun providesNewHistoryDao(boxStore: BoxStore): HistoryDao =
|
||||
HistoryDao(boxStore.boxFor())
|
||||
|
||||
@Provides @Singleton fun providesNewBookmarksDao(boxStore: BoxStore): NewBookmarksDao =
|
||||
NewBookmarksDao(boxStore.boxFor())
|
||||
|
||||
@Provides @Singleton fun providesNewNoteDao(boxStore: BoxStore): NewNoteDao =
|
||||
NewNoteDao(boxStore.boxFor())
|
||||
|
||||
@Provides @Singleton fun providesNewRecentSearchDao(
|
||||
boxStore: BoxStore,
|
||||
flowBuilder: FlowBuilder
|
||||
): NewRecentSearchDao = NewRecentSearchDao(boxStore.boxFor(), flowBuilder)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideYourDatabase(
|
||||
|
@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2021 Kiwix <android.kiwix.org>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.kiwix.kiwixmobile.core.dao
|
||||
|
||||
import io.mockk.CapturingSlot
|
||||
import io.mockk.clearAllMocks
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.slot
|
||||
import io.mockk.verify
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.query.Query
|
||||
import io.objectbox.query.QueryBuilder
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.HistoryEntity
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.HistoryEntity_
|
||||
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
||||
import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem
|
||||
import org.kiwix.kiwixmobile.core.page.historyItem
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
internal class HistoryDaoTest {
|
||||
private val box: Box<HistoryEntity> = mockk(relaxed = true)
|
||||
private val historyDao = HistoryDao(box)
|
||||
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
clearAllMocks()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deletePages() {
|
||||
val historyItem: HistoryListItem.HistoryItem = historyItem(zimReaderSource = mockk())
|
||||
val historyItemList: List<HistoryListItem.HistoryItem> = listOf(historyItem)
|
||||
val pagesToDelete: List<Page> = historyItemList
|
||||
historyDao.deletePages(pagesToDelete)
|
||||
verify { historyDao.deleteHistory(historyItemList) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun saveHistory() {
|
||||
val historyItem: HistoryListItem.HistoryItem =
|
||||
historyItem(
|
||||
historyUrl = "",
|
||||
dateString = "",
|
||||
zimReaderSource = mockk()
|
||||
)
|
||||
val slot: CapturingSlot<Callable<Unit>> = slot()
|
||||
every { box.store.callInTx(capture(slot)) } returns Unit
|
||||
val queryBuilder: QueryBuilder<HistoryEntity> = mockk()
|
||||
every { box.query() } returns queryBuilder
|
||||
every {
|
||||
queryBuilder.equal(HistoryEntity_.historyUrl, "", QueryBuilder.StringOrder.CASE_INSENSITIVE)
|
||||
} returns queryBuilder
|
||||
every { queryBuilder.and() } returns queryBuilder
|
||||
every {
|
||||
queryBuilder.equal(
|
||||
HistoryEntity_.dateString,
|
||||
"",
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
} returns queryBuilder
|
||||
val query: Query<HistoryEntity> = mockk(relaxed = true)
|
||||
every { queryBuilder.build() } returns query
|
||||
historyDao.saveHistory(historyItem)
|
||||
slot.captured.call()
|
||||
verify { query.remove() }
|
||||
verify { box.put(HistoryEntity(historyItem)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteAllHistory() {
|
||||
historyDao.deleteAllHistory()
|
||||
verify { box.removeAll() }
|
||||
}
|
||||
}
|
@ -1,295 +0,0 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2020 Kiwix <android.kiwix.org>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.kiwix.kiwixmobile.core.dao
|
||||
|
||||
import io.mockk.CapturingSlot
|
||||
import io.mockk.clearAllMocks
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.slot
|
||||
import io.mockk.verify
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.query.Query
|
||||
import io.objectbox.query.QueryBuilder
|
||||
import io.objectbox.reactive.SubscriptionBuilder
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.BookOnDiskEntity
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.BookOnDiskEntity_
|
||||
import org.kiwix.kiwixmobile.core.entity.LibkiwixBook
|
||||
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
|
||||
import org.kiwix.kiwixmobile.core.utils.files.testFlow
|
||||
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.BooksOnDiskListItem.BookOnDisk
|
||||
import org.kiwix.sharedFunctions.bookOnDisk
|
||||
import org.kiwix.sharedFunctions.bookOnDiskEntity
|
||||
import org.kiwix.sharedFunctions.libkiwixBook
|
||||
import java.io.File
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
const val MOCKK_TIMEOUT_FOR_VERIFICATION = 1000L
|
||||
|
||||
internal class NewBookDaoTest {
|
||||
private val box: Box<BookOnDiskEntity> = mockk(relaxed = true)
|
||||
private val newBookDao = NewBookDao(box)
|
||||
|
||||
@BeforeEach
|
||||
internal fun setUp() {
|
||||
clearAllMocks()
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class BooksTests {
|
||||
@Test
|
||||
fun `books emits entities whose file exists`() = flakyTest {
|
||||
runTest {
|
||||
val (expectedEntity, _) = expectEmissionOfExistingAndNotExistingBook()
|
||||
testFlow(
|
||||
flow = newBookDao.books(),
|
||||
triggerAction = {},
|
||||
assert = { assertThat(awaitItem()).contains(BookOnDisk(expectedEntity)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `books deletes entities whose file does not exist`() = flakyTest {
|
||||
runTest {
|
||||
val (_, deletedEntity) = expectEmissionOfExistingAndNotExistingBook()
|
||||
testFlow(
|
||||
flow = newBookDao.books(),
|
||||
triggerAction = {},
|
||||
assert = {
|
||||
coVerify(timeout = MOCKK_TIMEOUT_FOR_VERIFICATION) {
|
||||
box.remove(listOf(deletedEntity))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `books removes entities whose files are in the trash folder`() = flakyTest {
|
||||
runTest {
|
||||
val (_, _) = expectEmissionOfExistingAndNotExistingBook(true)
|
||||
testFlow(
|
||||
flow = newBookDao.books(),
|
||||
triggerAction = {},
|
||||
assert = { Assertions.assertEquals(emptyList<BookOnDisk>(), awaitItem()) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private fun expectEmissionOfExistingAndNotExistingBook(
|
||||
isInTrashFolder: Boolean = false
|
||||
): Pair<BookOnDiskEntity, BookOnDiskEntity> {
|
||||
val query: Query<BookOnDiskEntity> = mockk()
|
||||
val subscriptionBuilder: SubscriptionBuilder<MutableList<BookOnDiskEntity>> =
|
||||
mockk(relaxed = true)
|
||||
every { box.query().build() } returns query
|
||||
every { query.subscribe() } returns subscriptionBuilder
|
||||
val zimReaderSourceThatExists = mockk<ZimReaderSource>()
|
||||
val zimReaderSourceThatDoesNotExist = mockk<ZimReaderSource>()
|
||||
coEvery { zimReaderSourceThatExists.exists() } returns true
|
||||
coEvery { zimReaderSourceThatDoesNotExist.exists() } returns false
|
||||
every {
|
||||
zimReaderSourceThatExists.toDatabase()
|
||||
} returns if (isInTrashFolder) "/.Trash/test.zim" else ""
|
||||
every { zimReaderSourceThatDoesNotExist.toDatabase() } returns ""
|
||||
val entityThatExists = bookOnDiskEntity(zimReaderSource = zimReaderSourceThatExists)
|
||||
val entityThatDoesNotExist =
|
||||
bookOnDiskEntity(zimReaderSource = zimReaderSourceThatDoesNotExist)
|
||||
mockkStatic(Query::class)
|
||||
mockBoxAsFlow(box, mutableListOf(entityThatExists, entityThatDoesNotExist))
|
||||
return entityThatExists to entityThatDoesNotExist
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getBooks() =
|
||||
runTest {
|
||||
val entity = bookOnDiskEntity()
|
||||
every { box.all } returns mutableListOf(entity)
|
||||
assertThat(newBookDao.getBooks()).isEqualTo(listOf(BookOnDisk(entity)))
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class Insertion {
|
||||
@Test
|
||||
fun `insert transaction adds books to the box that have distinct ids`() {
|
||||
val slot: CapturingSlot<Callable<Unit>> = slot()
|
||||
every { box.store.callInTx(capture(slot)) } returns Unit
|
||||
val distinctBook: BookOnDisk = bookOnDisk(databaseId = 0, book = libkiwixBook(id = "same"))
|
||||
newBookDao.insert(
|
||||
listOf(distinctBook, bookOnDisk(databaseId = 1, book = libkiwixBook(id = "same")))
|
||||
)
|
||||
val queryBuilder: QueryBuilder<BookOnDiskEntity> = mockk(relaxed = true)
|
||||
every { box.query() } returns queryBuilder
|
||||
every {
|
||||
queryBuilder.`in`(
|
||||
BookOnDiskEntity_.zimReaderSource,
|
||||
arrayOf(distinctBook.zimReaderSource.toDatabase()),
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
} returns queryBuilder
|
||||
val query: Query<BookOnDiskEntity> = mockk(relaxed = true)
|
||||
every { queryBuilder.build() } returns query
|
||||
every {
|
||||
query.find()
|
||||
} returns listOf(bookOnDiskEntity(zimReaderSource = ZimReaderSource(File("matches_nothing"))))
|
||||
slot.captured.call()
|
||||
verify { box.put(listOf(BookOnDiskEntity(distinctBook))) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `insert transaction does not add books if a book with the same path exists in the box`() {
|
||||
val slot: CapturingSlot<Callable<Unit>> = slot()
|
||||
every { box.store.callInTx(capture(slot)) } returns Unit
|
||||
val distinctBook: BookOnDisk = bookOnDisk()
|
||||
newBookDao.insert(listOf(distinctBook))
|
||||
val queryBuilder: QueryBuilder<BookOnDiskEntity> = mockk(relaxed = true)
|
||||
every { box.query() } returns queryBuilder
|
||||
every {
|
||||
queryBuilder.`in`(
|
||||
BookOnDiskEntity_.zimReaderSource,
|
||||
arrayOf(distinctBook.zimReaderSource.toDatabase()),
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
} returns queryBuilder
|
||||
val query: Query<BookOnDiskEntity> = mockk(relaxed = true)
|
||||
every { queryBuilder.build() } returns query
|
||||
every {
|
||||
query.find()
|
||||
} returns listOf(bookOnDiskEntity(zimReaderSource = distinctBook.zimReaderSource))
|
||||
slot.captured.call()
|
||||
verify { box.put(listOf()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `insert transaction removes books with duplicate ids`() {
|
||||
val slot: CapturingSlot<Callable<Unit>> = slot()
|
||||
every { box.store.callInTx(capture(slot)) } returns Unit
|
||||
val distinctBook: BookOnDisk = bookOnDisk()
|
||||
newBookDao.insert(listOf(distinctBook))
|
||||
val queryBuilder: QueryBuilder<BookOnDiskEntity> = mockk()
|
||||
every { box.query() } returns queryBuilder
|
||||
every {
|
||||
queryBuilder.`in`(
|
||||
BookOnDiskEntity_.zimReaderSource,
|
||||
arrayOf(distinctBook.zimReaderSource.toDatabase()),
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
} returns queryBuilder
|
||||
val query: Query<BookOnDiskEntity> = mockk()
|
||||
every { queryBuilder.build() } returns query
|
||||
every { query.find() } returns listOf()
|
||||
every {
|
||||
queryBuilder.`in`(
|
||||
BookOnDiskEntity_.bookId,
|
||||
arrayOf(distinctBook.book.id),
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
} returns queryBuilder
|
||||
every { query.remove() } returns 0L
|
||||
slot.captured.call()
|
||||
verify { box.put(listOf(BookOnDiskEntity(distinctBook))) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun delete() {
|
||||
newBookDao.delete(0L)
|
||||
verify { box.remove(0L) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun migrationInsert() {
|
||||
val book: LibkiwixBook = libkiwixBook()
|
||||
val slot: CapturingSlot<Callable<Unit>> = slot()
|
||||
every { box.store.callInTx(capture(slot)) } returns Unit
|
||||
newBookDao.migrationInsert(listOf(book))
|
||||
slot.captured.call()
|
||||
verify {
|
||||
box.put(
|
||||
listOf(
|
||||
BookOnDiskEntity(
|
||||
BookOnDisk(
|
||||
book = book,
|
||||
zimReaderSource = ZimReaderSource(book.file!!)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `bookMatching queries file by title`() {
|
||||
val downloadTitle = "title"
|
||||
val queryBuilder: QueryBuilder<BookOnDiskEntity> = mockk()
|
||||
every { box.query() } returns queryBuilder
|
||||
every {
|
||||
queryBuilder.endsWith(
|
||||
BookOnDiskEntity_.zimReaderSource,
|
||||
downloadTitle,
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
} returns queryBuilder
|
||||
val query: Query<BookOnDiskEntity> = mockk()
|
||||
every { queryBuilder.build() } returns query
|
||||
val bookOnDiskEntity: BookOnDiskEntity = bookOnDiskEntity()
|
||||
every { query.findFirst() } returns bookOnDiskEntity
|
||||
assertThat(newBookDao.bookMatching(downloadTitle)).isEqualTo(bookOnDiskEntity)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> mockBoxAsFlow(box: Box<T>, result: List<T>) {
|
||||
mockkStatic("org.kiwix.kiwixmobile.core.dao.NewLanguagesDaoKt")
|
||||
every { box.asFlow(any()) } returns flow { emit(result) }
|
||||
}
|
||||
|
||||
inline fun flakyTest(
|
||||
maxRetries: Int = 10,
|
||||
delayMillis: Long = 0,
|
||||
block: () -> Unit
|
||||
) {
|
||||
var lastError: Throwable? = null
|
||||
|
||||
repeat(maxRetries) { attempt ->
|
||||
try {
|
||||
block()
|
||||
return
|
||||
} catch (e: Throwable) {
|
||||
lastError = e
|
||||
println("Test attempt ${attempt + 1} failed: ${e.message}")
|
||||
if (delayMillis > 0) Thread.sleep(delayMillis)
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError ?: AssertionError("Test failed after $maxRetries attempts")
|
||||
}
|
@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2021 Kiwix <android.kiwix.org>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.kiwix.kiwixmobile.core.dao
|
||||
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.query.Query
|
||||
import io.objectbox.query.QueryBuilder
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity_
|
||||
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
||||
import org.kiwix.kiwixmobile.core.page.bookmark
|
||||
import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem
|
||||
import org.kiwix.kiwixmobile.core.reader.ZimFileReader
|
||||
|
||||
internal class NewBookmarksDaoTest {
|
||||
private val box: Box<BookmarkEntity> = mockk(relaxed = true)
|
||||
private val newBookmarksDao = NewBookmarksDao(box)
|
||||
|
||||
@Test
|
||||
fun deletePages() {
|
||||
val bookmarkItem: BookmarkItem = bookmark(zimReaderSource = mockk())
|
||||
val bookmarkItemList: List<BookmarkItem> = listOf(bookmarkItem)
|
||||
val pagesToDelete: List<Page> = bookmarkItemList
|
||||
newBookmarksDao.deletePages(pagesToDelete)
|
||||
verify { newBookmarksDao.deleteBookmarks(bookmarkItemList) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getCurrentZimBookmarksUrl() {
|
||||
val bookmarkItem: BookmarkItem = mockk(relaxed = true)
|
||||
val zimFileReader: ZimFileReader? = mockk(relaxed = true)
|
||||
val queryBuilder: QueryBuilder<BookmarkEntity> = mockk()
|
||||
every { box.query() } returns queryBuilder
|
||||
every {
|
||||
queryBuilder.equal(
|
||||
BookmarkEntity_.zimId, "", QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
} returns queryBuilder
|
||||
every { queryBuilder.or() } returns queryBuilder
|
||||
every {
|
||||
queryBuilder.equal(
|
||||
BookmarkEntity_.zimName, "", QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
} returns queryBuilder
|
||||
every { queryBuilder.order(BookmarkEntity_.bookmarkTitle) } returns queryBuilder
|
||||
val query: Query<BookmarkEntity> = mockk(relaxed = true)
|
||||
every { queryBuilder.build() } returns query
|
||||
every { bookmarkItem.zimId } returns ""
|
||||
every { bookmarkItem.zimName } returns ""
|
||||
every { bookmarkItem.databaseId } returns 0L
|
||||
newBookmarksDao.getCurrentZimBookmarksUrl(zimFileReader)
|
||||
every {
|
||||
query.property(BookmarkEntity_.bookmarkUrl).findStrings().toList().distinct()
|
||||
} returns listOf("")
|
||||
verify { box.query() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bookmarkUrlsForCurrentBook() {
|
||||
val bookmarkItem: BookmarkItem = mockk(relaxed = true)
|
||||
val zimFileReader: ZimFileReader? = mockk(relaxed = true)
|
||||
val queryBuilder: QueryBuilder<BookmarkEntity> = mockk()
|
||||
every { box.query() } returns queryBuilder
|
||||
every {
|
||||
queryBuilder.equal(
|
||||
BookmarkEntity_.zimId,
|
||||
zimFileReader?.id ?: "", QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
} returns queryBuilder
|
||||
every { queryBuilder.or() } returns queryBuilder
|
||||
every {
|
||||
queryBuilder.equal(
|
||||
BookmarkEntity_.zimName,
|
||||
zimFileReader?.name ?: "", QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
} returns queryBuilder
|
||||
every { queryBuilder.order(BookmarkEntity_.bookmarkTitle) } returns queryBuilder
|
||||
val query: Query<BookmarkEntity> = mockk(relaxed = true)
|
||||
every { queryBuilder.build() } returns query
|
||||
every { bookmarkItem.zimId } returns ""
|
||||
every { bookmarkItem.zimName } returns ""
|
||||
every { bookmarkItem.databaseId } returns 0L
|
||||
newBookmarksDao.bookmarkUrlsForCurrentBook(zimFileReader)
|
||||
verify { box.query() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun saveBookmark() {
|
||||
val bookmarkItem: BookmarkItem = bookmark(zimReaderSource = mockk())
|
||||
newBookmarksDao.saveBookmark(bookmarkItem)
|
||||
verify { box.put(BookmarkEntity(bookmarkItem)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteBookmark() {
|
||||
val bookmarkUrl = "bookmarkUrl"
|
||||
val queryBuilder: QueryBuilder<BookmarkEntity> = mockk(relaxed = true)
|
||||
every { box.query() } returns queryBuilder
|
||||
every {
|
||||
queryBuilder.equal(
|
||||
BookmarkEntity_.bookmarkUrl,
|
||||
bookmarkUrl,
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
} returns queryBuilder
|
||||
val query: Query<BookmarkEntity> = mockk(relaxed = true)
|
||||
every { queryBuilder.build() } returns query
|
||||
newBookmarksDao.deleteBookmark(bookmarkUrl)
|
||||
verify { query.remove() }
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2021 Kiwix <android.kiwix.org>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.kiwix.kiwixmobile.core.dao
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import io.mockk.CapturingSlot
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.slot
|
||||
import io.mockk.verify
|
||||
import io.objectbox.Box
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.LanguageEntity
|
||||
import org.kiwix.kiwixmobile.core.zim_manager.Language
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
internal class NewLanguagesDaoTest {
|
||||
private val box: Box<LanguageEntity> = mockk(relaxed = true)
|
||||
private val newLanguagesDao = NewLanguagesDao(box)
|
||||
|
||||
@Test
|
||||
fun insert() {
|
||||
val id = 0L
|
||||
val active = false
|
||||
val occurrencesOfLanguage = 0
|
||||
val language: Language = mockk()
|
||||
val slot: CapturingSlot<Callable<Unit>> = slot()
|
||||
every { box.store.callInTx(capture(slot)) } returns Unit
|
||||
every { language.id } returns id
|
||||
every { language.active } returns active
|
||||
every { language.languageCode } returns Locale.ENGLISH.toString()
|
||||
every { language.occurencesOfLanguage } returns occurrencesOfLanguage
|
||||
newLanguagesDao.insert(listOf(language))
|
||||
slot.captured.call()
|
||||
verify { box.removeAll() }
|
||||
verify {
|
||||
box.put(
|
||||
listOf(
|
||||
LanguageEntity(
|
||||
id = 0L,
|
||||
locale = Locale.ENGLISH,
|
||||
active = false,
|
||||
occurencesOfLanguage = 0
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2022 Kiwix <android.kiwix.org>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.kiwix.kiwixmobile.core.dao
|
||||
|
||||
import io.mockk.CapturingSlot
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.slot
|
||||
import io.mockk.verify
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.query.Query
|
||||
import io.objectbox.query.QueryBuilder
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.NotesEntity
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.NotesEntity_
|
||||
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
||||
import org.kiwix.kiwixmobile.core.page.note
|
||||
import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
internal class NewNoteDaoTest {
|
||||
private val notesBox: Box<NotesEntity> = mockk(relaxed = true)
|
||||
private val newNotesDao = NewNoteDao(notesBox)
|
||||
|
||||
@Test
|
||||
fun deletePages() {
|
||||
val notesItem: NoteListItem = note(zimReaderSource = mockk())
|
||||
val notesItemList: List<NoteListItem> = listOf(notesItem)
|
||||
val pagesToDelete: List<Page> = notesItemList
|
||||
newNotesDao.deletePages(pagesToDelete)
|
||||
verify { newNotesDao.deleteNotes(notesItemList) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteNotePage() {
|
||||
val noteTitle = "abNotesTitle"
|
||||
val queryBuilder: QueryBuilder<NotesEntity> = mockk(relaxed = true)
|
||||
every { notesBox.query() } returns queryBuilder
|
||||
every {
|
||||
queryBuilder.equal(
|
||||
NotesEntity_.noteTitle,
|
||||
noteTitle,
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
} returns queryBuilder
|
||||
val query: Query<NotesEntity> = mockk(relaxed = true)
|
||||
every { queryBuilder.build() } returns query
|
||||
newNotesDao.deleteNote(noteTitle)
|
||||
verify { query.remove() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun saveNotePage() {
|
||||
val newNote: NoteListItem = note(title = "", zimReaderSource = mockk())
|
||||
val slot: CapturingSlot<Callable<Unit>> = slot()
|
||||
every { notesBox.store.callInTx(capture(slot)) } returns Unit
|
||||
val queryBuilder: QueryBuilder<NotesEntity> = mockk(relaxed = true)
|
||||
every { notesBox.query() } returns queryBuilder
|
||||
val query: Query<NotesEntity> = mockk(relaxed = true)
|
||||
every { queryBuilder.build() } returns query
|
||||
every {
|
||||
queryBuilder.equal(
|
||||
NotesEntity_.noteTitle,
|
||||
newNote.title,
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
} returns queryBuilder
|
||||
newNotesDao.saveNote(newNote)
|
||||
slot.captured.call()
|
||||
verify { notesBox.put(NotesEntity(newNote)) }
|
||||
}
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2020 Kiwix <android.kiwix.org>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.kiwix.kiwixmobile.core.dao
|
||||
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.query.Query
|
||||
import io.objectbox.query.QueryBuilder
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.RecentSearchEntity
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.RecentSearchEntity_
|
||||
import org.kiwix.kiwixmobile.core.search.SearchListItem.RecentSearchListItem
|
||||
import org.kiwix.kiwixmobile.core.search.viewmodel.test
|
||||
import org.kiwix.sharedFunctions.recentSearchEntity
|
||||
|
||||
internal class NewRecentSearchDaoTest {
|
||||
private val box: Box<RecentSearchEntity> = mockk(relaxed = true)
|
||||
private val flowBuilder: FlowBuilder = mockk()
|
||||
private val newRecentSearchDao = NewRecentSearchDao(box, flowBuilder)
|
||||
|
||||
@Nested
|
||||
inner class RecentSearchTests {
|
||||
@Test
|
||||
fun `recentSearches searches by Id passed`() =
|
||||
runTest {
|
||||
val zimId = "id"
|
||||
val queryResult = listOf<RecentSearchEntity>(recentSearchEntity())
|
||||
expectFromRecentSearches(queryResult, zimId)
|
||||
newRecentSearchDao.recentSearches(zimId)
|
||||
.test(this)
|
||||
.assertValues(
|
||||
mutableListOf(queryResult.map { RecentSearchListItem(it.searchTerm, it.url) })
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `recentSearches searches with blank Id if null passed`() =
|
||||
runTest {
|
||||
val queryResult = listOf<RecentSearchEntity>(recentSearchEntity())
|
||||
expectFromRecentSearches(queryResult, "")
|
||||
newRecentSearchDao.recentSearches(null)
|
||||
.test(this)
|
||||
.assertValues(
|
||||
mutableListOf(queryResult.map { RecentSearchListItem(it.searchTerm, it.url) })
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `recentSearches searches returns distinct entities by searchTerm`() =
|
||||
runTest {
|
||||
val queryResult = listOf<RecentSearchEntity>(recentSearchEntity(), recentSearchEntity())
|
||||
expectFromRecentSearches(queryResult, "")
|
||||
newRecentSearchDao.recentSearches("")
|
||||
.test(this)
|
||||
.assertValues(
|
||||
mutableListOf(queryResult.take(1).map { RecentSearchListItem(it.searchTerm, it.url) })
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `recentSearches searches returns a limitedNumber of entities`() =
|
||||
runTest {
|
||||
val searchResults: List<RecentSearchEntity> =
|
||||
(0..200).map { recentSearchEntity(searchTerm = "$it") }
|
||||
expectFromRecentSearches(searchResults, "")
|
||||
newRecentSearchDao.recentSearches("")
|
||||
.test(this)
|
||||
.assertLastValue { it.size == 100 }
|
||||
.finish()
|
||||
}
|
||||
|
||||
private fun expectFromRecentSearches(queryResult: List<RecentSearchEntity>, zimId: String) {
|
||||
val queryBuilder = mockk<QueryBuilder<RecentSearchEntity>>()
|
||||
every { box.query() } returns queryBuilder
|
||||
every {
|
||||
queryBuilder.equal(
|
||||
RecentSearchEntity_.zimId,
|
||||
zimId,
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
} returns queryBuilder
|
||||
every { queryBuilder.orderDesc(RecentSearchEntity_.id) } returns queryBuilder
|
||||
val query = mockk<Query<RecentSearchEntity>>()
|
||||
every { queryBuilder.build() } returns query
|
||||
every { flowBuilder.buildCallbackFlow(query) } returns flowOf(queryResult)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `saveSearch puts RecentSearchEntity into box`() {
|
||||
newRecentSearchDao.saveSearch("title", "id", "https://kiwix.app/mainPage")
|
||||
verify {
|
||||
box.put(
|
||||
recentSearchEntity(
|
||||
searchTerm = "title",
|
||||
zimId = "id",
|
||||
url = "https://kiwix.app/mainPage"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deleteSearchString removes query results for the term`() {
|
||||
val searchTerm = "searchTerm"
|
||||
val queryBuilder: QueryBuilder<RecentSearchEntity> = mockk()
|
||||
every { box.query() } returns queryBuilder
|
||||
every {
|
||||
queryBuilder.equal(
|
||||
RecentSearchEntity_.searchTerm,
|
||||
searchTerm,
|
||||
QueryBuilder.StringOrder.CASE_INSENSITIVE
|
||||
)
|
||||
} returns queryBuilder
|
||||
val query: Query<RecentSearchEntity> = mockk(relaxed = true)
|
||||
every { queryBuilder.build() } returns query
|
||||
newRecentSearchDao.deleteSearchString(searchTerm)
|
||||
verify { query.remove() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deleteSearchHistory deletes everything`() {
|
||||
newRecentSearchDao.deleteSearchHistory()
|
||||
verify { box.removeAll() }
|
||||
}
|
||||
}
|
@ -28,7 +28,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||
import org.kiwix.kiwixmobile.core.dao.NewBookmarksDao
|
||||
import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks
|
||||
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
|
||||
import org.kiwix.kiwixmobile.core.page.bookmarkState
|
||||
import org.kiwix.kiwixmobile.core.page.libkiwixBookmarkItem
|
||||
@ -41,7 +41,7 @@ import java.util.UUID
|
||||
|
||||
internal class ShowDeleteBookmarksDialogTest {
|
||||
val effects = mockk<MutableSharedFlow<SideEffect<*>>>(relaxed = true)
|
||||
private val newBookmarksDao = mockk<NewBookmarksDao>()
|
||||
private val libkiwixBookmark = mockk<LibkiwixBookmarks>()
|
||||
val activity = mockk<CoreMainActivity>()
|
||||
private val dialogShower = mockk<DialogShower>(relaxed = true)
|
||||
private val viewModelScope = CoroutineScope(Dispatchers.IO)
|
||||
@ -52,7 +52,7 @@ internal class ShowDeleteBookmarksDialogTest {
|
||||
ShowDeleteBookmarksDialog(
|
||||
effects,
|
||||
bookmarkState(),
|
||||
newBookmarksDao,
|
||||
libkiwixBookmark,
|
||||
viewModelScope,
|
||||
dialogShower
|
||||
)
|
||||
@ -61,7 +61,7 @@ internal class ShowDeleteBookmarksDialogTest {
|
||||
showDeleteBookmarksDialog.invokeWith(activity)
|
||||
verify { dialogShower.show(any(), capture(lambdaSlot)) }
|
||||
lambdaSlot.captured.invoke()
|
||||
verify { effects.tryEmit(DeletePageItems(bookmarkState(), newBookmarksDao, viewModelScope)) }
|
||||
verify { effects.tryEmit(DeletePageItems(bookmarkState(), libkiwixBookmark, viewModelScope)) }
|
||||
}
|
||||
|
||||
private fun mockkActivityInjection(showDeleteBookmarksDialog: ShowDeleteBookmarksDialog) {
|
||||
@ -88,7 +88,7 @@ internal class ShowDeleteBookmarksDialogTest {
|
||||
)
|
||||
)
|
||||
),
|
||||
newBookmarksDao,
|
||||
libkiwixBookmark,
|
||||
viewModelScope,
|
||||
dialogShower
|
||||
)
|
||||
@ -113,7 +113,7 @@ internal class ShowDeleteBookmarksDialogTest {
|
||||
)
|
||||
)
|
||||
),
|
||||
newBookmarksDao,
|
||||
libkiwixBookmark,
|
||||
viewModelScope,
|
||||
dialogShower
|
||||
)
|
||||
|
@ -10,7 +10,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||
import org.kiwix.kiwixmobile.core.dao.HistoryDao
|
||||
import org.kiwix.kiwixmobile.core.dao.HistoryRoomDao
|
||||
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
|
||||
import org.kiwix.kiwixmobile.core.page.historyItem
|
||||
import org.kiwix.kiwixmobile.core.page.historyState
|
||||
@ -21,7 +21,7 @@ import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.DeleteSelectedHistory
|
||||
|
||||
internal class ShowDeleteHistoryDialogTest {
|
||||
val effects = mockk<MutableSharedFlow<SideEffect<*>>>(relaxed = true)
|
||||
private val historyDao = mockk<HistoryDao>()
|
||||
private val historyRoomDao = mockk<HistoryRoomDao>()
|
||||
val activity = mockk<CoreMainActivity>()
|
||||
private val dialogShower = mockk<DialogShower>(relaxed = true)
|
||||
private val viewModelScope = CoroutineScope(Dispatchers.IO)
|
||||
@ -33,7 +33,7 @@ internal class ShowDeleteHistoryDialogTest {
|
||||
ShowDeleteHistoryDialog(
|
||||
effects,
|
||||
historyState(),
|
||||
historyDao,
|
||||
historyRoomDao,
|
||||
viewModelScope,
|
||||
dialogShower
|
||||
)
|
||||
@ -42,7 +42,7 @@ internal class ShowDeleteHistoryDialogTest {
|
||||
showDeleteHistoryDialog.invokeWith(activity)
|
||||
verify { dialogShower.show(any(), capture(lambdaSlot)) }
|
||||
lambdaSlot.captured.invoke()
|
||||
verify { effects.tryEmit(DeletePageItems(historyState(), historyDao, viewModelScope)) }
|
||||
verify { effects.tryEmit(DeletePageItems(historyState(), historyRoomDao, viewModelScope)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -52,7 +52,7 @@ internal class ShowDeleteHistoryDialogTest {
|
||||
ShowDeleteHistoryDialog(
|
||||
effects,
|
||||
historyState(listOf(historyItem(isSelected = true, zimReaderSource = mockk()))),
|
||||
historyDao,
|
||||
historyRoomDao,
|
||||
viewModelScope,
|
||||
dialogShower
|
||||
)
|
||||
@ -68,7 +68,7 @@ internal class ShowDeleteHistoryDialogTest {
|
||||
ShowDeleteHistoryDialog(
|
||||
effects,
|
||||
historyState(),
|
||||
historyDao,
|
||||
historyRoomDao,
|
||||
viewModelScope,
|
||||
dialogShower
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user