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.wm.shell.flicker.utils
20 
21 import android.tools.common.Rotation
22 import android.tools.common.datatypes.Region
23 import android.tools.common.flicker.subject.layers.LayerTraceEntrySubject
24 import android.tools.common.flicker.subject.layers.LayersTraceSubject
25 import android.tools.common.traces.component.IComponentMatcher
26 import android.tools.device.flicker.legacy.LegacyFlickerTest
27 import android.tools.device.helpers.WindowUtils
28 
29 fun LegacyFlickerTest.appPairsDividerIsVisibleAtEnd() {
30     assertLayersEnd { this.isVisible(APP_PAIR_SPLIT_DIVIDER_COMPONENT) }
31 }
32 
33 fun LegacyFlickerTest.appPairsDividerIsInvisibleAtEnd() {
34     assertLayersEnd { this.notContains(APP_PAIR_SPLIT_DIVIDER_COMPONENT) }
35 }
36 
37 fun LegacyFlickerTest.appPairsDividerBecomesVisible() {
38     assertLayers {
39         this.isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
40             .then()
41             .isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
42     }
43 }
44 
45 fun LegacyFlickerTest.splitScreenEntered(
46     component1: IComponentMatcher,
47     component2: IComponentMatcher,
48     fromOtherApp: Boolean,
49     appExistAtStart: Boolean = true
50 ) {
51     if (fromOtherApp) {
52         if (appExistAtStart) {
53             appWindowIsInvisibleAtStart(component1)
54         } else {
55             appWindowIsNotContainAtStart(component1)
56         }
57     } else {
58         appWindowIsVisibleAtStart(component1)
59     }
60     if (appExistAtStart) {
61         appWindowIsInvisibleAtStart(component2)
62     } else {
63         appWindowIsNotContainAtStart(component2)
64     }
65     splitScreenDividerIsInvisibleAtStart()
66 
67     appWindowIsVisibleAtEnd(component1)
68     appWindowIsVisibleAtEnd(component2)
69     splitScreenDividerIsVisibleAtEnd()
70 }
71 
72 fun LegacyFlickerTest.splitScreenDismissed(
73     component1: IComponentMatcher,
74     component2: IComponentMatcher,
75     toHome: Boolean
76 ) {
77     appWindowIsVisibleAtStart(component1)
78     appWindowIsVisibleAtStart(component2)
79     splitScreenDividerIsVisibleAtStart()
80 
81     appWindowIsInvisibleAtEnd(component1)
82     if (toHome) {
83         appWindowIsInvisibleAtEnd(component2)
84     } else {
85         appWindowIsVisibleAtEnd(component2)
86     }
87     splitScreenDividerIsInvisibleAtEnd()
88 }
89 
90 fun LegacyFlickerTest.splitScreenDividerIsVisibleAtStart() {
91     assertLayersStart { this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
92 }
93 
94 fun LegacyFlickerTest.splitScreenDividerIsVisibleAtEnd() {
95     assertLayersEnd { this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
96 }
97 
98 fun LegacyFlickerTest.splitScreenDividerIsInvisibleAtStart() {
99     assertLayersStart { this.isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
100 }
101 
102 fun LegacyFlickerTest.splitScreenDividerIsInvisibleAtEnd() {
103     assertLayersEnd { this.isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
104 }
105 
106 fun LegacyFlickerTest.splitScreenDividerBecomesVisible() {
107     layerBecomesVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
108 }
109 
110 fun LegacyFlickerTest.splitScreenDividerBecomesInvisible() {
111     assertLayers {
112         this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
113             .then()
114             .isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
115     }
116 }
117 
118 fun LegacyFlickerTest.layerBecomesVisible(component: IComponentMatcher) {
119     assertLayers { this.isInvisible(component).then().isVisible(component) }
120 }
121 
122 fun LegacyFlickerTest.layerBecomesInvisible(component: IComponentMatcher) {
123     assertLayers { this.isVisible(component).then().isInvisible(component) }
124 }
125 
126 fun LegacyFlickerTest.layerIsVisibleAtEnd(component: IComponentMatcher) {
127     assertLayersEnd { this.isVisible(component) }
128 }
129 
130 fun LegacyFlickerTest.layerKeepVisible(component: IComponentMatcher) {
131     assertLayers { this.isVisible(component) }
132 }
133 
134 fun LegacyFlickerTest.splitAppLayerBoundsBecomesVisible(
135     component: IComponentMatcher,
136     landscapePosLeft: Boolean,
137     portraitPosTop: Boolean
138 ) {
139     assertLayers {
140         this.notContains(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component), isOptional = true)
141             .then()
142             .isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component))
143             .then()
144             .splitAppLayerBoundsSnapToDivider(
145                 component,
146                 landscapePosLeft,
147                 portraitPosTop,
148                 scenario.endRotation
149             )
150     }
151 }
152 
153 fun LegacyFlickerTest.splitAppLayerBoundsBecomesVisibleByDrag(component: IComponentMatcher) {
154     assertLayers {
155         this.notContains(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component), isOptional = true)
156             .then()
157             .isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component))
158             .then()
159             // TODO(b/245472831): Verify the component should snap to divider.
160             .isVisible(component)
161     }
162 }
163 
164 fun LegacyFlickerTest.splitAppLayerBoundsBecomesInvisible(
165     component: IComponentMatcher,
166     landscapePosLeft: Boolean,
167     portraitPosTop: Boolean
168 ) {
169     assertLayers {
170         this.splitAppLayerBoundsSnapToDivider(
171                 component,
172                 landscapePosLeft,
173                 portraitPosTop,
174                 scenario.endRotation
175             )
176             .then()
177             .isVisible(component, true)
178             .then()
179             .isInvisible(component)
180     }
181 }
182 
183 fun LegacyFlickerTest.splitAppLayerBoundsIsVisibleAtEnd(
184     component: IComponentMatcher,
185     landscapePosLeft: Boolean,
186     portraitPosTop: Boolean
187 ) {
188     assertLayersEnd {
189         splitAppLayerBoundsSnapToDivider(
190             component,
191             landscapePosLeft,
192             portraitPosTop,
193             scenario.endRotation
194         )
195     }
196 }
197 
198 fun LegacyFlickerTest.splitAppLayerBoundsKeepVisible(
199     component: IComponentMatcher,
200     landscapePosLeft: Boolean,
201     portraitPosTop: Boolean
202 ) {
203     assertLayers {
204         splitAppLayerBoundsSnapToDivider(
205             component,
206             landscapePosLeft,
207             portraitPosTop,
208             scenario.endRotation
209         )
210     }
211 }
212 
213 fun LegacyFlickerTest.splitAppLayerBoundsChanges(
214     component: IComponentMatcher,
215     landscapePosLeft: Boolean,
216     portraitPosTop: Boolean
217 ) {
218     assertLayers {
219         if (landscapePosLeft) {
220             splitAppLayerBoundsSnapToDivider(
221                 component,
222                 landscapePosLeft,
223                 portraitPosTop,
224                 scenario.endRotation
225             )
226         } else {
227             splitAppLayerBoundsSnapToDivider(
228                     component,
229                     landscapePosLeft,
230                     portraitPosTop,
231                     scenario.endRotation
232                 )
233                 .then()
234                 .isInvisible(component)
235                 .then()
236                 .splitAppLayerBoundsSnapToDivider(
237                     component,
238                     landscapePosLeft,
239                     portraitPosTop,
240                     scenario.endRotation
241                 )
242         }
243     }
244 }
245 
246 fun LayersTraceSubject.splitAppLayerBoundsSnapToDivider(
247     component: IComponentMatcher,
248     landscapePosLeft: Boolean,
249     portraitPosTop: Boolean,
250     rotation: Rotation
251 ): LayersTraceSubject {
252     return invoke("splitAppLayerBoundsSnapToDivider") {
253         it.splitAppLayerBoundsSnapToDivider(component, landscapePosLeft, portraitPosTop, rotation)
254     }
255 }
256 
257 fun LayerTraceEntrySubject.splitAppLayerBoundsSnapToDivider(
258     component: IComponentMatcher,
259     landscapePosLeft: Boolean,
260     portraitPosTop: Boolean,
261     rotation: Rotation
262 ): LayerTraceEntrySubject {
263     val displayBounds = WindowUtils.getDisplayBounds(rotation)
264     return invoke {
265         val dividerRegion =
266             layer(SPLIT_SCREEN_DIVIDER_COMPONENT)?.visibleRegion?.region
267                 ?: error("$SPLIT_SCREEN_DIVIDER_COMPONENT component not found")
268         visibleRegion(component)
269             .coversAtMost(
270                 if (displayBounds.width > displayBounds.height) {
271                     if (landscapePosLeft) {
272                         Region.from(
273                             0,
274                             0,
275                             (dividerRegion.bounds.left + dividerRegion.bounds.right) / 2,
276                             displayBounds.bounds.bottom
277                         )
278                     } else {
279                         Region.from(
280                             (dividerRegion.bounds.left + dividerRegion.bounds.right) / 2,
281                             0,
282                             displayBounds.bounds.right,
283                             displayBounds.bounds.bottom
284                         )
285                     }
286                 } else {
287                     if (portraitPosTop) {
288                         Region.from(
289                             0,
290                             0,
291                             displayBounds.bounds.right,
292                             (dividerRegion.bounds.top + dividerRegion.bounds.bottom) / 2
293                         )
294                     } else {
295                         Region.from(
296                             0,
297                             (dividerRegion.bounds.top + dividerRegion.bounds.bottom) / 2,
298                             displayBounds.bounds.right,
299                             displayBounds.bounds.bottom
300                         )
301                     }
302                 }
303             )
304     }
305 }
306 
307 fun LegacyFlickerTest.appWindowBecomesVisible(component: IComponentMatcher) {
308     assertWm {
309         this.isAppWindowInvisible(component)
310             .then()
311             .notContains(component, isOptional = true)
312             .then()
313             .isAppWindowInvisible(component, isOptional = true)
314             .then()
315             .isAppWindowVisible(component)
316     }
317 }
318 
319 fun LegacyFlickerTest.appWindowBecomesInvisible(component: IComponentMatcher) {
320     assertWm { this.isAppWindowVisible(component).then().isAppWindowInvisible(component) }
321 }
322 
323 fun LegacyFlickerTest.appWindowIsVisibleAtStart(component: IComponentMatcher) {
324     assertWmStart { this.isAppWindowVisible(component) }
325 }
326 
327 fun LegacyFlickerTest.appWindowIsVisibleAtEnd(component: IComponentMatcher) {
328     assertWmEnd { this.isAppWindowVisible(component) }
329 }
330 
331 fun LegacyFlickerTest.appWindowIsInvisibleAtStart(component: IComponentMatcher) {
332     assertWmStart { this.isAppWindowInvisible(component) }
333 }
334 
335 fun LegacyFlickerTest.appWindowIsInvisibleAtEnd(component: IComponentMatcher) {
336     assertWmEnd { this.isAppWindowInvisible(component) }
337 }
338 
339 fun LegacyFlickerTest.appWindowIsNotContainAtStart(component: IComponentMatcher) {
340     assertWmStart { this.notContains(component) }
341 }
342 
343 fun LegacyFlickerTest.appWindowKeepVisible(component: IComponentMatcher) {
344     assertWm { this.isAppWindowVisible(component) }
345 }
346 
347 fun LegacyFlickerTest.dockedStackDividerIsVisibleAtEnd() {
348     assertLayersEnd { this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT) }
349 }
350 
351 fun LegacyFlickerTest.dockedStackDividerBecomesVisible() {
352     assertLayers {
353         this.isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
354             .then()
355             .isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
356     }
357 }
358 
359 fun LegacyFlickerTest.dockedStackDividerBecomesInvisible() {
360     assertLayers {
361         this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
362             .then()
363             .isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
364     }
365 }
366 
367 fun LegacyFlickerTest.dockedStackDividerNotExistsAtEnd() {
368     assertLayersEnd { this.notContains(DOCKED_STACK_DIVIDER_COMPONENT) }
369 }
370 
371 fun LegacyFlickerTest.appPairsPrimaryBoundsIsVisibleAtEnd(
372     rotation: Rotation,
373     primaryComponent: IComponentMatcher
374 ) {
375     assertLayersEnd {
376         val dividerRegion =
377             layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT)?.visibleRegion?.region
378                 ?: error("$APP_PAIR_SPLIT_DIVIDER_COMPONENT component not found")
379         visibleRegion(primaryComponent).overlaps(getPrimaryRegion(dividerRegion, rotation))
380     }
381 }
382 
383 fun LegacyFlickerTest.dockedStackPrimaryBoundsIsVisibleAtEnd(
384     rotation: Rotation,
385     primaryComponent: IComponentMatcher
386 ) {
387     assertLayersEnd {
388         val dividerRegion =
389             layer(DOCKED_STACK_DIVIDER_COMPONENT)?.visibleRegion?.region
390                 ?: error("$DOCKED_STACK_DIVIDER_COMPONENT component not found")
391         visibleRegion(primaryComponent).overlaps(getPrimaryRegion(dividerRegion, rotation))
392     }
393 }
394 
395 fun LegacyFlickerTest.appPairsSecondaryBoundsIsVisibleAtEnd(
396     rotation: Rotation,
397     secondaryComponent: IComponentMatcher
398 ) {
399     assertLayersEnd {
400         val dividerRegion =
401             layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT)?.visibleRegion?.region
402                 ?: error("$APP_PAIR_SPLIT_DIVIDER_COMPONENT component not found")
403         visibleRegion(secondaryComponent).overlaps(getSecondaryRegion(dividerRegion, rotation))
404     }
405 }
406 
407 fun LegacyFlickerTest.dockedStackSecondaryBoundsIsVisibleAtEnd(
408     rotation: Rotation,
409     secondaryComponent: IComponentMatcher
410 ) {
411     assertLayersEnd {
412         val dividerRegion =
413             layer(DOCKED_STACK_DIVIDER_COMPONENT)?.visibleRegion?.region
414                 ?: error("$DOCKED_STACK_DIVIDER_COMPONENT component not found")
415         visibleRegion(secondaryComponent).overlaps(getSecondaryRegion(dividerRegion, rotation))
416     }
417 }
418 
419 fun getPrimaryRegion(dividerRegion: Region, rotation: Rotation): Region {
420     val displayBounds = WindowUtils.getDisplayBounds(rotation)
421     return if (rotation.isRotated()) {
422         Region.from(
423             0,
424             0,
425             dividerRegion.bounds.left + WindowUtils.dockedStackDividerInset,
426             displayBounds.bounds.bottom
427         )
428     } else {
429         Region.from(
430             0,
431             0,
432             displayBounds.bounds.right,
433             dividerRegion.bounds.top + WindowUtils.dockedStackDividerInset
434         )
435     }
436 }
437 
438 fun getSecondaryRegion(dividerRegion: Region, rotation: Rotation): Region {
439     val displayBounds = WindowUtils.getDisplayBounds(rotation)
440     return if (rotation.isRotated()) {
441         Region.from(
442             dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset,
443             0,
444             displayBounds.bounds.right,
445             displayBounds.bounds.bottom
446         )
447     } else {
448         Region.from(
449             0,
450             dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset,
451             displayBounds.bounds.right,
452             displayBounds.bounds.bottom
453         )
454     }
455 }
456