1 /*
2 * Copyright (C) 2024 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.testsuite
18
19 import com.android.tools.lint.checks.infrastructure.TestFile
20 import com.android.tools.metalava.model.AnnotationItem
21 import com.android.tools.metalava.model.Codebase
22 import com.android.tools.metalava.model.TypeItem
23 import com.android.tools.metalava.model.TypeNullability
24 import com.android.tools.metalava.testing.KnownSourceFiles
25 import com.google.common.truth.Truth.*
26
27 class NullabilityCodebaseContext(
28 codebaseContext: BaseModelTest.CodebaseContext<Codebase>,
29 /**
30 * True if nullness information came from annotations, false if it came from kotlin null
31 * suffixes.
32 */
33 val nullabilityFromAnnotations: Boolean,
34 ) : BaseModelTest.CodebaseContext<Codebase> by codebaseContext
35
36 /**
37 * Runs a test where it matters whether nullability is provided by annotations (which it is in
38 * [javaSource] and [annotatedSignature]) or kotlin null suffixes (which it is in [kotlinSource] and
39 * [kotlinNullsSignature]).
40 *
41 * Runs [test] for the nullability-through-annotations inputs with `true` as the boolean parameter,
42 * and runs [test] for the nullability-through-suffixes inputs with `false` as the boolean
43 * parameter.
44 */
runNullabilityTestnull45 internal fun BaseModelTest.runNullabilityTest(
46 javaSource: TestFile,
47 annotatedSignature: TestFile,
48 kotlinSource: TestFile,
49 kotlinNullsSignature: TestFile,
50 test: NullabilityCodebaseContext.() -> Unit
51 ) {
52 runCodebaseTest(
53 inputSet(
54 javaSource,
55 // Access nullability annotations which are not type use.
56 KnownSourceFiles.notTypeUseNullableSource,
57 KnownSourceFiles.notTypeUseNonNullSource,
58 // Libcore nullability are type use.
59 KnownSourceFiles.libcoreNullableSource,
60 KnownSourceFiles.libcoreNonNullSource,
61 ),
62 inputSet(annotatedSignature)
63 ) {
64 val context = NullabilityCodebaseContext(this, true)
65 context.test()
66 }
67
68 runCodebaseTest(kotlinSource, kotlinNullsSignature) {
69 val context = NullabilityCodebaseContext(this, false)
70 context.test()
71 }
72 }
73
74 /**
75 * Make sure that this [TypeItem] has [TypeNullability.NONNULL] and check to make sure that it has
76 * (or does not have depending on [expectAnnotation]) an [AnnotationItem.isNonNull] annotation.
77 *
78 * @param expectAnnotation `true` if an appropriate annotation is expected, `false` if it is not,
79 * `null` disables the annotation check.
80 */
assertHasNonNullNullabilitynull81 internal fun TypeItem.assertHasNonNullNullability(
82 expectAnnotation: Boolean? = null,
83 message: String? = null,
84 ) {
85 assertWithMessage(message ?: "")
86 .that(modifiers.nullability())
87 .isEqualTo(TypeNullability.NONNULL)
88 val nullabilityAnnotations = modifiers.annotations().filter { it.isNullnessAnnotation() }
89 when (expectAnnotation) {
90 true -> assertThat(nullabilityAnnotations.single().isNonNull()).isTrue()
91 false -> assertThat(nullabilityAnnotations).isEmpty()
92 else -> {}
93 }
94 }
95
96 /**
97 * Make sure that this [TypeItem] has [TypeNullability.NULLABLE] and check to make sure that it has
98 * (or does not have depending on [expectAnnotation]) an [AnnotationItem.isNullable] annotation.
99 *
100 * @param expectAnnotation `true` if an appropriate annotation is expected, `false` if it is not,
101 * * `null` disables the annotation check.
102 */
assertHasNullableNullabilitynull103 internal fun TypeItem.assertHasNullableNullability(expectAnnotation: Boolean? = null) {
104 assertThat(modifiers.nullability()).isEqualTo(TypeNullability.NULLABLE)
105 val nullabilityAnnotations = modifiers.annotations().filter { it.isNullnessAnnotation() }
106 when (expectAnnotation) {
107 true -> assertThat(nullabilityAnnotations.single().isNullable()).isTrue()
108 false -> assertThat(nullabilityAnnotations).isEmpty()
109 else -> {}
110 }
111 }
112
113 /** Make sure that this [TypeItem] has [TypeNullability.PLATFORM]. */
assertHasPlatformNullabilitynull114 internal fun TypeItem.assertHasPlatformNullability() {
115 assertThat(modifiers.nullability()).isEqualTo(TypeNullability.PLATFORM)
116 }
117
118 /** Make sure that this [TypeItem] has [TypeNullability.UNDEFINED]. */
assertHasUndefinedNullabilitynull119 internal fun TypeItem.assertHasUndefinedNullability() {
120 assertThat(modifiers.nullability()).isEqualTo(TypeNullability.UNDEFINED)
121 }
122