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