• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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