From e638de685742c835e5baeca0c1cfd858c01fac35 Mon Sep 17 00:00:00 2001 From: Sad Ellie Date: Wed, 3 Jan 2024 18:31:48 +0300 Subject: [PATCH] BackupManager tests --- .../unitto/data/backup/BackupManagerTest.kt | 209 ++++++++---------- .../data/backup/FakeCalculatorHistoryDao.kt | 46 ++++ .../unitto/data/backup/FakeTimeZoneDao.kt | 16 +- .../unitto/data/backup/FakeUnitsDao.kt | 10 +- .../backup/FakeUserPreferencesRepository.kt | 56 ++--- .../unitto/data/backup/BackupManager.kt | 3 +- 6 files changed, 180 insertions(+), 160 deletions(-) create mode 100644 data/backup/src/androidTest/java/com/sadellie/unitto/data/backup/FakeCalculatorHistoryDao.kt diff --git a/data/backup/src/androidTest/java/com/sadellie/unitto/data/backup/BackupManagerTest.kt b/data/backup/src/androidTest/java/com/sadellie/unitto/data/backup/BackupManagerTest.kt index c61f77b0..10e3d67d 100644 --- a/data/backup/src/androidTest/java/com/sadellie/unitto/data/backup/BackupManagerTest.kt +++ b/data/backup/src/androidTest/java/com/sadellie/unitto/data/backup/BackupManagerTest.kt @@ -25,8 +25,10 @@ import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.preferencesDataStoreFile import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry +import com.sadellie.unitto.data.database.CalculatorHistoryEntity +import com.sadellie.unitto.data.database.TimeZoneEntity +import com.sadellie.unitto.data.database.UnitsEntity import com.sadellie.unitto.data.userprefs.PrefsKeys -import com.sadellie.unitto.data.userprefs.getThemingMode import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking import org.junit.Assert.* @@ -47,6 +49,7 @@ class BackupManagerTest { @Before fun setup() { + // Inserting dummy data as app data (db and prefs) val appContext = InstrumentationRegistry.getInstrumentation().targetContext dataStore = PreferenceDataStoreFactory.create( @@ -58,147 +61,111 @@ class BackupManagerTest { mContext = appContext, dataStore = dataStore, unitsDao = FakeUnitsDao, - timeZoneDao = FakeTimeZoneDao + timeZoneDao = FakeTimeZoneDao, + calculatorHistoryDao = FakeCalculatorHistoryDao ) runBlocking { dataStore.edit { - it[PrefsKeys.THEMING_MODE] = FakeUsrPreferenceValues.themingMode - it[PrefsKeys.ENABLE_DYNAMIC_THEME] = FakeUsrPreferenceValues.enableDynamicTheme - it[PrefsKeys.ENABLE_AMOLED_THEME] = FakeUsrPreferenceValues.enableAmoledTheme - it[PrefsKeys.CUSTOM_COLOR] = FakeUsrPreferenceValues.customColor - it[PrefsKeys.MONET_MODE] = FakeUsrPreferenceValues.monetMode - it[PrefsKeys.STARTING_SCREEN] = FakeUsrPreferenceValues.startingScreen - it[PrefsKeys.ENABLE_TOOLS_EXPERIMENT] = FakeUsrPreferenceValues.enableToolsExperiment - it[PrefsKeys.ENABLE_VIBRATIONS] = FakeUsrPreferenceValues.enableVibrations - it[PrefsKeys.MIDDLE_ZERO] = FakeUsrPreferenceValues.middleZero - it[PrefsKeys.AC_BUTTON] = FakeUsrPreferenceValues.acButton - it[PrefsKeys.RPN_MODE] = FakeUsrPreferenceValues.rpnMode - - // FORMATTER - it[PrefsKeys.DIGITS_PRECISION] = FakeUsrPreferenceValues.precision - it[PrefsKeys.SEPARATOR] = FakeUsrPreferenceValues.separator - it[PrefsKeys.OUTPUT_FORMAT] = FakeUsrPreferenceValues.outputFormat - - // CALCULATOR - it[PrefsKeys.RADIAN_MODE] = FakeUsrPreferenceValues.radianMode - it[PrefsKeys.PARTIAL_HISTORY_VIEW] = FakeUsrPreferenceValues.partialHistoryView - - // UNIT CONVERTER - it[PrefsKeys.LATEST_LEFT_SIDE] = FakeUsrPreferenceValues.latestLeftSide - it[PrefsKeys.LATEST_RIGHT_SIDE] = FakeUsrPreferenceValues.latestRightSide - it[PrefsKeys.SHOWN_UNIT_GROUPS] = FakeUsrPreferenceValues.shownUnitGroups.joinToString(",") - it[PrefsKeys.UNIT_CONVERTER_FAVORITES_ONLY] = FakeUsrPreferenceValues.unitConverterFavoritesOnly - it[PrefsKeys.UNIT_CONVERTER_FORMAT_TIME] = FakeUsrPreferenceValues.unitConverterFormatTime - it[PrefsKeys.UNIT_CONVERTER_SORTING] = FakeUsrPreferenceValues.unitConverterSorting.name + it[PrefsKeys.THEMING_MODE] = fakeUserData.themingMode + it[PrefsKeys.ENABLE_DYNAMIC_THEME] = fakeUserData.enableDynamicTheme + it[PrefsKeys.ENABLE_AMOLED_THEME] = fakeUserData.enableAmoledTheme + it[PrefsKeys.CUSTOM_COLOR] = fakeUserData.customColor + it[PrefsKeys.MONET_MODE] = fakeUserData.monetMode + it[PrefsKeys.STARTING_SCREEN] = fakeUserData.startingScreen + it[PrefsKeys.ENABLE_TOOLS_EXPERIMENT] = fakeUserData.enableToolsExperiment + it[PrefsKeys.SYSTEM_FONT] = fakeUserData.systemFont + it[PrefsKeys.ENABLE_VIBRATIONS] = fakeUserData.enableVibrations + it[PrefsKeys.MIDDLE_ZERO] = fakeUserData.middleZero + it[PrefsKeys.AC_BUTTON] = fakeUserData.acButton + it[PrefsKeys.RPN_MODE] = fakeUserData.rpnMode + it[PrefsKeys.DIGITS_PRECISION] = fakeUserData.precision + it[PrefsKeys.SEPARATOR] = fakeUserData.separator + it[PrefsKeys.OUTPUT_FORMAT] = fakeUserData.outputFormat + it[PrefsKeys.RADIAN_MODE] = fakeUserData.radianMode + it[PrefsKeys.PARTIAL_HISTORY_VIEW] = fakeUserData.partialHistoryView + it[PrefsKeys.LATEST_LEFT_SIDE] = fakeUserData.latestLeftSide + it[PrefsKeys.LATEST_RIGHT_SIDE] = fakeUserData.latestRightSide + it[PrefsKeys.SHOWN_UNIT_GROUPS] = fakeUserData.shownUnitGroups + it[PrefsKeys.UNIT_CONVERTER_FAVORITES_ONLY] = fakeUserData.unitConverterFavoritesOnly + it[PrefsKeys.UNIT_CONVERTER_FORMAT_TIME] = fakeUserData.unitConverterFormatTime + it[PrefsKeys.UNIT_CONVERTER_SORTING] = fakeUserData.unitConverterSorting } } } @Test - fun getUserDataTest() = runBlocking{ + fun testBackup() = runBlocking{ + // Backup manager also saves the file to disk, but it is not tested here. + // There is probably no need to test if the data in app is valid since moshi will throw an + // exceptions if the data in app is invalid. For example, if unitConverterSorting is set to + // "Qwerty" (this sorting doesn't exist) there will be an exception. val actualUserData = backupManager.userDataFromApp() - val expectedUserData = UserData( - themingMode = FakeUsrPreferenceValues.themingMode, - enableDynamicTheme = FakeUsrPreferenceValues.enableDynamicTheme, - enableAmoledTheme = FakeUsrPreferenceValues.enableAmoledTheme, - customColor = FakeUsrPreferenceValues.customColor, - monetMode = FakeUsrPreferenceValues.monetMode, - startingScreen = FakeUsrPreferenceValues.startingScreen, - enableToolsExperiment = FakeUsrPreferenceValues.enableToolsExperiment, - enableVibrations = FakeUsrPreferenceValues.enableVibrations, - middleZero = FakeUsrPreferenceValues.middleZero, - acButton = FakeUsrPreferenceValues.acButton, - rpnMode = FakeUsrPreferenceValues.rpnMode, - precision = FakeUsrPreferenceValues.precision, - separator = FakeUsrPreferenceValues.separator, - outputFormat = FakeUsrPreferenceValues.outputFormat, - radianMode = FakeUsrPreferenceValues.radianMode, - partialHistoryView = FakeUsrPreferenceValues.partialHistoryView, - latestLeftSide = FakeUsrPreferenceValues.latestLeftSide, - latestRightSide = FakeUsrPreferenceValues.latestRightSide, - shownUnitGroups = FakeUsrPreferenceValues.shownUnitGroups, - unitConverterFavoritesOnly = FakeUsrPreferenceValues.unitConverterFavoritesOnly, - unitConverterFormatTime = FakeUsrPreferenceValues.unitConverterFormatTime, - unitConverterSorting = FakeUsrPreferenceValues.unitConverterSorting, - unitsTable = units, - timeZoneTable = timeZones - ) + val expectedUserData = fakeUserData assertEquals(expectedUserData, actualUserData) } @Test - fun updateDatastoreTest() = runBlocking{ - backupManager.updateDatastore( - UserData( - themingMode = FakeUsrPreferenceValues.themingMode, - enableDynamicTheme = FakeUsrPreferenceValues.enableDynamicTheme, - enableAmoledTheme = FakeUsrPreferenceValues.enableAmoledTheme, - customColor = FakeUsrPreferenceValues.customColor, - monetMode = FakeUsrPreferenceValues.monetMode, - startingScreen = FakeUsrPreferenceValues.startingScreen, - enableToolsExperiment = FakeUsrPreferenceValues.enableToolsExperiment, - enableVibrations = FakeUsrPreferenceValues.enableVibrations, - middleZero = FakeUsrPreferenceValues.middleZero, - acButton = FakeUsrPreferenceValues.acButton, - rpnMode = FakeUsrPreferenceValues.rpnMode, - precision = FakeUsrPreferenceValues.precision, - separator = FakeUsrPreferenceValues.separator, - outputFormat = FakeUsrPreferenceValues.outputFormat, - radianMode = FakeUsrPreferenceValues.radianMode, - partialHistoryView = FakeUsrPreferenceValues.partialHistoryView, - clearInputAfterEquals = FakeUsrPreferenceValues.clearInputAfterEquals, - latestLeftSide = FakeUsrPreferenceValues.latestLeftSide, - latestRightSide = FakeUsrPreferenceValues.latestRightSide, - shownUnitGroups = FakeUsrPreferenceValues.shownUnitGroups, - unitConverterFavoritesOnly = FakeUsrPreferenceValues.unitConverterFavoritesOnly, - unitConverterFormatTime = FakeUsrPreferenceValues.unitConverterFormatTime, - unitConverterSorting = FakeUsrPreferenceValues.unitConverterSorting, - unitsTable = units, - timeZoneTable = timeZones - ) - ) + fun testRestoreDatastore() = runBlocking{ + // Backup manager also saves the file to disk, but it is not tested here. + // There is probably no need to test if the data in app is valid since moshi will throw an + // exceptions if the data in app is invalid. For example, if unitConverterSorting is set to + // "Qwerty" (this sorting doesn't exist) there will be an exception. + backupManager.updateDatastore(fakeUserData) - val data = dataStore.data.first() - // TODO Wrong implementation, should test all - assertEquals(FakeUsrPreferenceValues.themingMode, data.getThemingMode()) + assertEquals(backupManager.userDataFromApp(), fakeUserData) } @Test - fun updateUnitsTableTest() = runBlocking { - backupManager.updateUnitsTable( - UserData( - themingMode = FakeUsrPreferenceValues.themingMode, - enableDynamicTheme = FakeUsrPreferenceValues.enableDynamicTheme, - enableAmoledTheme = FakeUsrPreferenceValues.enableAmoledTheme, - customColor = FakeUsrPreferenceValues.customColor, - monetMode = FakeUsrPreferenceValues.monetMode, - startingScreen = FakeUsrPreferenceValues.startingScreen, - enableToolsExperiment = FakeUsrPreferenceValues.enableToolsExperiment, - enableVibrations = FakeUsrPreferenceValues.enableVibrations, - middleZero = FakeUsrPreferenceValues.middleZero, - acButton = FakeUsrPreferenceValues.acButton, - rpnMode = FakeUsrPreferenceValues.rpnMode, - precision = FakeUsrPreferenceValues.precision, - separator = FakeUsrPreferenceValues.separator, - outputFormat = FakeUsrPreferenceValues.outputFormat, - radianMode = FakeUsrPreferenceValues.radianMode, - partialHistoryView = FakeUsrPreferenceValues.partialHistoryView, - clearInputAfterEquals = FakeUsrPreferenceValues.clearInputAfterEquals, - latestLeftSide = FakeUsrPreferenceValues.latestLeftSide, - latestRightSide = FakeUsrPreferenceValues.latestRightSide, - shownUnitGroups = FakeUsrPreferenceValues.shownUnitGroups, - unitConverterFavoritesOnly = FakeUsrPreferenceValues.unitConverterFavoritesOnly, - unitConverterFormatTime = FakeUsrPreferenceValues.unitConverterFormatTime, - unitConverterSorting = FakeUsrPreferenceValues.unitConverterSorting, - unitsTable = emptyList(), - timeZoneTable = timeZones + fun testRestoreCalculatorHistoryTable() = runBlocking { + // Data for import + val fakeUserData2 = fakeUserData.copy( + calculatorHistoryTable = listOf( + CalculatorHistoryEntity( + timestamp = System.currentTimeMillis(), + expression = "69+420", + result = "444" + ) ) ) + backupManager.updateCalculatorHistoryTable(fakeUserData2) - val data = FakeUnitsDao.getAllFlow().first() - // TODO Wrong implementation - assertEquals("$units | $data", units, data) + assertEquals(FakeCalculatorHistoryDao.getAllDescending().first(), fakeUserData2.calculatorHistoryTable) + } + + @Test + fun testRestoreUnitsTable() = runBlocking { + // Data for import + val fakeUserData2 = fakeUserData.copy( + unitsTable = listOf( + UnitsEntity( + unitId = "UnitId3", + isFavorite = false, + pairedUnitId = "Pair4", + frequency = 123 + ) + ) + ) + backupManager.updateUnitsTable(fakeUserData2) + + assertEquals(FakeUnitsDao.getAllFlow().first(), fakeUserData2.unitsTable) + } + + @Test + fun testRestoreTimeZonesTable() = runBlocking { + // Data for import + val fakeUserData2 = fakeUserData.copy( + timeZoneTable = listOf( + TimeZoneEntity( + id = "id3", + position = 123, + label = "label456" + ) + ) + ) + backupManager.updateTimeZonesTable(fakeUserData2) + + assertEquals(FakeTimeZoneDao.getFavorites().first(), fakeUserData2.timeZoneTable) } } diff --git a/data/backup/src/androidTest/java/com/sadellie/unitto/data/backup/FakeCalculatorHistoryDao.kt b/data/backup/src/androidTest/java/com/sadellie/unitto/data/backup/FakeCalculatorHistoryDao.kt new file mode 100644 index 00000000..e5de78df --- /dev/null +++ b/data/backup/src/androidTest/java/com/sadellie/unitto/data/backup/FakeCalculatorHistoryDao.kt @@ -0,0 +1,46 @@ +/* + * Unitto is a unit converter for Android + * Copyright (c) 2023 Elshan Agaev + * + * 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 . + */ + +package com.sadellie.unitto.data.backup + +import com.sadellie.unitto.data.database.CalculatorHistoryDao +import com.sadellie.unitto.data.database.CalculatorHistoryEntity +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow + +var calculatorHistory: List = listOf( + CalculatorHistoryEntity( + timestamp = System.currentTimeMillis(), + expression = "2+2", + result = "4" + ) +) + +object FakeCalculatorHistoryDao: CalculatorHistoryDao { + override fun getAllDescending(): Flow> = flow { + emit(calculatorHistory) + } + + override suspend fun insert(vararg historyEntity: CalculatorHistoryEntity) { + calculatorHistory += historyEntity + } + + override suspend fun clear() { + calculatorHistory = emptyList() + } +} diff --git a/data/backup/src/androidTest/java/com/sadellie/unitto/data/backup/FakeTimeZoneDao.kt b/data/backup/src/androidTest/java/com/sadellie/unitto/data/backup/FakeTimeZoneDao.kt index 25cb4ef3..25ec3a22 100644 --- a/data/backup/src/androidTest/java/com/sadellie/unitto/data/backup/FakeTimeZoneDao.kt +++ b/data/backup/src/androidTest/java/com/sadellie/unitto/data/backup/FakeTimeZoneDao.kt @@ -23,7 +23,7 @@ import com.sadellie.unitto.data.database.TimeZoneEntity import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow -val timeZones = listOf( +var timeZones = listOf( TimeZoneEntity( id = "id1", position = 9, @@ -37,18 +37,20 @@ val timeZones = listOf( ) object FakeTimeZoneDao: TimeZoneDao { - override fun getFavorites(): Flow> { - return flow { - emit(timeZones) - } + override fun getFavorites(): Flow> = flow { + emit(timeZones) } override fun getMaxPosition(): Int = 0 - override suspend fun insert(vararg timeZoneEntity: TimeZoneEntity) {} + override suspend fun insert(vararg timeZoneEntity: TimeZoneEntity) { + timeZones += timeZoneEntity + } override suspend fun removeFromFavorites(id: String) {} override suspend fun updateLabel(id: String, label: String) {} override suspend fun updateDragged(id: String, oldPosition: Int, newPosition: Int) {} override suspend fun moveDown(currentPosition: Int, targetPosition: Int) {} override suspend fun moveUp(currentPosition: Int, targetPosition: Int) {} - override suspend fun clear() {} + override suspend fun clear() { + timeZones = emptyList() + } } \ No newline at end of file diff --git a/data/backup/src/androidTest/java/com/sadellie/unitto/data/backup/FakeUnitsDao.kt b/data/backup/src/androidTest/java/com/sadellie/unitto/data/backup/FakeUnitsDao.kt index a1a6dfc0..9f8ad9a0 100644 --- a/data/backup/src/androidTest/java/com/sadellie/unitto/data/backup/FakeUnitsDao.kt +++ b/data/backup/src/androidTest/java/com/sadellie/unitto/data/backup/FakeUnitsDao.kt @@ -23,7 +23,7 @@ import com.sadellie.unitto.data.database.UnitsEntity import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow -val units = listOf( +var units = listOf( UnitsEntity( unitId = "UnitId1", isFavorite = false, @@ -45,7 +45,11 @@ object FakeUnitsDao : UnitsDao { } } - override suspend fun insertUnit(unit: UnitsEntity) {} + override suspend fun insertUnit(unit: UnitsEntity) { + units += unit + } override suspend fun getById(unitId: String): UnitsEntity? = null - override suspend fun clear() {} + override suspend fun clear() { + units = emptyList() + } } \ No newline at end of file diff --git a/data/backup/src/androidTest/java/com/sadellie/unitto/data/backup/FakeUserPreferencesRepository.kt b/data/backup/src/androidTest/java/com/sadellie/unitto/data/backup/FakeUserPreferencesRepository.kt index 0abb45bd..cec8d9d2 100644 --- a/data/backup/src/androidTest/java/com/sadellie/unitto/data/backup/FakeUserPreferencesRepository.kt +++ b/data/backup/src/androidTest/java/com/sadellie/unitto/data/backup/FakeUserPreferencesRepository.kt @@ -18,31 +18,31 @@ package com.sadellie.unitto.data.backup -import com.sadellie.unitto.data.model.ALL_UNIT_GROUPS -import com.sadellie.unitto.data.model.UnitsListSorting - -object FakeUsrPreferenceValues { - const val themingMode = "ThemingMode" - const val enableDynamicTheme = false - const val enableAmoledTheme = false - const val customColor = 777L - const val monetMode = "MonetMode" - const val startingScreen = "StartingScreen" - const val enableToolsExperiment = false - const val enableVibrations = false - const val middleZero = false - const val acButton = false - const val rpnMode = false - const val precision = 69 - const val separator = 1 - const val outputFormat = 1 - const val radianMode = false - const val partialHistoryView = false - const val clearInputAfterEquals = false - const val latestLeftSide = "LeftSideUnit" - const val latestRightSide = "RightSideUnit" - val shownUnitGroups = ALL_UNIT_GROUPS - const val unitConverterFavoritesOnly = false - const val unitConverterFormatTime = false - val unitConverterSorting = UnitsListSorting.USAGE -} +internal val fakeUserData = UserData( + themingMode = "AUTO", + enableDynamicTheme = false, + enableAmoledTheme = false, + customColor = 777L, + monetMode = "TonalSpot", + startingScreen = "calculator_route", + enableToolsExperiment = false, + systemFont = false, + enableVibrations = false, + middleZero = false, + acButton = false, + rpnMode = false, + precision = 11, + separator = 1, + outputFormat = 1, + radianMode = false, + partialHistoryView = false, + latestLeftSide = "kilometer", + latestRightSide = "mile", + shownUnitGroups = "LENGTH", + unitConverterFavoritesOnly = false, + unitConverterFormatTime = false, + unitConverterSorting = "USAGE", + calculatorHistoryTable = calculatorHistory, + unitsTable = units, + timeZoneTable = timeZones +) diff --git a/data/backup/src/main/java/com/sadellie/unitto/data/backup/BackupManager.kt b/data/backup/src/main/java/com/sadellie/unitto/data/backup/BackupManager.kt index c7d462a4..f7740480 100644 --- a/data/backup/src/main/java/com/sadellie/unitto/data/backup/BackupManager.kt +++ b/data/backup/src/main/java/com/sadellie/unitto/data/backup/BackupManager.kt @@ -99,7 +99,8 @@ class BackupManager @Inject constructor( } // Return error - val userData: UserData = jsonAdapter.fromJson(jsonContent.toString()) ?: return@withContext IllegalArgumentException("Can't parse: $jsonContent") + val userData: UserData = jsonAdapter.fromJson(jsonContent.toString()) + ?: return@withContext IllegalArgumentException("Can't parse: $jsonContent") // Apply tables updateCalculatorHistoryTable(userData)