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