1 /* 2 * Copyright (C) 2021 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.server.wm.flicker 18 19 import android.app.Instrumentation 20 import android.platform.test.rule.NavigationModeRule 21 import android.platform.test.rule.PressHomeRule 22 import android.platform.test.rule.UnlockScreenRule 23 import android.view.Surface 24 import android.view.WindowManagerPolicyConstants 25 import androidx.annotation.VisibleForTesting 26 import com.android.server.wm.flicker.assertions.AssertionData 27 import com.android.server.wm.flicker.assertions.FlickerSubject 28 import com.android.server.wm.flicker.dsl.AssertionTag 29 import com.android.server.wm.flicker.dsl.FlickerBuilder 30 import com.android.server.wm.flicker.helpers.SampleAppHelper 31 import com.android.server.wm.flicker.rules.ChangeDisplayOrientationRule 32 import com.android.server.wm.flicker.rules.LaunchAppRule 33 import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule 34 import com.android.server.wm.flicker.traces.eventlog.EventLogSubject 35 import com.android.server.wm.flicker.traces.layers.LayerTraceEntrySubject 36 import com.android.server.wm.flicker.traces.layers.LayersTraceSubject 37 import com.android.server.wm.flicker.traces.region.RegionTraceSubject 38 import com.android.server.wm.flicker.traces.windowmanager.WindowManagerStateSubject 39 import com.android.server.wm.flicker.traces.windowmanager.WindowManagerTraceSubject 40 import com.android.server.wm.traces.common.FlickerComponentName 41 import org.junit.rules.RuleChain 42 import org.junit.rules.TestRule 43 44 /** 45 * Specification of a flicker test for JUnit ParameterizedRunner class 46 */ 47 data class FlickerTestParameter( 48 @JvmField val config: MutableMap<String, Any?>, 49 private val nameOverride: String? = null 50 ) { 51 private var internalFlicker: Flicker? = null 52 53 private val flicker: Flicker get() = internalFlicker ?: error("Flicker not initialized") 54 private val name: String get() = nameOverride ?: defaultName(this) 55 56 internal val isInitialized: Boolean get() = internalFlicker != null 57 internal val result: FlickerResult? get() = internalFlicker?.result 58 59 /** 60 * If the initial screen rotation is 90 (landscape) or 180 (seascape) degrees 61 */ 62 val isLandscapeOrSeascapeAtStart: Boolean 63 get() = startRotation == Surface.ROTATION_90 || startRotation == Surface.ROTATION_270 64 65 /** 66 * Initial screen rotation (see [Surface] for values) 67 * 68 * Defaults to [Surface.ROTATION_0] 69 */ 70 val startRotation: Int 71 get() = config.getOrDefault(START_ROTATION, Surface.ROTATION_0) as Int 72 73 /** 74 * Final screen rotation (see [Surface] for values) 75 * 76 * Defaults to [startRotation] 77 */ 78 val endRotation: Int 79 get() = config.getOrDefault(END_ROTATION, startRotation) as Int 80 81 /** 82 * Navigation mode, such as 3 button or gestural. 83 * 84 * See [WindowManagerPolicyConstants].NAV_BAR_MODE_* for possible values 85 * 86 * Defaults to [WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY] 87 */ 88 val navBarMode: String 89 get() = config.getOrDefault(NAV_BAR_MODE, 90 WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY) as String 91 92 val navBarModeName 93 get() = when (this.navBarMode) { 94 WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY -> "3_BUTTON_NAV" 95 WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY -> "2_BUTTON_NAV" 96 WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY -> "GESTURAL_NAV" 97 else -> "UNKNOWN_NAV_BAR_MODE(${this.navBarMode}" 98 } 99 100 val isGesturalNavigation = 101 navBarMode == WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY 102 103 /** 104 * Clean the internal flicker reference (cache) 105 */ clearnull106 fun clear() { 107 internalFlicker?.clear() 108 } 109 110 /** 111 * Builds a flicker object and assigns it to the test parameters 112 */ initializenull113 fun initialize(builder: FlickerBuilder, testName: String) { 114 internalFlicker = builder 115 .withTestName { "${testName}_$name" } 116 .repeat { config.getOrDefault(REPETITIONS, 1) as Int } 117 .build(TransitionRunnerWithRules(getTestSetupRules(builder.instrumentation))) 118 } 119 120 /** 121 * Execute [assertion] on the initial state of a WM trace (before transition) 122 * 123 * @param assertion Assertion predicate 124 */ assertWmStartnull125 fun assertWmStart(assertion: WindowManagerStateSubject.() -> Unit) { 126 val assertionData = buildWmStartAssertion(assertion) 127 this.flicker.checkAssertion(assertionData) 128 } 129 130 /** 131 * Execute [assertion] on the final state of a WM trace (after transition) 132 * 133 * @param assertion Assertion predicate 134 */ assertWmEndnull135 fun assertWmEnd(assertion: WindowManagerStateSubject.() -> Unit) { 136 val assertionData = buildWmEndAssertion(assertion) 137 this.flicker.checkAssertion(assertionData) 138 } 139 140 /** 141 * Execute [assertion] on a WM trace 142 * 143 * @param assertion Assertion predicate 144 */ assertWmnull145 fun assertWm(assertion: WindowManagerTraceSubject.() -> Unit) { 146 val assertionData = buildWMAssertion(assertion) 147 this.flicker.checkAssertion(assertionData) 148 } 149 150 /** 151 * Execute [assertion] on a user defined moment ([tag]) of a WM trace 152 * 153 * @param assertion Assertion predicate 154 */ assertWmTagnull155 fun assertWmTag(tag: String, assertion: WindowManagerStateSubject.() -> Unit) { 156 val assertionData = buildWMTagAssertion(tag, assertion) 157 this.flicker.checkAssertion(assertionData) 158 } 159 160 /** 161 * Execute [assertion] on the visible region of a component on the WM trace 162 * 163 * @param component The component for which we want to get the visible region for to run the 164 * assertion on 165 * @param assertion Assertion predicate 166 */ assertWmVisibleRegionnull167 fun assertWmVisibleRegion( 168 vararg components: FlickerComponentName, 169 assertion: RegionTraceSubject.() -> Unit 170 ) { 171 val assertionData = buildWmVisibleRegionAssertion(components = components, assertion) 172 this.flicker.checkAssertion(assertionData) 173 } 174 175 /** 176 * Execute [assertion] on the initial state of a SF trace (before transition) 177 * 178 * @param assertion Assertion predicate 179 */ assertLayersStartnull180 fun assertLayersStart(assertion: LayerTraceEntrySubject.() -> Unit) { 181 val assertionData = buildLayersStartAssertion(assertion) 182 this.flicker.checkAssertion(assertionData) 183 } 184 185 /** 186 * Execute [assertion] on the final state of a SF trace (after transition) 187 * 188 * @param assertion Assertion predicate 189 */ assertLayersEndnull190 fun assertLayersEnd(assertion: LayerTraceEntrySubject.() -> Unit) { 191 val assertionData = buildLayersEndAssertion(assertion) 192 this.flicker.checkAssertion(assertionData) 193 } 194 195 /** 196 * Execute [assertion] on a SF trace 197 * 198 * @param assertion Assertion predicate 199 */ assertLayersnull200 fun assertLayers(assertion: LayersTraceSubject.() -> Unit) { 201 val assertionData = buildLayersAssertion(assertion) 202 this.flicker.checkAssertion(assertionData) 203 } 204 205 /** 206 * Execute [assertion] on a user defined moment ([tag]) of a SF trace 207 * 208 * @param assertion Assertion predicate 209 */ assertLayersTagnull210 fun assertLayersTag(tag: String, assertion: LayerTraceEntrySubject.() -> Unit) { 211 val assertionData = buildLayersTagAssertion(tag, assertion) 212 this.flicker.checkAssertion(assertionData) 213 } 214 215 /** 216 * Execute [assertion] on the visible region of a component on the layers trace 217 * 218 * @param components The components for which we want to get the visible region for to run the 219 * assertion on. The assertion will run on the union of the regions of these components. 220 * @param useCompositionEngineRegionOnly If true, uses only the region calculated from the 221 * Composition Engine (CE) -- visibleRegion in the proto definition. Otherwise calculates 222 * the visible region when the information is not available from the CE 223 * @param assertion Assertion predicate 224 */ 225 @JvmOverloads assertLayersVisibleRegionnull226 fun assertLayersVisibleRegion( 227 vararg components: FlickerComponentName, 228 useCompositionEngineRegionOnly: Boolean = true, 229 assertion: RegionTraceSubject.() -> Unit 230 ) { 231 val assertionData = buildLayersVisibleRegionAssertion( 232 components = components, useCompositionEngineRegionOnly, assertion) 233 this.flicker.checkAssertion(assertionData) 234 } 235 236 /** 237 * Execute [assertion] on a sequence of event logs 238 * 239 * @param assertion Assertion predicate 240 */ assertEventLognull241 fun assertEventLog(assertion: EventLogSubject.() -> Unit) { 242 val assertionData = buildEventLogAssertion(assertion) 243 this.flicker.checkAssertion(assertionData) 244 } 245 246 /** 247 * Create the default flicker test setup rules. In order: 248 * - unlock device 249 * - change orientation 250 * - change navigation mode 251 * - launch an app 252 * - remove all apps 253 * - go to home screen 254 * 255 * (b/186740751) An app should be launched because, after changing the navigation mode, 256 * the first app launch is handled as a screen size change (similar to a rotation), this 257 * causes different problems during testing (e.g. IME now shown on app launch) 258 */ getTestSetupRulesnull259 fun getTestSetupRules(instrumentation: Instrumentation): TestRule = 260 RuleChain.outerRule(UnlockScreenRule()) 261 .around(NavigationModeRule(navBarMode)) 262 .around(LaunchAppRule(SampleAppHelper(instrumentation))) 263 .around(RemoveAllTasksButHomeRule()) 264 .around(ChangeDisplayOrientationRule(startRotation)) 265 .around(PressHomeRule()) 266 267 override fun toString(): String = name 268 269 companion object { 270 internal const val REPETITIONS = "repetitions" 271 internal const val START_ROTATION = "startRotation" 272 internal const val END_ROTATION = "endRotation" 273 internal const val NAV_BAR_MODE = "navBarMode" 274 275 @VisibleForTesting 276 @JvmStatic 277 fun buildWmStartAssertion(assertion: WindowManagerStateSubject.() -> Unit): AssertionData = 278 AssertionData(tag = AssertionTag.START, 279 expectedSubjectClass = WindowManagerStateSubject::class, 280 assertion = assertion as FlickerSubject.() -> Unit) 281 282 @VisibleForTesting 283 @JvmStatic 284 fun buildWmEndAssertion(assertion: WindowManagerStateSubject.() -> Unit): AssertionData = 285 AssertionData(tag = AssertionTag.END, 286 expectedSubjectClass = WindowManagerStateSubject::class, 287 assertion = assertion as FlickerSubject.() -> Unit) 288 289 @VisibleForTesting 290 @JvmStatic 291 fun buildWMAssertion(assertion: WindowManagerTraceSubject.() -> Unit): AssertionData { 292 val closedAssertion: WindowManagerTraceSubject.() -> Unit = { 293 this.clear() 294 assertion() 295 this.forAllEntries() 296 } 297 return AssertionData(tag = AssertionTag.ALL, 298 expectedSubjectClass = WindowManagerTraceSubject::class, 299 assertion = closedAssertion as FlickerSubject.() -> Unit) 300 } 301 302 @VisibleForTesting 303 @JvmStatic 304 fun buildWMTagAssertion( 305 tag: String, 306 assertion: WindowManagerStateSubject.() -> Unit 307 ): AssertionData = AssertionData(tag = tag, 308 expectedSubjectClass = WindowManagerStateSubject::class, 309 assertion = assertion as FlickerSubject.() -> Unit) 310 311 @VisibleForTesting 312 @JvmStatic 313 fun buildWmVisibleRegionAssertion( 314 vararg components: FlickerComponentName, 315 assertion: RegionTraceSubject.() -> Unit 316 ): AssertionData { 317 val closedAssertion: WindowManagerTraceSubject.() -> Unit = { 318 this.clear() 319 // convert WindowManagerTraceSubject to RegionTraceSubject 320 val regionTraceSubject = visibleRegion(*components) 321 // add assertions to the regionTraceSubject's AssertionChecker 322 assertion(regionTraceSubject) 323 // loop through all entries to validate assertions 324 regionTraceSubject.forAllEntries() 325 } 326 327 return AssertionData(tag = AssertionTag.ALL, 328 expectedSubjectClass = WindowManagerTraceSubject::class, 329 assertion = closedAssertion as FlickerSubject.() -> Unit) 330 } 331 332 @VisibleForTesting 333 @JvmStatic 334 fun buildLayersStartAssertion(assertion: LayerTraceEntrySubject.() -> Unit): AssertionData = 335 AssertionData(tag = AssertionTag.START, 336 expectedSubjectClass = LayerTraceEntrySubject::class, 337 assertion = assertion as FlickerSubject.() -> Unit) 338 339 @VisibleForTesting 340 @JvmStatic 341 fun buildLayersEndAssertion(assertion: LayerTraceEntrySubject.() -> Unit): AssertionData = 342 AssertionData(tag = AssertionTag.END, 343 expectedSubjectClass = LayerTraceEntrySubject::class, 344 assertion = assertion as FlickerSubject.() -> Unit) 345 346 @VisibleForTesting 347 @JvmStatic 348 fun buildLayersAssertion(assertion: LayersTraceSubject.() -> Unit): AssertionData { 349 val closedAssertion: LayersTraceSubject.() -> Unit = { 350 this.clear() 351 assertion() 352 this.forAllEntries() 353 } 354 355 return AssertionData(tag = AssertionTag.ALL, 356 expectedSubjectClass = LayersTraceSubject::class, 357 assertion = closedAssertion as FlickerSubject.() -> Unit) 358 } 359 360 @VisibleForTesting 361 @JvmStatic 362 fun buildLayersTagAssertion( 363 tag: String, 364 assertion: LayerTraceEntrySubject.() -> Unit 365 ): AssertionData = AssertionData(tag = tag, 366 expectedSubjectClass = LayerTraceEntrySubject::class, 367 assertion = assertion as FlickerSubject.() -> Unit) 368 369 @VisibleForTesting 370 @JvmOverloads 371 @JvmStatic 372 fun buildLayersVisibleRegionAssertion( 373 vararg components: FlickerComponentName, 374 useCompositionEngineRegionOnly: Boolean = true, 375 assertion: RegionTraceSubject.() -> Unit 376 ): AssertionData { 377 val closedAssertion: LayersTraceSubject.() -> Unit = { 378 this.clear() 379 // convert LayersTraceSubject to RegionTraceSubject 380 val regionTraceSubject = 381 visibleRegion(components = components, useCompositionEngineRegionOnly) 382 383 // add assertions to the regionTraceSubject's AssertionChecker 384 assertion(regionTraceSubject) 385 // loop through all entries to validate assertions 386 regionTraceSubject.forAllEntries() 387 } 388 389 return AssertionData(tag = AssertionTag.ALL, 390 expectedSubjectClass = LayersTraceSubject::class, 391 assertion = closedAssertion as FlickerSubject.() -> Unit) 392 } 393 394 @JvmStatic 395 fun buildEventLogAssertion(assertion: EventLogSubject.() -> Unit): AssertionData = 396 AssertionData(tag = AssertionTag.ALL, 397 expectedSubjectClass = EventLogSubject::class, 398 assertion = assertion as FlickerSubject.() -> Unit) 399 400 fun defaultName(test: FlickerTestParameter) = buildString { 401 append(Surface.rotationToString(test.startRotation)) 402 if (test.endRotation != test.startRotation) { 403 append("_${Surface.rotationToString(test.endRotation)}") 404 } 405 if (test.navBarMode.isNotEmpty()) { 406 append("_${test.navBarModeName}") 407 } 408 } 409 } 410 }