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.notification.collection
18 
19 import android.testing.AndroidTestingRunner
20 import android.testing.TestableLooper.RunWithLooper
21 import androidx.lifecycle.Observer
22 import androidx.test.filters.SmallTest
23 import com.android.systemui.SysuiTestCase
24 import com.android.systemui.util.concurrency.FakeExecutor
25 import com.android.systemui.util.mockito.mock
26 import com.android.systemui.util.time.FakeSystemClock
27 import com.google.common.truth.Truth.assertThat
28 import org.junit.Before
29 import org.junit.Test
30 import org.junit.runner.RunWith
31 import org.mockito.Mockito.eq
32 import org.mockito.Mockito.inOrder
33 import org.mockito.Mockito.verify
34 import org.mockito.Mockito.verifyNoMoreInteractions
35 
36 @SmallTest
37 @RunWith(AndroidTestingRunner::class)
38 @RunWithLooper
39 class NotifLiveDataImplTest : SysuiTestCase() {
40 
41     private val executor = FakeExecutor(FakeSystemClock())
42     private val liveDataImpl: NotifLiveDataImpl<Int> = NotifLiveDataImpl("tst", 9, executor)
43     private val syncObserver: Observer<Int> = mock()
44     private val asyncObserver: Observer<Int> = mock()
45 
46     @Before
47     fun setup() {
48         allowTestableLooperAsMainThread()
49         liveDataImpl.addSyncObserver(syncObserver)
50         liveDataImpl.addAsyncObserver(asyncObserver)
51     }
52 
53     @Test
54     fun testGetInitialValue() {
55         assertThat(liveDataImpl.value).isEqualTo(9)
56     }
57 
58     @Test
59     fun testGetModifiedValue() {
60         liveDataImpl.value = 13
61         assertThat(liveDataImpl.value).isEqualTo(13)
62     }
63 
64     @Test
65     fun testGetsModifiedValueFromWithinSyncObserver() {
66         liveDataImpl.addSyncObserver { intVal ->
67             assertThat(intVal).isEqualTo(13)
68             assertThat(liveDataImpl.value).isEqualTo(13)
69         }
70         liveDataImpl.value = 13
71     }
72 
73     @Test
74     fun testDoesNotAlertsRemovedObservers() {
75         liveDataImpl.removeObserver(syncObserver)
76         liveDataImpl.removeObserver(asyncObserver)
77 
78         liveDataImpl.value = 13
79 
80         // There should be no runnables on the executor
81         assertThat(executor.runAllReady()).isEqualTo(0)
82 
83         // And observers should not be called
84         verifyNoMoreInteractions(syncObserver, asyncObserver)
85     }
86 
87     @Test
88     fun testDoesNotAsyncObserversRemovedSinceChange() {
89         liveDataImpl.value = 13
90         liveDataImpl.removeObserver(asyncObserver)
91 
92         // There should be a runnable that will get executed...
93         assertThat(executor.runAllReady()).isEqualTo(1)
94 
95         // ...but async observers should not be called
96         verifyNoMoreInteractions(asyncObserver)
97     }
98 
99     @Test
100     fun testAlertsObservers() {
101         liveDataImpl.value = 13
102 
103         // Verify that the synchronous observer is called immediately
104         verify(syncObserver).onChanged(eq(13))
105         verifyNoMoreInteractions(syncObserver, asyncObserver)
106 
107         // Verify that the asynchronous observer is called when the executor runs
108         assertThat(executor.runAllReady()).isEqualTo(1)
109         verify(asyncObserver).onChanged(eq(13))
110         verifyNoMoreInteractions(syncObserver, asyncObserver)
111     }
112 
113     @Test
114     fun testAlertsObserversFromDispatcher() {
115         // GIVEN that we use setValueAndProvideDispatcher()
116         val dispatcher = liveDataImpl.setValueAndProvideDispatcher(13)
117 
118         // VERIFY that nothing is done before the dispatcher is called
119         assertThat(executor.numPending()).isEqualTo(0)
120         verifyNoMoreInteractions(syncObserver, asyncObserver)
121 
122         // WHEN the dispatcher is invoked...
123         dispatcher.invoke()
124 
125         // Verify that the synchronous observer is called immediately
126         verify(syncObserver).onChanged(eq(13))
127         verifyNoMoreInteractions(syncObserver, asyncObserver)
128 
129         // Verify that the asynchronous observer is called when the executor runs
130         assertThat(executor.runAllReady()).isEqualTo(1)
131         verify(asyncObserver).onChanged(eq(13))
132         verifyNoMoreInteractions(syncObserver, asyncObserver)
133     }
134 
135     @Test
136     fun testSkipsAllObserversIfValueDidNotChange() {
137         liveDataImpl.value = 9
138         // Does not add a runnable
139         assertThat(executor.runAllReady()).isEqualTo(0)
140         // Setting the current value does not call synchronous observers
141         verifyNoMoreInteractions(syncObserver, asyncObserver)
142     }
143 
144     @Test
145     fun testSkipsAsyncObserversWhenValueTogglesBack() {
146         liveDataImpl.value = 13
147         liveDataImpl.value = 11
148         liveDataImpl.value = 9
149 
150         // Synchronous observers will receive every change event immediately
151         inOrder(syncObserver).apply {
152             verify(syncObserver).onChanged(eq(13))
153             verify(syncObserver).onChanged(eq(11))
154             verify(syncObserver).onChanged(eq(9))
155         }
156         verifyNoMoreInteractions(syncObserver, asyncObserver)
157 
158         // Running the first runnable on the queue will just emit the most recent value
159         assertThat(executor.runNextReady()).isTrue()
160         verify(asyncObserver).onChanged(eq(9))
161         verifyNoMoreInteractions(syncObserver, asyncObserver)
162 
163         // Running the next 2 runnable will have no effect
164         assertThat(executor.runAllReady()).isEqualTo(2)
165         verifyNoMoreInteractions(syncObserver, asyncObserver)
166     }
167 }