xref: /aosp_15_r20/tools/metalava/metalava/src/test/java/com/android/tools/metalava/lint/ApiLintTest.kt (revision 115816f9299ab6ddd6b9673b81f34e707f6bacab)
1 /*
2  * Copyright (C) 2018 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.cli.common.ARG_ERROR
23 import com.android.tools.metalava.cli.common.ARG_HIDE
24 import com.android.tools.metalava.cli.lint.ARG_API_LINT
25 import com.android.tools.metalava.model.provider.Capability
26 import com.android.tools.metalava.model.testing.FilterAction
27 import com.android.tools.metalava.model.testing.FilterByProvider
28 import com.android.tools.metalava.model.testing.RequiresCapabilities
29 import com.android.tools.metalava.nonNullSource
30 import com.android.tools.metalava.testing.java
31 import com.android.tools.metalava.testing.kotlin
32 import org.junit.Test
33 
34 class ApiLintTest : DriverTest() {
35 
36     @RequiresCapabilities(Capability.KOTLIN)
37     @Test
Test namesnull38     fun `Test names`() {
39         // Make sure we only flag issues in new API
40         check(
41             apiLint = "", // enabled
42             extraArguments = arrayOf(ARG_HIDE, "MissingNullability"),
43             expectedIssues =
44                 """
45                 src/Dp.kt:3: warning: Acronyms should not be capitalized in method names: was `badCALL`, should this be `badCall`? [AcronymName]
46                 src/android/pkg/ALL_CAPS.java:3: warning: Acronyms should not be capitalized in class names: was `ALL_CAPS`, should this be `AllCaps`? [AcronymName]
47                 src/android/pkg/HTMLWriter.java:3: warning: Acronyms should not be capitalized in class names: was `HTMLWriter`, should this be `HtmlWriter`? [AcronymName]
48                 src/android/pkg/MyStringImpl.java:3: error: Don't expose your implementation details: `MyStringImpl` ends with `Impl` [EndsWithImpl]
49                 src/android/pkg/badlyNamedClass.java:5: error: Class must start with uppercase char: badlyNamedClass [StartWithUpper]
50                 src/android/pkg/badlyNamedClass.java:6: error: Constant field names must be named with only upper case characters: `android.pkg.badlyNamedClass#BadlyNamedField`, should be `BADLY_NAMED_FIELD`? [AllUpper]
51                 src/android/pkg/badlyNamedClass.java:7: error: Method name must start with lowercase char: BadlyNamedMethod1 [StartWithLower]
52                 src/android/pkg/badlyNamedClass.java:9: warning: Acronyms should not be capitalized in method names: was `fromHTMLToHTML`, should this be `fromHtmlToHtml`? [AcronymName]
53                 src/android/pkg/badlyNamedClass.java:10: warning: Acronyms should not be capitalized in method names: was `toXML`, should this be `toXml`? [AcronymName]
54                 src/android/pkg/badlyNamedClass.java:12: warning: Acronyms should not be capitalized in method names: was `getID`, should this be `getId`? [AcronymName]
55                 """,
56             expectedFail = DefaultLintErrorMessage,
57             sourceFiles =
58                 arrayOf(
59                     java(
60                         """
61                     package android.pkg;
62 
63                     import androidx.annotation.Nullable;
64 
65                     public class badlyNamedClass {
66                         public static final int BadlyNamedField = 1;
67                         public void BadlyNamedMethod1() { }
68 
69                         public void fromHTMLToHTML() { }
70                         public void toXML() { }
71                         @Nullable
72                         public String getID() { return null; }
73                         public void setZOrderOnTop() { } // OK
74                     }
75                     """
76                     ),
77                     java(
78                         """
79                     package android.pkg;
80 
81                     public class ALL_CAPS { // like android.os.Build.VERSION_CODES
82                     }
83                     """
84                     ),
85                     java(
86                         """
87                     package android.pkg;
88 
89                     public class HTMLWriter {
90                     }
91                     """
92                     ),
93                     java(
94                         """
95                     package android.pkg;
96 
97                     public class MyStringImpl {
98                     }
99                     """
100                     ),
101                     kotlin(
102                         """
103                     inline class Dp(val value: Float)
104                     fun greatCall(width: Dp)
105                     fun badCALL(width: Dp)
106                 """
107                     ),
108                     androidxNullableSource
109                 )
110         )
111     }
112 
113     @Test
Test names against previous APInull114     fun `Test names against previous API`() {
115         check(
116             apiLint =
117                 """
118                 package android.pkg {
119                   public class badlyNamedClass {
120                     ctor public badlyNamedClass();
121                     method public void BadlyNamedMethod1();
122                     method public void fromHTMLToHTML();
123                     method public String getID();
124                     method public void toXML();
125                     field public static final int BadlyNamedField = 1; // 0x1
126                   }
127                 }
128             """
129                     .trimIndent(),
130             expectedIssues =
131                 """
132                 src/android/pkg/badlyNamedClass.java:8: warning: Acronyms should not be capitalized in method names: was `toXML2`, should this be `toXmL2`? [AcronymName]
133                 src/android/pkg2/HTMLWriter.java:3: warning: Acronyms should not be capitalized in class names: was `HTMLWriter`, should this be `HtmlWriter`? [AcronymName]
134                 src/android/pkg2/HTMLWriter.java:4: warning: Acronyms should not be capitalized in method names: was `fromHTMLToHTML`, should this be `fromHtmlToHtml`? [AcronymName]
135                 """,
136             sourceFiles =
137                 arrayOf(
138                     java(
139                         """
140                     package android.pkg;
141 
142                     public class badlyNamedClass {
143                         public static final int BadlyNamedField = 1;
144 
145                         public void fromHTMLToHTML() { }
146                         public void toXML() { }
147                         public void toXML2() { }
148                         public String getID() { return null; }
149                     }
150                     """
151                     ),
152                     java(
153                         """
154                     package android.pkg2;
155 
156                     public class HTMLWriter {
157                         public void fromHTMLToHTML() { }
158                     }
159                     """
160                     )
161                 )
162         )
163     }
164 
165     @Test
Test constantsnull166     fun `Test constants`() {
167         check(
168             apiLint = "", // enabled
169             expectedIssues =
170                 """
171                 src/android/pkg/Constants.java:8: error: Constant field names must be named with only upper case characters: `android.pkg.Constants#strings`, should be `STRINGS`? [AllUpper]
172                 src/android/pkg/Constants.java:10: error: Constant field names must be named with only upper case characters: `android.pkg.Constants#myStrings`, should be `MY_STRINGS`? [AllUpper]
173                 src/android/pkg/Constants.java:11: warning: If min/max could change in future, make them dynamic methods: android.pkg.Constants#MIN_FOO [MinMaxConstant]
174                 src/android/pkg/Constants.java:12: warning: If min/max could change in future, make them dynamic methods: android.pkg.Constants#MAX_FOO [MinMaxConstant]
175                 src/android/pkg/Constants.java:14: error: All constants must be defined at compile time: android.pkg.Constants#FOO [CompileTimeConstant]
176                 """,
177             expectedFail = DefaultLintErrorMessage,
178             sourceFiles =
179                 arrayOf(
180                     java(
181                         """
182                     package android.pkg;
183 
184                     import androidx.annotation.NonNull;
185 
186                     public class Constants {
187                         private Constants() { }
188                         @NonNull
189                         public static final String[] strings = { "NONE", "WPA_PSK" };
190                         @NonNull
191                         public static final String[] myStrings = { "NONE", "WPA_PSK" };
192                         public static final int MIN_FOO = 1;
193                         public static final int MAX_FOO = 10;
194                         @NonNull
195                         public static final String FOO = System.getProperty("foo");
196                     }
197                     """
198                     ),
199                     androidxNonNullSource,
200                     androidxNullableSource
201                 )
202         )
203     }
204 
205     @Test
No enumsnull206     fun `No enums`() {
207         check(
208             apiLint = "", // enabled
209             expectedIssues =
210                 """
211                 src/android/pkg/MyEnum.java:3: error: Enums are discouraged in Android APIs [Enum]
212                 """,
213             expectedFail = DefaultLintErrorMessage,
214             sourceFiles =
215                 arrayOf(
216                     java(
217                         """
218                     package android.pkg;
219 
220                     public enum MyEnum {
221                        FOO, BAR
222                     }
223                     """
224                     )
225                 )
226         )
227     }
228 
229     @Test
Test callbacksnull230     fun `Test callbacks`() {
231         check(
232             extraArguments = arrayOf(ARG_ERROR, "CallbackInterface"),
233             apiLint = "", // enabled
234             expectedIssues =
235                 """
236                 src/android/pkg/MyCallback.java:9: error: Callback method names must follow the on<Something> style: bar [CallbackMethodName]
237                 src/android/pkg/MyCallbacks.java:3: error: Callback class names should be singular: MyCallbacks [SingularCallback]
238                 src/android/pkg/MyInterfaceCallback.java:3: error: Callbacks must be abstract class instead of interface to enable extension in future API levels: MyInterfaceCallback [CallbackInterface]
239                 src/android/pkg/MyObserver.java:3: warning: Class should be named MyCallback [CallbackName]
240                 """,
241             expectedFail = DefaultLintErrorMessage,
242             sourceFiles =
243                 arrayOf(
244                     java(
245                         """
246                     package android.pkg;
247 
248                     public class MyCallbacks {
249                     }
250                     """
251                     ),
252                     java(
253                         """
254                     package android.pkg;
255 
256                     public final class RemoteCallback {
257                         public void sendResult();
258                     }
259                     """
260                     ),
261                     java(
262                         """
263                     package android.pkg;
264 
265                     public class MyObserver {
266                     }
267                     """
268                     ),
269                     java(
270                         """
271                     package android.pkg;
272 
273                     public interface MyInterfaceCallback {
274                     }
275                     """
276                     ),
277                     java(
278                         """
279                     package android.pkg;
280 
281                     public class MyCallback {
282                         public void onFoo() {
283                         }
284                         public void onAnimationStart() {
285                         }
286                         public void onN1GoodMethod() { }
287                         public void bar() {
288                         }
289                         public static void staticMethod() {
290                         }
291                         public final void finalMethod() {
292                         }
293                     }
294                     """
295                     )
296                 )
297         )
298     }
299 
300     @Test
Test listenersnull301     fun `Test listeners`() {
302         check(
303             apiLint = "", // enabled
304             expectedIssues =
305                 """
306                 src/android/pkg/MyClassListener.java:3: error: Listeners should be an interface, or otherwise renamed Callback: MyClassListener [ListenerInterface]
307                 src/android/pkg/MyListener.java:6: error: Listener method names must follow the on<Something> style: bar [CallbackMethodName]
308                 """,
309             expectedFail = DefaultLintErrorMessage,
310             sourceFiles =
311                 arrayOf(
312                     java(
313                         """
314                     package android.pkg;
315 
316                     public abstract class MyClassListener {
317                     }
318                     """
319                     ),
320                     java(
321                         """
322                     package android.pkg;
323 
324                     public interface OnFooBarListener1 {
325                         void bar();
326                     }
327                     """
328                     ),
329                     java(
330                         """
331                     package android.pkg;
332 
333                     public interface OnFooBarListener2 {
334                         void onFooBar(); // OK
335                     }
336                     """
337                     ),
338                     java(
339                         """
340                     package android.pkg;
341 
342                     public interface MyInterfaceListener {
343                     }
344                     """
345                     ),
346                     java(
347                         """
348                     package android.pkg;
349 
350                     public interface MyListener {
351                         public void onFoo() {
352                         }
353                         public void bar() {
354                         }
355                         public static void staticMethod() {
356                         }
357                         public static void finalMethod() {
358                         }
359                     }
360                     """
361                     )
362                 )
363         )
364     }
365 
366     @Test
Test actionsnull367     fun `Test actions`() {
368         check(
369             apiLint = "", // enabled
370             expectedIssues =
371                 """
372                 src/android/accounts/Actions.java:6: error: Inconsistent action value; expected `android.accounts.action.ACCOUNT_OPENED`, was `android.accounts.ACCOUNT_OPENED` [ActionValue]
373                 src/android/accounts/Actions.java:7: error: Intent action constant name must be ACTION_FOO: ACCOUNT_ADDED [IntentName]
374                 src/android/accounts/Actions.java:8: error: Intent action constant name must be ACTION_FOO: SOMETHING [IntentName]
375                 """,
376             expectedFail = DefaultLintErrorMessage,
377             sourceFiles =
378                 arrayOf(
379                     java(
380                         """
381                     package android.accounts;
382 
383                     public class Actions {
384                         private Actions() { }
385                         public static final String ACTION_ACCOUNT_REMOVED = "android.accounts.action.ACCOUNT_REMOVED";
386                         public static final String ACTION_ACCOUNT_OPENED = "android.accounts.ACCOUNT_OPENED";
387                         public static final String ACCOUNT_ADDED = "android.accounts.action.ACCOUNT_ADDED";
388                         public static final String SOMETHING = "android.accounts.action.ACCOUNT_MOVED";
389                     }
390                     """
391                     )
392                 )
393         )
394     }
395 
396     @Test
Test extrasnull397     fun `Test extras`() {
398         check(
399             apiLint = "", // enabled
400             expectedIssues =
401                 """
402                 src/android/accounts/Extras.java:5: error: Inconsistent extra value; expected `android.accounts.extra.AUTOMATIC_RULE_ID`, was `android.app.extra.AUTOMATIC_RULE_ID` [ActionValue]
403                 src/android/accounts/Extras.java:6: error: Intent extra constant name must be EXTRA_FOO: SOMETHING_EXTRA [IntentName]
404                 src/android/accounts/Extras.java:7: error: Intent extra constant name must be EXTRA_FOO: RULE_ID [IntentName]
405                 """,
406             expectedFail = DefaultLintErrorMessage,
407             sourceFiles =
408                 arrayOf(
409                     java(
410                         """
411                     package android.accounts;
412 
413                     public class Extras {
414                         private Extras() { }
415                         public static final String EXTRA_AUTOMATIC_RULE_ID = "android.app.extra.AUTOMATIC_RULE_ID";
416                         public static final String SOMETHING_EXTRA = "something.here";
417                         public static final String RULE_ID = "android.app.extra.AUTOMATIC_RULE_ID";
418                     }
419                     """
420                     )
421                 )
422         )
423     }
424 
425     @Test
Test Parcelablenull426     fun `Test Parcelable`() {
427         check(
428             apiLint = "", // enabled
429             expectedIssues =
430                 """
431                 src/android/pkg/MissingCreator.java:5: error: Parcelable requires a `CREATOR` field; missing in android.pkg.MissingCreator [ParcelCreator]
432                 src/android/pkg/MissingDescribeContents.java:5: error: Parcelable requires `public int describeContents()`; missing in android.pkg.MissingDescribeContents [ParcelCreator]
433                 src/android/pkg/MissingWriteToParcel.java:5: error: Parcelable requires `void writeToParcel(Parcel, int)`; missing in android.pkg.MissingWriteToParcel [ParcelCreator]
434                 src/android/pkg/NonFinalParcelable.java:5: error: Parcelable classes must be final: android.pkg.NonFinalParcelable is not final [ParcelNotFinal]
435                 src/android/pkg/ParcelableConstructor.java:6: error: Parcelable inflation is exposed through CREATOR, not raw constructors, in android.pkg.ParcelableConstructor [ParcelConstructor]
436                 """,
437             expectedFail = DefaultLintErrorMessage,
438             sourceFiles =
439                 arrayOf(
440                     java(
441                         """
442                     package android.pkg;
443 
444                     import androidx.annotation.Nullable;
445 
446                     public final class ParcelableConstructor implements android.os.Parcelable {
447                         public ParcelableConstructor(@Nullable android.os.Parcel p) { }
448                         public int describeContents() { return 0; }
449                         public void writeToParcel(@Nullable android.os.Parcel p, int f) { }
450                         @Nullable
451                         public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR = null;
452                     }
453                     """
454                     ),
455                     java(
456                         """
457                     package android.pkg;
458 
459                     import androidx.annotation.Nullable;
460 
461                     public class NonFinalParcelable implements android.os.Parcelable {
462                         public NonFinalParcelable() { }
463                         public int describeContents() { return 0; }
464                         public void writeToParcel(@Nullable android.os.Parcel p, int f) { }
465                         @Nullable
466                         public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR = null;
467                     }
468                     """
469                     ),
470                     java(
471                         """
472                     package android.pkg;
473 
474                     import androidx.annotation.Nullable;
475 
476                     public final class MissingCreator implements android.os.Parcelable {
477                         public MissingCreator() { }
478                         public int describeContents() { return 0; }
479                         public void writeToParcel(@Nullable android.os.Parcel p, int f) { }
480                     }
481                     """
482                     ),
483                     java(
484                         """
485                     package android.pkg;
486 
487                     import androidx.annotation.Nullable;
488 
489                     public final class MissingDescribeContents implements android.os.Parcelable {
490                         public MissingDescribeContents() { }
491                         public void writeToParcel(@Nullable android.os.Parcel p, int f) { }
492                         @Nullable
493                         public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR = null;
494                     }
495                     """
496                     ),
497                     java(
498                         """
499                     package android.pkg;
500 
501                     import androidx.annotation.Nullable;
502 
503                     public final class MissingWriteToParcel implements android.os.Parcelable {
504                         public MissingWriteToParcel() { }
505                         public int describeContents() { return 0; }
506                         @Nullable
507                         public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR = null;
508                     }
509                     """
510                     ),
511                     androidxNullableSource
512                 )
513         )
514     }
515 
516     @Test
No protected methods or fields are allowednull517     fun `No protected methods or fields are allowed`() {
518         check(
519             apiLint = "", // enabled
520             expectedIssues =
521                 """
522                 src/android/pkg/MyClass.java:6: error: Protected methods not allowed; must be public: method android.pkg.MyClass.wrong()} [ProtectedMember]
523                 src/android/pkg/MyClass.java:8: error: Protected fields not allowed; must be public: field android.pkg.MyClass.wrong} [ProtectedMember]
524                 """,
525             expectedFail = DefaultLintErrorMessage,
526             sourceFiles =
527                 arrayOf(
528                     java(
529                         """
530                     package android.pkg;
531 
532                     public abstract class MyClass implements AutoCloseable {
533                         public void ok() { }
534                         protected void finalize() { } // OK
535                         protected void wrong() { }
536                         public final int ok = 42;
537                         protected final int wrong = 5;
538                         private int ok2 = 2;
539                     }
540                     """
541                     )
542                 )
543         )
544     }
545 
546     @RequiresCapabilities(Capability.KOTLIN)
547     @Test
Fields must be final and properly namednull548     fun `Fields must be final and properly named`() {
549         check(
550             apiLint = "", // enabled
551             expectedIssues =
552                 """
553                 src/android/pkg/MyClass.java:5: error: Bare field badMutable must be marked final, or moved behind accessors if mutable [MutableBareField]
554                 src/android/pkg/MyClass.java:6: error: Internal field mBadName must not be exposed [InternalField]
555                 src/android/pkg/MyClass.java:7: error: Non-static field AlsoBadName must be named using fooBar style [StartWithLower]
556                 src/android/pkg/MyClass.java:8: error: Constant field names must be named with only upper case characters: `android.pkg.MyClass#sBadStaticName`, should be `S_BAD_STATIC_NAME`? [AllUpper]
557                 src/android/pkg/MyClass.java:8: error: Internal field sBadStaticName must not be exposed [InternalField]
558                 src/android/pkg/MyClass.java:9: error: Bare field badStaticMutable must be marked final, or moved behind accessors if mutable [MutableBareField]
559                 src/android/pkg/MyClass.java:10: error: Constant BAD_CONSTANT must be marked static final [AllUpper]
560                 src/android/pkg/MyClass.java:10: error: Bare field BAD_CONSTANT must be marked final, or moved behind accessors if mutable [MutableBareField]
561                 src/android/pkg/MyClass.java:11: error: Constant ALSO_BAD_CONSTANT must be marked static final [AllUpper]
562                 src/android/pkg/MyClass.java:11: error: Non-static field ALSO_BAD_CONSTANT must be named using fooBar style [StartWithLower]
563                 src/android/pkg/MyClass.java:15: error: Internal field mBad must not be exposed [InternalField]
564                 """,
565             expectedFail = DefaultLintErrorMessage,
566             sourceFiles =
567                 arrayOf(
568                     java(
569                         """
570                     package android.pkg;
571 
572                     public class MyClass {
573                         private int mOk;
574                         public int badMutable;
575                         public final int mBadName;
576                         public final int AlsoBadName;
577                         public static final int sBadStaticName;
578                         public static int badStaticMutable;
579                         public static int BAD_CONSTANT;
580                         public final int ALSO_BAD_CONSTANT;
581 
582                         public static class LayoutParams {
583                             public int ok;
584                             public int mBad;
585                         }
586                     }
587                     """
588                     ),
589                     kotlin(
590                         """
591                     package android.pkg
592 
593                     class KotlinClass(val ok: Int) {
594                         companion object {
595                             const val OkConstant = 1
596                             const val OK_CONSTANT = 2
597                             @JvmField
598                             val OkSingleton = KotlinClass(3)
599                             @JvmField
600                             val OK_SINGLETON = KotlinClass(4)
601                         }
602 
603                         object OkObject
604                     }
605                     """
606                     )
607                 )
608         )
609     }
610 
611     @Test
Only android_net_Uri allowednull612     fun `Only android_net_Uri allowed`() {
613         check(
614             apiLint = "", // enabled
615             expectedIssues =
616                 """
617                 src/android/pkg/MyClass.java:7: error: Use android.net.Uri instead of java.net.URL (method android.pkg.MyClass.bad1()) [AndroidUri]
618                 src/android/pkg/MyClass.java:8: error: Use android.net.Uri instead of java.net.URI (parameter param in android.pkg.MyClass.bad2(java.util.List<java.net.URI> param)) [AndroidUri]
619                 src/android/pkg/MyClass.java:9: error: Use android.net.Uri instead of android.net.URL (parameter param in android.pkg.MyClass.bad3(android.net.URL param)) [AndroidUri]
620                 """,
621             expectedFail = DefaultLintErrorMessage,
622             extraArguments = arrayOf(ARG_HIDE, "AcronymName"),
623             sourceFiles =
624                 arrayOf(
625                     java(
626                         """
627                     package android.pkg;
628 
629                     import java.util.List;import java.util.concurrent.CompletableFuture;import java.util.concurrent.Future;
630                     import androidx.annotation.NonNull;
631 
632                     public final class MyClass {
633                         public @NonNull java.net.URL bad1() { throw new RuntimeException(); }
634                         public void bad2(@NonNull List<java.net.URI> param) { }
635                         public void bad3(@NonNull android.net.URL param) { }
636                         public void good(@NonNull android.net.Uri param) { }
637                     }
638                     """
639                     ),
640                     java(
641                         """
642                             package android.net;
643                             public class URL {
644                                 private URL() {}
645                             }
646                         """
647                     ),
648                     java(
649                         """
650                             package android.net;
651                             public class Uri {}
652                         """
653                     ),
654                     androidxNonNullSource
655                 )
656         )
657     }
658 
659     @Test
CompletableFuture and plain Future not allowednull660     fun `CompletableFuture and plain Future not allowed`() {
661         check(
662             apiLint = "", // enabled
663             expectedIssues =
664                 """
665                 src/android/pkg/MyClass.java:9: error: Use ListenableFuture (library), or a combination of OutcomeReceiver<R,E>, Executor, and CancellationSignal (platform) instead of java.util.concurrent.CompletableFuture (method android.pkg.MyClass.bad1()) [BadFuture]
666                 src/android/pkg/MyClass.java:10: error: Use ListenableFuture (library), or a combination of OutcomeReceiver<R,E>, Executor, and CancellationSignal (platform) instead of java.util.concurrent.CompletableFuture (parameter param in android.pkg.MyClass.bad2(java.util.concurrent.CompletableFuture<java.lang.String> param)) [BadFuture]
667                 src/android/pkg/MyClass.java:11: error: Use ListenableFuture (library), or a combination of OutcomeReceiver<R,E>, Executor, and CancellationSignal (platform) instead of java.util.concurrent.Future (method android.pkg.MyClass.bad3()) [BadFuture]
668                 src/android/pkg/MyClass.java:12: error: Use ListenableFuture (library), or a combination of OutcomeReceiver<R,E>, Executor, and CancellationSignal (platform) instead of java.util.concurrent.Future (parameter param in android.pkg.MyClass.bad4(java.util.concurrent.Future<java.lang.String> param)) [BadFuture]
669                 src/android/pkg/MyClass.java:17: error: BadFuture should not extend `java.util.concurrent.Future`. In AndroidX, use (but do not extend) ListenableFuture. In platform, use a combination of OutcomeReceiver<R,E>, Executor, and CancellationSignal`. [BadFuture]
670                 src/android/pkg/MyClass.java:19: error: BadFutureClass should not implement `java.util.concurrent.Future`. In AndroidX, use (but do not extend) ListenableFuture. In platform, use a combination of OutcomeReceiver<R,E>, Executor, and CancellationSignal`. [BadFuture]
671                 src/android/pkg/MyClass.java:21: error: BadCompletableFuture should not extend `java.util.concurrent.CompletableFuture`. In AndroidX, use (but do not extend) ListenableFuture. In platform, use a combination of OutcomeReceiver<R,E>, Executor, and CancellationSignal`. [BadFuture]
672                 """,
673             expectedFail = DefaultLintErrorMessage,
674             sourceFiles =
675                 arrayOf(
676                     java(
677                         """
678                     package android.pkg;
679 
680                     import java.util.concurrent.CompletableFuture;
681                     import java.util.concurrent.Future;
682                     import androidx.annotation.Nullable;
683                     import com.google.common.util.concurrent.ListenableFuture;
684 
685                     public final class MyClass {
686                         public @Nullable CompletableFuture<String> bad1() { return null; }
687                         public void bad2(@Nullable CompletableFuture<String> param) { }
688                         public @Nullable Future<String> bad3() { return null; }
689                         public void bad4(@Nullable Future<String> param) { }
690 
691                         public @Nullable ListenableFuture<String> okAsync() { return null; }
692                         public void ok2(@Nullable ListenableFuture<String> param) { }
693 
694                         public interface BadFuture<T> extends AnotherInterface, Future<T> {
695                         }
696                         public static abstract class BadFutureClass<T> implements Future<T> {
697                         }
698                         public class BadCompletableFuture<T> extends CompletableFuture<T> {
699                         }
700                         public interface AnotherInterface {
701                         }
702                     }
703                     """
704                     ),
705                     java(
706                         """
707                     package com.google.common.util.concurrent;
708                     public class ListenableFuture<T> {
709                     }
710                     """
711                     ),
712                     androidxNullableSource
713                 )
714         )
715     }
716 
717     @Test
Typedef must be hiddennull718     fun `Typedef must be hidden`() {
719         check(
720             apiLint = "", // enabled
721             expectedIssues =
722                 """
723                 src/android/pkg/MyClass.java:14: error: Don't expose @StringDef: SomeString must be hidden. [PublicTypedef]
724                 src/android/pkg/MyClass.java:19: error: Don't expose @IntDef: SomeInt must be hidden. [PublicTypedef]
725                 src/android/pkg/MyClass.java:24: error: Don't expose @LongDef: SomeLong must be hidden. [PublicTypedef]
726                 """,
727             expectedFail = DefaultLintErrorMessage,
728             sourceFiles =
729                 arrayOf(
730                     java(
731                         """
732                     package android.pkg;
733 
734                     public final class MyClass {
735                             private MyClass() {}
736 
737                             public static final String SOME_STRING = "abc";
738                             public static final int SOME_INT = 1;
739                             public static final long SOME_LONG = 1L;
740 
741                             @android.annotation.StringDef(value = {
742                                     SOME_STRING
743                             })
744                             @Retention(RetentionPolicy.SOURCE)
745                             public @interface SomeString {}
746                             @android.annotation.IntDef(value = {
747                                     SOME_INT
748                             })
749                             @Retention(RetentionPolicy.SOURCE)
750                             public @interface SomeInt {}
751                             @android.annotation.LongDef(value = {
752                                     SOME_LONG
753                             })
754                             @Retention(RetentionPolicy.SOURCE)
755                             public @interface SomeLong {}
756                     }
757                     """
758                     )
759                 )
760         )
761     }
762 
763     @Test
Ensure registration methods are matchednull764     fun `Ensure registration methods are matched`() {
765         check(
766             apiLint = "", // enabled
767             expectedIssues =
768                 """
769                 src/android/pkg/RegistrationInterface.java:6: error: Found registerOverriddenUnpairedCallback but not unregisterOverriddenUnpairedCallback in android.pkg.RegistrationInterface [PairedRegistration]
770                 src/android/pkg/RegistrationMethods.java:8: error: Found registerUnpairedCallback but not unregisterUnpairedCallback in android.pkg.RegistrationMethods [PairedRegistration]
771                 src/android/pkg/RegistrationMethods.java:12: error: Found unregisterMismatchedCallback but not registerMismatchedCallback in android.pkg.RegistrationMethods [PairedRegistration]
772                 src/android/pkg/RegistrationMethods.java:13: error: Found addUnpairedCallback but not removeUnpairedCallback in android.pkg.RegistrationMethods [PairedRegistration]
773                 src/android/pkg/RegistrationMethods.java:18: error: Found addUnpairedListener but not removeUnpairedListener in android.pkg.RegistrationMethods [PairedRegistration]
774                 src/android/pkg/RegistrationMethods.java:19: error: Found removeMismatchedListener but not addMismatchedListener in android.pkg.RegistrationMethods [PairedRegistration]
775                 src/android/pkg/RegistrationMethods.java:20: error: Found registerUnpairedListener but not unregisterUnpairedListener in android.pkg.RegistrationMethods [PairedRegistration]
776                 """,
777             expectedFail = DefaultLintErrorMessage,
778             sourceFiles =
779                 arrayOf(
780                     java(
781                         """
782                     package android.pkg;
783 
784                     import androidx.annotation.Nullable;
785 
786                     public class RegistrationMethods implements RegistrationInterface {
787                         public void registerOkCallback(@Nullable Runnable r) { } // OK
788                         public void unregisterOkCallback(@Nullable Runnable r) { } // OK
789                         public void registerUnpairedCallback(@Nullable Runnable r) { }
790                         // OK here because it is override
791                         @Override
792                         public void registerOverriddenUnpairedCallback(@Nullable Runnable r) { }
793                         public void unregisterMismatchedCallback(@Nullable Runnable r) { }
794                         public void addUnpairedCallback(@Nullable Runnable r) { }
795 
796                         public void addOkListener(@Nullable Runnable r) { } // OK
797                         public void removeOkListener(@Nullable Runnable r) { } // OK
798 
799                         public void addUnpairedListener(@Nullable Runnable r) { }
800                         public void removeMismatchedListener(@Nullable Runnable r) { }
801                         public void registerUnpairedListener(@Nullable Runnable r) { }
802                     }
803                     """
804                     ),
805                     java(
806                         """
807                     package android.pkg;
808 
809                     import androidx.annotation.Nullable;
810 
811                     public interface RegistrationInterface {
812                         void registerOverriddenUnpairedCallback(@Nullable Runnable r) { }
813                     }
814                     """
815                     ),
816                     androidxNullableSource
817                 )
818         )
819     }
820 
821     @Test
Check intent builder namesnull822     fun `Check intent builder names`() {
823         check(
824             apiLint = "", // enabled
825             expectedIssues =
826                 """
827                 src/android/pkg/IntentBuilderNames.java:9: warning: Methods creating an Intent should be named `create<Foo>Intent()`, was `makeMyIntent` [IntentBuilderName]
828                 src/android/pkg/IntentBuilderNames.java:11: warning: Methods creating an Intent should be named `create<Foo>Intent()`, was `createIntentNow` [IntentBuilderName]
829                 """,
830             sourceFiles =
831                 arrayOf(
832                     java(
833                         """
834                     package android.pkg;
835                     import android.content.Intent;
836                     import androidx.annotation.Nullable;
837 
838                     public class IntentBuilderNames {
839                         @Nullable
840                         public Intent createEnrollIntent() { return null; } // OK
841                         @Nullable
842                         public Intent makeMyIntent() { return null; } // WARN
843                         @Nullable
844                         public Intent createIntentNow() { return null; } // WARN
845                     }
846                     """
847                     ),
848                     androidxNullableSource
849                 )
850         )
851     }
852 
853     @Test
Check helper classesnull854     fun `Check helper classes`() {
855         check(
856             apiLint = "", // enabled
857             expectedIssues =
858                 """
859                 src/android/pkg/MyClass1.java:3: error: Inconsistent class name; should be `<Foo>Activity`, was `MyClass1` [ContextNameSuffix]
860                 src/android/pkg/MyClass1.java:3: error: MyClass1 should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead. [ForbiddenSuperClass]
861                 src/android/pkg/MyClass1.java:6: warning: Methods implemented by developers should follow the on<Something> style, was `badlyNamedAbstractMethod` [OnNameExpected]
862                 src/android/pkg/MyClass1.java:7: warning: If implemented by developer, should follow the on<Something> style; otherwise consider marking final [OnNameExpected]
863                 src/android/pkg/MyClass2.java:3: error: Inconsistent class name; should be `<Foo>Provider`, was `MyClass2` [ContextNameSuffix]
864                 src/android/pkg/MyClass3.java:3: error: Inconsistent class name; should be `<Foo>Service`, was `MyClass3` [ContextNameSuffix]
865                 src/android/pkg/MyClass4.java:3: error: Inconsistent class name; should be `<Foo>Receiver`, was `MyClass4` [ContextNameSuffix]
866                 src/android/pkg/MyOkActivity.java:3: error: MyOkActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead. [ForbiddenSuperClass]
867                 """,
868             expectedFail = DefaultLintErrorMessage,
869             sourceFiles =
870                 arrayOf(
871                     java(
872                         """
873                     package android.pkg;
874 
875                     public class MyClass1 extends android.app.Activity {
876                         public void onOk() { }
877                         public final void ok() { }
878                         public abstract void badlyNamedAbstractMethod();
879                         public void badlyNamedMethod() { }
880                         public static void staticOk() { }
881                     }
882                     """
883                     ),
884                     java(
885                         """
886                     package android.pkg;
887 
888                     public class MyClass2 extends android.content.ContentProvider {
889                         public static final String PROVIDER_INTERFACE = "android.pkg.MyClass2";
890                         public final void ok();
891                     }
892                     """
893                     ),
894                     java(
895                         """
896                     package android.pkg;
897 
898                     public class MyClass3 extends android.app.Service {
899                         public static final String SERVICE_INTERFACE = "android.pkg.MyClass3";
900                         public final void ok();
901                     }
902                     """
903                     ),
904                     java(
905                         """
906                     package android.pkg;
907 
908                     public class MyClass4 extends android.content.BroadcastReceiver {
909                     }
910                     """
911                     ),
912                     java(
913                         """
914                     package android.pkg;
915 
916                     public class MyOkActivity extends android.app.Activity {
917                     }
918                     """
919                     )
920                 )
921         )
922     }
923 
924     @Test
Check suppress works on inherited methodsnull925     fun `Check suppress works on inherited methods`() {
926         check(
927             apiLint = "", // enabled
928             expectedIssues =
929                 """
930                 src/android/pkg/Ok.java:11: warning: Should avoid odd sized primitives; use `int` instead of `short` in method android.pkg.Ok.Public.shouldFail(PublicT) [NoByteOrShort]
931                 """,
932             sourceFiles =
933                 arrayOf(
934                     java(
935                         """
936                 package android.pkg;
937 
938                 import androidx.annotation.NonNull;
939 
940                 public class Ok {
941 
942                     static final class Hidden<HiddenT> {
943                         @SuppressWarnings("NoByteOrShort")
944                         public short suppressed(HiddenT t) { return null; }
945 
946                         public short shouldFail(HiddenT t) { return null; }
947                     }
948 
949                     public static final class Public<PublicT> extends Hidden<PublicT> {
950                     }
951                 }
952                 """
953                     ),
954                     androidxNonNullSource
955                 )
956         )
957     }
958 
959     @Test
Raw AIDLnull960     fun `Raw AIDL`() {
961         check(
962             apiLint = "", // enabled
963             expectedIssues =
964                 """
965                 src/android/pkg/MyClass1.java:3: error: Raw AIDL interfaces must not be exposed: MyClass1 extends Binder [RawAidl]
966                 src/android/pkg/MyClass2.java:3: error: Raw AIDL interfaces must not be exposed: MyClass2 implements IInterface [RawAidl]
967                 """,
968             expectedFail = DefaultLintErrorMessage,
969             sourceFiles =
970                 arrayOf(
971                     java(
972                         """
973                     package android.pkg;
974 
975                     public abstract class MyClass1 extends android.os.Binder {
976                     }
977                     """
978                     ),
979                     java(
980                         """
981                     package android.pkg;
982 
983                     public abstract class MyClass2 implements android.os.IInterface {
984                     }
985                     """
986                     ),
987                     java(
988                         """
989                     package android.pkg;
990                     // Ensure that we don't flag transitively implementing IInterface
991                     public class MyClass3 extends MyClass1 implements MyClass2 {
992                     }
993                     """
994                     )
995                 )
996         )
997     }
998 
999     @Test
Internal packagesnull1000     fun `Internal packages`() {
1001         check(
1002             apiLint = "", // enabled
1003             expectedIssues =
1004                 """
1005                 src/com/android/pkg/MyClass.java:3: error: Internal classes must not be exposed [InternalClasses]
1006                 """,
1007             expectedFail = DefaultLintErrorMessage,
1008             sourceFiles =
1009                 arrayOf(
1010                     java(
1011                         """
1012                     package com.android.pkg;
1013 
1014                     public class MyClass {
1015                     }
1016                     """
1017                     )
1018                 )
1019         )
1020     }
1021 
1022     @Test
Check package layeringnull1023     fun `Check package layering`() {
1024         check(
1025             apiLint = "", // enabled
1026             expectedIssues =
1027                 """
1028                 src/android/content/MyClass1.java:8: warning: Field type `android.view.View` violates package layering: nothing in `package android.content` should depend on `package android.view` [PackageLayering]
1029                 src/android/content/MyClass1.java:8: warning: Method parameter type `android.view.View` violates package layering: nothing in `package android.content` should depend on `package android.view` [PackageLayering]
1030                 src/android/content/MyClass1.java:8: warning: Method parameter type `android.view.View` violates package layering: nothing in `package android.content` should depend on `package android.view` [PackageLayering]
1031                 src/android/content/MyClass1.java:8: warning: Method return type `android.view.View` violates package layering: nothing in `package android.content` should depend on `package android.view` [PackageLayering]
1032                 """,
1033             sourceFiles =
1034                 arrayOf(
1035                     java(
1036                         """
1037                     package android.content;
1038 
1039                     import android.graphics.drawable.Drawable;
1040                     import android.graphics.Bitmap;
1041                     import android.view.View;
1042                     import androidx.annotation.Nullable;
1043 
1044                     public class MyClass1 {
1045                         @Nullable
1046                         public final View view = null;
1047                         @Nullable
1048                         public final Drawable drawable = null;
1049                         @Nullable
1050                         public final Bitmap bitmap = null;
1051                         @Nullable
1052                         public View ok(@Nullable View view, @Nullable Drawable drawable) { return null; }
1053                         @Nullable
1054                         public Bitmap wrong(@Nullable View view, @Nullable Bitmap bitmap) { return null; }
1055                     }
1056                     """
1057                     ),
1058                     androidxNullableSource
1059                 )
1060         )
1061     }
1062 
1063     @Test
Check boolean getter and setter naming patternsnull1064     fun `Check boolean getter and setter naming patterns`() {
1065         check(
1066             apiLint = "", // enabled
1067             expectedIssues =
1068                 """
1069                     src/android/pkg/MyClass.java:25: error: Symmetric method for `isVisibleBad` must be named `setVisibleBad`; was `setIsVisibleBad` [GetterSetterNames]
1070                     src/android/pkg/MyClass.java:28: error: Symmetric method for `hasTransientStateBad` must be named `setHasTransientStateBad`; was `setTransientStateBad` [GetterSetterNames]
1071                     src/android/pkg/MyClass.java:32: error: Symmetric method for `setHasTransientStateAlsoBad` must be named `hasTransientStateAlsoBad`; was `isHasTransientStateAlsoBad` [GetterSetterNames]
1072                     src/android/pkg/MyClass.java:35: error: Symmetric method for `setCanRecordBad` must be named `canRecordBad`; was `isCanRecordBad` [GetterSetterNames]
1073                     src/android/pkg/MyClass.java:38: error: Symmetric method for `setShouldFitWidthBad` must be named `shouldFitWidthBad`; was `isShouldFitWidthBad` [GetterSetterNames]
1074                     src/android/pkg/MyClass.java:41: error: Symmetric method for `setWiFiRoamingSettingEnabledBad` must be named `isWiFiRoamingSettingEnabledBad`; was `getWiFiRoamingSettingEnabledBad` [GetterSetterNames]
1075                     src/android/pkg/MyClass.java:44: error: Symmetric method for `setEnabledBad` must be named `isEnabledBad`; was `getEnabledBad` [GetterSetterNames]
1076                 """,
1077             expectedFail = DefaultLintErrorMessage,
1078             sourceFiles =
1079                 arrayOf(
1080                     java(
1081                         """
1082                     package android.pkg;
1083                     import androidx.annotation.NonNull;
1084                     public class MyClass {
1085                         // Correct
1086                         public void setVisible(boolean visible) {}
1087                         public boolean isVisible() { return false; }
1088 
1089                         public void setHasTransientState(boolean hasTransientState) {}
1090                         public boolean hasTransientState() { return false; }
1091 
1092                         public void setCanRecord(boolean canRecord) {}
1093                         public boolean canRecord() { return false; }
1094 
1095                         public void setShouldFitWidth(boolean shouldFitWidth) {}
1096                         public boolean shouldFitWidth() { return false; }
1097 
1098                         public void setWiFiRoamingSettingEnabled(boolean enabled) {}
1099                         public boolean isWiFiRoamingSettingEnabled() { return false; }
1100 
1101                         public boolean hasRoute() { return false; }
1102                         public @NonNull String getRoute() { return ""; }
1103                         public void setRoute(@NonNull String route) {}
1104 
1105                         // Bad
1106                         public void setIsVisibleBad(boolean visible) {}
1107                         public boolean isVisibleBad() { return false; }
1108 
1109                         public void setTransientStateBad(boolean hasTransientState) {}
1110                         public boolean hasTransientStateBad() { return false; }
1111 
1112                         public void setHasTransientStateAlsoBad(boolean hasTransientState) {}
1113                         public boolean isHasTransientStateAlsoBad() { return false; }
1114 
1115                         public void setCanRecordBad(boolean canRecord) {}
1116                         public boolean isCanRecordBad() { return false; }
1117 
1118                         public void setShouldFitWidthBad(boolean shouldFitWidth) {}
1119                         public boolean isShouldFitWidthBad() { return false; }
1120 
1121                         public void setWiFiRoamingSettingEnabledBad(boolean enabled) {}
1122                         public boolean getWiFiRoamingSettingEnabledBad() { return false; }
1123 
1124                         public void setEnabledBad(boolean enabled) {}
1125                         public boolean getEnabledBad() { return false; }
1126                     }
1127                     """
1128                     ),
1129                     androidxNonNullSource
1130                 )
1131         )
1132     }
1133 
1134     @RequiresCapabilities(Capability.KOTLIN)
1135     @Test
Check boolean property accessor naming patterns in Kotlinnull1136     fun `Check boolean property accessor naming patterns in Kotlin`() {
1137         check(
1138             apiLint = "", // enabled
1139             expectedIssues =
1140                 """
1141                     src/android/pkg/MyClass.kt:19: error: Invalid name for boolean property `visibleBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames]
1142                     src/android/pkg/MyClass.kt:22: error: Invalid name for boolean property setter `setIsVisibleBad`, should be `setVisibleSetterBad`. [GetterSetterNames]
1143                     src/android/pkg/MyClass.kt:25: error: Invalid name for boolean property `transientStateBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames]
1144                     src/android/pkg/MyClass.kt:27: error: Invalid prefix `isHas` for boolean property `isHasTransientStateAlsoBad`. [GetterSetterNames]
1145                     src/android/pkg/MyClass.kt:29: error: Invalid prefix `isCan` for boolean property `isCanRecordBad`. [GetterSetterNames]
1146                     src/android/pkg/MyClass.kt:31: error: Invalid prefix `isShould` for boolean property `isShouldFitWidthBad`. [GetterSetterNames]
1147                     src/android/pkg/MyClass.kt:33: error: Invalid name for boolean property `wiFiRoamingSettingEnabledBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames]
1148                     src/android/pkg/MyClass.kt:35: error: Invalid name for boolean property `enabledBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames]
1149                     src/android/pkg/MyClass.kt:37: error: Getter for boolean property `hasTransientStateGetterBad` is named `getHasTransientStateGetterBad` but should match the property name. Use `@get:JvmName` to rename. [GetterSetterNames]
1150                     src/android/pkg/MyClass.kt:39: error: Getter for boolean property `canRecordGetterBad` is named `getCanRecordGetterBad` but should match the property name. Use `@get:JvmName` to rename. [GetterSetterNames]
1151                     src/android/pkg/MyClass.kt:41: error: Getter for boolean property `shouldFitWidthGetterBad` is named `getShouldFitWidthGetterBad` but should match the property name. Use `@get:JvmName` to rename. [GetterSetterNames]
1152             """,
1153             expectedFail = DefaultLintErrorMessage,
1154             sourceFiles =
1155                 arrayOf(
1156                     kotlin(
1157                         """
1158                     package android.pkg
1159 
1160                     class MyClass {
1161                         // Correct
1162                         var isVisible: Boolean = false
1163 
1164                         @get:JvmName("hasTransientState")
1165                         var hasTransientState: Boolean = false
1166 
1167                         @get:JvmName("canRecord")
1168                         var canRecord: Boolean = false
1169 
1170                         @get:JvmName("shouldFitWidth")
1171                         var shouldFitWidth: Boolean = false
1172 
1173                         var isWiFiRoamingSettingEnabled: Boolean = false
1174 
1175                         // Bad
1176                         var visibleBad: Boolean = false
1177 
1178                         @set:JvmName("setIsVisibleBad")
1179                         var isVisibleSetterBad: Boolean = false
1180 
1181                         @get:JvmName("hasTransientStateBad")
1182                         var transientStateBad: Boolean = false
1183 
1184                         var isHasTransientStateAlsoBad: Boolean = false
1185 
1186                         var isCanRecordBad: Boolean = false
1187 
1188                         var isShouldFitWidthBad: Boolean = false
1189 
1190                         var wiFiRoamingSettingEnabledBad: Boolean = false
1191 
1192                         var enabledBad: Boolean = false
1193 
1194                         var hasTransientStateGetterBad: Boolean = false
1195 
1196                         var canRecordGetterBad: Boolean = false
1197 
1198                         var shouldFitWidthGetterBad: Boolean = false
1199                     }
1200                     """
1201                     )
1202                 )
1203         )
1204     }
1205 
Check boolean constructor parameter accessor naming patterns in Kotlinnull1206     private fun `Check boolean constructor parameter accessor naming patterns in Kotlin`(
1207         expectedIssues: String?,
1208     ) {
1209         check(
1210             apiLint = "", // enabled
1211             expectedIssues = expectedIssues,
1212             expectedFail = DefaultLintErrorMessage,
1213             sourceFiles =
1214                 arrayOf(
1215                     kotlin(
1216                         """
1217                     package android.pkg
1218 
1219                     class MyClass(
1220                         // Correct
1221                         var isVisible: Boolean,
1222 
1223                         @get:JvmName("hasTransientState")
1224                         var hasTransientState: Boolean,
1225 
1226                         @get:JvmName("canRecord")
1227                         var canRecord: Boolean,
1228 
1229                         @get:JvmName("shouldFitWidth")
1230                         var shouldFitWidth: Boolean,
1231 
1232                         var isWiFiRoamingSettingEnabled: Boolean,
1233 
1234                         // Bad
1235                         var visibleBad: Boolean,
1236 
1237                         @set:JvmName("setIsVisibleBad")
1238                         var isVisibleSetterBad: Boolean,
1239 
1240                         @get:JvmName("hasTransientStateBad")
1241                         var transientStateBad: Boolean,
1242 
1243                         var isHasTransientStateAlsoBad: Boolean,
1244 
1245                         var isCanRecordBad: Boolean,
1246 
1247                         var isShouldFitWidthBad: Boolean,
1248 
1249                         var wiFiRoamingSettingEnabledBad: Boolean,
1250 
1251                         var enabledBad: Boolean,
1252 
1253                         var hasTransientStateGetterBad: Boolean,
1254 
1255                         var canRecordGetterBad: Boolean,
1256 
1257                         var shouldFitWidthGetterBad: Boolean
1258                     )
1259                     """
1260                     )
1261                 )
1262         )
1263     }
1264 
1265     @RequiresCapabilities(Capability.KOTLIN)
1266     @FilterByProvider("psi", "k2", action = FilterAction.EXCLUDE)
1267     @Test
Check boolean constructor parameter accessor naming patterns in Kotlin -- K1null1268     fun `Check boolean constructor parameter accessor naming patterns in Kotlin -- K1`() {
1269         `Check boolean constructor parameter accessor naming patterns in Kotlin`(
1270             // missing errors for `isVisibleSetterBad`,
1271             // `hasTransientStateGetterBad`, `canRecordGetterBad`, `shouldFitWidthGetterBad`
1272             expectedIssues =
1273                 """
1274                 src/android/pkg/MyClass.kt:19: error: Invalid name for boolean property `visibleBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames]
1275                 src/android/pkg/MyClass.kt:25: error: Invalid name for boolean property `transientStateBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames]
1276                 src/android/pkg/MyClass.kt:27: error: Invalid prefix `isHas` for boolean property `isHasTransientStateAlsoBad`. [GetterSetterNames]
1277                 src/android/pkg/MyClass.kt:29: error: Invalid prefix `isCan` for boolean property `isCanRecordBad`. [GetterSetterNames]
1278                 src/android/pkg/MyClass.kt:31: error: Invalid prefix `isShould` for boolean property `isShouldFitWidthBad`. [GetterSetterNames]
1279                 src/android/pkg/MyClass.kt:33: error: Invalid name for boolean property `wiFiRoamingSettingEnabledBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames]
1280                 src/android/pkg/MyClass.kt:35: error: Invalid name for boolean property `enabledBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames]
1281                               """,
1282         )
1283     }
1284 
1285     @RequiresCapabilities(Capability.KOTLIN)
1286     @FilterByProvider("psi", "k1", action = FilterAction.EXCLUDE)
1287     @Test
Check boolean constructor parameter accessor naming patterns in Kotlin -- K2null1288     fun `Check boolean constructor parameter accessor naming patterns in Kotlin -- K2`() {
1289         `Check boolean constructor parameter accessor naming patterns in Kotlin`(
1290             expectedIssues =
1291                 """
1292                 src/android/pkg/MyClass.kt:19: error: Invalid name for boolean property `visibleBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames]
1293                 src/android/pkg/MyClass.kt:22: error: Invalid name for boolean property setter `setIsVisibleBad`, should be `setVisibleSetterBad`. [GetterSetterNames]
1294                 src/android/pkg/MyClass.kt:25: error: Invalid name for boolean property `transientStateBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames]
1295                 src/android/pkg/MyClass.kt:27: error: Invalid prefix `isHas` for boolean property `isHasTransientStateAlsoBad`. [GetterSetterNames]
1296                 src/android/pkg/MyClass.kt:29: error: Invalid prefix `isCan` for boolean property `isCanRecordBad`. [GetterSetterNames]
1297                 src/android/pkg/MyClass.kt:31: error: Invalid prefix `isShould` for boolean property `isShouldFitWidthBad`. [GetterSetterNames]
1298                 src/android/pkg/MyClass.kt:33: error: Invalid name for boolean property `wiFiRoamingSettingEnabledBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames]
1299                 src/android/pkg/MyClass.kt:35: error: Invalid name for boolean property `enabledBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames]
1300                 src/android/pkg/MyClass.kt:37: error: Getter for boolean property `hasTransientStateGetterBad` is named `getHasTransientStateGetterBad` but should match the property name. Use `@get:JvmName` to rename. [GetterSetterNames]
1301                 src/android/pkg/MyClass.kt:39: error: Getter for boolean property `canRecordGetterBad` is named `getCanRecordGetterBad` but should match the property name. Use `@get:JvmName` to rename. [GetterSetterNames]
1302                 src/android/pkg/MyClass.kt:41: error: Getter for boolean property `shouldFitWidthGetterBad` is named `getShouldFitWidthGetterBad` but should match the property name. Use `@get:JvmName` to rename. [GetterSetterNames]
1303                               """,
1304         )
1305     }
1306 
1307     @Test
Check banned collectionsnull1308     fun `Check banned collections`() {
1309         check(
1310             apiLint = "", // enabled
1311             expectedIssues =
1312                 """
1313                 src/android/pkg/MyClass.java:6: error: Parameter type is concrete collection (`java.util.HashMap`); must be higher-level interface [ConcreteCollection]
1314                 src/android/pkg/MyClass.java:10: error: Parameter type is concrete collection (`java.util.LinkedList`); must be higher-level interface [ConcreteCollection]
1315                 src/android/pkg/MyClass.java:10: error: Return type is concrete collection (`java.util.Vector`); must be higher-level interface [ConcreteCollection]
1316                 """,
1317             expectedFail = DefaultLintErrorMessage,
1318             sourceFiles =
1319                 arrayOf(
1320                     java(
1321                         """
1322                     package android.pkg;
1323 
1324                     import androidx.annotation.NonNull;
1325 
1326                     public class MyClass {
1327                         public MyClass(@NonNull java.util.HashMap<String,String> map1,
1328                                 @NonNull java.util.Map<String,String> map2) {
1329                         }
1330                         @NonNull
1331                         public java.util.Vector<String> getList(@NonNull java.util.LinkedList<String> list) {
1332                             throw new RuntimeException();
1333                         }
1334                     }
1335                     """
1336                     ),
1337                     androidxNonNullSource
1338                 )
1339         )
1340     }
1341 
1342     @Test
Check non-overlapping flagsnull1343     fun `Check non-overlapping flags`() {
1344         check(
1345             apiLint = "", // enabled
1346             expectedIssues =
1347                 """
1348                 src/android/accounts/OverlappingFlags.java:19: warning: Found overlapping flag constant values: `TEST1_FLAG_SECOND` with value 3 (0x3) and overlapping flag value 1 (0x1) from `TEST1_FLAG_FIRST` [OverlappingConstants]
1349                 """,
1350             sourceFiles =
1351                 arrayOf(
1352                     java(
1353                         """
1354                     package android.accounts;
1355 
1356                     public class OverlappingFlags {
1357                         private OverlappingFlags() { }
1358                         public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80
1359                         public static final int DRAG_FLAG_GLOBAL_URI_READ = 1; // 0x1
1360                         public static final int DRAG_FLAG_GLOBAL_URI_WRITE = 2; // 0x2
1361                         public static final int DRAG_FLAG_OPAQUE = 512; // 0x200
1362                         public static final int SYSTEM_UI_FLAG_FULLSCREEN = 4; // 0x4
1363                         public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2; // 0x2
1364                         public static final int SYSTEM_UI_FLAG_IMMERSIVE = 2048; // 0x800
1365                         public static final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 4096; // 0x1000
1366                         public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024; // 0x400
1367                         public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512; // 0x200
1368                         public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 256; // 0x100
1369                         public static final int SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR = 16; // 0x10
1370 
1371                         public static final int TEST1_FLAG_FIRST = 1;
1372                         public static final int TEST1_FLAG_SECOND = 3;
1373                         public static final int TEST2_FLAG_FIRST = 5;
1374                     }
1375                     """
1376                     )
1377                 )
1378         )
1379     }
1380 
1381     @Test
Check no mentions of Google in APIsnull1382     fun `Check no mentions of Google in APIs`() {
1383         check(
1384             apiLint = "", // enabled
1385             expectedIssues =
1386                 """
1387                 src/android/pkg/MyClass.java:4: error: Must never reference Google (`MyGoogleService`) [MentionsGoogle]
1388                 src/android/pkg/MyClass.java:5: error: Must never reference Google (`callGoogle`) [MentionsGoogle]
1389                 """,
1390             expectedFail = DefaultLintErrorMessage,
1391             sourceFiles =
1392                 arrayOf(
1393                     java(
1394                         """
1395                     package android.pkg;
1396 
1397                     public class MyClass {
1398                         public static class MyGoogleService {
1399                             public void callGoogle() { }
1400                         }
1401                     }
1402                     """
1403                     )
1404                 )
1405         )
1406     }
1407 
1408     @Test
Check no usages of heavy BitSetnull1409     fun `Check no usages of heavy BitSet`() {
1410         check(
1411             apiLint = "", // enabled
1412             expectedIssues =
1413                 """
1414                 src/android/pkg/MyClass.java:7: error: Type must not be heavy BitSet (field android.pkg.MyClass.bitset) [HeavyBitSet]
1415                 src/android/pkg/MyClass.java:9: error: Type must not be heavy BitSet (method android.pkg.MyClass.reverse(java.util.BitSet)) [HeavyBitSet]
1416                 src/android/pkg/MyClass.java:9: error: Type must not be heavy BitSet (parameter bitset in android.pkg.MyClass.reverse(java.util.BitSet bitset)) [HeavyBitSet]
1417                 """,
1418             expectedFail = DefaultLintErrorMessage,
1419             sourceFiles =
1420                 arrayOf(
1421                     java(
1422                         """
1423                     package android.pkg;
1424                     import androidx.annotation.Nullable;
1425                     import java.util.BitSet;
1426 
1427                     public class MyClass {
1428                         @Nullable
1429                         public final BitSet bitset;
1430                         @Nullable
1431                         public BitSet reverse(@Nullable BitSet bitset) { return null; }
1432                     }
1433                     """
1434                     ),
1435                     androidxNullableSource
1436                 )
1437         )
1438     }
1439 
1440     @Test
Check Manager related issuesnull1441     fun `Check Manager related issues`() {
1442         check(
1443             apiLint = "", // enabled
1444             expectedIssues =
1445                 """
1446                 src/android/pkg/MyFirstManager.java:6: error: Managers must always be obtained from Context; no direct constructors [ManagerConstructor]
1447                 src/android/pkg/MyFirstManager.java:9: error: Managers must always be obtained from Context (`get`) [ManagerLookup]
1448                 """,
1449             expectedFail = DefaultLintErrorMessage,
1450             sourceFiles =
1451                 arrayOf(
1452                     java(
1453                         """
1454                     package android.pkg;
1455 
1456                     import androidx.annotation.Nullable;
1457 
1458                     public class MyFirstManager {
1459                         public MyFirstManager() {
1460                         }
1461                         @Nullable
1462                         public MyFirstManager get() { return null; }
1463                         @Nullable
1464                         public MySecondManager ok() { return null; }
1465                     }
1466                     """
1467                     ),
1468                     java(
1469                         """
1470                     package android.pkg;
1471 
1472                     public class MySecondManager {
1473                         private MySecondManager() {
1474                         }
1475                     }
1476                     """
1477                     ),
1478                     androidxNullableSource
1479                 )
1480         )
1481     }
1482 
1483     @Test
Check static utilitiesnull1484     fun `Check static utilities`() {
1485         check(
1486             apiLint = "", // enabled
1487             expectedIssues =
1488                 """
1489                 src/android/pkg/MyUtils1.java:3: error: Fully-static utility classes must not have constructor [StaticUtils]
1490                 src/android/pkg/MyUtils2.java:3: error: Fully-static utility classes must not have constructor [StaticUtils]
1491                 """,
1492             expectedFail = DefaultLintErrorMessage,
1493             sourceFiles =
1494                 arrayOf(
1495                     java(
1496                         """
1497                     package android.pkg;
1498 
1499                     public class MyUtils1 {
1500                         // implicit constructor
1501                         public static void foo() { }
1502                     }
1503                     """
1504                     ),
1505                     java(
1506                         """
1507                     package android.pkg;
1508 
1509                     public class MyUtils2 {
1510                         public MyUtils2() { }
1511                         public static void foo() { }
1512                     }
1513                     """
1514                     ),
1515                     java(
1516                         """
1517                     package android.pkg;
1518 
1519                     public class MyUtils3 {
1520                         private MyUtils3() { }
1521                         public static void foo() { }
1522                     }
1523                     """
1524                     ),
1525                     java(
1526                         """
1527                     package android.pkg;
1528 
1529                     public class MyUtils4 {
1530                         // OK: instance method
1531                         public void foo() { }
1532                     }
1533                     """
1534                     ),
1535                     java(
1536                         """
1537                     package android.pkg;
1538 
1539                     public class MyUtils5 {
1540                         // OK: instance field
1541                         public final int foo = 42;
1542                         public static void foo() { }
1543                     }
1544                     """
1545                     )
1546                 )
1547         )
1548     }
1549 
1550     @RequiresCapabilities(Capability.KOTLIN)
1551     @Test
Check context firstnull1552     fun `Check context first`() {
1553         check(
1554             apiLint = "", // enabled
1555             expectedIssues =
1556                 """
1557                 src/android/pkg/MyClass.java:11: error: Context is distinct, so it must be the first argument (method `wrong`) [ContextFirst]
1558                 src/android/pkg/MyClass.java:12: error: ContentResolver is distinct, so it must be the first argument (method `wrong`) [ContextFirst]
1559                 src/android/pkg/test.kt:5: error: Context is distinct, so it must be the first argument (method `badCall`) [ContextFirst]
1560                 """,
1561             expectedFail = DefaultLintErrorMessage,
1562             sourceFiles =
1563                 arrayOf(
1564                     java(
1565                         """
1566                     package android.pkg;
1567                     import android.content.Context;
1568                     import android.content.ContentResolver;
1569                     import androidx.annotation.Nullable;
1570 
1571                     public class MyClass {
1572                         public MyClass(@Nullable Context context1, @Nullable Context context2) {
1573                         }
1574                         public void ok(@Nullable ContentResolver resolver, int i) { }
1575                         public void ok(@Nullable Context context, int i) { }
1576                         public void wrong(int i, @Nullable Context context) { }
1577                         public void wrong(int i, @Nullable ContentResolver resolver) { }
1578                     }
1579                     """
1580                     ),
1581                     kotlin(
1582                         """
1583                     package android.pkg
1584                     import android.content.Context
1585                     fun String.okCall(context: Context) {}
1586                     fun String.okCall(context: Context, value: Int) {}
1587                     fun String.badCall(value: Int, context: Context) {}
1588                     """
1589                     ),
1590                     androidxNullableSource
1591                 )
1592         )
1593     }
1594 
1595     @Test
Check listener lastnull1596     fun `Check listener last`() {
1597         check(
1598             extraArguments = arrayOf(ARG_API_LINT, ARG_HIDE, "ExecutorRegistration"),
1599             expectedIssues =
1600                 """
1601                 src/android/pkg/MyClass.java:7: warning: Listeners should always be at end of argument list (method `MyClass`) [ListenerLast]
1602                 src/android/pkg/MyClass.java:10: warning: Listeners should always be at end of argument list (method `wrong`) [ListenerLast]
1603                 """,
1604             sourceFiles =
1605                 arrayOf(
1606                     java(
1607                         """
1608                     package android.pkg;
1609                     import android.pkg.MyCallback;
1610                     import android.content.Context;
1611                     import androidx.annotation.Nullable;
1612 
1613                     public class MyClass {
1614                         public MyClass(@Nullable MyCallback listener, int i) {
1615                         }
1616                         public void ok(@Nullable Context context, int i, @Nullable MyCallback listener) { }
1617                         public void wrong(@Nullable MyCallback listener, int i) { }
1618                     }
1619                     """
1620                     ),
1621                     java(
1622                         """
1623                     package android.pkg;
1624 
1625                     @SuppressWarnings("WeakerAccess")
1626                     public abstract class MyCallback {
1627                     }
1628                     """
1629                     ),
1630                     androidxNullableSource
1631                 )
1632         )
1633     }
1634 
1635     @RequiresCapabilities(Capability.KOTLIN)
1636     @Test
Check listener last for suspend functionsnull1637     fun `Check listener last for suspend functions`() {
1638         check(
1639             extraArguments = arrayOf(ARG_API_LINT, ARG_HIDE, "ExecutorRegistration"),
1640             expectedIssues =
1641                 """
1642                 src/android/pkg/MyClass.kt:6: warning: Listeners should always be at end of argument list (method `wrong`) [ListenerLast]
1643             """,
1644             sourceFiles =
1645                 arrayOf(
1646                     java(
1647                         """
1648                     package android.pkg;
1649 
1650                     @SuppressWarnings("WeakerAccess")
1651                     public abstract class MyCallback {
1652                     }
1653                     """
1654                     ),
1655                     kotlin(
1656                         """
1657                     package android.pkg
1658                     import android.pkg.MyCallback
1659 
1660                     class MyClass {
1661                         suspend fun ok(i: Int, callback: MyCallback) {}
1662                         suspend fun wrong(callback: MyCallback, i: Int) {}
1663                     }
1664                     """
1665                     )
1666                 )
1667         )
1668     }
1669 
1670     @Test
Check overloaded argumentsnull1671     fun `Check overloaded arguments`() {
1672         // TODO: This check is not yet hooked up
1673         check(
1674             apiLint = "", // enabled
1675             expectedIssues = """
1676                 """,
1677             sourceFiles =
1678                 arrayOf(
1679                     java(
1680                         """
1681                     package android.pkg;
1682 
1683                     public class MyClass {
1684                         private MyClass() {
1685                         }
1686 
1687                         public void name1() { }
1688                         public void name1(int i) { }
1689                         public void name1(int i, int j) { }
1690                         public void name1(int i, int j, int k) { }
1691                         public void name1(int i, int j, int k, float f) { }
1692 
1693                         public void name2(int i) { }
1694                         public void name2(int i, int j) { }
1695                         public void name2(int i, float j, float k) { }
1696                         public void name2(int i, int j, int k, float f) { }
1697                         public void name2(int i, float f, int j) { }
1698 
1699                         public void name3() { }
1700                         public void name3(int i) { }
1701                         public void name3(int i, int j) { }
1702                         public void name3(int i, float j, int k) { }
1703 
1704                         public void name4(int i, int j, float f, long z) { }
1705                         public void name4(double d, int i, int j, float f) { }
1706                     }
1707                     """
1708                     )
1709                 )
1710         )
1711     }
1712 
1713     @Test
Check Callback Handlersnull1714     fun `Check Callback Handlers`() {
1715         check(
1716             apiLint = "", // enabled
1717             expectedIssues =
1718                 """
1719                 src/android/pkg/MyClass.java:6: warning: Registration methods should have overload that accepts delivery Executor: `MyClass` [ExecutorRegistration]
1720                 src/android/pkg/MyClass.java:16: warning: Registration methods should have overload that accepts delivery Executor: `registerWrongCallback` [ExecutorRegistration]
1721                 """,
1722             sourceFiles =
1723                 arrayOf(
1724                     java(
1725                         """
1726                     package android.pkg;
1727 
1728                     import androidx.annotation.Nullable;
1729 
1730                     public class MyClass {
1731                         public MyClass(@Nullable MyCallback callback) {
1732                         }
1733 
1734                         public void registerStreamEventCallback(@Nullable MyCallback callback);
1735                         public void unregisterStreamEventCallback(@Nullable MyCallback callback);
1736                         public void registerStreamEventCallback(@Nullable java.util.concurrent.Executor executor,
1737                                 @Nullable MyCallback callback);
1738                         public void unregisterStreamEventCallback(@Nullable java.util.concurrent.Executor executor,
1739                                 @Nullable MyCallback callback);
1740 
1741                         public void registerWrongCallback(@Nullable MyCallback callback);
1742                         public void unregisterWrongCallback(@Nullable MyCallback callback);
1743                     }
1744                     """
1745                     ),
1746                     java(
1747                         """
1748                     package android.graphics;
1749 
1750                     import android.pkg.MyCallback;
1751                     import androidx.annotation.Nullable;
1752 
1753                     public class MyUiClass {
1754                         public void registerWrongCallback(@Nullable MyCallback callback);
1755                         public void unregisterWrongCallback(@Nullable MyCallback callback);
1756                     }
1757                     """
1758                     ),
1759                     java(
1760                         """
1761                     package android.pkg;
1762 
1763                     @SuppressWarnings("WeakerAccess")
1764                     public abstract class MyCallback {
1765                     }
1766                     """
1767                     ),
1768                     androidxNullableSource
1769                 )
1770         )
1771     }
1772 
1773     @Test
Check resource namesnull1774     fun `Check resource names`() {
1775         check(
1776             apiLint = "", // enabled
1777             expectedIssues =
1778                 """
1779                 src/android/R.java:11: error: Expected resource name in `android.R.id` to be in the `fooBarBaz` style, was `wrong_style` [ResourceValueFieldName]
1780                 src/android/R.java:17: error: Expected config name to be in the `config_fooBarBaz` style, was `config_wrong_config_style` [ConfigFieldName]
1781                 src/android/R.java:20: error: Expected resource name in `android.R.layout` to be in the `foo_bar_baz` style, was `wrongNameStyle` [ResourceFieldName]
1782                 src/android/R.java:31: error: Expected resource name in `android.R.style` to be in the `FooBar_Baz` style, was `wrong_style_name` [ResourceStyleFieldName]
1783                 """,
1784             expectedFail = DefaultLintErrorMessage,
1785             sourceFiles =
1786                 arrayOf(
1787                     java(
1788                         """
1789                     package android;
1790 
1791                     public final class R {
1792                         public static final class id {
1793                             public static final int text = 7000;
1794                             public static final int config_fooBar = 7001;
1795                             public static final int layout_fooBar = 7002;
1796                             public static final int state_foo = 7003;
1797                             public static final int foo = 7004;
1798                             public static final int fooBar = 7005;
1799                             public static final int wrong_style = 7006;
1800                         }
1801                         public static final class layout {
1802                             public static final int text = 7000;
1803                             public static final int config_fooBar = 7001;
1804                             public static final int config_foo = 7002;
1805                             public static final int config_wrong_config_style = 7003;
1806 
1807                             public static final int ok_name_style = 7004;
1808                             public static final int wrongNameStyle = 7005;
1809                         }
1810                         public static final class style {
1811                             public static final int TextAppearance_Compat_Notification = 0x7f0c00ec;
1812                             public static final int TextAppearance_Compat_Notification_Info = 0x7f0c00ed;
1813                             public static final int TextAppearance_Compat_Notification_Line2 = 0x7f0c00ef;
1814                             public static final int TextAppearance_Compat_Notification_Time = 0x7f0c00f2;
1815                             public static final int TextAppearance_Compat_Notification_Title = 0x7f0c00f4;
1816                             public static final int Widget_Compat_NotificationActionContainer = 0x7f0c015d;
1817                             public static final int Widget_Compat_NotificationActionText = 0x7f0c015e;
1818 
1819                             public static final int wrong_style_name = 7000;
1820                         }
1821                     }
1822                     """
1823                     )
1824                 )
1825         )
1826     }
1827 
1828     @Test
Check filesnull1829     fun `Check files`() {
1830         check(
1831             apiLint = "", // enabled
1832             expectedIssues =
1833                 """
1834                 src/android/pkg/CheckFiles.java:9: warning: Methods accepting `File` should also accept `FileDescriptor` or streams: constructor android.pkg.CheckFiles(android.content.Context,java.io.File) [StreamFiles]
1835                 src/android/pkg/CheckFiles.java:13: warning: Methods accepting `File` should also accept `FileDescriptor` or streams: method android.pkg.CheckFiles.error(int,java.io.File) [StreamFiles]
1836                 """,
1837             sourceFiles =
1838                 arrayOf(
1839                     java(
1840                         """
1841                     package android.pkg;
1842                     import android.content.Context;
1843                     import android.content.ContentResolver;
1844                     import androidx.annotation.Nullable;
1845                     import java.io.File;
1846                     import java.io.InputStream;
1847 
1848                     public class CheckFiles {
1849                         public CheckFiles(@Nullable Context context, @Nullable File file) {
1850                         }
1851                         public void ok(int i, @Nullable File file) { }
1852                         public void ok(int i, @Nullable InputStream stream) { }
1853                         public void error(int i, @Nullable File file) { }
1854                     }
1855                     """
1856                     ),
1857                     androidxNullableSource
1858                 )
1859         )
1860     }
1861 
1862     @Test
Check parcelable listsnull1863     fun `Check parcelable lists`() {
1864         check(
1865             apiLint = "", // enabled
1866             expectedIssues =
1867                 """
1868                 src/android/pkg/CheckFiles.java:9: warning: Methods accepting `File` should also accept `FileDescriptor` or streams: constructor android.pkg.CheckFiles(android.content.Context,java.io.File) [StreamFiles]
1869                 src/android/pkg/CheckFiles.java:13: warning: Methods accepting `File` should also accept `FileDescriptor` or streams: method android.pkg.CheckFiles.error(int,java.io.File) [StreamFiles]
1870                 """,
1871             sourceFiles =
1872                 arrayOf(
1873                     java(
1874                         """
1875                     package android.pkg;
1876                     import android.content.Context;
1877                     import android.content.ContentResolver;
1878                     import androidx.annotation.Nullable;
1879                     import java.io.File;
1880                     import java.io.InputStream;
1881 
1882                     public class CheckFiles {
1883                         public CheckFiles(@Nullable Context context, @Nullable File file) {
1884                         }
1885                         public void ok(int i, @Nullable File file) { }
1886                         public void ok(int i, @Nullable InputStream stream) { }
1887                         public void error(int i, @Nullable File file) { }
1888                     }
1889                     """
1890                     ),
1891                     androidxNullableSource
1892                 )
1893         )
1894     }
1895 
1896     @Test
Check abstract innernull1897     fun `Check abstract inner`() {
1898         check(
1899             apiLint = "", // enabled
1900             expectedIssues =
1901                 """
1902                 src/android/pkg/MyManager.java:9: warning: Abstract inner classes should be static to improve testability: class android.pkg.MyManager.MyInnerManager [AbstractInner]
1903                 """,
1904             sourceFiles =
1905                 arrayOf(
1906                     java(
1907                         """
1908                     package android.pkg;
1909                     import android.content.Context;
1910                     import android.content.ContentResolver;
1911                     import java.io.File;
1912                     import java.io.InputStream;
1913 
1914                     public abstract class MyManager {
1915                          private MyManager() {}
1916                          public abstract class MyInnerManager {
1917                              private MyInnerManager() {}
1918                          }
1919                          public abstract static class MyOkManager {
1920                              private MyOkManager() {}
1921                          }
1922                     }
1923                     """
1924                     )
1925                 )
1926         )
1927     }
1928 
1929     @Test
Check for extending errorsnull1930     fun `Check for extending errors`() {
1931         check(
1932             apiLint = "", // enabled
1933             expectedIssues =
1934                 """
1935                 src/android/pkg/MyClass.java:3: error: Trouble must be reported through an `Exception`, not an `Error` (`MyClass` extends `Error`) [ExtendsError]
1936                 src/android/pkg/MySomething.java:3: error: Exceptions must be named `FooException`, was `MySomething` [ExceptionName]
1937                 """,
1938             expectedFail = DefaultLintErrorMessage,
1939             sourceFiles =
1940                 arrayOf(
1941                     java(
1942                         """
1943                     package android.pkg;
1944 
1945                     public class MyClass extends Error {
1946                     }
1947                     """
1948                     ),
1949                     java(
1950                         """
1951                     package android.pkg;
1952 
1953                     public class MySomething extends RuntimeException {
1954                     }
1955                     """
1956                     )
1957                 )
1958         )
1959     }
1960 
1961     @Test
Check units and method namesnull1962     fun `Check units and method names`() {
1963         check(
1964             extraArguments = arrayOf(ARG_API_LINT, ARG_HIDE, "NoByteOrShort"),
1965             expectedIssues =
1966                 """
1967                 src/android/pkg/UnitNameTest.java:7: error: Expected method name units to be `Hours`, was `Hr` in `getErrorHr` [MethodNameUnits]
1968                 src/android/pkg/UnitNameTest.java:8: error: Expected method name units to be `Nanos`, was `Ns` in `getErrorNs` [MethodNameUnits]
1969                 src/android/pkg/UnitNameTest.java:9: error: Expected method name units to be `Bytes`, was `Byte` in `getErrorByte` [MethodNameUnits]
1970                 src/android/pkg/UnitNameTest.java:14: error: Fractions must use floats, was `int` in `getErrorFraction` [FractionFloat]
1971                 src/android/pkg/UnitNameTest.java:15: error: Fractions must use floats, was `int` in `setErrorFraction` [FractionFloat]
1972                 src/android/pkg/UnitNameTest.java:19: error: Percentage must use ints, was `float` in `getErrorPercentage` [PercentageInt]
1973                 src/android/pkg/UnitNameTest.java:20: error: Percentage must use ints, was `float` in `setErrorPercentage` [PercentageInt]
1974                 src/android/pkg/UnitNameTest.java:22: error: Expected method name units to be `Bytes`, was `Byte` in `readSingleByte` [MethodNameUnits]
1975                 """,
1976             expectedFail = DefaultLintErrorMessage,
1977             sourceFiles =
1978                 arrayOf(
1979                     androidxNonNullSource,
1980                     java(
1981                         """
1982                     package android.pkg;
1983 
1984                     import androidx.annotation.NonNull;
1985 
1986                     public class UnitNameTest {
1987                         public int okay() { return 0; }
1988                         public int getErrorHr() { return 0; }
1989                         public int getErrorNs() { return 0; }
1990                         public short getErrorByte() { return (short)0; }
1991 
1992                         public float getOkFraction() { return 0f; }
1993                         public void setOkFraction(float f) { }
1994                         public void setOkFraction(int n, int d) { }
1995                         public int getErrorFraction() { return 0; }
1996                         public void setErrorFraction(int i) { }
1997 
1998                         public int getOkPercentage() { return 0f; }
1999                         public void setOkPercentage(int i) { }
2000                         public float getErrorPercentage() { return 0f; }
2001                         public void setErrorPercentage(float f) { }
2002 
2003                         public int readSingleByte() { return 0; }
2004 
2005                         public static final class UnitNameTestBuilder {
2006                             public UnitNameTestBuilder() {}
2007 
2008                             @NonNull
2009                             public UnitNameTest build() { return null; }
2010 
2011                             @NonNull
2012                             public UnitNameTestBuilder setOkFraction(float f) { return this; }
2013 
2014                             @NonNull
2015                             public UnitNameTestBuilder setOkPercentage(int i) { return this; }
2016                         }
2017 
2018                         @NamedDataSpace
2019                         public int getOkDataSpace() { return 0; }
2020 
2021                         /** @hide */
2022                         @android.annotation.IntDef
2023                         public @interface NamedDataSpace {}
2024                     }
2025                     """
2026                     )
2027                 )
2028         )
2029     }
2030 
2031     @Test
Check closeablenull2032     fun `Check closeable`() {
2033         check(
2034             apiLint = "", // enabled
2035             expectedIssues =
2036                 """
2037                 src/android/pkg/MyErrorClass1.java:3: warning: Classes that release resources (close()) should implement AutoCloseable and CloseGuard: class android.pkg.MyErrorClass1 [NotCloseable]
2038                 src/android/pkg/MyErrorClass2.java:3: warning: Classes that release resources (finalize(), shutdown()) should implement AutoCloseable and CloseGuard: class android.pkg.MyErrorClass2 [NotCloseable]
2039                 """,
2040             sourceFiles =
2041                 arrayOf(
2042                     java(
2043                         """
2044                     package android.pkg;
2045 
2046                     public abstract class MyOkClass1 implements java.io.Closeable {
2047                         public void close() {}
2048                     }
2049                     """
2050                     ),
2051                     java(
2052                         """
2053                     package android.pkg;
2054 
2055                     // Ok: indirectly implementing AutoCloseable
2056                     public abstract class MyOkClass2 implements MyInterface {
2057                         public void close() {}
2058                     }
2059                     """
2060                     ),
2061                     java(
2062                         """
2063                     package android.pkg;
2064 
2065                     public class MyInterface extends AutoCloseable {
2066                         public void close() {}
2067                     }
2068                     """
2069                     ),
2070                     java(
2071                         """
2072                     package android.pkg;
2073 
2074                     public abstract class MyErrorClass1 {
2075                         public void close() {}
2076                     }
2077                     """
2078                     ),
2079                     java(
2080                         """
2081                     package android.pkg;
2082 
2083                     public abstract class MyErrorClass2 {
2084                         public void finalize() {}
2085                         public void shutdown() {}
2086                     }
2087                     """
2088                     )
2089                 )
2090         )
2091     }
2092 
2093     @Test
Check closeable for minSdkVersion 19null2094     fun `Check closeable for minSdkVersion 19`() {
2095         check(
2096             apiLint = "", // enabled
2097             expectedIssues =
2098                 """
2099                 src/android/pkg/MyErrorClass1.java:3: warning: Classes that release resources (close()) should implement AutoCloseable and CloseGuard: class android.pkg.MyErrorClass1 [NotCloseable]
2100                 src/android/pkg/MyErrorClass2.java:3: warning: Classes that release resources (finalize(), shutdown()) should implement AutoCloseable and CloseGuard: class android.pkg.MyErrorClass2 [NotCloseable]
2101             """,
2102             manifest =
2103                 """<?xml version="1.0" encoding="UTF-8"?>
2104                 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2105                     <uses-sdk android:minSdkVersion="19" />
2106                 </manifest>
2107             """
2108                     .trimIndent(),
2109             sourceFiles =
2110                 arrayOf(
2111                     java(
2112                         """
2113                     package android.pkg;
2114 
2115                     public abstract class MyErrorClass1 {
2116                         public void close() {}
2117                     }
2118                     """
2119                     ),
2120                     java(
2121                         """
2122                     package android.pkg;
2123 
2124                     public abstract class MyErrorClass2 {
2125                         public void finalize() {}
2126                         public void shutdown() {}
2127                     }
2128                     """
2129                     )
2130                 )
2131         )
2132     }
2133 
2134     @Test
Do not check closeable for minSdkVersion less than 19null2135     fun `Do not check closeable for minSdkVersion less than 19`() {
2136         check(
2137             apiLint = "", // enabled
2138             expectedIssues = "",
2139             manifest =
2140                 """<?xml version="1.0" encoding="UTF-8"?>
2141                 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2142                     <uses-sdk android:minSdkVersion="18" />
2143                 </manifest>
2144             """
2145                     .trimIndent(),
2146             sourceFiles =
2147                 arrayOf(
2148                     java(
2149                         """
2150                     package android.pkg;
2151 
2152                     public abstract class MyErrorClass1 {
2153                         public void close() {}
2154                     }
2155                     """
2156                     ),
2157                     java(
2158                         """
2159                     package android.pkg;
2160 
2161                     public abstract class MyErrorClass2 {
2162                         public void finalize() {}
2163                         public void shutdown() {}
2164                     }
2165                     """
2166                     )
2167                 )
2168         )
2169     }
2170 
2171     @Test
Check ICU types for minSdkVersion 24null2172     fun `Check ICU types for minSdkVersion 24`() {
2173         check(
2174             apiLint = "", // enabled
2175             expectedIssues =
2176                 """
2177                 src/android/pkg/MyErrorClass1.java:8: warning: Type `java.text.NumberFormat` should be replaced with richer ICU type `android.icu.text.NumberFormat` [UseIcu]
2178             """,
2179             manifest =
2180                 """<?xml version="1.0" encoding="UTF-8"?>
2181                 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2182                     <uses-sdk android:minSdkVersion="24" />
2183                 </manifest>
2184             """
2185                     .trimIndent(),
2186             sourceFiles =
2187                 arrayOf(
2188                     java(
2189                         """
2190                     package android.pkg;
2191 
2192                     import android.annotation.NonNull;
2193                     import java.text.NumberFormat;
2194 
2195                     public abstract class MyErrorClass1 {
2196                         @NonNull
2197                         public NumberFormat getDefaultNumberFormat() {
2198                            return NumberFormat.getInstance();
2199                         }
2200                     }
2201                     """
2202                     ),
2203                     nonNullSource
2204                 )
2205         )
2206     }
2207 
2208     @Test
Do not check ICU types for minSdkVersion less than 24null2209     fun `Do not check ICU types for minSdkVersion less than 24`() {
2210         check(
2211             apiLint = "", // enabled
2212             expectedIssues = "",
2213             manifest =
2214                 """<?xml version="1.0" encoding="UTF-8"?>
2215                 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2216                     <uses-sdk android:minSdkVersion="23" />
2217                 </manifest>
2218             """
2219                     .trimIndent(),
2220             sourceFiles =
2221                 arrayOf(
2222                     java(
2223                         """
2224                     package android.pkg;
2225 
2226                     import android.annotation.NonNull;
2227                     import java.text.NumberFormat;
2228 
2229                     public abstract class MyErrorClass1 {
2230                         @NonNull
2231                         public NumberFormat getDefaultNumberFormat() {
2232                             return NumberFormat.getInstance();
2233                         }
2234                     }
2235                     """
2236                     ),
2237                     nonNullSource
2238                 )
2239         )
2240     }
2241 
2242     @Test
Check Kotlin keywordsnull2243     fun `Check Kotlin keywords`() {
2244         check(
2245             apiLint = "", // enabled
2246             expectedIssues =
2247                 """
2248                 src/android/pkg/KotlinKeywordTest.java:7: error: Avoid method names that are Kotlin hard keywords ("fun"); see https://android.github.io/kotlin-guides/interop.html#no-hard-keywords [KotlinKeyword]
2249                 src/android/pkg/KotlinKeywordTest.java:8: error: Avoid field names that are Kotlin hard keywords ("as"); see https://android.github.io/kotlin-guides/interop.html#no-hard-keywords [KotlinKeyword]
2250                 """,
2251             expectedFail = DefaultLintErrorMessage,
2252             sourceFiles =
2253                 arrayOf(
2254                     java(
2255                         """
2256                     package android.pkg;
2257 
2258                     public class KotlinKeywordTest {
2259                         public void okay();
2260                         public final int okay = 0;
2261 
2262                         public void fun() {} // error
2263                         public final int as = 0; // error
2264                     }
2265                     """
2266                     ),
2267                     androidxNonNullSource,
2268                     androidxNullableSource
2269                 )
2270         )
2271     }
2272 
2273     @RequiresCapabilities(Capability.KOTLIN)
2274     @Test
Return collections instead of arraysnull2275     fun `Return collections instead of arrays`() {
2276         check(
2277             extraArguments = arrayOf(ARG_API_LINT, ARG_HIDE, "AutoBoxing"),
2278             expectedIssues =
2279                 """
2280                 src/android/pkg/ArrayTest.java:13: warning: Method should return Collection<Object> (or subclass) instead of raw array; was `java.lang.Object[]` [ArrayReturn]
2281                 src/android/pkg/ArrayTest.java:14: warning: Method parameter should be Collection<Number> (or subclass) instead of raw array; was `java.lang.Number[]` [ArrayReturn]
2282                 """,
2283             sourceFiles =
2284                 arrayOf(
2285                     java(
2286                         """
2287                     package android.pkg;
2288 
2289                     import androidx.annotation.NonNull;
2290                     import androidx.annotation.Nullable;
2291 
2292                     public class ArrayTest {
2293                         @NonNull
2294                         public int[] ok1() { throw new RuntimeException(); }
2295                         @NonNull
2296                         public String[] ok2() { throw new RuntimeException(); }
2297                         public void ok3(@Nullable int[] i) { }
2298                         @NonNull
2299                         public Object[] error1() { throw new RuntimeException(); }
2300                         public void error2(@NonNull Number[] i) { }
2301                         public void ok(@NonNull Number... args) { }
2302                     }
2303                     """
2304                     ),
2305                     kotlin(
2306                         """
2307                     package test.pkg
2308                     fun okMethod(vararg values: Integer, foo: Float, bar: Float)
2309                     """
2310                     ),
2311                     androidxNonNullSource,
2312                     androidxNullableSource,
2313                 )
2314         )
2315     }
2316 
2317     @Test
Check user handle namesnull2318     fun `Check user handle names`() {
2319         check(
2320             apiLint = "", // enabled
2321             expectedIssues =
2322                 """
2323                 src/android/pkg/MyManager.java:7: warning: When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added [UserHandle]
2324                 src/android/pkg/UserHandleTest.java:8: warning: Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `error` [UserHandleName]
2325                 """,
2326             sourceFiles =
2327                 arrayOf(
2328                     java(
2329                         """
2330                     package android.pkg;
2331                     import android.os.UserHandle;
2332                     import androidx.annotation.Nullable;
2333 
2334                     public class UserHandleTest {
2335                         public void doFooAsUser(int i, @Nullable UserHandle handle) {} //OK
2336                         public void doFooForUser(int i, @Nullable UserHandle handle) {} //OK
2337                         public void error(int i, @Nullable UserHandle handle) {}
2338                     }
2339                     """
2340                     ),
2341                     java(
2342                         """
2343                     package android.pkg;
2344                     import android.os.UserHandle;
2345                     import androidx.annotation.Nullable;
2346 
2347                     public class MyManager {
2348                         private MyManager() { }
2349                         public void error(int i, @Nullable UserHandle handle) {}
2350                     }
2351                     """
2352                     ),
2353                     androidxNullableSource
2354                 )
2355         )
2356     }
2357 
2358     @Test
Check parametersnull2359     fun `Check parameters`() {
2360         check(
2361             apiLint = "", // enabled
2362             expectedIssues =
2363                 """
2364                 src/android/pkg/FooOptions.java:3: warning: Classes holding a set of parameters should be called `FooParams`, was `FooOptions` [UserHandleName]
2365                 """,
2366             sourceFiles =
2367                 arrayOf(
2368                     java(
2369                         """
2370                     package android.pkg;
2371 
2372                     public class FooOptions {
2373                     }
2374                     """
2375                     ),
2376                     java(
2377                         """
2378                     package android.app;
2379 
2380                     public class ActivityOptions {
2381                     }
2382                     """
2383                     )
2384                 )
2385         )
2386     }
2387 
2388     @Test
Check service namesnull2389     fun `Check service names`() {
2390         check(
2391             apiLint = "", // enabled
2392             expectedIssues =
2393                 """
2394                 src/android/content/Context.java:10: error: Inconsistent service value; expected `other`, was `something` (Note: Do not change the name of already released services, which will break tools using `adb shell dumpsys`. Instead add `@SuppressLint("ServiceName"))` [ServiceName]
2395                 src/android/content/Context.java:11: error: Inconsistent service constant name; expected `SOMETHING_SERVICE`, was `OTHER_MANAGER` [ServiceName]
2396                 src/android/content/Context.java:12: error: Inconsistent service constant name; expected `OTHER_SERVICE`, was `OTHER_MANAGER_SERVICE` [ServiceName]
2397                 """,
2398             expectedFail = DefaultLintErrorMessage,
2399             sourceFiles =
2400                 arrayOf(
2401                     java(
2402                         """
2403                     package android.content;
2404 
2405                     public class Context {
2406                         private Context() { }
2407                         // Good
2408                         public static final String FOO_BAR_SERVICE = "foo_bar";
2409                         // Unrelated
2410                         public static final int NON_STRING_SERVICE = 42;
2411                         // Bad
2412                         public static final String OTHER_SERVICE = "something";
2413                         public static final String OTHER_MANAGER = "something";
2414                         public static final String OTHER_MANAGER_SERVICE = "other_manager";
2415                     }
2416                     """
2417                     ),
2418                     java(
2419                         """
2420                     package android.pkg;
2421 
2422                     // Unrelated
2423                     public class ServiceNameTest {
2424                         private ServiceNameTest() { }
2425                         public static final String FOO_BAR_SERVICE = "foo_bar";
2426                         public static final String OTHER_SERVICE = "something";
2427                         public static final int NON_STRING_SERVICE = 42;
2428                         public static final String BIND_SOME_SERVICE = "android.permission.BIND_SOME_SERVICE";
2429                     }
2430                     """
2431                     )
2432                 )
2433         )
2434     }
2435 
2436     @Test
Check method name tensenull2437     fun `Check method name tense`() {
2438         check(
2439             apiLint = "", // enabled
2440             expectedIssues =
2441                 """
2442                 src/android/pkg/MethodNameTest.java:6: warning: Unexpected tense; probably meant `enabled`, was `fooEnable` [MethodNameTense]
2443                 src/android/pkg/MethodNameTest.java:7: warning: Unexpected tense; probably meant `enabled`, was `mustEnable` [MethodNameTense]
2444                 """,
2445             sourceFiles =
2446                 arrayOf(
2447                     java(
2448                         """
2449                     package android.pkg;
2450 
2451                     public class MethodNameTest {
2452                         private MethodNameTest() { }
2453                         public void enable() { } // ok, not Enable
2454                         public void fooEnable() { } // warn
2455                         public boolean mustEnable() { return true; } // warn
2456                         public boolean isEnabled() { return true; }
2457                     }
2458                     """
2459                     )
2460                 )
2461         )
2462     }
2463 
2464     @Test
Check no clonenull2465     fun `Check no clone`() {
2466         check(
2467             apiLint = "", // enabled
2468             expectedIssues =
2469                 """
2470                 src/android/pkg/CloneTest.java:8: error: Provide an explicit copy constructor instead of implementing `clone()` [NoClone]
2471                 """,
2472             expectedFail = DefaultLintErrorMessage,
2473             sourceFiles =
2474                 arrayOf(
2475                     java(
2476                         """
2477                     package android.pkg;
2478 
2479                     import androidx.annotation.NonNull;
2480 
2481                     public class CloneTest {
2482                         public void clone(int i) { } // ok
2483                         @NonNull
2484                         public CloneTest clone() { return super.clone(); } // error
2485                     }
2486                     """
2487                     ),
2488                     androidxNonNullSource
2489                 )
2490         )
2491     }
2492 
2493     @Test
Check ICU typesnull2494     fun `Check ICU types`() {
2495         check(
2496             apiLint = "", // enabled
2497             expectedIssues =
2498                 """
2499                 src/android/pkg/IcuTest.java:6: warning: Type `java.text.NumberFormat` should be replaced with richer ICU type `android.icu.text.NumberFormat` [UseIcu]
2500                 src/android/pkg/IcuTest.java:8: warning: Type `java.text.BreakIterator` should be replaced with richer ICU type `android.icu.text.BreakIterator` [UseIcu]
2501                 src/android/pkg/IcuTest.java:8: warning: Type `java.text.Collator` should be replaced with richer ICU type `android.icu.text.Collator` [UseIcu]
2502                 """,
2503             sourceFiles =
2504                 arrayOf(
2505                     java(
2506                         """
2507                     package android.pkg;
2508 
2509                     import androidx.annotation.Nullable;
2510 
2511                     public abstract class IcuTest {
2512                         public IcuTest(@Nullable java.text.NumberFormat nf) { }
2513                         @Nullable
2514                         public abstract java.text.BreakIterator foo(@Nullable java.text.Collator collator);
2515                     }
2516                     """
2517                     ),
2518                     androidxNullableSource
2519                 )
2520         )
2521     }
2522 
2523     @Test
Check using parcel file descriptorsnull2524     fun `Check using parcel file descriptors`() {
2525         check(
2526             apiLint = "", // enabled
2527             expectedIssues =
2528                 """
2529                 src/android/pkg/PdfTest.java:6: error: Must use ParcelFileDescriptor instead of FileDescriptor in parameter fd in android.pkg.PdfTest.error1(java.io.FileDescriptor fd) [UseParcelFileDescriptor]
2530                 src/android/pkg/PdfTest.java:7: error: Must use ParcelFileDescriptor instead of FileDescriptor in method android.pkg.PdfTest.getFileDescriptor() [UseParcelFileDescriptor]
2531                 """,
2532             expectedFail = DefaultLintErrorMessage,
2533             sourceFiles =
2534                 arrayOf(
2535                     java(
2536                         """
2537                     package android.pkg;
2538 
2539                     import androidx.annotation.Nullable;
2540 
2541                     public abstract class PdfTest {
2542                         public void error1(@Nullable java.io.FileDescriptor fd) { }
2543                         public int getFileDescriptor() { return -1; }
2544                         public void ok(@Nullable android.os.ParcelFileDescriptor fd) { }
2545                     }
2546                     """
2547                     ),
2548                     java(
2549                         """
2550                     package android.system;
2551 
2552                     import androidx.annotation.Nullable;
2553 
2554                     public class Os {
2555                         public void ok(@Nullable java.io.FileDescriptor fd) { }
2556                         public int getFileDescriptor() { return -1; }
2557                     }
2558                     """
2559                     ),
2560                     java(
2561                         """
2562                     package android.yada;
2563 
2564                     import android.annotation.Nullable;
2565 
2566                     public class YadaService extends android.app.Service {
2567                         @Override
2568                         public final void dump(@Nullable java.io.FileDescriptor fd, @Nullable java.io.PrintWriter pw, @Nullable String[] args) {
2569                         }
2570                     }
2571                     """
2572                     ),
2573                     androidxNullableSource,
2574                     nonNullSource
2575                 )
2576         )
2577     }
2578 
2579     @Test
Check using bytes and shortsnull2580     fun `Check using bytes and shorts`() {
2581         check(
2582             apiLint = "", // enabled
2583             expectedIssues =
2584                 """
2585                 src/android/pkg/ByteTest.java:4: warning: Should avoid odd sized primitives; use `int` instead of `byte` in parameter b in android.pkg.ByteTest.error1(byte b) [NoByteOrShort]
2586                 src/android/pkg/ByteTest.java:5: warning: Should avoid odd sized primitives; use `int` instead of `short` in parameter s in android.pkg.ByteTest.error2(short s) [NoByteOrShort]
2587                 """,
2588             sourceFiles =
2589                 arrayOf(
2590                     java(
2591                         """
2592                     package android.pkg;
2593 
2594                     public abstract class ByteTest {
2595                         public void error1(byte b) { }
2596                         public void error2(short s) { }
2597                     }
2598                     """
2599                     )
2600                 )
2601         )
2602     }
2603 
2604     @Test
Check singleton constructorsnull2605     fun `Check singleton constructors`() {
2606         check(
2607             apiLint = "", // enabled
2608             expectedIssues =
2609                 """
2610                 src/android/pkg/MySingleton.java:8: error: Singleton classes should use `getInstance()` methods: `MySingleton` [SingletonConstructor]
2611                 """,
2612             expectedFail = DefaultLintErrorMessage,
2613             sourceFiles =
2614                 arrayOf(
2615                     java(
2616                         """
2617                     package android.pkg;
2618 
2619                     import androidx.annotation.Nullable;
2620 
2621                     public abstract class MySingleton {
2622                         @Nullable
2623                         public static MySingleton getMyInstance() { return null; }
2624                         public MySingleton() { }
2625                         public void foo() { }
2626                     }
2627                     """
2628                     ),
2629                     java(
2630                         """
2631                     package android.pkg;
2632 
2633                     import androidx.annotation.Nullable;
2634 
2635                     public abstract class MySingleton2 {
2636                         @Nullable
2637                         public static MySingleton2 getMyInstance() { return null; }
2638                         private MySingleton2() { } // OK, private
2639                         public void foo() { }
2640                     }
2641                     """
2642                     ),
2643                     androidxNullableSource
2644                 )
2645         )
2646     }
2647 
2648     @Test
Check forbidden super-classesnull2649     fun `Check forbidden super-classes`() {
2650         check(
2651             apiLint = "", // enabled
2652             expectedIssues =
2653                 """
2654                 src/android/pkg/FirstActivity.java:2: error: FirstActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead. [ForbiddenSuperClass]
2655                 src/android/pkg/IndirectActivity.java:2: error: IndirectActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead. [ForbiddenSuperClass]
2656                 src/android/pkg/MyTask.java:2: error: MyTask should not extend `AsyncTask`. AsyncTask is an implementation detail. Expose a listener or, in androidx, a `ListenableFuture` API instead [ForbiddenSuperClass]
2657                 """,
2658             expectedFail = DefaultLintErrorMessage,
2659             sourceFiles =
2660                 arrayOf(
2661                     java(
2662                         """
2663                     package android.pkg;
2664                     public abstract class FirstActivity extends android.app.Activity {
2665                         private FirstActivity() { }
2666                     }
2667                     """
2668                     ),
2669                     java(
2670                         """
2671                     package android.pkg;
2672                     public abstract class IndirectActivity extends android.app.ListActivity {
2673                         private IndirectActivity() { }
2674                     }
2675                     """
2676                     ),
2677                     java(
2678                         """
2679                     package android.pkg;
2680                     public abstract class MyTask extends android.os.AsyncTask<String,String,String> {
2681                         private MyTask() { }
2682                     }
2683                     """
2684                     )
2685                 )
2686         )
2687     }
2688 
2689     @Test
No new setting keysnull2690     fun `No new setting keys`() {
2691         check(
2692             apiLint = "", // enabled
2693             extraArguments = arrayOf(ARG_ERROR, "NoSettingsProvider"),
2694             expectedIssues =
2695                 """
2696                 src/android/provider/Settings.java:9: error: New setting keys are not allowed (Field: BAD1); use getters/setters in relevant manager class [NoSettingsProvider]
2697                 src/android/provider/Settings.java:12: error: Bare field okay2 must be marked final, or moved behind accessors if mutable [MutableBareField]
2698                 src/android/provider/Settings.java:17: error: New setting keys are not allowed (Field: BAD1); use getters/setters in relevant manager class [NoSettingsProvider]
2699                 src/android/provider/Settings.java:21: error: New setting keys are not allowed (Field: BAD1); use getters/setters in relevant manager class [NoSettingsProvider]
2700             """,
2701             expectedFail = DefaultLintErrorMessage,
2702             sourceFiles =
2703                 arrayOf(
2704                     java(
2705                         """
2706                     package android.provider;
2707 
2708                     import androidx.annotation.Nullable;
2709 
2710                     public class Settings {
2711                         private Settings() { }
2712                         public static class Global {
2713                             private Global() { }
2714                             public static final String BAD1 = "";
2715                             public final String okay1 = "";
2716                             @Nullable
2717                             public static String okay2 = "";
2718                             public static final int OKAY3 = 0;
2719                         }
2720                         public static class Secure {
2721                             private Secure() { }
2722                             public static final String BAD1 = "";
2723                         }
2724                         public static class System {
2725                             private System() { }
2726                             public static final String BAD1 = "";
2727                         }
2728                         public static class Other {
2729                             private Other() { }
2730                             public static final String OKAY1 = "";
2731                         }
2732                     }
2733                     """
2734                     ),
2735                     androidxNullableSource
2736                 )
2737         )
2738     }
2739 
2740     @RequiresCapabilities(Capability.KOTLIN)
2741     @Test
vararg use in annotationsnull2742     fun `vararg use in annotations`() {
2743         check(
2744             apiLint = "", // enabled
2745             expectedIssues = "",
2746             sourceFiles =
2747                 arrayOf(
2748                     kotlin(
2749                         """
2750                         package test.pkg
2751 
2752                         import kotlin.reflect.KClass
2753 
2754                         annotation class MyAnnotation(
2755                             vararg val markerClass: KClass<out Annotation>
2756                         )
2757                     """
2758                     )
2759                 )
2760         )
2761     }
2762 
2763     @Test
Inherited interface constantsnull2764     fun `Inherited interface constants`() {
2765         check(
2766             expectedIssues = "",
2767             expectedFail = "",
2768             apiLint =
2769                 """
2770                 package javax.microedition.khronos.egl {
2771                     public interface EGL {
2772                     }
2773                     public interface EGL10 extends javax.microedition.khronos.egl.EGL {
2774                         field public static final int EGL_SUCCESS = 0;
2775                     }
2776                     public interface EGL11 extends javax.microedition.khronos.egl.EGL10 {
2777                         field public static final int EGL_CONTEXT_LOST = 1;
2778                     }
2779                     public interface EGLDisplay {
2780                     }
2781                 }
2782                 """,
2783             sourceFiles =
2784                 arrayOf(
2785                     java(
2786                         """
2787                         package javax.microedition.khronos.egl;
2788 
2789                         public interface EGL {
2790                         }
2791                     """
2792                     ),
2793                     java(
2794                         """
2795                         package javax.microedition.khronos.egl;
2796 
2797                         public interface EGL10 extends EGL {
2798                             int EGL_SUCCESS = 0;
2799                         }
2800                     """
2801                     ),
2802                     java(
2803                         """
2804                         package javax.microedition.khronos.egl;
2805 
2806                         public interface EGL11 extends EGL10 {
2807                             int EGL_CONTEXT_LOST = 1;
2808                         }
2809                     """
2810                     ),
2811                     java(
2812                         """
2813                         package javax.microedition.khronos.egl;
2814 
2815                         public abstract class EGLDisplay {
2816                         }
2817                     """
2818                     )
2819                 )
2820         )
2821     }
2822 
2823     @Test
Inherited interface constants inherited through parents into childrennull2824     fun `Inherited interface constants inherited through parents into children`() {
2825         check(
2826             expectedIssues = "",
2827             expectedFail = "",
2828             apiLint =
2829                 """
2830                 package android.provider {
2831                   public static final class Settings.Global extends android.provider.Settings.NameValueTable {
2832                   }
2833                   public static class Settings.NameValueTable implements android.provider.BaseColumns {
2834                   }
2835                   public interface BaseColumns {
2836                       field public static final String _ID = "_id";
2837                   }
2838                 }
2839                 """,
2840             sourceFiles =
2841                 arrayOf(
2842                     java(
2843                         """
2844                         package android.provider;
2845 
2846                         public class Settings {
2847                             private Settings() { }
2848                             public static final class Global extends NameValueTable {
2849                             }
2850                             public static final class NameValueTable implements BaseColumns {
2851                             }
2852                         }
2853                     """
2854                     ),
2855                     java(
2856                         """
2857                         package android.provider;
2858 
2859                         public interface BaseColumns {
2860                             public static final String _ID = "_id";
2861                         }
2862                     """
2863                     )
2864                 ),
2865             extraArguments = arrayOf("--error", "NoSettingsProvider")
2866         )
2867     }
2868 
2869     @Test
Methods returning ListenableFuture end with asyncnull2870     fun `Methods returning ListenableFuture end with async`() {
2871         check(
2872             apiLint = "", // enabled
2873             expectedIssues =
2874                 """
2875                 src/android/pkg/MyClass.java:7: error: Methods returning com.google.common.util.concurrent.ListenableFuture should have a suffix *Async to reserve unmodified name for a suspend function [AsyncSuffixFuture]
2876             """,
2877             expectedFail = DefaultLintErrorMessage,
2878             sourceFiles =
2879                 arrayOf(
2880                     java(
2881                         """
2882                     package android.pkg;
2883 
2884                     import androidx.annotation.Nullable;
2885                     import com.google.common.util.concurrent.ListenableFuture;
2886 
2887                     public final class MyClass {
2888                         public @Nullable ListenableFuture<String> bad() { return null; }
2889                         public @Nullable ListenableFuture<String> goodAsync() { return null; }
2890                     }
2891                     """
2892                     ),
2893                     java(
2894                         """
2895                     package com.google.common.util.concurrent;
2896                     public class ListenableFuture<T> {
2897                     }
2898                     """
2899                     ),
2900                     androidxNullableSource
2901                 )
2902         )
2903     }
2904 
2905     @Test
Listener replaceable with OutcomeReceiver or ListenableFuturenull2906     fun `Listener replaceable with OutcomeReceiver or ListenableFuture`() {
2907         check(
2908             apiLint = "", // enabled
2909             expectedIssues =
2910                 """
2911                 src/android/pkg/Cases.java:7: error: Cases.BadCallback can be replaced with OutcomeReceiver<R,E> (platform) or suspend fun / ListenableFuture (AndroidX). [GenericCallbacks]
2912                 src/android/pkg/Cases.java:11: error: Cases.BadListener can be replaced with OutcomeReceiver<R,E> (platform) or suspend fun / ListenableFuture (AndroidX). [GenericCallbacks]
2913                 src/android/pkg/Cases.java:15: error: Cases.BadGenericListener can be replaced with OutcomeReceiver<R,E> (platform) or suspend fun / ListenableFuture (AndroidX). [GenericCallbacks]
2914             """,
2915             expectedFail = DefaultLintErrorMessage,
2916             sourceFiles =
2917                 arrayOf(
2918                     java(
2919                         """
2920                     package android.pkg;
2921 
2922                     import androidx.annotation.NonNull;
2923                     import java.io.IOException;
2924 
2925                     public final class Cases {
2926                         public class BadCallback {
2927                             public abstract void onSuccess(@NonNull String result);
2928                             public void onFailure(@NonNull Throwable error) {}
2929                         }
2930                         public interface BadListener {
2931                             void onResult(@NonNull Object result);
2932                             void onError(@NonNull IOException error);
2933                         }
2934                         public interface BadGenericListener<R, E extends Throwable> {
2935                             void onResult(@NonNull R result);
2936                             void onError(@NonNull E error);
2937                         }
2938                         public interface OkListener {
2939                             void onResult(@NonNull Object result);
2940                             void onError(@NonNull int error);
2941                         }
2942                         public interface Ok2Listener {
2943                             void onResult(@NonNull int result);
2944                             void onError(@NonNull Throwable error);
2945                         }
2946                         public interface Ok3Listener {
2947                             void onSuccess(@NonNull String result);
2948                             void onOtherThing(@NonNull String result);
2949                             void onFailure(@NonNull Throwable error);
2950                         }
2951                     }
2952                     """
2953                     ),
2954                     androidxNonNullSource
2955                 )
2956         )
2957     }
2958 
2959     @RequiresCapabilities(Capability.KOTLIN)
2960     @Test
No warning on generic return typenull2961     fun `No warning on generic return type`() {
2962         check(
2963             expectedIssues = "",
2964             apiLint = "",
2965             sourceFiles =
2966                 arrayOf(
2967                     kotlin(
2968                         """
2969                         package test.pkg
2970                         class SimpleArrayMap<K, V> {
2971                             override fun getOrDefault(key: K, defaultValue: V): V {}
2972                         }
2973                     """
2974                     )
2975                 )
2976         )
2977     }
2978 
2979     @Test
No crash when setter start with numbersnull2980     fun `No crash when setter start with numbers`() {
2981         check(
2982             expectedIssues = "",
2983             apiLint = "",
2984             sourceFiles =
2985                 arrayOf(
2986                     java(
2987                         """
2988                         package test.pkg;
2989                         import androidx.annotation.NonNull;
2990                         public class Config {
2991                             public boolean is80211mcSupported() { return true; }
2992                             public static final class Builder {
2993                                 @NonNull
2994                                 public Builder set80211mcSupported(boolean supports80211mc) {
2995                                 }
2996                                 @NonNull
2997                                 public Config build() {
2998                                     return Config();
2999                                 }
3000                             }
3001                         }
3002                     """
3003                     ),
3004                     androidxNonNullSource,
3005                 )
3006         )
3007     }
3008 
3009     @RequiresCapabilities(Capability.KOTLIN)
3010     @Test
Kotlin required parameters must come before optional parametersnull3011     fun `Kotlin required parameters must come before optional parameters`() {
3012         check(
3013             apiLint = "", // enabled
3014             extraArguments =
3015                 arrayOf(
3016                     // JvmOverloads warning, not what we want to test here
3017                     ARG_HIDE,
3018                     "MissingJvmstatic"
3019                 ),
3020             expectedIssues =
3021                 """
3022 src/android/pkg/Interface.kt:18: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder]
3023 src/android/pkg/Interface.kt:28: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder]
3024 src/android/pkg/Interface.kt:39: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder]
3025 src/android/pkg/Interface.kt:50: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder]
3026 src/android/pkg/Interface.kt:56: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder]
3027 src/android/pkg/Interface.kt:66: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder]
3028 src/android/pkg/Interface.kt:72: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder]
3029 src/android/pkg/Interface.kt:82: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder]
3030 src/android/pkg/Interface.kt:92: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder]
3031 src/android/pkg/Interface.kt:104: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder]
3032 src/android/pkg/Interface.kt:114: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder]
3033 src/android/pkg/Interface.kt:125: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder]
3034 src/android/pkg/Interface.kt:136: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder]
3035 src/android/pkg/Interface.kt:142: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder]
3036 src/android/pkg/Interface.kt:152: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder]
3037 src/android/pkg/Interface.kt:158: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder]
3038             """
3039                     .trimIndent(),
3040             expectedFail = DefaultLintErrorMessage,
3041             sourceFiles =
3042                 arrayOf(
3043                     java(
3044                         """
3045                         package android.pkg;
3046 
3047                         public interface JavaInterface {
3048                             void doSomething();
3049                             void doSomethingElse();
3050                         }
3051                     """
3052                             .trimIndent()
3053                     ),
3054                     java(
3055                         """
3056                         package android.pkg;
3057 
3058                         public interface JavaSamInterface {
3059                             void doSomething();
3060                         }
3061                     """
3062                             .trimIndent()
3063                     ),
3064                     kotlin(
3065                         """
3066                         package android.pkg
3067 
3068                         interface Interface {
3069                             fun foo()
3070                         }
3071 
3072                         fun interface FunInterface {
3073                             fun foo()
3074                         }
3075 
3076                         // Functions
3077                         fun ok1(
3078                             required: Boolean,
3079                             default: Int = 0,
3080                         ) {}
3081 
3082                         fun error1(
3083                             default: Int = 0,
3084                             required: Boolean,
3085                         ) {}
3086 
3087                         fun ok2(
3088                             default: Int = 0,
3089                             trailing: () -> Unit
3090                         ) {}
3091 
3092                         fun error2(
3093                             default: Int = 0,
3094                             required: () -> Unit,
3095                             default2: Int = 0,
3096                         ) {}
3097 
3098                         fun ok3(
3099                             default: Int = 0,
3100                             trailing: suspend () -> Unit
3101                         ) {}
3102 
3103                         fun error3(
3104                             default: Int = 0,
3105                             required: suspend () -> Unit,
3106                             default2: Int = 0,
3107                         ) {}
3108 
3109                         fun ok4(
3110                             default: Int = 0,
3111                             trailing: FunInterface
3112                         ) {}
3113 
3114                         fun error4(
3115                             default: Int = 0,
3116                             required: FunInterface,
3117                             default2: Int = 0,
3118                         ) {}
3119 
3120                         fun error5(
3121                             default: Int = 0,
3122                             trailing: Interface
3123                         ) {}
3124 
3125                         fun ok6(
3126                             default: Int = 0,
3127                             trailing: JavaSamInterface
3128                         ) {}
3129 
3130                         fun error6(
3131                             default: Int = 0,
3132                             required: JavaSamInterface,
3133                             default2: Int = 0,
3134                         ) {}
3135 
3136                         fun error7(
3137                             default: Int = 0,
3138                             trailing: JavaInterface
3139                         ) {}
3140 
3141                         suspend fun ok8(
3142                             required: Boolean,
3143                             default: Int = 0,
3144                         ) {}
3145 
3146                         suspend fun error8(
3147                             default: Int = 0,
3148                             required: Boolean,
3149                         ) {}
3150 
3151                         suspend fun ok9(
3152                             default: Int = 0,
3153                             trailing: () -> Unit
3154                         ) {}
3155 
3156                         suspend fun error9(
3157                             default: Int = 0,
3158                             required: () -> Unit,
3159                             default2: Int = 0,
3160                         ) {}
3161 
3162                         // Class constructors
3163                         class Ok1(
3164                             required: Boolean,
3165                             default: Int = 0,
3166                         )
3167 
3168                         class Error1(
3169                             default: Int = 0,
3170                             required: Boolean,
3171                         )
3172 
3173                         class Ok2(
3174                             default: Int = 0,
3175                             trailing: () -> Unit
3176                         )
3177 
3178                         class Error2(
3179                             default: Int = 0,
3180                             required: () -> Unit,
3181                             default2: Int = 0,
3182                         )
3183 
3184                         class Ok3(
3185                             default: Int = 0,
3186                             trailing: suspend () -> Unit
3187                         )
3188 
3189                         class Error3(
3190                             default: Int = 0,
3191                             required: suspend () -> Unit,
3192                             default2: Int = 0,
3193                         )
3194 
3195                         class Ok4(
3196                             default: Int = 0,
3197                             trailing: FunInterface
3198                         )
3199 
3200                         class Error4(
3201                             default: Int = 0,
3202                             required: FunInterface,
3203                             default2: Int = 0,
3204                         )
3205 
3206                         class Error5(
3207                             default: Int = 0,
3208                             trailing: Interface
3209                         )
3210 
3211                         class Ok6(
3212                             default: Int = 0,
3213                             trailing: JavaSamInterface
3214                         )
3215 
3216                         class Error6(
3217                             default: Int = 0,
3218                             required: JavaSamInterface,
3219                             default2: Int = 0,
3220                         )
3221 
3222                         class Error7(
3223                             default: Int = 0,
3224                             trailing: JavaInterface
3225                         )
3226                     """
3227                     )
3228                 )
3229         )
3230     }
3231 
3232     @RequiresCapabilities(Capability.KOTLIN)
3233     @Test
No parameter ordering for sealed class constructornull3234     fun `No parameter ordering for sealed class constructor`() {
3235         check(
3236             expectedIssues = "",
3237             apiLint = "",
3238             sourceFiles =
3239                 arrayOf(
3240                     kotlin(
3241                         """
3242                     package test.pkg
3243 
3244                     sealed class Foo(
3245                         default: Int = 0,
3246                         required: () -> Unit,
3247                     )
3248                     """
3249                     )
3250                 )
3251         )
3252     }
3253 
3254     @RequiresCapabilities(Capability.KOTLIN)
3255     @Test
members in sealed class are not hidden abstractnull3256     fun `members in sealed class are not hidden abstract`() {
3257         check(
3258             expectedIssues = "",
3259             apiLint = "",
3260             sourceFiles =
3261                 arrayOf(
3262                     kotlin(
3263                         """
3264                         package test.pkg
3265 
3266                         sealed class ModifierLocalMap() {
3267                             internal abstract operator fun <T> set(key: ModifierLocal<T>, value: T)
3268                             internal abstract operator fun <T> get(key: ModifierLocal<T>): T?
3269                             internal abstract operator fun contains(key: ModifierLocal<*>): Boolean
3270                         }
3271                     """
3272                     )
3273                 )
3274         )
3275     }
3276 
3277     @RequiresCapabilities(Capability.KOTLIN)
3278     @Test
throw unresolved errornull3279     fun `throw unresolved error`() {
3280         // Regression test from b/364736827
3281         check(
3282             expectedIssues = "",
3283             apiLint = "",
3284             sourceFiles =
3285                 arrayOf(
3286                     kotlin(
3287                         """
3288                         package test.pkg
3289 
3290                         import kotlin.random.Random
3291 
3292                         public class SubspaceSemanticsNodeInteraction() {
3293                             /**
3294                              * @throws [UnresolvedAssertionError] if failed
3295                              */
3296                             public fun assertDoesNotExist() {
3297                                 if (Random.nextBoolean()) {
3298                                     throw UnresolvedAssertionError("Failed")
3299                                 }
3300                             }
3301                         }
3302                     """
3303                     )
3304                 )
3305         )
3306     }
3307 }
3308