1 /* 2 * Copyright (C) 2018 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; 18 19 import static com.google.common.truth.Truth.assertAbout; 20 import static com.google.common.truth.Truth.assertWithMessage; 21 22 import android.graphics.Rect; 23 import android.util.Log; 24 25 import androidx.annotation.Nullable; 26 27 import com.android.server.wm.flicker.Assertions.Result; 28 import com.android.server.wm.flicker.LayersTrace.Entry; 29 import com.android.server.wm.flicker.TransitionRunner.TransitionResult; 30 31 import com.google.common.truth.FailureMetadata; 32 import com.google.common.truth.Subject; 33 34 import java.util.List; 35 import java.util.Locale; 36 import java.util.function.Consumer; 37 import java.util.stream.Collectors; 38 39 /** Truth subject for {@link LayersTrace} objects. */ 40 public class LayersTraceSubject extends Subject<LayersTraceSubject, LayersTrace> { 41 public static final String TAG = "FLICKER"; 42 43 // Boiler-plate Subject.Factory for LayersTraceSubject 44 private static final Subject.Factory<LayersTraceSubject, LayersTrace> FACTORY = 45 LayersTraceSubject::new; 46 47 private AssertionsChecker<Entry> mChecker = new AssertionsChecker<>(); 48 private boolean mNewAssertion = true; 49 addAssertion(Assertions.TraceAssertion<Entry> assertion, String name)50 private void addAssertion(Assertions.TraceAssertion<Entry> assertion, String name) { 51 if (mNewAssertion) { 52 mChecker.add(assertion, name); 53 } else { 54 mChecker.append(assertion, name); 55 } 56 } 57 LayersTraceSubject(FailureMetadata fm, @Nullable LayersTrace subject)58 private LayersTraceSubject(FailureMetadata fm, @Nullable LayersTrace subject) { 59 super(fm, subject); 60 } 61 62 // User-defined entry point assertThat(@ullable LayersTrace entry)63 public static LayersTraceSubject assertThat(@Nullable LayersTrace entry) { 64 return assertAbout(FACTORY).that(entry); 65 } 66 67 // User-defined entry point. Ignores orphaned layers because of b/141326137 assertThat(@ullable TransitionResult result)68 public static LayersTraceSubject assertThat(@Nullable TransitionResult result) { 69 Consumer<LayersTrace.Layer> orphanLayerCallback = 70 layer -> 71 Log.w( 72 TAG, 73 String.format( 74 Locale.getDefault(), "Ignoring orphaned layer %s", layer)); 75 76 return assertThat(result, orphanLayerCallback); 77 } 78 79 // User-defined entry point assertThat(@ullable TransitionResult result, Consumer<LayersTrace.Layer> orphanLayerCallback)80 public static LayersTraceSubject assertThat(@Nullable TransitionResult result, 81 Consumer<LayersTrace.Layer> orphanLayerCallback) { 82 LayersTrace entries = 83 LayersTrace.parseFrom( 84 result.getLayersTrace(), 85 result.getLayersTracePath(), 86 result.getLayersTraceChecksum(), 87 orphanLayerCallback); 88 return assertWithMessage(result.toString()).about(FACTORY).that(entries); 89 } 90 91 // Static method for getting the subject factory (for use with assertAbout()) entries()92 public static Subject.Factory<LayersTraceSubject, LayersTrace> entries() { 93 return FACTORY; 94 } 95 forAllEntries()96 public void forAllEntries() { 97 test(); 98 } 99 forRange(long startTime, long endTime)100 public void forRange(long startTime, long endTime) { 101 mChecker.filterByRange(startTime, endTime); 102 test(); 103 } 104 then()105 public LayersTraceSubject then() { 106 mNewAssertion = true; 107 mChecker.checkChangingAssertions(); 108 return this; 109 } 110 and()111 public LayersTraceSubject and() { 112 mNewAssertion = false; 113 mChecker.checkChangingAssertions(); 114 return this; 115 } 116 117 /** 118 * Ignores the first entries in the trace, until the first assertion passes. If it reaches the 119 * end of the trace without passing any assertion, return a failure with the name/reason from 120 * the first assertion 121 * 122 * @return 123 */ skipUntilFirstAssertion()124 public LayersTraceSubject skipUntilFirstAssertion() { 125 mChecker.skipUntilFirstAssertion(); 126 return this; 127 } 128 inTheBeginning()129 public void inTheBeginning() { 130 if (actual().getEntries().isEmpty()) { 131 fail("No entries found."); 132 } 133 mChecker.checkFirstEntry(); 134 test(); 135 } 136 atTheEnd()137 public void atTheEnd() { 138 if (actual().getEntries().isEmpty()) { 139 fail("No entries found."); 140 } 141 mChecker.checkLastEntry(); 142 test(); 143 } 144 test()145 private void test() { 146 List<Result> failures = mChecker.test(actual().getEntries()); 147 if (!failures.isEmpty()) { 148 String failureLogs = 149 failures.stream().map(Result::toString).collect(Collectors.joining("\n")); 150 String tracePath = ""; 151 if (actual().getSource().isPresent()) { 152 tracePath = 153 "\nLayers Trace can be found in: " 154 + actual().getSource().get().toAbsolutePath() 155 + "\nChecksum: " 156 + actual().getSourceChecksum() 157 + "\n"; 158 } 159 fail(tracePath + failureLogs); 160 } 161 } 162 coversRegion(Rect rect)163 public LayersTraceSubject coversRegion(Rect rect) { 164 addAssertion(entry -> entry.coversRegion(rect), "coversRegion(" + rect + ")"); 165 return this; 166 } 167 hasVisibleRegion(String layerName, Rect size)168 public LayersTraceSubject hasVisibleRegion(String layerName, Rect size) { 169 addAssertion( 170 entry -> entry.hasVisibleRegion(layerName, size), 171 "hasVisibleRegion(" + layerName + size + ")"); 172 return this; 173 } 174 hasNotLayer(String layerName)175 public LayersTraceSubject hasNotLayer(String layerName) { 176 addAssertion(entry -> entry.exists(layerName).negate(), "hasNotLayer(" + layerName + ")"); 177 return this; 178 } 179 hasLayer(String layerName)180 public LayersTraceSubject hasLayer(String layerName) { 181 addAssertion(entry -> entry.exists(layerName), "hasLayer(" + layerName + ")"); 182 return this; 183 } 184 showsLayer(String layerName)185 public LayersTraceSubject showsLayer(String layerName) { 186 addAssertion(entry -> entry.isVisible(layerName), "showsLayer(" + layerName + ")"); 187 return this; 188 } 189 replaceVisibleLayer( String previousLayerName, String currentLayerName)190 public LayersTraceSubject replaceVisibleLayer( 191 String previousLayerName, String currentLayerName) { 192 return hidesLayer(previousLayerName).and().showsLayer(currentLayerName); 193 } 194 hidesLayer(String layerName)195 public LayersTraceSubject hidesLayer(String layerName) { 196 addAssertion(entry -> entry.isVisible(layerName).negate(), "hidesLayer(" + layerName + ")"); 197 return this; 198 } 199 200 } 201