blob: 6b8f7dfecfe2e8e498409fe8b25cf2e9b2b47fbc [file] [log] [blame]
/*
* 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.wallpaper.picker.customization.ui.viewmodel
import androidx.test.filters.SmallTest
import com.android.wallpaper.picker.customization.data.content.FakeWallpaperClient
import com.android.wallpaper.picker.customization.data.repository.WallpaperRepository
import com.android.wallpaper.picker.customization.domain.interactor.WallpaperInteractor
import com.android.wallpaper.picker.customization.domain.interactor.WallpaperSnapshotRestorer
import com.android.wallpaper.picker.customization.shared.model.WallpaperDestination
import com.android.wallpaper.picker.customization.shared.model.WallpaperModel
import com.android.wallpaper.testing.FakeSnapshotStore
import com.android.wallpaper.testing.collectLastValue
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.junit.After
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 WallpaperQuickSwitchViewModelTest {
private lateinit var underTest: WallpaperQuickSwitchViewModel
private lateinit var client: FakeWallpaperClient
private lateinit var testScope: TestScope
private lateinit var snapshotRestorer: WallpaperSnapshotRestorer
@Before
fun setUp() {
client = FakeWallpaperClient()
val testDispatcher = StandardTestDispatcher()
Dispatchers.setMain(testDispatcher)
testScope = TestScope(testDispatcher)
val interactor =
WallpaperInteractor(
repository =
WallpaperRepository(
scope = testScope.backgroundScope,
client = client,
backgroundDispatcher = testDispatcher,
),
snapshotRestorer = { snapshotRestorer },
)
underTest =
WallpaperQuickSwitchViewModel(
interactor = interactor,
maxOptions = FakeWallpaperClient.INITIAL_RECENT_WALLPAPERS.size,
)
snapshotRestorer =
WallpaperSnapshotRestorer(
interactor = interactor,
)
runBlocking { snapshotRestorer.setUpSnapshotRestorer(FakeSnapshotStore()) }
}
@After
fun tearDown() {
Dispatchers.resetMain()
}
@Test
fun `initial options`() =
testScope.runTest {
val options = collectLastValue(underTest.options)
assertOptions(
observed = options(),
expected = expectations(),
)
}
@Test
fun `updates options`() =
testScope.runTest {
val options = collectLastValue(underTest.options)
val models =
listOf(
WallpaperModel(
wallpaperId = "aaa",
placeholderColor = 1200,
),
WallpaperModel(
wallpaperId = "bbb",
placeholderColor = 1300,
),
WallpaperModel(
wallpaperId = "ccc",
placeholderColor = 1400,
),
)
client.setRecentWallpapers(WallpaperDestination.HOME, models)
assertOptions(
observed = options(),
expected =
expectations(
models = models,
),
)
}
@Test
fun `switches to third option`() =
testScope.runTest {
val options = collectLastValue(underTest.options)
// Pause the client so we can examine the interim state.
client.pause()
val selectedIndex = 2
val optionToSelect = checkNotNull(options()?.get(selectedIndex))
val onSelected = collectLastValue(optionToSelect.onSelected)
onSelected()?.invoke()
assertOptions(
observed = options(),
expected =
expectations(
selectingIndex = selectedIndex,
),
)
// Unpause the client so we can examine the final state.
client.unpause()
runCurrent()
assertOptions(
observed = options(),
expected =
expectations(
selectedIndex = selectedIndex,
),
)
}
@Test
fun `switches between screens`() =
testScope.runTest {
val options = collectLastValue(underTest.options)
// We begin on the home screen by default.
// Select option at index 2 on the home screen.
val selectedIndex = 2
val optionToSelect = checkNotNull(options()?.get(selectedIndex))
val onSelected = collectLastValue(optionToSelect.onSelected)
onSelected()?.invoke()
runCurrent()
assertOptions(
observed = options(),
expected =
expectations(
selectedIndex = selectedIndex,
),
)
// Switch to the lock screen, it should still have the original option selected.
underTest.setOnLockScreen(isLockScreenSelected = true)
runCurrent()
assertOptions(
observed = options(),
expected = expectations(),
)
// Switch back to the home screen, it should still have option at index 2 selected.
underTest.setOnLockScreen(isLockScreenSelected = false)
runCurrent()
assertOptions(
observed = options(),
expected =
expectations(
models =
listOf(
FakeWallpaperClient.INITIAL_RECENT_WALLPAPERS[2],
FakeWallpaperClient.INITIAL_RECENT_WALLPAPERS[0],
FakeWallpaperClient.INITIAL_RECENT_WALLPAPERS[1],
),
),
)
}
private fun expectations(
models: List<WallpaperModel> = FakeWallpaperClient.INITIAL_RECENT_WALLPAPERS,
selectedIndex: Int = 0,
selectingIndex: Int? = null,
): List<ExpectedOption> {
return models.mapIndexed { index, model ->
val nothingBeingSelected = selectingIndex == null
val isBeingSelected = selectingIndex == index
val isSelected = selectedIndex == index
ExpectedOption(
wallpaperId = model.wallpaperId,
placeholderColor = model.placeholderColor,
isLarge = isBeingSelected || (nothingBeingSelected && isSelected),
isSelectionIconVisible = nothingBeingSelected && isSelected,
isSelectionBorderVisible = isBeingSelected || (nothingBeingSelected && isSelected),
isProgressIndicatorVisible = isBeingSelected,
isSelectable =
(!nothingBeingSelected && !isBeingSelected) ||
(nothingBeingSelected && !isSelected),
)
}
}
private fun TestScope.assertOptions(
observed: List<WallpaperQuickSwitchOptionViewModel>?,
expected: List<ExpectedOption>,
) {
checkNotNull(observed)
assertThat(observed).hasSize(expected.size)
observed.forEachIndexed { index, option ->
assertWithMessage("mismatching wallpaperId for index $index.")
.that(option.wallpaperId)
.isEqualTo(expected[index].wallpaperId)
assertWithMessage("mismatching isLarge for index $index.")
.that(collectLastValue(option.isLarge)())
.isEqualTo(expected[index].isLarge)
assertWithMessage("mismatching placeholderColor for index $index.")
.that(option.placeholderColor)
.isEqualTo(expected[index].placeholderColor)
assertWithMessage("mismatching isProgressIndicatorVisible for index $index.")
.that(collectLastValue(option.isProgressIndicatorVisible)())
.isEqualTo(expected[index].isProgressIndicatorVisible)
assertWithMessage("mismatching isSelectionIconVisible for index $index.")
.that(collectLastValue(option.isSelectionIconVisible)())
.isEqualTo(expected[index].isSelectionIconVisible)
assertWithMessage("mismatching isSelectionBorderVisible for index $index.")
.that(collectLastValue(option.isSelectionBorderVisible)())
.isEqualTo(expected[index].isSelectionBorderVisible)
assertWithMessage("mismatching isSelectable for index $index.")
.that(collectLastValue(option.onSelected)() != null)
.isEqualTo(expected[index].isSelectable)
}
}
private data class ExpectedOption(
val wallpaperId: String,
val placeholderColor: Int,
val isLarge: Boolean = false,
val isProgressIndicatorVisible: Boolean = false,
val isSelectionIconVisible: Boolean = false,
val isSelectionBorderVisible: Boolean = false,
val isSelectable: Boolean = true,
)
}