• 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.dsl
18 
19 import android.app.Instrumentation
20 import android.support.test.launcherhelper.ILauncherStrategy
21 import android.support.test.launcherhelper.LauncherStrategyFactory
22 import androidx.test.uiautomator.UiDevice
23 import com.android.server.wm.flicker.Flicker
24 import com.android.server.wm.flicker.FlickerDslMarker
25 import com.android.server.wm.flicker.TransitionRunner
26 import com.android.server.wm.flicker.getDefaultFlickerOutputDir
27 import com.android.server.wm.flicker.monitor.EventLogMonitor
28 import com.android.server.wm.flicker.monitor.ITransitionMonitor
29 import com.android.server.wm.flicker.monitor.LayersTraceMonitor
30 import com.android.server.wm.flicker.monitor.ScreenRecorder
31 import com.android.server.wm.flicker.monitor.WindowManagerTraceMonitor
32 import com.android.server.wm.traces.common.layers.LayersTrace
33 import com.android.server.wm.traces.common.layers.BaseLayerTraceEntry
34 import com.android.server.wm.traces.common.windowmanager.WindowManagerState
35 import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace
36 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
37 import java.nio.file.Path
38 
39 /**
40  * Build Flicker tests using Flicker DSL
41  */
42 @FlickerDslMarker
43 class FlickerBuilder private constructor(
44     internal val instrumentation: Instrumentation,
45     private val launcherStrategy: ILauncherStrategy,
46     private val outputDir: Path,
47     private val wmHelper: WindowManagerStateHelper,
48     private var testName: String,
49     private var iterations: Int,
50     private val setupCommands: TestCommandsBuilder,
51     private val teardownCommands: TestCommandsBuilder,
52     private val transitionCommands: MutableList<Flicker.() -> Any>,
53     val device: UiDevice,
54     private val traceMonitors: MutableList<ITransitionMonitor>
55 ) {
56     /**
57      * Default flicker builder constructor
58      */
59     @JvmOverloads
60     constructor(
61         /**
62          * Instrumentation to run the tests
63          */
64         instrumentation: Instrumentation,
65         /**
66          * Strategy used to interact with the launcher
67          */
68         launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
69             .getInstance(instrumentation).launcherStrategy,
70         /**
71          * Output directory for the test results
72          */
73         outputDir: Path = getDefaultFlickerOutputDir(),
74         /**
75          * Helper object for WM Synchronization
76          */
77         wmHelper: WindowManagerStateHelper = WindowManagerStateHelper(instrumentation),
78         traceMonitors: MutableList<ITransitionMonitor> = mutableListOf<ITransitionMonitor>()
<lambda>null79                 .also {
80                     it.add(WindowManagerTraceMonitor(outputDir))
81                     it.add(LayersTraceMonitor(outputDir))
82                     it.add(ScreenRecorder(instrumentation.targetContext, outputDir))
83                     it.add(EventLogMonitor())
84                 }
85     ) : this(
86         instrumentation,
87         launcherStrategy,
88         outputDir,
89         wmHelper,
90         testName = "",
91         iterations = 1,
92         setupCommands = TestCommandsBuilder(),
93         teardownCommands = TestCommandsBuilder(),
94         transitionCommands = mutableListOf(),
95         device = UiDevice.getInstance(instrumentation),
96         traceMonitors = traceMonitors
97     )
98 
99     /**
100      * Copy constructor
101      */
102     constructor(otherBuilder: FlickerBuilder) : this(
103         otherBuilder.instrumentation,
104         otherBuilder.launcherStrategy,
105         otherBuilder.outputDir.toAbsolutePath(),
106         otherBuilder.wmHelper,
107         otherBuilder.testName,
108         otherBuilder.iterations,
109         TestCommandsBuilder(otherBuilder.setupCommands),
110         TestCommandsBuilder(otherBuilder.teardownCommands),
111         otherBuilder.transitionCommands.toMutableList(),
112         UiDevice.getInstance(otherBuilder.instrumentation),
113         otherBuilder.traceMonitors.toMutableList()
114     )
115 
116     /**
117      * Test name used to store the test results
118      *
119      * If reused throughout the test, only the last value is stored
120      */
<lambda>null121     fun withTestName(testName: () -> String): FlickerBuilder = apply {
122         val name = testName()
123         require(!name.contains(" ")) {
124             "The test tag can not contain spaces since it is a part of the file name"
125         }
126         this.testName = name
127     }
128 
129     /**
130      * Disable [WindowManagerTraceMonitor].
131      */
<lambda>null132     fun withoutWindowManagerTracing(): FlickerBuilder = apply {
133         withWindowManagerTracing { null }
134     }
135 
136     /**
137      * Configure a [WindowManagerTraceMonitor] to obtain [WindowManagerTrace]
138      *
139      * By default the tracing is always active. To disable tracing return null
140      *
141      * If this tracing is disabled, the assertions for [WindowManagerTrace] and
142      * [WindowManagerState] will not be executed
143      */
withWindowManagerTracingnull144     fun withWindowManagerTracing(
145         traceMonitor: (Path) -> WindowManagerTraceMonitor?
146     ): FlickerBuilder = apply {
147         traceMonitors.removeIf { it is WindowManagerTraceMonitor }
148         val newMonitor = traceMonitor(outputDir)
149 
150         if (newMonitor != null) {
151             traceMonitors.add(newMonitor)
152         }
153     }
154 
155     /**
156      * Disable [LayersTraceMonitor].
157      */
<lambda>null158     fun withoutLayerTracing(): FlickerBuilder = apply {
159         withLayerTracing { null }
160     }
161 
162     /**
163      * Configure a [LayersTraceMonitor] to obtain [LayersTrace].
164      *
165      * By default the tracing is always active. To disable tracing return null
166      *
167      * If this tracing is disabled, the assertions for [LayersTrace] and [BaseLayerTraceEntry]
168      * will not be executed
169      */
withLayerTracingnull170     fun withLayerTracing(
171         traceMonitor: (Path) -> LayersTraceMonitor?
172     ): FlickerBuilder = apply {
173         traceMonitors.removeIf { it is LayersTraceMonitor }
174         val newMonitor = traceMonitor(outputDir)
175 
176         if (newMonitor != null) {
177             traceMonitors.add(newMonitor)
178         }
179     }
180 
181     /**
182      * Configure a [ScreenRecorder].
183      *
184      * By default the tracing is always active. To disable tracing return null
185      */
withScreenRecordernull186     fun withScreenRecorder(
187         screenRecorder: (Path) -> ScreenRecorder?
188     ): FlickerBuilder = apply {
189         traceMonitors.removeIf { it is ScreenRecorder }
190         val newMonitor = screenRecorder(outputDir)
191 
192         if (newMonitor != null) {
193             traceMonitors.add(newMonitor)
194         }
195     }
196 
197     /**
198      * Defines how many times the test run should be repeated
199      */
<lambda>null200     fun repeat(predicate: () -> Int): FlickerBuilder = apply {
201         val repeat = predicate()
202         require(repeat >= 1) { "Number of repetitions should be greater or equal to 1" }
203         iterations = repeat
204     }
205 
206     /**
207      * Defines the test ([TestCommandsBuilder.testCommands]) and run ([TestCommandsBuilder.runCommands])
208      * commands executed before the [transitions] to test
209      */
<lambda>null210     fun setup(commands: TestCommandsBuilder.() -> Unit): FlickerBuilder = apply {
211         setupCommands.apply { commands() }
212     }
213 
214     /**
215      * Defines the test ([TestCommandsBuilder.testCommands]) and run ([TestCommandsBuilder.runCommands])
216      * commands executed after the [transitions] to test
217      */
<lambda>null218     fun teardown(commands: TestCommandsBuilder.() -> Unit): FlickerBuilder = apply {
219         teardownCommands.apply { commands() }
220     }
221 
222     /**
223      * Defines the commands that trigger the behavior to test
224      */
<lambda>null225     fun transitions(command: Flicker.() -> Unit): FlickerBuilder = apply {
226         transitionCommands.add(command)
227     }
228 
229     /**
230      * Creates a new Flicker runner based on the current builder configuration
231      */
232     @JvmOverloads
buildnull233     fun build(runner: TransitionRunner = TransitionRunner()) = Flicker(
234         instrumentation,
235         device,
236         launcherStrategy,
237         outputDir,
238         testName,
239         iterations,
240         traceMonitors,
241         setupCommands.buildTestCommands(),
242         setupCommands.buildRunCommands(),
243         teardownCommands.buildTestCommands(),
244         teardownCommands.buildRunCommands(),
245         transitionCommands,
246         runner,
247         wmHelper
248     )
249 
250     /**
251      * Returns a copy of the current builder with the changes of [block] applied
252      */
253     fun copy(block: FlickerBuilder.() -> Unit) = FlickerBuilder(this).apply(block)
254 }
255