1 /* 2 * Copyright (C) 2022 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.controls.management 18 19 import android.content.ComponentName 20 import android.content.res.Resources 21 import android.testing.AndroidTestingRunner 22 import android.testing.TestableLooper 23 import android.view.LayoutInflater 24 import android.view.View 25 import androidx.test.filters.SmallTest 26 import com.android.settingslib.core.lifecycle.Lifecycle 27 import com.android.systemui.SysuiTestCase 28 import com.android.systemui.controls.ControlsServiceInfo 29 import com.android.systemui.util.concurrency.FakeExecutor 30 import com.android.systemui.util.mockito.any 31 import com.android.systemui.util.mockito.argumentCaptor 32 import com.android.systemui.util.mockito.capture 33 import com.android.systemui.util.mockito.mock 34 import com.android.systemui.util.time.FakeSystemClock 35 import com.google.common.truth.Truth.assertThat 36 import org.junit.Before 37 import org.junit.Test 38 import org.junit.runner.RunWith 39 import org.mockito.ArgumentCaptor 40 import org.mockito.Mock 41 import org.mockito.Mockito.`when` 42 import org.mockito.Mockito.clearInvocations 43 import org.mockito.Mockito.verify 44 import org.mockito.MockitoAnnotations 45 import java.text.Collator 46 47 @SmallTest 48 @RunWith(AndroidTestingRunner::class) 49 @TestableLooper.RunWithLooper(setAsMainLooper = true) 50 class AppAdapterTest : SysuiTestCase() { 51 private val fakeSystemClock = FakeSystemClock() 52 private val backgroundExecutor = FakeExecutor(fakeSystemClock) 53 private val uiExecutor = FakeExecutor(fakeSystemClock) 54 @Mock lateinit var lifecycle: Lifecycle 55 @Mock lateinit var controlsListingController: ControlsListingController 56 @Mock lateinit var layoutInflater: LayoutInflater 57 @Mock lateinit var onAppSelected: (ControlsServiceInfo) -> Unit 58 @Mock lateinit var favoritesRenderer: FavoritesRenderer 59 val resources: Resources = context.resources 60 lateinit var adapter: AppAdapter 61 @Before 62 fun setUp() { 63 MockitoAnnotations.initMocks(this) 64 } 65 66 @Test 67 fun testOnServicesUpdated_nullLoadLabel() { 68 adapter = createAdapterWithAuthorizedPanels(emptySet()) 69 val captor = ArgumentCaptor 70 .forClass(ControlsListingController.ControlsListingCallback::class.java) 71 val controlsServiceInfo = mock<ControlsServiceInfo>() 72 val serviceInfo = listOf(controlsServiceInfo) 73 `when`(controlsServiceInfo.loadLabel()).thenReturn(null) 74 verify(controlsListingController).observe(any(Lifecycle::class.java), captor.capture()) 75 76 captor.value.onServicesUpdated(serviceInfo) 77 FakeExecutor.exhaustExecutors(backgroundExecutor, uiExecutor) 78 79 assertThat(adapter.itemCount).isEqualTo(serviceInfo.size) 80 } 81 82 @Test 83 fun testOnServicesUpdated_showsNotAuthorizedPanels() { 84 adapter = createAdapterWithAuthorizedPanels(emptySet()) 85 val captor = ArgumentCaptor 86 .forClass(ControlsListingController.ControlsListingCallback::class.java) 87 val serviceInfo = listOf( 88 ControlsServiceInfo("no panel", null), 89 ControlsServiceInfo("panel", mock()) 90 ) 91 verify(controlsListingController).observe(any(Lifecycle::class.java), captor.capture()) 92 93 captor.value.onServicesUpdated(serviceInfo) 94 FakeExecutor.exhaustExecutors(backgroundExecutor, uiExecutor) 95 96 assertThat(adapter.itemCount).isEqualTo(2) 97 } 98 99 @Test 100 fun testOnServicesUpdated_doesntShowAuthorizedPanels() { 101 adapter = createAdapterWithAuthorizedPanels(setOf(TEST_PACKAGE)) 102 103 val captor = ArgumentCaptor 104 .forClass(ControlsListingController.ControlsListingCallback::class.java) 105 val serviceInfo = listOf( 106 ControlsServiceInfo("no panel", null), 107 ControlsServiceInfo("panel", ComponentName(TEST_PACKAGE, "cls")) 108 ) 109 verify(controlsListingController).observe(any(Lifecycle::class.java), captor.capture()) 110 111 captor.value.onServicesUpdated(serviceInfo) 112 FakeExecutor.exhaustExecutors(backgroundExecutor, uiExecutor) 113 114 assertThat(adapter.itemCount).isEqualTo(1) 115 } 116 117 @Test 118 fun testOnBindSetsClickListenerToCallOnAppSelected() { 119 adapter = createAdapterWithAuthorizedPanels(emptySet()) 120 121 val captor = ArgumentCaptor 122 .forClass(ControlsListingController.ControlsListingCallback::class.java) 123 val serviceInfo = listOf( 124 ControlsServiceInfo("no panel", null), 125 ControlsServiceInfo("panel", ComponentName(TEST_PACKAGE, "cls")) 126 ) 127 verify(controlsListingController).observe(any(Lifecycle::class.java), captor.capture()) 128 129 captor.value.onServicesUpdated(serviceInfo) 130 FakeExecutor.exhaustExecutors(backgroundExecutor, uiExecutor) 131 132 val sorted = serviceInfo.sortedWith( 133 compareBy(Collator.getInstance(resources.configuration.locales[0])) { 134 it.loadLabel() ?: "" 135 }) 136 137 sorted.forEachIndexed { index, info -> 138 val fakeView: View = mock() 139 val fakeHolder: AppAdapter.Holder = mock() 140 `when`(fakeHolder.view).thenReturn(fakeView) 141 142 clearInvocations(onAppSelected) 143 adapter.onBindViewHolder(fakeHolder, index) 144 val listenerCaptor: ArgumentCaptor<View.OnClickListener> = argumentCaptor() 145 verify(fakeView).setOnClickListener(capture(listenerCaptor)) 146 listenerCaptor.value.onClick(fakeView) 147 148 verify(onAppSelected).invoke(info) 149 } 150 } 151 152 private fun createAdapterWithAuthorizedPanels(packages: Set<String>): AppAdapter { 153 return AppAdapter(backgroundExecutor, 154 uiExecutor, 155 lifecycle, 156 controlsListingController, 157 layoutInflater, 158 onAppSelected, 159 favoritesRenderer, 160 resources, 161 packages) 162 } 163 164 companion object { 165 private fun ControlsServiceInfo( 166 label: CharSequence, 167 panelComponentName: ComponentName? = null 168 ): ControlsServiceInfo { 169 return mock { 170 `when`(loadLabel()).thenReturn(label) 171 `when`(panelActivity).thenReturn(panelComponentName) 172 `when`(loadIcon()).thenReturn(mock()) 173 } 174 } 175 176 private const val TEST_PACKAGE = "package" 177 } 178 } 179