1 /* 2 * 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 @file:OptIn(ExperimentalCoroutinesApi::class) 17 18 package com.android.systemui.statusbar.notification.collection.coordinator 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.keyguard.data.repository.FakeKeyguardRepository 24 import com.android.systemui.plugins.statusbar.StatusBarStateController 25 import com.android.systemui.statusbar.StatusBarState 26 import com.android.systemui.statusbar.SysuiStatusBarStateController 27 import com.android.systemui.statusbar.notification.collection.NotifPipeline 28 import com.android.systemui.statusbar.notification.collection.NotificationEntry 29 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder 30 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter 31 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable 32 import com.android.systemui.util.mockito.any 33 import com.android.systemui.util.mockito.eq 34 import com.android.systemui.util.mockito.withArgCaptor 35 import com.google.common.truth.Truth.assertThat 36 import kotlinx.coroutines.ExperimentalCoroutinesApi 37 import kotlinx.coroutines.test.TestScope 38 import kotlinx.coroutines.test.UnconfinedTestDispatcher 39 import kotlinx.coroutines.test.runCurrent 40 import kotlinx.coroutines.test.runTest 41 import org.junit.Before 42 import org.junit.Test 43 import org.junit.runner.RunWith 44 import org.mockito.Mock 45 import org.mockito.Mockito.clearInvocations 46 import org.mockito.Mockito.never 47 import org.mockito.Mockito.verify 48 import org.mockito.Mockito.`when` as whenever 49 import org.mockito.MockitoAnnotations 50 51 @SmallTest 52 @RunWith(AndroidJUnit4::class) 53 class DreamCoordinatorTest : SysuiTestCase() { 54 @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController 55 @Mock private lateinit var notifPipeline: NotifPipeline 56 @Mock private lateinit var filterListener: Pluggable.PluggableListener<NotifFilter> 57 58 private val keyguardRepository = FakeKeyguardRepository() 59 private var fakeEntry: NotificationEntry = NotificationEntryBuilder().build() 60 val testDispatcher = UnconfinedTestDispatcher() 61 val testScope = TestScope(testDispatcher) 62 63 private lateinit var filter: NotifFilter 64 private lateinit var statusBarListener: StatusBarStateController.StateListener 65 private lateinit var dreamCoordinator: DreamCoordinator 66 67 @Before setUpnull68 fun setUp() { 69 MockitoAnnotations.initMocks(this) 70 whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) 71 72 // Build the coordinator 73 dreamCoordinator = 74 DreamCoordinator( 75 statusBarStateController, 76 testScope.backgroundScope, 77 keyguardRepository 78 ) 79 80 // Attach the pipeline and capture the listeners/filters that it registers 81 dreamCoordinator.attach(notifPipeline) 82 83 filter = withArgCaptor { verify(notifPipeline).addPreGroupFilter(capture()) } 84 filter.setInvalidationListener(filterListener) 85 86 statusBarListener = withArgCaptor { 87 verify(statusBarStateController).addCallback(capture()) 88 } 89 } 90 91 @Test hideNotifications_whenDreamingAndOnKeyguardnull92 fun hideNotifications_whenDreamingAndOnKeyguard() = 93 testScope.runTest { 94 // GIVEN we are on keyguard and not dreaming 95 keyguardRepository.setKeyguardShowing(true) 96 keyguardRepository.setIsActiveDreamLockscreenHosted(false) 97 runCurrent() 98 99 // THEN notifications are not filtered out 100 verifyPipelinesNotInvalidated() 101 assertThat(filter.shouldFilterOut(fakeEntry, 0L)).isFalse() 102 103 // WHEN dreaming starts and the active dream is hosted in lockscreen 104 keyguardRepository.setIsActiveDreamLockscreenHosted(true) 105 runCurrent() 106 107 // THEN pipeline is notified and notifications should all be filtered out 108 verifyPipelinesInvalidated() 109 assertThat(filter.shouldFilterOut(fakeEntry, 0L)).isTrue() 110 } 111 112 @Test showNotifications_whenDreamingAndNotOnKeyguardnull113 fun showNotifications_whenDreamingAndNotOnKeyguard() = 114 testScope.runTest { 115 // GIVEN we are on the keyguard and active dream is hosted in lockscreen 116 keyguardRepository.setKeyguardShowing(true) 117 keyguardRepository.setIsActiveDreamLockscreenHosted(true) 118 runCurrent() 119 120 // THEN pipeline is notified and notifications are all filtered out 121 verifyPipelinesInvalidated() 122 clearPipelineInvocations() 123 assertThat(filter.shouldFilterOut(fakeEntry, 0L)).isTrue() 124 125 // WHEN we are no longer on the keyguard 126 statusBarListener.onStateChanged(StatusBarState.SHADE) 127 128 // THEN pipeline is notified and notifications are not filtered out 129 verifyPipelinesInvalidated() 130 assertThat(filter.shouldFilterOut(fakeEntry, 0L)).isFalse() 131 } 132 133 @Test showNotifications_whenOnKeyguardAndNotDreamingnull134 fun showNotifications_whenOnKeyguardAndNotDreaming() = 135 testScope.runTest { 136 // GIVEN we are on the keyguard and active dream is hosted in lockscreen 137 keyguardRepository.setKeyguardShowing(true) 138 keyguardRepository.setIsActiveDreamLockscreenHosted(true) 139 runCurrent() 140 141 // THEN pipeline is notified and notifications are all filtered out 142 verifyPipelinesInvalidated() 143 clearPipelineInvocations() 144 assertThat(filter.shouldFilterOut(fakeEntry, 0L)).isTrue() 145 146 // WHEN the lockscreen hosted dream stops 147 keyguardRepository.setIsActiveDreamLockscreenHosted(false) 148 runCurrent() 149 150 // THEN pipeline is notified and notifications are not filtered out 151 verifyPipelinesInvalidated() 152 assertThat(filter.shouldFilterOut(fakeEntry, 0L)).isFalse() 153 } 154 verifyPipelinesInvalidatednull155 private fun verifyPipelinesInvalidated() { 156 verify(filterListener).onPluggableInvalidated(eq(filter), any()) 157 } 158 verifyPipelinesNotInvalidatednull159 private fun verifyPipelinesNotInvalidated() { 160 verify(filterListener, never()).onPluggableInvalidated(eq(filter), any()) 161 } 162 clearPipelineInvocationsnull163 private fun clearPipelineInvocations() { 164 clearInvocations(filterListener) 165 } 166 } 167