• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * 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.assertions
18 
19 import com.google.common.truth.Fact
20 import kotlin.math.max
21 
22 /**
23  * Runs sequences of assertions on sequences of subjects.
24  *
25  * Starting at the first assertion and first trace entry, executes the assertions iteratively
26  * on the trace until all assertions and trace entries succeed.
27  *
28  * @param <T> trace entry type </T>
29  */
30 class AssertionsChecker<T : FlickerSubject> {
31     private val assertions = mutableListOf<CompoundAssertion<T>>()
32     private var skipUntilFirstAssertion = false
33 
addnull34     fun add(name: String, assertion: Assertion<T>) {
35         assertions.add(CompoundAssertion(assertion, name))
36     }
37 
38     /**
39      * Append [assertion] to the last existing set of assertions.
40      */
appendnull41     fun append(name: String, assertion: Assertion<T>) {
42         assertions.last().add(assertion, name)
43     }
44 
45     /**
46      * Filters trace entries then runs assertions returning a list of failures.
47      *
48      * @param entries list of entries to perform assertions on
49      * @return list of failed assertion results
50      */
testnull51     fun test(entries: List<T>): Unit = assertChanges(entries)
52 
53     /**
54      * Steps through each trace entry checking if provided assertions are true in the order they are
55      * added. Each assertion must be true for at least a single trace entry.
56      *
57      *
58      * This can be used to check for asserting a change in property over a trace. Such as
59      * visibility for a window changes from true to false or top-most window changes from A to B and
60      * back to A again.
61      *
62      *
63      * It is also possible to ignore failures on initial elements, until the first assertion
64      * passes, this allows the trace to be recorded for longer periods, and the checks to happen
65      * only after some time.
66      */
67     private fun assertChanges(entries: List<T>) {
68         if (assertions.isEmpty() || entries.isEmpty()) {
69             return
70         }
71 
72         val failures = mutableListOf<Throwable>()
73         var entryIndex = 0
74         var assertionIndex = 0
75         var lastPassedAssertionIndex = -1
76         while (assertionIndex < assertions.size && entryIndex < entries.size) {
77             val currentAssertion = assertions[assertionIndex]
78             val currEntry = entries[entryIndex]
79             try {
80                 currentAssertion.invoke(currEntry)
81                 lastPassedAssertionIndex = assertionIndex
82                 entryIndex++
83             } catch (e: Throwable) {
84                 val ignoreFailure = skipUntilFirstAssertion && lastPassedAssertionIndex == -1
85                 if (ignoreFailure) {
86                     entryIndex++
87                     continue
88                 }
89                 if (lastPassedAssertionIndex != assertionIndex) {
90                     val prevEntry = entries[max(entryIndex - 1, 0)]
91                     prevEntry.fail(e)
92                 }
93                 assertionIndex++
94                 if (assertionIndex == assertions.size) {
95                     val prevEntry = entries[max(entryIndex - 1, 0)]
96                     prevEntry.fail(e)
97                 }
98             }
99         }
100         if (lastPassedAssertionIndex == -1 && assertions.isNotEmpty() && failures.isEmpty()) {
101             entries.first().fail("Assertion never passed", assertions.first())
102         }
103 
104         if (failures.isEmpty() && assertionIndex != assertions.lastIndex) {
105             val reason = listOf(
106                 Fact.fact("Assertion never became false", assertions[assertionIndex]),
107                 Fact.fact("Passed assertions", assertions.take(assertionIndex).joinToString(",")),
108                 Fact.fact("Untested assertions",
109                     assertions.drop(assertionIndex + 1).joinToString(","))
110             )
111             entries.first().fail(reason)
112         }
113     }
114 
115     /**
116      * Ignores the first entries in the trace, until the first assertion passes. If it reaches the
117      * end of the trace without passing any assertion, return a failure with the name/reason from
118      * the first assertion
119      */
skipUntilFirstAssertionnull120     fun skipUntilFirstAssertion() {
121         skipUntilFirstAssertion = true
122     }
123 }
124