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.statusbar.policy
18 
19 import android.os.Handler
20 import android.testing.AndroidTestingRunner
21 import android.testing.TestableLooper
22 import android.view.View
23 import androidx.test.filters.SmallTest
24 import com.android.systemui.SysuiTestCase
25 import com.android.systemui.broadcast.BroadcastDispatcher
26 import com.android.systemui.shade.domain.interactor.ShadeInteractor
27 import com.android.systemui.util.mockito.any
28 import com.android.systemui.util.mockito.capture
29 import com.android.systemui.util.mockito.mock
30 import com.android.systemui.util.time.FakeSystemClock
31 import com.google.common.truth.Truth.assertThat
32 import kotlinx.coroutines.flow.MutableStateFlow
33 import org.junit.Before
34 import org.junit.Test
35 import org.junit.runner.RunWith
36 import org.mockito.ArgumentCaptor
37 import org.mockito.Captor
38 import org.mockito.Mock
39 import org.mockito.Mockito.anyString
40 import org.mockito.Mockito.verify
41 import org.mockito.Mockito.`when`
42 import org.mockito.MockitoAnnotations
43 import java.util.Date
44 
45 @RunWith(AndroidTestingRunner::class)
46 @TestableLooper.RunWithLooper(setAsMainLooper = true)
47 @SmallTest
48 class VariableDateViewControllerTest : SysuiTestCase() {
49 
50     companion object {
51         private const val TIME_STAMP = 1_500_000_000_000
52         private const val LONG_PATTERN = "EEEMMMd"
53         private const val SHORT_PATTERN = "MMMd"
54         private const val CHAR_WIDTH = 10f
55     }
56 
57     @Mock
58     private lateinit var broadcastDispatcher: BroadcastDispatcher
59     @Mock
60     private lateinit var view: VariableDateView
61     @Mock
62     private lateinit var shadeInteractor: ShadeInteractor
63     @Captor
64     private lateinit var onMeasureListenerCaptor: ArgumentCaptor<VariableDateView.OnMeasureListener>
65 
66     private val qsExpansion = MutableStateFlow(0F)
67 
68     private var lastText: String? = null
69 
70     private lateinit var systemClock: FakeSystemClock
71     private lateinit var testableLooper: TestableLooper
72     private lateinit var testableHandler: Handler
73     private lateinit var controller: VariableDateViewController
74 
75     private lateinit var longText: String
76     private lateinit var shortText: String
77 
78     @Before
setUpnull79     fun setUp() {
80         MockitoAnnotations.initMocks(this)
81         testableLooper = TestableLooper.get(this)
82         testableHandler = Handler(testableLooper.looper)
83 
84         systemClock = FakeSystemClock()
85         systemClock.setCurrentTimeMillis(TIME_STAMP)
86 
87         `when`(shadeInteractor.qsExpansion).thenReturn(qsExpansion)
88 
89         `when`(view.longerPattern).thenReturn(LONG_PATTERN)
90         `when`(view.shorterPattern).thenReturn(SHORT_PATTERN)
91         `when`(view.handler).thenReturn(testableHandler)
92 
93         `when`(view.setText(anyString())).thenAnswer {
94             lastText = it.arguments[0] as? String
95             Unit
96         }
97         `when`(view.isAttachedToWindow).thenReturn(true)
98         `when`(view.viewTreeObserver).thenReturn(mock())
99 
100         val date = Date(TIME_STAMP)
101         longText = getTextForFormat(date, getFormatFromPattern(LONG_PATTERN))
102         shortText = getTextForFormat(date, getFormatFromPattern(SHORT_PATTERN))
103 
104         // Assume some sizes for the text, the controller doesn't need to know if these sizes are
105         // the true ones
106         `when`(view.getDesiredWidthForText(any())).thenAnswer {
107             getTextLength(it.arguments[0] as CharSequence)
108         }
109 
110         controller = VariableDateViewController(
111             systemClock,
112             broadcastDispatcher,
113             shadeInteractor,
114             mock(),
115             testableHandler,
116             view
117         )
118 
119         controller.init()
120         testableLooper.processAllMessages()
121 
122         verify(view).onAttach(capture(onMeasureListenerCaptor))
123     }
124 
125     @Test
testViewStartsWithLongTextnull126     fun testViewStartsWithLongText() {
127         assertThat(lastText).isEqualTo(longText)
128     }
129 
130     @Test
testListenerNotNullnull131     fun testListenerNotNull() {
132         assertThat(onMeasureListenerCaptor.value).isNotNull()
133     }
134 
135     @Test
testLotsOfSpaceUseLongTextnull136     fun testLotsOfSpaceUseLongText() {
137         onMeasureListenerCaptor.value.onMeasureAction(10000, View.MeasureSpec.EXACTLY)
138 
139         testableLooper.processAllMessages()
140         assertThat(lastText).isEqualTo(longText)
141     }
142 
143     @Test
testSmallSpaceUseEmptynull144     fun testSmallSpaceUseEmpty() {
145         onMeasureListenerCaptor.value.onMeasureAction(1, View.MeasureSpec.EXACTLY)
146         testableLooper.processAllMessages()
147 
148         assertThat(lastText).isEmpty()
149     }
150 
151     @Test
testSpaceInBetweenUseShortTextnull152     fun testSpaceInBetweenUseShortText() {
153         val average = ((getTextLength(longText) + getTextLength(shortText)) / 2).toInt()
154 
155         onMeasureListenerCaptor.value.onMeasureAction(average, View.MeasureSpec.EXACTLY)
156         testableLooper.processAllMessages()
157 
158         assertThat(lastText).isEqualTo(shortText)
159     }
160 
161     @Test
testSwitchBackToLongernull162     fun testSwitchBackToLonger() {
163         onMeasureListenerCaptor.value.onMeasureAction(1, View.MeasureSpec.EXACTLY)
164         testableLooper.processAllMessages()
165 
166         onMeasureListenerCaptor.value.onMeasureAction(10000, View.MeasureSpec.EXACTLY)
167         testableLooper.processAllMessages()
168 
169         assertThat(lastText).isEqualTo(longText)
170     }
171 
172     @Test
testNoSwitchingWhenFrozennull173     fun testNoSwitchingWhenFrozen() {
174         `when`(view.freezeSwitching).thenReturn(true)
175 
176         val average = ((getTextLength(longText) + getTextLength(shortText)) / 2).toInt()
177         onMeasureListenerCaptor.value.onMeasureAction(average, View.MeasureSpec.EXACTLY)
178         testableLooper.processAllMessages()
179         assertThat(lastText).isEqualTo(longText)
180 
181         onMeasureListenerCaptor.value.onMeasureAction(1, View.MeasureSpec.EXACTLY)
182         testableLooper.processAllMessages()
183         assertThat(lastText).isEqualTo(longText)
184     }
185 
186     @Test
testQsExpansionTrue_ignoreAtMostMeasureRequestsnull187     fun testQsExpansionTrue_ignoreAtMostMeasureRequests() {
188         qsExpansion.value = 0f
189 
190         onMeasureListenerCaptor.value.onMeasureAction(
191                 getTextLength(shortText).toInt(),
192                 View.MeasureSpec.EXACTLY
193             )
194         testableLooper.processAllMessages()
195 
196         onMeasureListenerCaptor.value.onMeasureAction(10000, View.MeasureSpec.AT_MOST)
197         testableLooper.processAllMessages()
198         assertThat(lastText).isEqualTo(shortText)
199     }
200 
201     @Test
testQsExpansionFalse_acceptAtMostMeasureRequestsnull202     fun testQsExpansionFalse_acceptAtMostMeasureRequests() {
203         qsExpansion.value = 1f
204 
205         onMeasureListenerCaptor.value.onMeasureAction(
206                 getTextLength(shortText).toInt(),
207                 View.MeasureSpec.EXACTLY
208         )
209         testableLooper.processAllMessages()
210 
211         onMeasureListenerCaptor.value.onMeasureAction(10000, View.MeasureSpec.AT_MOST)
212         testableLooper.processAllMessages()
213         assertThat(lastText).isEqualTo(longText)
214     }
215 
getTextLengthnull216     private fun getTextLength(text: CharSequence): Float {
217         return text.length * CHAR_WIDTH
218     }
219 }