1 /* 2 * Copyright (C) 2023 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 android.tools.device.flicker.legacy 18 19 import android.app.Instrumentation 20 import android.tools.common.io.TraceType 21 import android.tools.common.traces.surfaceflinger.LayerTraceEntry 22 import android.tools.common.traces.surfaceflinger.LayersTrace 23 import android.tools.common.traces.surfaceflinger.TransactionsTrace 24 import android.tools.common.traces.wm.TransitionsTrace 25 import android.tools.common.traces.wm.WindowManagerState 26 import android.tools.common.traces.wm.WindowManagerTrace 27 import android.tools.device.traces.getDefaultFlickerOutputDir 28 import android.tools.device.traces.monitors.ITransitionMonitor 29 import android.tools.device.traces.monitors.NoTraceMonitor 30 import android.tools.device.traces.monitors.ScreenRecorder 31 import android.tools.device.traces.monitors.events.EventLogMonitor 32 import android.tools.device.traces.monitors.surfaceflinger.LayersTraceMonitor 33 import android.tools.device.traces.monitors.surfaceflinger.TransactionsTraceMonitor 34 import android.tools.device.traces.monitors.wm.ShellTransitionTraceMonitor 35 import android.tools.device.traces.monitors.wm.WindowManagerTraceMonitor 36 import android.tools.device.traces.monitors.wm.WmTransitionTraceMonitor 37 import android.tools.device.traces.parsers.WindowManagerStateHelper 38 import androidx.test.uiautomator.UiDevice 39 import java.io.File 40 41 /** Build Flicker tests using Flicker DSL */ 42 @FlickerDslMarker 43 class FlickerBuilder( 44 private val instrumentation: Instrumentation, 45 private val outputDir: File = getDefaultFlickerOutputDir(), 46 private val wmHelper: WindowManagerStateHelper = 47 WindowManagerStateHelper(instrumentation, clearCacheAfterParsing = false), 48 private val setupCommands: MutableList<FlickerTestData.() -> Any> = mutableListOf(), 49 private val transitionCommands: MutableList<FlickerTestData.() -> Any> = mutableListOf(), 50 private val teardownCommands: MutableList<FlickerTestData.() -> Any> = mutableListOf(), 51 val device: UiDevice = UiDevice.getInstance(instrumentation), 52 private val traceMonitors: MutableList<ITransitionMonitor> = <lambda>null53 mutableListOf<ITransitionMonitor>().also { 54 it.add(WindowManagerTraceMonitor()) 55 it.add(LayersTraceMonitor()) 56 it.add(WmTransitionTraceMonitor()) 57 it.add(ShellTransitionTraceMonitor()) 58 it.add(TransactionsTraceMonitor()) 59 it.add(ScreenRecorder(instrumentation.targetContext)) 60 it.add(EventLogMonitor()) 61 } 62 ) { 63 private var usingExistingTraces = false 64 65 /** Disable [WindowManagerTraceMonitor]. */ <lambda>null66 fun withoutWindowManagerTracing(): FlickerBuilder = apply { withWindowManagerTracing { null } } 67 68 /** 69 * Configure a [WindowManagerTraceMonitor] to obtain [WindowManagerTrace] 70 * 71 * By default, the tracing is always active. To disable tracing return null 72 * 73 * If this tracing is disabled, the assertions for [WindowManagerTrace] and [WindowManagerState] 74 * will not be executed 75 */ withWindowManagerTracingnull76 fun withWindowManagerTracing(traceMonitor: () -> WindowManagerTraceMonitor?): FlickerBuilder = 77 apply { 78 traceMonitors.removeIf { it is WindowManagerTraceMonitor } 79 addMonitor(traceMonitor()) 80 } 81 82 /** Disable [LayersTraceMonitor]. */ <lambda>null83 fun withoutLayerTracing(): FlickerBuilder = apply { withLayerTracing { null } } 84 85 /** 86 * Configure a [LayersTraceMonitor] to obtain [LayersTrace]. 87 * 88 * By default the tracing is always active. To disable tracing return null 89 * 90 * If this tracing is disabled, the assertions for [LayersTrace] and [LayerTraceEntry] will not 91 * be executed 92 */ <lambda>null93 fun withLayerTracing(traceMonitor: () -> LayersTraceMonitor?): FlickerBuilder = apply { 94 traceMonitors.removeIf { it is LayersTraceMonitor } 95 addMonitor(traceMonitor()) 96 } 97 98 /** Disable [WmTransitionTraceMonitor]. */ <lambda>null99 fun withoutTransitionTracing(): FlickerBuilder = apply { withTransitionTracing { null } } 100 101 /** 102 * Configure a [WmTransitionTraceMonitor] to obtain [TransitionsTrace]. 103 * 104 * By default, shell transition tracing is disabled. 105 */ withTransitionTracingnull106 fun withTransitionTracing(traceMonitor: () -> WmTransitionTraceMonitor?): FlickerBuilder = 107 apply { 108 traceMonitors.removeIf { it is WmTransitionTraceMonitor } 109 addMonitor(traceMonitor()) 110 } 111 112 /** Disable [TransactionsTraceMonitor]. */ <lambda>null113 fun withoutTransactionsTracing(): FlickerBuilder = apply { withTransactionsTracing { null } } 114 115 /** 116 * Configure a [TransactionsTraceMonitor] to obtain [TransactionsTrace]. 117 * 118 * By default, shell transition tracing is disabled. 119 */ withTransactionsTracingnull120 fun withTransactionsTracing(traceMonitor: () -> TransactionsTraceMonitor?): FlickerBuilder = 121 apply { 122 traceMonitors.removeIf { it is TransactionsTraceMonitor } 123 addMonitor(traceMonitor()) 124 } 125 126 /** 127 * Configure a [ScreenRecorder]. 128 * 129 * By default, the tracing is always active. To disable tracing return null 130 */ <lambda>null131 fun withScreenRecorder(screenRecorder: () -> ScreenRecorder?): FlickerBuilder = apply { 132 traceMonitors.removeIf { it is ScreenRecorder } 133 addMonitor(screenRecorder()) 134 } 135 <lambda>null136 fun withoutScreenRecorder(): FlickerBuilder = apply { 137 traceMonitors.removeIf { it is ScreenRecorder } 138 } 139 140 /** Defines the setup commands executed before the [transitions] to test */ <lambda>null141 fun setup(commands: FlickerTestData.() -> Unit): FlickerBuilder = apply { 142 setupCommands.add(commands) 143 } 144 145 /** Defines the teardown commands executed after the [transitions] to test */ <lambda>null146 fun teardown(commands: FlickerTestData.() -> Unit): FlickerBuilder = apply { 147 teardownCommands.add(commands) 148 } 149 150 /** Defines the commands that trigger the behavior to test */ <lambda>null151 fun transitions(command: FlickerTestData.() -> Unit): FlickerBuilder = apply { 152 require(!usingExistingTraces) { 153 "Can't update transition after calling usingExistingTraces" 154 } 155 transitionCommands.add(command) 156 } 157 158 data class TraceFiles( 159 val wmTrace: File, 160 val layersTrace: File, 161 val transactions: File, 162 val wmTransitions: File, 163 val shellTransitions: File, 164 val eventLog: File 165 ) 166 167 /** Use pre-executed results instead of running transitions to get the traces */ <lambda>null168 fun usingExistingTraces(_traceFiles: () -> TraceFiles): FlickerBuilder = apply { 169 val traceFiles = _traceFiles() 170 // Remove all trace monitor and use only monitor that read from existing trace file 171 this.traceMonitors.clear() 172 addMonitor(NoTraceMonitor { it.addTraceResult(TraceType.WM, traceFiles.wmTrace) }) 173 addMonitor(NoTraceMonitor { it.addTraceResult(TraceType.SF, traceFiles.layersTrace) }) 174 addMonitor( 175 NoTraceMonitor { it.addTraceResult(TraceType.TRANSACTION, traceFiles.transactions) } 176 ) 177 addMonitor( 178 NoTraceMonitor { it.addTraceResult(TraceType.WM_TRANSITION, traceFiles.wmTransitions) } 179 ) 180 addMonitor( 181 NoTraceMonitor { 182 it.addTraceResult(TraceType.SHELL_TRANSITION, traceFiles.shellTransitions) 183 } 184 ) 185 addMonitor(NoTraceMonitor { it.addTraceResult(TraceType.EVENT_LOG, traceFiles.eventLog) }) 186 187 // Remove all transitions execution 188 this.transitionCommands.clear() 189 this.usingExistingTraces = true 190 } 191 192 /** Creates a new Flicker runner based on the current builder configuration */ buildnull193 fun build(): FlickerTestData { 194 return FlickerTestDataImpl( 195 instrumentation, 196 device, 197 outputDir, 198 traceMonitors, 199 setupCommands, 200 transitionCommands, 201 teardownCommands, 202 wmHelper 203 ) 204 } 205 206 /** Returns a copy of the current builder with the changes of [block] applied */ copynull207 fun copy(block: FlickerBuilder.() -> Unit) = 208 FlickerBuilder( 209 instrumentation, 210 outputDir.absoluteFile, 211 wmHelper, 212 setupCommands.toMutableList(), 213 transitionCommands.toMutableList(), 214 teardownCommands.toMutableList(), 215 device, 216 traceMonitors.toMutableList(), 217 ) 218 .apply(block) 219 220 private fun addMonitor(newMonitor: ITransitionMonitor?) { 221 require(!usingExistingTraces) { "Can't add monitors after calling usingExistingTraces" } 222 223 if (newMonitor != null) { 224 traceMonitors.add(newMonitor) 225 } 226 } 227 } 228