1 /* 2 * Copyright (C) 2024 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.flicker.legacy 18 19 import android.app.Instrumentation 20 import android.tools.flicker.Utils.ALL_MONITORS 21 import android.tools.io.TraceType 22 import android.tools.traces.getDefaultFlickerOutputDir 23 import android.tools.traces.monitors.ITransitionMonitor 24 import android.tools.traces.monitors.NoTraceMonitor 25 import android.tools.traces.monitors.ScreenRecorder 26 import android.tools.traces.parsers.WindowManagerStateHelper 27 import androidx.test.uiautomator.UiDevice 28 import java.io.File 29 30 /** Build Flicker tests using Flicker DSL */ 31 @FlickerDslMarker 32 class FlickerBuilder( 33 private val instrumentation: Instrumentation, 34 private val outputDir: File = getDefaultFlickerOutputDir(), 35 private val wmHelper: WindowManagerStateHelper = 36 WindowManagerStateHelper(instrumentation, clearCacheAfterParsing = false), 37 private val setupCommands: MutableList<FlickerTestData.() -> Any> = mutableListOf(), 38 private val transitionCommands: MutableList<FlickerTestData.() -> Any> = mutableListOf(), 39 private val teardownCommands: MutableList<FlickerTestData.() -> Any> = mutableListOf(), 40 val device: UiDevice = UiDevice.getInstance(instrumentation), 41 private val traceMonitors: MutableList<ITransitionMonitor> = ALL_MONITORS.toMutableList() 42 ) { 43 private var usingExistingTraces = false 44 45 /** 46 * Configure a [ScreenRecorder]. 47 * 48 * By default, the tracing is always active. To disable tracing return null 49 */ <lambda>null50 fun withScreenRecorder(screenRecorder: () -> ScreenRecorder?): FlickerBuilder = apply { 51 traceMonitors.removeIf { it is ScreenRecorder } 52 addMonitor(screenRecorder()) 53 } 54 <lambda>null55 fun withoutScreenRecorder(): FlickerBuilder = apply { 56 traceMonitors.removeIf { it is ScreenRecorder } 57 } 58 59 /** Defines the setup commands executed before the [transitions] to test */ <lambda>null60 fun setup(commands: FlickerTestData.() -> Unit): FlickerBuilder = apply { 61 setupCommands.add(commands) 62 } 63 64 /** Defines the teardown commands executed after the [transitions] to test */ <lambda>null65 fun teardown(commands: FlickerTestData.() -> Unit): FlickerBuilder = apply { 66 teardownCommands.add(commands) 67 } 68 69 /** Defines the commands that trigger the behavior to test */ <lambda>null70 fun transitions(command: FlickerTestData.() -> Unit): FlickerBuilder = apply { 71 require(!usingExistingTraces) { 72 "Can't update transition after calling usingExistingTraces" 73 } 74 transitionCommands.add(command) 75 } 76 77 data class TraceFiles( 78 val wmTrace: File, 79 val perfetto: File, 80 val wmTransitions: File, 81 val shellTransitions: File, 82 val eventLog: File 83 ) 84 85 /** Use pre-executed results instead of running transitions to get the traces */ <lambda>null86 fun usingExistingTraces(_traceFiles: () -> TraceFiles): FlickerBuilder = apply { 87 val traceFiles = _traceFiles() 88 // Remove all trace monitor and use only monitor that read from existing trace file 89 this.traceMonitors.clear() 90 addMonitor(NoTraceMonitor { it.addTraceResult(TraceType.WM, traceFiles.wmTrace) }) 91 addMonitor(NoTraceMonitor { it.addTraceResult(TraceType.SF, traceFiles.perfetto) }) 92 addMonitor(NoTraceMonitor { it.addTraceResult(TraceType.TRANSACTION, traceFiles.perfetto) }) 93 addMonitor( 94 NoTraceMonitor { 95 it.addTraceResult(TraceType.LEGACY_WM_TRANSITION, traceFiles.wmTransitions) 96 } 97 ) 98 addMonitor( 99 NoTraceMonitor { 100 it.addTraceResult(TraceType.LEGACY_SHELL_TRANSITION, traceFiles.shellTransitions) 101 } 102 ) 103 addMonitor(NoTraceMonitor { it.addTraceResult(TraceType.EVENT_LOG, traceFiles.eventLog) }) 104 105 // Remove all transitions execution 106 this.transitionCommands.clear() 107 this.usingExistingTraces = true 108 } 109 110 /** Creates a new Flicker runner based on the current builder configuration */ buildnull111 fun build(): FlickerTestData { 112 return FlickerTestDataImpl( 113 instrumentation, 114 device, 115 outputDir, 116 traceMonitors, 117 setupCommands, 118 transitionCommands, 119 teardownCommands, 120 wmHelper 121 ) 122 } 123 124 /** Returns a copy of the current builder with the changes of [block] applied */ copynull125 fun copy(block: FlickerBuilder.() -> Unit) = 126 FlickerBuilder( 127 instrumentation, 128 outputDir.absoluteFile, 129 wmHelper, 130 setupCommands.toMutableList(), 131 transitionCommands.toMutableList(), 132 teardownCommands.toMutableList(), 133 device, 134 traceMonitors.toMutableList(), 135 ) 136 .apply(block) 137 138 private fun addMonitor(newMonitor: ITransitionMonitor?) { 139 require(!usingExistingTraces) { "Can't add monitors after calling usingExistingTraces" } 140 141 if (newMonitor != null) { 142 traceMonitors.add(newMonitor) 143 } 144 } 145 } 146