1 /*
2  * Copyright (C) 2020 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 @file:JvmName("CommonAssertions")
18 
19 package com.android.server.wm.flicker
20 
21 import android.tools.common.PlatformConsts
22 import android.tools.common.flicker.subject.region.RegionSubject
23 import android.tools.common.traces.component.ComponentNameMatcher
24 import android.tools.common.traces.component.IComponentNameMatcher
25 import android.tools.common.traces.wm.WindowManagerTrace
26 import android.tools.device.flicker.legacy.LegacyFlickerTest
27 import android.tools.device.helpers.WindowUtils
28 
29 /**
30  * Checks that [ComponentNameMatcher.STATUS_BAR] window is visible and above the app windows in all
31  * WM trace entries
32  */
33 fun LegacyFlickerTest.statusBarWindowIsAlwaysVisible() {
34     assertWm { this.isAboveAppWindowVisible(ComponentNameMatcher.STATUS_BAR) }
35 }
36 
37 /**
38  * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows in all WM
39  * trace entries
40  */
41 fun LegacyFlickerTest.navBarWindowIsAlwaysVisible() {
42     assertWm { this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR) }
43 }
44 
45 /**
46  * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows at the
47  * start and end of the WM trace
48  */
49 fun LegacyFlickerTest.navBarWindowIsVisibleAtStartAndEnd() {
50     this.navBarWindowIsVisibleAtStart()
51     this.navBarWindowIsVisibleAtEnd()
52 }
53 
54 /**
55  * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows at the
56  * start of the WM trace
57  */
58 fun LegacyFlickerTest.navBarWindowIsVisibleAtStart() {
59     assertWmStart { this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR) }
60 }
61 
62 /**
63  * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows at the end
64  * of the WM trace
65  */
66 fun LegacyFlickerTest.navBarWindowIsVisibleAtEnd() {
67     assertWmEnd { this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR) }
68 }
69 
70 /**
71  * Checks that [ComponentNameMatcher.TASK_BAR] window is visible and above the app windows in all WM
72  * trace entries
73  */
74 fun LegacyFlickerTest.taskBarWindowIsAlwaysVisible() {
75     assertWm { this.isAboveAppWindowVisible(ComponentNameMatcher.TASK_BAR) }
76 }
77 
78 /**
79  * Checks that [ComponentNameMatcher.TASK_BAR] window is visible and above the app windows in all WM
80  * trace entries
81  */
82 fun LegacyFlickerTest.taskBarWindowIsVisibleAtEnd() {
83     assertWmEnd { this.isAboveAppWindowVisible(ComponentNameMatcher.TASK_BAR) }
84 }
85 
86 /**
87  * If [allStates] is true, checks if the stack space of all displays is fully covered by any visible
88  * layer, during the whole transitions
89  *
90  * Otherwise, checks if the stack space of all displays is fully covered by any visible layer, at
91  * the start and end of the transition
92  *
93  * @param allStates if all states should be checked, othersie, just initial and final
94  */
95 @JvmOverloads
96 fun LegacyFlickerTest.entireScreenCovered(allStates: Boolean = true) {
97     if (allStates) {
98         assertLayers {
99             this.invoke("entireScreenCovered") { entry ->
100                 entry.entry.displays
101                     .filter { it.isOn }
102                     .forEach { display ->
103                         entry.visibleRegion().coversAtLeast(display.layerStackSpace)
104                     }
105             }
106         }
107     } else {
108         assertLayersStart {
109             this.entry.displays
110                 .filter { it.isOn }
111                 .forEach { display -> this.visibleRegion().coversAtLeast(display.layerStackSpace) }
112         }
113         assertLayersEnd {
114             this.entry.displays
115                 .filter { it.isOn }
116                 .forEach { display -> this.visibleRegion().coversAtLeast(display.layerStackSpace) }
117         }
118     }
119 }
120 
121 /** Checks that [ComponentNameMatcher.NAV_BAR] layer is visible at the start of the SF trace */
122 fun LegacyFlickerTest.navBarLayerIsVisibleAtStart() {
123     assertLayersStart { this.isVisible(ComponentNameMatcher.NAV_BAR) }
124 }
125 
126 /** Checks that [ComponentNameMatcher.NAV_BAR] layer is visible at the end of the SF trace */
127 fun LegacyFlickerTest.navBarLayerIsVisibleAtEnd() {
128     assertLayersEnd { this.isVisible(ComponentNameMatcher.NAV_BAR) }
129 }
130 
131 /**
132  * Checks that [ComponentNameMatcher.NAV_BAR] layer is visible at the start and end of the SF trace
133  */
134 fun LegacyFlickerTest.navBarLayerIsVisibleAtStartAndEnd() {
135     this.navBarLayerIsVisibleAtStart()
136     this.navBarLayerIsVisibleAtEnd()
137 }
138 
139 /**
140  * Checks that [ComponentNameMatcher.TASK_BAR] layer is visible at the start and end of the SF trace
141  */
142 fun LegacyFlickerTest.taskBarLayerIsVisibleAtStartAndEnd() {
143     this.taskBarLayerIsVisibleAtStart()
144     this.taskBarLayerIsVisibleAtEnd()
145 }
146 
147 /** Checks that [ComponentNameMatcher.TASK_BAR] layer is visible at the start of the SF trace */
148 fun LegacyFlickerTest.taskBarLayerIsVisibleAtStart() {
149     assertLayersStart { this.isVisible(ComponentNameMatcher.TASK_BAR) }
150 }
151 
152 /** Checks that [ComponentNameMatcher.TASK_BAR] layer is visible at the end of the SF trace */
153 fun LegacyFlickerTest.taskBarLayerIsVisibleAtEnd() {
154     assertLayersEnd { this.isVisible(ComponentNameMatcher.TASK_BAR) }
155 }
156 
157 /**
158  * Checks that [ComponentNameMatcher.STATUS_BAR] layer is visible at the start and end of the SF
159  * trace
160  */
161 fun LegacyFlickerTest.statusBarLayerIsVisibleAtStartAndEnd() {
162     assertLayersStart { this.isVisible(ComponentNameMatcher.STATUS_BAR) }
163     assertLayersEnd { this.isVisible(ComponentNameMatcher.STATUS_BAR) }
164 }
165 
166 /**
167  * Asserts that the [ComponentNameMatcher.NAV_BAR] layer is at the correct position at the start of
168  * the SF trace
169  */
170 fun LegacyFlickerTest.navBarLayerPositionAtStart() {
171     assertLayersStart {
172         val display =
173             this.entry.displays.firstOrNull { !it.isVirtual } ?: error("There is no display!")
174         this.visibleRegion(ComponentNameMatcher.NAV_BAR)
175             .coversExactly(
176                 WindowUtils.getNavigationBarPosition(display, scenario.isGesturalNavigation)
177             )
178     }
179 }
180 
181 /**
182  * Asserts that the [ComponentNameMatcher.NAV_BAR] layer is at the correct position at the end of
183  * the SF trace
184  */
185 fun LegacyFlickerTest.navBarLayerPositionAtEnd() {
186     assertLayersEnd {
187         val display =
188             this.entry.displays.minByOrNull { it.id }
189                 ?: throw RuntimeException("There is no display!")
190         this.visibleRegion(ComponentNameMatcher.NAV_BAR)
191             .coversExactly(
192                 WindowUtils.getNavigationBarPosition(display, scenario.isGesturalNavigation)
193             )
194     }
195 }
196 
197 /**
198  * Asserts that the [ComponentNameMatcher.NAV_BAR] layer is at the correct position at the start and
199  * end of the SF trace
200  */
201 fun LegacyFlickerTest.navBarLayerPositionAtStartAndEnd() {
202     navBarLayerPositionAtStart()
203     navBarLayerPositionAtEnd()
204 }
205 
206 /**
207  * Asserts that the [ComponentNameMatcher.STATUS_BAR] layer is at the correct position at the start
208  * of the SF trace
209  */
210 fun LegacyFlickerTest.statusBarLayerPositionAtStart(
211     wmTrace: WindowManagerTrace? = this.reader.readWmTrace()
212 ) {
213     // collect navbar position for the equivalent WM state
214     val state = wmTrace?.entries?.firstOrNull() ?: error("WM state missing in $this")
215     val display = state.getDisplay(PlatformConsts.DEFAULT_DISPLAY) ?: error("Display not found")
216     val navBarPosition = WindowUtils.getExpectedStatusBarPosition(display)
217     assertLayersStart {
218         this.visibleRegion(ComponentNameMatcher.STATUS_BAR).coversExactly(navBarPosition)
219     }
220 }
221 
222 /**
223  * Asserts that the [ComponentNameMatcher.STATUS_BAR] layer is at the correct position at the end of
224  * the SF trace
225  */
226 fun LegacyFlickerTest.statusBarLayerPositionAtEnd(
227     wmTrace: WindowManagerTrace? = this.reader.readWmTrace()
228 ) {
229     // collect navbar position for the equivalent WM state
230     val state = wmTrace?.entries?.lastOrNull() ?: error("WM state missing in $this")
231     val display = state.getDisplay(PlatformConsts.DEFAULT_DISPLAY) ?: error("Display not found")
232     val navBarPosition = WindowUtils.getExpectedStatusBarPosition(display)
233     assertLayersEnd {
234         this.visibleRegion(ComponentNameMatcher.STATUS_BAR).coversExactly(navBarPosition)
235     }
236 }
237 
238 /**
239  * Asserts that the [ComponentNameMatcher.STATUS_BAR] layer is at the correct position at the start
240  * and end of the SF trace
241  */
242 fun LegacyFlickerTest.statusBarLayerPositionAtStartAndEnd() {
243     statusBarLayerPositionAtStart()
244     statusBarLayerPositionAtEnd()
245 }
246 
247 /**
248  * Asserts that the visibleRegion of the [ComponentNameMatcher.SNAPSHOT] layer can cover the
249  * visibleRegion of the given app component exactly
250  */
251 fun LegacyFlickerTest.snapshotStartingWindowLayerCoversExactlyOnApp(
252     component: IComponentNameMatcher
253 ) {
254     assertLayers {
255         invoke("snapshotStartingWindowLayerCoversExactlyOnApp") {
256             val snapshotLayers =
257                 it.subjects.filter { subject ->
258                     ComponentNameMatcher.SNAPSHOT.layerMatchesAnyOf(subject.layer) &&
259                         subject.isVisible
260                 }
261             val visibleAreas =
262                 snapshotLayers
263                     .mapNotNull { snapshotLayer -> snapshotLayer.layer.visibleRegion }
264                     .toTypedArray()
265             val snapshotRegion = RegionSubject(visibleAreas, timestamp)
266             // Verify the size of snapshotRegion covers appVisibleRegion exactly in animation.
267             if (snapshotRegion.region.isNotEmpty) {
268                 val appVisibleRegion = it.visibleRegion(component)
269                 snapshotRegion.coversExactly(appVisibleRegion.region)
270             }
271         }
272     }
273 }
274 
275 /**
276  * Asserts that:
277  * ```
278  *     [originalLayer] is visible at the start of the trace
279  *     [originalLayer] becomes invisible during the trace and (in the same entry) [newLayer]
280  *         becomes visible
281  *     [newLayer] remains visible until the end of the trace
282  *
283  * @param originalLayer
284  * ```
285  *
286  * Layer that should be visible at the start
287  *
288  * @param newLayer Layer that should be visible at the end
289  * @param ignoreEntriesWithRotationLayer If entries with a visible rotation layer should be ignored
290  *
291  * ```
292  *      when checking the transition. If true we will not fail the assertion if a rotation layer is
293  *      visible to fill the gap between the [originalLayer] being visible and the [newLayer] being
294  *      visible.
295  * @param ignoreSnapshot
296  * ```
297  *
298  * If the snapshot layer should be ignored during the transition
299  *
300  * ```
301  *     (useful mostly for app launch)
302  * @param ignoreSplashscreen
303  * ```
304  *
305  * If the splashscreen layer should be ignored during the transition.
306  *
307  * ```
308  *      If true then we will allow for a splashscreen to be shown before the layer is shown,
309  *      otherwise we won't and the layer must appear immediately.
310  * ```
311  */
312 fun LegacyFlickerTest.replacesLayer(
313     originalLayer: IComponentNameMatcher,
314     newLayer: IComponentNameMatcher,
315     ignoreEntriesWithRotationLayer: Boolean = false,
316     ignoreSnapshot: Boolean = false,
317     ignoreSplashscreen: Boolean = true
318 ) {
319     assertLayers {
320         val assertion = this.isVisible(originalLayer)
321 
322         if (ignoreEntriesWithRotationLayer) {
323             assertion.then().isVisible(ComponentNameMatcher.ROTATION, isOptional = true)
324         }
325         if (ignoreSnapshot) {
326             assertion.then().isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
327         }
328         if (ignoreSplashscreen) {
329             assertion.then().isSplashScreenVisibleFor(newLayer, isOptional = true)
330         }
331 
332         assertion.then().isVisible(newLayer)
333     }
334 
335     assertLayersStart { this.isVisible(originalLayer).isInvisible(newLayer) }
336 
337     assertLayersEnd { this.isInvisible(originalLayer).isVisible(newLayer) }
338 }
339