• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.wm
18 
19 import android.tools.Rotation
20 import android.tools.flicker.subject.FlickerTraceSubject
21 import android.tools.flicker.subject.region.RegionTraceSubject
22 import android.tools.function.AssertionPredicate
23 import android.tools.io.Reader
24 import android.tools.traces.component.ComponentNameMatcher
25 import android.tools.traces.component.IComponentMatcher
26 import android.tools.traces.region.RegionTrace
27 import android.tools.traces.wm.WindowManagerTrace
28 import android.tools.traces.wm.WindowState
29 import java.util.function.Predicate
30 
31 /**
32  * Subject for [WindowManagerTrace] objects, used to make assertions over behaviors that occur
33  * throughout a whole trace.
34  *
35  * To make assertions over a trace it is recommended to create a subject using
36  * [WindowManagerTraceSubject](myTrace).
37  *
38  * Example:
39  * ```
40  *    val trace = WindowManagerTraceParser().parse(myTraceFile)
41  *    val subject = WindowManagerTraceSubject(trace)
42  *        .contains("ValidWindow")
43  *        .notContains("ImaginaryWindow")
44  *        .showsAboveAppWindow("NavigationBar")
45  *        .forAllEntries()
46  * ```
47  *
48  * Example2:
49  * ```
50  *    val trace = WindowManagerTraceParser().parse(myTraceFile)
51  *    val subject = WindowManagerTraceSubject(trace) {
52  *        check(myCustomAssertion(this)) { "My assertion lazy message" }
53  *    }
54  * ```
55  */
56 class WindowManagerTraceSubject
57 @JvmOverloads
58 constructor(val trace: WindowManagerTrace, override val reader: Reader? = null) :
59     FlickerTraceSubject<WindowManagerStateSubject>(),
60     IWindowManagerSubject<WindowManagerTraceSubject, RegionTraceSubject> {
61 
62     override val subjects by lazy {
63         trace.entries.map { WindowManagerStateSubject(it, reader, this) }
64     }
65 
66     /** {@inheritDoc} */
67     override fun then(): WindowManagerTraceSubject = apply { super.then() }
68 
69     /** {@inheritDoc} */
70     override fun skipUntilFirstAssertion(): WindowManagerTraceSubject = apply {
71         super.skipUntilFirstAssertion()
72     }
73 
74     /** {@inheritDoc} */
75     override fun isEmpty(): WindowManagerTraceSubject = apply {
76         check { "Trace is empty" }.that(trace.entries.isEmpty()).isEqual(true)
77     }
78 
79     /** {@inheritDoc} */
80     override fun isNotEmpty(): WindowManagerTraceSubject = apply {
81         check { "Trace is not empty" }.that(trace.entries.isEmpty()).isEqual(false)
82     }
83 
84     /**
85      * @return List of [WindowStateSubject]s matching [componentMatcher] in the order they
86      *
87      * ```
88      *      appear on the trace
89      *
90      * @param componentMatcher
91      * ```
92      *
93      * Components to search
94      */
95     fun windowStates(componentMatcher: IComponentMatcher): List<WindowStateSubject> = windowStates {
96         componentMatcher.windowMatchesAnyOf(it)
97     }
98 
99     /**
100      * @return List of [WindowStateSubject]s matching [predicate] in the order they
101      *
102      * ```
103      *      appear on the trace
104      *
105      * @param predicate
106      * ```
107      *
108      * To search
109      */
110     fun windowStates(predicate: Predicate<WindowState>): List<WindowStateSubject> {
111         return subjects.mapNotNull { it.windowState { window -> predicate.test(window) } }
112     }
113 
114     /** {@inheritDoc} */
115     override fun notContains(componentMatcher: IComponentMatcher): WindowManagerTraceSubject =
116         notContains(componentMatcher, isOptional = false)
117 
118     /** See [notContains] */
119     fun notContains(
120         componentMatcher: IComponentMatcher,
121         isOptional: Boolean,
122     ): WindowManagerTraceSubject = apply {
123         addAssertion("notContains(${componentMatcher.toWindowIdentifier()})", isOptional) {
124             it.notContains(componentMatcher)
125         }
126     }
127 
128     /** {@inheritDoc} */
129     override fun isAboveAppWindowVisible(
130         componentMatcher: IComponentMatcher
131     ): WindowManagerTraceSubject = isAboveAppWindowVisible(componentMatcher, isOptional = false)
132 
133     /** See [isAboveAppWindowVisible] */
134     fun isAboveAppWindowVisible(
135         componentMatcher: IComponentMatcher,
136         isOptional: Boolean,
137     ): WindowManagerTraceSubject = apply {
138         addAssertion(
139             "isAboveAppWindowVisible(${componentMatcher.toWindowIdentifier()})",
140             isOptional,
141         ) {
142             it.isAboveAppWindowVisible(componentMatcher)
143         }
144     }
145 
146     /** {@inheritDoc} */
147     override fun isAboveAppWindowInvisible(
148         componentMatcher: IComponentMatcher
149     ): WindowManagerTraceSubject = isAboveAppWindowInvisible(componentMatcher, isOptional = false)
150 
151     /** See [isAboveAppWindowInvisible] */
152     fun isAboveAppWindowInvisible(
153         componentMatcher: IComponentMatcher,
154         isOptional: Boolean,
155     ): WindowManagerTraceSubject = apply {
156         addAssertion(
157             "isAboveAppWindowInvisible(${componentMatcher.toWindowIdentifier()})",
158             isOptional,
159         ) {
160             it.isAboveAppWindowInvisible(componentMatcher)
161         }
162     }
163 
164     /** {@inheritDoc} */
165     override fun isBelowAppWindowVisible(
166         componentMatcher: IComponentMatcher
167     ): WindowManagerTraceSubject = isBelowAppWindowVisible(componentMatcher, isOptional = false)
168 
169     /** See [isBelowAppWindowVisible] */
170     fun isBelowAppWindowVisible(
171         componentMatcher: IComponentMatcher,
172         isOptional: Boolean,
173     ): WindowManagerTraceSubject = apply {
174         addAssertion(
175             "isBelowAppWindowVisible(${componentMatcher.toWindowIdentifier()})",
176             isOptional,
177         ) {
178             it.isBelowAppWindowVisible(componentMatcher)
179         }
180     }
181 
182     /** {@inheritDoc} */
183     override fun isBelowAppWindowInvisible(
184         componentMatcher: IComponentMatcher
185     ): WindowManagerTraceSubject = isBelowAppWindowInvisible(componentMatcher, isOptional = false)
186 
187     /** See [isBelowAppWindowInvisible] */
188     fun isBelowAppWindowInvisible(
189         componentMatcher: IComponentMatcher,
190         isOptional: Boolean,
191     ): WindowManagerTraceSubject = apply {
192         addAssertion(
193             "isBelowAppWindowInvisible(${componentMatcher.toWindowIdentifier()})",
194             isOptional,
195         ) {
196             it.isBelowAppWindowInvisible(componentMatcher)
197         }
198     }
199 
200     /** {@inheritDoc} */
201     override fun isNonAppWindowVisible(
202         componentMatcher: IComponentMatcher
203     ): WindowManagerTraceSubject = isNonAppWindowVisible(componentMatcher, isOptional = false)
204 
205     /** See [isNonAppWindowVisible] */
206     fun isNonAppWindowVisible(
207         componentMatcher: IComponentMatcher,
208         isOptional: Boolean,
209     ): WindowManagerTraceSubject = apply {
210         addAssertion(
211             "isNonAppWindowVisible(${componentMatcher.toWindowIdentifier()})",
212             isOptional,
213         ) {
214             it.isNonAppWindowVisible(componentMatcher)
215         }
216     }
217 
218     /** {@inheritDoc} */
219     override fun isNonAppWindowInvisible(
220         componentMatcher: IComponentMatcher,
221         mustExist: Boolean,
222     ): WindowManagerTraceSubject =
223         isNonAppWindowInvisible(componentMatcher, isOptional = false, mustExist)
224 
225     /** See [isNonAppWindowInvisible] */
226     fun isNonAppWindowInvisible(
227         componentMatcher: IComponentMatcher,
228         isOptional: Boolean,
229         mustExist: Boolean = false,
230     ): WindowManagerTraceSubject = apply {
231         addAssertion(
232             "isNonAppWindowInvisible(${componentMatcher.toWindowIdentifier()})",
233             isOptional,
234         ) {
235             it.isNonAppWindowInvisible(componentMatcher, mustExist)
236         }
237     }
238 
239     /** {@inheritDoc} */
240     override fun isAppWindowOnTop(componentMatcher: IComponentMatcher): WindowManagerTraceSubject =
241         isAppWindowOnTop(componentMatcher, isOptional = false)
242 
243     /** See [isAppWindowOnTop] */
244     fun isAppWindowOnTop(
245         componentMatcher: IComponentMatcher,
246         isOptional: Boolean,
247     ): WindowManagerTraceSubject = apply {
248         addAssertion("isAppWindowOnTop(${componentMatcher.toWindowIdentifier()})", isOptional) {
249             it.isAppWindowOnTop(componentMatcher)
250         }
251     }
252 
253     /** {@inheritDoc} */
254     override fun isAppWindowNotOnTop(
255         componentMatcher: IComponentMatcher
256     ): WindowManagerTraceSubject = isAppWindowNotOnTop(componentMatcher, isOptional = false)
257 
258     /** See [isAppWindowNotOnTop] */
259     fun isAppWindowNotOnTop(
260         componentMatcher: IComponentMatcher,
261         isOptional: Boolean,
262     ): WindowManagerTraceSubject = apply {
263         addAssertion("appWindowNotOnTop(${componentMatcher.toWindowIdentifier()})", isOptional) {
264             it.isAppWindowNotOnTop(componentMatcher)
265         }
266     }
267 
268     /** {@inheritDoc} */
269     override fun isAppWindowVisible(
270         componentMatcher: IComponentMatcher
271     ): WindowManagerTraceSubject = isAppWindowVisible(componentMatcher, isOptional = false)
272 
273     /** See [isAppWindowVisible] */
274     fun isAppWindowVisible(
275         componentMatcher: IComponentMatcher,
276         isOptional: Boolean,
277     ): WindowManagerTraceSubject = apply {
278         addAssertion("isAppWindowVisible(${componentMatcher.toWindowIdentifier()})", isOptional) {
279             it.isAppWindowVisible(componentMatcher)
280         }
281     }
282 
283     /** {@inheritDoc} */
284     override fun hasNoVisibleAppWindow(): WindowManagerTraceSubject =
285         hasNoVisibleAppWindow(isOptional = false)
286 
287     /** See [hasNoVisibleAppWindow] */
288     fun hasNoVisibleAppWindow(isOptional: Boolean): WindowManagerTraceSubject = apply {
289         addAssertion("hasNoVisibleAppWindow()", isOptional) { it.hasNoVisibleAppWindow() }
290     }
291 
292     /** {@inheritDoc} */
293     override fun isKeyguardShowing(): WindowManagerTraceSubject =
294         isKeyguardShowing(isOptional = false)
295 
296     /** See [isKeyguardShowing] */
297     fun isKeyguardShowing(isOptional: Boolean): WindowManagerTraceSubject = apply {
298         addAssertion("isKeyguardShowing()", isOptional) { it.isKeyguardShowing() }
299     }
300 
301     /** {@inheritDoc} */
302     override fun isAppSnapshotStartingWindowVisibleFor(
303         componentMatcher: IComponentMatcher
304     ): WindowManagerTraceSubject =
305         isAppSnapshotStartingWindowVisibleFor(componentMatcher, isOptional = false)
306 
307     /** See [isAppSnapshotStartingWindowVisibleFor] */
308     fun isAppSnapshotStartingWindowVisibleFor(
309         componentMatcher: IComponentMatcher,
310         isOptional: Boolean,
311     ): WindowManagerTraceSubject = apply {
312         addAssertion(
313             "isAppSnapshotStartingWindowVisibleFor(${componentMatcher.toWindowIdentifier()})",
314             isOptional,
315         ) {
316             it.isAppSnapshotStartingWindowVisibleFor(componentMatcher)
317         }
318     }
319 
320     /** {@inheritDoc} */
321     override fun isAppWindowInvisible(
322         componentMatcher: IComponentMatcher,
323         mustExist: Boolean,
324     ): WindowManagerTraceSubject =
325         isAppWindowInvisible(componentMatcher, isOptional = false, mustExist)
326 
327     /** See [isAppWindowInvisible] */
328     fun isAppWindowInvisible(
329         componentMatcher: IComponentMatcher,
330         isOptional: Boolean,
331         mustExist: Boolean = false,
332     ): WindowManagerTraceSubject = apply {
333         addAssertion("isAppWindowInvisible(${componentMatcher.toWindowIdentifier()})", isOptional) {
334             it.isAppWindowInvisible(componentMatcher, mustExist)
335         }
336     }
337 
338     /** {@inheritDoc} */
339     override fun doNotOverlap(
340         vararg componentMatcher: IComponentMatcher
341     ): WindowManagerTraceSubject = apply {
342         val repr = componentMatcher.joinToString(", ") { it.toWindowIdentifier() }
343         addAssertion("noWindowsOverlap($repr)") { it.doNotOverlap(*componentMatcher) }
344     }
345 
346     /** {@inheritDoc} */
347     override fun isAboveWindow(
348         aboveWindowComponentMatcher: IComponentMatcher,
349         belowWindowComponentMatcher: IComponentMatcher,
350     ): WindowManagerTraceSubject = apply {
351         val aboveWindowTitle = aboveWindowComponentMatcher.toWindowIdentifier()
352         val belowWindowTitle = belowWindowComponentMatcher.toWindowIdentifier()
353         addAssertion("$aboveWindowTitle is above $belowWindowTitle") {
354             it.isAboveWindow(aboveWindowComponentMatcher, belowWindowComponentMatcher)
355         }
356     }
357 
358     /** See [isAppWindowInvisible] */
359     override fun visibleRegion(componentMatcher: IComponentMatcher?): RegionTraceSubject {
360         val regionTrace =
361             RegionTrace(
362                 componentMatcher,
363                 subjects.map { it.visibleRegion(componentMatcher).regionEntry },
364             )
365 
366         return RegionTraceSubject(regionTrace, reader)
367     }
368 
369     /** {@inheritDoc} */
370     override fun contains(componentMatcher: IComponentMatcher): WindowManagerTraceSubject =
371         contains(componentMatcher, isOptional = false)
372 
373     /** See [contains] */
374     fun contains(
375         componentMatcher: IComponentMatcher,
376         isOptional: Boolean,
377     ): WindowManagerTraceSubject = apply {
378         addAssertion("contains(${componentMatcher.toWindowIdentifier()})", isOptional) {
379             it.contains(componentMatcher)
380         }
381     }
382 
383     /** {@inheritDoc} */
384     override fun containsAboveAppWindow(
385         componentMatcher: IComponentMatcher
386     ): WindowManagerTraceSubject = containsAboveAppWindow(componentMatcher, isOptional = false)
387 
388     /** See [containsAboveAppWindow] */
389     fun containsAboveAppWindow(
390         componentMatcher: IComponentMatcher,
391         isOptional: Boolean,
392     ): WindowManagerTraceSubject = apply {
393         addAssertion(
394             "containsAboveAppWindow(${componentMatcher.toWindowIdentifier()})",
395             isOptional,
396         ) {
397             it.containsAboveAppWindow(componentMatcher)
398         }
399     }
400 
401     /** {@inheritDoc} */
402     override fun containsAppWindow(componentMatcher: IComponentMatcher): WindowManagerTraceSubject =
403         containsAppWindow(componentMatcher, isOptional = false)
404 
405     /** See [containsAppWindow] */
406     fun containsAppWindow(
407         componentMatcher: IComponentMatcher,
408         isOptional: Boolean,
409     ): WindowManagerTraceSubject = apply {
410         addAssertion("containsAppWindow(${componentMatcher.toWindowIdentifier()})", isOptional) {
411             it.containsAboveAppWindow(componentMatcher)
412         }
413     }
414 
415     /** {@inheritDoc} */
416     override fun containsBelowAppWindow(
417         componentMatcher: IComponentMatcher
418     ): WindowManagerTraceSubject = containsBelowAppWindow(componentMatcher, isOptional = false)
419 
420     /** See [containsBelowAppWindow] */
421     fun containsBelowAppWindow(
422         componentMatcher: IComponentMatcher,
423         isOptional: Boolean,
424     ): WindowManagerTraceSubject = apply {
425         addAssertion(
426             "containsBelowAppWindows(${componentMatcher.toWindowIdentifier()})",
427             isOptional,
428         ) {
429             it.containsBelowAppWindow(componentMatcher)
430         }
431     }
432 
433     /** {@inheritDoc} */
434     override fun containsNonAppWindow(
435         componentMatcher: IComponentMatcher
436     ): WindowManagerTraceSubject = containsNonAppWindow(componentMatcher, isOptional = false)
437 
438     /** See [containsNonAppWindow] */
439     fun containsNonAppWindow(
440         componentMatcher: IComponentMatcher,
441         isOptional: Boolean,
442     ): WindowManagerTraceSubject = apply {
443         addAssertion("containsNonAppWindow(${componentMatcher.toWindowIdentifier()})", isOptional) {
444             it.containsNonAppWindow(componentMatcher)
445         }
446     }
447 
448     /** {@inheritDoc} */
449     override fun isHomeActivityInvisible(): WindowManagerTraceSubject =
450         isHomeActivityInvisible(isOptional = false)
451 
452     /** See [isHomeActivityInvisible] */
453     fun isHomeActivityInvisible(isOptional: Boolean): WindowManagerTraceSubject = apply {
454         addAssertion("isHomeActivityInvisible", isOptional) { it.isHomeActivityInvisible() }
455     }
456 
457     /** {@inheritDoc} */
458     override fun isHomeActivityVisible(): WindowManagerTraceSubject =
459         isHomeActivityVisible(isOptional = false)
460 
461     /** See [isHomeActivityVisible] */
462     fun isHomeActivityVisible(isOptional: Boolean): WindowManagerTraceSubject = apply {
463         addAssertion("isHomeActivityVisible", isOptional) { it.isHomeActivityVisible() }
464     }
465 
466     /** {@inheritDoc} */
467     override fun hasRotation(rotation: Rotation, displayId: Int): WindowManagerTraceSubject =
468         hasRotation(rotation, displayId, isOptional = false)
469 
470     /** See [hasRotation] */
471     fun hasRotation(
472         rotation: Rotation,
473         displayId: Int,
474         isOptional: Boolean,
475     ): WindowManagerTraceSubject = apply {
476         addAssertion("hasRotation($rotation, display=$displayId)", isOptional) {
477             it.hasRotation(rotation, displayId)
478         }
479     }
480 
481     /** {@inheritDoc} */
482     override fun isNotPinned(componentMatcher: IComponentMatcher): WindowManagerTraceSubject =
483         isNotPinned(componentMatcher, isOptional = false)
484 
485     /** See [isNotPinned] */
486     fun isNotPinned(
487         componentMatcher: IComponentMatcher,
488         isOptional: Boolean,
489     ): WindowManagerTraceSubject = apply {
490         addAssertion("isNotPinned(${componentMatcher.toWindowIdentifier()})", isOptional) {
491             it.isNotPinned(componentMatcher)
492         }
493     }
494 
495     /** {@inheritDoc} */
496     override fun isFocusedApp(app: String): WindowManagerTraceSubject =
497         isFocusedApp(app, isOptional = false)
498 
499     /** See [isFocusedApp] */
500     fun isFocusedApp(app: String, isOptional: Boolean): WindowManagerTraceSubject = apply {
501         addAssertion("isFocusedApp($app)", isOptional) { it.isFocusedApp(app) }
502     }
503 
504     /** {@inheritDoc} */
505     override fun isNotFocusedApp(app: String): WindowManagerTraceSubject =
506         isNotFocusedApp(app, isOptional = false)
507 
508     /** See [isNotFocusedApp] */
509     fun isNotFocusedApp(app: String, isOptional: Boolean): WindowManagerTraceSubject = apply {
510         addAssertion("isNotFocusedApp($app)", isOptional) { it.isNotFocusedApp(app) }
511     }
512 
513     /** {@inheritDoc} */
514     override fun isPinned(componentMatcher: IComponentMatcher): WindowManagerTraceSubject =
515         isPinned(componentMatcher, isOptional = false)
516 
517     /** See [isPinned] */
518     fun isPinned(
519         componentMatcher: IComponentMatcher,
520         isOptional: Boolean,
521     ): WindowManagerTraceSubject = apply {
522         addAssertion("isPinned(${componentMatcher.toWindowIdentifier()})", isOptional) {
523             it.isPinned(componentMatcher)
524         }
525     }
526 
527     /** {@inheritDoc} */
528     override fun isRecentsActivityInvisible(): WindowManagerTraceSubject =
529         isRecentsActivityInvisible(isOptional = false)
530 
531     /** See [isRecentsActivityInvisible] */
532     fun isRecentsActivityInvisible(isOptional: Boolean): WindowManagerTraceSubject = apply {
533         addAssertion("isRecentsActivityInvisible", isOptional) { it.isRecentsActivityInvisible() }
534     }
535 
536     /** {@inheritDoc} */
537     override fun isRecentsActivityVisible(): WindowManagerTraceSubject =
538         isRecentsActivityVisible(isOptional = false)
539 
540     /** See [isRecentsActivityVisible] */
541     fun isRecentsActivityVisible(isOptional: Boolean): WindowManagerTraceSubject = apply {
542         addAssertion("isRecentsActivityVisible", isOptional) { it.isRecentsActivityVisible() }
543     }
544 
545     override fun isValid(): WindowManagerTraceSubject = apply {
546         addAssertion("isValid") { it.isValid() }
547     }
548 
549     /** {@inheritDoc} */
550     override fun notContainsAppWindow(
551         componentMatcher: IComponentMatcher
552     ): WindowManagerTraceSubject = notContainsAppWindow(componentMatcher, isOptional = false)
553 
554     /** See [notContainsAppWindow] */
555     fun notContainsAppWindow(
556         componentMatcher: IComponentMatcher,
557         isOptional: Boolean,
558     ): WindowManagerTraceSubject = apply {
559         addAssertion("notContainsAppWindow(${componentMatcher.toWindowIdentifier()})", isOptional) {
560             it.notContainsAppWindow(componentMatcher)
561         }
562     }
563 
564     /** {@inheritDoc} */
565     override fun containsAtLeastOneDisplay(): WindowManagerTraceSubject = apply {
566         addAssertion("containAtLeastOneDisplay", isOptional = false) {
567             it.containsAtLeastOneDisplay()
568         }
569     }
570 
571     /** Checks that all visible layers are shown for more than one consecutive entry */
572     fun visibleWindowsShownMoreThanOneConsecutiveEntry(
573         ignoreWindows: List<ComponentNameMatcher> =
574             listOf(
575                 ComponentNameMatcher.SPLASH_SCREEN,
576                 ComponentNameMatcher.SNAPSHOT,
577                 ComponentNameMatcher.SECONDARY_HOME_HANDLE,
578                 ComponentNameMatcher.EDGE_BACK_GESTURE_HANDLER,
579             )
580     ): WindowManagerTraceSubject = apply {
581         visibleEntriesShownMoreThanOneConsecutiveTime { subject ->
582             subject.wmState.windowStates
583                 .filter { it.isVisible }
584                 .filter { window ->
585                     ignoreWindows.none { matcher -> matcher.windowMatchesAnyOf(window) }
586                 }
587                 .map { it.name }
588                 .toSet()
589         }
590     }
591 
592     /** Executes a custom [assertion] on the current subject */
593     operator fun invoke(
594         name: String,
595         isOptional: Boolean = false,
596         assertion: AssertionPredicate<WindowManagerStateSubject>,
597     ): WindowManagerTraceSubject = apply { addAssertion(name, isOptional, assertion) }
598 
599     /** Run the assertions for all trace entries within the specified time range */
600     fun forElapsedTimeRange(startTime: Long, endTime: Long) {
601         val subjectsInRange =
602             subjects.filter { it.wmState.timestamp.elapsedNanos in startTime..endTime }
603         assertionsChecker.test(subjectsInRange)
604     }
605 
606     /**
607      * User-defined entry point for the trace entry with [timestamp]
608      *
609      * @param timestamp of the entry
610      */
611     fun getEntryByElapsedTimestamp(timestamp: Long): WindowManagerStateSubject =
612         subjects.first { it.wmState.timestamp.elapsedNanos == timestamp }
613 }
614