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