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.communal.ui.widgets 18 19 import android.testing.TestableLooper 20 import android.testing.TestableLooper.RunWithLooper 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.communal.shared.model.fakeGlanceableHubMultiUserHelper 25 import com.android.systemui.communal.widgets.CommunalAppWidgetHost 26 import com.android.systemui.coroutines.collectLastValue 27 import com.android.systemui.kosmos.applicationCoroutineScope 28 import com.android.systemui.kosmos.testScope 29 import com.android.systemui.log.logcatLogBuffer 30 import com.android.systemui.testKosmos 31 import com.android.systemui.util.mockito.any 32 import com.android.systemui.util.mockito.eq 33 import com.android.systemui.util.mockito.mock 34 import com.google.common.truth.Truth.assertThat 35 import kotlinx.coroutines.ExperimentalCoroutinesApi 36 import kotlinx.coroutines.test.runCurrent 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.Mockito.clearInvocations 42 import org.mockito.Mockito.never 43 import org.mockito.Mockito.verify 44 45 @OptIn(ExperimentalCoroutinesApi::class) 46 @SmallTest 47 @RunWithLooper(setAsMainLooper = true) 48 @RunWith(AndroidJUnit4::class) 49 class CommunalAppWidgetHostTest : SysuiTestCase() { 50 private val kosmos = testKosmos() 51 private val testScope = kosmos.testScope 52 53 private lateinit var testableLooper: TestableLooper 54 private lateinit var underTest: CommunalAppWidgetHost 55 56 @Before setUpnull57 fun setUp() { 58 testableLooper = TestableLooper.get(this) 59 underTest = 60 CommunalAppWidgetHost( 61 context = context, 62 backgroundScope = kosmos.applicationCoroutineScope, 63 hostId = 116, 64 logBuffer = logcatLogBuffer("CommunalAppWidgetHostTest"), 65 glanceableHubMultiUserHelper = kosmos.fakeGlanceableHubMultiUserHelper, 66 ) 67 } 68 69 @Test appWidgetIdToRemove_emitnull70 fun appWidgetIdToRemove_emit() = 71 testScope.runTest { 72 val appWidgetIdToRemove by collectLastValue(underTest.appWidgetIdToRemove) 73 74 // Nothing should be emitted yet 75 assertThat(appWidgetIdToRemove).isNull() 76 77 underTest.onAppWidgetRemoved(appWidgetId = 1) 78 runCurrent() 79 80 assertThat(appWidgetIdToRemove).isEqualTo(1) 81 82 underTest.onAppWidgetRemoved(appWidgetId = 2) 83 runCurrent() 84 85 assertThat(appWidgetIdToRemove).isEqualTo(2) 86 } 87 88 @Test observer_onHostStartListeningTriggeredWhileObserverActivenull89 fun observer_onHostStartListeningTriggeredWhileObserverActive() = 90 testScope.runTest { 91 // Observer added 92 val observer = mock<CommunalAppWidgetHost.Observer>() 93 underTest.addObserver(observer) 94 runCurrent() 95 96 // Verify callback triggered 97 verify(observer, never()).onHostStartListening() 98 underTest.startListening() 99 runCurrent() 100 verify(observer).onHostStartListening() 101 102 clearInvocations(observer) 103 104 // Observer removed 105 underTest.removeObserver(observer) 106 runCurrent() 107 108 // Verify callback not triggered 109 underTest.startListening() 110 runCurrent() 111 verify(observer, never()).onHostStartListening() 112 } 113 114 @Test observer_onHostStopListeningTriggeredWhileObserverActivenull115 fun observer_onHostStopListeningTriggeredWhileObserverActive() = 116 testScope.runTest { 117 // Observer added 118 val observer = mock<CommunalAppWidgetHost.Observer>() 119 underTest.addObserver(observer) 120 runCurrent() 121 122 // Verify callback triggered 123 verify(observer, never()).onHostStopListening() 124 underTest.stopListening() 125 runCurrent() 126 verify(observer).onHostStopListening() 127 128 clearInvocations(observer) 129 130 // Observer removed 131 underTest.removeObserver(observer) 132 runCurrent() 133 134 // Verify callback not triggered 135 underTest.stopListening() 136 runCurrent() 137 verify(observer, never()).onHostStopListening() 138 } 139 140 @Test observer_onAllocateAppWidgetIdTriggeredWhileObserverActivenull141 fun observer_onAllocateAppWidgetIdTriggeredWhileObserverActive() = 142 testScope.runTest { 143 // Observer added 144 val observer = mock<CommunalAppWidgetHost.Observer>() 145 underTest.addObserver(observer) 146 runCurrent() 147 148 // Verify callback triggered 149 verify(observer, never()).onAllocateAppWidgetId(any()) 150 val id = underTest.allocateAppWidgetId() 151 runCurrent() 152 verify(observer).onAllocateAppWidgetId(eq(id)) 153 154 clearInvocations(observer) 155 156 // Observer removed 157 underTest.removeObserver(observer) 158 runCurrent() 159 160 // Verify callback not triggered 161 underTest.allocateAppWidgetId() 162 runCurrent() 163 verify(observer, never()).onAllocateAppWidgetId(any()) 164 } 165 166 @Test observer_onDeleteAppWidgetIdTriggeredWhileObserverActivenull167 fun observer_onDeleteAppWidgetIdTriggeredWhileObserverActive() = 168 testScope.runTest { 169 // Observer added 170 val observer = mock<CommunalAppWidgetHost.Observer>() 171 underTest.addObserver(observer) 172 runCurrent() 173 174 // Verify callback triggered 175 verify(observer, never()).onDeleteAppWidgetId(any()) 176 underTest.deleteAppWidgetId(1) 177 runCurrent() 178 verify(observer).onDeleteAppWidgetId(eq(1)) 179 180 clearInvocations(observer) 181 182 // Observer removed 183 underTest.removeObserver(observer) 184 runCurrent() 185 186 // Verify callback not triggered 187 underTest.deleteAppWidgetId(2) 188 runCurrent() 189 verify(observer, never()).onDeleteAppWidgetId(any()) 190 } 191 192 @Test observer_multipleObserversnull193 fun observer_multipleObservers() = 194 testScope.runTest { 195 // Set up two observers 196 val observer1 = mock<CommunalAppWidgetHost.Observer>() 197 val observer2 = mock<CommunalAppWidgetHost.Observer>() 198 underTest.addObserver(observer1) 199 underTest.addObserver(observer2) 200 runCurrent() 201 202 // Verify both observers triggered 203 verify(observer1, never()).onHostStartListening() 204 verify(observer2, never()).onHostStartListening() 205 underTest.startListening() 206 runCurrent() 207 verify(observer1).onHostStartListening() 208 verify(observer2).onHostStartListening() 209 210 // Observer 1 removed 211 underTest.removeObserver(observer1) 212 runCurrent() 213 214 // Verify only observer 2 is triggered 215 underTest.stopListening() 216 runCurrent() 217 verify(observer2).onHostStopListening() 218 verify(observer1, never()).onHostStopListening() 219 } 220 } 221