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 import org.junit.rules.TestRule 30 31 /** Build Flicker tests using Flicker DSL */ 32 @FlickerDslMarker 33 class FlickerBuilder( 34 private val instrumentation: Instrumentation, 35 private val outputDir: File = getDefaultFlickerOutputDir(), 36 private val wmHelper: WindowManagerStateHelper = 37 WindowManagerStateHelper(instrumentation, clearCacheAfterParsing = false), 38 private val setupCommands: MutableList<FlickerTestData.() -> Any> = mutableListOf(), 39 private val transitionCommands: MutableList<FlickerTestData.() -> Any> = mutableListOf(), 40 private val teardownCommands: MutableList<FlickerTestData.() -> Any> = mutableListOf(), 41 val device: UiDevice = UiDevice.getInstance(instrumentation), 42 private val rules: MutableList<TestRule> = mutableListOf(), 43 private val traceMonitors: MutableList<ITransitionMonitor> = ALL_MONITORS.toMutableList(), 44 ) { 45 private var usingExistingTraces = false 46 47 /** 48 * Configure a [ScreenRecorder]. 49 * 50 * By default, the tracing is always active. To disable tracing return null 51 */ <lambda>null52 fun withScreenRecorder(screenRecorder: () -> ScreenRecorder?): FlickerBuilder = apply { 53 traceMonitors.removeIf { it is ScreenRecorder } 54 addMonitor(screenRecorder()) 55 } 56 <lambda>null57 fun withoutScreenRecorder(): FlickerBuilder = apply { 58 traceMonitors.removeIf { it is ScreenRecorder } 59 } 60 61 /** Defines the setup commands executed before the [transitions] to test */ <lambda>null62 fun setup(commands: FlickerTestData.() -> Unit): FlickerBuilder = apply { 63 setupCommands.add(commands) 64 } 65 66 /** Defines the teardown commands executed after the [transitions] to test */ <lambda>null67 fun teardown(commands: FlickerTestData.() -> Unit): FlickerBuilder = apply { 68 teardownCommands.add(commands) 69 } 70 71 /** Defines the commands that trigger the behavior to test */ <lambda>null72 fun transitions(command: FlickerTestData.() -> Unit): FlickerBuilder = apply { 73 require(!usingExistingTraces) { 74 "Can't update transition after calling usingExistingTraces" 75 } 76 transitionCommands.add(command) 77 } 78 79 /** Adds JUnit rules to be executed with the provided transitions. */ withRulesnull80 fun withRules(vararg rules: TestRule) { 81 this.rules.addAll(rules) 82 } 83 84 data class TraceFiles( 85 val wmTrace: File, 86 val perfetto: File, 87 val wmTransitions: File, 88 val shellTransitions: File, 89 val eventLog: File, 90 ) 91 92 /** Use pre-executed results instead of running transitions to get the traces */ <lambda>null93 fun usingExistingTraces(_traceFiles: () -> TraceFiles): FlickerBuilder = apply { 94 val traceFiles = _traceFiles() 95 // Remove all trace monitor and use only monitor that read from existing trace file 96 this.traceMonitors.clear() 97 addMonitor(NoTraceMonitor { it.addTraceResult(TraceType.WM, traceFiles.wmTrace) }) 98 addMonitor(NoTraceMonitor { it.addTraceResult(TraceType.SF, traceFiles.perfetto) }) 99 addMonitor(NoTraceMonitor { it.addTraceResult(TraceType.TRANSACTION, traceFiles.perfetto) }) 100 addMonitor( 101 NoTraceMonitor { 102 it.addTraceResult(TraceType.LEGACY_WM_TRANSITION, traceFiles.wmTransitions) 103 } 104 ) 105 addMonitor( 106 NoTraceMonitor { 107 it.addTraceResult(TraceType.LEGACY_SHELL_TRANSITION, traceFiles.shellTransitions) 108 } 109 ) 110 addMonitor(NoTraceMonitor { it.addTraceResult(TraceType.EVENT_LOG, traceFiles.eventLog) }) 111 112 // Remove all transitions execution 113 this.transitionCommands.clear() 114 this.usingExistingTraces = true 115 } 116 117 /** Creates a new Flicker runner based on the current builder configuration */ buildnull118 fun build(): FlickerTestData { 119 return FlickerTestDataImpl( 120 instrumentation, 121 device, 122 outputDir, 123 traceMonitors, 124 setupCommands, 125 transitionCommands, 126 teardownCommands, 127 rules, 128 wmHelper, 129 ) 130 } 131 132 /** Returns a copy of the current builder with the changes of [block] applied */ copynull133 fun copy(block: FlickerBuilder.() -> Unit) = 134 FlickerBuilder( 135 instrumentation, 136 outputDir.absoluteFile, 137 wmHelper, 138 setupCommands.toMutableList(), 139 transitionCommands.toMutableList(), 140 teardownCommands.toMutableList(), 141 device, 142 rules.toMutableList(), 143 traceMonitors.toMutableList(), 144 ) 145 .apply(block) 146 147 private fun addMonitor(newMonitor: ITransitionMonitor?) { 148 require(!usingExistingTraces) { "Can't add monitors after calling usingExistingTraces" } 149 150 if (newMonitor != null) { 151 traceMonitors.add(newMonitor) 152 } 153 } 154 } 155