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.runner 18 19 import android.app.Instrumentation 20 import android.platform.test.rule.ArtifactSaver 21 import android.tools.common.Logger 22 import android.tools.common.Scenario 23 import android.tools.common.io.TraceType 24 import android.tools.device.flicker.FlickerTag 25 import android.tools.device.flicker.legacy.FlickerTestData 26 import android.tools.device.traces.getCurrentState 27 import android.tools.device.traces.io.ResultWriter 28 import android.tools.device.traces.monitors.NoTraceMonitor 29 import android.tools.device.traces.now 30 import android.tools.device.traces.parsers.WindowManagerStateHelper 31 import android.util.EventLog 32 import java.io.File 33 import org.junit.rules.TestRule 34 import org.junit.runner.Description 35 import org.junit.runners.model.Statement 36 37 /** 38 * Test rule to execute the transition and update [resultWriter] 39 * 40 * @param flicker test definition 41 * @param resultWriter to write 42 * @param scenario to run the transition 43 * @param instrumentation to interact with the device 44 * @param commands to run during the transition 45 * @param wmHelper to stabilize the UI before/after transitions 46 */ 47 class TransitionExecutionRule( 48 private val flicker: FlickerTestData, 49 private val resultWriter: ResultWriter, 50 private val scenario: Scenario, 51 private val instrumentation: Instrumentation = flicker.instrumentation, 52 private val commands: List<FlickerTestData.() -> Any> = flicker.transitions, 53 private val wmHelper: WindowManagerStateHelper = flicker.wmHelper 54 ) : TestRule { 55 private var tags = mutableSetOf<String>() 56 applynull57 override fun apply(base: Statement?, description: Description?): Statement { 58 return object : Statement() { 59 override fun evaluate() { 60 Logger.withTracing("transition") { 61 try { 62 Utils.notifyRunnerProgress(scenario, "Running transition $description") 63 doRunBeforeTransition() 64 commands.forEach { it.invoke(flicker) } 65 base?.evaluate() 66 } catch (e: Throwable) { 67 ArtifactSaver.onError(Utils.expandDescription(description, "transition"), e) 68 throw e 69 } finally { 70 doRunAfterTransition() 71 } 72 } 73 } 74 } 75 } 76 doRunBeforeTransitionnull77 private fun doRunBeforeTransition() { 78 Logger.withTracing("doRunBeforeTransition") { 79 Utils.notifyRunnerProgress(scenario, "Running doRunBeforeTransition") 80 Logger.d(FLICKER_RUNNER_TAG, "doRunBeforeTransition") 81 val now = now() 82 resultWriter.setTransitionStartTime(now) 83 EventLog.writeEvent( 84 FlickerTag.TRANSITION_START, 85 now.unixNanos, 86 now.elapsedNanos, 87 now.systemUptimeNanos 88 ) 89 flicker.setCreateTagListener { doCreateTag(it) } 90 doValidate() 91 } 92 } 93 doRunAfterTransitionnull94 private fun doRunAfterTransition() { 95 Logger.withTracing("doRunAfterTransition") { 96 Utils.notifyRunnerProgress(scenario, "Running doRunAfterTransition") 97 Logger.d(FLICKER_RUNNER_TAG, "doRunAfterTransition") 98 Utils.doWaitForUiStabilize(wmHelper) 99 val now = now() 100 resultWriter.setTransitionEndTime(now) 101 EventLog.writeEvent( 102 FlickerTag.TRANSITION_END, 103 now.unixNanos, 104 now.elapsedNanos, 105 now.systemUptimeNanos 106 ) 107 flicker.clearTagListener() 108 } 109 } 110 doValidatenull111 private fun doValidate() { 112 require(flicker.traceMonitors.isNotEmpty()) { NO_MONITORS_ERROR } 113 require(commands.isNotEmpty() || flicker.traceMonitors.all { it is NoTraceMonitor }) { 114 EMPTY_TRANSITIONS_ERROR 115 } 116 } 117 doValidateTagnull118 private fun doValidateTag(tag: String) { 119 require(!tags.contains(tag)) { "Tag `$tag` already used" } 120 require(!tag.contains(" ")) { "Tag can't contain spaces, instead it was `$tag`" } 121 require(!tag.contains("__")) { "Tag can't `__``, instead it was `$tag`" } 122 } 123 doCreateTagnull124 private fun doCreateTag(tag: String) { 125 Logger.withTracing("doRunAfterTransition") { 126 Utils.notifyRunnerProgress(scenario, "Creating tag $tag") 127 doValidateTag(tag) 128 tags.add(tag) 129 130 val deviceStateBytes = getCurrentState() 131 val wmDumpFile = File.createTempFile(TraceType.WM_DUMP.fileName, tag) 132 val layersDumpFile = File.createTempFile(TraceType.SF_DUMP.fileName, tag) 133 134 wmDumpFile.writeBytes(deviceStateBytes.first) 135 layersDumpFile.writeBytes(deviceStateBytes.second) 136 137 resultWriter.addTraceResult(TraceType.WM_DUMP, wmDumpFile, tag) 138 resultWriter.addTraceResult(TraceType.SF_DUMP, layersDumpFile, tag) 139 } 140 } 141 } 142