1 /* <lambda>null2 * 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.display.data.repository 18 19 import android.hardware.display.DisplayManager 20 import android.os.Looper 21 import android.testing.AndroidTestingRunner 22 import android.testing.TestableLooper 23 import android.view.Display 24 import androidx.test.filters.SmallTest 25 import com.android.systemui.SysuiTestCase 26 import com.android.systemui.coroutines.FlowValue 27 import com.android.systemui.coroutines.collectLastValue 28 import com.android.systemui.util.mockito.eq 29 import com.android.systemui.util.mockito.kotlinArgumentCaptor 30 import com.android.systemui.util.mockito.mock 31 import com.android.systemui.util.mockito.whenever 32 import com.android.systemui.utils.os.FakeHandler 33 import com.google.common.truth.Truth.assertThat 34 import kotlinx.coroutines.ExperimentalCoroutinesApi 35 import kotlinx.coroutines.test.TestScope 36 import kotlinx.coroutines.test.UnconfinedTestDispatcher 37 import kotlinx.coroutines.test.runTest 38 import org.junit.Before 39 import org.junit.Test 40 import org.junit.runner.RunWith 41 import org.mockito.ArgumentMatchers.any 42 import org.mockito.ArgumentMatchers.anyLong 43 import org.mockito.Mockito.never 44 import org.mockito.Mockito.times 45 import org.mockito.Mockito.verify 46 47 @RunWith(AndroidTestingRunner::class) 48 @TestableLooper.RunWithLooper 49 @OptIn(ExperimentalCoroutinesApi::class) 50 @SmallTest 51 class DisplayRepositoryTest : SysuiTestCase() { 52 53 private val displayManager = mock<DisplayManager>() 54 private val displayListener = kotlinArgumentCaptor<DisplayManager.DisplayListener>() 55 56 private val testHandler = FakeHandler(Looper.getMainLooper()) 57 private val testScope = TestScope(UnconfinedTestDispatcher()) 58 59 private lateinit var displayRepository: DisplayRepositoryImpl 60 61 @Before 62 fun setup() { 63 setDisplays(emptyList()) 64 displayRepository = 65 DisplayRepositoryImpl( 66 displayManager, 67 testHandler, 68 TestScope(UnconfinedTestDispatcher()), 69 UnconfinedTestDispatcher() 70 ) 71 verify(displayManager, never()).registerDisplayListener(any(), any()) 72 } 73 74 @Test 75 fun onFlowCollection_displayListenerRegistered() = 76 testScope.runTest { 77 val value by latestDisplayFlowValue() 78 79 assertThat(value).isEmpty() 80 81 verify(displayManager).registerDisplayListener(any(), eq(testHandler), anyLong()) 82 } 83 84 @Test 85 fun afterFlowCollection_displayListenerUnregistered() { 86 testScope.runTest { 87 val value by latestDisplayFlowValue() 88 89 assertThat(value).isEmpty() 90 91 verify(displayManager).registerDisplayListener(any(), eq(testHandler), anyLong()) 92 } 93 verify(displayManager).unregisterDisplayListener(any()) 94 } 95 96 @Test 97 fun afterFlowCollection_multipleSusbcriptions_oneRemoved_displayListenerNotUnregistered() { 98 testScope.runTest { 99 val firstSubscriber by latestDisplayFlowValue() 100 101 assertThat(firstSubscriber).isEmpty() 102 verify(displayManager, times(1)) 103 .registerDisplayListener(displayListener.capture(), eq(testHandler), anyLong()) 104 105 val innerScope = TestScope() 106 innerScope.runTest { 107 val secondSubscriber by latestDisplayFlowValue() 108 assertThat(secondSubscriber).isEmpty() 109 110 // No new registration, just the precedent one. 111 verify(displayManager, times(1)) 112 .registerDisplayListener(any(), eq(testHandler), anyLong()) 113 } 114 115 // Let's make sure it has *NOT* been unregistered, as there is still a subscriber. 116 setDisplays(1) 117 displayListener.value.onDisplayAdded(1) 118 assertThat(firstSubscriber?.ids()).containsExactly(1) 119 } 120 121 // All subscribers are done, unregister should have been called. 122 verify(displayManager).unregisterDisplayListener(any()) 123 } 124 @Test 125 fun onDisplayAdded_propagated() = 126 testScope.runTest { 127 val value by latestDisplayFlowValue() 128 129 setDisplays(1) 130 displayListener.value.onDisplayAdded(1) 131 132 assertThat(value?.ids()).containsExactly(1) 133 } 134 135 @Test 136 fun onDisplayRemoved_propagated() = 137 testScope.runTest { 138 val value by latestDisplayFlowValue() 139 140 setDisplays(1, 2, 3, 4) 141 displayListener.value.onDisplayAdded(1) 142 displayListener.value.onDisplayAdded(2) 143 displayListener.value.onDisplayAdded(3) 144 displayListener.value.onDisplayAdded(4) 145 146 setDisplays(1, 2, 3) 147 displayListener.value.onDisplayRemoved(4) 148 149 assertThat(value?.ids()).containsExactly(1, 2, 3) 150 } 151 152 @Test 153 fun onDisplayChanged_propagated() = 154 testScope.runTest { 155 val value by latestDisplayFlowValue() 156 157 setDisplays(1, 2, 3, 4) 158 displayListener.value.onDisplayAdded(1) 159 displayListener.value.onDisplayAdded(2) 160 displayListener.value.onDisplayAdded(3) 161 displayListener.value.onDisplayAdded(4) 162 163 displayListener.value.onDisplayChanged(4) 164 165 assertThat(value?.ids()).containsExactly(1, 2, 3, 4) 166 } 167 168 private fun Iterable<Display>.ids(): List<Int> = map { it.displayId } 169 170 // Wrapper to capture the displayListener. 171 private fun TestScope.latestDisplayFlowValue(): FlowValue<Set<Display>?> { 172 val flowValue = collectLastValue(displayRepository.displays) 173 verify(displayManager) 174 .registerDisplayListener(displayListener.capture(), eq(testHandler), anyLong()) 175 return flowValue 176 } 177 178 private fun setDisplays(displays: List<Display>) { 179 whenever(displayManager.displays).thenReturn(displays.toTypedArray()) 180 } 181 182 private fun setDisplays(vararg ids: Int) { 183 setDisplays(ids.map { display(it) }) 184 } 185 186 private fun display(id: Int): Display { 187 return mock<Display>().also { mockDisplay -> 188 whenever(mockDisplay.displayId).thenReturn(id) 189 } 190 } 191 } 192