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