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