diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt index fa11b33de..2d55e9a8b 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt @@ -39,9 +39,11 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Assertions.assertTrue import org.kiwix.kiwixmobile.core.CoreApp +import org.kiwix.kiwixmobile.core.dao.entities.BookOnDiskEntity import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity import org.kiwix.kiwixmobile.core.data.remote.ObjectBoxToLibkiwixMigrator import org.kiwix.kiwixmobile.core.di.modules.DatabaseModule +import org.kiwix.kiwixmobile.core.entity.LibkiwixBook import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.reader.ZimReaderSource import org.kiwix.kiwixmobile.core.utils.LanguageUtils.Companion.handleLocaleChange @@ -51,6 +53,7 @@ import org.kiwix.kiwixmobile.testutils.RetryRule import org.kiwix.kiwixmobile.testutils.TestUtils import org.kiwix.kiwixmobile.utils.KiwixIdlingResource import org.kiwix.libkiwix.Book +import org.kiwix.libzim.Archive import java.io.File import java.io.FileOutputStream import java.io.OutputStream @@ -62,7 +65,8 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { // take the existing boxStore object private var boxStore: BoxStore? = null private lateinit var zimFile: File - private lateinit var box: Box + private lateinit var bookOnDiskBox: Box + private lateinit var bookmarkBox: Box private val expectedZimName = "Alpine_Linux" private val expectedZimId = "60094d1e-1c9a-a60b-2011-4fb02f8db6c3" private val expectedZimFilePath: String by lazy { zimFile.canonicalPath } @@ -82,6 +86,27 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { ) } + private val bookOnDiskEntity: BookOnDiskEntity by lazy { + BookOnDiskEntity( + id = 0, + file = zimFile, + zimReaderSource = ZimReaderSource(zimFile), + bookId = expectedZimId, + title = "", + description = "", + language = "", + creator = "", + publisher = "", + date = "", + url = "", + articleCount = "", + mediaCount = "", + size = "", + name = expectedZimName, + favIcon = "" + ) + } + @Rule @JvmField var retryRule = RetryRule() @@ -128,18 +153,26 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { " check is your application running" ) } - box = boxStore!!.boxFor(BookmarkEntity::class.java) + bookmarkBox = boxStore!!.boxFor(BookmarkEntity::class.java) // clear the data before running the test case runBlocking { clearBookmarks() } + bookOnDiskBox = boxStore!!.boxFor(BookOnDiskEntity::class.java) + // clear the data before running the test case + runBlocking { clearBookOnDisk() } + // add a file in fileSystem because we need to actual file path for making object of Archive. + zimFile = getZimFile("testzim.zim") + } + + private fun getZimFile(zimFileName: String): File { val loadFileStream = - ObjectBoxToLibkiwixMigratorTest::class.java.classLoader.getResourceAsStream("testzim.zim") - zimFile = + ObjectBoxToLibkiwixMigratorTest::class.java.classLoader.getResourceAsStream(zimFileName) + val zimFile = File( context.getExternalFilesDirs(null)[0], - "testzim.zim" + zimFileName ) if (zimFile.exists()) zimFile.delete() zimFile.createNewFile() @@ -153,15 +186,16 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { } } } + return zimFile } @Test fun testSingleDataMigration(): Unit = runBlocking { - box.put(bookmarkEntity) - // migrate data into room database - objectBoxToLibkiwixMigrator.migrateBookMarks(box) - // check if data successfully migrated to room + bookmarkBox.put(bookmarkEntity) + // migrate data into room libkiwix. + objectBoxToLibkiwixMigrator.migrateBookMarks(bookmarkBox) + // check if data successfully migrated to libkiwix. val actualDataAfterMigration = objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().first() assertEquals(1, actualDataAfterMigration.size) @@ -173,11 +207,89 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { clearBookmarks() } + @Test + fun migrateBookOnDisk_ShouldInsertDataInLibkiwix(): Unit = + runBlocking { + // test with single entity + bookOnDiskBox.put(bookOnDiskEntity) + // migrate data into libkiwix + objectBoxToLibkiwixMigrator.migrateLocalBooks(bookOnDiskBox) + // check if data successfully migrated to libkiwix. + var actualDataAfterMigration = + objectBoxToLibkiwixMigrator.libkiwixBookOnDisk.getBooks() + assertEquals(1, actualDataAfterMigration.size) + assertEquals( + actualDataAfterMigration[0].book.zimReaderSource.toDatabase(), + expectedZimFilePath + ) + assertEquals(actualDataAfterMigration[0].book.id, expectedZimId) + assertEquals(actualDataAfterMigration[0].book.title, "Test_Zim") + // Clear the bookOnDisk list from device to not affect the other test cases. + clearBookOnDisk() + + // test with empty data + objectBoxToLibkiwixMigrator.migrateLocalBooks(bookOnDiskBox) + // check if data successfully migrated to libkiwix. + actualDataAfterMigration = + objectBoxToLibkiwixMigrator.libkiwixBookOnDisk.getBooks() + assertTrue(actualDataAfterMigration.isEmpty()) + // Clear the bookOnDisk list from device to not affect the other test cases. + clearBookOnDisk() + + // Test if data successfully migrated to libkiwix and existing data is preserved + zimFile = getZimFile("testzim.zim") + val secondZimFile = getZimFile("small.zim") + val archive = Archive(secondZimFile.path) + val book = Book().apply { + update(archive) + } + objectBoxToLibkiwixMigrator.libkiwixBookOnDisk.insert(listOf(book)) + val thirdZim = getZimFile("characters_encoding.zim") + val thirdEntity = BookOnDiskEntity( + id = 0, + file = thirdZim, + zimReaderSource = ZimReaderSource(thirdZim), + bookId = expectedZimId, + title = "", + description = "", + language = "", + creator = "", + publisher = "", + date = "", + url = "", + articleCount = "", + mediaCount = "", + size = "", + name = expectedZimName, + favIcon = "" + ) + bookOnDiskBox.put(thirdEntity) + bookOnDiskBox.put(bookOnDiskEntity) + // Migrate data into libkiwix + objectBoxToLibkiwixMigrator.migrateLocalBooks(bookOnDiskBox) + actualDataAfterMigration = + objectBoxToLibkiwixMigrator.libkiwixBookOnDisk.getBooks() + assertEquals(3, actualDataAfterMigration.size) + val existingItem = + actualDataAfterMigration.find { + it.book.zimReaderSource.toDatabase() == secondZimFile.path + } + assertNotNull(existingItem) + val newItem = + actualDataAfterMigration.find { + it.book.zimReaderSource.toDatabase() == expectedZimFilePath + } + assertNotNull(newItem) + // Clear the bookmarks list from device to not affect the other test cases. + clearBookmarks() + secondZimFile.delete() + } + @Test fun testMigrationWithEmptyData(): Unit = runBlocking { // Migrate data from empty ObjectBox database - objectBoxToLibkiwixMigrator.migrateBookMarks(box) + objectBoxToLibkiwixMigrator.migrateBookMarks(bookmarkBox) val actualDataAfterMigration = objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().first() assertTrue(actualDataAfterMigration.isEmpty()) @@ -209,9 +321,9 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { libkiwixBook ) ) - box.put(bookmarkEntity) - // Migrate data into Room database - objectBoxToLibkiwixMigrator.migrateBookMarks(box) + bookmarkBox.put(bookmarkEntity) + // Migrate data into libkiwix + objectBoxToLibkiwixMigrator.migrateBookMarks(bookmarkBox) val actualDataAfterMigration = objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().first() assertEquals(2, actualDataAfterMigration.size) @@ -234,7 +346,7 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { runBlocking { // Test large data migration for recent searches for (i in 1..1000) { - box.put( + bookmarkBox.put( BookmarkEntity( 0, expectedZimId, @@ -247,9 +359,9 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { ) ) } - // Migrate data into Room database - objectBoxToLibkiwixMigrator.migrateBookMarks(box) - // Check if data successfully migrated to Room + // Migrate data into libkiwix + objectBoxToLibkiwixMigrator.migrateBookMarks(bookmarkBox) + // Check if data successfully migrated to libkiwix val actualDataAfterMigration = objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().first() assertEquals(1000, actualDataAfterMigration.size) @@ -272,10 +384,10 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { expectedTitle, expectedFavicon ) - box.put(bookmarkEntity) - // migrate data into room database - objectBoxToLibkiwixMigrator.migrateBookMarks(box) - // check if data successfully migrated to room + bookmarkBox.put(bookmarkEntity) + // migrate data into libkiwix + objectBoxToLibkiwixMigrator.migrateBookMarks(bookmarkBox) + // check if data successfully migrated to libkiwix val actualDataAfterMigration = objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().first() assertEquals(1, actualDataAfterMigration.size) @@ -303,10 +415,10 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { expectedTitle, expectedFavicon ) - box.put(bookmarkEntity) - // migrate data into room database - objectBoxToLibkiwixMigrator.migrateBookMarks(box) - // check if data successfully migrated to room + bookmarkBox.put(bookmarkEntity) + // migrate data into libkiwix + objectBoxToLibkiwixMigrator.migrateBookMarks(bookmarkBox) + // check if data successfully migrated to libkiwix val actualDataAfterMigration = objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().first() assertEquals(1, actualDataAfterMigration.size) @@ -318,13 +430,24 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { clearBookmarks() } + private suspend fun clearBookOnDisk() { + objectBoxToLibkiwixMigrator.libkiwixBookOnDisk.delete( + objectBoxToLibkiwixMigrator.libkiwixBookOnDisk.getBooks() + .map { LibkiwixBook(it.book.nativeBook) } + ) + bookOnDiskBox.removeAll() + if (::zimFile.isInitialized) { + zimFile.delete() // delete the temp ZIM file to free up the memory + } + } + private suspend fun clearBookmarks() { // delete bookmarks for testing other edge cases objectBoxToLibkiwixMigrator.libkiwixBookmarks.deleteBookmarks( objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks() .first() as List ) - box.removeAll() + bookmarkBox.removeAll() if (::zimFile.isInitialized) { zimFile.delete() // delete the temp ZIM file to free up the memory } diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/ImportBookmarkTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/ImportBookmarkTest.kt index 4858a8ce9..961d14538 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/ImportBookmarkTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/ImportBookmarkTest.kt @@ -40,9 +40,8 @@ import org.junit.Rule import org.junit.Test import org.junit.jupiter.api.Assertions.assertEquals import org.kiwix.kiwixmobile.BaseActivityTest +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.entities.BookOnDiskEntity import org.kiwix.kiwixmobile.core.di.modules.DatabaseModule import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.utils.LanguageUtils @@ -63,7 +62,7 @@ class ImportBookmarkTest : BaseActivityTest() { private var boxStore: BoxStore? = null private val library = Library() private val manager = Manager(library) - private lateinit var newBookDao: NewBookDao + private lateinit var libkiwixBookOnDisk: LibkiwixBookOnDisk private lateinit var libkiwixBookmarks: LibkiwixBookmarks private val bookmarkXmlData = @@ -138,13 +137,14 @@ class ImportBookmarkTest : BaseActivityTest() { } } boxStore = DatabaseModule.boxStore - newBookDao = NewBookDao(boxStore!!.boxFor(BookOnDiskEntity::class.java)) + val sharedPreferenceUtils = SharedPreferenceUtil(context) + libkiwixBookOnDisk = LibkiwixBookOnDisk(library, manager, sharedPreferenceUtils) libkiwixBookmarks = LibkiwixBookmarks( library, manager, - SharedPreferenceUtil(context), - newBookDao, + sharedPreferenceUtils, + libkiwixBookOnDisk, null ) } diff --git a/app/src/test/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModelTest.kt b/app/src/test/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModelTest.kt index 2ac3b1483..28d4e774c 100644 --- a/app/src/test/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModelTest.kt +++ b/app/src/test/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModelTest.kt @@ -28,6 +28,7 @@ import app.cash.turbine.TurbineTestContext import app.cash.turbine.test import com.jraska.livedata.test import io.mockk.clearAllMocks +import io.mockk.coVerify import io.mockk.every import io.mockk.mockk import io.mockk.verify @@ -41,9 +42,9 @@ import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.resetMain -import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain +import kotlinx.coroutines.withTimeout import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeEach @@ -129,8 +130,8 @@ class ZimManageViewModelTest { @AfterAll fun teardown() { - Dispatchers.resetMain() viewModel.onClearedExposed() + Dispatchers.resetMain() } @BeforeEach @@ -236,24 +237,24 @@ class ZimManageViewModelTest { @Test fun `books found on filesystem are filtered by books already in db`() = runTest { every { application.getString(any()) } returns "" - val expectedBook = libkiwixBook("1") - val bookToRemove = libkiwixBook("2") + val expectedBook = bookOnDisk(1L, libkiwixBook("1", nativeBook = BookTestWrapper("1"))) + val bookToRemove = bookOnDisk(1L, libkiwixBook("2", nativeBook = BookTestWrapper("2"))) advanceUntilIdle() viewModel.requestFileSystemCheck.emit(Unit) advanceUntilIdle() - // books.emit(listOf(bookToRemove)) - // advanceUntilIdle() - // booksOnFileSystem.emit( - // listOf( - // expectedBook, - // expectedBook, - // bookToRemove - // ) - // ) - // advanceUntilIdle() - // coVerify { - // libkiwixBookOnDisk.insert(listOf(expectedBook.book)) - // } + books.emit(listOf(bookToRemove)) + advanceUntilIdle() + booksOnFileSystem.emit( + listOfNotNull( + expectedBook.book.nativeBook, + expectedBook.book.nativeBook, + bookToRemove.book.nativeBook + ) + ) + advanceUntilIdle() + coVerify(timeout = 500) { + libkiwixBookOnDisk.insert(listOfNotNull(expectedBook.book.nativeBook)) + } } } @@ -358,7 +359,7 @@ class ZimManageViewModelTest { language(isActive = true, occurencesOfLanguage = 1) ) advanceUntilIdle() - verify { + verify(timeout = 500) { newLanguagesDao.insert( listOf( dbLanguage.copy(occurencesOfLanguage = 2), @@ -384,9 +385,9 @@ class ZimManageViewModelTest { every { application.getString(any(), any()) } returns "" every { defaultLanguageProvider.provide() } returns defaultLanguage viewModel.networkLibrary.emit(networkBooks) - runCurrent() + advanceUntilIdle() languages.value = dbBooks - runCurrent() + advanceUntilIdle() networkStates.value = CONNECTED advanceUntilIdle() } @@ -475,13 +476,21 @@ class ZimManageViewModelTest { viewModel.networkLibrary.emit(listOf(bookOver4Gb)) }, assert = { - skipItems(1) - assertThat(awaitItem()).isEqualTo( - listOf( - LibraryListItem.DividerItem(Long.MIN_VALUE, R.string.other_languages), - LibraryListItem.BookItem(bookOver4Gb, CannotWrite4GbFile) - ) - ) + withTimeout(5000) { + while (true) { + val item = awaitItem() + val bookItem = item.filterIsInstance().firstOrNull() + if (bookItem?.fileSystemState == CannotWrite4GbFile) { + assertThat(item).isEqualTo( + listOf( + LibraryListItem.DividerItem(Long.MIN_VALUE, R.string.other_languages), + LibraryListItem.BookItem(bookOver4Gb, CannotWrite4GbFile) + ) + ) + break + } + } + } } ) } @@ -610,3 +619,9 @@ suspend fun TestScope.testFlow( } job.join() } + +class BookTestWrapper(private val id: String) : Book(0L) { + override fun getId(): String = id + override fun equals(other: Any?): Boolean = other is BookTestWrapper && getId() == other.getId() + override fun hashCode(): Int = getId().hashCode() +} diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/StorageObserver.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/StorageObserver.kt index 56ad210a6..aa56ae365 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/StorageObserver.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/StorageObserver.kt @@ -39,7 +39,8 @@ class StorageObserver @Inject constructor( private val downloadRoomDao: DownloadRoomDao, private val fileSearch: FileSearch, private val zimReaderFactory: ZimFileReader.Factory, - private val libkiwixBookmarks: LibkiwixBookmarks + private val libkiwixBookmarks: LibkiwixBookmarks, + private val libkiwixBookFactory: LibkiwixBookFactory ) { fun getBooksOnFileSystem( scanningProgressListener: ScanningProgressListener, @@ -64,7 +65,7 @@ class StorageObserver @Inject constructor( private suspend fun convertToLibkiwixBook(file: File) = zimReaderFactory.create(ZimReaderSource(file)) ?.let { zimFileReader -> - Book().apply { + libkiwixBookFactory.create().apply { update(zimFileReader.jniKiwixReader) }.also { // add the book to libkiwix library to validate the imported bookmarks @@ -73,3 +74,7 @@ class StorageObserver @Inject constructor( } } } + +interface LibkiwixBookFactory { + fun create(): Book +} diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookOnDisk.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookOnDisk.kt index 4296f4220..b378e2ec9 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookOnDisk.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookOnDisk.kt @@ -205,7 +205,7 @@ class LibkiwixBookOnDisk @Inject constructor( private suspend fun isInTrashFolder(filePath: String) = Regex("/\\.Trash/").containsMatchIn(filePath) - private suspend fun delete(books: List) { + suspend fun delete(books: List) { runCatching { books.forEach { library.removeBookById(it.id) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/di/components/CoreComponent.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/di/components/CoreComponent.kt index 83648865c..4dee3f60d 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/di/components/CoreComponent.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/di/components/CoreComponent.kt @@ -25,12 +25,13 @@ import dagger.BindsInstance import dagger.Component import kotlinx.coroutines.sync.Mutex 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.LibkiwixBookmarks 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 @@ -85,6 +86,7 @@ interface CoreComponent { fun zimReaderContainer(): ZimReaderContainer fun sharedPrefUtil(): SharedPreferenceUtil fun zimFileReaderFactory(): ZimFileReader.Factory + fun libkiwixBookFactory(): LibkiwixBookFactory fun storageObserver(): StorageObserver fun kiwixService(): KiwixService fun application(): Application diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/JNIModule.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/JNIModule.kt index 166babdb6..5d68d1b17 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/JNIModule.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/JNIModule.kt @@ -20,10 +20,12 @@ package org.kiwix.kiwixmobile.core.di.modules import android.content.Context import dagger.Module import dagger.Provides +import org.kiwix.kiwixmobile.core.LibkiwixBookFactory import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil +import org.kiwix.libkiwix.Book import org.kiwix.libkiwix.JNIKiwix import org.kiwix.libkiwix.Library import org.kiwix.libkiwix.Manager @@ -83,6 +85,14 @@ class JNIModule { @Named(LOCAL_BOOKS_MANAGER) manager: Manager, sharedPreferenceUtil: SharedPreferenceUtil, ): LibkiwixBookOnDisk = LibkiwixBookOnDisk(library, manager, sharedPreferenceUtil) + + /** + * We are not making this singleton because we need multiple objects of this. + */ + @Provides + fun provideBookFactory(): LibkiwixBookFactory = object : LibkiwixBookFactory { + override fun create(): Book = Book() + } } const val BOOKMARK_LIBRARY = "bookmarkLibrary" diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/entity/LibkiwixBook.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/entity/LibkiwixBook.kt index f986f3bfa..4fa19d882 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/entity/LibkiwixBook.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/entity/LibkiwixBook.kt @@ -29,7 +29,7 @@ import java.io.File */ @Suppress("ConstructorParameterNaming") data class LibkiwixBook( - val nativeBook: Book? = null, + var nativeBook: Book? = null, private var _id: String = "", private var _title: String = "", private var _description: String? = null, diff --git a/core/src/sharedTestFunctions/java/org/kiwix/sharedFunctions/TestModelFunctions.kt b/core/src/sharedTestFunctions/java/org/kiwix/sharedFunctions/TestModelFunctions.kt index 772bbf204..0582e9fe4 100644 --- a/core/src/sharedTestFunctions/java/org/kiwix/sharedFunctions/TestModelFunctions.kt +++ b/core/src/sharedTestFunctions/java/org/kiwix/sharedFunctions/TestModelFunctions.kt @@ -35,6 +35,7 @@ import org.kiwix.kiwixmobile.core.entity.MetaLinkNetworkEntity.Url import org.kiwix.kiwixmobile.core.reader.ZimReaderSource import org.kiwix.kiwixmobile.core.zim_manager.Language import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.BooksOnDiskListItem.BookOnDisk +import org.kiwix.libkiwix.Book import java.io.File fun bookOnDisk( @@ -146,8 +147,11 @@ fun libkiwixBook( size: String = "1024", name: String = "name", favIcon: String = "favIcon", - file: File = File("") + file: File = File(""), + nativeBook: Book? = null, + tags: String? = "" ) = LibkiwixBook().apply { + this.nativeBook = nativeBook this.id = id this.title = title this.description = description @@ -162,6 +166,7 @@ fun libkiwixBook( this.file = file bookName = name favicon = favIcon + this.tags = tags } fun recentSearchEntity( diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/StorageObserverTest.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/StorageObserverTest.kt index f3bac330a..5edd63f2a 100644 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/StorageObserverTest.kt +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/StorageObserverTest.kt @@ -19,6 +19,8 @@ package org.kiwix.kiwixmobile.core import io.mockk.clearAllMocks +import io.mockk.coEvery +import io.mockk.coVerify import io.mockk.every import io.mockk.mockk import io.mockk.verify @@ -39,9 +41,9 @@ import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil import org.kiwix.kiwixmobile.core.utils.files.FileSearch import org.kiwix.kiwixmobile.core.utils.files.ScanningProgressListener import org.kiwix.kiwixmobile.core.utils.files.testFlow -import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.BooksOnDiskListItem.BookOnDisk +import org.kiwix.libkiwix.Book +import org.kiwix.libzim.Archive import org.kiwix.sharedFunctions.libkiwixBook -import org.kiwix.sharedFunctions.bookOnDisk import java.io.File class StorageObserverTest { @@ -58,6 +60,8 @@ class StorageObserverTest { private val files = MutableStateFlow>(emptyList()) private val downloads = MutableStateFlow>(emptyList()) + private val libkiwixBookFactory: LibkiwixBookFactory = mockk() + private val libkiwixBook: Book = BookTestWrapper("id") private lateinit var storageObserver: StorageObserver @@ -66,9 +70,17 @@ class StorageObserverTest { every { sharedPreferenceUtil.prefStorage } returns "a" every { fileSearch.scan(scanningProgressListener) } returns files every { downloadRoomDao.downloads() } returns downloads + coEvery { libkiwixBookmarks.addBookToLibrary(any()) } returns Unit every { zimFileReader.jniKiwixReader } returns mockk() every { runBlocking { readerFactory.create(zimReaderSource) } } returns zimFileReader - storageObserver = StorageObserver(downloadRoomDao, fileSearch, readerFactory, libkiwixBookmarks) + every { libkiwixBookFactory.create() } returns libkiwixBook + storageObserver = StorageObserver( + downloadRoomDao, + fileSearch, + readerFactory, + libkiwixBookmarks, + libkiwixBookFactory + ) } @Test @@ -77,7 +89,7 @@ class StorageObserverTest { testFlow( flow = booksOnFileSystem(), triggerAction = {}, - assert = { assertThat(awaitItem()).isEqualTo(listOf()) } + assert = { assertThat(awaitItem()).isEqualTo(listOf()) } ) } @@ -87,7 +99,7 @@ class StorageObserverTest { val expectedBook = libkiwixBook( "id", "title", "1", "favicon", "creator", "publisher", "date", - "description", "language" + "description", "language", nativeBook = libkiwixBook ) withNoFiltering() every { zimFileReader.toBook() } returns expectedBook @@ -97,15 +109,14 @@ class StorageObserverTest { triggerAction = {}, assert = { assertThat(awaitItem()).isEqualTo( - listOf( - bookOnDisk( - book = expectedBook, - zimReaderSource = zimReaderSource - ) + listOfNotNull( + expectedBook.nativeBook ) ) } ) + // test the book is added to bookmark's library. + coVerify { libkiwixBookmarks.addBookToLibrary(archive = any()) } verify { zimFileReader.dispose() } } @@ -128,3 +139,12 @@ class StorageObserverTest { every { zimReaderSource.file } returns file } } + +class BookTestWrapper(private val id: String) : Book(0L) { + override fun getId(): String = id + override fun equals(other: Any?): Boolean = other is BookTestWrapper && getId() == other.getId() + override fun hashCode(): Int = getId().hashCode() + override fun update(archive: Archive?) { + // do nothing + } +} diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/utils/DonationDialogHandlerTest.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/utils/DonationDialogHandlerTest.kt index 696f7aae1..88556f6bc 100644 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/utils/DonationDialogHandlerTest.kt +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/utils/DonationDialogHandlerTest.kt @@ -36,7 +36,7 @@ import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.getPackageInformation -import org.kiwix.kiwixmobile.core.dao.NewBookDao +import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions @@ -44,7 +44,7 @@ import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions class DonationDialogHandlerTest { private lateinit var activity: Activity private lateinit var sharedPreferenceUtil: SharedPreferenceUtil - private lateinit var newBookDao: NewBookDao + private lateinit var libkiwixBookOnDisk: LibkiwixBookOnDisk private lateinit var donationDialogHandler: DonationDialogHandler private lateinit var showDonationDialogCallback: DonationDialogHandler.ShowDonationDialogCallback private lateinit var packageManager: PackageManager @@ -56,10 +56,10 @@ class DonationDialogHandlerTest { every { activity.packageManager } returns packageManager every { activity.packageName } returns "org.kiwix.kiwixmobile" sharedPreferenceUtil = mockk(relaxed = true) - newBookDao = mockk(relaxed = true) + libkiwixBookOnDisk = mockk(relaxed = true) showDonationDialogCallback = mockk(relaxed = true) donationDialogHandler = - DonationDialogHandler(activity, sharedPreferenceUtil, newBookDao) + DonationDialogHandler(activity, sharedPreferenceUtil, libkiwixBookOnDisk) donationDialogHandler.setDonationDialogCallBack(showDonationDialogCallback) } @@ -69,7 +69,7 @@ class DonationDialogHandlerTest { donationDialogHandler = spyk(donationDialogHandler) every { sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds } returns 0L every { sharedPreferenceUtil.laterClickedMilliSeconds } returns 0L - coEvery { newBookDao.getBooks() } returns listOf(mockk()) + coEvery { libkiwixBookOnDisk.getBooks() } returns listOf(mockk()) every { donationDialogHandler.shouldShowInitialPopup(any()) } returns true @@ -81,7 +81,7 @@ class DonationDialogHandlerTest { fun `should not show donation popup if app is not three month old`() = runTest { every { sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds } returns 0L - coEvery { newBookDao.getBooks() } returns listOf(mockk()) + coEvery { libkiwixBookOnDisk.getBooks() } returns listOf(mockk()) val currentMillis = System.currentTimeMillis() val installTime = currentMillis - 1000 val packageInfo = @@ -99,7 +99,7 @@ class DonationDialogHandlerTest { fun `should not show donation popup when no ZIM files available in library`() = runTest { every { sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds } returns 0L - coEvery { newBookDao.getBooks() } returns emptyList() + coEvery { libkiwixBookOnDisk.getBooks() } returns emptyList() val currentMillis = System.currentTimeMillis() val threeMonthsAgo = currentMillis - THREE_MONTHS_IN_MILLISECONDS val packageInfo = @@ -119,7 +119,7 @@ class DonationDialogHandlerTest { every { sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds } returns currentMilliSeconds - (THREE_MONTHS_IN_MILLISECONDS / 2) - coEvery { newBookDao.getBooks() } returns listOf(mockk()) + coEvery { libkiwixBookOnDisk.getBooks() } returns listOf(mockk()) donationDialogHandler.attemptToShowDonationPopup() verify(exactly = 0) { showDonationDialogCallback.showDonationDialog() } } @@ -131,7 +131,7 @@ class DonationDialogHandlerTest { every { sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds } returns currentMilliSeconds - (THREE_MONTHS_IN_MILLISECONDS + 1000) - coEvery { newBookDao.getBooks() } returns listOf(mockk()) + coEvery { libkiwixBookOnDisk.getBooks() } returns listOf(mockk()) donationDialogHandler.attemptToShowDonationPopup() verify { showDonationDialogCallback.showDonationDialog() } } @@ -145,7 +145,7 @@ class DonationDialogHandlerTest { sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds } returns 0L every { donationDialogHandler.shouldShowInitialPopup(any()) } returns true - coEvery { newBookDao.getBooks() } returns listOf(mockk()) + coEvery { libkiwixBookOnDisk.getBooks() } returns listOf(mockk()) every { sharedPreferenceUtil.laterClickedMilliSeconds } returns currentMilliSeconds - (THREE_MONTHS_IN_MILLISECONDS + 1000) @@ -162,7 +162,7 @@ class DonationDialogHandlerTest { sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds } returns 0L every { donationDialogHandler.shouldShowInitialPopup(any()) } returns true - coEvery { newBookDao.getBooks() } returns listOf(mockk()) + coEvery { libkiwixBookOnDisk.getBooks() } returns listOf(mockk()) every { sharedPreferenceUtil.laterClickedMilliSeconds } returns currentMilliSeconds - 10000L @@ -243,7 +243,7 @@ class DonationDialogHandlerTest { with(mockk()) { every { activity.packageName } returns "org.kiwix.kiwixmobile" every { activity.isCustomApp() } returns false - coEvery { newBookDao.getBooks() } returns emptyList() + coEvery { libkiwixBookOnDisk.getBooks() } returns emptyList() val result = donationDialogHandler.isZimFilesAvailableInLibrary() assertFalse(result) } @@ -255,7 +255,7 @@ class DonationDialogHandlerTest { with(mockk()) { every { activity.packageName } returns "org.kiwix.kiwixmobile" every { activity.isCustomApp() } returns false - coEvery { newBookDao.getBooks() } returns listOf(mockk()) + coEvery { libkiwixBookOnDisk.getBooks() } returns listOf(mockk()) val result = donationDialogHandler.isZimFilesAvailableInLibrary() assertTrue(result) }