From fc588798413487148d0eb4805a1457ad6168a164 Mon Sep 17 00:00:00 2001 From: Paul Klauser Date: Mon, 3 Jun 2024 16:26:47 -0400 Subject: [PATCH 1/2] Remove unused categoryPages field This was "going to" solve a problem that was solved elsewhere, and can be removed. --- .../settings/EditCategoriesViewModel.kt | 7 --- .../settings/EditCategoriesViewModelTest.kt | 45 ------------------- 2 files changed, 52 deletions(-) diff --git a/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesViewModel.kt b/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesViewModel.kt index 7e0cde26..a2b707be 100644 --- a/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesViewModel.kt +++ b/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesViewModel.kt @@ -8,9 +8,7 @@ import androidx.lifecycle.viewModelScope import com.willowtree.vocable.ICategoriesUseCase import com.willowtree.vocable.presets.Category import com.willowtree.vocable.room.CategorySortOrder -import com.willowtree.vocable.settings.editcategories.EditCategoriesPage import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch class EditCategoriesViewModel( @@ -19,11 +17,6 @@ class EditCategoriesViewModel( val categoryList: LiveData> = categoriesUseCase.categories().asLiveData() - val categoryPages = categoriesUseCase.categories().map { categories -> - val pageSize = 8 - categories.chunked(pageSize).map { EditCategoriesPage(it) } - } - private val liveLastViewedIndex = MutableLiveData() val lastViewedIndex: LiveData = liveLastViewedIndex diff --git a/app/src/test/java/com/willowtree/vocable/settings/EditCategoriesViewModelTest.kt b/app/src/test/java/com/willowtree/vocable/settings/EditCategoriesViewModelTest.kt index 6d522a5e..8208be11 100644 --- a/app/src/test/java/com/willowtree/vocable/settings/EditCategoriesViewModelTest.kt +++ b/app/src/test/java/com/willowtree/vocable/settings/EditCategoriesViewModelTest.kt @@ -5,10 +5,7 @@ import com.willowtree.vocable.FakeCategoriesUseCase import com.willowtree.vocable.MainDispatcherRule import com.willowtree.vocable.getOrAwaitValue import com.willowtree.vocable.presets.createStoredCategory -import com.willowtree.vocable.settings.editcategories.EditCategoriesPage -import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.update -import kotlinx.coroutines.test.runTest import org.junit.Assert.assertEquals import org.junit.Rule import org.junit.Test @@ -113,46 +110,4 @@ class EditCategoriesViewModelTest { ) } - @Test - fun `category pages are populated`() = runTest { - categoriesUseCase._categories.update { - listOf( - createStoredCategory(categoryId = "1"), - createStoredCategory(categoryId = "2"), - createStoredCategory(categoryId = "3"), - createStoredCategory(categoryId = "4"), - createStoredCategory(categoryId = "5"), - createStoredCategory(categoryId = "6"), - createStoredCategory(categoryId = "7"), - createStoredCategory(categoryId = "8"), - createStoredCategory(categoryId = "9"), - ) - } - val vm = createViewModel() - vm.refreshCategories() - - assertEquals( - listOf( - EditCategoriesPage( - listOf( - createStoredCategory(categoryId = "1"), - createStoredCategory(categoryId = "2"), - createStoredCategory(categoryId = "3"), - createStoredCategory(categoryId = "4"), - createStoredCategory(categoryId = "5"), - createStoredCategory(categoryId = "6"), - createStoredCategory(categoryId = "7"), - createStoredCategory(categoryId = "8"), - ) - ), - EditCategoriesPage( - listOf( - createStoredCategory(categoryId = "9"), - ) - ) - ), - vm.categoryPages.first() - ) - } - } \ No newline at end of file From d17dbf84598ce7541935b92969969079b47f07d9 Mon Sep 17 00:00:00 2001 From: Paul Klauser Date: Mon, 3 Jun 2024 16:31:34 -0400 Subject: [PATCH 2/2] Migrate categoryList to Flow This is going to make testing this ViewModel easier --- app/build.gradle | 2 + .../settings/EditCategoriesFragment.kt | 14 +++- .../settings/EditCategoriesListFragment.kt | 38 +++++---- .../settings/EditCategoriesViewModel.kt | 3 +- .../settings/EditCategoriesViewModelTest.kt | 79 ++++++++++--------- gradle/libs.versions.toml | 5 ++ 6 files changed, 83 insertions(+), 58 deletions(-) create mode 100644 gradle/libs.versions.toml diff --git a/app/build.gradle b/app/build.gradle index 97754b2a..d9645f85 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -113,6 +113,7 @@ dependencies { testImplementation 'junit:junit:4.13.2' testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" testImplementation "androidx.arch.core:core-testing:2.2.0" + testImplementation(libs.turbine) androidTestImplementation "androidx.room:room-testing:2.2.5" androidTestImplementation 'androidx.test:runner:1.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' @@ -123,6 +124,7 @@ dependencies { androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" androidTestImplementation 'androidx.test:rules:1.4.0' androidTestImplementation "androidx.arch.core:core-testing:2.2.0" + androidTestImplementation(libs.turbine) // Testing Navigation androidTestImplementation "androidx.navigation:navigation-testing:2.3.1" diff --git a/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesFragment.kt b/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesFragment.kt index bf5ba55d..c840c491 100644 --- a/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesFragment.kt +++ b/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesFragment.kt @@ -4,6 +4,9 @@ import android.os.Bundle import android.view.View import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController import androidx.viewpager2.widget.ViewPager2 import com.willowtree.vocable.BaseFragment @@ -13,6 +16,7 @@ import com.willowtree.vocable.R import com.willowtree.vocable.databinding.FragmentEditCategoriesBinding import com.willowtree.vocable.presets.Category import com.willowtree.vocable.utils.VocableFragmentStateAdapter +import kotlinx.coroutines.launch import org.koin.androidx.viewmodel.ViewModelOwner import org.koin.androidx.viewmodel.ext.android.viewModel import kotlin.math.min @@ -107,9 +111,13 @@ class EditCategoriesFragment : BaseFragment() { } private fun subscribeToViewModel() { - editCategoriesViewModel.categoryList.observe(viewLifecycleOwner) { - it?.let { categories -> - categoriesAdapter.setItems(categories) + viewLifecycleOwner.lifecycleScope.launch { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + editCategoriesViewModel.categoryList.collect { + it.let { categories -> + categoriesAdapter.setItems(categories) + } + } } } diff --git a/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesListFragment.kt b/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesListFragment.kt index 0ed08a0b..155df677 100644 --- a/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesListFragment.kt +++ b/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesListFragment.kt @@ -4,6 +4,9 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController import com.willowtree.vocable.BaseFragment import com.willowtree.vocable.BindingInflater @@ -12,6 +15,7 @@ import com.willowtree.vocable.databinding.CategoryEditButtonBinding import com.willowtree.vocable.databinding.FragmentEditCategoriesListBinding import com.willowtree.vocable.presets.Category import com.willowtree.vocable.utils.locale.LocalizedResourceUtility +import kotlinx.coroutines.launch import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ViewModelOwner import org.koin.androidx.viewmodel.ext.android.viewModel @@ -101,22 +105,26 @@ class EditCategoriesListFragment : BaseFragment - list?.let { overallList -> - val hiddenCategories = overallList.filter { it.hidden } - if (endPosition > overallList.size) { - endPosition = overallList.size - 1 - } - if (startPosition <= endPosition) { - overallList.subList(startPosition, endPosition) - .forEachIndexed { index, category -> - bindCategoryEditButton( - editButtonList[index], - category, - startPosition + index, - overallList.size - hiddenCategories.size - ) + viewLifecycleOwner.lifecycleScope.launch { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + editCategoriesViewModel.categoryList.collect { list -> + list.let { overallList -> + val hiddenCategories = overallList.filter { it.hidden } + if (endPosition > overallList.size) { + endPosition = overallList.size - 1 + } + if (startPosition <= endPosition) { + overallList.subList(startPosition, endPosition) + .forEachIndexed { index, category -> + bindCategoryEditButton( + editButtonList[index], + category, + startPosition + index, + overallList.size - hiddenCategories.size + ) + } } + } } } } diff --git a/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesViewModel.kt b/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesViewModel.kt index a2b707be..e8b7338d 100644 --- a/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesViewModel.kt +++ b/app/src/main/java/com/willowtree/vocable/settings/EditCategoriesViewModel.kt @@ -3,7 +3,6 @@ package com.willowtree.vocable.settings import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import com.willowtree.vocable.ICategoriesUseCase import com.willowtree.vocable.presets.Category @@ -15,7 +14,7 @@ class EditCategoriesViewModel( private val categoriesUseCase: ICategoriesUseCase ) : ViewModel() { - val categoryList: LiveData> = categoriesUseCase.categories().asLiveData() + val categoryList = categoriesUseCase.categories() private val liveLastViewedIndex = MutableLiveData() val lastViewedIndex: LiveData = liveLastViewedIndex diff --git a/app/src/test/java/com/willowtree/vocable/settings/EditCategoriesViewModelTest.kt b/app/src/test/java/com/willowtree/vocable/settings/EditCategoriesViewModelTest.kt index 8208be11..85fecc1a 100644 --- a/app/src/test/java/com/willowtree/vocable/settings/EditCategoriesViewModelTest.kt +++ b/app/src/test/java/com/willowtree/vocable/settings/EditCategoriesViewModelTest.kt @@ -1,11 +1,11 @@ package com.willowtree.vocable.settings -import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import app.cash.turbine.test import com.willowtree.vocable.FakeCategoriesUseCase import com.willowtree.vocable.MainDispatcherRule -import com.willowtree.vocable.getOrAwaitValue import com.willowtree.vocable.presets.createStoredCategory import kotlinx.coroutines.flow.update +import kotlinx.coroutines.test.runTest import org.junit.Assert.assertEquals import org.junit.Rule import org.junit.Test @@ -15,9 +15,6 @@ class EditCategoriesViewModelTest { @get:Rule val mainDispatcherRule = MainDispatcherRule() - @get:Rule - val instantTaskExecutorRule = InstantTaskExecutorRule() - private val categoriesUseCase = FakeCategoriesUseCase() private fun createViewModel(): EditCategoriesViewModel { @@ -27,7 +24,7 @@ class EditCategoriesViewModelTest { } @Test - fun `categories are populated`() { + fun `categories are populated`() = runTest { categoriesUseCase._categories.update { listOf( createStoredCategory(categoryId = "1") @@ -36,16 +33,18 @@ class EditCategoriesViewModelTest { val vm = createViewModel() vm.refreshCategories() - assertEquals( - listOf( - createStoredCategory(categoryId = "1") - ), - vm.categoryList.getOrAwaitValue() - ) + vm.categoryList.test { + assertEquals( + listOf( + createStoredCategory(categoryId = "1") + ), + awaitItem() + ) + } } @Test - fun `move category up`() { + fun `move category up`() = runTest { categoriesUseCase._categories.update { listOf( createStoredCategory( @@ -62,23 +61,25 @@ class EditCategoriesViewModelTest { vm.refreshCategories() vm.moveCategoryUp("2") - assertEquals( - listOf( - createStoredCategory( - categoryId = "2", - sortOrder = 0 + vm.categoryList.test { + assertEquals( + listOf( + createStoredCategory( + categoryId = "2", + sortOrder = 0 + ), + createStoredCategory( + categoryId = "1", + sortOrder = 1 + ) ), - createStoredCategory( - categoryId = "1", - sortOrder = 1 - ) - ), - vm.categoryList.getOrAwaitValue() - ) + awaitItem() + ) + } } @Test - fun `move category down`() { + fun `move category down`() = runTest { categoriesUseCase._categories.update { listOf( createStoredCategory( @@ -95,19 +96,21 @@ class EditCategoriesViewModelTest { vm.refreshCategories() vm.moveCategoryDown("1") - assertEquals( - listOf( - createStoredCategory( - categoryId = "2", - sortOrder = 0 + vm.categoryList.test { + assertEquals( + listOf( + createStoredCategory( + categoryId = "2", + sortOrder = 0 + ), + createStoredCategory( + categoryId = "1", + sortOrder = 1 + ) ), - createStoredCategory( - categoryId = "1", - sortOrder = 1 - ) - ), - vm.categoryList.getOrAwaitValue() - ) + awaitItem() + ) + } } } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 00000000..0421dfe6 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,5 @@ +[versions] +turbine = "1.1.0" + +[libraries] +turbine = { group = "app.cash.turbine", name = "turbine", version.ref = "turbine" } \ No newline at end of file