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.lint 18 19 import com.android.tools.metalava.DriverTest 20 import com.android.tools.metalava.androidxNonNullSource 21 import com.android.tools.metalava.androidxNullableSource 22 import com.android.tools.metalava.testing.java 23 import org.junit.Test 24 25 /** Tests for the [ApiLint.checkBuilder] method. */ 26 class CheckBuilderTest : DriverTest() { 27 28 @Test Check buildersnull29 fun `Check builders`() { 30 check( 31 apiLint = "", // enabled 32 expectedIssues = 33 """ 34 src/android/pkg/Bad.java:13: warning: Builder must be final: android.pkg.Bad.BadBuilder [StaticFinalBuilder] 35 src/android/pkg/Bad.java:13: warning: Builder must be static: android.pkg.Bad.BadBuilder [StaticFinalBuilder] 36 src/android/pkg/Bad.java:14: warning: Builder constructor arguments must be mandatory (i.e. not @Nullable): parameter badParameter in android.pkg.Bad.BadBuilder(String badParameter) [OptionalBuilderConstructorArgument] 37 src/android/pkg/Bad.java:20: warning: android.pkg.Bad does not declare a `getWithoutMatchingGetters()` method matching method android.pkg.Bad.BadBuilder.addWithoutMatchingGetter(String) [MissingGetterMatchingBuilder] 38 src/android/pkg/Bad.java:23: warning: android.pkg.Bad does not declare a `isWithoutMatchingGetter()` method matching method android.pkg.Bad.BadBuilder.setWithoutMatchingGetter(boolean) [MissingGetterMatchingBuilder] 39 src/android/pkg/Bad.java:26: warning: android.pkg.Bad does not declare a `getPluralWithoutMatchingGetters()` method matching method android.pkg.Bad.BadBuilder.addPluralWithoutMatchingGetter(java.util.Collection<java.lang.String>) [MissingGetterMatchingBuilder] 40 src/android/pkg/Bad.java:32: warning: android.pkg.Bad does not declare a getter method matching method android.pkg.Bad.BadBuilder.addPluralWithoutMatchingGetters(java.util.Collection<java.lang.String>) (expected one of: [getPluralWithoutMatchingGetters(), getPluralWithoutMatchingGetterses()]) [MissingGetterMatchingBuilder] 41 src/android/pkg/Bad.java:38: warning: Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.pkg.Bad.BadBuilder.withBadSetterStyle(boolean) [BuilderSetStyle] 42 src/android/pkg/Bad.java:41: warning: Builder setter must be @NonNull: method android.pkg.Bad.BadBuilder.setReturnsNullable(boolean) [SetterReturnsThis] 43 src/android/pkg/Bad.java:43: warning: Getter should be on the built object, not the builder: method android.pkg.Bad.BadBuilder.getOnBuilder() [GetterOnBuilder] 44 src/android/pkg/Bad.java:45: warning: android.pkg.Bad does not declare a `isNotReturningBuilder()` method matching method android.pkg.Bad.BadBuilder.setNotReturningBuilder(boolean) [MissingGetterMatchingBuilder] 45 src/android/pkg/Bad.java:45: warning: Methods must return the builder object (return type android.pkg.Bad.BadBuilder instead of void): method android.pkg.Bad.BadBuilder.setNotReturningBuilder(boolean) [SetterReturnsThis] 46 src/android/pkg/Bad.java:51: warning: android.pkg.Bad.NoBuildMethodBuilder does not declare a `build()` method, but builder classes are expected to [MissingBuildMethod] 47 src/android/pkg/Bad.java:57: warning: Methods must return the builder object (return type android.pkg.Bad.BadGenericBuilder<T> instead of T): method android.pkg.Bad.BadGenericBuilder.setBoolean(boolean) [SetterReturnsThis] 48 src/android/pkg/TopLevelBuilder.java:3: warning: android.pkg.TopLevelBuilder does not declare a `build()` method, but builder classes are expected to [MissingBuildMethod] 49 src/android/pkg/TopLevelBuilder.java:3: warning: Builder should be defined as inner class: android.pkg.TopLevelBuilder [TopLevelBuilder] 50 src/test/pkg/BadClass.java:6: warning: Builder must be final: test.pkg.BadClass.Builder [StaticFinalBuilder] 51 src/test/pkg/BadInterface.java:6: warning: Builder must be final: test.pkg.BadInterface.Builder [StaticFinalBuilder] 52 """, 53 sourceFiles = 54 arrayOf( 55 java( 56 """ 57 package android.pkg; 58 59 public final class TopLevelBuilder { 60 } 61 """ 62 ), 63 java( 64 """ 65 package android.pkg; 66 67 import androidx.annotation.NonNull; 68 import androidx.annotation.Nullable; 69 70 public class Ok { 71 72 public int getInt(); 73 @NonNull 74 public List<String> getStrings(); 75 @NonNull 76 public List<String> getProperties(); 77 @NonNull 78 public List<String> getRays(); 79 @NonNull 80 public List<String> getBuses(); 81 @NonNull 82 public List<String> getTaxes(); 83 @NonNull 84 public List<String> getMessages(); 85 public boolean isBoolean(); 86 public boolean hasBoolean2(); 87 public boolean shouldBoolean3(); 88 89 public static final class OkBuilder { 90 public OkBuilder(@NonNull String goodParameter, int goodParameter2) {} 91 92 @NonNull 93 public Ok build() { return null; } 94 95 @NonNull 96 public OkBuilder setInt(int value) { return this; } 97 98 @NonNull 99 public OkBuilder addString(@NonNull String value) { return this; } 100 101 @NonNull 102 public OkBuilder addProperty(@NonNull String value) { return this; } 103 104 @NonNull 105 public OkBuilder addRay(@NonNull String value) { return this; } 106 107 @NonNull 108 public OkBuilder addBus(@NonNull String value) { return this; } 109 110 @NonNull 111 public OkBuilder addTax(@NonNull String value) { return this; } 112 113 @NonNull 114 public OkBuilder addMessages(@NonNull Collection<String> value) { 115 return this; 116 } 117 118 @NonNull 119 public OkBuilder clearStrings() { return this; } 120 121 @NonNull 122 public OkBuilder setBoolean(boolean v) { return this; } 123 124 @NonNull 125 public OkBuilder setHasBoolean2(boolean v) { return this; } 126 127 @NonNull 128 public OkBuilder setShouldBoolean3(boolean v) { return this; } 129 130 @NonNull 131 public OkBuilder clear() { return this; } 132 133 @NonNull 134 public OkBuilder clearAll() { return this; } 135 } 136 137 public static final class GenericBuilder<B extends GenericBuilder> { 138 @NonNull 139 public B setBoolean(boolean value) { return this; } 140 141 @NonNull 142 public Ok build() { return null; } 143 } 144 } 145 """ 146 ), 147 java( 148 """ 149 package android.pkg; 150 151 import androidx.annotation.NonNull; 152 153 public class SubOk extends Ok { 154 155 public static final class Builder { 156 public Builder() {} 157 158 @NonNull 159 public SubOk build() { return null; } 160 161 @NonNull 162 public Builder setInt(int value) { return this; } 163 } 164 } 165 """ 166 ), 167 java( 168 """ 169 package android.pkg; 170 171 import androidx.annotation.NonNull; 172 import androidx.annotation.Nullable; 173 import java.util.Collection; 174 175 public class Bad { 176 177 public boolean isBoolean(); 178 public boolean getWithoutMatchingGetter(); 179 public boolean isReturnsNullable(); 180 181 public class BadBuilder { 182 public BadBuilder(@Nullable String badParameter) {} 183 184 @NonNull 185 public Bad build() { return null; } 186 187 @NonNull 188 public BadBuilder addWithoutMatchingGetter(@NonNull String value) { return this; } 189 190 @NonNull 191 public BadBuilder setWithoutMatchingGetter(boolean v) { return this; } 192 193 @NonNull 194 public BadBuilder addPluralWithoutMatchingGetter( 195 @NonNull Collection<String> value) { 196 return this; 197 } 198 199 @NonNull 200 public BadBuilder addPluralWithoutMatchingGetters( 201 @NonNull Collection<String> value) { 202 return this; 203 } 204 205 @NonNull 206 public BadBuilder withBadSetterStyle(boolean v) { return this; } 207 208 @Nullable 209 public BadBuilder setReturnsNullable(boolean v) { return this; } 210 211 public boolean getOnBuilder() { return true; } 212 213 public void setNotReturningBuilder(boolean v) { return this; } 214 215 @NonNull 216 public BadBuilder () { return this; } 217 } 218 219 public static final class NoBuildMethodBuilder { 220 public NoBuildMethodBuilder() {} 221 } 222 223 public static final class BadGenericBuilder<T extends Bad> { 224 @NonNull 225 public T setBoolean(boolean value) { return this; } 226 227 @NonNull 228 public Bad build() { return null; } 229 } 230 } 231 """ 232 ), 233 java( 234 """ 235 package test.pkg; 236 237 import androidx.annotation.NonNull; 238 239 public class GoodInterface { 240 public interface Builder extends java.lang.Runnable { 241 @NonNull 242 GoodInterface build(); 243 } 244 } 245 """ 246 .trimIndent() 247 ), 248 java( 249 """ 250 package test.pkg; 251 252 import androidx.annotation.NonNull; 253 254 public class GoodClass { 255 public static abstract class Builder extends Base { 256 @NonNull 257 public abstract GoodClass build(); 258 } 259 public class Base {} 260 } 261 """ 262 .trimIndent() 263 ), 264 java( 265 """ 266 package test.pkg; 267 268 import androidx.annotation.NonNull; 269 270 public class BadInterface { 271 public interface Builder { 272 @NonNull 273 BadInterface build(); 274 } 275 } 276 """ 277 .trimIndent() 278 ), 279 java( 280 """ 281 package test.pkg; 282 283 import androidx.annotation.NonNull; 284 285 public class BadClass { 286 public static abstract class Builder { 287 @NonNull 288 public abstract BadClass build(); 289 } 290 } 291 """ 292 .trimIndent() 293 ), 294 androidxNonNullSource, 295 androidxNullableSource 296 ) 297 ) 298 } 299 } 300