xref: /aosp_15_r20/tools/metalava/metalava-model/src/testFixtures/java/com/android/tools/metalava/model/Assertions.kt (revision 115816f9299ab6ddd6b9673b81f34e707f6bacab)
1 /*
2  * 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 com.android.tools.metalava.model
18 
19 import com.android.tools.metalava.model.testing.testTypeString
20 import com.google.common.truth.Truth.assertThat
21 import kotlin.test.assertEquals
22 import kotlin.test.assertIs
23 import kotlin.test.assertNotNull
24 
25 interface Assertions {
26 
27     /**
28      * Get the class from the [Codebase], failing if it does not exist.
29      *
30      * Checks to make sure that returned [ClassItem]'s [ClassItem.emit] property matches
31      * [expectedEmit]. That defaults to `true` as this is usually used to retrieve a class that is
32      * present in the source which have `emit = true` by default.
33      */
assertClassnull34     fun Codebase.assertClass(qualifiedName: String, expectedEmit: Boolean = true): ClassItem {
35         val classItem = findClass(qualifiedName)
36         assertNotNull(classItem, message = "Expected $qualifiedName to be defined")
37         assertEquals(
38             expectedEmit,
39             classItem.emit,
40             message = "Expected $qualifiedName to have emit=$expectedEmit"
41         )
42         return classItem
43     }
44 
45     /**
46      * Resolve the class from the [Codebase], failing if it does not exist.
47      *
48      * Checks to make sure that returned [ClassItem]'s [ClassItem.emit] property matches
49      * [expectedEmit]. That defaults to `true` as this is usually used to retrieve a class that is
50      * present in the source which have `emit = true` by default.
51      */
Codebasenull52     fun Codebase.assertResolvedClass(
53         qualifiedName: String,
54         expectedEmit: Boolean = false
55     ): ClassItem {
56         // Resolve the class which should make it available to assertClass(...) if it could be
57         // found.
58         resolveClass(qualifiedName)
59         // Assert that the class exists and has correct setting of `emit`.
60         return assertClass(qualifiedName, expectedEmit)
61     }
62 
63     /** Get the package from the [Codebase], failing if it does not exist. */
assertPackagenull64     fun Codebase.assertPackage(pkgName: String): PackageItem {
65         val packageItem = findPackage(pkgName)
66         assertNotNull(packageItem, message = "Expected $pkgName to be defined")
67         return packageItem
68     }
69 
70     /**
71      * Return a dump of the state of [SelectableItem.selectedApiVariants] across this [Codebase].
72      */
<lambda>null73     private fun Codebase.dumpSelectedApiVariants() = buildString {
74         accept(
75             object :
76                 BaseItemVisitor(
77                     preserveClassNesting = true,
78                 ) {
79                 private var indent = ""
80 
81                 override fun visitSelectableItem(item: SelectableItem) {
82                     append("$indent${item.describe()} - ${item.selectedApiVariants}\n")
83                     indent += "  "
84                 }
85 
86                 override fun afterVisitSelectableItem(item: SelectableItem) {
87                     indent = indent.substring(2)
88                 }
89             }
90         )
91     }
92 
93     /** Assert that the [dumpSelectedApiVariants] matches [expected]. */
assertSelectedApiVariantsnull94     fun Codebase.assertSelectedApiVariants(expected: String, message: String? = null) {
95         val actual = dumpSelectedApiVariants()
96         assertEquals(expected.trimIndent(), actual.trimEnd(), message)
97     }
98 
99     /** Get the field from the [ClassItem], failing if it does not exist. */
ClassItemnull100     fun ClassItem.assertField(fieldName: String): FieldItem {
101         val fieldItem = findField(fieldName)
102         assertNotNull(fieldItem, message = "Expected $fieldName to be defined")
103         return fieldItem
104     }
105 
106     /** Get the method from the [ClassItem], failing if it does not exist. */
ClassItemnull107     fun ClassItem.assertMethod(methodName: String, parameters: String): MethodItem {
108         val methodItem = findMethod(methodName, parameters)
109         assertNotNull(methodItem, message = "Expected $methodName($parameters) to be defined")
110         return methodItem
111     }
112 
113     /** Get the constructor from the [ClassItem], failing if it does not exist. */
ClassItemnull114     fun ClassItem.assertConstructor(parameters: String): ConstructorItem {
115         val constructorItem = findConstructor(parameters)
116         assertNotNull(
117             constructorItem,
118             message = "Expected ${simpleName()}($parameters) to be defined"
119         )
120         return assertIs(constructorItem)
121     }
122 
123     /** Get the property from the [ClassItem], failing if it does not exist. */
ClassItemnull124     fun ClassItem.assertProperty(propertyName: String): PropertyItem {
125         val propertyItem = properties().firstOrNull { it.name() == propertyName }
126         assertNotNull(propertyItem, message = "Expected $propertyName to be defined")
127         return propertyItem
128     }
129 
130     /** Get the annotation from the [Item], failing if it does not exist. */
Itemnull131     fun Item.assertAnnotation(qualifiedName: String): AnnotationItem {
132         val annoItem = modifiers.findAnnotation(qualifiedName)
133         assertNotNull(annoItem, message = "Expected item to be annotated with ($qualifiedName)")
134         return assertIs(annoItem)
135     }
136 
137     /**
138      * Check the [Item.originallyDeprecated] and [Item.effectivelyDeprecated] are
139      * [explicitlyDeprecated] and [implicitlyDeprecated] respectively.
140      */
Itemnull141     private fun Item.assertDeprecatedStatus(
142         explicitlyDeprecated: Boolean,
143         implicitlyDeprecated: Boolean = explicitlyDeprecated,
144     ) {
145         assertEquals(
146             explicitlyDeprecated,
147             originallyDeprecated,
148             message = "$this: originallyDeprecated"
149         )
150         assertEquals(
151             implicitlyDeprecated,
152             effectivelyDeprecated,
153             message = "$this: effectivelyDeprecated"
154         )
155     }
156 
157     /** Make sure that the item is not deprecated explicitly, or implicitly. */
Itemnull158     fun Item.assertNotDeprecated() {
159         assertDeprecatedStatus(explicitlyDeprecated = false)
160     }
161 
162     /** Make sure that the item is explicitly deprecated. */
assertExplicitlyDeprecatednull163     fun Item.assertExplicitlyDeprecated() {
164         assertDeprecatedStatus(explicitlyDeprecated = true)
165     }
166 
167     /**
168      * Make sure that the item is implicitly deprecated, this will fail if the item is explicitly
169      * deprecated.
170      */
Itemnull171     fun Item.assertImplicitlyDeprecated() {
172         assertDeprecatedStatus(
173             explicitlyDeprecated = false,
174             implicitlyDeprecated = true,
175         )
176     }
177 
178     /**
179      * Create a Kotlin like method description. It uses Kotlin structure for a method and Kotlin
180      * style nulls but not Kotlin types.
181      */
<lambda>null182     fun CallableItem.kotlinLikeDescription(): String = buildString {
183         if (isConstructor()) {
184             append("constructor ")
185         } else {
186             append("fun ")
187         }
188         append(name())
189         append("(")
190         parameters().joinTo(this) {
191             "${it.name()}: ${it.type().testTypeString(kotlinStyleNulls = true)}"
192         }
193         append("): ")
194         append(returnType().testTypeString(kotlinStyleNulls = true))
195     }
196 
197     /** Get the list of fully qualified annotation names associated with the [TypeItem]. */
TypeItemnull198     fun TypeItem.annotationNames(): List<String?> {
199         return modifiers.annotations.map { it.qualifiedName }
200     }
201 
202     /** Get the list of fully qualified annotation names associated with the [Item]. */
Itemnull203     fun Item.annotationNames(): List<String?> {
204         return modifiers.annotations().map { it.qualifiedName }
205     }
206 
207     /**
208      * Check to make sure that this [TypeItem] is actually a [VariableTypeItem] whose
209      * [VariableTypeItem.asTypeParameter] references the supplied [typeParameter] and then run the
210      * optional lambda on the [VariableTypeItem].
211      */
assertReferencesTypeParameternull212     fun TypeItem.assertReferencesTypeParameter(
213         typeParameter: TypeParameterItem,
214         body: (VariableTypeItem.() -> Unit)? = null
215     ) {
216         assertVariableTypeItem {
217             assertThat(asTypeParameter).isSameInstanceAs(typeParameter)
218             if (body != null) this.body()
219         }
220     }
221 
222     /**
223      * Check to make sure that this nullable [TypeItem] is actually a [TypeItem] and then run the
224      * optional lambda on the [TypeItem].
225      */
assertNotNullTypeItemnull226     fun <T : TypeItem> T?.assertNotNullTypeItem(body: (T.() -> Unit)? = null) {
227         assertThat(this).isNotNull()
228         if (body != null) this?.body()
229     }
230 
231     /**
232      * Check to make sure that this [TypeItem] is actually a [ArrayTypeItem] and then run the
233      * optional lambda on the [ArrayTypeItem].
234      */
assertArrayTypeItemnull235     fun TypeItem?.assertArrayTypeItem(body: (ArrayTypeItem.() -> Unit)? = null) {
236         assertIsInstanceOf(body ?: {})
237     }
238 
239     /**
240      * Check to make sure that this [TypeItem] is actually a [ClassTypeItem] and then run the
241      * optional lambda on the [ClassTypeItem].
242      */
assertClassTypeItemnull243     fun TypeItem?.assertClassTypeItem(body: (ClassTypeItem.() -> Unit)? = null) {
244         assertIsInstanceOf(body ?: {})
245     }
246 
247     /**
248      * Check to make sure that this [TypeItem] is actually a [PrimitiveTypeItem] and then run the
249      * optional lambda on the [PrimitiveTypeItem].
250      */
assertPrimitiveTypeItemnull251     fun TypeItem?.assertPrimitiveTypeItem(body: (PrimitiveTypeItem.() -> Unit)? = null) {
252         assertIsInstanceOf(body ?: {})
253     }
254 
255     /**
256      * Check to make sure that this [TypeItem] is actually a [LambdaTypeItem] and then run the
257      * optional lambda on the [LambdaTypeItem].
258      */
assertLambdaTypeItemnull259     fun TypeItem?.assertLambdaTypeItem(body: (LambdaTypeItem.() -> Unit)? = null) {
260         assertIsInstanceOf(body ?: {})
261     }
262 
263     /**
264      * Check to make sure that this [TypeItem] is actually a [VariableTypeItem] and then run the
265      * optional lambda on the [VariableTypeItem].
266      */
assertVariableTypeItemnull267     fun TypeItem?.assertVariableTypeItem(body: (VariableTypeItem.() -> Unit)? = null) {
268         assertIsInstanceOf(body ?: {})
269     }
270 
271     /**
272      * Check to make sure that this [TypeItem] is actually a [WildcardTypeItem] and then run the
273      * optional lambda on the [WildcardTypeItem].
274      */
assertWildcardItemnull275     fun TypeItem?.assertWildcardItem(body: (WildcardTypeItem.() -> Unit)? = null) {
276         assertIsInstanceOf(body ?: {})
277     }
278 }
279 
assertIsInstanceOfnull280 private inline fun <reified T> Any?.assertIsInstanceOf(body: (T).() -> Unit) {
281     assertThat(this).isInstanceOf(T::class.java)
282     (this as T).body()
283 }
284