1 /*
2  * Copyright (C) 2024 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.surfaceeffects.loadingeffect
18 
19 import android.graphics.Paint
20 import android.graphics.RenderEffect
21 import android.testing.TestableLooper
22 import androidx.test.ext.junit.runners.AndroidJUnit4
23 import androidx.test.filters.SmallTest
24 import com.android.systemui.SysuiTestCase
25 import com.android.systemui.animation.AnimatorTestRule
26 import com.android.systemui.surfaceeffects.PaintDrawCallback
27 import com.android.systemui.surfaceeffects.RenderEffectDrawCallback
28 import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseAnimationConfig
29 import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseShader
30 import com.google.common.truth.Truth.assertThat
31 import org.junit.Rule
32 import org.junit.Test
33 import org.junit.runner.RunWith
34 
35 @SmallTest
36 @RunWith(AndroidJUnit4::class)
37 @TestableLooper.RunWithLooper(setAsMainLooper = true)
38 class LoadingEffectTest : SysuiTestCase() {
39 
40     @get:Rule val animatorTestRule = AnimatorTestRule(this)
41 
42     @Test
play_paintCallback_triggersDrawCallbacknull43     fun play_paintCallback_triggersDrawCallback() {
44         var paintFromCallback: Paint? = null
45         val drawCallback =
46             object : PaintDrawCallback {
47                 override fun onDraw(paint: Paint) {
48                     paintFromCallback = paint
49                 }
50             }
51         val loadingEffect =
52             LoadingEffect(
53                 baseType = TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE,
54                 TurbulenceNoiseAnimationConfig(),
55                 paintCallback = drawCallback,
56                 animationStateChangedCallback = null
57             )
58 
59         assertThat(paintFromCallback).isNull()
60 
61         loadingEffect.play()
62         animatorTestRule.advanceTimeBy(500L)
63 
64         assertThat(paintFromCallback).isNotNull()
65     }
66 
67     @Test
play_renderEffectCallback_triggersDrawCallbacknull68     fun play_renderEffectCallback_triggersDrawCallback() {
69         var renderEffectFromCallback: RenderEffect? = null
70         val drawCallback =
71             object : RenderEffectDrawCallback {
72                 override fun onDraw(renderEffect: RenderEffect) {
73                     renderEffectFromCallback = renderEffect
74                 }
75             }
76         val loadingEffect =
77             LoadingEffect(
78                 baseType = TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE,
79                 TurbulenceNoiseAnimationConfig(),
80                 renderEffectCallback = drawCallback,
81                 animationStateChangedCallback = null
82             )
83 
84         assertThat(renderEffectFromCallback).isNull()
85 
86         loadingEffect.play()
87         animatorTestRule.advanceTimeBy(500L)
88 
89         assertThat(renderEffectFromCallback).isNotNull()
90     }
91 
92     @Test
play_animationStateChangesInOrdernull93     fun play_animationStateChangesInOrder() {
94         val config = TurbulenceNoiseAnimationConfig()
95         val states = mutableListOf(LoadingEffect.AnimationState.NOT_PLAYING)
96         val stateChangedCallback =
97             object : LoadingEffect.AnimationStateChangedCallback {
98                 override fun onStateChanged(
99                     oldState: LoadingEffect.AnimationState,
100                     newState: LoadingEffect.AnimationState
101                 ) {
102                     states.add(newState)
103                 }
104             }
105         val drawCallback =
106             object : PaintDrawCallback {
107                 override fun onDraw(paint: Paint) {}
108             }
109         val loadingEffect =
110             LoadingEffect(
111                 baseType = TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE,
112                 config,
113                 paintCallback = drawCallback,
114                 stateChangedCallback
115             )
116 
117         loadingEffect.play()
118 
119         // Execute all the animators by advancing each duration with some buffer.
120         animatorTestRule.advanceAnimationDuration(config.easeInDuration.toLong())
121         animatorTestRule.advanceAnimationDuration(config.maxDuration.toLong())
122         animatorTestRule.advanceAnimationDuration(config.easeOutDuration.toLong())
123         animatorTestRule.advanceTimeBy(500)
124 
125         assertThat(states)
126             .containsExactly(
127                 LoadingEffect.AnimationState.NOT_PLAYING,
128                 LoadingEffect.AnimationState.EASE_IN,
129                 LoadingEffect.AnimationState.MAIN,
130                 LoadingEffect.AnimationState.EASE_OUT,
131                 LoadingEffect.AnimationState.NOT_PLAYING
132             )
133     }
134 
135     @Test
play_alreadyPlaying_playsOnlyOncenull136     fun play_alreadyPlaying_playsOnlyOnce() {
137         val config = TurbulenceNoiseAnimationConfig()
138         var numPlay = 0
139         val stateChangedCallback =
140             object : LoadingEffect.AnimationStateChangedCallback {
141                 override fun onStateChanged(
142                     oldState: LoadingEffect.AnimationState,
143                     newState: LoadingEffect.AnimationState
144                 ) {
145                     if (
146                         oldState == LoadingEffect.AnimationState.NOT_PLAYING &&
147                             newState == LoadingEffect.AnimationState.EASE_IN
148                     ) {
149                         numPlay++
150                     }
151                 }
152             }
153         val drawCallback =
154             object : PaintDrawCallback {
155                 override fun onDraw(paint: Paint) {}
156             }
157         val loadingEffect =
158             LoadingEffect(
159                 baseType = TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE,
160                 config,
161                 paintCallback = drawCallback,
162                 stateChangedCallback
163             )
164 
165         assertThat(numPlay).isEqualTo(0)
166 
167         loadingEffect.play()
168         loadingEffect.play()
169         loadingEffect.play()
170         loadingEffect.play()
171         loadingEffect.play()
172 
173         assertThat(numPlay).isEqualTo(1)
174     }
175 
176     @Test
finish_finishesLoadingEffectnull177     fun finish_finishesLoadingEffect() {
178         val config = TurbulenceNoiseAnimationConfig(maxDuration = 1000f)
179         val drawCallback =
180             object : PaintDrawCallback {
181                 override fun onDraw(loadingPaint: Paint) {}
182             }
183         var isFinished = false
184         val stateChangedCallback =
185             object : LoadingEffect.AnimationStateChangedCallback {
186                 override fun onStateChanged(
187                     oldState: LoadingEffect.AnimationState,
188                     newState: LoadingEffect.AnimationState
189                 ) {
190                     if (
191                         oldState == LoadingEffect.AnimationState.EASE_OUT &&
192                             newState == LoadingEffect.AnimationState.NOT_PLAYING
193                     ) {
194                         isFinished = true
195                     }
196                 }
197             }
198         val loadingEffect =
199             LoadingEffect(
200                 baseType = TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE,
201                 config,
202                 paintCallback = drawCallback,
203                 stateChangedCallback
204             )
205 
206         assertThat(isFinished).isFalse()
207 
208         loadingEffect.play()
209         animatorTestRule.advanceAnimationDuration(config.easeInDuration.toLong() + 500L)
210 
211         assertThat(isFinished).isFalse()
212 
213         loadingEffect.finish()
214         animatorTestRule.advanceAnimationDuration(config.easeOutDuration.toLong() + 500L)
215 
216         assertThat(isFinished).isTrue()
217     }
218 
219     @Test
finish_notMainState_hasNoEffectnull220     fun finish_notMainState_hasNoEffect() {
221         val config = TurbulenceNoiseAnimationConfig(maxDuration = 1000f)
222         val drawCallback =
223             object : PaintDrawCallback {
224                 override fun onDraw(paint: Paint) {}
225             }
226         var isFinished = false
227         val stateChangedCallback =
228             object : LoadingEffect.AnimationStateChangedCallback {
229                 override fun onStateChanged(
230                     oldState: LoadingEffect.AnimationState,
231                     newState: LoadingEffect.AnimationState
232                 ) {
233                     if (
234                         oldState == LoadingEffect.AnimationState.MAIN &&
235                             newState == LoadingEffect.AnimationState.NOT_PLAYING
236                     ) {
237                         isFinished = true
238                     }
239                 }
240             }
241         val loadingEffect =
242             LoadingEffect(
243                 baseType = TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE,
244                 config,
245                 paintCallback = drawCallback,
246                 stateChangedCallback
247             )
248 
249         assertThat(isFinished).isFalse()
250 
251         loadingEffect.finish()
252 
253         assertThat(isFinished).isFalse()
254     }
255 
256     @Test
getNoiseOffset_returnsNoiseOffsetnull257     fun getNoiseOffset_returnsNoiseOffset() {
258         val expectedNoiseOffset = arrayOf(0.1f, 0.2f, 0.3f)
259         val config =
260             TurbulenceNoiseAnimationConfig(
261                 noiseOffsetX = expectedNoiseOffset[0],
262                 noiseOffsetY = expectedNoiseOffset[1],
263                 noiseOffsetZ = expectedNoiseOffset[2]
264             )
265         val drawCallback =
266             object : PaintDrawCallback {
267                 override fun onDraw(paint: Paint) {}
268             }
269         val loadingEffect =
270             LoadingEffect(
271                 baseType = TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE,
272                 config,
273                 paintCallback = drawCallback,
274                 animationStateChangedCallback = null
275             )
276 
277         assertThat(loadingEffect.getNoiseOffset()).isEqualTo(expectedNoiseOffset)
278     }
279 }
280