Loading app/src/test/java/foundation/e/apps/data/install/updates/ManualUpdateChainStoreTest.kt 0 → 100644 +165 −0 Original line number Diff line number Diff line package foundation.e.apps.data.install.updates import android.content.Context import androidx.test.core.app.ApplicationProvider import com.google.gson.Gson import com.google.gson.GsonBuilder import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.enums.Source import foundation.e.apps.data.enums.Type import foundation.e.apps.data.installation.model.SharedLib import foundation.e.apps.domain.model.install.Status import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @OptIn(ExperimentalCoroutinesApi::class) @RunWith(RobolectricTestRunner::class) class ManualUpdateChainStoreTest { private lateinit var context: Context private lateinit var store: ManualUpdateChainStore @Before fun setUp() { context = ApplicationProvider.getApplicationContext() store = ManualUpdateChainStore(context, testGson()) } @After fun tearDown() = runTest { store.clearSnapshot(CHAIN_ID) store.clearSnapshot(OTHER_CHAIN_ID) } @Test fun writeSnapshot_readsBackMatchingSnapshot() = runTest { val snapshot = createSnapshot(chainId = CHAIN_ID, cursor = 1) store.writeSnapshot(snapshot) val storedSnapshot = store.readSnapshot(CHAIN_ID) assertEquals(snapshot.chainId, storedSnapshot?.chainId) assertEquals(snapshot.cursor, storedSnapshot?.cursor) assertEquals(snapshot.packages.map { it.package_name }, storedSnapshot?.packages?.map { it.package_name }) assertNull(store.readSnapshot(OTHER_CHAIN_ID)) } @Test fun writeSnapshot_preservesApplicationFieldsUsedByManualUpdateChain() = runTest { val snapshot = createSnapshot(chainId = CHAIN_ID, cursor = 1) store.writeSnapshot(snapshot) val storedSnapshot = store.readSnapshot(CHAIN_ID) assertNotNull(storedSnapshot) assertEquals(snapshot.createdAtMillis, storedSnapshot?.createdAtMillis) assertApplicationFields(snapshot.packages[0], storedSnapshot!!.packages[0]) assertApplicationFields(snapshot.packages[1], storedSnapshot.packages[1]) assertApplicationFields(snapshot.packages[2], storedSnapshot.packages[2]) } @Test fun advanceSnapshot_updatesCursor_andCapsAtPackageCount() = runTest { val snapshot = createSnapshot(chainId = CHAIN_ID, cursor = 1) store.writeSnapshot(snapshot) val advancedSnapshot = store.advanceSnapshot(CHAIN_ID, consumedCount = 10) assertNotNull(advancedSnapshot) assertEquals(3, advancedSnapshot?.cursor) assertEquals(3, store.readSnapshot(CHAIN_ID)?.cursor) assertApplicationFields(snapshot.packages[0], advancedSnapshot!!.packages[0]) } @Test fun clearSnapshot_removesOnlyMatchingChain() = runTest { store.writeSnapshot(createSnapshot(chainId = CHAIN_ID)) store.clearSnapshot(OTHER_CHAIN_ID) assertNotNull(store.readSnapshot(CHAIN_ID)) store.clearSnapshot(CHAIN_ID) assertNull(store.readSnapshot(CHAIN_ID)) } private fun createSnapshot( chainId: String, cursor: Int = 0, ): ManualUpdateChainSnapshot { return ManualUpdateChainSnapshot( chainId = chainId, cursor = cursor, createdAtMillis = 1234L, packages = listOf( createApplication("one"), createApplication("two"), createApplication("three"), ), ) } private fun createApplication(packageSuffix: String): Application { return Application( _id = "app-$packageSuffix", name = "App $packageSuffix", package_name = "foundation.e.apps.$packageSuffix", source = Source.PLAY_STORE, status = Status.UPDATABLE, type = Type.NATIVE, icon_image_path = "icons/$packageSuffix", latest_version_code = 42L, offer_type = 1, isFree = true, originalSize = 4096L, url = "https://example.com/$packageSuffix.apk", isSystemApp = false, dependentLibraries = listOf( SharedLib( packageName = "foundation.e.apps.lib.$packageSuffix", versionCode = 7L, offerType = 2, downloadUrls = listOf("https://example.com/lib-$packageSuffix.apk"), downloadIds = mapOf(123L to true), ) ), ) } private fun assertApplicationFields( expected: Application, actual: Application, ) { assertEquals(expected._id, actual._id) assertEquals(expected.name, actual.name) assertEquals(expected.package_name, actual.package_name) assertEquals(expected.source, actual.source) assertEquals(expected.status, actual.status) assertEquals(expected.type, actual.type) assertEquals(expected.icon_image_path, actual.icon_image_path) assertEquals(expected.latest_version_code, actual.latest_version_code) assertEquals(expected.offer_type, actual.offer_type) assertEquals(expected.isFree, actual.isFree) assertEquals(expected.originalSize, actual.originalSize) assertEquals(expected.url, actual.url) assertEquals(expected.isSystemApp, actual.isSystemApp) assertEquals(expected.dependentLibraries, actual.dependentLibraries) } private fun testGson(): Gson { return GsonBuilder() .enableComplexMapKeySerialization() .create() } companion object { private const val CHAIN_ID = "chain-1" private const val OTHER_CHAIN_ID = "chain-2" } } app/src/test/java/foundation/e/apps/data/install/updates/UpdatesWorkManagerTest.kt +29 −3 Original line number Diff line number Diff line Loading @@ -62,13 +62,17 @@ class UpdatesWorkManagerTest { @Test fun startUpdateAllWork_buildsExpectedOneTimeRequest() { UpdatesWorkManager.startUpdateAllWork(context) UpdatesWorkManager.startUpdateAllWork(context, CHAIN_ID) val workInfo = getActiveUniqueWorkInfo("updates_work_user") val workSpec = getWorkSpec(workInfo.id) assertThat(workInfo.tags).contains(UpdatesWorkManager.TAG_WORK_USER_INITIATED_UPDATE) assertThat(workSpec.input.getBoolean(UpdatesWorker.IS_AUTO_UPDATE, true)).isFalse() assertThat(workSpec.input.getString(UpdatesWorkManager.INPUT_KEY_CHAIN_ID)).isEqualTo(CHAIN_ID) assertThat( workSpec.input.getBoolean(UpdatesWorkManager.INPUT_KEY_IS_MANUAL_CHAIN_CONTINUATION, true) ).isFalse() assertThat(workSpec.constraints.requiredNetworkType).isEqualTo(NetworkType.CONNECTED) assertThat(workSpec.expedited).isTrue() assertThat(workSpec.outOfQuotaPolicy) Loading @@ -77,10 +81,10 @@ class UpdatesWorkManagerTest { @Test fun startUpdateAllWork_replacesExistingUniqueWork() { UpdatesWorkManager.startUpdateAllWork(context) UpdatesWorkManager.startUpdateAllWork(context, CHAIN_ID) val firstWorkId = getActiveUniqueWorkInfo("updates_work_user").id UpdatesWorkManager.startUpdateAllWork(context) UpdatesWorkManager.startUpdateAllWork(context, "$CHAIN_ID-next") val allWorkInfos = workManager.getWorkInfosForUniqueWork("updates_work_user").get() val activeWorkInfos = allWorkInfos.filter { !it.state.isFinished } Loading @@ -89,6 +93,24 @@ class UpdatesWorkManagerTest { assertThat(activeWorkInfos.single().id).isNotEqualTo(firstWorkId) } @Test fun appendUpdateAllWork_appendsContinuationRequestWithManualTag() { UpdatesWorkManager.startUpdateAllWork(context, CHAIN_ID) UpdatesWorkManager.appendUpdateAllWork(context, CHAIN_ID) val workInfos = workManager.getWorkInfosForUniqueWork("updates_work_user").get() val continuationWorkInfo = workInfos.single { it.state == WorkInfo.State.BLOCKED } val continuationWorkSpec = getWorkSpec(continuationWorkInfo.id) assertThat(continuationWorkInfo.tags).contains(UpdatesWorkManager.TAG_WORK_USER_INITIATED_UPDATE) assertThat(continuationWorkSpec.input.getString(UpdatesWorkManager.INPUT_KEY_CHAIN_ID)) .isEqualTo(CHAIN_ID) assertThat( continuationWorkSpec.input.getBoolean(UpdatesWorkManager.INPUT_KEY_IS_MANUAL_CHAIN_CONTINUATION, false) ).isTrue() } @Test fun enqueueWork_buildsExpectedPeriodicRequest() { UpdatesWorkManager.enqueueWork(context, interval = 6, ExistingPeriodicWorkPolicy.REPLACE) Loading Loading @@ -132,4 +154,8 @@ class UpdatesWorkManagerTest { val workManagerImpl = WorkManagerImpl.getInstance(context) return requireNotNull(workManagerImpl.workDatabase.workSpecDao().getWorkSpec(workId.toString())) } companion object { private const val CHAIN_ID = "chain-1" } } app/src/test/java/foundation/e/apps/data/install/updates/UpdatesWorkerTest.kt +391 −3 File changed.Preview size limit exceeded, changes collapsed. Show changes app/src/test/java/foundation/e/apps/ui/updates/UpdatesFragmentTest.kt 0 → 100644 +93 −0 Original line number Diff line number Diff line package foundation.e.apps.ui.updates import android.content.Context import android.view.LayoutInflater import android.view.ContextThemeWrapper import androidx.test.core.app.ApplicationProvider import androidx.work.WorkInfo import foundation.e.apps.R import foundation.e.apps.data.application.data.Application import foundation.e.apps.databinding.FragmentUpdatesBinding import foundation.e.apps.domain.model.install.Status import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.mock import org.mockito.kotlin.whenever import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) class UpdatesFragmentTest { @Test fun updateButtonAvailability_disablesButton_whenManualChainWorkIsActive() { val fragment = createFragment() setPrivateField(fragment, "displayedUpdates", listOf(Application(status = Status.UPDATABLE))) setPrivateField(fragment, "userUpdateWorkInfos", listOf(createWorkInfo(WorkInfo.State.RUNNING))) invokePrivateMethod(fragment, "updateButtonAvailability") assertFalse(readBinding(fragment).button.isEnabled) } @Test fun updateAllClick_doesNothing_whenManualChainWorkIsAlreadyActive() { val fragment = createFragment() val binding = readBinding(fragment) setPrivateField(fragment, "displayedUpdates", listOf(Application(status = Status.UPDATABLE))) setPrivateField(fragment, "userUpdateWorkInfos", listOf(createWorkInfo(WorkInfo.State.ENQUEUED))) binding.button.isEnabled = true invokePrivateMethod(fragment, "initUpdateAllButton") binding.button.performClick() assertTrue(binding.button.isEnabled) } private fun createFragment(): UpdatesFragment { val fragment = UpdatesFragment() val context = ApplicationProvider.getApplicationContext<Context>() val themedContext = ContextThemeWrapper(context, R.style.Theme_Apps) val binding = FragmentUpdatesBinding.inflate(LayoutInflater.from(themedContext)) setPrivateField(fragment, "_binding", binding) setPrivateField(fragment, "displayedUpdates", emptyList<Application>()) setPrivateField(fragment, "periodicUpdateWorkInfos", emptyList<WorkInfo>()) setPrivateField(fragment, "userUpdateWorkInfos", emptyList<WorkInfo>()) setPrivateField(fragment, "taggedInstallWorkInfos", emptyList<WorkInfo>()) setPrivateField(fragment, "legacyInstallWorkInfos", emptyList<WorkInfo>()) return fragment } private fun createWorkInfo(state: WorkInfo.State): WorkInfo { return mock<WorkInfo>().also { workInfo -> whenever(workInfo.state).thenReturn(state) } } private fun readBinding(fragment: UpdatesFragment): FragmentUpdatesBinding { return readPrivateField(fragment, "_binding") } private fun invokePrivateMethod(target: Any, methodName: String) { target.javaClass.getDeclaredMethod(methodName).apply { isAccessible = true invoke(target) } } private fun setPrivateField(target: Any, fieldName: String, value: Any?) { target.javaClass.getDeclaredField(fieldName).apply { isAccessible = true set(target, value) } } @Suppress("UNCHECKED_CAST") private fun <T> readPrivateField(target: Any, fieldName: String): T { return target.javaClass.getDeclaredField(fieldName).apply { isAccessible = true }.get(target) as T } } app/src/test/java/foundation/e/apps/ui/updates/UpdatesViewModelTest.kt +0 −3 Original line number Diff line number Diff line Loading @@ -2,7 +2,6 @@ package foundation.e.apps.ui.updates import androidx.arch.core.executor.testing.InstantTaskExecutorRule import foundation.e.apps.data.Stores import foundation.e.apps.data.application.ApplicationRepository import foundation.e.apps.data.application.UpdatesDao import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.enums.ResultStatus Loading Loading @@ -60,7 +59,6 @@ class UpdatesViewModelTest { @get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule() private val applicationRepository by lazy { mock<ApplicationRepository>() } private val updatesManagerImpl by lazy { mock<UpdatesManagerImpl>() } private val sessionRepository by lazy { mock<SessionRepository>() } private val stores by lazy { mock<Stores>() } Loading Loading @@ -99,7 +97,6 @@ class UpdatesViewModelTest { updatesViewModel = UpdatesViewModel( updatesManagerRepository = updatesManagerRepository, applicationRepository = applicationRepository, sessionRepository = sessionRepository, stores = stores, appPreferencesRepository = appPreferencesRepository Loading Loading
app/src/test/java/foundation/e/apps/data/install/updates/ManualUpdateChainStoreTest.kt 0 → 100644 +165 −0 Original line number Diff line number Diff line package foundation.e.apps.data.install.updates import android.content.Context import androidx.test.core.app.ApplicationProvider import com.google.gson.Gson import com.google.gson.GsonBuilder import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.enums.Source import foundation.e.apps.data.enums.Type import foundation.e.apps.data.installation.model.SharedLib import foundation.e.apps.domain.model.install.Status import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @OptIn(ExperimentalCoroutinesApi::class) @RunWith(RobolectricTestRunner::class) class ManualUpdateChainStoreTest { private lateinit var context: Context private lateinit var store: ManualUpdateChainStore @Before fun setUp() { context = ApplicationProvider.getApplicationContext() store = ManualUpdateChainStore(context, testGson()) } @After fun tearDown() = runTest { store.clearSnapshot(CHAIN_ID) store.clearSnapshot(OTHER_CHAIN_ID) } @Test fun writeSnapshot_readsBackMatchingSnapshot() = runTest { val snapshot = createSnapshot(chainId = CHAIN_ID, cursor = 1) store.writeSnapshot(snapshot) val storedSnapshot = store.readSnapshot(CHAIN_ID) assertEquals(snapshot.chainId, storedSnapshot?.chainId) assertEquals(snapshot.cursor, storedSnapshot?.cursor) assertEquals(snapshot.packages.map { it.package_name }, storedSnapshot?.packages?.map { it.package_name }) assertNull(store.readSnapshot(OTHER_CHAIN_ID)) } @Test fun writeSnapshot_preservesApplicationFieldsUsedByManualUpdateChain() = runTest { val snapshot = createSnapshot(chainId = CHAIN_ID, cursor = 1) store.writeSnapshot(snapshot) val storedSnapshot = store.readSnapshot(CHAIN_ID) assertNotNull(storedSnapshot) assertEquals(snapshot.createdAtMillis, storedSnapshot?.createdAtMillis) assertApplicationFields(snapshot.packages[0], storedSnapshot!!.packages[0]) assertApplicationFields(snapshot.packages[1], storedSnapshot.packages[1]) assertApplicationFields(snapshot.packages[2], storedSnapshot.packages[2]) } @Test fun advanceSnapshot_updatesCursor_andCapsAtPackageCount() = runTest { val snapshot = createSnapshot(chainId = CHAIN_ID, cursor = 1) store.writeSnapshot(snapshot) val advancedSnapshot = store.advanceSnapshot(CHAIN_ID, consumedCount = 10) assertNotNull(advancedSnapshot) assertEquals(3, advancedSnapshot?.cursor) assertEquals(3, store.readSnapshot(CHAIN_ID)?.cursor) assertApplicationFields(snapshot.packages[0], advancedSnapshot!!.packages[0]) } @Test fun clearSnapshot_removesOnlyMatchingChain() = runTest { store.writeSnapshot(createSnapshot(chainId = CHAIN_ID)) store.clearSnapshot(OTHER_CHAIN_ID) assertNotNull(store.readSnapshot(CHAIN_ID)) store.clearSnapshot(CHAIN_ID) assertNull(store.readSnapshot(CHAIN_ID)) } private fun createSnapshot( chainId: String, cursor: Int = 0, ): ManualUpdateChainSnapshot { return ManualUpdateChainSnapshot( chainId = chainId, cursor = cursor, createdAtMillis = 1234L, packages = listOf( createApplication("one"), createApplication("two"), createApplication("three"), ), ) } private fun createApplication(packageSuffix: String): Application { return Application( _id = "app-$packageSuffix", name = "App $packageSuffix", package_name = "foundation.e.apps.$packageSuffix", source = Source.PLAY_STORE, status = Status.UPDATABLE, type = Type.NATIVE, icon_image_path = "icons/$packageSuffix", latest_version_code = 42L, offer_type = 1, isFree = true, originalSize = 4096L, url = "https://example.com/$packageSuffix.apk", isSystemApp = false, dependentLibraries = listOf( SharedLib( packageName = "foundation.e.apps.lib.$packageSuffix", versionCode = 7L, offerType = 2, downloadUrls = listOf("https://example.com/lib-$packageSuffix.apk"), downloadIds = mapOf(123L to true), ) ), ) } private fun assertApplicationFields( expected: Application, actual: Application, ) { assertEquals(expected._id, actual._id) assertEquals(expected.name, actual.name) assertEquals(expected.package_name, actual.package_name) assertEquals(expected.source, actual.source) assertEquals(expected.status, actual.status) assertEquals(expected.type, actual.type) assertEquals(expected.icon_image_path, actual.icon_image_path) assertEquals(expected.latest_version_code, actual.latest_version_code) assertEquals(expected.offer_type, actual.offer_type) assertEquals(expected.isFree, actual.isFree) assertEquals(expected.originalSize, actual.originalSize) assertEquals(expected.url, actual.url) assertEquals(expected.isSystemApp, actual.isSystemApp) assertEquals(expected.dependentLibraries, actual.dependentLibraries) } private fun testGson(): Gson { return GsonBuilder() .enableComplexMapKeySerialization() .create() } companion object { private const val CHAIN_ID = "chain-1" private const val OTHER_CHAIN_ID = "chain-2" } }
app/src/test/java/foundation/e/apps/data/install/updates/UpdatesWorkManagerTest.kt +29 −3 Original line number Diff line number Diff line Loading @@ -62,13 +62,17 @@ class UpdatesWorkManagerTest { @Test fun startUpdateAllWork_buildsExpectedOneTimeRequest() { UpdatesWorkManager.startUpdateAllWork(context) UpdatesWorkManager.startUpdateAllWork(context, CHAIN_ID) val workInfo = getActiveUniqueWorkInfo("updates_work_user") val workSpec = getWorkSpec(workInfo.id) assertThat(workInfo.tags).contains(UpdatesWorkManager.TAG_WORK_USER_INITIATED_UPDATE) assertThat(workSpec.input.getBoolean(UpdatesWorker.IS_AUTO_UPDATE, true)).isFalse() assertThat(workSpec.input.getString(UpdatesWorkManager.INPUT_KEY_CHAIN_ID)).isEqualTo(CHAIN_ID) assertThat( workSpec.input.getBoolean(UpdatesWorkManager.INPUT_KEY_IS_MANUAL_CHAIN_CONTINUATION, true) ).isFalse() assertThat(workSpec.constraints.requiredNetworkType).isEqualTo(NetworkType.CONNECTED) assertThat(workSpec.expedited).isTrue() assertThat(workSpec.outOfQuotaPolicy) Loading @@ -77,10 +81,10 @@ class UpdatesWorkManagerTest { @Test fun startUpdateAllWork_replacesExistingUniqueWork() { UpdatesWorkManager.startUpdateAllWork(context) UpdatesWorkManager.startUpdateAllWork(context, CHAIN_ID) val firstWorkId = getActiveUniqueWorkInfo("updates_work_user").id UpdatesWorkManager.startUpdateAllWork(context) UpdatesWorkManager.startUpdateAllWork(context, "$CHAIN_ID-next") val allWorkInfos = workManager.getWorkInfosForUniqueWork("updates_work_user").get() val activeWorkInfos = allWorkInfos.filter { !it.state.isFinished } Loading @@ -89,6 +93,24 @@ class UpdatesWorkManagerTest { assertThat(activeWorkInfos.single().id).isNotEqualTo(firstWorkId) } @Test fun appendUpdateAllWork_appendsContinuationRequestWithManualTag() { UpdatesWorkManager.startUpdateAllWork(context, CHAIN_ID) UpdatesWorkManager.appendUpdateAllWork(context, CHAIN_ID) val workInfos = workManager.getWorkInfosForUniqueWork("updates_work_user").get() val continuationWorkInfo = workInfos.single { it.state == WorkInfo.State.BLOCKED } val continuationWorkSpec = getWorkSpec(continuationWorkInfo.id) assertThat(continuationWorkInfo.tags).contains(UpdatesWorkManager.TAG_WORK_USER_INITIATED_UPDATE) assertThat(continuationWorkSpec.input.getString(UpdatesWorkManager.INPUT_KEY_CHAIN_ID)) .isEqualTo(CHAIN_ID) assertThat( continuationWorkSpec.input.getBoolean(UpdatesWorkManager.INPUT_KEY_IS_MANUAL_CHAIN_CONTINUATION, false) ).isTrue() } @Test fun enqueueWork_buildsExpectedPeriodicRequest() { UpdatesWorkManager.enqueueWork(context, interval = 6, ExistingPeriodicWorkPolicy.REPLACE) Loading Loading @@ -132,4 +154,8 @@ class UpdatesWorkManagerTest { val workManagerImpl = WorkManagerImpl.getInstance(context) return requireNotNull(workManagerImpl.workDatabase.workSpecDao().getWorkSpec(workId.toString())) } companion object { private const val CHAIN_ID = "chain-1" } }
app/src/test/java/foundation/e/apps/data/install/updates/UpdatesWorkerTest.kt +391 −3 File changed.Preview size limit exceeded, changes collapsed. Show changes
app/src/test/java/foundation/e/apps/ui/updates/UpdatesFragmentTest.kt 0 → 100644 +93 −0 Original line number Diff line number Diff line package foundation.e.apps.ui.updates import android.content.Context import android.view.LayoutInflater import android.view.ContextThemeWrapper import androidx.test.core.app.ApplicationProvider import androidx.work.WorkInfo import foundation.e.apps.R import foundation.e.apps.data.application.data.Application import foundation.e.apps.databinding.FragmentUpdatesBinding import foundation.e.apps.domain.model.install.Status import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.mock import org.mockito.kotlin.whenever import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) class UpdatesFragmentTest { @Test fun updateButtonAvailability_disablesButton_whenManualChainWorkIsActive() { val fragment = createFragment() setPrivateField(fragment, "displayedUpdates", listOf(Application(status = Status.UPDATABLE))) setPrivateField(fragment, "userUpdateWorkInfos", listOf(createWorkInfo(WorkInfo.State.RUNNING))) invokePrivateMethod(fragment, "updateButtonAvailability") assertFalse(readBinding(fragment).button.isEnabled) } @Test fun updateAllClick_doesNothing_whenManualChainWorkIsAlreadyActive() { val fragment = createFragment() val binding = readBinding(fragment) setPrivateField(fragment, "displayedUpdates", listOf(Application(status = Status.UPDATABLE))) setPrivateField(fragment, "userUpdateWorkInfos", listOf(createWorkInfo(WorkInfo.State.ENQUEUED))) binding.button.isEnabled = true invokePrivateMethod(fragment, "initUpdateAllButton") binding.button.performClick() assertTrue(binding.button.isEnabled) } private fun createFragment(): UpdatesFragment { val fragment = UpdatesFragment() val context = ApplicationProvider.getApplicationContext<Context>() val themedContext = ContextThemeWrapper(context, R.style.Theme_Apps) val binding = FragmentUpdatesBinding.inflate(LayoutInflater.from(themedContext)) setPrivateField(fragment, "_binding", binding) setPrivateField(fragment, "displayedUpdates", emptyList<Application>()) setPrivateField(fragment, "periodicUpdateWorkInfos", emptyList<WorkInfo>()) setPrivateField(fragment, "userUpdateWorkInfos", emptyList<WorkInfo>()) setPrivateField(fragment, "taggedInstallWorkInfos", emptyList<WorkInfo>()) setPrivateField(fragment, "legacyInstallWorkInfos", emptyList<WorkInfo>()) return fragment } private fun createWorkInfo(state: WorkInfo.State): WorkInfo { return mock<WorkInfo>().also { workInfo -> whenever(workInfo.state).thenReturn(state) } } private fun readBinding(fragment: UpdatesFragment): FragmentUpdatesBinding { return readPrivateField(fragment, "_binding") } private fun invokePrivateMethod(target: Any, methodName: String) { target.javaClass.getDeclaredMethod(methodName).apply { isAccessible = true invoke(target) } } private fun setPrivateField(target: Any, fieldName: String, value: Any?) { target.javaClass.getDeclaredField(fieldName).apply { isAccessible = true set(target, value) } } @Suppress("UNCHECKED_CAST") private fun <T> readPrivateField(target: Any, fieldName: String): T { return target.javaClass.getDeclaredField(fieldName).apply { isAccessible = true }.get(target) as T } }
app/src/test/java/foundation/e/apps/ui/updates/UpdatesViewModelTest.kt +0 −3 Original line number Diff line number Diff line Loading @@ -2,7 +2,6 @@ package foundation.e.apps.ui.updates import androidx.arch.core.executor.testing.InstantTaskExecutorRule import foundation.e.apps.data.Stores import foundation.e.apps.data.application.ApplicationRepository import foundation.e.apps.data.application.UpdatesDao import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.enums.ResultStatus Loading Loading @@ -60,7 +59,6 @@ class UpdatesViewModelTest { @get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule() private val applicationRepository by lazy { mock<ApplicationRepository>() } private val updatesManagerImpl by lazy { mock<UpdatesManagerImpl>() } private val sessionRepository by lazy { mock<SessionRepository>() } private val stores by lazy { mock<Stores>() } Loading Loading @@ -99,7 +97,6 @@ class UpdatesViewModelTest { updatesViewModel = UpdatesViewModel( updatesManagerRepository = updatesManagerRepository, applicationRepository = applicationRepository, sessionRepository = sessionRepository, stores = stores, appPreferencesRepository = appPreferencesRepository Loading