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.support.test.launcherhelper.ILauncherStrategy 21 import android.util.Log 22 import androidx.test.uiautomator.UiDevice 23 import com.android.server.wm.flicker.assertions.AssertionData 24 import com.android.server.wm.flicker.monitor.ITransitionMonitor 25 import com.android.server.wm.traces.common.layers.LayersTrace 26 import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace 27 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper 28 import java.nio.file.Path 29 30 @DslMarker 31 annotation class FlickerDslMarker 32 33 /** 34 * Defines the runner for the flicker tests. This component is responsible for running the flicker 35 * tests and executing assertions on the traces to check for inconsistent behaviors on 36 * [WindowManagerTrace] and [LayersTrace] 37 */ 38 @FlickerDslMarker 39 class Flicker( 40 /** 41 * Instrumentation to run the tests 42 */ 43 @JvmField val instrumentation: Instrumentation, 44 /** 45 * Test automation component used to interact with the device 46 */ 47 @JvmField val device: UiDevice, 48 /** 49 * Strategy used to interact with the launcher 50 */ 51 @JvmField val launcherStrategy: ILauncherStrategy, 52 /** 53 * Output directory for test results 54 */ 55 @JvmField val outputDir: Path, 56 /** 57 * Test name used to store the test results 58 */ 59 @JvmField val testName: String, 60 /** 61 * Number of times the test should be executed 62 */ 63 @JvmField var repetitions: Int, 64 /** 65 * Enabled tracing monitors 66 */ 67 @JvmField val traceMonitors: List<ITransitionMonitor>, 68 /** 69 * Commands to be executed before each run 70 */ 71 @JvmField val testSetup: List<Flicker.() -> Any>, 72 /** 73 * Commands to be executed before the test 74 */ 75 @JvmField val runSetup: List<Flicker.() -> Any>, 76 /** 77 * Commands to be executed after the test 78 */ 79 @JvmField val testTeardown: List<Flicker.() -> Any>, 80 /** 81 * Commands to be executed after the run 82 */ 83 @JvmField val runTeardown: List<Flicker.() -> Any>, 84 /** 85 * Test commands 86 */ 87 @JvmField val transitions: List<Flicker.() -> Any>, 88 /** 89 * Runner to execute the test transitions 90 */ 91 @JvmField val runner: TransitionRunner, 92 /** 93 * Helper object for WM Synchronization 94 */ 95 val wmHelper: WindowManagerStateHelper 96 ) { 97 var result: FlickerResult? = null 98 private set 99 100 /** 101 * Executes the test transition. 102 * 103 * @throws IllegalStateException If cannot execute the transition 104 */ <lambda>null105 fun execute(): Flicker = apply { 106 val result = runner.execute(this) 107 this.result = result 108 checkHasSuccessfullyExecutedATransitionRun() 109 } 110 111 /** 112 * Asserts if at least a run of the transition of this flicker test has been executed 113 * successfully, indicating that there is something the run the assertions on. 114 */ checkHasSuccessfullyExecutedATransitionRunnull115 private fun checkHasSuccessfullyExecutedATransitionRun() { 116 val result = result 117 if (result == null) { 118 execute() 119 } else { 120 if (result.successfulRuns.isEmpty()) { 121 // Only throw the execution exception here if there are no successful transition 122 // runs, otherwise we want to execute the assertions on the successful runs and only 123 // throw the exception after we have collected the transition assertion data, in 124 // which case the execution exception will be thrown in the 125 // result.checkForExecutionErrors() call in this.clear(). 126 val executionError = if (result.executionErrors.size == 1) { 127 result.executionErrors[0] 128 } else { 129 result.combinedExecutionError 130 } 131 132 throw executionError 133 } 134 } 135 } 136 137 /** 138 * Run an assertion on the trace 139 * 140 * @param assertion Assertion to run 141 * @throws AssertionError If the assertions fail or the transition crashed 142 */ checkAssertionnull143 fun checkAssertion(assertion: AssertionData) { 144 checkHasSuccessfullyExecutedATransitionRun() 145 val result = result 146 requireNotNull(result) 147 148 val failures = result.checkAssertion(assertion) 149 if (failures.isNotEmpty()) { 150 throw failures.first() 151 } 152 } 153 154 /** 155 * Saves the traces files assertions were run on, clears the cached runner results, and assert 156 * any error that occurred when executing the transitions. 157 */ clearnull158 fun clear() { 159 Log.v(FLICKER_TAG, "Cleaning up spec $testName") 160 runner.cleanUp() 161 result?.checkForExecutionErrors() 162 result = null 163 } 164 165 /** 166 * Runs a set of commands and, at the end, creates a tag containing the device state 167 * 168 * @param tag Identifier for the tag to be created 169 * @param commands Commands to execute before creating the tag 170 * @throws IllegalArgumentException If [tag] cannot be converted to a valid filename 171 */ withTagnull172 fun withTag(tag: String, commands: Flicker.() -> Any) { 173 commands() 174 runner.createTag(this, tag) 175 } 176 createTagnull177 fun createTag(tag: String) { 178 withTag(tag) {} 179 } 180 toStringnull181 override fun toString(): String { 182 return this.testName 183 } 184 } 185