1 /*
<lambda>null2  * 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.flicker.subject.layers
18 
19 import android.tools.flicker.subject.FlickerTraceSubject
20 import android.tools.flicker.subject.exceptions.ExceptionMessageBuilder
21 import android.tools.flicker.subject.exceptions.InvalidElementException
22 import android.tools.flicker.subject.exceptions.InvalidPropertyException
23 import android.tools.flicker.subject.region.RegionTraceSubject
24 import android.tools.function.AssertionPredicate
25 import android.tools.io.Reader
26 import android.tools.traces.component.ComponentNameMatcher
27 import android.tools.traces.component.EdgeExtensionComponentMatcher
28 import android.tools.traces.component.IComponentMatcher
29 import android.tools.traces.component.IComponentNameMatcher
30 import android.tools.traces.component.SurfaceViewBackgroundMatcher
31 import android.tools.traces.region.RegionTrace
32 import android.tools.traces.surfaceflinger.Layer
33 import android.tools.traces.surfaceflinger.LayersTrace
34 import java.util.function.Predicate
35 
36 /**
37  * Subject for [LayersTrace] objects, used to make assertions over behaviors that occur throughout a
38  * whole trace
39  *
40  * To make assertions over a trace it is recommended to create a subject using
41  * [LayersTraceSubject](myTrace).
42  *
43  * Example:
44  * ```
45  *    val trace = LayersTraceParser().parse(myTraceFile)
46  *    val subject = LayersTraceSubject(trace)
47  *        .contains("ValidLayer")
48  *        .notContains("ImaginaryLayer")
49  *        .coversExactly(DISPLAY_AREA)
50  *        .forAllEntries()
51  * ```
52  *
53  * Example2:
54  * ```
55  *    val trace = LayersTraceParser().parse(myTraceFile)
56  *    val subject = LayersTraceSubject(trace) {
57  *        check("Custom check") { myCustomAssertion(this) }
58  *    }
59  * ```
60  */
61 class LayersTraceSubject
62 @JvmOverloads
63 constructor(val trace: LayersTrace, override val reader: Reader? = null) :
64     FlickerTraceSubject<LayerTraceEntrySubject>(),
65     ILayerSubject<LayersTraceSubject, RegionTraceSubject> {
66 
67     override val subjects by lazy {
68         trace.entries.map { LayerTraceEntrySubject(it, reader, trace) }
69     }
70 
71     /** {@inheritDoc} */
72     override fun then(): LayersTraceSubject = apply { super.then() }
73 
74     /** {@inheritDoc} */
75     override fun isEmpty(): LayersTraceSubject = apply {
76         check { "Trace is empty" }.that(trace.entries.isEmpty()).isEqual(true)
77     }
78 
79     /** {@inheritDoc} */
80     override fun isNotEmpty(): LayersTraceSubject = apply {
81         check { "Trace is not empty" }.that(trace.entries.isNotEmpty()).isEqual(true)
82     }
83 
84     /** {@inheritDoc} */
85     override fun layer(name: String, frameNumber: Long): LayerSubject {
86         val result = subjects.firstNotNullOfOrNull { it.layer(name, frameNumber) }
87 
88         if (result == null) {
89             val errorMsgBuilder =
90                 ExceptionMessageBuilder()
91                     .forSubject(this)
92                     .forInvalidElement(name, expectElementExists = true)
93                     .addExtraDescription("Frame number", frameNumber)
94             throw InvalidElementException(errorMsgBuilder)
95         }
96 
97         return result
98     }
99 
100     /** @return List of [LayerSubject]s matching [name] in the order they appear on the trace */
101     fun layers(name: String): List<LayerSubject> =
102         subjects.mapNotNull { it.layer { layer -> layer.name.contains(name) } }
103 
104     /**
105      * @return List of [LayerSubject]s matching [predicate] in the order they appear on the trace
106      */
107     fun layers(predicate: Predicate<Layer>): List<LayerSubject> =
108         subjects.mapNotNull { it.layer { layer -> predicate.test(layer) } }
109 
110     /** Checks that all visible layers are shown for more than one consecutive entry */
111     fun visibleLayersShownMoreThanOneConsecutiveEntry(
112         ignoreLayers: List<IComponentMatcher> = VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS
113     ): LayersTraceSubject = apply {
114         visibleEntriesShownMoreThanOneConsecutiveTime { subject ->
115             subject.entry.visibleLayers
116                 .filter { visibleLayer ->
117                     ignoreLayers.none { matcher -> matcher.layerMatchesAnyOf(visibleLayer) }
118                 }
119                 .map { it.name }
120                 .toSet()
121         }
122     }
123 
124     /** {@inheritDoc} */
125     override fun notContains(componentMatcher: IComponentMatcher): LayersTraceSubject =
126         notContains(componentMatcher, isOptional = false)
127 
128     /** See [notContains] */
129     fun notContains(componentMatcher: IComponentMatcher, isOptional: Boolean): LayersTraceSubject =
130         apply {
131             addAssertion("notContains(${componentMatcher.toLayerIdentifier()})", isOptional) {
132                 it.notContains(componentMatcher)
133             }
134         }
135 
136     /** {@inheritDoc} */
137     override fun contains(componentMatcher: IComponentMatcher): LayersTraceSubject =
138         contains(componentMatcher, isOptional = false)
139 
140     /** See [contains] */
141     fun contains(componentMatcher: IComponentMatcher, isOptional: Boolean): LayersTraceSubject =
142         apply {
143             addAssertion("contains(${componentMatcher.toLayerIdentifier()})", isOptional) {
144                 it.contains(componentMatcher)
145             }
146         }
147 
148     /** {@inheritDoc} */
149     override fun isVisible(componentMatcher: IComponentMatcher): LayersTraceSubject =
150         isVisible(componentMatcher, isOptional = false)
151 
152     /** See [isVisible] */
153     fun isVisible(componentMatcher: IComponentMatcher, isOptional: Boolean): LayersTraceSubject =
154         apply {
155             addAssertion("isVisible(${componentMatcher.toLayerIdentifier()})", isOptional) {
156                 it.isVisible(componentMatcher)
157             }
158         }
159 
160     /** {@inheritDoc} */
161     override fun isInvisible(componentMatcher: IComponentMatcher): LayersTraceSubject =
162         isInvisible(componentMatcher, isOptional = false)
163 
164     /** See [isInvisible] */
165     fun isInvisible(componentMatcher: IComponentMatcher, isOptional: Boolean): LayersTraceSubject =
166         apply {
167             addAssertion("isInvisible(${componentMatcher.toLayerIdentifier()})", isOptional) {
168                 it.isInvisible(componentMatcher)
169             }
170         }
171 
172     /** {@inheritDoc} */
173     override fun isSplashScreenVisibleFor(
174         componentMatcher: IComponentNameMatcher
175     ): LayersTraceSubject = isSplashScreenVisibleFor(componentMatcher, isOptional = false)
176 
177     /** {@inheritDoc} */
178     override fun hasColor(componentMatcher: IComponentMatcher): LayersTraceSubject = apply {
179         addAssertion("hasColor(${componentMatcher.toLayerIdentifier()})") {
180             it.hasColor(componentMatcher)
181         }
182     }
183 
184     /** {@inheritDoc} */
185     override fun hasNoColor(componentMatcher: IComponentMatcher): LayersTraceSubject = apply {
186         addAssertion("hasNoColor(${componentMatcher.toLayerIdentifier()})") {
187             it.hasNoColor(componentMatcher)
188         }
189     }
190 
191     /** {@inheritDoc} */
192     override fun hasRoundedCorners(componentMatcher: IComponentMatcher): LayersTraceSubject =
193         apply {
194             addAssertion("hasRoundedCorners(${componentMatcher.toLayerIdentifier()})") {
195                 it.hasRoundedCorners(componentMatcher)
196             }
197         }
198 
199     /** {@inheritDoc} */
200     override fun hasNoRoundedCorners(componentMatcher: IComponentMatcher): LayersTraceSubject =
201         apply {
202             addAssertion("hasNoRoundedCorners(${componentMatcher.toLayerIdentifier()})") {
203                 it.hasNoRoundedCorners(componentMatcher)
204             }
205         }
206 
207     /** See [isSplashScreenVisibleFor] */
208     fun isSplashScreenVisibleFor(
209         componentMatcher: IComponentNameMatcher,
210         isOptional: Boolean,
211     ): LayersTraceSubject = apply {
212         addAssertion(
213             "isSplashScreenVisibleFor(${componentMatcher.toLayerIdentifier()})",
214             isOptional,
215         ) {
216             it.isSplashScreenVisibleFor(componentMatcher)
217         }
218     }
219 
220     /** See [visibleRegion] */
221     fun visibleRegion(): RegionTraceSubject =
222         visibleRegion(componentMatcher = null, useCompositionEngineRegionOnly = false)
223 
224     /** See [visibleRegion] */
225     fun visibleRegion(componentMatcher: IComponentMatcher?): RegionTraceSubject =
226         visibleRegion(componentMatcher, useCompositionEngineRegionOnly = false)
227 
228     /** {@inheritDoc} */
229     override fun visibleRegion(
230         componentMatcher: IComponentMatcher?,
231         useCompositionEngineRegionOnly: Boolean,
232     ): RegionTraceSubject {
233         val regionTrace =
234             RegionTrace(
235                 componentMatcher,
236                 subjects.map {
237                     it.visibleRegion(componentMatcher, useCompositionEngineRegionOnly).regionEntry
238                 },
239             )
240         return RegionTraceSubject(regionTrace, reader)
241     }
242 
243     fun atLeastOneEntryContainsOneDisplayOn(): LayersTraceSubject = apply {
244         isNotEmpty()
245         val anyEntryWithDisplayOn =
246             subjects.any { it.entry.displays.any { display -> display.isOn } }
247         if (!anyEntryWithDisplayOn) {
248             val errorMsgBuilder =
249                 ExceptionMessageBuilder()
250                     .forInvalidProperty("Display")
251                     .setExpected("At least one display On in any entry")
252                     .setActual("No displays on in any entry")
253             throw InvalidPropertyException(errorMsgBuilder)
254         }
255     }
256 
257     override fun containsAtLeastOneDisplay(): LayersTraceSubject = apply {
258         addAssertion("containAtLeastOneDisplay", isOptional = false) {
259             it.containsAtLeastOneDisplay()
260         }
261     }
262 
263     /** Executes a custom [assertion] on the current subject */
264     @JvmOverloads
265     operator fun invoke(
266         name: String,
267         isOptional: Boolean = false,
268         assertion: AssertionPredicate<LayerTraceEntrySubject>,
269     ): LayersTraceSubject = apply { addAssertion(name, isOptional, assertion) }
270 
271     fun hasFrameSequence(name: String, frameNumbers: Iterable<Long>): LayersTraceSubject =
272         hasFrameSequence(ComponentNameMatcher("", name), frameNumbers)
273 
274     fun hasFrameSequence(
275         componentMatcher: IComponentMatcher,
276         frameNumbers: Iterable<Long>,
277     ): LayersTraceSubject = apply {
278         val firstFrame = frameNumbers.first()
279         val entries =
280             trace.entries
281                 .asSequence()
282                 // map entry to buffer layers with name
283                 .map { it.getLayerWithBuffer(componentMatcher) }
284                 // removing all entries without the layer
285                 .filterNotNull()
286                 // removing all entries with the same frame number
287                 .distinctBy { it.currFrame }
288                 // drop until the first frame we are interested in
289                 .dropWhile { layer -> layer.currFrame != firstFrame }
290 
291         var numFound = 0
292         val frameNumbersMatch =
293             entries
294                 .zip(frameNumbers.asSequence()) { layer, frameNumber ->
295                     numFound++
296                     layer.currFrame == frameNumber
297                 }
298                 .all { it }
299         val allFramesFound = frameNumbers.count() == numFound
300         if (!allFramesFound || !frameNumbersMatch) {
301             val errorMsgBuilder =
302                 ExceptionMessageBuilder()
303                     .forSubject(this)
304                     .forInvalidElement(
305                         componentMatcher.toLayerIdentifier(),
306                         expectElementExists = true,
307                     )
308                     .addExtraDescription("With frame sequence", frameNumbers.joinToString(","))
309             throw InvalidElementException(errorMsgBuilder)
310         }
311     }
312 
313     /** Run the assertions for all trace entries within the specified time range */
314     fun forSystemUpTimeRange(startTime: Long, endTime: Long) {
315         val subjectsInRange =
316             subjects.filter { it.entry.timestamp.systemUptimeNanos in startTime..endTime }
317         assertionsChecker.test(subjectsInRange)
318     }
319 
320     /**
321      * User-defined entry point for the trace entry with [timestamp]
322      *
323      * @param timestamp of the entry
324      */
325     @JvmOverloads
326     fun getEntryBySystemUpTime(
327         timestamp: Long,
328         byElapsedTimestamp: Boolean = false,
329     ): LayerTraceEntrySubject {
330         return if (byElapsedTimestamp) {
331             subjects.first { it.entry.elapsedTimestamp == timestamp }
332         } else {
333             subjects.first { it.entry.timestamp.systemUptimeNanos == timestamp }
334         }
335     }
336 
337     companion object {
338         @JvmField
339         val VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS =
340             listOf(
341                 ComponentNameMatcher.SPLASH_SCREEN,
342                 ComponentNameMatcher.SNAPSHOT,
343                 ComponentNameMatcher.IME_SNAPSHOT,
344                 ComponentNameMatcher.PIP_CONTENT_OVERLAY,
345                 ComponentNameMatcher.EDGE_BACK_GESTURE_HANDLER,
346                 ComponentNameMatcher.COLOR_FADE,
347                 ComponentNameMatcher.TRANSITION_SNAPSHOT,
348                 ComponentNameMatcher.FLOATING_ROTATION_BUTTON,
349                 ComponentNameMatcher.WIRED_CHARGING_ANIMATION,
350                 EdgeExtensionComponentMatcher(),
351                 SurfaceViewBackgroundMatcher(),
352             )
353     }
354 }
355