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