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