1 /*
<lambda>null2  * 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 
18 package com.android.systemui.keyboard.backlight.ui
19 
20 import androidx.test.ext.junit.runners.AndroidJUnit4
21 import androidx.test.filters.SmallTest
22 import com.android.systemui.SysuiTestCase
23 import com.android.systemui.keyboard.backlight.domain.interactor.KeyboardBacklightInteractor
24 import com.android.systemui.keyboard.backlight.ui.view.KeyboardBacklightDialog
25 import com.android.systemui.keyboard.backlight.ui.viewmodel.BacklightDialogViewModel
26 import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
27 import com.android.systemui.keyboard.shared.model.BacklightModel
28 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
29 import com.android.systemui.util.mockito.any
30 import com.android.systemui.util.mockito.whenever
31 import com.google.common.truth.Truth
32 import kotlinx.coroutines.ExperimentalCoroutinesApi
33 import kotlinx.coroutines.test.StandardTestDispatcher
34 import kotlinx.coroutines.test.TestScope
35 import kotlinx.coroutines.test.advanceTimeBy
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.Mock
42 import org.mockito.Mockito.never
43 import org.mockito.Mockito.times
44 import org.mockito.Mockito.verify
45 import org.mockito.MockitoAnnotations
46 
47 @OptIn(ExperimentalCoroutinesApi::class)
48 @SmallTest
49 @RunWith(AndroidJUnit4::class)
50 class KeyboardBacklightDialogCoordinatorTest : SysuiTestCase() {
51 
52     @Mock private lateinit var accessibilityManagerWrapper: AccessibilityManagerWrapper
53     @Mock private lateinit var dialog: KeyboardBacklightDialog
54 
55     private val keyboardRepository = FakeKeyboardRepository()
56     private lateinit var underTest: KeyboardBacklightDialogCoordinator
57     private val timeoutMillis = 3000L
58     private val testScope = TestScope(StandardTestDispatcher())
59 
60     private val createDialog = { value: Int, maxValue: Int ->
61         dialogCreationValue = value
62         dialogCreationMaxValue = maxValue
63         dialog
64     }
65     private var dialogCreationValue = -1
66     private var dialogCreationMaxValue = -1
67 
68     @Before
69     fun setUp() {
70         MockitoAnnotations.initMocks(this)
71         whenever(accessibilityManagerWrapper.getRecommendedTimeoutMillis(any(), any()))
72             .thenReturn(timeoutMillis.toInt())
73         val viewModel =
74             BacklightDialogViewModel(
75                 KeyboardBacklightInteractor(keyboardRepository),
76                 accessibilityManagerWrapper
77             )
78         underTest =
79             KeyboardBacklightDialogCoordinator(testScope.backgroundScope, viewModel, createDialog)
80         underTest.startListening()
81         keyboardRepository.setIsAnyKeyboardConnected(true)
82     }
83 
84     @Test
85     fun showsDialog_afterBacklightChange() =
86         testScope.runTest {
87             setBacklightValue(1)
88 
89             verify(dialog).show()
90         }
91 
92     @Test
93     fun updatesDialog_withLatestValues_afterBacklightChange() =
94         testScope.runTest {
95             setBacklightValue(value = 1, maxValue = 5)
96             setBacklightValue(value = 2, maxValue = 5)
97 
98             verify(dialog).updateState(2, 5)
99         }
100 
101     @Test
102     fun showsDialog_withDataFromBacklightChange() =
103         testScope.runTest {
104             setBacklightValue(value = 4, maxValue = 5)
105 
106             Truth.assertThat(dialogCreationValue).isEqualTo(4)
107             Truth.assertThat(dialogCreationMaxValue).isEqualTo(5)
108         }
109 
110     @Test
111     fun dismissesDialog_afterTimeout() =
112         testScope.runTest {
113             setBacklightValue(1)
114 
115             advanceTimeBy(timeoutMillis + 1)
116 
117             verify(dialog).dismiss()
118         }
119 
120     @Test
121     fun dismissesDialog_onlyAfterTimeout_fromLastBacklightChange() =
122         testScope.runTest {
123             setBacklightValue(1)
124             advanceTimeBy(timeoutMillis * 2 / 3)
125             // majority of timeout passed
126 
127             // this should restart timeout
128             setBacklightValue(2)
129             advanceTimeBy(timeoutMillis * 2 / 3)
130             verify(dialog, never()).dismiss()
131 
132             advanceTimeBy(timeoutMillis * 2 / 3)
133             // finally timeout reached and dialog was dismissed
134             verify(dialog, times(1)).dismiss()
135         }
136 
137     @Test
138     fun showsDialog_ifItWasAlreadyShownAndDismissedBySomethingElse() =
139         testScope.runTest {
140             setBacklightValue(1)
141             // let's pretend dialog is dismissed e.g. by user tapping on the screen
142             whenever(dialog.isShowing).thenReturn(false)
143 
144             // no advancing time, we're still in timeout period
145             setBacklightValue(2)
146 
147             verify(dialog, times(2)).show()
148         }
149 
150     private fun TestScope.setBacklightValue(value: Int, maxValue: Int = MAX_BACKLIGHT) {
151         keyboardRepository.setBacklight(BacklightModel(value, maxValue))
152         runCurrent()
153     }
154 
155     private companion object {
156         const val MAX_BACKLIGHT = 5
157     }
158 }
159