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 
17 package com.android.systemui.common.ui.data.repository
18 
19 import android.content.res.Configuration
20 import android.view.Display
21 import androidx.test.ext.junit.runners.AndroidJUnit4
22 import androidx.test.filters.SmallTest
23 import com.android.systemui.SysuiTestCase
24 import com.android.systemui.coroutines.collectLastValue
25 import com.android.systemui.statusbar.policy.ConfigurationController
26 import com.android.systemui.util.mockito.any
27 import com.android.systemui.util.mockito.whenever
28 import com.android.systemui.util.mockito.withArgCaptor
29 import com.android.systemui.util.wrapper.DisplayUtilsWrapper
30 import com.google.common.truth.Truth.assertThat
31 import kotlinx.coroutines.ExperimentalCoroutinesApi
32 import kotlinx.coroutines.test.TestScope
33 import kotlinx.coroutines.test.runCurrent
34 import kotlinx.coroutines.test.runTest
35 import org.junit.Before
36 import org.junit.Test
37 import org.junit.runner.RunWith
38 import org.mockito.ArgumentMatchers.anyInt
39 import org.mockito.Mock
40 import org.mockito.Mockito.mock
41 import org.mockito.Mockito.verify
42 import org.mockito.MockitoAnnotations
43 
44 @ExperimentalCoroutinesApi
45 @SmallTest
46 @RunWith(AndroidJUnit4::class)
47 class ConfigurationRepositoryImplTest : SysuiTestCase() {
48     private var displaySizeRatio = 0f
49     @Mock private lateinit var configurationController: ConfigurationController
50     @Mock private lateinit var displayUtils: DisplayUtilsWrapper
51 
52     private lateinit var testScope: TestScope
53     private lateinit var underTest: ConfigurationRepositoryImpl
54 
55     @Before
56     fun setUp() {
57         MockitoAnnotations.initMocks(this)
58         setPhysicalPixelDisplaySizeRatio(displaySizeRatio)
59 
60         testScope = TestScope()
61         underTest =
62             ConfigurationRepositoryImpl(
63                 configurationController,
64                 context,
65                 testScope.backgroundScope,
66                 displayUtils,
67             )
68     }
69 
70     @Test
71     fun onAnyConfigurationChange_updatesOnUiModeChanged() =
72         testScope.runTest {
73             val lastAnyConfigurationChange by collectLastValue(underTest.onAnyConfigurationChange)
74             assertThat(lastAnyConfigurationChange).isNull()
75 
76             val configurationCallback = withArgCaptor {
77                 verify(configurationController).addCallback(capture())
78             }
79 
80             configurationCallback.onUiModeChanged()
81             runCurrent()
82             assertThat(lastAnyConfigurationChange).isNotNull()
83         }
84 
85     @Test
86     fun onAnyConfigurationChange_updatesOnThemeChanged() =
87         testScope.runTest {
88             val lastAnyConfigurationChange by collectLastValue(underTest.onAnyConfigurationChange)
89             assertThat(lastAnyConfigurationChange).isNull()
90 
91             val configurationCallback = withArgCaptor {
92                 verify(configurationController).addCallback(capture())
93             }
94 
95             configurationCallback.onThemeChanged()
96             runCurrent()
97             assertThat(lastAnyConfigurationChange).isNotNull()
98         }
99 
100     @Test
101     fun onAnyConfigurationChange_updatesOnConfigChanged() =
102         testScope.runTest {
103             val lastAnyConfigurationChange by collectLastValue(underTest.onAnyConfigurationChange)
104             assertThat(lastAnyConfigurationChange).isNull()
105 
106             val configurationCallback = withArgCaptor {
107                 verify(configurationController).addCallback(capture())
108             }
109 
110             configurationCallback.onConfigChanged(mock(Configuration::class.java))
111             runCurrent()
112             assertThat(lastAnyConfigurationChange).isNotNull()
113         }
114 
115     @Test
116     fun onResolutionScale_updatesOnConfigurationChange() =
117         testScope.runTest {
118             val scaleForResolution by collectLastValue(underTest.scaleForResolution)
119             assertThat(scaleForResolution).isEqualTo(displaySizeRatio)
120 
121             val configurationCallback = withArgCaptor {
122                 verify(configurationController).addCallback(capture())
123             }
124 
125             setPhysicalPixelDisplaySizeRatio(2f)
126             configurationCallback.onConfigChanged(mock(Configuration::class.java))
127             assertThat(scaleForResolution).isEqualTo(displaySizeRatio)
128 
129             setPhysicalPixelDisplaySizeRatio(.21f)
130             configurationCallback.onConfigChanged(mock(Configuration::class.java))
131             assertThat(scaleForResolution).isEqualTo(displaySizeRatio)
132         }
133 
134     @Test
135     fun onResolutionScale_nullMaxResolution() =
136         testScope.runTest {
137             val scaleForResolution by collectLastValue(underTest.scaleForResolution)
138             runCurrent()
139 
140             givenNullMaxResolutionDisplayMode()
141             val configurationCallback = withArgCaptor {
142                 verify(configurationController).addCallback(capture())
143             }
144             configurationCallback.onConfigChanged(mock(Configuration::class.java))
145             assertThat(scaleForResolution).isEqualTo(1f)
146         }
147 
148     @Test
149     fun getResolutionScale_nullMaxResolutionDisplayMode() {
150         givenNullMaxResolutionDisplayMode()
151         assertThat(underTest.getResolutionScale()).isEqualTo(1f)
152     }
153 
154     @Test
155     fun getResolutionScale_infiniteDisplayRatios() {
156         setPhysicalPixelDisplaySizeRatio(Float.POSITIVE_INFINITY)
157         assertThat(underTest.getResolutionScale()).isEqualTo(1f)
158     }
159 
160     @Test
161     fun getResolutionScale_differentDisplayRatios() {
162         setPhysicalPixelDisplaySizeRatio(.5f)
163         assertThat(underTest.getResolutionScale()).isEqualTo(displaySizeRatio)
164 
165         setPhysicalPixelDisplaySizeRatio(.283f)
166         assertThat(underTest.getResolutionScale()).isEqualTo(displaySizeRatio)
167 
168         setPhysicalPixelDisplaySizeRatio(3.58f)
169         assertThat(underTest.getResolutionScale()).isEqualTo(displaySizeRatio)
170 
171         setPhysicalPixelDisplaySizeRatio(0f)
172         assertThat(underTest.getResolutionScale()).isEqualTo(displaySizeRatio)
173 
174         setPhysicalPixelDisplaySizeRatio(1f)
175         assertThat(underTest.getResolutionScale()).isEqualTo(displaySizeRatio)
176     }
177 
178     private fun givenNullMaxResolutionDisplayMode() {
179         whenever(displayUtils.getMaximumResolutionDisplayMode(any())).thenReturn(null)
180     }
181 
182     private fun setPhysicalPixelDisplaySizeRatio(ratio: Float) {
183         displaySizeRatio = ratio
184         whenever(displayUtils.getMaximumResolutionDisplayMode(any()))
185             .thenReturn(Display.Mode(0, 0, 0, 90f))
186         whenever(
187                 displayUtils.getPhysicalPixelDisplaySizeRatio(
188                     anyInt(),
189                     anyInt(),
190                     anyInt(),
191                     anyInt()
192                 )
193             )
194             .thenReturn(ratio)
195     }
196 }
197