summaryrefslogtreecommitdiff
path: root/tests/src/com/android/customization/model/grid
diff options
context:
space:
mode:
Diffstat (limited to 'tests/src/com/android/customization/model/grid')
-rw-r--r--tests/src/com/android/customization/model/grid/data/repository/FakeGridRepository.kt91
-rw-r--r--tests/src/com/android/customization/model/grid/domain/interactor/GridInteractorTest.kt146
-rw-r--r--tests/src/com/android/customization/model/grid/domain/interactor/GridSnapshotRestorerTest.kt111
-rw-r--r--tests/src/com/android/customization/model/grid/ui/viewmodel/GridScreenViewModelTest.kt110
4 files changed, 458 insertions, 0 deletions
diff --git a/tests/src/com/android/customization/model/grid/data/repository/FakeGridRepository.kt b/tests/src/com/android/customization/model/grid/data/repository/FakeGridRepository.kt
new file mode 100644
index 00000000..59539379
--- /dev/null
+++ b/tests/src/com/android/customization/model/grid/data/repository/FakeGridRepository.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.customization.model.grid.data.repository
+
+import com.android.customization.model.grid.shared.model.GridOptionItemModel
+import com.android.customization.model.grid.shared.model.GridOptionItemsModel
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+class FakeGridRepository(
+ private val scope: CoroutineScope,
+ initialOptionCount: Int,
+ var available: Boolean = true
+) : GridRepository {
+ private val _optionChanges =
+ MutableSharedFlow<Unit>(
+ replay = 1,
+ onBufferOverflow = BufferOverflow.DROP_OLDEST,
+ )
+
+ override suspend fun isAvailable(): Boolean = available
+
+ override fun getOptionChanges(): Flow<Unit> = _optionChanges.asSharedFlow()
+
+ private val selectedOptionIndex = MutableStateFlow(0)
+ private var options: GridOptionItemsModel = createOptions(count = initialOptionCount)
+
+ override suspend fun getOptions(): GridOptionItemsModel {
+ return options
+ }
+
+ fun setOptions(
+ count: Int,
+ selectedIndex: Int = 0,
+ ) {
+ options = createOptions(count, selectedIndex)
+ _optionChanges.tryEmit(Unit)
+ }
+
+ private fun createOptions(
+ count: Int,
+ selectedIndex: Int = 0,
+ ): GridOptionItemsModel {
+ selectedOptionIndex.value = selectedIndex
+ return GridOptionItemsModel.Loaded(
+ options =
+ buildList {
+ repeat(times = count) { index ->
+ add(
+ GridOptionItemModel(
+ name = "option_$index",
+ cols = 4,
+ rows = index * 2,
+ isSelected =
+ selectedOptionIndex
+ .map { it == index }
+ .stateIn(
+ scope = scope,
+ started = SharingStarted.Eagerly,
+ initialValue = false,
+ ),
+ onSelected = { selectedOptionIndex.value = index },
+ )
+ )
+ }
+ }
+ )
+ }
+}
diff --git a/tests/src/com/android/customization/model/grid/domain/interactor/GridInteractorTest.kt b/tests/src/com/android/customization/model/grid/domain/interactor/GridInteractorTest.kt
new file mode 100644
index 00000000..f73d5a38
--- /dev/null
+++ b/tests/src/com/android/customization/model/grid/domain/interactor/GridInteractorTest.kt
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.customization.model.grid.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.customization.model.grid.data.repository.FakeGridRepository
+import com.android.customization.model.grid.shared.model.GridOptionItemsModel
+import com.android.wallpaper.testing.FakeSnapshotStore
+import com.android.wallpaper.testing.collectLastValue
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class GridInteractorTest {
+
+ private lateinit var underTest: GridInteractor
+ private lateinit var testScope: TestScope
+ private lateinit var repository: FakeGridRepository
+ private lateinit var store: FakeSnapshotStore
+
+ @Before
+ fun setUp() {
+ testScope = TestScope()
+ repository =
+ FakeGridRepository(
+ scope = testScope.backgroundScope,
+ initialOptionCount = 3,
+ )
+ store = FakeSnapshotStore()
+ underTest =
+ GridInteractor(
+ applicationScope = testScope.backgroundScope,
+ repository = repository,
+ snapshotRestorer = {
+ GridSnapshotRestorer(
+ interactor = underTest,
+ )
+ .apply {
+ runBlocking {
+ setUpSnapshotRestorer(
+ store = store,
+ )
+ }
+ }
+ },
+ )
+ }
+
+ @Test
+ fun selectingOptionThroughModel_updatesOptions() =
+ testScope.runTest {
+ val options = collectLastValue(underTest.options)
+ assertThat(options()).isInstanceOf(GridOptionItemsModel.Loaded::class.java)
+ (options() as? GridOptionItemsModel.Loaded)?.let { loaded ->
+ assertThat(loaded.options).hasSize(3)
+ assertThat(loaded.options[0].isSelected.value).isTrue()
+ assertThat(loaded.options[1].isSelected.value).isFalse()
+ assertThat(loaded.options[2].isSelected.value).isFalse()
+ }
+
+ val storedSnapshot = store.retrieve()
+ (options() as? GridOptionItemsModel.Loaded)?.let { loaded ->
+ loaded.options[1].onSelected()
+ }
+
+ assertThat(options()).isInstanceOf(GridOptionItemsModel.Loaded::class.java)
+ (options() as? GridOptionItemsModel.Loaded)?.let { loaded ->
+ assertThat(loaded.options).hasSize(3)
+ assertThat(loaded.options[0].isSelected.value).isFalse()
+ assertThat(loaded.options[1].isSelected.value).isTrue()
+ assertThat(loaded.options[2].isSelected.value).isFalse()
+ }
+ assertThat(store.retrieve()).isNotEqualTo(storedSnapshot)
+ }
+
+ @Test
+ fun selectingOptionThroughSetter_returnsSelectedOptionFromGetter() =
+ testScope.runTest {
+ val options = collectLastValue(underTest.options)
+ assertThat(options()).isInstanceOf(GridOptionItemsModel.Loaded::class.java)
+ (options() as? GridOptionItemsModel.Loaded)?.let { loaded ->
+ assertThat(loaded.options).hasSize(3)
+ }
+
+ val storedSnapshot = store.retrieve()
+ (options() as? GridOptionItemsModel.Loaded)?.let { loaded ->
+ underTest.setSelectedOption(loaded.options[1])
+ runCurrent()
+ assertThat(underTest.getSelectedOption()?.name).isEqualTo(loaded.options[1].name)
+ assertThat(store.retrieve()).isNotEqualTo(storedSnapshot)
+ }
+ }
+
+ @Test
+ fun externalUpdates_reloadInvoked() =
+ testScope.runTest {
+ val options = collectLastValue(underTest.options)
+ assertThat(options()).isInstanceOf(GridOptionItemsModel.Loaded::class.java)
+ (options() as? GridOptionItemsModel.Loaded)?.let { loaded ->
+ assertThat(loaded.options).hasSize(3)
+ }
+
+ val storedSnapshot = store.retrieve()
+ repository.setOptions(4)
+
+ assertThat(options()).isInstanceOf(GridOptionItemsModel.Loaded::class.java)
+ (options() as? GridOptionItemsModel.Loaded)?.let { loaded ->
+ assertThat(loaded.options).hasSize(4)
+ }
+ // External updates do not record a new snapshot with the undo system.
+ assertThat(store.retrieve()).isEqualTo(storedSnapshot)
+ }
+
+ @Test
+ fun unavailableRepository_emptyOptions() =
+ testScope.runTest {
+ repository.available = false
+ val options = collectLastValue(underTest.options)
+ assertThat(options()).isNull()
+ }
+}
diff --git a/tests/src/com/android/customization/model/grid/domain/interactor/GridSnapshotRestorerTest.kt b/tests/src/com/android/customization/model/grid/domain/interactor/GridSnapshotRestorerTest.kt
new file mode 100644
index 00000000..c2712b1d
--- /dev/null
+++ b/tests/src/com/android/customization/model/grid/domain/interactor/GridSnapshotRestorerTest.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.customization.model.grid.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.customization.model.grid.data.repository.FakeGridRepository
+import com.android.customization.model.grid.shared.model.GridOptionItemsModel
+import com.android.wallpaper.testing.FakeSnapshotStore
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class GridSnapshotRestorerTest {
+
+ private lateinit var underTest: GridSnapshotRestorer
+ private lateinit var testScope: TestScope
+ private lateinit var repository: FakeGridRepository
+ private lateinit var store: FakeSnapshotStore
+
+ @Before
+ fun setUp() {
+ testScope = TestScope()
+ repository =
+ FakeGridRepository(
+ scope = testScope.backgroundScope,
+ initialOptionCount = 4,
+ )
+ underTest =
+ GridSnapshotRestorer(
+ interactor =
+ GridInteractor(
+ applicationScope = testScope.backgroundScope,
+ repository = repository,
+ snapshotRestorer = { underTest },
+ )
+ )
+ store = FakeSnapshotStore()
+ }
+
+ @Test
+ fun restoreToSnapshot_noCallsToStore_restoresToInitialSnapshot() =
+ testScope.runTest {
+ runCurrent()
+ val initialSnapshot = underTest.setUpSnapshotRestorer(store = store)
+ assertThat(initialSnapshot.args).isNotEmpty()
+ repository.setOptions(
+ count = 4,
+ selectedIndex = 2,
+ )
+ runCurrent()
+ assertThat(getSelectedIndex()).isEqualTo(2)
+
+ underTest.restoreToSnapshot(initialSnapshot)
+ runCurrent()
+
+ assertThat(getSelectedIndex()).isEqualTo(0)
+ }
+
+ @Test
+ fun restoreToSnapshot_withCallToStore_restoresToInitialSnapshot() =
+ testScope.runTest {
+ runCurrent()
+ val initialSnapshot = underTest.setUpSnapshotRestorer(store = store)
+ assertThat(initialSnapshot.args).isNotEmpty()
+ repository.setOptions(
+ count = 4,
+ selectedIndex = 2,
+ )
+ runCurrent()
+ assertThat(getSelectedIndex()).isEqualTo(2)
+ underTest.store((repository.getOptions() as GridOptionItemsModel.Loaded).options[1])
+ runCurrent()
+
+ underTest.restoreToSnapshot(initialSnapshot)
+ runCurrent()
+
+ assertThat(getSelectedIndex()).isEqualTo(0)
+ }
+
+ private suspend fun getSelectedIndex(): Int {
+ return (repository.getOptions() as? GridOptionItemsModel.Loaded)?.options?.indexOfFirst {
+ optionItem ->
+ optionItem.isSelected.value
+ }
+ ?: -1
+ }
+}
diff --git a/tests/src/com/android/customization/model/grid/ui/viewmodel/GridScreenViewModelTest.kt b/tests/src/com/android/customization/model/grid/ui/viewmodel/GridScreenViewModelTest.kt
new file mode 100644
index 00000000..58c5d99f
--- /dev/null
+++ b/tests/src/com/android/customization/model/grid/ui/viewmodel/GridScreenViewModelTest.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.customization.model.grid.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.customization.model.grid.data.repository.FakeGridRepository
+import com.android.customization.model.grid.domain.interactor.GridInteractor
+import com.android.customization.model.grid.domain.interactor.GridSnapshotRestorer
+import com.android.wallpaper.picker.option.ui.viewmodel.OptionItemViewModel
+import com.android.wallpaper.testing.FakeSnapshotStore
+import com.android.wallpaper.testing.collectLastValue
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class GridScreenViewModelTest {
+
+ private lateinit var underTest: GridScreenViewModel
+ private lateinit var testScope: TestScope
+ private lateinit var interactor: GridInteractor
+ private lateinit var store: FakeSnapshotStore
+
+ @Before
+ fun setUp() {
+ testScope = TestScope()
+ store = FakeSnapshotStore()
+ interactor =
+ GridInteractor(
+ applicationScope = testScope.backgroundScope,
+ repository =
+ FakeGridRepository(
+ scope = testScope.backgroundScope,
+ initialOptionCount = 4,
+ ),
+ snapshotRestorer = {
+ GridSnapshotRestorer(
+ interactor = interactor,
+ )
+ .apply { runBlocking { setUpSnapshotRestorer(store) } }
+ }
+ )
+
+ underTest =
+ GridScreenViewModel(
+ context = InstrumentationRegistry.getInstrumentation().targetContext,
+ interactor = interactor,
+ )
+ }
+
+ @Test
+ @Ignore("b/270371382")
+ fun clickOnItem_itGetsSelected() =
+ testScope.runTest {
+ val optionItemsValueProvider = collectLastValue(underTest.optionItems)
+ var optionItemsValue = checkNotNull(optionItemsValueProvider.invoke())
+ assertThat(optionItemsValue).hasSize(4)
+ assertThat(getSelectedIndex(optionItemsValue)).isEqualTo(0)
+ assertThat(getOnClick(optionItemsValue[0])).isNull()
+
+ val item1OnClickedValue = getOnClick(optionItemsValue[1])
+ assertThat(item1OnClickedValue).isNotNull()
+ item1OnClickedValue?.invoke()
+
+ optionItemsValue = checkNotNull(optionItemsValueProvider.invoke())
+ assertThat(optionItemsValue).hasSize(4)
+ assertThat(getSelectedIndex(optionItemsValue)).isEqualTo(1)
+ assertThat(getOnClick(optionItemsValue[0])).isNotNull()
+ assertThat(getOnClick(optionItemsValue[1])).isNull()
+ }
+
+ private fun TestScope.getSelectedIndex(
+ optionItems: List<OptionItemViewModel<GridIconViewModel>>
+ ): Int {
+ return optionItems.indexOfFirst { optionItem ->
+ collectLastValue(optionItem.isSelected).invoke() == true
+ }
+ }
+
+ private fun TestScope.getOnClick(
+ optionItem: OptionItemViewModel<GridIconViewModel>
+ ): (() -> Unit)? {
+ return collectLastValue(optionItem.onClicked).invoke()
+ }
+}