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