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.assertions 18 19 import android.tools.function.AssertionPredicate 20 21 /** Utility class to store assertions composed of multiple individual assertions */ 22 class CompoundAssertion<T>(assertion: AssertionPredicate<T>, name: String, optional: Boolean) : 23 Assertion<T> { 24 private val assertions = mutableListOf<NamedAssertion<T>>() 25 26 init { 27 add(assertion, name, optional) 28 } 29 30 override val isOptional 31 get() = assertions.all { it.isOptional } 32 33 override val name 34 get() = assertions.joinToString(" and ") { it.name } 35 36 /** 37 * Executes all [assertions] on [target] 38 * 39 * In case of failure, returns the first non-optional failure (if available) or the first failed 40 * assertion 41 */ 42 override fun invoke(target: T) { 43 val failures = 44 assertions.mapNotNull { assertion -> 45 val error = kotlin.runCatching { assertion.invoke(target) }.exceptionOrNull() 46 if (error != null) { 47 Pair(assertion, error) 48 } else { 49 null 50 } 51 } 52 val nonOptionalFailure = failures.firstOrNull { !it.first.isOptional } 53 if (nonOptionalFailure != null) { 54 throw nonOptionalFailure.second 55 } 56 val firstFailure = failures.firstOrNull() 57 // Only throw first failure if all siblings are also optional otherwise don't throw anything 58 // If the CompoundAssertion is fully optional (i.e. all assertions in the compound assertion 59 // are optional), then we want to make sure the AssertionsChecker knows about the failure to 60 // not advance to the next state. Otherwise, the AssertionChecker doesn't need to know about 61 // the failure and can just consider the assertion as passed and advance to the next state 62 // since there were non-optional assertions which passed. 63 if (firstFailure != null && isOptional) { 64 throw firstFailure.second 65 } 66 } 67 68 /** Adds a new assertion to the list */ 69 fun add(assertion: AssertionPredicate<T>, name: String, optional: Boolean) { 70 assertions.add(NamedAssertion(assertion, name, optional)) 71 } 72 73 override fun toString(): String = name 74 75 override fun equals(other: Any?): Boolean { 76 if (other !is CompoundAssertion<*>) { 77 return false 78 } 79 if (!super.equals(other)) { 80 return false 81 } 82 assertions.forEachIndexed { index, assertion -> 83 if (assertion != other.assertions[index]) { 84 return false 85 } 86 } 87 return true 88 } 89 90 override fun hashCode(): Int { 91 return assertions.hashCode() 92 } 93 } 94