mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-09-17 11:25:34 -04:00
Added KiwixRoomDatabaseTest, ObjectBoxToRoomMigratorTest, RecentSearchRoomDaoTest for testing all the scenarios of migration and saving the recent searches in room database.
* Improved the SaveSearchToRecentsTest. * Removed unused code from project.
This commit is contained in:
parent
106e83c704
commit
0613d36ab9
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Kiwix Android
|
||||||
|
* Copyright (c) 2024 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
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.kiwix.kiwixmobile.core.dao.RecentSearchRoomDao
|
||||||
|
import org.kiwix.kiwixmobile.core.dao.entities.RecentSearchRoomEntity
|
||||||
|
import org.kiwix.kiwixmobile.core.data.KiwixRoomDatabase
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class KiwixRoomDatabaseTest {
|
||||||
|
private lateinit var recentSearchRoomDao: RecentSearchRoomDao
|
||||||
|
private lateinit var db: KiwixRoomDatabase
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun teardown() {
|
||||||
|
db.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRecentSearchRoomDao() = runBlocking {
|
||||||
|
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||||
|
db = Room.inMemoryDatabaseBuilder(context, KiwixRoomDatabase::class.java)
|
||||||
|
.allowMainThreadQueries()
|
||||||
|
.build()
|
||||||
|
val zimId = "34388L"
|
||||||
|
val searchTerm = "title 1"
|
||||||
|
val searchTerm2 = "title 2"
|
||||||
|
val url = ""
|
||||||
|
recentSearchRoomDao = db.recentSearchRoomDao()
|
||||||
|
val recentSearch = RecentSearchRoomEntity(zimId = zimId, searchTerm = searchTerm, url = url)
|
||||||
|
val recentSearch1 = RecentSearchRoomEntity(zimId = zimId, searchTerm = searchTerm2, url = url)
|
||||||
|
|
||||||
|
// test inserting into recent search database
|
||||||
|
recentSearchRoomDao.saveSearch(recentSearch.searchTerm, recentSearch.zimId, url = url)
|
||||||
|
var recentSearches = recentSearchRoomDao.search(zimId).first()
|
||||||
|
assertEquals(recentSearches.size, 1)
|
||||||
|
assertEquals(recentSearch.searchTerm, recentSearches.first().searchTerm)
|
||||||
|
assertEquals(recentSearch.zimId, recentSearches.first().zimId)
|
||||||
|
|
||||||
|
// test deleting recent search
|
||||||
|
recentSearchRoomDao.deleteSearchString(searchTerm)
|
||||||
|
recentSearches = recentSearchRoomDao.search(searchTerm).first()
|
||||||
|
assertEquals(recentSearches.size, 0)
|
||||||
|
|
||||||
|
// test deleting all recent search history
|
||||||
|
recentSearchRoomDao.saveSearch(recentSearch.searchTerm, recentSearch.zimId, url = url)
|
||||||
|
recentSearchRoomDao.saveSearch(recentSearch1.searchTerm, recentSearch1.zimId, url = url)
|
||||||
|
recentSearches = recentSearchRoomDao.search(zimId).first()
|
||||||
|
assertEquals(recentSearches.size, 2)
|
||||||
|
recentSearchRoomDao.deleteSearchHistory()
|
||||||
|
recentSearches = recentSearchRoomDao.search(searchTerm).first()
|
||||||
|
assertEquals(recentSearches.size, 0)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
* Kiwix Android
|
||||||
|
* Copyright (c) 2024 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
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import io.objectbox.Box
|
||||||
|
import io.objectbox.BoxStore
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertNotNull
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.kiwix.kiwixmobile.core.dao.entities.RecentSearchEntity
|
||||||
|
import org.kiwix.kiwixmobile.core.data.KiwixRoomDatabase
|
||||||
|
import org.kiwix.kiwixmobile.core.data.remote.ObjectBoxToRoomMigrator
|
||||||
|
import org.kiwix.kiwixmobile.core.di.modules.DatabaseModule
|
||||||
|
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class ObjectBoxToRoomMigratorTest {
|
||||||
|
private lateinit var context: Context
|
||||||
|
private lateinit var kiwixRoomDatabase: KiwixRoomDatabase
|
||||||
|
private lateinit var boxStore: BoxStore
|
||||||
|
private lateinit var objectBoxToRoomMigrator: ObjectBoxToRoomMigrator
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
context = ApplicationProvider.getApplicationContext()
|
||||||
|
kiwixRoomDatabase = Room.inMemoryDatabaseBuilder(context, KiwixRoomDatabase::class.java)
|
||||||
|
.allowMainThreadQueries()
|
||||||
|
.build()
|
||||||
|
boxStore = DatabaseModule.boxStore!!
|
||||||
|
objectBoxToRoomMigrator = ObjectBoxToRoomMigrator()
|
||||||
|
objectBoxToRoomMigrator.kiwixRoomDatabase = kiwixRoomDatabase
|
||||||
|
objectBoxToRoomMigrator.boxStore = boxStore
|
||||||
|
objectBoxToRoomMigrator.sharedPreferenceUtil = SharedPreferenceUtil(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun cleanup() {
|
||||||
|
kiwixRoomDatabase.close()
|
||||||
|
boxStore.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun migrateRecentSearch_shouldInsertDataIntoRoomDatabase() = runBlocking {
|
||||||
|
val box = boxStore.boxFor(RecentSearchEntity::class.java)
|
||||||
|
// clear both databases for recent searches to test more edge cases
|
||||||
|
clearRecentSearchDatabases(box)
|
||||||
|
val expectedSearchTerm = "test search"
|
||||||
|
val expectedZimId = "8812214350305159407L"
|
||||||
|
val expectedUrl = "http://kiwix.app/mainPage"
|
||||||
|
val recentSearchEntity =
|
||||||
|
RecentSearchEntity(searchTerm = expectedSearchTerm, zimId = expectedZimId, url = expectedUrl)
|
||||||
|
// insert into object box
|
||||||
|
box.put(recentSearchEntity)
|
||||||
|
// migrate data into room database
|
||||||
|
objectBoxToRoomMigrator.migrateRecentSearch(box)
|
||||||
|
// check if data successfully migrated to room
|
||||||
|
val actual = kiwixRoomDatabase.recentSearchRoomDao().search(expectedZimId).first()
|
||||||
|
assertEquals(actual.size, 1)
|
||||||
|
assertEquals(actual[0].searchTerm, expectedSearchTerm)
|
||||||
|
assertEquals(actual[0].zimId, expectedZimId)
|
||||||
|
|
||||||
|
// clear both databases for recent searches to test more edge cases
|
||||||
|
clearRecentSearchDatabases(box)
|
||||||
|
|
||||||
|
// Migrate data from empty ObjectBox database
|
||||||
|
objectBoxToRoomMigrator.migrateRecentSearch(box)
|
||||||
|
val actualData = kiwixRoomDatabase.recentSearchRoomDao().fullSearch().first()
|
||||||
|
assertTrue(actualData.isEmpty())
|
||||||
|
|
||||||
|
// Test if data successfully migrated to Room and existing data is preserved
|
||||||
|
val existingSearchTerm = "existing search"
|
||||||
|
val existingZimId = "8812214350305159407L"
|
||||||
|
kiwixRoomDatabase.recentSearchRoomDao()
|
||||||
|
.saveSearch(existingSearchTerm, existingZimId, "$expectedUrl/1")
|
||||||
|
box.put(recentSearchEntity)
|
||||||
|
// Migrate data into Room database
|
||||||
|
objectBoxToRoomMigrator.migrateRecentSearch(box)
|
||||||
|
val actualDataAfterMigration = kiwixRoomDatabase.recentSearchRoomDao().fullSearch().first()
|
||||||
|
assertEquals(1, actual.size)
|
||||||
|
val existingItem =
|
||||||
|
actualDataAfterMigration.find {
|
||||||
|
it.searchTerm == existingSearchTerm && it.zimId == existingZimId
|
||||||
|
}
|
||||||
|
assertNotNull(existingItem)
|
||||||
|
val newItem =
|
||||||
|
actualDataAfterMigration.find {
|
||||||
|
it.searchTerm == expectedSearchTerm && it.zimId == expectedZimId
|
||||||
|
}
|
||||||
|
assertNotNull(newItem)
|
||||||
|
|
||||||
|
clearRecentSearchDatabases(box)
|
||||||
|
|
||||||
|
// Test migration if ObjectBox has null values
|
||||||
|
lateinit var undefinedSearchTerm: String
|
||||||
|
lateinit var undefinedZimId: String
|
||||||
|
lateinit var undefinedUrl: String
|
||||||
|
try {
|
||||||
|
val invalidSearchEntity =
|
||||||
|
RecentSearchEntity(
|
||||||
|
searchTerm = undefinedSearchTerm,
|
||||||
|
zimId = undefinedZimId,
|
||||||
|
url = undefinedUrl
|
||||||
|
)
|
||||||
|
box.put(invalidSearchEntity)
|
||||||
|
// Migrate data into Room database
|
||||||
|
objectBoxToRoomMigrator.migrateRecentSearch(box)
|
||||||
|
} catch (_: Exception) {
|
||||||
|
}
|
||||||
|
// Ensure Room database remains empty or unaffected by the invalid data
|
||||||
|
val actualDataAfterInvalidMigration =
|
||||||
|
kiwixRoomDatabase.recentSearchRoomDao().fullSearch().first()
|
||||||
|
assertTrue(actualDataAfterInvalidMigration.isEmpty())
|
||||||
|
|
||||||
|
// Test large data migration for recent searches
|
||||||
|
val numEntities = 1000
|
||||||
|
// Insert a large number of recent search entities into ObjectBox
|
||||||
|
for (i in 1..numEntities) {
|
||||||
|
val searchTerm = "search_$i"
|
||||||
|
val zimId = "$i"
|
||||||
|
val recentSearchEntity =
|
||||||
|
RecentSearchEntity(searchTerm = searchTerm, zimId = zimId, url = "$expectedUrl$i")
|
||||||
|
box.put(recentSearchEntity)
|
||||||
|
}
|
||||||
|
val startTime = System.currentTimeMillis()
|
||||||
|
// Migrate data into Room database
|
||||||
|
objectBoxToRoomMigrator.migrateRecentSearch(box)
|
||||||
|
val endTime = System.currentTimeMillis()
|
||||||
|
val migrationTime = endTime - startTime
|
||||||
|
// Check if data successfully migrated to Room
|
||||||
|
val actualDataAfterLargeMigration =
|
||||||
|
kiwixRoomDatabase.recentSearchRoomDao().fullSearch().first()
|
||||||
|
assertEquals(numEntities, actualDataAfterLargeMigration.size)
|
||||||
|
// Assert that the migration completes within a reasonable time frame
|
||||||
|
assertTrue("Migration took too long: $migrationTime ms", migrationTime < 5000)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearRecentSearchDatabases(box: Box<RecentSearchEntity>) {
|
||||||
|
// delete history for testing other edge cases
|
||||||
|
kiwixRoomDatabase.recentSearchRoomDao().deleteSearchHistory()
|
||||||
|
box.removeAll()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* Kiwix Android
|
||||||
|
* Copyright (c) 2024 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
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.hamcrest.MatcherAssert.assertThat
|
||||||
|
import org.hamcrest.core.IsEqual.equalTo
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.kiwix.kiwixmobile.core.dao.RecentSearchRoomDao
|
||||||
|
import org.kiwix.kiwixmobile.core.data.KiwixRoomDatabase
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class RecentSearchRoomDaoTest {
|
||||||
|
|
||||||
|
private lateinit var kiwixRoomDatabase: KiwixRoomDatabase
|
||||||
|
private lateinit var recentSearchRoomDao: RecentSearchRoomDao
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
kiwixRoomDatabase.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRecentSearchRoomDao() = runBlocking {
|
||||||
|
val zimId = "8812214350305159407L"
|
||||||
|
val url = "http://kiwix.app/mainPage"
|
||||||
|
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||||
|
kiwixRoomDatabase = Room.inMemoryDatabaseBuilder(context, KiwixRoomDatabase::class.java).build()
|
||||||
|
recentSearchRoomDao = kiwixRoomDatabase.recentSearchRoomDao()
|
||||||
|
// Save a recent search entity
|
||||||
|
val query =
|
||||||
|
"This is a long search term to test whether it will be saved into the room database."
|
||||||
|
recentSearchRoomDao.saveSearch(query, zimId, url)
|
||||||
|
// Search for recent search entities with a matching zimId
|
||||||
|
val result = getRecentSearchByZimId(zimId)
|
||||||
|
// Verify that the result contains the saved entity
|
||||||
|
assertThat(result.size, equalTo(1))
|
||||||
|
assertThat(result[0].searchTerm, equalTo(query))
|
||||||
|
assertThat(result[0].zimId, equalTo(zimId))
|
||||||
|
|
||||||
|
// Delete the saved entity by search term
|
||||||
|
recentSearchRoomDao.deleteSearchString(query)
|
||||||
|
// Verify that the result does not contain the deleted entity
|
||||||
|
assertThat(getRecentSearchByZimId(zimId).size, equalTo(0))
|
||||||
|
|
||||||
|
// Testing deleting all recent searched history
|
||||||
|
// Save two recent search entities
|
||||||
|
recentSearchRoomDao.saveSearch(query, zimId, url)
|
||||||
|
recentSearchRoomDao.saveSearch("query 2", zimId, url)
|
||||||
|
// Delete all recent search entities
|
||||||
|
recentSearchRoomDao.deleteSearchHistory()
|
||||||
|
// Verify that the result is empty
|
||||||
|
assertThat(getRecentSearchByZimId(zimId).size, equalTo(0))
|
||||||
|
|
||||||
|
// test to save empty values for recent search
|
||||||
|
val emptyQuery = ""
|
||||||
|
recentSearchRoomDao.saveSearch(emptyQuery, zimId, url)
|
||||||
|
// verify that the result is not empty
|
||||||
|
assertThat(getRecentSearchByZimId(zimId).size, equalTo(1))
|
||||||
|
|
||||||
|
// we are not saving undefined or null values into database.
|
||||||
|
// test to save undefined value for recent search.
|
||||||
|
lateinit var undefinedQuery: String
|
||||||
|
try {
|
||||||
|
recentSearchRoomDao.saveSearch(undefinedQuery, zimId, url)
|
||||||
|
assertThat(
|
||||||
|
"Undefined value was saved into database",
|
||||||
|
false
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
assertThat("Undefined value was not saved, as expected.", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete all recent search entities for testing unicodes values
|
||||||
|
recentSearchRoomDao.deleteSearchHistory()
|
||||||
|
|
||||||
|
// save unicode values into database
|
||||||
|
val unicodeQuery = "title \u03A3" // Unicode character for Greek capital letter Sigma
|
||||||
|
recentSearchRoomDao.saveSearch(unicodeQuery, zimId, url)
|
||||||
|
assertThat(getRecentSearchByZimId(zimId)[0].searchTerm, equalTo("title Σ"))
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getRecentSearchByZimId(zimId: String) =
|
||||||
|
recentSearchRoomDao.search(zimId).first()
|
||||||
|
}
|
@ -20,13 +20,8 @@ package org.kiwix.kiwixmobile.core.dao
|
|||||||
|
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import io.objectbox.Box
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.kiwix.kiwixmobile.core.dao.entities.RecentSearchEntity
|
|
||||||
import org.kiwix.kiwixmobile.core.dao.entities.RecentSearchRoomEntity
|
import org.kiwix.kiwixmobile.core.dao.entities.RecentSearchRoomEntity
|
||||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem
|
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem
|
||||||
|
|
||||||
@ -80,18 +75,6 @@ abstract class RecentSearchRoomDao {
|
|||||||
@Query("DELETE FROM RecentSearchRoomEntity")
|
@Query("DELETE FROM RecentSearchRoomEntity")
|
||||||
abstract fun deleteSearchHistory()
|
abstract fun deleteSearchHistory()
|
||||||
|
|
||||||
fun migrationToRoomInsert(
|
|
||||||
box: Box<RecentSearchEntity>
|
|
||||||
) {
|
|
||||||
val searchRoomEntityList = box.all
|
|
||||||
searchRoomEntityList.forEachIndexed { _, recentSearchEntity ->
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
saveSearch(recentSearchEntity.searchTerm, recentSearchEntity.zimId, recentSearchEntity.url)
|
|
||||||
// Todo Should we remove object store data now?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val NUM_RECENT_RESULTS = 100
|
private const val NUM_RECENT_RESULTS = 100
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ internal class SaveSearchToRecentsTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `invoke with non null Id saves search`() = runBlocking {
|
fun `invoke with non null Id saves search`() = runBlocking {
|
||||||
val id = "id"
|
val id = "8812214350305159407L"
|
||||||
SaveSearchToRecents(
|
SaveSearchToRecents(
|
||||||
newRecentSearchDao,
|
newRecentSearchDao,
|
||||||
searchListItem,
|
searchListItem,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user