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