Merge "Import translations. DO NOT MERGE ANYWHERE" into main
diff --git a/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModelTest.kt b/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModelTest.kt
new file mode 100644
index 0000000..677bafe
--- /dev/null
+++ b/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModelTest.kt
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2024 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.customization.ui.viewmodel
+
+import android.content.Context
+import android.stats.style.StyleEnums
+import androidx.test.filters.SmallTest
+import com.android.customization.module.logging.TestThemesUserEventLogger
+import com.android.customization.picker.clock.data.repository.FakeClockPickerRepository
+import com.android.customization.picker.clock.domain.interactor.ClockPickerInteractor
+import com.android.customization.picker.clock.domain.interactor.ClockPickerSnapshotRestorer
+import com.android.customization.picker.clock.shared.ClockSize
+import com.android.customization.picker.clock.shared.model.ClockMetadataModel
+import com.android.customization.picker.clock.ui.viewmodel.ClockColorViewModel
+import com.android.customization.picker.clock.ui.viewmodel.ClockSettingsViewModel
+import com.android.customization.picker.color.data.repository.FakeColorPickerRepository
+import com.android.customization.picker.color.domain.interactor.ColorPickerInteractor
+import com.android.customization.picker.color.domain.interactor.ColorPickerSnapshotRestorer
+import com.android.wallpaper.testing.FakeSnapshotStore
+import com.android.wallpaper.testing.collectLastValue
+import com.google.common.truth.Truth.assertThat
+import dagger.hilt.android.qualifiers.ApplicationContext
+import dagger.hilt.android.testing.HiltAndroidRule
+import dagger.hilt.android.testing.HiltAndroidTest
+import javax.inject.Inject
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.TestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@HiltAndroidTest
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(RobolectricTestRunner::class)
+class ClockPickerViewModelTest {
+
+    private val logger = TestThemesUserEventLogger()
+
+    @get:Rule var hiltRule = HiltAndroidRule(this)
+    @Inject @ApplicationContext lateinit var context: Context
+    @Inject lateinit var testDispatcher: TestDispatcher
+    @Inject lateinit var testScope: TestScope
+
+    private lateinit var colorMap: Map<String, ClockColorViewModel>
+    private lateinit var underTest: ClockPickerViewModel
+
+    @Before
+    fun setUp() {
+        hiltRule.inject()
+        Dispatchers.setMain(testDispatcher)
+        val repository = FakeClockPickerRepository()
+        val clockPickerInteractor =
+            ClockPickerInteractor(
+                repository = repository,
+                snapshotRestorer =
+                    ClockPickerSnapshotRestorer(repository = repository).apply {
+                        runBlocking { setUpSnapshotRestorer(store = FakeSnapshotStore()) }
+                    },
+            )
+        val colorPickerRepository = FakeColorPickerRepository(context = context)
+        val colorPickerInteractor =
+            ColorPickerInteractor(
+                repository = colorPickerRepository,
+                snapshotRestorer =
+                    ColorPickerSnapshotRestorer(repository = colorPickerRepository).apply {
+                        runBlocking { setUpSnapshotRestorer(store = FakeSnapshotStore()) }
+                    },
+            )
+        colorMap = ClockColorViewModel.getPresetColorMap(context.resources)
+        underTest =
+            ClockPickerViewModel(
+                context = context,
+                resources = context.resources,
+                clockPickerInteractor = clockPickerInteractor,
+                colorPickerInteractor = colorPickerInteractor,
+                logger = logger,
+                backgroundDispatcher = testDispatcher,
+                viewModelScope = testScope,
+            )
+
+        testScope.launch {
+            clockPickerInteractor.setSelectedClock(FakeClockPickerRepository.CLOCK_ID_0)
+        }
+    }
+
+    @After
+    fun tearDown() {
+        Dispatchers.resetMain()
+    }
+
+    @Test
+    fun setTab() = runTest {
+        val tab = collectLastValue(underTest.selectedTab)
+        underTest.setTab(ClockPickerViewModel.Tab.STYLE)
+        assertThat(tab()).isEqualTo(ClockPickerViewModel.Tab.STYLE)
+        underTest.setTab(ClockPickerViewModel.Tab.COLOR)
+        assertThat(tab()).isEqualTo(ClockPickerViewModel.Tab.COLOR)
+        underTest.setTab(ClockPickerViewModel.Tab.SIZE)
+        assertThat(tab()).isEqualTo(ClockPickerViewModel.Tab.SIZE)
+    }
+
+    @Test
+    fun setClockStyle() = runTest {
+        val clockStyleOptions = collectLastValue(underTest.clockStyleOptions)
+        // Advance CLOCKS_EVENT_UPDATE_DELAY_MILLIS since there is a delay from clockStyleOptions
+        advanceTimeBy(ClockPickerViewModel.CLOCKS_EVENT_UPDATE_DELAY_MILLIS)
+        val option0IsSelected = collectLastValue(clockStyleOptions()!![0].isSelected)
+        val option0OnClicked = collectLastValue(clockStyleOptions()!![0].onClicked)
+        assertThat(option0IsSelected()).isTrue()
+        assertThat(option0OnClicked()).isNull()
+
+        val option1OnClickedBefore = collectLastValue(clockStyleOptions()!![1].onClicked)
+        option1OnClickedBefore()?.invoke()
+        // Advance CLOCKS_EVENT_UPDATE_DELAY_MILLIS since there is a delay from clockColorOptions
+        advanceTimeBy(ClockPickerViewModel.CLOCKS_EVENT_UPDATE_DELAY_MILLIS)
+        val option1IsSelected = collectLastValue(clockStyleOptions()!![1].isSelected)
+        val option1OnClickedAfter = collectLastValue(clockStyleOptions()!![1].onClicked)
+        assertThat(option0IsSelected()).isFalse()
+        assertThat(option1IsSelected()).isTrue()
+        assertThat(option1OnClickedAfter()).isNull()
+    }
+
+    @Test
+    fun setSelectedColor() = runTest {
+        val clockColorOptions = collectLastValue(underTest.clockColorOptions)
+        val observedSliderProgress = collectLastValue(underTest.sliderProgress)
+        val observedSeedColor = collectLastValue(underTest.seedColor)
+        // Advance COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS since there is a delay from
+        // clockColorOptions
+        advanceTimeBy(ClockPickerViewModel.COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS)
+        val option0IsSelected = collectLastValue(clockColorOptions()!![0].isSelected)
+        val option0OnClicked = collectLastValue(clockColorOptions()!![0].onClicked)
+        assertThat(option0IsSelected()).isTrue()
+        assertThat(option0OnClicked()).isNull()
+
+        val option1OnClickedBefore = collectLastValue(clockColorOptions()!![1].onClicked)
+        option1OnClickedBefore()?.invoke()
+        // Advance COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS since there is a delay from
+        // clockColorOptions
+        advanceTimeBy(ClockPickerViewModel.COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS)
+        val option1IsSelected = collectLastValue(clockColorOptions()!![1].isSelected)
+        val option1OnClickedAfter = collectLastValue(clockColorOptions()!![1].onClicked)
+        assertThat(option0IsSelected()).isFalse()
+        assertThat(option1IsSelected()).isTrue()
+        assertThat(option1OnClickedAfter()).isNull()
+        assertThat(observedSliderProgress())
+            .isEqualTo(ClockMetadataModel.DEFAULT_COLOR_TONE_PROGRESS)
+        val expectedSelectedColorModel = colorMap.values.first() // RED
+        assertThat(observedSeedColor())
+            .isEqualTo(
+                ClockSettingsViewModel.blendColorWithTone(
+                    expectedSelectedColorModel.color,
+                    expectedSelectedColorModel.getColorTone(
+                        ClockMetadataModel.DEFAULT_COLOR_TONE_PROGRESS
+                    ),
+                )
+            )
+    }
+
+    @Test
+    fun setColorTone() = runTest {
+        val clockColorOptions = collectLastValue(underTest.clockColorOptions)
+        val observedIsSliderEnabled = collectLastValue(underTest.isSliderEnabled)
+        val observedSliderProgress = collectLastValue(underTest.sliderProgress)
+        val observedSeedColor = collectLastValue(underTest.seedColor)
+        // Advance COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS since there is a delay from
+        // clockColorOptions
+        advanceTimeBy(ClockPickerViewModel.COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS)
+        val option0IsSelected = collectLastValue(clockColorOptions()!![0].isSelected)
+        assertThat(option0IsSelected()).isTrue()
+        assertThat(observedIsSliderEnabled()).isFalse()
+
+        val option1OnClicked = collectLastValue(clockColorOptions()!![1].onClicked)
+        option1OnClicked()?.invoke()
+
+        // Advance COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS since there is a delay from
+        // clockColorOptions
+        advanceTimeBy(ClockPickerViewModel.COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS)
+        assertThat(observedIsSliderEnabled()).isTrue()
+        val targetProgress1 = 99
+        underTest.onSliderProgressChanged(targetProgress1)
+        assertThat(observedSliderProgress()).isEqualTo(targetProgress1)
+        val targetProgress2 = 55
+        testScope.launch { underTest.onSliderProgressStop(targetProgress2) }
+        assertThat(observedSliderProgress()).isEqualTo(targetProgress2)
+        val expectedSelectedColorModel = colorMap.values.first() // RED
+        assertThat(observedSeedColor())
+            .isEqualTo(
+                ClockSettingsViewModel.blendColorWithTone(
+                    expectedSelectedColorModel.color,
+                    expectedSelectedColorModel.getColorTone(targetProgress2),
+                )
+            )
+    }
+
+    @Test
+    fun getIsReactiveToTone() = runTest {
+        val clockColorOptions = collectLastValue(underTest.clockColorOptions)
+        val isSliderEnabled = collectLastValue(underTest.isSliderEnabled)
+        // Advance COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS since there is a delay from
+        // clockColorOptions
+        advanceTimeBy(ClockPickerViewModel.COLOR_OPTIONS_EVENT_UPDATE_DELAY_MILLIS)
+        val option1OnClicked = collectLastValue(clockColorOptions()!![1].onClicked)
+        option1OnClicked()?.invoke()
+
+        underTest.setSelectedClock(FakeClockPickerRepository.CLOCK_ID_0)
+        assertThat(isSliderEnabled()).isTrue()
+
+        underTest.setSelectedClock(FakeClockPickerRepository.CLOCK_ID_3)
+        assertThat(isSliderEnabled()).isFalse()
+    }
+
+    @Test
+    fun setClockSize() = runTest {
+        val selectedClockSize = collectLastValue(underTest.selectedClockSize)
+        underTest.setClockSize(ClockSize.DYNAMIC)
+        assertThat(selectedClockSize()).isEqualTo(ClockSize.DYNAMIC)
+        assertThat(logger.getLoggedClockSize()).isEqualTo(StyleEnums.CLOCK_SIZE_DYNAMIC)
+
+        underTest.setClockSize(ClockSize.SMALL)
+        assertThat(selectedClockSize()).isEqualTo(ClockSize.SMALL)
+        assertThat(logger.getLoggedClockSize()).isEqualTo(StyleEnums.CLOCK_SIZE_SMALL)
+    }
+}