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