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 
17 package com.android.systemui.unfold.progress
18 
19 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
20 import com.android.systemui.util.leak.ReferenceTestUtils.waitForCondition
21 import com.google.common.truth.Truth.assertThat
22 import com.google.common.truth.Truth.assertWithMessage
23 
24 /** Listener usable by tests with some handy assertions. */
25 class TestUnfoldProgressListener : UnfoldTransitionProgressProvider.TransitionProgressListener {
26 
27     private val recordings: MutableList<UnfoldTransitionRecording> = arrayListOf()
28     private var currentRecording: UnfoldTransitionRecording? = null
29     var lastCallbackThread: Thread? = null
30         private set
31 
onTransitionStartednull32     override fun onTransitionStarted() {
33         lastCallbackThread = Thread.currentThread()
34         assertWithMessage("Trying to start a transition when it is already in progress")
35             .that(currentRecording)
36             .isNull()
37 
38         currentRecording = UnfoldTransitionRecording()
39     }
40 
onTransitionProgressnull41     override fun onTransitionProgress(progress: Float) {
42         lastCallbackThread = Thread.currentThread()
43         assertWithMessage("Received transition progress event when it's not started")
44             .that(currentRecording)
45             .isNotNull()
46         currentRecording!!.addProgress(progress)
47     }
48 
onTransitionFinishingnull49     override fun onTransitionFinishing() {
50         lastCallbackThread = Thread.currentThread()
51         assertWithMessage("Received transition finishing event when it's not started")
52             .that(currentRecording)
53             .isNotNull()
54         currentRecording!!.onFinishing()
55     }
56 
onTransitionFinishednull57     override fun onTransitionFinished() {
58         lastCallbackThread = Thread.currentThread()
59         assertWithMessage("Received transition finish event when it's not started")
60             .that(currentRecording)
61             .isNotNull()
62         recordings += currentRecording!!
63         currentRecording = null
64     }
65 
ensureTransitionFinishednull66     fun ensureTransitionFinished(): UnfoldTransitionRecording {
67         waitForCondition { recordings.size == 1 }
68         return recordings.first()
69     }
70 
assertStartednull71     fun assertStarted() {
72         assertWithMessage("Transition didn't start").that(currentRecording).isNotNull()
73     }
74 
assertNotStartednull75     fun assertNotStarted() {
76         assertWithMessage("Transition started").that(currentRecording).isNull()
77     }
78 
assertLastProgressnull79     fun assertLastProgress(progress: Float) {
80         currentRecording?.assertLastProgress(progress) ?: error("unfold not in progress.")
81     }
82 
clearnull83     fun clear() {
84         currentRecording = null
85         recordings.clear()
86     }
87 
88     class UnfoldTransitionRecording {
89         private val progressHistory: MutableList<Float> = arrayListOf()
90         private var finishingInvocations: Int = 0
91 
addProgressnull92         fun addProgress(progress: Float) {
93             assertThat(progress).isAtMost(1.0f)
94             assertThat(progress).isAtLeast(0.0f)
95 
96             progressHistory += progress
97         }
98 
onFinishingnull99         fun onFinishing() {
100             finishingInvocations++
101         }
102 
assertIncreasingProgressnull103         fun assertIncreasingProgress() {
104             assertThat(progressHistory.size).isGreaterThan(MIN_ANIMATION_EVENTS)
105             assertThat(progressHistory).isInOrder()
106         }
107 
assertDecreasingProgressnull108         fun assertDecreasingProgress() {
109             assertThat(progressHistory.size).isGreaterThan(MIN_ANIMATION_EVENTS)
110             assertThat(progressHistory).isInOrder(Comparator.reverseOrder<Float>())
111         }
112 
assertFinishedWithUnfoldnull113         fun assertFinishedWithUnfold() {
114             assertThat(progressHistory).isNotEmpty()
115             assertThat(progressHistory.last()).isEqualTo(1.0f)
116         }
117 
assertFinishedWithFoldnull118         fun assertFinishedWithFold() {
119             assertThat(progressHistory).isNotEmpty()
120             assertThat(progressHistory.last()).isEqualTo(0.0f)
121         }
122 
assertHasFoldAnimationAtTheEndnull123         fun assertHasFoldAnimationAtTheEnd() {
124             // Check that there are at least a few decreasing events at the end
125             assertThat(progressHistory.size).isGreaterThan(MIN_ANIMATION_EVENTS)
126             assertThat(progressHistory.takeLast(MIN_ANIMATION_EVENTS))
127                 .isInOrder(Comparator.reverseOrder<Float>())
128             assertThat(progressHistory.last()).isEqualTo(0.0f)
129         }
130 
assertHasSingleFinishingEventnull131         fun assertHasSingleFinishingEvent() {
132             assertWithMessage(
133                     "onTransitionFinishing callback should be invoked exactly " + "one time"
134                 )
135                 .that(finishingInvocations)
136                 .isEqualTo(1)
137         }
138 
assertLastProgressnull139         fun assertLastProgress(progress: Float) {
140             waitForCondition { progress == progressHistory.lastOrNull() }
141         }
142     }
143 
144     private companion object {
145         private const val MIN_ANIMATION_EVENTS = 5
146     }
147 }
148