1 /*
2  * Copyright (C) 2019 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.app.PendingIntent
20 import android.content.ComponentName
21 import android.service.controls.Control
22 import android.testing.AndroidTestingRunner
23 import androidx.test.filters.SmallTest
24 import com.android.systemui.SysuiTestCase
25 import com.android.systemui.controls.ControlStatus
26 import com.android.systemui.controls.controller.ControlInfo
27 import org.junit.Assert.assertEquals
28 import org.junit.Assert.assertFalse
29 import org.junit.Assert.assertTrue
30 import org.junit.Before
31 import org.junit.Test
32 import org.junit.runner.RunWith
33 import org.mockito.Mock
34 import org.mockito.Mockito.never
35 import org.mockito.Mockito.times
36 import org.mockito.Mockito.verify
37 import org.mockito.MockitoAnnotations
38 
39 @SmallTest
40 @RunWith(AndroidTestingRunner::class)
41 class AllModelTest : SysuiTestCase() {
42 
43     companion object {
44         private const val EMPTY_STRING = "Other"
45     }
46 
47     @Mock
48     lateinit var pendingIntent: PendingIntent
49     @Mock
50     lateinit var controlsModelCallback: ControlsModel.ControlsModelCallback
51 
52     val idPrefix = "controlId"
53     val favoritesIndices = listOf(7, 3, 1, 9)
54     val favoritesList = favoritesIndices.map { "controlId$it" }
55     lateinit var controls: List<ControlStatus>
56 
57     lateinit var model: AllModel
58 
59     private fun zoneMap(id: Int): String? {
60         return when (id) {
61             10 -> ""
62             11 -> null
63             else -> ((id + 1) % 3).toString()
64         }
65     }
66 
67     @Before
68     fun setUp() {
69         MockitoAnnotations.initMocks(this)
70 
71         // controlId0 --> zone = 1
72         // controlId1 --> zone = 2, favorite
73         // controlId2 --> zone = 0
74         // controlId3 --> zone = 1, favorite
75         // controlId4 --> zone = 2
76         // controlId5 --> zone = 0
77         // controlId6 --> zone = 1
78         // controlId7 --> zone = 2, favorite
79         // controlId8 --> zone = 0
80         // controlId9 --> zone = 1, favorite
81         // controlId10 --> zone = ""
82         // controlId11 --> zone = null
83         controls = (0..11).map {
84             ControlStatus(
85                     Control.StatelessBuilder("$idPrefix$it", pendingIntent)
86                             .setZone(zoneMap(it))
87                             .build(),
88                     ComponentName("", ""),
89                     it in favoritesIndices
90             )
91         }
92         model = AllModel(controls, favoritesList, EMPTY_STRING, controlsModelCallback)
93     }
94 
95     @Test
96     fun testElements() {
97 
98         // Zones are sorted by order of appearance, with empty at the end with special header.
99         val expected = listOf(
100                 ZoneNameWrapper("1"),
101                 ControlStatusWrapper(controls[0]),
102                 ControlStatusWrapper(controls[3]),
103                 ControlStatusWrapper(controls[6]),
104                 ControlStatusWrapper(controls[9]),
105                 ZoneNameWrapper("2"),
106                 ControlStatusWrapper(controls[1]),
107                 ControlStatusWrapper(controls[4]),
108                 ControlStatusWrapper(controls[7]),
109                 ZoneNameWrapper("0"),
110                 ControlStatusWrapper(controls[2]),
111                 ControlStatusWrapper(controls[5]),
112                 ControlStatusWrapper(controls[8]),
113                 ZoneNameWrapper(EMPTY_STRING),
114                 ControlStatusWrapper(controls[10]),
115                 ControlStatusWrapper(controls[11])
116         )
117         expected.zip(model.elements).forEachIndexed { index, it ->
118             assertEquals("Error in item at index $index", it.first, it.second)
119         }
120     }
121 
122     private fun sameControl(controlInfo: ControlInfo, control: Control): Boolean {
123         return controlInfo.controlId == control.controlId &&
124                 controlInfo.controlTitle == control.title &&
125                 controlInfo.controlSubtitle == control.subtitle &&
126                 controlInfo.deviceType == control.deviceType
127     }
128 
129     @Test
130     fun testAllEmpty_noHeader() {
131         val selected_controls = listOf(controls[10], controls[11])
132         val new_model = AllModel(selected_controls, emptyList(), EMPTY_STRING,
133                 controlsModelCallback)
134         val expected = listOf(
135                 ControlStatusWrapper(controls[10]),
136                 ControlStatusWrapper(controls[11])
137         )
138 
139         expected.zip(new_model.elements).forEachIndexed { index, it ->
140             assertEquals("Error in item at index $index", it.first, it.second)
141         }
142     }
143 
144     @Test
145     fun testFavorites() {
146         val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control)
147         model.favorites.zip(expectedFavorites).forEach {
148             assertTrue(sameControl(it.first, it.second))
149         }
150     }
151 
152     @Test
153     fun testAddFavorite() {
154         val indexToAdd = 6
155         model.changeFavoriteStatus("$idPrefix$indexToAdd", true)
156 
157         val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control) +
158                 controls[indexToAdd].control
159 
160         model.favorites.zip(expectedFavorites).forEach {
161             assertTrue(sameControl(it.first, it.second))
162         }
163 
164         verify(controlsModelCallback).onFirstChange()
165         verify(controlsModelCallback).onChange()
166     }
167 
168     @Test
169     fun testAddFavorite_changesModelFlag() {
170         val indexToAdd = 6
171         val id = "$idPrefix$indexToAdd"
172         model.changeFavoriteStatus(id, true)
173         assertTrue(
174                 (model.elements.first {
175                     it is ControlStatusWrapper && it.controlStatus.control.controlId == id
176                 } as ControlStatusWrapper)
177                         .controlStatus.favorite
178         )
179 
180         verify(controlsModelCallback).onFirstChange()
181         verify(controlsModelCallback).onChange()
182     }
183 
184     @Test
185     fun testAddFavorite_alreadyThere() {
186         val indexToAdd = 7
187         model.changeFavoriteStatus("$idPrefix$indexToAdd", true)
188 
189         val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control)
190 
191         assertEquals(expectedFavorites.size, model.favorites.size)
192         model.favorites.zip(expectedFavorites).forEach {
193             assertTrue(sameControl(it.first, it.second))
194         }
195 
196         verify(controlsModelCallback, never()).onFirstChange()
197         verify(controlsModelCallback, never()).onChange()
198     }
199 
200     @Test
201     fun testRemoveFavorite() {
202         val indexToRemove = 3
203         model.changeFavoriteStatus("$idPrefix$indexToRemove", false)
204 
205         val expectedFavorites = (favoritesIndices.filterNot { it == indexToRemove })
206                 .map(controls::get)
207                 .map(ControlStatus::control)
208 
209         model.favorites.zip(expectedFavorites).forEach {
210             assertTrue(sameControl(it.first, it.second))
211         }
212 
213         verify(controlsModelCallback).onFirstChange()
214         verify(controlsModelCallback).onChange()
215     }
216 
217     @Test
218     fun testRemoveFavorite_changesModelFlag() {
219         val indexToRemove = 3
220         val id = "$idPrefix$indexToRemove"
221         model.changeFavoriteStatus(id, false)
222         assertFalse(
223                 (model.elements.first {
224                     it is ControlStatusWrapper && it.controlStatus.control.controlId == id
225                 } as ControlStatusWrapper)
226                         .controlStatus.favorite
227         )
228 
229         verify(controlsModelCallback).onFirstChange()
230         verify(controlsModelCallback).onChange()
231     }
232 
233     @Test
234     fun testRemoveFavorite_notThere() {
235         val indexToRemove = 4
236         model.changeFavoriteStatus("$idPrefix$indexToRemove", false)
237 
238         val expectedFavorites = favoritesIndices.map(controls::get).map(ControlStatus::control)
239 
240         model.favorites.zip(expectedFavorites).forEach {
241             assertTrue(sameControl(it.first, it.second))
242         }
243 
244         verify(controlsModelCallback, never()).onFirstChange()
245         verify(controlsModelCallback, never()).onChange()
246     }
247 
248     @Test
249     fun testAddSecondChange_callbacks() {
250         model.changeFavoriteStatus("${idPrefix}4", true)
251         model.changeFavoriteStatus("${idPrefix}5", true)
252 
253         verify(controlsModelCallback).onFirstChange()
254         verify(controlsModelCallback, times(2)).onChange()
255     }
256 
257     @Test
258     fun testRemoveSecondChange_callbacks() {
259         model.changeFavoriteStatus("${idPrefix}1", false)
260         model.changeFavoriteStatus("${idPrefix}3", false)
261 
262         verify(controlsModelCallback).onFirstChange()
263         verify(controlsModelCallback, times(2)).onChange()
264     }
265 }
266