• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2021 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 com.android.server.wm.flicker.traces.layers
18 
19 import com.android.server.wm.flicker.assertions.Assertion
20 import com.android.server.wm.flicker.traces.FlickerFailureStrategy
21 import com.android.server.wm.flicker.traces.FlickerTraceSubject
22 import com.android.server.wm.flicker.traces.region.RegionTraceSubject
23 import com.android.server.wm.traces.common.FlickerComponentName
24 import com.android.server.wm.traces.common.layers.Layer
25 import com.android.server.wm.traces.common.layers.LayersTrace
26 import com.android.server.wm.traces.common.region.RegionTrace
27 import com.google.common.truth.FailureMetadata
28 import com.google.common.truth.FailureStrategy
29 import com.google.common.truth.StandardSubjectBuilder
30 import com.google.common.truth.Subject
31 import com.google.common.truth.Subject.Factory
32 
33 /**
34  * Truth subject for [LayersTrace] objects, used to make assertions over behaviors that occur
35  * throughout a whole trace
36  *
37  * To make assertions over a trace it is recommended to create a subject using
38  * [LayersTraceSubject.assertThat](myTrace). Alternatively, it is also possible to use
39  * Truth.assertAbout(LayersTraceSubject.FACTORY), however it will provide less debug
40  * information because it uses Truth's default [FailureStrategy].
41  *
42  * Example:
43  *    val trace = LayersTraceParser.parseFromTrace(myTraceFile)
44  *    val subject = LayersTraceSubject.assertThat(trace)
45  *        .contains("ValidLayer")
46  *        .notContains("ImaginaryLayer")
47  *        .coversExactly(DISPLAY_AREA)
48  *        .forAllEntries()
49  *
50  * Example2:
51  *    val trace = LayersTraceParser.parseFromTrace(myTraceFile)
52  *    val subject = LayersTraceSubject.assertThat(trace) {
53  *        check("Custom check") { myCustomAssertion(this) }
54  *    }
55  */
56 class LayersTraceSubject private constructor(
57     fm: FailureMetadata,
58     val trace: LayersTrace,
59     override val parent: LayersTraceSubject?
60 ) : FlickerTraceSubject<LayerTraceEntrySubject>(fm, trace) {
61     override val selfFacts
62         get() = super.selfFacts.toMutableList()
63     override val subjects by lazy {
64         trace.entries.map { LayerTraceEntrySubject.assertThat(it, trace, this) }
65     }
66 
67     /**
68      * Executes a custom [assertion] on the current subject
69      */
70     operator fun invoke(assertion: Assertion<LayersTrace>): LayersTraceSubject = apply {
71         assertion(this.trace)
72     }
73 
74     /** {@inheritDoc} */
75     override fun then(): LayersTraceSubject = apply { super.then() }
76 
77     fun isEmpty(): LayersTraceSubject = apply {
78         check("Trace is empty").that(trace).isEmpty()
79     }
80 
81     fun isNotEmpty() = apply {
82         check("Trace is not empty").that(trace).isNotEmpty()
83     }
84 
85     /**
86      * @return LayerSubject that can be used to make assertions on a single layer matching
87      * [name] and [frameNumber].
88      */
89     fun layer(name: String, frameNumber: Long): LayerSubject {
90         return subjects
91             .map { it.layer(name, frameNumber) }
92             .firstOrNull { it.isNotEmpty }
93             ?: LayerSubject.assertThat(null, this,
94                 timestamp = subjects.firstOrNull()?.entry?.timestamp ?: 0L)
95     }
96 
97     /**
98      * @return List of [LayerSubject]s matching [name] in the order they appear on the trace
99      */
100     fun layers(name: String): List<LayerSubject> {
101         return subjects
102             .map { it.layer { layer -> layer.name.contains(name) } }
103             .filter { it.isNotEmpty }
104     }
105 
106     /**
107      * @return List of [LayerSubject]s matching [predicate] in the order they appear on the trace
108      */
109     fun layers(predicate: (Layer) -> Boolean): List<LayerSubject> {
110         return subjects
111             .map { it.layer { layer -> predicate(layer) } }
112             .filter { it.isNotEmpty }
113     }
114 
115     /**
116      * Checks that all visible layers are shown for more than one consecutive entry
117      */
118     @JvmOverloads
119     fun visibleLayersShownMoreThanOneConsecutiveEntry(
120         ignoreLayers: List<FlickerComponentName> = listOf(FlickerComponentName.SPLASH_SCREEN,
121             FlickerComponentName.SNAPSHOT)
122     ): LayersTraceSubject = apply {
123         visibleEntriesShownMoreThanOneConsecutiveTime { subject ->
124             subject.entry.visibleLayers
125                 .filter { ignoreLayers.none { component -> component.toLayerName() in it.name } }
126                 .map { it.name }
127                 .toSet()
128         }
129     }
130 
131     /**
132      * Asserts that each entry in the trace doesn't contain a [Layer] with [Layer.name]
133      * containing [component].
134      *
135      * @param component Name of the layer to search
136      * @param isOptional If this assertion is optional or must pass
137      */
138     @JvmOverloads
139     fun notContains(
140         component: FlickerComponentName,
141         isOptional: Boolean = false
142     ): LayersTraceSubject =
143         apply {
144             addAssertion("notContains(${component.toLayerName()})", isOptional) {
145                 it.notContains(component)
146             }
147         }
148 
149     /**
150      * Asserts that each entry in the trace contains a [Layer] with [Layer.name] containing any of
151      * [component].
152      *
153      * @param component Name of the layer to search
154      * @param isOptional If this assertion is optional or must pass
155      */
156     @JvmOverloads
157     fun contains(
158         component: FlickerComponentName,
159         isOptional: Boolean = false
160     ): LayersTraceSubject =
161         apply { addAssertion("contains(${component.toLayerName()})", isOptional) {
162             it.contains(component) }
163         }
164 
165     /**
166      * Asserts that each entry in the trace contains a [Layer] with [Layer.name] containing any of
167      * [component] that is visible.
168      *
169      * @param component Name of the layer to search
170      */
171     @JvmOverloads
172     fun isVisible(
173         component: FlickerComponentName,
174         isOptional: Boolean = false
175     ): LayersTraceSubject =
176         apply { addAssertion("isVisible(${component.toLayerName()})", isOptional) {
177             it.isVisible(component) }
178         }
179 
180     /**
181      * Asserts that each entry in the trace doesn't contain a [Layer] with [Layer.name]
182      * containing [component] or that the layer is not visible .
183      *
184      * @param component Name of the layer to search
185      */
186     @JvmOverloads
187     fun isInvisible(
188         component: FlickerComponentName,
189         isOptional: Boolean = false
190     ): LayersTraceSubject =
191         apply {
192             addAssertion("isInvisible(${component.toLayerName()})", isOptional) {
193                 it.isInvisible(component)
194             }
195         }
196 
197     /**
198      * Asserts that each entry in the trace contains a visible splash screen [Layer] for a [layer]
199      * with [Layer.name] containing any of [component].
200      *
201      * @param component Name of the layer to search
202      */
203     @JvmOverloads
204     fun isSplashScreenVisibleFor(
205         component: FlickerComponentName,
206         isOptional: Boolean = false
207     ): LayersTraceSubject = apply {
208         addAssertion("isSplashScreenVisibleFor(${component.toLayerName()})", isOptional) {
209             it.isSplashScreenVisibleFor(component)
210         }
211     }
212 
213     /**
214      * Obtains the trace of regions occupied by all layers with name containing [components]
215      *
216      * @param components Components to search for
217      * @param useCompositionEngineRegionOnly If true, uses only the region calculated from the
218      *   Composition Engine (CE) -- visibleRegion in the proto definition. Otherwise calculates
219      *   the visible region when the information is not available from the CE
220      */
221     @JvmOverloads
222     fun visibleRegion(
223         vararg components: FlickerComponentName,
224         useCompositionEngineRegionOnly: Boolean = true
225     ): RegionTraceSubject {
226         val regionTrace = RegionTrace(components, subjects.map {
227             it.visibleRegion(components = components, useCompositionEngineRegionOnly)
228                             .regionEntry
229         }.toTypedArray())
230         return RegionTraceSubject.assertThat(regionTrace, this)
231     }
232 
233     /**
234      * Executes a custom [assertion] on the current subject
235      */
236     operator fun invoke(
237         name: String,
238         isOptional: Boolean = false,
239         assertion: Assertion<LayerTraceEntrySubject>
240     ): LayersTraceSubject = apply { addAssertion(name, isOptional, assertion) }
241 
242     fun hasFrameSequence(name: String, frameNumbers: Iterable<Long>): LayersTraceSubject = apply {
243         val firstFrame = frameNumbers.first()
244         val entries = trace.entries.asSequence()
245             // map entry to buffer layers with name
246             .map { it.getLayerWithBuffer(name) }
247             // removing all entries without the layer
248             .filterNotNull()
249             // removing all entries with the same frame number
250             .distinctBy { it.currFrame }
251             // drop until the first frame we are interested in
252             .dropWhile { layer -> layer.currFrame != firstFrame }
253 
254         var numFound = 0
255         val frameNumbersMatch = entries.zip(frameNumbers.asSequence()) { layer, frameNumber ->
256             numFound++
257             layer.currFrame == frameNumber
258         }.all { it }
259         val allFramesFound = frameNumbers.count() == numFound
260         if (!allFramesFound || !frameNumbersMatch) {
261             val message = "Could not find Layer:" + name +
262                 " with frame sequence:" + frameNumbers.joinToString(",") +
263                 " Found:\n" + entries.joinToString("\n")
264             fail(message)
265         }
266     }
267 
268     /**
269      * Run the assertions for all trace entries within the specified time range
270      */
271     fun forRange(startTime: Long, endTime: Long) {
272         val subjectsInRange = subjects.filter { it.entry.timestamp in startTime..endTime }
273         assertionsChecker.test(subjectsInRange)
274     }
275 
276     /**
277      * User-defined entry point for the trace entry with [timestamp]
278      *
279      * @param timestamp of the entry
280      */
281     fun entry(timestamp: Long): LayerTraceEntrySubject =
282         subjects.first { it.entry.timestamp == timestamp }
283 
284     companion object {
285         /**
286          * Boiler-plate Subject.Factory for LayersTraceSubject
287          */
288         private fun getFactory(parent: LayersTraceSubject?): Factory<Subject, LayersTrace> =
289             Factory { fm, subject -> LayersTraceSubject(fm, subject, parent) }
290 
291         /**
292          * Creates a [LayersTraceSubject] to representing a SurfaceFlinger trace,
293          * which can be used to make assertions.
294          *
295          * @param trace SurfaceFlinger trace
296          */
297         @JvmStatic
298         @JvmOverloads
299         fun assertThat(trace: LayersTrace, parent: LayersTraceSubject? = null): LayersTraceSubject {
300             val strategy = FlickerFailureStrategy()
301             val subject = StandardSubjectBuilder.forCustomFailureStrategy(strategy)
302                 .about(getFactory(parent))
303                 .that(trace) as LayersTraceSubject
304             strategy.init(subject)
305             return subject
306         }
307 
308         /**
309          * Static method for getting the subject factory (for use with assertAbout())
310          */
311         @JvmStatic
312         fun entries(parent: LayersTraceSubject?): Factory<Subject, LayersTrace> {
313             return getFactory(parent)
314         }
315     }
316 }
317