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.domain.interactor 18 19 import android.content.res.Configuration 20 import android.graphics.Rect 21 import android.util.LayoutDirection 22 import android.view.Surface 23 import androidx.test.ext.junit.runners.AndroidJUnit4 24 import androidx.test.filters.SmallTest 25 import com.android.systemui.SysuiTestCase 26 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository 27 import com.android.systemui.coroutines.collectLastValue 28 import com.android.systemui.coroutines.collectValues 29 import com.google.common.truth.Truth.assertThat 30 import kotlinx.coroutines.ExperimentalCoroutinesApi 31 import kotlinx.coroutines.test.TestScope 32 import kotlinx.coroutines.test.runCurrent 33 import kotlinx.coroutines.test.runTest 34 import org.junit.Before 35 import org.junit.Test 36 import org.junit.runner.RunWith 37 import org.mockito.MockitoAnnotations 38 import org.mockito.kotlin.mock 39 import org.mockito.kotlin.whenever 40 41 @ExperimentalCoroutinesApi 42 @SmallTest 43 @RunWith(AndroidJUnit4::class) 44 class ConfigurationInteractorTest : SysuiTestCase() { 45 private lateinit var testScope: TestScope 46 private lateinit var configuration: Configuration 47 private lateinit var underTest: ConfigurationInteractor 48 private lateinit var configurationRepository: FakeConfigurationRepository 49 50 @Before setUpnull51 fun setUp() { 52 MockitoAnnotations.initMocks(this) 53 configuration = context.resources.configuration 54 val testableResources = context.getOrCreateTestableResources() 55 testableResources.overrideConfiguration(configuration) 56 configurationRepository = FakeConfigurationRepository() 57 testScope = TestScope() 58 underTest = ConfigurationInteractorImpl(configurationRepository) 59 } 60 61 @Test dimensionPixelSizenull62 fun dimensionPixelSize() = 63 testScope.runTest { 64 val resourceId = 1001 65 val pixelSize = 501 66 configurationRepository.setDimensionPixelSize(resourceId, pixelSize) 67 68 val dimensionPixelSize by collectLastValue(underTest.dimensionPixelSize(resourceId)) 69 70 configurationRepository.onAnyConfigurationChange() 71 72 assertThat(dimensionPixelSize).isEqualTo(pixelSize) 73 } 74 75 @Test directionalDimensionPixelSizenull76 fun directionalDimensionPixelSize() = 77 testScope.runTest { 78 val resourceId = 1001 79 val pixelSize = 501 80 configurationRepository.setDimensionPixelSize(resourceId, pixelSize) 81 82 val config: Configuration = mock() 83 val dimensionPixelSize by 84 collectLastValue( 85 underTest.directionalDimensionPixelSize(LayoutDirection.LTR, resourceId) 86 ) 87 88 whenever(config.layoutDirection).thenReturn(LayoutDirection.LTR) 89 configurationRepository.onConfigurationChange(config) 90 assertThat(dimensionPixelSize).isEqualTo(pixelSize) 91 92 whenever(config.layoutDirection).thenReturn(LayoutDirection.RTL) 93 configurationRepository.onConfigurationChange(config) 94 assertThat(dimensionPixelSize).isEqualTo(-pixelSize) 95 } 96 97 @Test dimensionPixelSizesnull98 fun dimensionPixelSizes() = 99 testScope.runTest { 100 val resourceId1 = 1001 101 val pixelSize1 = 501 102 val resourceId2 = 1002 103 val pixelSize2 = 502 104 configurationRepository.setDimensionPixelSize(resourceId1, pixelSize1) 105 configurationRepository.setDimensionPixelSize(resourceId2, pixelSize2) 106 107 val dimensionPixelSizes by 108 collectLastValue(underTest.dimensionPixelSize(setOf(resourceId1, resourceId2))) 109 110 configurationRepository.onAnyConfigurationChange() 111 112 assertThat(dimensionPixelSizes!![resourceId1]).isEqualTo(pixelSize1) 113 assertThat(dimensionPixelSizes!![resourceId2]).isEqualTo(pixelSize2) 114 } 115 116 @Test maxBoundsChange_emitsMaxBoundsChangenull117 fun maxBoundsChange_emitsMaxBoundsChange() = 118 testScope.runTest { 119 val values by collectValues(underTest.maxBounds) 120 121 updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT) 122 runCurrent() 123 updateDisplay(width = DISPLAY_WIDTH * 2, height = DISPLAY_HEIGHT * 3) 124 runCurrent() 125 126 assertThat(values) 127 .containsExactly( 128 Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT), 129 Rect(0, 0, DISPLAY_WIDTH * 2, DISPLAY_HEIGHT * 3), 130 ) 131 .inOrder() 132 } 133 134 @Test maxBoundsSameOnConfigChange_doesNotEmitMaxBoundsChangenull135 fun maxBoundsSameOnConfigChange_doesNotEmitMaxBoundsChange() = 136 testScope.runTest { 137 val values by collectValues(underTest.maxBounds) 138 139 updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT) 140 runCurrent() 141 updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT) 142 runCurrent() 143 144 assertThat(values).containsExactly(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT)) 145 } 146 147 @Test firstMaxBoundsChange_emitsMaxBoundsChangenull148 fun firstMaxBoundsChange_emitsMaxBoundsChange() = 149 testScope.runTest { 150 val values by collectValues(underTest.maxBounds) 151 152 updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT) 153 runCurrent() 154 155 assertThat(values).containsExactly(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT)) 156 } 157 158 @Test maxBoundsChange_emitsNaturalMaxBoundsChangenull159 fun maxBoundsChange_emitsNaturalMaxBoundsChange() = 160 testScope.runTest { 161 val values by collectValues(underTest.naturalMaxBounds) 162 163 updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT) 164 runCurrent() 165 updateDisplay(width = DISPLAY_WIDTH * 2, height = DISPLAY_HEIGHT * 3) 166 runCurrent() 167 168 assertThat(values) 169 .containsExactly( 170 Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT), 171 Rect(0, 0, DISPLAY_WIDTH * 2, DISPLAY_HEIGHT * 3), 172 ) 173 .inOrder() 174 } 175 176 @Test maxBoundsSameOnConfigChange_doesNotEmitNaturalMaxBoundsChangenull177 fun maxBoundsSameOnConfigChange_doesNotEmitNaturalMaxBoundsChange() = 178 testScope.runTest { 179 val values by collectValues(underTest.naturalMaxBounds) 180 181 updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT) 182 runCurrent() 183 updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT) 184 runCurrent() 185 186 assertThat(values).containsExactly(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT)) 187 } 188 189 @Test firstMaxBoundsChange_emitsNaturalMaxBoundsChangenull190 fun firstMaxBoundsChange_emitsNaturalMaxBoundsChange() = 191 testScope.runTest { 192 val values by collectValues(underTest.naturalMaxBounds) 193 194 updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT) 195 runCurrent() 196 197 assertThat(values).containsExactly(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT)) 198 } 199 200 @Test displayRotatedButMaxBoundsTheSame_doesNotEmitNewNaturalMaxBoundsChangenull201 fun displayRotatedButMaxBoundsTheSame_doesNotEmitNewNaturalMaxBoundsChange() = 202 testScope.runTest { 203 val values by collectValues(underTest.naturalMaxBounds) 204 205 updateDisplay(width = DISPLAY_WIDTH, height = DISPLAY_HEIGHT) 206 runCurrent() 207 updateDisplay( 208 width = DISPLAY_HEIGHT, 209 height = DISPLAY_WIDTH, 210 rotation = Surface.ROTATION_90, 211 ) 212 runCurrent() 213 214 assertThat(values).containsExactly(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT)) 215 } 216 updateDisplaynull217 private fun updateDisplay( 218 width: Int = DISPLAY_WIDTH, 219 height: Int = DISPLAY_HEIGHT, 220 @Surface.Rotation rotation: Int = Surface.ROTATION_0, 221 ) { 222 configuration.windowConfiguration.maxBounds.set(Rect(0, 0, width, height)) 223 configuration.windowConfiguration.displayRotation = rotation 224 225 configurationRepository.onConfigurationChange(configuration) 226 } 227 228 private companion object { 229 private const val DISPLAY_WIDTH = 100 230 private const val DISPLAY_HEIGHT = 200 231 } 232 } 233