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