1 /*
2  * Copyright (C) 2021 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.qs.external
18 
19 import android.content.ComponentName
20 import android.content.Context
21 import android.content.SharedPreferences
22 import android.service.quicksettings.Tile
23 import android.testing.AndroidTestingRunner
24 import androidx.test.filters.SmallTest
25 import com.android.systemui.SysuiTestCase
26 import com.android.systemui.util.mockito.capture
27 import com.android.systemui.util.mockito.eq
28 import com.google.common.truth.Truth.assertThat
29 import org.junit.Before
30 import org.junit.Test
31 import org.junit.runner.RunWith
32 import org.mockito.Answers
33 import org.mockito.ArgumentCaptor
34 import org.mockito.ArgumentMatchers.any
35 import org.mockito.ArgumentMatchers.anyInt
36 import org.mockito.ArgumentMatchers.anyString
37 import org.mockito.Captor
38 import org.mockito.Mock
39 import org.mockito.Mockito.`when`
40 import org.mockito.Mockito.verify
41 import org.mockito.MockitoAnnotations
42 
43 @SmallTest
44 @RunWith(AndroidTestingRunner::class)
45 class CustomTileStatePersisterTest : SysuiTestCase() {
46 
47     companion object {
48         private val TEST_COMPONENT = ComponentName("pkg", "cls")
49         private const val TEST_USER = 0
50         private val KEY = TileServiceKey(TEST_COMPONENT, TEST_USER)
51 
52         private const val TEST_STATE = Tile.STATE_INACTIVE
53         private const val TEST_LABEL = "test_label"
54         private const val TEST_SUBTITLE = "test_subtitle"
55         private const val TEST_CONTENT_DESCRIPTION = "test_content_description"
56         private const val TEST_STATE_DESCRIPTION = "test_state_description"
57 
58         private fun Tile.isEqualTo(other: Tile): Boolean {
59             return state == other.state &&
60                     label == other.label &&
61                     subtitle == other.subtitle &&
62                     contentDescription == other.contentDescription &&
63                     stateDescription == other.stateDescription
64         }
65     }
66 
67     @Mock
68     private lateinit var mockContext: Context
69     @Mock
70     private lateinit var sharedPreferences: SharedPreferences
71     @Mock(answer = Answers.RETURNS_SELF)
72     private lateinit var editor: SharedPreferences.Editor
73     private lateinit var tile: Tile
74     private lateinit var customTileStatePersister: CustomTileStatePersister
75 
76     @Captor
77     private lateinit var stringCaptor: ArgumentCaptor<String>
78 
79     @Before
80     fun setUp() {
81         MockitoAnnotations.initMocks(this)
82         `when`(mockContext.getSharedPreferences(anyString(), anyInt()))
83                 .thenReturn(sharedPreferences)
84         `when`(sharedPreferences.edit()).thenReturn(editor)
85 
86         tile = Tile()
87         customTileStatePersister = CustomTileStatePersister(mockContext)
88     }
89 
90     @Test
91     fun testWriteState() {
92         tile.apply {
93             state = TEST_STATE
94             label = TEST_LABEL
95             subtitle = TEST_SUBTITLE
96             contentDescription = TEST_CONTENT_DESCRIPTION
97             stateDescription = TEST_STATE_DESCRIPTION
98         }
99 
100         customTileStatePersister.persistState(KEY, tile)
101 
102         verify(editor).putString(eq(KEY.toString()), capture(stringCaptor))
103 
104         assertThat(tile.isEqualTo(readTileFromString(stringCaptor.value))).isTrue()
105     }
106 
107     @Test
108     fun testReadState() {
109         tile.apply {
110             state = TEST_STATE
111             label = TEST_LABEL
112             subtitle = TEST_SUBTITLE
113             contentDescription = TEST_CONTENT_DESCRIPTION
114             stateDescription = TEST_STATE_DESCRIPTION
115         }
116 
117         `when`(sharedPreferences.getString(eq(KEY.toString()), any()))
118                 .thenReturn(writeToString(tile))
119 
120         assertThat(tile.isEqualTo(customTileStatePersister.readState(KEY)!!)).isTrue()
121     }
122 
123     @Test
124     fun testReadStateDefault() {
125         `when`(sharedPreferences.getString(any(), any())).thenAnswer {
126             it.getArgument(1)
127         }
128 
129         assertThat(customTileStatePersister.readState(KEY)).isNull()
130     }
131 
132     @Test
133     fun testStoreNulls() {
134         assertThat(tile.label).isNull()
135 
136         customTileStatePersister.persistState(KEY, tile)
137 
138         verify(editor).putString(eq(KEY.toString()), capture(stringCaptor))
139 
140         assertThat(readTileFromString(stringCaptor.value).label).isNull()
141     }
142 
143     @Test
144     fun testReadNulls() {
145         assertThat(tile.label).isNull()
146 
147         `when`(sharedPreferences.getString(eq(KEY.toString()), any()))
148                 .thenReturn(writeToString(tile))
149 
150         assertThat(customTileStatePersister.readState(KEY)!!.label).isNull()
151     }
152 
153     @Test
154     fun testRemoveState() {
155         customTileStatePersister.removeState(KEY)
156 
157         verify(editor).remove(KEY.toString())
158     }
159 }