1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package com.google.jetpackcamera.settings
17
18 import android.content.Context
19 import androidx.datastore.core.DataStore
20 import androidx.datastore.core.DataStoreFactory
21 import androidx.datastore.dataStoreFile
22 import androidx.test.core.app.ApplicationProvider
23 import androidx.test.platform.app.InstrumentationRegistry
24 import com.google.common.truth.Truth.assertThat
25 import com.google.jetpackcamera.settings.model.DarkMode
26 import com.google.jetpackcamera.settings.model.LensFacing
27 import com.google.jetpackcamera.settings.model.TYPICAL_SYSTEM_CONSTRAINTS
28 import java.io.File
29 import junit.framework.TestCase.assertEquals
30 import kotlinx.coroutines.CoroutineScope
31 import kotlinx.coroutines.Dispatchers
32 import kotlinx.coroutines.ExperimentalCoroutinesApi
33 import kotlinx.coroutines.SupervisorJob
34 import kotlinx.coroutines.cancel
35 import kotlinx.coroutines.flow.first
36 import kotlinx.coroutines.test.StandardTestDispatcher
37 import kotlinx.coroutines.test.advanceUntilIdle
38 import kotlinx.coroutines.test.runTest
39 import kotlinx.coroutines.test.setMain
40 import org.junit.After
41 import org.junit.Before
42 import org.junit.Test
43
44 @OptIn(ExperimentalCoroutinesApi::class)
45 internal class CameraAppSettingsViewModelTest {
46 private val testContext: Context = InstrumentationRegistry.getInstrumentation().targetContext
47 private lateinit var testDataStore: DataStore<JcaSettings>
48 private lateinit var datastoreScope: CoroutineScope
49 private lateinit var settingsViewModel: SettingsViewModel
50
51 @Before
<lambda>null52 fun setup() = runTest(StandardTestDispatcher()) {
53 Dispatchers.setMain(StandardTestDispatcher())
54 datastoreScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
55
56 testDataStore = DataStoreFactory.create(
57 serializer = JcaSettingsSerializer,
58 scope = datastoreScope
59 ) {
60 testContext.dataStoreFile("test_jca_settings.pb")
61 }
62 val settingsRepository = LocalSettingsRepository(testDataStore)
63 val constraintsRepository = SettableConstraintsRepositoryImpl().apply {
64 updateSystemConstraints(TYPICAL_SYSTEM_CONSTRAINTS)
65 }
66 settingsViewModel = SettingsViewModel(settingsRepository, constraintsRepository)
67 advanceUntilIdle()
68 }
69
70 @After
tearDownnull71 fun tearDown() {
72 File(
73 ApplicationProvider.getApplicationContext<Context>().filesDir,
74 "datastore"
75 ).deleteRecursively()
76
77 datastoreScope.cancel()
78 }
79
80 @Test
<lambda>null81 fun getSettingsUiState() = runTest(StandardTestDispatcher()) {
82 val uiState = settingsViewModel.settingsUiState.first {
83 it is SettingsUiState.Enabled
84 }
85
86 assertThat(uiState).isEqualTo(
87 TYPICAL_SETTINGS_UISTATE
88 )
89 }
90
91 @Test
<lambda>null92 fun setDefaultToFrontCamera() = runTest(StandardTestDispatcher()) {
93 // Wait for first Enabled state
94 val initialState = settingsViewModel.settingsUiState.first {
95 it is SettingsUiState.Enabled
96 }
97
98 val initialCameraLensFacing =
99 assertIsEnabled(initialState).lensFlipUiState.currentLensFacing
100 val nextCameraLensFacing = if (initialCameraLensFacing == LensFacing.FRONT) {
101 LensFacing.BACK
102 } else {
103 LensFacing.FRONT
104 }
105 settingsViewModel.setDefaultLensFacing(nextCameraLensFacing)
106
107 advanceUntilIdle()
108
109 assertIsEnabled(settingsViewModel.settingsUiState.value).also {
110 assertThat(it.lensFlipUiState.currentLensFacing).isEqualTo(nextCameraLensFacing)
111 }
112 }
113
114 @Test
<lambda>null115 fun setDarkMode() = runTest(StandardTestDispatcher()) {
116 // Wait for first Enabled state
117 val initialState = settingsViewModel.settingsUiState.first {
118 it is SettingsUiState.Enabled
119 }
120
121 val initialDarkMode =
122 (assertIsEnabled(initialState).darkModeUiState as DarkModeUiState.Enabled)
123 .currentDarkMode
124
125 settingsViewModel.setDarkMode(DarkMode.DARK)
126
127 advanceUntilIdle()
128
129 val newDarkMode =
130 (
131 assertIsEnabled(settingsViewModel.settingsUiState.value)
132 .darkModeUiState as DarkModeUiState.Enabled
133 )
134 .currentDarkMode
135
136 assertEquals(initialDarkMode, DarkMode.SYSTEM)
137 assertEquals(DarkMode.DARK, newDarkMode)
138 }
139 }
140
assertIsEnablednull141 private fun assertIsEnabled(settingsUiState: SettingsUiState): SettingsUiState.Enabled {
142 return when (settingsUiState) {
143 is SettingsUiState.Enabled -> settingsUiState
144 else -> throw AssertionError(
145 "SettingsUiState expected to be Enabled, but was ${settingsUiState::class}"
146 )
147 }
148 }
149