• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
18 
19 import org.junit.Test
20 
21 class ApiLintTest : DriverTest() {
22 
23     @Test
Test namesnull24     fun `Test names`() {
25         // Make sure we only flag issues in new API
26         check(
27             apiLint = "", // enabled
28             extraArguments = arrayOf(
29                 ARG_API_LINT_IGNORE_PREFIX,
30                 "android.icu.",
31                 ARG_API_LINT_IGNORE_PREFIX,
32                 "java.",
33                 ARG_HIDE, "MissingNullability"
34             ),
35             expectedIssues = """
36                 src/Dp.kt:3: warning: Acronyms should not be capitalized in method names: was `badCALL`, should this be `badCall`? [AcronymName] [See https://s.android.com/api-guidelines#acronyms-in-method-name]
37                 src/android/pkg/ALL_CAPS.java:3: warning: Acronyms should not be capitalized in class names: was `ALL_CAPS`, should this be `AllCaps`? [AcronymName] [See https://s.android.com/api-guidelines#acronyms-in-method-name]
38                 src/android/pkg/HTMLWriter.java:3: warning: Acronyms should not be capitalized in class names: was `HTMLWriter`, should this be `HtmlWriter`? [AcronymName] [See https://s.android.com/api-guidelines#acronyms-in-method-name]
39                 src/android/pkg/MyStringImpl.java:3: error: Don't expose your implementation details: `MyStringImpl` ends with `Impl` [EndsWithImpl] [See https://s.android.com/api-guidelines#dont-end-with-impl]
40                 src/android/pkg/badlyNamedClass.java:5: error: Class must start with uppercase char: badlyNamedClass [StartWithUpper] [See https://s.android.com/api-guidelines#style-conventions]
41                 src/android/pkg/badlyNamedClass.java:7: error: Method name must start with lowercase char: BadlyNamedMethod1 [StartWithLower] [See https://s.android.com/api-guidelines#style-conventions]
42                 src/android/pkg/badlyNamedClass.java:9: warning: Acronyms should not be capitalized in method names: was `fromHTMLToHTML`, should this be `fromHtmlToHtml`? [AcronymName] [See https://s.android.com/api-guidelines#acronyms-in-method-name]
43                 src/android/pkg/badlyNamedClass.java:10: warning: Acronyms should not be capitalized in method names: was `toXML`, should this be `toXml`? [AcronymName] [See https://s.android.com/api-guidelines#acronyms-in-method-name]
44                 src/android/pkg/badlyNamedClass.java:12: warning: Acronyms should not be capitalized in method names: was `getID`, should this be `getId`? [AcronymName] [See https://s.android.com/api-guidelines#acronyms-in-method-name]
45                 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] [See https://s.android.com/api-guidelines#constant-naming]
46                 """,
47             expectedFail = DefaultLintErrorMessage,
48             sourceFiles = arrayOf(
49                 java(
50                     """
51                     package android.pkg;
52 
53                     import androidx.annotation.Nullable;
54 
55                     public class badlyNamedClass {
56                         public static final int BadlyNamedField = 1;
57                         public void BadlyNamedMethod1() { }
58 
59                         public void fromHTMLToHTML() { }
60                         public void toXML() { }
61                         @Nullable
62                         public String getID() { return null; }
63                         public void setZOrderOnTop() { } // OK
64                     }
65                     """
66                 ),
67                 java(
68                     """
69                     package android.pkg;
70 
71                     public class ALL_CAPS { // like android.os.Build.VERSION_CODES
72                     }
73                     """
74                 ),
75                 java(
76                     """
77                     package android.pkg;
78 
79                     public class HTMLWriter {
80                     }
81                     """
82                 ),
83                 java(
84                     """
85                     package android.pkg;
86 
87                     public class MyStringImpl {
88                     }
89                     """
90                 ),
91                 java(
92                     """
93                     package android.icu;
94 
95                     import androidx.annotation.Nullable;
96 
97                     // Same as above android.pkg.badlyNamedClass but in a package
98                     // that API lint is supposed to ignore (see ARG_API_LINT_IGNORE_PREFIX)
99                     public class badlyNamedClass {
100                         public static final int BadlyNamedField = 1;
101                         public void BadlyNamedMethod1() { }
102 
103                         public void toXML() { }
104                         @Nullable
105                         public String getID() { return null; }
106                         public void setZOrderOnTop() { }
107                     }
108                     """
109                 ),
110                 java(
111                     """
112                     package android.icu.sub;
113 
114                     import androidx.annotation.Nullable;
115 
116                     // Same as above android.pkg.badlyNamedClass but in a package
117                     // that API lint is supposed to ignore (see ARG_API_LINT_IGNORE_PREFIX)
118                     public class badlyNamedClass {
119                         public static final int BadlyNamedField = 1;
120                         public void BadlyNamedMethod1() { }
121 
122                         public void toXML() { }
123                         @Nullable
124                         public String getID() { return null; }
125                         public void setZOrderOnTop() { }
126                     }
127                     """
128                 ),
129                 java(
130                     """
131                     package java;
132 
133                     import androidx.annotation.Nullable;
134 
135                     // Same as above android.pkg.badlyNamedClass but in a package
136                     // that API lint is supposed to ignore (see ARG_API_LINT_IGNORE_PREFIX)
137                     public class badlyNamedClass {
138                         public static final int BadlyNamedField = 1;
139                         public void BadlyNamedMethod1() { }
140 
141                         public void toXML() { }
142                         @Nullable
143                         public String getID() { return null; }
144                         public void setZOrderOnTop() { }
145                     }
146                     """
147                 ),
148                 kotlin(
149                     """
150                     inline class Dp(val value: Float)
151                     fun greatCall(width: Dp)
152                     fun badCALL(width: Dp)
153                 """
154                 ),
155                 androidxNullableSource
156             )
157             /*
158             expectedOutput = """
159                 9 new API lint issues were found.
160                 See tools/metalava/API-LINT.md for how to handle these.
161             """
162              */
163         )
164     }
165 
166     @Test
Test names against previous APInull167     fun `Test names against previous API`() {
168         check(
169             apiLint = """
170                 package android.pkg {
171                   public class badlyNamedClass {
172                     ctor public badlyNamedClass();
173                     method public void BadlyNamedMethod1();
174                     method public void fromHTMLToHTML();
175                     method public String getID();
176                     method public void toXML();
177                     field public static final int BadlyNamedField = 1; // 0x1
178                   }
179                 }
180             """.trimIndent(),
181             expectedIssues = """
182                 src/android/pkg/badlyNamedClass.java:8: warning: Acronyms should not be capitalized in method names: was `toXML2`, should this be `toXmL2`? [AcronymName] [See https://s.android.com/api-guidelines#acronyms-in-method-name]
183                 src/android/pkg2/HTMLWriter.java:3: warning: Acronyms should not be capitalized in class names: was `HTMLWriter`, should this be `HtmlWriter`? [AcronymName] [See https://s.android.com/api-guidelines#acronyms-in-method-name]
184                 src/android/pkg2/HTMLWriter.java:4: warning: Acronyms should not be capitalized in method names: was `fromHTMLToHTML`, should this be `fromHtmlToHtml`? [AcronymName] [See https://s.android.com/api-guidelines#acronyms-in-method-name]
185                 """,
186             sourceFiles = arrayOf(
187                 java(
188                     """
189                     package android.pkg;
190 
191                     public class badlyNamedClass {
192                         public static final int BadlyNamedField = 1;
193 
194                         public void fromHTMLToHTML() { }
195                         public void toXML() { }
196                         public void toXML2() { }
197                         public String getID() { return null; }
198                     }
199                     """
200                 ),
201                 java(
202                     """
203                     package android.pkg2;
204 
205                     public class HTMLWriter {
206                         public void fromHTMLToHTML() { }
207                     }
208                     """
209                 )
210             )
211         )
212     }
213 
214     @Test
Test constantsnull215     fun `Test constants`() {
216         check(
217             apiLint = "", // enabled
218             expectedIssues = """
219                 src/android/pkg/Constants.java:14: error: All constants must be defined at compile time: android.pkg.Constants#FOO [CompileTimeConstant]
220                 src/android/pkg/Constants.java:12: warning: If min/max could change in future, make them dynamic methods: android.pkg.Constants#MAX_FOO [MinMaxConstant] [See https://s.android.com/api-guidelines#min-max-constants]
221                 src/android/pkg/Constants.java:11: warning: If min/max could change in future, make them dynamic methods: android.pkg.Constants#MIN_FOO [MinMaxConstant] [See https://s.android.com/api-guidelines#min-max-constants]
222                 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] [See https://s.android.com/api-guidelines#constant-naming]
223                 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] [See https://s.android.com/api-guidelines#constant-naming]
224                 """,
225             expectedFail = DefaultLintErrorMessage,
226             sourceFiles = arrayOf(
227                 java(
228                     """
229                     package android.pkg;
230 
231                     import androidx.annotation.NonNull;
232 
233                     public class Constants {
234                         private Constants() { }
235                         @NonNull
236                         public static final String[] strings = { "NONE", "WPA_PSK" };
237                         @NonNull
238                         public static final String[] myStrings = { "NONE", "WPA_PSK" };
239                         public static final int MIN_FOO = 1;
240                         public static final int MAX_FOO = 10;
241                         @NonNull
242                         public static final String FOO = System.getProperty("foo");
243                     }
244                     """
245                 ),
246                 androidxNonNullSource,
247                 androidxNullableSource
248             )
249         )
250     }
251 
252     @Test
No enumsnull253     fun `No enums`() {
254         check(
255             apiLint = "", // enabled
256             expectedIssues = """
257                 src/android/pkg/MyEnum.java:3: error: Enums are discouraged in Android APIs [Enum] [See https://s.android.com/api-guidelines#avoid-enum]
258                 """,
259             expectedFail = DefaultLintErrorMessage,
260             sourceFiles = arrayOf(
261                 java(
262                     """
263                     package android.pkg;
264 
265                     public enum MyEnum {
266                        FOO, BAR
267                     }
268                     """
269                 )
270             )
271         )
272     }
273 
274     @Test
Test callbacksnull275     fun `Test callbacks`() {
276         check(
277             apiLint = "", // enabled
278             expectedIssues = """
279                 src/android/pkg/MyCallback.java:9: error: Callback method names must follow the on<Something> style: bar [CallbackMethodName] [See https://s.android.com/api-guidelines#callback-method-naming]
280                 src/android/pkg/MyCallbacks.java:3: error: Callback class names should be singular: MyCallbacks [SingularCallback] [See https://s.android.com/api-guidelines#callback-class-singular]
281                 src/android/pkg/MyInterfaceCallback.java:3: error: Callbacks must be abstract class instead of interface to enable extension in future API levels: MyInterfaceCallback [CallbackInterface] [See https://s.android.com/api-guidelines#callback-abstract-instead-of-interface]
282                 src/android/pkg/MyObserver.java:3: warning: Class should be named MyCallback [CallbackName] [See https://s.android.com/api-guidelines#observer-should-be-callback]
283                 """,
284             expectedFail = DefaultLintErrorMessage,
285             sourceFiles = arrayOf(
286                 java(
287                     """
288                     package android.pkg;
289 
290                     public class MyCallbacks {
291                     }
292                     """
293                 ),
294                 java(
295                     """
296                     package android.pkg;
297 
298                     public final class RemoteCallback {
299                         public void sendResult();
300                     }
301                     """
302                 ),
303                 java(
304                     """
305                     package android.pkg;
306 
307                     public class MyObserver {
308                     }
309                     """
310                 ),
311                 java(
312                     """
313                     package android.pkg;
314 
315                     public interface MyInterfaceCallback {
316                     }
317                     """
318                 ),
319                 java(
320                     """
321                     package android.pkg;
322 
323                     public class MyCallback {
324                         public void onFoo() {
325                         }
326                         public void onAnimationStart() {
327                         }
328                         public void onN1GoodMethod() { }
329                         public void bar() {
330                         }
331                         public static void staticMethod() {
332                         }
333                         public final void finalMethod() {
334                         }
335                     }
336                     """
337                 )
338             )
339         )
340     }
341 
342     @Test
Test listenersnull343     fun `Test listeners`() {
344         check(
345             apiLint = "", // enabled
346             expectedIssues = """
347                 src/android/pkg/MyClassListener.java:3: error: Listeners should be an interface, or otherwise renamed Callback: MyClassListener [ListenerInterface] [See https://s.android.com/api-guidelines#callbacks-listener]
348                 src/android/pkg/MyListener.java:6: error: Listener method names must follow the on<Something> style: bar [CallbackMethodName] [See https://s.android.com/api-guidelines#callback-method-naming]
349                 """,
350             expectedFail = DefaultLintErrorMessage,
351             sourceFiles = arrayOf(
352                 java(
353                     """
354                     package android.pkg;
355 
356                     public abstract class MyClassListener {
357                     }
358                     """
359                 ),
360                 java(
361                     """
362                     package android.pkg;
363 
364                     public interface OnFooBarListener {
365                         void bar();
366                     }
367                     """
368                 ),
369                 java(
370                     """
371                     package android.pkg;
372 
373                     public interface OnFooBarListener {
374                         void onFooBar(); // OK
375                     }
376                     """
377                 ),
378                 java(
379                     """
380                     package android.pkg;
381 
382                     public interface MyInterfaceListener {
383                     }
384                     """
385                 ),
386                 java(
387                     """
388                     package android.pkg;
389 
390                     public interface MyListener {
391                         public void onFoo() {
392                         }
393                         public void bar() {
394                         }
395                         public static void staticMethod() {
396                         }
397                         public static void finalMethod() {
398                         }
399                     }
400                     """
401                 )
402             )
403         )
404     }
405 
406     @Test
Test actionsnull407     fun `Test actions`() {
408         check(
409             apiLint = "", // enabled
410             expectedIssues = """
411                 src/android/accounts/Actions.java:7: error: Intent action constant name must be ACTION_FOO: ACCOUNT_ADDED [IntentName] [See https://s.android.com/api-guidelines#use-standard-prefixes-for-constants]
412                 src/android/accounts/Actions.java:6: error: Inconsistent action value; expected `android.accounts.action.ACCOUNT_OPENED`, was `android.accounts.ACCOUNT_OPENED` [ActionValue]
413                 src/android/accounts/Actions.java:8: error: Intent action constant name must be ACTION_FOO: SOMETHING [IntentName] [See https://s.android.com/api-guidelines#use-standard-prefixes-for-constants]
414                 """,
415             expectedFail = DefaultLintErrorMessage,
416             sourceFiles = arrayOf(
417                 java(
418                     """
419                     package android.accounts;
420 
421                     public class Actions {
422                         private Actions() { }
423                         public static final String ACTION_ACCOUNT_REMOVED = "android.accounts.action.ACCOUNT_REMOVED";
424                         public static final String ACTION_ACCOUNT_OPENED = "android.accounts.ACCOUNT_OPENED";
425                         public static final String ACCOUNT_ADDED = "android.accounts.action.ACCOUNT_ADDED";
426                         public static final String SOMETHING = "android.accounts.action.ACCOUNT_MOVED";
427                     }
428                     """
429                 )
430             )
431         )
432     }
433 
434     @Test
Test extrasnull435     fun `Test extras`() {
436         check(
437             apiLint = "", // enabled
438             expectedIssues = """
439                 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]
440                 src/android/accounts/Extras.java:7: error: Intent extra constant name must be EXTRA_FOO: RULE_ID [IntentName] [See https://s.android.com/api-guidelines#use-standard-prefixes-for-constants]
441                 src/android/accounts/Extras.java:6: error: Intent extra constant name must be EXTRA_FOO: SOMETHING_EXTRA [IntentName] [See https://s.android.com/api-guidelines#use-standard-prefixes-for-constants]
442                 """,
443             expectedFail = DefaultLintErrorMessage,
444             sourceFiles = arrayOf(
445                 java(
446                     """
447                     package android.accounts;
448 
449                     public class Extras {
450                         private Extras() { }
451                         public static final String EXTRA_AUTOMATIC_RULE_ID = "android.app.extra.AUTOMATIC_RULE_ID";
452                         public static final String SOMETHING_EXTRA = "something.here";
453                         public static final String RULE_ID = "android.app.extra.AUTOMATIC_RULE_ID";
454                     }
455                     """
456                 )
457             )
458         )
459     }
460 
461     @Test
Test equals and hashCodenull462     fun `Test equals and hashCode`() {
463         check(
464             apiLint = "", // enabled
465             expectedIssues = """
466                 src/android/pkg/MissingEquals.java:4: error: Must override both equals and hashCode; missing one in android.pkg.MissingEquals [EqualsAndHashCode] [See https://s.android.com/api-guidelines#equals-and-hashcode]
467                 src/android/pkg/MissingHashCode.java:7: error: Must override both equals and hashCode; missing one in android.pkg.MissingHashCode [EqualsAndHashCode] [See https://s.android.com/api-guidelines#equals-and-hashcode]
468                 """,
469             expectedFail = DefaultLintErrorMessage,
470             sourceFiles = arrayOf(
471                 java(
472                     """
473                     package android.pkg;
474 
475                     import androidx.annotation.Nullable;
476 
477                     @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
478                     public class Ok {
479                         public boolean equals(@Nullable Object other) { return true; }
480                         public int hashCode() { return 0; }
481                     }
482                     """
483                 ),
484                 java(
485                     """
486                     package android.pkg;
487 
488                     public class MissingEquals {
489                         public int hashCode() { return 0; }
490                     }
491                     """
492                 ),
493                 java(
494                     """
495                     package android.pkg;
496 
497                     import androidx.annotation.Nullable;
498 
499                     @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
500                     public class MissingHashCode {
501                         public boolean equals(@Nullable Object other) { return true; }
502                     }
503                     """
504                 ),
505                 java(
506                     """
507                     package android.pkg;
508 
509                     import androidx.annotation.Nullable;
510 
511                     public class UnrelatedEquals {
512                         @SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
513                         public static boolean equals(@Nullable Object other) { return true; } // static
514                         public boolean equals(int other) { return false; } // wrong parameter type
515                         public boolean equals(@Nullable Object other, int bar) { return false; } // wrong signature
516                     }
517                     """
518                 ),
519                 androidxNullableSource
520             )
521         )
522     }
523 
524     @Test
Test Parcelablenull525     fun `Test Parcelable`() {
526         check(
527             apiLint = "", // enabled
528             expectedIssues = """
529                 src/android/pkg/MissingCreator.java:5: error: Parcelable requires a `CREATOR` field; missing in android.pkg.MissingCreator [ParcelCreator] [See https://s.android.com/api-guidelines#parcelable-creator]
530                 src/android/pkg/MissingDescribeContents.java:5: error: Parcelable requires `public int describeContents()`; missing in android.pkg.MissingDescribeContents [ParcelCreator] [See https://s.android.com/api-guidelines#parcelable-creator]
531                 src/android/pkg/MissingWriteToParcel.java:5: error: Parcelable requires `void writeToParcel(Parcel, int)`; missing in android.pkg.MissingWriteToParcel [ParcelCreator] [See https://s.android.com/api-guidelines#parcelable-creator]
532                 src/android/pkg/NonFinalParcelable.java:5: error: Parcelable classes must be final: android.pkg.NonFinalParcelable is not final [ParcelNotFinal] [See https://s.android.com/api-guidelines#parcelable-final]
533                 src/android/pkg/ParcelableConstructor.java:6: error: Parcelable inflation is exposed through CREATOR, not raw constructors, in android.pkg.ParcelableConstructor [ParcelConstructor] [See https://s.android.com/api-guidelines#parcelable-creator]
534                 """,
535             expectedFail = DefaultLintErrorMessage,
536             sourceFiles = arrayOf(
537                 java(
538                     """
539                     package android.pkg;
540 
541                     import androidx.annotation.Nullable;
542 
543                     public final class ParcelableConstructor implements android.os.Parcelable {
544                         public ParcelableConstructor(@Nullable android.os.Parcel p) { }
545                         public int describeContents() { return 0; }
546                         public void writeToParcel(@Nullable android.os.Parcel p, int f) { }
547                         @Nullable
548                         public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR = null;
549                     }
550                     """
551                 ),
552                 java(
553                     """
554                     package android.pkg;
555 
556                     import androidx.annotation.Nullable;
557 
558                     public class NonFinalParcelable implements android.os.Parcelable {
559                         public NonFinalParcelable() { }
560                         public int describeContents() { return 0; }
561                         public void writeToParcel(@Nullable android.os.Parcel p, int f) { }
562                         @Nullable
563                         public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR = null;
564                     }
565                     """
566                 ),
567                 java(
568                     """
569                     package android.pkg;
570 
571                     import androidx.annotation.Nullable;
572 
573                     public final class MissingCreator implements android.os.Parcelable {
574                         public MissingCreator() { }
575                         public int describeContents() { return 0; }
576                         public void writeToParcel(@Nullable android.os.Parcel p, int f) { }
577                     }
578                     """
579                 ),
580                 java(
581                     """
582                     package android.pkg;
583 
584                     import androidx.annotation.Nullable;
585 
586                     public final class MissingDescribeContents implements android.os.Parcelable {
587                         public MissingDescribeContents() { }
588                         public void writeToParcel(@Nullable android.os.Parcel p, int f) { }
589                         @Nullable
590                         public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR = null;
591                     }
592                     """
593                 ),
594                 java(
595                     """
596                     package android.pkg;
597 
598                     import androidx.annotation.Nullable;
599 
600                     public final class MissingWriteToParcel implements android.os.Parcelable {
601                         public MissingWriteToParcel() { }
602                         public int describeContents() { return 0; }
603                         @Nullable
604                         public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR = null;
605                     }
606                     """
607                 ),
608                 androidxNullableSource
609             )
610         )
611     }
612 
613     @Test
No protected methods or fields are allowednull614     fun `No protected methods or fields are allowed`() {
615         check(
616             apiLint = "", // enabled
617             expectedIssues = """
618                 src/android/pkg/MyClass.java:6: error: Protected methods not allowed; must be public: method android.pkg.MyClass.wrong()} [ProtectedMember] [See https://s.android.com/api-guidelines#avoid-protected]
619                 src/android/pkg/MyClass.java:8: error: Protected fields not allowed; must be public: field android.pkg.MyClass.wrong} [ProtectedMember] [See https://s.android.com/api-guidelines#avoid-protected]
620                 """,
621             expectedFail = DefaultLintErrorMessage,
622             sourceFiles = arrayOf(
623                 java(
624                     """
625                     package android.pkg;
626 
627                     public abstract class MyClass implements AutoCloseable {
628                         public void ok() { }
629                         protected void finalize() { } // OK
630                         protected void wrong() { }
631                         public final int ok = 42;
632                         protected final int wrong = 5;
633                         private int ok2 = 2;
634                     }
635                     """
636                 )
637             )
638         )
639     }
640 
641     @Test
Fields must be final and properly namednull642     fun `Fields must be final and properly named`() {
643         check(
644             apiLint = "", // enabled
645             expectedIssues = """
646                 src/android/pkg/MyClass.java:11: error: Non-static field ALSO_BAD_CONSTANT must be named using fooBar style [StartWithLower] [See https://s.android.com/api-guidelines#style-conventions]
647                 src/android/pkg/MyClass.java:11: error: Constant ALSO_BAD_CONSTANT must be marked static final [AllUpper] [See https://s.android.com/api-guidelines#constant-naming]
648                 src/android/pkg/MyClass.java:7: error: Non-static field AlsoBadName must be named using fooBar style [StartWithLower] [See https://s.android.com/api-guidelines#style-conventions]
649                 src/android/pkg/MyClass.java:10: error: Bare field BAD_CONSTANT must be marked final, or moved behind accessors if mutable [MutableBareField] [See https://s.android.com/api-guidelines#mutable-bare-field]
650                 src/android/pkg/MyClass.java:10: error: Constant BAD_CONSTANT must be marked static final [AllUpper] [See https://s.android.com/api-guidelines#constant-naming]
651                 src/android/pkg/MyClass.java:5: error: Bare field badMutable must be marked final, or moved behind accessors if mutable [MutableBareField] [See https://s.android.com/api-guidelines#mutable-bare-field]
652                 src/android/pkg/MyClass.java:9: error: Bare field badStaticMutable must be marked final, or moved behind accessors if mutable [MutableBareField] [See https://s.android.com/api-guidelines#mutable-bare-field]
653                 src/android/pkg/MyClass.java:6: error: Internal field mBadName must not be exposed [InternalField] [See https://s.android.com/api-guidelines#internal-fields]
654                 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] [See https://s.android.com/api-guidelines#constant-naming]
655                 src/android/pkg/MyClass.java:8: error: Internal field sBadStaticName must not be exposed [InternalField] [See https://s.android.com/api-guidelines#internal-fields]
656                 src/android/pkg/MyClass.java:15: error: Internal field mBad must not be exposed [InternalField] [See https://s.android.com/api-guidelines#internal-fields]
657                 """,
658             expectedFail = DefaultLintErrorMessage,
659             sourceFiles = arrayOf(
660                 java(
661                     """
662                     package android.pkg;
663 
664                     public class MyClass {
665                         private int mOk;
666                         public int badMutable;
667                         public final int mBadName;
668                         public final int AlsoBadName;
669                         public static final int sBadStaticName;
670                         public static int badStaticMutable;
671                         public static int BAD_CONSTANT;
672                         public final int ALSO_BAD_CONSTANT;
673 
674                         public static class LayoutParams {
675                             public int ok;
676                             public int mBad;
677                         }
678                     }
679                     """
680                 ),
681                 kotlin(
682                     """
683                     package android.pkg
684 
685                     class KotlinClass(val ok: Int) {
686                         companion object {
687                             const val OkConstant = 1
688                             const val OK_CONSTANT = 2
689                             @JvmField
690                             val OkSingleton = KotlinClass(3)
691                             @JvmField
692                             val OK_SINGLETON = KotlinClass(4)
693                         }
694 
695                         object OkObject
696                     }
697                     """
698                 )
699             )
700         )
701     }
702 
703     @Test
Only android_net_Uri allowednull704     fun `Only android_net_Uri allowed`() {
705         check(
706             apiLint = "", // enabled
707             expectedIssues = """
708                 src/android/pkg/MyClass.java:7: error: Use android.net.Uri instead of java.net.URL (method android.pkg.MyClass.bad1()) [AndroidUri] [See https://s.android.com/api-guidelines#android-uri]
709                 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] [See https://s.android.com/api-guidelines#android-uri]
710                 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] [See https://s.android.com/api-guidelines#android-uri]
711                 """,
712             expectedFail = DefaultLintErrorMessage,
713             sourceFiles = arrayOf(
714                 java(
715                     """
716                     package android.pkg;
717 
718                     import java.util.List;import java.util.concurrent.CompletableFuture;import java.util.concurrent.Future;
719                     import androidx.annotation.NonNull;
720 
721                     public final class MyClass {
722                         public @NonNull java.net.URL bad1() { throw new RuntimeException(); }
723                         public void bad2(@NonNull List<java.net.URI> param) { }
724                         public void bad3(@NonNull android.net.URL param) { }
725                     }
726                     """
727                 ),
728                 androidxNonNullSource
729             )
730         )
731     }
732 
733     @Test
CompletableFuture and plain Future not allowednull734     fun `CompletableFuture and plain Future not allowed`() {
735         check(
736             apiLint = "", // enabled
737             expectedIssues = """
738                 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] [See https://s.android.com/api-guidelines#bad-future]
739                 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] [See https://s.android.com/api-guidelines#bad-future]
740                 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] [See https://s.android.com/api-guidelines#bad-future]
741                 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] [See https://s.android.com/api-guidelines#bad-future]
742                 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] [See https://s.android.com/api-guidelines#bad-future]
743                 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] [See https://s.android.com/api-guidelines#bad-future]
744                 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] [See https://s.android.com/api-guidelines#bad-future]
745                 """,
746             expectedFail = DefaultLintErrorMessage,
747             sourceFiles = arrayOf(
748                 java(
749                     """
750                     package android.pkg;
751 
752                     import java.util.concurrent.CompletableFuture;
753                     import java.util.concurrent.Future;
754                     import androidx.annotation.Nullable;
755                     import com.google.common.util.concurrent.ListenableFuture;
756 
757                     public final class MyClass {
758                         public @Nullable CompletableFuture<String> bad1() { return null; }
759                         public void bad2(@Nullable CompletableFuture<String> param) { }
760                         public @Nullable Future<String> bad3() { return null; }
761                         public void bad4(@Nullable Future<String> param) { }
762 
763                         public @Nullable ListenableFuture<String> okAsync() { return null; }
764                         public void ok2(@Nullable ListenableFuture<String> param) { }
765 
766                         public interface BadFuture<T> extends Future<T> {
767                         }
768                         public static abstract class BadFutureClass<T> implements Future<T> {
769                         }
770                         public class BadCompletableFuture<T> extends CompletableFuture<T> {
771                         }
772                     }
773                     """
774                 ),
775                 java(
776                     """
777                     package com.google.common.util.concurrent;
778                     public class ListenableFuture<T> {
779                     }
780                     """
781                 ),
782                 androidxNullableSource
783             )
784         )
785     }
786 
787     @Test
Typedef must be hiddennull788     fun `Typedef must be hidden`() {
789         check(
790             apiLint = "", // enabled
791             expectedIssues = """
792                 src/android/pkg/MyClass.java:19: error: Don't expose @IntDef: SomeInt must be hidden. [PublicTypedef] [See https://s.android.com/api-guidelines#no-public-typedefs]
793                 src/android/pkg/MyClass.java:24: error: Don't expose @LongDef: SomeLong must be hidden. [PublicTypedef] [See https://s.android.com/api-guidelines#no-public-typedefs]
794                 src/android/pkg/MyClass.java:14: error: Don't expose @StringDef: SomeString must be hidden. [PublicTypedef] [See https://s.android.com/api-guidelines#no-public-typedefs]
795                 """,
796             expectedFail = DefaultLintErrorMessage,
797             sourceFiles = arrayOf(
798                 java(
799                     """
800                     package android.pkg;
801 
802                     public final class MyClass {
803                             private MyClass() {}
804 
805                             public static final String SOME_STRING = "abc";
806                             public static final int SOME_INT = 1;
807                             public static final long SOME_LONG = 1L;
808 
809                             @android.annotation.StringDef(value = {
810                                     SOME_STRING
811                             })
812                             @Retention(RetentionPolicy.SOURCE)
813                             public @interface SomeString {}
814                             @android.annotation.IntDef(value = {
815                                     SOME_INT
816                             })
817                             @Retention(RetentionPolicy.SOURCE)
818                             public @interface SomeInt {}
819                             @android.annotation.LongDef(value = {
820                                     SOME_LONG
821                             })
822                             @Retention(RetentionPolicy.SOURCE)
823                             public @interface SomeLong {}
824                     }
825                     """
826                 )
827             )
828         )
829     }
830 
831     @Test
Ensure registration methods are matchednull832     fun `Ensure registration methods are matched`() {
833         check(
834             apiLint = "", // enabled
835             expectedIssues = """
836                 src/android/pkg/RegistrationInterface.java:6: error: Found registerOverriddenUnpairedCallback but not unregisterOverriddenUnpairedCallback in android.pkg.RegistrationInterface [PairedRegistration] [See https://s.android.com/api-guidelines#callbacks-symmetry]
837                 src/android/pkg/RegistrationMethods.java:8: error: Found registerUnpairedCallback but not unregisterUnpairedCallback in android.pkg.RegistrationMethods [PairedRegistration] [See https://s.android.com/api-guidelines#callbacks-symmetry]
838                 src/android/pkg/RegistrationMethods.java:12: error: Found unregisterMismatchedCallback but not registerMismatchedCallback in android.pkg.RegistrationMethods [PairedRegistration] [See https://s.android.com/api-guidelines#callbacks-symmetry]
839                 src/android/pkg/RegistrationMethods.java:13: error: Callback methods should be named register/unregister; was addCallback [RegistrationName] [See https://s.android.com/api-guidelines#callbacks-accessors]
840                 src/android/pkg/RegistrationMethods.java:18: error: Found addUnpairedListener but not removeUnpairedListener in android.pkg.RegistrationMethods [PairedRegistration] [See https://s.android.com/api-guidelines#callbacks-symmetry]
841                 src/android/pkg/RegistrationMethods.java:19: error: Found removeMismatchedListener but not addMismatchedListener in android.pkg.RegistrationMethods [PairedRegistration] [See https://s.android.com/api-guidelines#callbacks-symmetry]
842                 src/android/pkg/RegistrationMethods.java:20: error: Listener methods should be named add/remove; was registerWrongListener [RegistrationName] [See https://s.android.com/api-guidelines#callbacks-accessors]
843                 """,
844             expectedFail = DefaultLintErrorMessage,
845             sourceFiles = arrayOf(
846                 java(
847                     """
848                     package android.pkg;
849 
850                     import androidx.annotation.Nullable;
851 
852                     public class RegistrationMethods implements RegistrationInterface {
853                         public void registerOkCallback(@Nullable Runnable r) { } // OK
854                         public void unregisterOkCallback(@Nullable Runnable r) { } // OK
855                         public void registerUnpairedCallback(@Nullable Runnable r) { }
856                         // OK here because it is override
857                         @Override
858                         public void registerOverriddenUnpairedCallback(@Nullable Runnable r) { }
859                         public void unregisterMismatchedCallback(@Nullable Runnable r) { }
860                         public void addCallback(@Nullable Runnable r) { }
861 
862                         public void addOkListener(@Nullable Runnable r) { } // OK
863                         public void removeOkListener(@Nullable Runnable r) { } // OK
864 
865                         public void addUnpairedListener(@Nullable Runnable r) { }
866                         public void removeMismatchedListener(@Nullable Runnable r) { }
867                         public void registerWrongListener(@Nullable Runnable r) { }
868                     }
869                     """
870                 ),
871                 java(
872                     """
873                     package android.pkg;
874 
875                     import androidx.annotation.Nullable;
876 
877                     public interface RegistrationInterface {
878                         void registerOverriddenUnpairedCallback(@Nullable Runnable r) { }
879                     }
880                     """
881                 ),
882                 androidxNullableSource
883             )
884         )
885     }
886 
887     @Test
Api methods should not be synchronized in their signaturenull888     fun `Api methods should not be synchronized in their signature`() {
889         check(
890             apiLint = "", // enabled
891             expectedIssues = """
892                 src/android/pkg/CheckSynchronization.java:12: error: Internal locks must not be exposed: method android.pkg.CheckSynchronization.errorMethod1(Runnable) [VisiblySynchronized] [See https://s.android.com/api-guidelines#avoid-synchronized]
893                 src/android/pkg/CheckSynchronization.java:14: error: Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.pkg.CheckSynchronization.errorMethod2() [VisiblySynchronized] [See https://s.android.com/api-guidelines#avoid-synchronized]
894                 src/android/pkg/CheckSynchronization.java:18: error: Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.pkg.CheckSynchronization.errorMethod2() [VisiblySynchronized] [See https://s.android.com/api-guidelines#avoid-synchronized]
895                 src/android/pkg/CheckSynchronization.java:23: error: Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.pkg.CheckSynchronization.errorMethod3() [VisiblySynchronized] [See https://s.android.com/api-guidelines#avoid-synchronized]
896                 src/android/pkg/CheckSynchronization2.kt:5: error: Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.pkg.CheckSynchronization2.errorMethod1() [VisiblySynchronized] [See https://s.android.com/api-guidelines#avoid-synchronized]
897                 src/android/pkg/CheckSynchronization2.kt:8: error: Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.pkg.CheckSynchronization2.errorMethod2() [VisiblySynchronized] [See https://s.android.com/api-guidelines#avoid-synchronized]
898                 src/android/pkg/CheckSynchronization2.kt:13: error: Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.pkg.CheckSynchronization2.errorMethod3() [VisiblySynchronized] [See https://s.android.com/api-guidelines#avoid-synchronized]
899                 src/android/pkg/CheckSynchronization2.kt:16: error: Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.pkg.CheckSynchronization2.errorMethod4() [VisiblySynchronized] [See https://s.android.com/api-guidelines#avoid-synchronized]
900                 src/android/pkg/CheckSynchronization2.kt:18: error: Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.pkg.CheckSynchronization2.errorMethod5() [VisiblySynchronized] [See https://s.android.com/api-guidelines#avoid-synchronized]
901                 """,
902             expectedFail = DefaultLintErrorMessage,
903             sourceFiles = arrayOf(
904                 java(
905                     """
906                     package android.pkg;
907 
908                     import androidx.annotation.Nullable;
909 
910                     public class CheckSynchronization {
911                         public void okMethod1(@Nullable Runnable r) { }
912                         private static final Object LOCK = new Object();
913                         public void okMethod2() {
914                             synchronized(LOCK) {
915                             }
916                         }
917                         public synchronized void errorMethod1(@Nullable Runnable r) { } // ERROR
918                         public void errorMethod2() {
919                             synchronized(this) {
920                             }
921                         }
922                         public void errorMethod2() {
923                             synchronized(CheckSynchronization.class) {
924                             }
925                         }
926                         public void errorMethod3() {
927                             if (true) {
928                                 synchronized(CheckSynchronization.class) {
929                                 }
930                             }
931                         }
932                     }
933                     """
934                 ),
935                 kotlin(
936                     """
937                     package android.pkg
938 
939                     class CheckSynchronization2 {
940                         fun errorMethod1() {
941                             synchronized(this) { println("hello") }
942                         }
943                         fun errorMethod2() {
944                             synchronized(CheckSynchronization2::class.java) { println("hello") }
945                         }
946                         fun errorMethod3() {
947                             @Suppress("ConstantConditionIf")
948                             if (true) {
949                                 synchronized(CheckSynchronization2::class.java) { println("hello") }
950                             }
951                         }
952                         fun errorMethod4() = synchronized(this) { println("hello") }
953                         fun errorMethod5() {
954                             synchronized(CheckSynchronization2::class) { println("hello") }
955                         }
956                         fun okMethod() {
957                             val lock = Object()
958                             synchronized(lock) { println("hello") }
959                         }
960                     }
961                     """
962                 ),
963                 androidxNullableSource,
964                 nullableSource
965             )
966         )
967     }
968 
969     @Test
Check intent builder namesnull970     fun `Check intent builder names`() {
971         check(
972             apiLint = "", // enabled
973             expectedIssues = """
974                 src/android/pkg/IntentBuilderNames.java:9: warning: Methods creating an Intent should be named `create<Foo>Intent()`, was `makeMyIntent` [IntentBuilderName] [See https://s.android.com/api-guidelines#intent-builder-createintent]
975                 src/android/pkg/IntentBuilderNames.java:11: warning: Methods creating an Intent should be named `create<Foo>Intent()`, was `createIntentNow` [IntentBuilderName] [See https://s.android.com/api-guidelines#intent-builder-createintent]
976                 """,
977             sourceFiles = arrayOf(
978                 java(
979                     """
980                     package android.pkg;
981                     import android.content.Intent;
982                     import androidx.annotation.Nullable;
983 
984                     public class IntentBuilderNames {
985                         @Nullable
986                         public Intent createEnrollIntent() { return null; } // OK
987                         @Nullable
988                         public Intent makeMyIntent() { return null; } // WARN
989                         @Nullable
990                         public Intent createIntentNow() { return null; } // WARN
991                     }
992                     """
993                 ),
994                 androidxNullableSource
995             )
996         )
997     }
998 
999     @Test
Check helper classesnull1000     fun `Check helper classes`() {
1001         check(
1002             apiLint = "", // enabled
1003             expectedIssues = """
1004                 src/android/pkg/MyClass1.java:3: error: Inconsistent class name; should be `<Foo>Activity`, was `MyClass1` [ContextNameSuffix] [See https://s.android.com/api-guidelines#classes-subclass-naming]
1005                 src/android/pkg/MyClass1.java:6: warning: Methods implemented by developers should follow the on<Something> style, was `badlyNamedAbstractMethod` [OnNameExpected] [See https://s.android.com/api-guidelines#callback-method-naming]
1006                 src/android/pkg/MyClass1.java:7: warning: If implemented by developer, should follow the on<Something> style; otherwise consider marking final [OnNameExpected] [See https://s.android.com/api-guidelines#callback-method-naming]
1007                 src/android/pkg/MyClass1.java:3: error: MyClass1 should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead. [ForbiddenSuperClass]
1008                 src/android/pkg/MyClass2.java:3: error: Inconsistent class name; should be `<Foo>Provider`, was `MyClass2` [ContextNameSuffix] [See https://s.android.com/api-guidelines#classes-subclass-naming]
1009                 src/android/pkg/MyClass3.java:3: error: Inconsistent class name; should be `<Foo>Service`, was `MyClass3` [ContextNameSuffix] [See https://s.android.com/api-guidelines#classes-subclass-naming]
1010                 src/android/pkg/MyClass4.java:3: error: Inconsistent class name; should be `<Foo>Receiver`, was `MyClass4` [ContextNameSuffix] [See https://s.android.com/api-guidelines#classes-subclass-naming]
1011                 src/android/pkg/MyOkActivity.java:3: error: MyOkActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead. [ForbiddenSuperClass]
1012                 """,
1013             expectedFail = DefaultLintErrorMessage,
1014             sourceFiles = arrayOf(
1015                 java(
1016                     """
1017                     package android.pkg;
1018 
1019                     public class MyClass1 extends android.app.Activity {
1020                         public void onOk() { }
1021                         public final void ok() { }
1022                         public abstract void badlyNamedAbstractMethod();
1023                         public void badlyNamedMethod() { }
1024                         public static void staticOk() { }
1025                     }
1026                     """
1027                 ),
1028                 java(
1029                     """
1030                     package android.pkg;
1031 
1032                     public class MyClass2 extends android.content.ContentProvider {
1033                         public static final String PROVIDER_INTERFACE = "android.pkg.MyClass2";
1034                         public final void ok();
1035                     }
1036                     """
1037                 ),
1038                 java(
1039                     """
1040                     package android.pkg;
1041 
1042                     public class MyClass3 extends android.app.Service {
1043                         public static final String SERVICE_INTERFACE = "android.pkg.MyClass3";
1044                         public final void ok();
1045                     }
1046                     """
1047                 ),
1048                 java(
1049                     """
1050                     package android.pkg;
1051 
1052                     public class MyClass4 extends android.content.BroadcastReceiver {
1053                     }
1054                     """
1055                 ),
1056                 java(
1057                     """
1058                     package android.pkg;
1059 
1060                     public class MyOkActivity extends android.app.Activity {
1061                     }
1062                     """
1063                 )
1064             )
1065         )
1066     }
1067 
1068     @Test
Check buildersnull1069     fun `Check builders`() {
1070         check(
1071             apiLint = "", // enabled
1072             expectedIssues = """
1073                 src/android/pkg/Bad.java:12: warning: Builder must be final: android.pkg.Bad.BadBuilder [StaticFinalBuilder] [See https://s.android.com/api-guidelines#builders-static-inner]
1074                 src/android/pkg/Bad.java:12: warning: Builder must be static: android.pkg.Bad.BadBuilder [StaticFinalBuilder] [See https://s.android.com/api-guidelines#builders-static-inner]
1075                 src/android/pkg/Bad.java:13: warning: Builder constructor arguments must be mandatory (i.e. not @Nullable): parameter badParameter in android.pkg.Bad.BadBuilder(String badParameter) [OptionalBuilderConstructorArgument] [See https://s.android.com/api-guidelines#builders-nonnull-constructors]
1076                 src/android/pkg/Bad.java:37: warning: Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.pkg.Bad.BadBuilder.withBadSetterStyle(boolean) [BuilderSetStyle] [See https://s.android.com/api-guidelines#builder-method-naming]
1077                 src/android/pkg/Bad.java:40: warning: Builder setter must be @NonNull: method android.pkg.Bad.BadBuilder.setReturnsNullable(boolean) [SetterReturnsThis]
1078                 src/android/pkg/Bad.java:42: warning: Getter should be on the built object, not the builder: method android.pkg.Bad.BadBuilder.getOnBuilder() [GetterOnBuilder] [See https://s.android.com/api-guidelines#getter-on-builder]
1079                 src/android/pkg/Bad.java:44: warning: Methods must return the builder object (return type android.pkg.Bad.BadBuilder instead of void): method android.pkg.Bad.BadBuilder.setNotReturningBuilder(boolean) [SetterReturnsThis]
1080                 src/android/pkg/Bad.java:19: warning: android.pkg.Bad does not declare a `getWithoutMatchingGetters()` method matching method android.pkg.Bad.BadBuilder.addWithoutMatchingGetter(String) [MissingGetterMatchingBuilder] [See https://s.android.com/api-guidelines#builders-symmetric-setters]
1081                 src/android/pkg/Bad.java:22: warning: android.pkg.Bad does not declare a `isWithoutMatchingGetter()` method matching method android.pkg.Bad.BadBuilder.setWithoutMatchingGetter(boolean) [MissingGetterMatchingBuilder] [See https://s.android.com/api-guidelines#builders-symmetric-setters]
1082                 src/android/pkg/Bad.java:25: warning: android.pkg.Bad does not declare a `getPluralWithoutMatchingGetters()` method matching method android.pkg.Bad.BadBuilder.addPluralWithoutMatchingGetter(Collection<String>) [MissingGetterMatchingBuilder] [See https://s.android.com/api-guidelines#builders-symmetric-setters]
1083                 src/android/pkg/Bad.java:31: warning: android.pkg.Bad does not declare a getter method matching method android.pkg.Bad.BadBuilder.addPluralWithoutMatchingGetters(Collection<String>) (expected one of: [getPluralWithoutMatchingGetters(), getPluralWithoutMatchingGetterses()]) [MissingGetterMatchingBuilder] [See https://s.android.com/api-guidelines#builders-symmetric-setters]
1084                 src/android/pkg/Bad.java:44: warning: android.pkg.Bad does not declare a `isNotReturningBuilder()` method matching method android.pkg.Bad.BadBuilder.setNotReturningBuilder(boolean) [MissingGetterMatchingBuilder] [See https://s.android.com/api-guidelines#builders-symmetric-setters]
1085                 src/android/pkg/Bad.java:56: warning: Methods must return the builder object (return type android.pkg.Bad.BadGenericBuilder<T> instead of T): method android.pkg.Bad.BadGenericBuilder.setBoolean(boolean) [SetterReturnsThis]
1086                 src/android/pkg/Bad.java:50: warning: android.pkg.Bad.NoBuildMethodBuilder does not declare a `build()` method, but builder classes are expected to [MissingBuildMethod] [See https://s.android.com/api-guidelines#builder-must-declare-build]
1087                 src/android/pkg/TopLevelBuilder.java:3: warning: Builder should be defined as inner class: android.pkg.TopLevelBuilder [TopLevelBuilder] [See https://s.android.com/api-guidelines#builders-static-inner]
1088                 src/android/pkg/TopLevelBuilder.java:3: warning: android.pkg.TopLevelBuilder does not declare a `build()` method, but builder classes are expected to [MissingBuildMethod] [See https://s.android.com/api-guidelines#builder-must-declare-build]
1089                 """,
1090             sourceFiles = arrayOf(
1091                 java(
1092                     """
1093                     package android.pkg;
1094 
1095                     public final class TopLevelBuilder {
1096                     }
1097                     """
1098                 ),
1099                 java(
1100                     """
1101                     package android.pkg;
1102 
1103                     import androidx.annotation.NonNull;
1104                     import androidx.annotation.Nullable;
1105 
1106                     public class Ok {
1107 
1108                         public int getInt();
1109                         @NonNull
1110                         public List<String> getStrings();
1111                         @NonNull
1112                         public List<String> getProperties();
1113                         @NonNull
1114                         public List<String> getRays();
1115                         @NonNull
1116                         public List<String> getBuses();
1117                         @NonNull
1118                         public List<String> getTaxes();
1119                         @NonNull
1120                         public List<String> getMessages();
1121                         public boolean isBoolean();
1122                         public boolean hasBoolean2();
1123                         public boolean shouldBoolean3();
1124 
1125                         public static final class OkBuilder {
1126                             public OkBuilder(@NonNull String goodParameter, int goodParameter2) {}
1127 
1128                             @NonNull
1129                             public Ok build() { return null; }
1130 
1131                             @NonNull
1132                             public OkBuilder setInt(int value) { return this; }
1133 
1134                             @NonNull
1135                             public OkBuilder addString(@NonNull String value) { return this; }
1136 
1137                             @NonNull
1138                             public OkBuilder addProperty(@NonNull String value) { return this; }
1139 
1140                             @NonNull
1141                             public OkBuilder addRay(@NonNull String value) { return this; }
1142 
1143                             @NonNull
1144                             public OkBuilder addBus(@NonNull String value) { return this; }
1145 
1146                             @NonNull
1147                             public OkBuilder addTax(@NonNull String value) { return this; }
1148 
1149                             @NonNull
1150                             public OkBuilder addMessages(@NonNull Collection<String> value) {
1151                                 return this;
1152                             }
1153 
1154                             @NonNull
1155                             public OkBuilder clearStrings() { return this; }
1156 
1157                             @NonNull
1158                             public OkBuilder setBoolean(boolean v) { return this; }
1159 
1160                             @NonNull
1161                             public OkBuilder setHasBoolean2(boolean v) { return this; }
1162 
1163                             @NonNull
1164                             public OkBuilder setShouldBoolean3(boolean v) { return this; }
1165 
1166                             @NonNull
1167                             public OkBuilder clear() { return this; }
1168 
1169                             @NonNull
1170                             public OkBuilder clearAll() { return this; }
1171                         }
1172 
1173                         public static final class GenericBuilder<B extends GenericBuilder> {
1174                             @NonNull
1175                             public B setBoolean(boolean value) { return this; }
1176 
1177                             @NonNull
1178                             public Ok build() { return null; }
1179                         }
1180                     }
1181                     """
1182                 ),
1183                 java(
1184                     """
1185                     package android.pkg;
1186 
1187                     public class SubOk extends Ok {
1188 
1189                         public static final class Builder {
1190                             public Builder() {}
1191 
1192                             @NonNull
1193                             public SubOk build() { return null; }
1194 
1195                             @NonNull
1196                             public Builder setInt(int value) { return this; }
1197                         }
1198                     }
1199                     """
1200                 ),
1201                 java(
1202                     """
1203                     package android.pkg;
1204 
1205                     import androidx.annotation.NonNull;
1206                     import androidx.annotation.Nullable;
1207 
1208                     public class Bad {
1209 
1210                         public boolean isBoolean();
1211                         public boolean getWithoutMatchingGetter();
1212                         public boolean isReturnsNullable();
1213 
1214                         public class BadBuilder {
1215                             public BadBuilder(@Nullable String badParameter) {}
1216 
1217                             @NonNull
1218                             public Bad build() { return null; }
1219 
1220                             @NonNull
1221                             public BadBuilder addWithoutMatchingGetter(@NonNull String value) { return this; }
1222 
1223                             @NonNull
1224                             public BadBuilder setWithoutMatchingGetter(boolean v) { return this; }
1225 
1226                             @NonNull
1227                             public BadBuilder addPluralWithoutMatchingGetter(
1228                                 @NonNull Collection<String> value) {
1229                                 return this;
1230                             }
1231 
1232                             @NonNull
1233                             public BadBuilder addPluralWithoutMatchingGetters(
1234                                 @NonNull Collection<String> value) {
1235                                 return this;
1236                             }
1237 
1238                             @NonNull
1239                             public BadBuilder withBadSetterStyle(boolean v) { return this; }
1240 
1241                             @Nullable
1242                             public BadBuilder setReturnsNullable(boolean v) { return this; }
1243 
1244                             public boolean getOnBuilder() { return true; }
1245 
1246                             public void setNotReturningBuilder(boolean v) { return this; }
1247 
1248                             @NonNull
1249                             public BadBuilder () { return this; }
1250                         }
1251 
1252                         public static final class NoBuildMethodBuilder {
1253                             public NoBuildMethodBuilder() {}
1254                         }
1255 
1256                         public static final class BadGenericBuilder<T extends Bad> {
1257                             @NonNull
1258                             public T setBoolean(boolean value) { return this; }
1259 
1260                             @NonNull
1261                             public Bad build() { return null; }
1262                         }
1263                     }
1264                     """
1265                 ),
1266                 androidxNonNullSource,
1267                 androidxNullableSource
1268             )
1269         )
1270     }
1271 
1272     @Test
Check suppress works on inherited methodsnull1273     fun `Check suppress works on inherited methods`() {
1274         check(
1275             apiLint = "", // enabled
1276             expectedIssues = """
1277                 warning: Should avoid odd sized primitives; use `int` instead of `short` in method android.pkg.Ok.Public.shouldFail(PublicT) [NoByteOrShort] [See https://s.android.com/api-guidelines#avoid-short-byte]
1278                 """,
1279             sourceFiles = arrayOf(
1280                 java(
1281                     """
1282                 package android.pkg;
1283 
1284                 import androidx.annotation.NonNull;
1285 
1286                 public class Ok {
1287 
1288                     static final class Hidden<HiddenT> {
1289                         @SuppressWarnings("NoByteOrShort")
1290                         public short suppressed(HiddenT t) { return null; }
1291 
1292                         public short shouldFail(HiddenT t) { return null; }
1293                     }
1294 
1295                     public static final class Public<PublicT> extends Hidden<PublicT> {
1296                     }
1297                 }
1298                 """
1299                 ),
1300                 androidxNonNullSource
1301             )
1302         )
1303     }
1304 
1305     @Test
Raw AIDLnull1306     fun `Raw AIDL`() {
1307         check(
1308             apiLint = "", // enabled
1309             expectedIssues = """
1310                 src/android/pkg/MyClass1.java:3: error: Raw AIDL interfaces must not be exposed: MyClass1 extends Binder [RawAidl] [See https://s.android.com/api-guidelines#no-public-binder]
1311                 src/android/pkg/MyClass2.java:3: error: Raw AIDL interfaces must not be exposed: MyClass2 implements IInterface [RawAidl] [See https://s.android.com/api-guidelines#no-public-binder]
1312                 """,
1313             expectedFail = DefaultLintErrorMessage,
1314             sourceFiles = arrayOf(
1315                 java(
1316                     """
1317                     package android.pkg;
1318 
1319                     public abstract class MyClass1 extends android.os.Binder {
1320                     }
1321                     """
1322                 ),
1323                 java(
1324                     """
1325                     package android.pkg;
1326 
1327                     public abstract class MyClass2 implements android.os.IInterface {
1328                     }
1329                     """
1330                 ),
1331                 java(
1332                     """
1333                     package android.pkg;
1334                     // Ensure that we don't flag transitively implementing IInterface
1335                     public class MyClass3 extends MyClass1 implements MyClass2 {
1336                     }
1337                     """
1338                 )
1339             )
1340         )
1341     }
1342 
1343     @Test
Internal packagesnull1344     fun `Internal packages`() {
1345         check(
1346             apiLint = "", // enabled
1347             expectedIssues = """
1348                 src/com/android/pkg/MyClass.java:3: error: Internal classes must not be exposed [InternalClasses]
1349                 """,
1350             expectedFail = DefaultLintErrorMessage,
1351             sourceFiles = arrayOf(
1352                 java(
1353                     """
1354                     package com.android.pkg;
1355 
1356                     public class MyClass {
1357                     }
1358                     """
1359                 )
1360             )
1361         )
1362     }
1363 
1364     @Test
Check package layeringnull1365     fun `Check package layering`() {
1366         check(
1367             apiLint = "", // enabled
1368             expectedIssues = """
1369                 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]
1370                 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]
1371                 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]
1372                 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]
1373                 """,
1374             sourceFiles = arrayOf(
1375                 java(
1376                     """
1377                     package android.content;
1378 
1379                     import android.graphics.drawable.Drawable;
1380                     import android.graphics.Bitmap;
1381                     import android.view.View;
1382                     import androidx.annotation.Nullable;
1383 
1384                     public class MyClass1 {
1385                         @Nullable
1386                         public final View view = null;
1387                         @Nullable
1388                         public final Drawable drawable = null;
1389                         @Nullable
1390                         public final Bitmap bitmap = null;
1391                         @Nullable
1392                         public View ok(@Nullable View view, @Nullable Drawable drawable) { return null; }
1393                         @Nullable
1394                         public Bitmap wrong(@Nullable View view, @Nullable Bitmap bitmap) { return null; }
1395                     }
1396                     """
1397                 ),
1398                 androidxNullableSource
1399             )
1400         )
1401     }
1402 
1403     @Test
Check boolean getter and setter naming patternsnull1404     fun `Check boolean getter and setter naming patterns`() {
1405         check(
1406             apiLint = "", // enabled
1407             expectedIssues = """
1408                     src/android/pkg/MyClass.java:21: error: Symmetric method for `isVisibleBad` must be named `setVisibleBad`; was `setIsVisibleBad` [GetterSetterNames]
1409                     src/android/pkg/MyClass.java:24: error: Symmetric method for `hasTransientStateBad` must be named `setHasTransientStateBad`; was `setTransientStateBad` [GetterSetterNames]
1410                     src/android/pkg/MyClass.java:28: error: Symmetric method for `setHasTransientStateAlsoBad` must be named `hasTransientStateAlsoBad`; was `isHasTransientStateAlsoBad` [GetterSetterNames]
1411                     src/android/pkg/MyClass.java:31: error: Symmetric method for `setCanRecordBad` must be named `canRecordBad`; was `isCanRecordBad` [GetterSetterNames]
1412                     src/android/pkg/MyClass.java:34: error: Symmetric method for `setShouldFitWidthBad` must be named `shouldFitWidthBad`; was `isShouldFitWidthBad` [GetterSetterNames]
1413                     src/android/pkg/MyClass.java:37: error: Symmetric method for `setWiFiRoamingSettingEnabledBad` must be named `isWiFiRoamingSettingEnabledBad`; was `getWiFiRoamingSettingEnabledBad` [GetterSetterNames]
1414                     src/android/pkg/MyClass.java:40: error: Symmetric method for `setEnabledBad` must be named `isEnabledBad`; was `getEnabledBad` [GetterSetterNames]
1415                 """,
1416             expectedFail = DefaultLintErrorMessage,
1417             sourceFiles = arrayOf(
1418                 java(
1419                     """
1420                     package android.pkg;
1421 
1422                     public class MyClass {
1423                         // Correct
1424                         public void setVisible(boolean visible) {}
1425                         public boolean isVisible() { return false; }
1426 
1427                         public void setHasTransientState(boolean hasTransientState) {}
1428                         public boolean hasTransientState() { return false; }
1429 
1430                         public void setCanRecord(boolean canRecord) {}
1431                         public boolean canRecord() { return false; }
1432 
1433                         public void setShouldFitWidth(boolean shouldFitWidth) {}
1434                         public boolean shouldFitWidth() { return false; }
1435 
1436                         public void setWiFiRoamingSettingEnabled(boolean enabled) {}
1437                         public boolean isWiFiRoamingSettingEnabled() { return false; }
1438 
1439                         // Bad
1440                         public void setIsVisibleBad(boolean visible) {}
1441                         public boolean isVisibleBad() { return false; }
1442 
1443                         public void setTransientStateBad(boolean hasTransientState) {}
1444                         public boolean hasTransientStateBad() { return false; }
1445 
1446                         public void setHasTransientStateAlsoBad(boolean hasTransientState) {}
1447                         public boolean isHasTransientStateAlsoBad() { return false; }
1448 
1449                         public void setCanRecordBad(boolean canRecord) {}
1450                         public boolean isCanRecordBad() { return false; }
1451 
1452                         public void setShouldFitWidthBad(boolean shouldFitWidth) {}
1453                         public boolean isShouldFitWidthBad() { return false; }
1454 
1455                         public void setWiFiRoamingSettingEnabledBad(boolean enabled) {}
1456                         public boolean getWiFiRoamingSettingEnabledBad() { return false; }
1457 
1458                         public void setEnabledBad(boolean enabled) {}
1459                         public boolean getEnabledBad() { return false; }
1460                     }
1461                     """
1462                 )
1463             )
1464         )
1465     }
1466 
1467     @Test
Check banned collectionsnull1468     fun `Check banned collections`() {
1469         check(
1470             apiLint = "", // enabled
1471             expectedIssues = """
1472                 src/android/pkg/MyClass.java:6: error: Parameter type is concrete collection (`java.util.HashMap`); must be higher-level interface [ConcreteCollection] [See https://s.android.com/api-guidelines#classes-collections]
1473                 src/android/pkg/MyClass.java:10: error: Return type is concrete collection (`java.util.Vector`); must be higher-level interface [ConcreteCollection] [See https://s.android.com/api-guidelines#classes-collections]
1474                 src/android/pkg/MyClass.java:10: error: Parameter type is concrete collection (`java.util.LinkedList`); must be higher-level interface [ConcreteCollection] [See https://s.android.com/api-guidelines#classes-collections]
1475                 """,
1476             expectedFail = DefaultLintErrorMessage,
1477             sourceFiles = arrayOf(
1478                 java(
1479                     """
1480                     package android.pkg;
1481 
1482                     import androidx.annotation.NonNull;
1483 
1484                     public class MyClass {
1485                         public MyClass(@NonNull java.util.HashMap<String,String> map1,
1486                                 @NonNull java.util.Map<String,String> map2) {
1487                         }
1488                         @NonNull
1489                         public java.util.Vector<String> getList(@NonNull java.util.LinkedList<String> list) {
1490                             throw new RuntimeException();
1491                         }
1492                     }
1493                     """
1494                 ),
1495                 androidxNonNullSource
1496             )
1497         )
1498     }
1499 
1500     @Test
Check nullable collectionsnull1501     fun `Check nullable collections`() {
1502         check(
1503             apiLint = "", // enabled
1504             expectedIssues = """
1505                 src/android/pkg/MySubClass.java:5: warning: Public class android.pkg.MySubClass stripped of unavailable superclass android.pkg.MyHiddenInterface [HiddenSuperclass]
1506                 src/android/pkg/MyCallback.java:4: warning: Type of parameter list in android.pkg.MyCallback.onFoo(java.util.List<java.lang.String> list) is a nullable collection (`java.util.List`); must be non-null [NullableCollection] [See https://s.android.com/api-guidelines#methods-prefer-non-null-collections]
1507                 src/android/pkg/MyClass.java:9: warning: Return type of method android.pkg.MyClass.getList(java.util.List<java.lang.String>) is a nullable collection (`java.util.List`); must be non-null [NullableCollection] [See https://s.android.com/api-guidelines#methods-prefer-non-null-collections]
1508                 src/android/pkg/MyClass.java:13: warning: Type of field android.pkg.MyClass.STRINGS is a nullable collection (`java.lang.String[]`); must be non-null [NullableCollection] [See https://s.android.com/api-guidelines#methods-prefer-non-null-collections]
1509                 src/android/pkg/MySubClass.java:14: warning: Return type of method android.pkg.MySubClass.getOtherList(java.util.List<java.lang.String>) is a nullable collection (`java.util.List`); must be non-null [NullableCollection] [See https://s.android.com/api-guidelines#methods-prefer-non-null-collections]
1510                 """,
1511             sourceFiles = arrayOf(
1512                 java(
1513                     """
1514                     package android.pkg;
1515 
1516                     import androidx.annotation.Nullable;
1517 
1518                     public class MyClass {
1519                         public MyClass() { }
1520 
1521                         @Nullable
1522                         public java.util.List<String> getList(@Nullable java.util.List<String> list) {
1523                             return null;
1524                         }
1525                         @Nullable
1526                         public static final String[] STRINGS = null;
1527 
1528                         /** @deprecated don't use this. */
1529                         @Deprecated
1530                         @Nullable
1531                         public String[] ignoredBecauseDeprecated(@Nullable String[] ignored) {
1532                             return null;
1533                         }
1534 
1535                         protected MyClass() {
1536                         }
1537                     }
1538                     """
1539                 ),
1540                 java(
1541                     """
1542                     package android.pkg;
1543 
1544                     import androidx.annotation.Nullable;
1545 
1546                     /** @hide */
1547                     public interface MyHiddenInterface {
1548                         @Nullable
1549                         java.util.List<String> getOtherList(@Nullable java.util.List<String> list);
1550                     }
1551                     """
1552                 ),
1553                 java(
1554                     """
1555                     package android.pkg;
1556 
1557                     import androidx.annotation.Nullable;
1558 
1559                     public class MySubClass extends MyClass implements MyHiddenInterface {
1560                         @Nullable
1561                         public java.util.List<String> getList(@Nullable java.util.List<String> list) {
1562                             // Ignored because it has the same nullability as its super method
1563                             return null;
1564                         }
1565 
1566                         @Override
1567                         @Nullable
1568                         public java.util.List<String> getOtherList(@Nullable java.util.List<String> list) {
1569                             // Reported because the super method is hidden.
1570                             return null;
1571                         }
1572                     }
1573                     """
1574                 ),
1575                 java(
1576                     """
1577                     package android.pkg;
1578 
1579                     public class MyCallback {
1580                         public void onFoo(@Nullable java.util.List<String> list) {
1581                         }
1582                     }
1583                     """
1584                 ),
1585                 androidxNullableSource
1586             )
1587         )
1588     }
1589 
1590     @Test
Check non-overlapping flagsnull1591     fun `Check non-overlapping flags`() {
1592         check(
1593             apiLint = "", // enabled
1594             expectedIssues = """
1595                 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] [See https://s.android.com/api-guidelines#overlapping-constants]
1596                 """,
1597             sourceFiles = arrayOf(
1598                 java(
1599                     """
1600                     package android.accounts;
1601 
1602                     public class OverlappingFlags {
1603                         private OverlappingFlags() { }
1604                         public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80
1605                         public static final int DRAG_FLAG_GLOBAL_URI_READ = 1; // 0x1
1606                         public static final int DRAG_FLAG_GLOBAL_URI_WRITE = 2; // 0x2
1607                         public static final int DRAG_FLAG_OPAQUE = 512; // 0x200
1608                         public static final int SYSTEM_UI_FLAG_FULLSCREEN = 4; // 0x4
1609                         public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2; // 0x2
1610                         public static final int SYSTEM_UI_FLAG_IMMERSIVE = 2048; // 0x800
1611                         public static final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 4096; // 0x1000
1612                         public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024; // 0x400
1613                         public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512; // 0x200
1614                         public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 256; // 0x100
1615                         public static final int SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR = 16; // 0x10
1616 
1617                         public static final int TEST1_FLAG_FIRST = 1;
1618                         public static final int TEST1_FLAG_SECOND = 3;
1619                         public static final int TEST2_FLAG_FIRST = 5;
1620                     }
1621                     """
1622                 )
1623             )
1624         )
1625     }
1626 
1627     @Test
Check exception related issuesnull1628     fun `Check exception related issues`() {
1629         check(
1630             extraArguments = arrayOf(
1631                 ARG_API_LINT,
1632                 // Conflicting advice:
1633                 ARG_HIDE, "BannedThrow"
1634             ),
1635             expectedIssues = """
1636                 src/android/pkg/MyClass.java:6: error: Methods must not throw generic exceptions (`java.lang.Exception`) [GenericException] [See https://s.android.com/api-guidelines#appropriate-exception]
1637                 src/android/pkg/MyClass.java:7: error: Methods must not throw generic exceptions (`java.lang.Throwable`) [GenericException] [See https://s.android.com/api-guidelines#appropriate-exception]
1638                 src/android/pkg/MyClass.java:8: error: Methods must not throw generic exceptions (`java.lang.Error`) [GenericException] [See https://s.android.com/api-guidelines#appropriate-exception]
1639                 src/android/pkg/MyClass.java:11: error: Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause) [RethrowRemoteException] [See https://s.android.com/api-guidelines#appropriate-exception]
1640                 """,
1641             expectedFail = DefaultLintErrorMessage,
1642             sourceFiles = arrayOf(
1643                 java(
1644                     """
1645                     package android.pkg;
1646                     import android.os.RemoteException;
1647 
1648                     @SuppressWarnings("RedundantThrows")
1649                     public class MyClass {
1650                         public void method1() throws Exception { }
1651                         public void method2() throws Throwable { }
1652                         public void method3() throws Error { }
1653                         public void method4() throws IllegalArgumentException { }
1654                         public void method4() throws NullPointerException { }
1655                         public void method5() throws RemoteException { }
1656                         public void ok(int p) throws NullPointerException { }
1657                     }
1658                     """
1659                 )
1660             )
1661         )
1662     }
1663 
1664     @Test
Check no mentions of Google in APIsnull1665     fun `Check no mentions of Google in APIs`() {
1666         check(
1667             apiLint = "", // enabled
1668             expectedIssues = """
1669                 src/android/pkg/MyClass.java:4: error: Must never reference Google (`MyGoogleService`) [MentionsGoogle] [See https://s.android.com/api-guidelines#mentions-google]
1670                 src/android/pkg/MyClass.java:5: error: Must never reference Google (`callGoogle`) [MentionsGoogle] [See https://s.android.com/api-guidelines#mentions-google]
1671                 """,
1672             expectedFail = DefaultLintErrorMessage,
1673             sourceFiles = arrayOf(
1674                 java(
1675                     """
1676                     package android.pkg;
1677 
1678                     public class MyClass {
1679                         public static class MyGoogleService {
1680                             public void callGoogle() { }
1681                         }
1682                     }
1683                     """
1684                 )
1685             )
1686         )
1687     }
1688 
1689     @Test
Check no usages of heavy BitSetnull1690     fun `Check no usages of heavy BitSet`() {
1691         check(
1692             apiLint = "", // enabled
1693             expectedIssues = """
1694                 src/android/pkg/MyClass.java:9: error: Type must not be heavy BitSet (method android.pkg.MyClass.reverse(java.util.BitSet)) [HeavyBitSet] [See https://s.android.com/api-guidelines#avoid-bitset]
1695                 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] [See https://s.android.com/api-guidelines#avoid-bitset]
1696                 src/android/pkg/MyClass.java:7: error: Type must not be heavy BitSet (field android.pkg.MyClass.bitset) [HeavyBitSet] [See https://s.android.com/api-guidelines#avoid-bitset]
1697                 """,
1698             expectedFail = DefaultLintErrorMessage,
1699             sourceFiles = arrayOf(
1700                 java(
1701                     """
1702                     package android.pkg;
1703                     import androidx.annotation.Nullable;
1704                     import java.util.BitSet;
1705 
1706                     public class MyClass {
1707                         @Nullable
1708                         public final BitSet bitset;
1709                         @Nullable
1710                         public BitSet reverse(@Nullable BitSet bitset) { return null; }
1711                     }
1712                     """
1713                 ),
1714                 androidxNullableSource
1715             )
1716         )
1717     }
1718 
1719     @Test
Check Manager related issuesnull1720     fun `Check Manager related issues`() {
1721         check(
1722             apiLint = "", // enabled
1723             expectedIssues = """
1724                 src/android/pkg/MyFirstManager.java:6: error: Managers must always be obtained from Context; no direct constructors [ManagerConstructor]
1725                 src/android/pkg/MyFirstManager.java:9: error: Managers must always be obtained from Context (`get`) [ManagerLookup]
1726                 """,
1727             expectedFail = DefaultLintErrorMessage,
1728             sourceFiles = arrayOf(
1729                 java(
1730                     """
1731                     package android.pkg;
1732 
1733                     import androidx.annotation.Nullable;
1734 
1735                     public class MyFirstManager {
1736                         public MyFirstManager() {
1737                         }
1738                         @Nullable
1739                         public MyFirstManager get() { return null; }
1740                         @Nullable
1741                         public MySecondManager ok() { return null; }
1742                     }
1743                     """
1744                 ),
1745                 java(
1746                     """
1747                     package android.pkg;
1748 
1749                     public class MySecondManager {
1750                         private MySecondManager() {
1751                         }
1752                     }
1753                     """
1754                 ),
1755                 androidxNullableSource
1756             )
1757         )
1758     }
1759 
1760     @Test
Check boxed typesnull1761     fun `Check boxed types`() {
1762         check(
1763             apiLint = "", // enabled
1764             expectedIssues = """
1765                 src/test/pkg/KotlinClass.kt:4: error: Must avoid boxed primitives (`java.lang.Double`) [AutoBoxing] [See https://s.android.com/api-guidelines#auto-boxing]
1766                 src/test/pkg/KotlinClass.kt:6: error: Must avoid boxed primitives (`java.lang.Boolean`) [AutoBoxing] [See https://s.android.com/api-guidelines#auto-boxing]
1767                 src/test/pkg/MyClass.java:9: error: Must avoid boxed primitives (`java.lang.Long`) [AutoBoxing] [See https://s.android.com/api-guidelines#auto-boxing]
1768                 src/test/pkg/MyClass.java:12: error: Must avoid boxed primitives (`java.lang.Short`) [AutoBoxing] [See https://s.android.com/api-guidelines#auto-boxing]
1769                 src/test/pkg/MyClass.java:12: error: Must avoid boxed primitives (`java.lang.Double`) [AutoBoxing] [See https://s.android.com/api-guidelines#auto-boxing]
1770                 src/test/pkg/MyClass.java:14: error: Must avoid boxed primitives (`java.lang.Boolean`) [AutoBoxing] [See https://s.android.com/api-guidelines#auto-boxing]
1771                 src/test/pkg/MyClass.java:7: error: Must avoid boxed primitives (`java.lang.Integer`) [AutoBoxing] [See https://s.android.com/api-guidelines#auto-boxing]
1772                 """,
1773             expectedFail = DefaultLintErrorMessage,
1774             sourceFiles = arrayOf(
1775                 java(
1776                     """
1777                     package test.pkg;
1778 
1779                     import androidx.annotation.Nullable;
1780 
1781                     public class MyClass {
1782                         @Nullable
1783                         public final Integer integer1;
1784                         public final int integer2;
1785                         public MyClass(@Nullable Long l) {
1786                         }
1787                         @Nullable
1788                         public Short getDouble(@Nullable Double l) { return null; }
1789                         @Nullable
1790                         public Boolean getBoolean() { return null; }
1791                     }
1792                     """
1793                 ),
1794                 kotlin(
1795                     """
1796                     package test.pkg
1797                     class KotlinClass {
1798                         fun getIntegerOk(): Double { TODO() }
1799                         fun getIntegerBad(): Double? { TODO() }
1800                         fun getBooleanOk(): Boolean { TODO() }
1801                         fun getBooleanBad(): Boolean? { TODO() }
1802                     }
1803                 """
1804                 ),
1805                 androidxNullableSource
1806             )
1807         )
1808     }
1809 
1810     @Test
Check static utilitiesnull1811     fun `Check static utilities`() {
1812         check(
1813             apiLint = "", // enabled
1814             expectedIssues = """
1815                 src/android/pkg/MyUtils1.java:3: error: Fully-static utility classes must not have constructor [StaticUtils]
1816                 src/android/pkg/MyUtils2.java:3: error: Fully-static utility classes must not have constructor [StaticUtils]
1817                 """,
1818             expectedFail = DefaultLintErrorMessage,
1819             sourceFiles = arrayOf(
1820                 java(
1821                     """
1822                     package android.pkg;
1823 
1824                     public class MyUtils1 {
1825                         // implicit constructor
1826                         public static void foo() { }
1827                     }
1828                     """
1829                 ),
1830                 java(
1831                     """
1832                     package android.pkg;
1833 
1834                     public class MyUtils2 {
1835                         public MyUtils2() { }
1836                         public static void foo() { }
1837                     }
1838                     """
1839                 ),
1840                 java(
1841                     """
1842                     package android.pkg;
1843 
1844                     public class MyUtils3 {
1845                         private MyUtils3() { }
1846                         public static void foo() { }
1847                     }
1848                     """
1849                 ),
1850                 java(
1851                     """
1852                     package android.pkg;
1853 
1854                     public class MyUtils4 {
1855                         // OK: instance method
1856                         public void foo() { }
1857                     }
1858                     """
1859                 ),
1860                 java(
1861                     """
1862                     package android.pkg;
1863 
1864                     public class MyUtils5 {
1865                         // OK: instance field
1866                         public final int foo = 42;
1867                         public static void foo() { }
1868                     }
1869                     """
1870                 )
1871             )
1872         )
1873     }
1874 
1875     @Test
Check context firstnull1876     fun `Check context first`() {
1877         check(
1878             apiLint = "", // enabled
1879             expectedIssues = """
1880                 src/android/pkg/MyClass.java:11: error: Context is distinct, so it must be the first argument (method `wrong`) [ContextFirst]
1881                 src/android/pkg/MyClass.java:12: error: ContentResolver is distinct, so it must be the first argument (method `wrong`) [ContextFirst]
1882                 """,
1883             expectedFail = DefaultLintErrorMessage,
1884             sourceFiles = arrayOf(
1885                 java(
1886                     """
1887                     package android.pkg;
1888                     import android.content.Context;
1889                     import android.content.ContentResolver;
1890                     import androidx.annotation.Nullable;
1891 
1892                     public class MyClass {
1893                         public MyClass(@Nullable Context context1, @Nullable Context context2) {
1894                         }
1895                         public void ok(@Nullable ContentResolver resolver, int i) { }
1896                         public void ok(@Nullable Context context, int i) { }
1897                         public void wrong(int i, @Nullable Context context) { }
1898                         public void wrong(int i, @Nullable ContentResolver resolver) { }
1899                     }
1900                     """
1901                 ),
1902                 androidxNullableSource
1903             )
1904         )
1905     }
1906 
1907     @Test
Check listener lastnull1908     fun `Check listener last`() {
1909         check(
1910             extraArguments = arrayOf(ARG_API_LINT, ARG_HIDE, "ExecutorRegistration"),
1911             expectedIssues = """
1912                 src/android/pkg/MyClass.java:7: warning: Listeners should always be at end of argument list (method `MyClass`) [ListenerLast] [See https://s.android.com/api-guidelines#placement-of-sam-parameters]
1913                 src/android/pkg/MyClass.java:10: warning: Listeners should always be at end of argument list (method `wrong`) [ListenerLast] [See https://s.android.com/api-guidelines#placement-of-sam-parameters]
1914                 """,
1915             sourceFiles = arrayOf(
1916                 java(
1917                     """
1918                     package android.pkg;
1919                     import android.pkg.MyCallback;
1920                     import android.content.Context;
1921                     import androidx.annotation.Nullable;
1922 
1923                     public class MyClass {
1924                         public MyClass(@Nullable MyCallback listener, int i) {
1925                         }
1926                         public void ok(@Nullable Context context, int i, @Nullable MyCallback listener) { }
1927                         public void wrong(@Nullable MyCallback listener, int i) { }
1928                     }
1929                     """
1930                 ),
1931                 java(
1932                     """
1933                     package android.pkg;
1934 
1935                     @SuppressWarnings("WeakerAccess")
1936                     public abstract class MyCallback {
1937                     }
1938                     """
1939                 ),
1940                 androidxNullableSource
1941             )
1942         )
1943     }
1944 
1945     @Test
Check overloaded argumentsnull1946     fun `Check overloaded arguments`() {
1947         // TODO: This check is not yet hooked up
1948         check(
1949             apiLint = "", // enabled
1950             expectedIssues = """
1951                 """,
1952             sourceFiles = arrayOf(
1953                 java(
1954                     """
1955                     package android.pkg;
1956 
1957                     public class MyClass {
1958                         private MyClass() {
1959                         }
1960 
1961                         public void name1() { }
1962                         public void name1(int i) { }
1963                         public void name1(int i, int j) { }
1964                         public void name1(int i, int j, int k) { }
1965                         public void name1(int i, int j, int k, float f) { }
1966 
1967                         public void name2(int i) { }
1968                         public void name2(int i, int j) { }
1969                         public void name2(int i, float j, float k) { }
1970                         public void name2(int i, int j, int k, float f) { }
1971                         public void name2(int i, float f, int j) { }
1972 
1973                         public void name3() { }
1974                         public void name3(int i) { }
1975                         public void name3(int i, int j) { }
1976                         public void name3(int i, float j, int k) { }
1977 
1978                         public void name4(int i, int j, float f, long z) { }
1979                         public void name4(double d, int i, int j, float f) { }
1980                     }
1981                     """
1982                 )
1983             )
1984         )
1985     }
1986 
1987     @Test
Check Callback Handlersnull1988     fun `Check Callback Handlers`() {
1989         check(
1990             apiLint = "", // enabled
1991             expectedIssues = """
1992                 src/android/pkg/MyClass.java:16: warning: Registration methods should have overload that accepts delivery Executor: `registerWrongCallback` [ExecutorRegistration] [See https://s.android.com/api-guidelines#callbacks-listener]
1993                 src/android/pkg/MyClass.java:6: warning: Registration methods should have overload that accepts delivery Executor: `MyClass` [ExecutorRegistration] [See https://s.android.com/api-guidelines#callbacks-listener]
1994                 """,
1995             sourceFiles = arrayOf(
1996                 java(
1997                     """
1998                     package android.pkg;
1999 
2000                     import androidx.annotation.Nullable;
2001 
2002                     public class MyClass {
2003                         public MyClass(@Nullable MyCallback callback) {
2004                         }
2005 
2006                         public void registerStreamEventCallback(@Nullable MyCallback callback);
2007                         public void unregisterStreamEventCallback(@Nullable MyCallback callback);
2008                         public void registerStreamEventCallback(@Nullable java.util.concurrent.Executor executor,
2009                                 @Nullable MyCallback callback);
2010                         public void unregisterStreamEventCallback(@Nullable java.util.concurrent.Executor executor,
2011                                 @Nullable MyCallback callback);
2012 
2013                         public void registerWrongCallback(@Nullable MyCallback callback);
2014                         public void unregisterWrongCallback(@Nullable MyCallback callback);
2015                     }
2016                     """
2017                 ),
2018                 java(
2019                     """
2020                     package android.graphics;
2021 
2022                     import android.pkg.MyCallback;
2023                     import androidx.annotation.Nullable;
2024 
2025                     public class MyUiClass {
2026                         public void registerWrongCallback(@Nullable MyCallback callback);
2027                         public void unregisterWrongCallback(@Nullable MyCallback callback);
2028                     }
2029                     """
2030                 ),
2031                 java(
2032                     """
2033                     package android.pkg;
2034 
2035                     @SuppressWarnings("WeakerAccess")
2036                     public abstract class MyCallback {
2037                     }
2038                     """
2039                 ),
2040                 androidxNullableSource
2041             )
2042         )
2043     }
2044 
2045     @Test
Check resource namesnull2046     fun `Check resource names`() {
2047         check(
2048             apiLint = "", // enabled
2049             expectedIssues = """
2050                 src/android/R.java:11: error: Expected resource name in `android.R.id` to be in the `fooBarBaz` style, was `wrong_style` [ResourceValueFieldName]
2051                 src/android/R.java:17: error: Expected config name to be in the `config_fooBarBaz` style, was `config_wrong_config_style` [ConfigFieldName]
2052                 src/android/R.java:20: error: Expected resource name in `android.R.layout` to be in the `foo_bar_baz` style, was `wrongNameStyle` [ResourceFieldName]
2053                 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]
2054                 """,
2055             expectedFail = DefaultLintErrorMessage,
2056             sourceFiles = arrayOf(
2057                 java(
2058                     """
2059                     package android;
2060 
2061                     public final class R {
2062                         public static final class id {
2063                             public static final int text = 7000;
2064                             public static final int config_fooBar = 7001;
2065                             public static final int layout_fooBar = 7002;
2066                             public static final int state_foo = 7003;
2067                             public static final int foo = 7004;
2068                             public static final int fooBar = 7005;
2069                             public static final int wrong_style = 7006;
2070                         }
2071                         public static final class layout {
2072                             public static final int text = 7000;
2073                             public static final int config_fooBar = 7001;
2074                             public static final int config_foo = 7002;
2075                             public static final int config_wrong_config_style = 7003;
2076 
2077                             public static final int ok_name_style = 7004;
2078                             public static final int wrongNameStyle = 7005;
2079                         }
2080                         public static final class style {
2081                             public static final int TextAppearance_Compat_Notification = 0x7f0c00ec;
2082                             public static final int TextAppearance_Compat_Notification_Info = 0x7f0c00ed;
2083                             public static final int TextAppearance_Compat_Notification_Line2 = 0x7f0c00ef;
2084                             public static final int TextAppearance_Compat_Notification_Time = 0x7f0c00f2;
2085                             public static final int TextAppearance_Compat_Notification_Title = 0x7f0c00f4;
2086                             public static final int Widget_Compat_NotificationActionContainer = 0x7f0c015d;
2087                             public static final int Widget_Compat_NotificationActionText = 0x7f0c015e;
2088 
2089                             public static final int wrong_style_name = 7000;
2090                         }
2091                     }
2092                     """
2093                 )
2094             )
2095         )
2096     }
2097 
2098     @Test
Check filesnull2099     fun `Check files`() {
2100         check(
2101             apiLint = "", // enabled
2102             expectedIssues = """
2103                 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]
2104                 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]
2105                 """,
2106             sourceFiles = arrayOf(
2107                 java(
2108                     """
2109                     package android.pkg;
2110                     import android.content.Context;
2111                     import android.content.ContentResolver;
2112                     import androidx.annotation.Nullable;
2113                     import java.io.File;
2114                     import java.io.InputStream;
2115 
2116                     public class CheckFiles {
2117                         public CheckFiles(@Nullable Context context, @Nullable File file) {
2118                         }
2119                         public void ok(int i, @Nullable File file) { }
2120                         public void ok(int i, @Nullable InputStream stream) { }
2121                         public void error(int i, @Nullable File file) { }
2122                     }
2123                     """
2124                 ),
2125                 androidxNullableSource
2126             )
2127         )
2128     }
2129 
2130     @Test
Check parcelable listsnull2131     fun `Check parcelable lists`() {
2132         check(
2133             apiLint = "", // enabled
2134             expectedIssues = """
2135                 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]
2136                 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]
2137                 """,
2138             sourceFiles = arrayOf(
2139                 java(
2140                     """
2141                     package android.pkg;
2142                     import android.content.Context;
2143                     import android.content.ContentResolver;
2144                     import androidx.annotation.Nullable;
2145                     import java.io.File;
2146                     import java.io.InputStream;
2147 
2148                     public class CheckFiles {
2149                         public CheckFiles(@Nullable Context context, @Nullable File file) {
2150                         }
2151                         public void ok(int i, @Nullable File file) { }
2152                         public void ok(int i, @Nullable InputStream stream) { }
2153                         public void error(int i, @Nullable File file) { }
2154                     }
2155                     """
2156                 ),
2157                 androidxNullableSource
2158             )
2159         )
2160     }
2161 
2162     @Test
Check abstract innernull2163     fun `Check abstract inner`() {
2164         check(
2165             apiLint = "", // enabled
2166             expectedIssues = """
2167                 src/android/pkg/MyManager.java:9: warning: Abstract inner classes should be static to improve testability: class android.pkg.MyManager.MyInnerManager [AbstractInner]
2168                 """,
2169             sourceFiles = arrayOf(
2170                 java(
2171                     """
2172                     package android.pkg;
2173                     import android.content.Context;
2174                     import android.content.ContentResolver;
2175                     import java.io.File;
2176                     import java.io.InputStream;
2177 
2178                     public abstract class MyManager {
2179                          private MyManager() {}
2180                          public abstract class MyInnerManager {
2181                              private MyInnerManager() {}
2182                          }
2183                          public abstract static class MyOkManager {
2184                              private MyOkManager() {}
2185                          }
2186                     }
2187                     """
2188                 )
2189             )
2190         )
2191     }
2192 
2193     @Test
Check for extending errorsnull2194     fun `Check for extending errors`() {
2195         check(
2196             apiLint = "", // enabled
2197             expectedIssues = """
2198                 src/android/pkg/MyClass.java:3: error: Trouble must be reported through an `Exception`, not an `Error` (`MyClass` extends `Error`) [ExtendsError]
2199                 src/android/pkg/MySomething.java:3: error: Exceptions must be named `FooException`, was `MySomething` [ExceptionName]
2200                 """,
2201             expectedFail = DefaultLintErrorMessage,
2202             sourceFiles = arrayOf(
2203                 java(
2204                     """
2205                     package android.pkg;
2206 
2207                     public class MyClass extends Error {
2208                     }
2209                     """
2210                 ),
2211                 java(
2212                     """
2213                     package android.pkg;
2214 
2215                     public class MySomething extends RuntimeException {
2216                     }
2217                     """
2218                 )
2219             )
2220         )
2221     }
2222 
2223     @Test
Check units and method namesnull2224     fun `Check units and method names`() {
2225         check(
2226             extraArguments = arrayOf(ARG_API_LINT, ARG_HIDE, "NoByteOrShort"),
2227             expectedIssues = """
2228                 src/android/pkg/UnitNameTest.java:7: error: Expected method name units to be `Hours`, was `Hr` in `getErrorHr` [MethodNameUnits] [See https://s.android.com/api-guidelines#unit-names]
2229                 src/android/pkg/UnitNameTest.java:8: error: Expected method name units to be `Nanos`, was `Ns` in `getErrorNs` [MethodNameUnits] [See https://s.android.com/api-guidelines#unit-names]
2230                 src/android/pkg/UnitNameTest.java:9: error: Expected method name units to be `Bytes`, was `Byte` in `getErrorByte` [MethodNameUnits] [See https://s.android.com/api-guidelines#unit-names]
2231                 src/android/pkg/UnitNameTest.java:14: error: Fractions must use floats, was `int` in `getErrorFraction` [FractionFloat]
2232                 src/android/pkg/UnitNameTest.java:15: error: Fractions must use floats, was `int` in `setErrorFraction` [FractionFloat]
2233                 src/android/pkg/UnitNameTest.java:19: error: Percentage must use ints, was `float` in `getErrorPercentage` [PercentageInt]
2234                 src/android/pkg/UnitNameTest.java:20: error: Percentage must use ints, was `float` in `setErrorPercentage` [PercentageInt]
2235                 src/android/pkg/UnitNameTest.java:22: error: Expected method name units to be `Bytes`, was `Byte` in `readSingleByte` [MethodNameUnits] [See https://s.android.com/api-guidelines#unit-names]
2236                 """,
2237             expectedFail = DefaultLintErrorMessage,
2238             sourceFiles = arrayOf(
2239                 androidxNonNullSource,
2240                 java(
2241                     """
2242                     package android.pkg;
2243 
2244                     import androidx.annotation.NonNull;
2245 
2246                     public class UnitNameTest {
2247                         public int okay() { return 0; }
2248                         public int getErrorHr() { return 0; }
2249                         public int getErrorNs() { return 0; }
2250                         public short getErrorByte() { return (short)0; }
2251 
2252                         public float getOkFraction() { return 0f; }
2253                         public void setOkFraction(float f) { }
2254                         public void setOkFraction(int n, int d) { }
2255                         public int getErrorFraction() { return 0; }
2256                         public void setErrorFraction(int i) { }
2257 
2258                         public int getOkPercentage() { return 0f; }
2259                         public void setOkPercentage(int i) { }
2260                         public float getErrorPercentage() { return 0f; }
2261                         public void setErrorPercentage(float f) { }
2262 
2263                         public int readSingleByte() { return 0; }
2264 
2265                         public static final class UnitNameTestBuilder {
2266                             public UnitNameTestBuilder() {}
2267 
2268                             @NonNull
2269                             public UnitNameTest build() { return null; }
2270 
2271                             @NonNull
2272                             public UnitNameTestBuilder setOkFraction(float f) { return this; }
2273 
2274                             @NonNull
2275                             public UnitNameTestBuilder setOkPercentage(int i) { return this; }
2276                         }
2277                     }
2278                     """
2279                 )
2280             )
2281         )
2282     }
2283 
2284     @Test
Check closeablenull2285     fun `Check closeable`() {
2286         check(
2287             apiLint = "", // enabled
2288             expectedIssues = """
2289                 src/android/pkg/MyErrorClass1.java:3: warning: Classes that release resources (close()) should implement AutoClosable and CloseGuard: class android.pkg.MyErrorClass1 [NotCloseable]
2290                 src/android/pkg/MyErrorClass2.java:3: warning: Classes that release resources (finalize(), shutdown()) should implement AutoClosable and CloseGuard: class android.pkg.MyErrorClass2 [NotCloseable]
2291                 """,
2292             sourceFiles = arrayOf(
2293                 java(
2294                     """
2295                     package android.pkg;
2296 
2297                     public abstract class MyOkClass1 implements java.io.Closeable {
2298                         public void close() {}
2299                     }
2300                     """
2301                 ),
2302                 java(
2303                     """
2304                     package android.pkg;
2305 
2306                     // Ok: indirectly implementing AutoCloseable
2307                     public abstract class MyOkClass2 implements MyInterface {
2308                         public void close() {}
2309                     }
2310                     """
2311                 ),
2312                 java(
2313                     """
2314                     package android.pkg;
2315 
2316                     public class MyInterface extends AutoCloseable {
2317                         public void close() {}
2318                     }
2319                     """
2320                 ),
2321                 java(
2322                     """
2323                     package android.pkg;
2324 
2325                     public abstract class MyErrorClass1 {
2326                         public void close() {}
2327                     }
2328                     """
2329                 ),
2330                 java(
2331                     """
2332                     package android.pkg;
2333 
2334                     public abstract class MyErrorClass2 {
2335                         public void finalize() {}
2336                         public void shutdown() {}
2337                     }
2338                     """
2339                 )
2340             )
2341         )
2342     }
2343 
2344     @Test
Check closeable for minSdkVersion 19null2345     fun `Check closeable for minSdkVersion 19`() {
2346         check(
2347             apiLint = "", // enabled
2348             expectedIssues = """
2349                 src/android/pkg/MyErrorClass1.java:3: warning: Classes that release resources (close()) should implement AutoClosable and CloseGuard: class android.pkg.MyErrorClass1 [NotCloseable]
2350                 src/android/pkg/MyErrorClass2.java:3: warning: Classes that release resources (finalize(), shutdown()) should implement AutoClosable and CloseGuard: class android.pkg.MyErrorClass2 [NotCloseable]
2351             """,
2352             manifest = """<?xml version="1.0" encoding="UTF-8"?>
2353                 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2354                     <uses-sdk android:minSdkVersion="19" />
2355                 </manifest>
2356             """.trimIndent(),
2357             sourceFiles = arrayOf(
2358                 java(
2359                     """
2360                     package android.pkg;
2361 
2362                     public abstract class MyErrorClass1 {
2363                         public void close() {}
2364                     }
2365                     """
2366                 ),
2367                 java(
2368                     """
2369                     package android.pkg;
2370 
2371                     public abstract class MyErrorClass2 {
2372                         public void finalize() {}
2373                         public void shutdown() {}
2374                     }
2375                     """
2376                 )
2377             )
2378         )
2379     }
2380 
2381     @Test
Do not check closeable for minSdkVersion less than 19null2382     fun `Do not check closeable for minSdkVersion less than 19`() {
2383         check(
2384             apiLint = "", // enabled
2385             expectedIssues = "",
2386             manifest = """<?xml version="1.0" encoding="UTF-8"?>
2387                 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2388                     <uses-sdk android:minSdkVersion="18" />
2389                 </manifest>
2390             """.trimIndent(),
2391             sourceFiles = arrayOf(
2392                 java(
2393                     """
2394                     package android.pkg;
2395 
2396                     public abstract class MyErrorClass1 {
2397                         public void close() {}
2398                     }
2399                     """
2400                 ),
2401                 java(
2402                     """
2403                     package android.pkg;
2404 
2405                     public abstract class MyErrorClass2 {
2406                         public void finalize() {}
2407                         public void shutdown() {}
2408                     }
2409                     """
2410                 )
2411             )
2412         )
2413     }
2414 
2415     @Test
Check ICU types for minSdkVersion 24null2416     fun `Check ICU types for minSdkVersion 24`() {
2417         check(
2418             apiLint = "", // enabled
2419             expectedIssues = """
2420                 src/android/pkg/MyErrorClass1.java:8: warning: Type `java.util.TimeZone` should be replaced with richer ICU type `android.icu.util.TimeZone` [UseIcu]
2421             """,
2422             manifest = """<?xml version="1.0" encoding="UTF-8"?>
2423                 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2424                     <uses-sdk android:minSdkVersion="24" />
2425                 </manifest>
2426             """.trimIndent(),
2427             sourceFiles = arrayOf(
2428                 java(
2429                     """
2430                     package android.pkg;
2431 
2432                     import android.annotation.NonNull;
2433                     import java.util.TimeZone;
2434 
2435                     public abstract class MyErrorClass1 {
2436                         @NonNull
2437                         public TimeZone getDefaultTimeZone() {
2438                             return TimeZone.getDefault();
2439                         }
2440                     }
2441                     """
2442                 ),
2443                 nonNullSource
2444             )
2445         )
2446     }
2447 
2448     @Test
Do not check ICU types for minSdkVersion less than 24null2449     fun `Do not check ICU types for minSdkVersion less than 24`() {
2450         check(
2451             apiLint = "", // enabled
2452             expectedIssues = "",
2453             manifest = """<?xml version="1.0" encoding="UTF-8"?>
2454                 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2455                     <uses-sdk android:minSdkVersion="23" />
2456                 </manifest>
2457             """.trimIndent(),
2458             sourceFiles = arrayOf(
2459                 java(
2460                     """
2461                     package android.pkg;
2462 
2463                     import android.annotation.NonNull;
2464                     import java.util.TimeZone;
2465 
2466                     public abstract class MyErrorClass1 {
2467                         @NonNull
2468                         public TimeZone getDefaultTimeZone() {
2469                             return TimeZone.getDefault();
2470                         }
2471                     }
2472                     """
2473                 ),
2474                 nonNullSource
2475             )
2476         )
2477     }
2478 
2479     @Test
Check Kotlin keywordsnull2480     fun `Check Kotlin keywords`() {
2481         check(
2482             apiLint = "", // enabled
2483             expectedIssues = """
2484                 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]
2485                 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]
2486                 """,
2487             expectedFail = DefaultLintErrorMessage,
2488             sourceFiles = arrayOf(
2489                 java(
2490                     """
2491                     package android.pkg;
2492 
2493                     public class KotlinKeywordTest {
2494                         public void okay();
2495                         public final int okay = 0;
2496 
2497                         public void fun() {} // error
2498                         public final int as = 0; // error
2499                     }
2500                     """
2501                 ),
2502                 androidxNonNullSource,
2503                 androidxNullableSource
2504             )
2505         )
2506     }
2507 
2508     @Test
Check Kotlin operatorsnull2509     fun `Check Kotlin operators`() {
2510         check(
2511             apiLint = "", // enabled
2512             expectedIssues = """
2513                 src/android/pkg/KotlinOperatorTest.java:6: info: Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object) [KotlinOperator]
2514                 src/android/pkg/KotlinOperatorTest.java:7: info: Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object) [KotlinOperator]
2515                 src/android/pkg/KotlinOperatorTest.java:8: info: Method can be invoked with function call syntax from Kotlin: `invoke` (this is usually desirable; just make sure it makes sense for this type of object) [KotlinOperator]
2516                 src/android/pkg/KotlinOperatorTest.java:9: info: Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object) [KotlinOperator]
2517                 src/android/pkg/KotlinOperatorTest.java:9: error: Only one of `plus` and `plusAssign` methods should be present for Kotlin [UniqueKotlinOperator]
2518                 src/android/pkg/KotlinOperatorTest.java:10: info: Method can be invoked as a compound assignment operator from Kotlin: `plusAssign` (this is usually desirable; just make sure it makes sense for this type of object) [KotlinOperator]
2519                 """,
2520             expectedFail = DefaultLintErrorMessage,
2521             sourceFiles = arrayOf(
2522                 java(
2523                     """
2524                     package android.pkg;
2525 
2526                     import androidx.annotation.Nullable;
2527 
2528                     public class KotlinOperatorTest {
2529                         public int get(int i) { return i + 2; }
2530                         public void set(int i, int j, int k) { }
2531                         public void invoke(int i, int j, int k) { }
2532                         public int plus(@Nullable JavaClass other) { return 0; }
2533                         public void plusAssign(@Nullable JavaClass other) { }
2534                     }
2535                     """
2536                 ),
2537                 androidxNullableSource
2538             )
2539         )
2540     }
2541 
2542     @Test
Return collections instead of arraysnull2543     fun `Return collections instead of arrays`() {
2544         check(
2545             extraArguments = arrayOf(ARG_API_LINT, ARG_HIDE, "AutoBoxing"),
2546             expectedIssues = """
2547                 src/android/pkg/ArrayTest.java:12: warning: Method should return Collection<Object> (or subclass) instead of raw array; was `java.lang.Object[]` [ArrayReturn] [See https://s.android.com/api-guidelines#methods-prefer-collection-over-array]
2548                 src/android/pkg/ArrayTest.java:13: warning: Method parameter should be Collection<Number> (or subclass) instead of raw array; was `java.lang.Number[]` [ArrayReturn] [See https://s.android.com/api-guidelines#methods-prefer-collection-over-array]
2549                 """,
2550             sourceFiles = arrayOf(
2551                 java(
2552                     """
2553                     package android.pkg;
2554 
2555                     import androidx.annotation.NonNull;
2556 
2557                     public class ArrayTest {
2558                         @NonNull
2559                         public int[] ok1() { throw new RuntimeException(); }
2560                         @NonNull
2561                         public String[] ok2() { throw new RuntimeException(); }
2562                         public void ok3(@Nullable int[] i) { }
2563                         @NonNull
2564                         public Object[] error1() { throw new RuntimeException(); }
2565                         public void error2(@NonNull Number[] i) { }
2566                         public void ok(@NonNull Number... args) { }
2567                     }
2568                     """
2569                 ),
2570                 kotlin(
2571                     """
2572                     package test.pkg
2573                     fun okMethod(vararg values: Integer, foo: Float, bar: Float)
2574                     """
2575                 ),
2576                 androidxNonNullSource
2577             )
2578         )
2579     }
2580 
2581     @Test
Check user handle namesnull2582     fun `Check user handle names`() {
2583         check(
2584             apiLint = "", // enabled
2585             expectedIssues = """
2586                 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]
2587                 src/android/pkg/UserHandleTest.java:8: warning: Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `error` [UserHandleName]
2588                 """,
2589             sourceFiles = arrayOf(
2590                 java(
2591                     """
2592                     package android.pkg;
2593                     import android.os.UserHandle;
2594                     import androidx.annotation.Nullable;
2595 
2596                     public class UserHandleTest {
2597                         public void doFooAsUser(int i, @Nullable UserHandle handle) {} //OK
2598                         public void doFooForUser(int i, @Nullable UserHandle handle) {} //OK
2599                         public void error(int i, @Nullable UserHandle handle) {}
2600                     }
2601                     """
2602                 ),
2603                 java(
2604                     """
2605                     package android.pkg;
2606                     import android.os.UserHandle;
2607                     import androidx.annotation.Nullable;
2608 
2609                     public class MyManager {
2610                         private MyManager() { }
2611                         public void error(int i, @Nullable UserHandle handle) {}
2612                     }
2613                     """
2614                 ),
2615                 androidxNullableSource
2616             )
2617         )
2618     }
2619 
2620     @Test
Check parametersnull2621     fun `Check parameters`() {
2622         check(
2623             apiLint = "", // enabled
2624             expectedIssues = """
2625                 src/android/pkg/FooOptions.java:3: warning: Classes holding a set of parameters should be called `FooParams`, was `FooOptions` [UserHandleName]
2626                 """,
2627             sourceFiles = arrayOf(
2628                 java(
2629                     """
2630                     package android.pkg;
2631 
2632                     public class FooOptions {
2633                     }
2634                     """
2635                 ),
2636                 java(
2637                     """
2638                     package android.app;
2639 
2640                     public class ActivityOptions {
2641                     }
2642                     """
2643                 )
2644             )
2645         )
2646     }
2647 
2648     @Test
Check service namesnull2649     fun `Check service names`() {
2650         check(
2651             apiLint = "", // enabled
2652             expectedIssues = """
2653                 src/android/content/Context.java:11: error: Inconsistent service constant name; expected `SOMETHING_SERVICE`, was `OTHER_MANAGER` [ServiceName]
2654                 src/android/content/Context.java:12: error: Inconsistent service constant name; expected `OTHER_SERVICE`, was `OTHER_MANAGER_SERVICE` [ServiceName]
2655                 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]
2656                 """,
2657             expectedFail = DefaultLintErrorMessage,
2658             sourceFiles = arrayOf(
2659                 java(
2660                     """
2661                     package android.content;
2662 
2663                     public class Context {
2664                         private Context() { }
2665                         // Good
2666                         public static final String FOO_BAR_SERVICE = "foo_bar";
2667                         // Unrelated
2668                         public static final int NON_STRING_SERVICE = 42;
2669                         // Bad
2670                         public static final String OTHER_SERVICE = "something";
2671                         public static final String OTHER_MANAGER = "something";
2672                         public static final String OTHER_MANAGER_SERVICE = "other_manager";
2673                     }
2674                     """
2675                 ),
2676                 java(
2677                     """
2678                     package android.pkg;
2679 
2680                     // Unrelated
2681                     public class ServiceNameTest {
2682                         private ServiceNameTest() { }
2683                         public static final String FOO_BAR_SERVICE = "foo_bar";
2684                         public static final String OTHER_SERVICE = "something";
2685                         public static final int NON_STRING_SERVICE = 42;
2686                         public static final String BIND_SOME_SERVICE = "android.permission.BIND_SOME_SERVICE";
2687                     }
2688                     """
2689                 )
2690             )
2691         )
2692     }
2693 
2694     @Test
Check method name tensenull2695     fun `Check method name tense`() {
2696         check(
2697             apiLint = "", // enabled
2698             expectedIssues = """
2699                 src/android/pkg/MethodNameTest.java:6: warning: Unexpected tense; probably meant `enabled`, was `fooEnable` [MethodNameTense]
2700                 src/android/pkg/MethodNameTest.java:7: warning: Unexpected tense; probably meant `enabled`, was `mustEnable` [MethodNameTense]
2701                 """,
2702             sourceFiles = arrayOf(
2703                 java(
2704                     """
2705                     package android.pkg;
2706 
2707                     public class MethodNameTest {
2708                         private MethodNameTest() { }
2709                         public void enable() { } // ok, not Enable
2710                         public void fooEnable() { } // warn
2711                         public boolean mustEnable() { return true; } // warn
2712                         public boolean isEnabled() { return true; }
2713                     }
2714                     """
2715                 )
2716             )
2717         )
2718     }
2719 
2720     @Test
Check no clonenull2721     fun `Check no clone`() {
2722         check(
2723             apiLint = "", // enabled
2724             expectedIssues = """
2725                 src/android/pkg/CloneTest.java:8: error: Provide an explicit copy constructor instead of implementing `clone()` [NoClone] [See https://s.android.com/api-guidelines#avoid-clone]
2726                 """,
2727             expectedFail = DefaultLintErrorMessage,
2728             sourceFiles = arrayOf(
2729                 java(
2730                     """
2731                     package android.pkg;
2732 
2733                     import androidx.annotation.Nullable;
2734 
2735                     public class CloneTest {
2736                         public void clone(int i) { } // ok
2737                         @NonNull
2738                         public CloneTest clone() { return super.clone(); } // error
2739                     }
2740                     """
2741                 ),
2742                 androidxNullableSource
2743             )
2744         )
2745     }
2746 
2747     @Test
Check ICU typesnull2748     fun `Check ICU types`() {
2749         check(
2750             apiLint = "", // enabled
2751             expectedIssues = """
2752                 src/android/pkg/IcuTest.java:6: warning: Type `java.util.TimeZone` should be replaced with richer ICU type `android.icu.util.TimeZone` [UseIcu]
2753                 src/android/pkg/IcuTest.java:8: warning: Type `java.text.BreakIterator` should be replaced with richer ICU type `android.icu.text.BreakIterator` [UseIcu]
2754                 src/android/pkg/IcuTest.java:8: warning: Type `java.text.Collator` should be replaced with richer ICU type `android.icu.text.Collator` [UseIcu]
2755                 """,
2756             sourceFiles = arrayOf(
2757                 java(
2758                     """
2759                     package android.pkg;
2760 
2761                     import androidx.annotation.Nullable;
2762 
2763                     public abstract class IcuTest {
2764                         public IcuTest(@Nullable java.util.TimeZone timeZone) { }
2765                         @Nullable
2766                         public abstract java.text.BreakIterator foo(@Nullable java.text.Collator collator);
2767                     }
2768                     """
2769                 ),
2770                 androidxNullableSource
2771             )
2772         )
2773     }
2774 
2775     @Test
Check using parcel file descriptorsnull2776     fun `Check using parcel file descriptors`() {
2777         check(
2778             apiLint = "", // enabled
2779             expectedIssues = """
2780                 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] [See https://s.android.com/api-guidelines#prefer-parcelfiledescriptor]
2781                 src/android/pkg/PdfTest.java:7: error: Must use ParcelFileDescriptor instead of FileDescriptor in method android.pkg.PdfTest.getFileDescriptor() [UseParcelFileDescriptor] [See https://s.android.com/api-guidelines#prefer-parcelfiledescriptor]
2782                 """,
2783             expectedFail = DefaultLintErrorMessage,
2784             sourceFiles = arrayOf(
2785                 java(
2786                     """
2787                     package android.pkg;
2788 
2789                     import androidx.annotation.Nullable;
2790 
2791                     public abstract class PdfTest {
2792                         public void error1(@Nullable java.io.FileDescriptor fd) { }
2793                         public int getFileDescriptor() { return -1; }
2794                         public void ok(@Nullable android.os.ParcelFileDescriptor fd) { }
2795                     }
2796                     """
2797                 ),
2798                 java(
2799                     """
2800                     package android.system;
2801 
2802                     public class Os {
2803                         public void ok(@Nullable java.io.FileDescriptor fd) { }
2804                         public int getFileDescriptor() { return -1; }
2805                     }
2806                     """
2807                 ),
2808                 java(
2809                     """
2810                     package android.yada;
2811 
2812                     import android.annotation.Nullable;
2813 
2814                     public class YadaService extends android.app.Service {
2815                         @Override
2816                         public final void dump(@Nullable java.io.FileDescriptor fd, @Nullable java.io.PrintWriter pw, @Nullable String[] args) {
2817                         }
2818                     }
2819                     """
2820                 ),
2821                 androidxNullableSource,
2822                 nonNullSource
2823             )
2824         )
2825     }
2826 
2827     @Test
Check using bytes and shortsnull2828     fun `Check using bytes and shorts`() {
2829         check(
2830             apiLint = "", // enabled
2831             expectedIssues = """
2832                 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] [See https://s.android.com/api-guidelines#avoid-short-byte]
2833                 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] [See https://s.android.com/api-guidelines#avoid-short-byte]
2834                 """,
2835             sourceFiles = arrayOf(
2836                 java(
2837                     """
2838                     package android.pkg;
2839 
2840                     public abstract class ByteTest {
2841                         public void error1(byte b) { }
2842                         public void error2(short s) { }
2843                     }
2844                     """
2845                 )
2846             )
2847         )
2848     }
2849 
2850     @Test
Check singleton constructorsnull2851     fun `Check singleton constructors`() {
2852         check(
2853             apiLint = "", // enabled
2854             expectedIssues = """
2855                 src/android/pkg/MySingleton.java:8: error: Singleton classes should use `getInstance()` methods: `MySingleton` [SingletonConstructor] [See https://s.android.com/api-guidelines#singleton-class]
2856                 """,
2857             expectedFail = DefaultLintErrorMessage,
2858             sourceFiles = arrayOf(
2859                 java(
2860                     """
2861                     package android.pkg;
2862 
2863                     import androidx.annotation.Nullable;
2864 
2865                     public abstract class MySingleton {
2866                         @Nullable
2867                         public static MySingleton getMyInstance() { return null; }
2868                         public MySingleton() { }
2869                         public void foo() { }
2870                     }
2871                     """
2872                 ),
2873                 java(
2874                     """
2875                     package android.pkg;
2876 
2877                     import androidx.annotation.Nullable;
2878 
2879                     public abstract class MySingleton2 {
2880                         @Nullable
2881                         public static MySingleton2 getMyInstance() { return null; }
2882                         private MySingleton2() { } // OK, private
2883                         public void foo() { }
2884                     }
2885                     """
2886                 ),
2887                 androidxNullableSource
2888             )
2889         )
2890     }
2891 
2892     @Test
Check forbidden super-classesnull2893     fun `Check forbidden super-classes`() {
2894         check(
2895             apiLint = "", // enabled
2896             expectedIssues = """
2897                 src/android/pkg/FirstActivity.java:2: error: FirstActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead. [ForbiddenSuperClass]
2898                 src/android/pkg/IndirectActivity.java:2: error: IndirectActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead. [ForbiddenSuperClass]
2899                 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]
2900                 """,
2901             expectedFail = DefaultLintErrorMessage,
2902             sourceFiles = arrayOf(
2903                 java(
2904                     """
2905                     package android.pkg;
2906                     public abstract class FirstActivity extends android.app.Activity {
2907                         private FirstActivity() { }
2908                     }
2909                     """
2910                 ),
2911                 java(
2912                     """
2913                     package android.pkg;
2914                     public abstract class IndirectActivity extends android.app.ListActivity {
2915                         private IndirectActivity() { }
2916                     }
2917                     """
2918                 ),
2919                 java(
2920                     """
2921                     package android.pkg;
2922                     public abstract class MyTask extends android.os.AsyncTask<String,String,String> {
2923                         private MyTask() { }
2924                     }
2925                     """
2926                 )
2927             )
2928         )
2929     }
2930 
2931     @Test
KotlinOperator check only applies when not using operator modifiernull2932     fun `KotlinOperator check only applies when not using operator modifier`() {
2933         check(
2934             apiLint = "", // enabled
2935             expectedIssues = """
2936                 src/android/pkg/A.kt:3: info: Note that adding the `operator` keyword would allow calling this method using operator syntax [KotlinOperator]
2937                 src/android/pkg/Bar.kt:4: info: Note that adding the `operator` keyword would allow calling this method using operator syntax [KotlinOperator]
2938                 src/android/pkg/Foo.java:8: info: Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object) [KotlinOperator]
2939                 """,
2940             sourceFiles = arrayOf(
2941                 java(
2942                     """
2943                         package android.pkg;
2944 
2945                         import androidx.annotation.Nullable;
2946 
2947                         public class Foo {
2948                             private Foo() { }
2949                             @Nullable
2950                             public Foo div(int value) { }
2951                         }
2952                     """
2953                 ),
2954                 kotlin(
2955                     """
2956                         package android.pkg
2957                         class Bar {
2958                             operator fun div(value: Int): Bar { TODO() }
2959                             fun plus(value: Int): Bar { TODO() }
2960                         }
2961                     """
2962                 ),
2963                 kotlin(
2964                     """
2965                         package android.pkg
2966                         class FontFamily(val fonts: List<String>) : List<String> by fonts
2967                     """
2968                 ),
2969                 kotlin(
2970                     """
2971                         package android.pkg
2972                         class B: A() {
2973                             override fun get(i: Int): A {
2974                                 return A()
2975                             }
2976                         }
2977                     """
2978                 ),
2979                 kotlin(
2980                     """
2981                         package android.pkg
2982                         open class A {
2983                             open fun get(i: Int): A {
2984                                 return A()
2985                             }
2986                         }
2987                     """
2988                 ),
2989                 androidxNullableSource
2990             )
2991         )
2992     }
2993 
2994     @Test
Test fields, parameters and returns require nullabilitynull2995     fun `Test fields, parameters and returns require nullability`() {
2996         check(
2997             apiLint = "", // enabled
2998             extraArguments = arrayOf(ARG_API_LINT, ARG_HIDE, "AllUpper,StaticUtils,Enum"),
2999             expectedIssues = """
3000                 src/android/pkg/Foo.java:11: error: Missing nullability on parameter `name` in method `Foo` [MissingNullability] [See https://s.android.com/api-guidelines#annotations]
3001                 src/android/pkg/Foo.java:12: error: Missing nullability on parameter `value` in method `setBadValue` [MissingNullability] [See https://s.android.com/api-guidelines#annotations]
3002                 src/android/pkg/Foo.java:13: error: Missing nullability on method `getBadValue` return [MissingNullability] [See https://s.android.com/api-guidelines#annotations]
3003                 src/android/pkg/Foo.java:20: error: Missing nullability on parameter `duration` in method `methodMissingParamAnnotations` [MissingNullability] [See https://s.android.com/api-guidelines#annotations]
3004                 src/android/pkg/Foo.java:7: error: Missing nullability on field `badField` in class `class android.pkg.Foo` [MissingNullability] [See https://s.android.com/api-guidelines#annotations]
3005                 """,
3006             expectedFail = DefaultLintErrorMessage,
3007             sourceFiles = arrayOf(
3008                 java(
3009                     """
3010                         package android.pkg;
3011 
3012                         import androidx.annotation.NonNull;
3013                         import androidx.annotation.Nullable;
3014 
3015                         public class Foo<T> {
3016                             public final Foo badField;
3017                             @Nullable
3018                             public final Foo goodField;
3019 
3020                             public Foo(String name, int number) { }
3021                             public void setBadValue(Foo value) { }
3022                             public Foo getBadValue(int number) { throw UnsupportedOperationExceptions(); }
3023                             public void setGoodValue(@Nullable Foo value) { }
3024                             public void setGoodIgnoredGenericValue(T value) { }
3025                             @NonNull
3026                             public Foo getGoodValue(int number) { throw UnsupportedOperationExceptions(); }
3027 
3028                             @NonNull
3029                             public Foo methodMissingParamAnnotations(java.time.Duration duration) {
3030                                 throw UnsupportedOperationException();
3031                             }
3032                         }
3033                     """
3034                 ),
3035                 java(
3036                     """
3037                     package test.pkg;
3038                     @SuppressWarnings("ALL")
3039                     public enum Foo {
3040                         A, B;
3041                     }
3042                     """
3043                 ),
3044                 kotlin(
3045                     """
3046                     package test.pkg
3047                     enum class Language {
3048                         KOTLIN,
3049                         JAVA
3050                     }
3051                     """
3052                 ),
3053                 kotlin(
3054                     """
3055                     package android.pkg
3056 
3057                     object Bar
3058 
3059                     class FooBar {
3060                         companion object
3061                     }
3062 
3063                     class FooBarNamed {
3064                         companion object Named
3065                     }
3066                     """
3067                 ),
3068                 androidxNullableSource,
3069                 androidxNonNullSource
3070             )
3071         )
3072     }
3073 
3074     @Test
Test equals, toString, non-null constants, enums and annotation members don't require nullabilitynull3075     fun `Test equals, toString, non-null constants, enums and annotation members don't require nullability`() {
3076         check(
3077             apiLint = "", // enabled
3078             expectedIssues = "",
3079             sourceFiles = arrayOf(
3080                 java(
3081                     """
3082                         package android.pkg;
3083 
3084                         import android.annotation.SuppressLint;
3085 
3086                         public class Foo<T> {
3087                             public static final String FOO_CONSTANT = "test";
3088 
3089                             public boolean equals(Object other) {
3090                                 return other == this;
3091                             }
3092 
3093                             public int hashCode() {
3094                                 return 0;
3095                             }
3096 
3097                             public String toString() {
3098                                 return "Foo";
3099                             }
3100 
3101                             @SuppressLint("Enum")
3102                             public enum FooEnum {
3103                                 FOO, BAR
3104                             }
3105 
3106                             public @interface FooAnnotation {
3107                                 String value() default "";
3108                             }
3109                         }
3110                     """
3111                 ),
3112                 androidxNullableSource,
3113                 androidxNonNullSource
3114             )
3115         )
3116     }
3117 
3118     @Test
3119     fun `Nullability check for generic methods referencing parent type parameter`() {
3120         check(
3121             apiLint = "", // enabled
3122             expectedIssues = """
3123                 src/test/pkg/MyClass.java:14: error: Missing nullability on method `method4` return [MissingNullability] [See https://s.android.com/api-guidelines#annotations]
3124                 src/test/pkg/MyClass.java:14: error: Missing nullability on parameter `input` in method `method4` [MissingNullability] [See https://s.android.com/api-guidelines#annotations]
3125             """,
3126             expectedFail = DefaultLintErrorMessage,
3127             sourceFiles = arrayOf(
3128                 java(
3129                     """
3130                     package test.pkg;
3131 
3132                     import androidx.annotation.NonNull;
3133                     import androidx.annotation.Nullable;
3134 
3135                     public class MyClass extends HiddenParent<String> {
3136                         public void method1() { }
3137 
3138                         @NonNull
3139                         @Override
3140                         public String method3(@Nullable String input) { return null; }
3141 
3142                         @Override
3143                         public String method4(String input) { return null; }
3144                     }
3145                     """
3146                 ),
3147                 java(
3148                     """
3149                     package test.pkg;
3150 
3151                     class HiddenParent<T> {
3152                         public T method2(T t) { }
3153                         public T method3(T t) { }
3154                         public T method4(T t) { }
3155                     }
3156                     """
3157                 ),
3158                 androidxNullableSource,
3159                 androidxNonNullSource
3160             )
3161         )
3162     }
3163 
3164     @Test
3165     fun `No new setting keys`() {
3166         check(
3167             apiLint = "", // enabled
3168             extraArguments = arrayOf(
3169                 ARG_ERROR,
3170                 "NoSettingsProvider"
3171             ),
3172             expectedIssues = """
3173                 src/android/provider/Settings.java:9: error: New setting keys are not allowed (Field: BAD1); use getters/setters in relevant manager class [NoSettingsProvider] [See https://s.android.com/api-guidelines#no-settings-provider]
3174                 src/android/provider/Settings.java:12: error: Bare field okay2 must be marked final, or moved behind accessors if mutable [MutableBareField] [See https://s.android.com/api-guidelines#mutable-bare-field]
3175                 src/android/provider/Settings.java:17: error: New setting keys are not allowed (Field: BAD1); use getters/setters in relevant manager class [NoSettingsProvider] [See https://s.android.com/api-guidelines#no-settings-provider]
3176                 src/android/provider/Settings.java:21: error: New setting keys are not allowed (Field: BAD1); use getters/setters in relevant manager class [NoSettingsProvider] [See https://s.android.com/api-guidelines#no-settings-provider]
3177             """,
3178             expectedFail = DefaultLintErrorMessage,
3179             sourceFiles = arrayOf(
3180                 java(
3181                     """
3182                     package android.provider;
3183 
3184                     import androidx.annotation.Nullable;
3185 
3186                     public class Settings {
3187                         private Settings() { }
3188                         public static class Global {
3189                             private Global() { }
3190                             public static final String BAD1 = "";
3191                             public final String okay1 = "";
3192                             @Nullable
3193                             public static String okay2 = "";
3194                             public static final int OKAY3 = 0;
3195                         }
3196                         public static class Secure {
3197                             private Secure() { }
3198                             public static final String BAD1 = "";
3199                         }
3200                         public static class System {
3201                             private System() { }
3202                             public static final String BAD1 = "";
3203                         }
3204                         public static class Other {
3205                             private Other() { }
3206                             public static final String OKAY1 = "";
3207                         }
3208                     }
3209                     """
3210                 ),
3211                 androidxNullableSource
3212             )
3213         )
3214     }
3215 
3216     @Test
3217     fun `No issues for ignored packages`() {
3218         check(
3219             apiLint = """
3220                 package java.math {
3221                   public class BigInteger {
3222                     ctor public BigInteger();
3223                   }
3224                 }
3225             """.trimIndent(),
3226             extraArguments = arrayOf(
3227                 ARG_API_LINT_IGNORE_PREFIX,
3228                 "java."
3229             ),
3230             expectedIssues = "",
3231             sourceFiles = arrayOf(
3232                 java(
3233                     """
3234                     package java.math;
3235 
3236                     public class BigInteger {
3237                         public byte newMethod() {
3238                             return 0;
3239                         }
3240                     }
3241                     """
3242                 )
3243             )
3244         )
3245     }
3246 
3247     @Test
3248     fun `vararg use in annotations`() {
3249         check(
3250             apiLint = "", // enabled
3251             expectedIssues = "",
3252             sourceFiles = arrayOf(
3253                 kotlin(
3254                     """
3255                         package test.pkg
3256 
3257                         import kotlin.reflect.KClass
3258 
3259                         annotation class MyAnnotation(
3260                             vararg val markerClass: KClass<out Annotation>
3261                         )
3262                     """
3263                 )
3264             )
3265         )
3266     }
3267 
3268     @Test
3269     fun `Inherited interface constants`() {
3270         check(
3271             expectedIssues = "",
3272             expectedFail = "",
3273             apiLint = """
3274                 package javax.microedition.khronos.egl {
3275                     public interface EGL {
3276                     }
3277                     public interface EGL10 extends javax.microedition.khronos.egl.EGL {
3278                         field public static final int EGL_SUCCESS = 0;
3279                     }
3280                     public interface EGL11 extends javax.microedition.khronos.egl.EGL10 {
3281                         field public static final int EGL_CONTEXT_LOST = 1;
3282                     }
3283                     public interface EGLDisplay {
3284                     }
3285                 }
3286                 """,
3287             sourceFiles = arrayOf(
3288                 java(
3289                     """
3290                         package javax.microedition.khronos.egl;
3291 
3292                         public interface EGL {
3293                         }
3294                     """
3295                 ),
3296                 java(
3297                     """
3298                         package javax.microedition.khronos.egl;
3299 
3300                         public interface EGL10 extends EGL {
3301                             EGLDisplay EGL_SUCCESS = new EGLImpl();
3302                         }
3303                     """
3304                 ),
3305                 java(
3306                     """
3307                         package javax.microedition.khronos.egl;
3308 
3309                         public interface EGL11 extends EGL10 {
3310                             int EGL_CONTEXT_LOST = 1;
3311                         }
3312                     """
3313                 ),
3314                 java(
3315                     """
3316                         package javax.microedition.khronos.egl;
3317 
3318                         public abstract class EGLDisplay {
3319                         }
3320                     """
3321                 )
3322             )
3323         )
3324     }
3325 
3326     @Test
3327     fun `Inherited interface constants inherited through parents into children`() {
3328         check(
3329             expectedIssues = "",
3330             expectedFail = "",
3331             apiLint = """
3332                 package android.provider {
3333                   public static final class Settings.Global extends android.provider.Settings.NameValueTable {
3334                   }
3335                   public static class Settings.NameValueTable implements android.provider.BaseColumns {
3336                   }
3337                   public interface BaseColumns {
3338                       field public static final String _ID = "_id";
3339                   }
3340                 }
3341                 """,
3342             sourceFiles = arrayOf(
3343                 java(
3344                     """
3345                         package android.provider;
3346 
3347                         public class Settings {
3348                             private Settings() { }
3349                             public static final class Global extends NameValueTable {
3350                             }
3351                             public static final class NameValueTable implements BaseColumns {
3352                             }
3353                         }
3354                     """
3355                 ),
3356                 java(
3357                     """
3358                         package android.provider;
3359 
3360                         public interface BaseColumns {
3361                             public static final String _ID = "_id";
3362                         }
3363                     """
3364                 )
3365             ),
3366             extraArguments = arrayOf("--error", "NoSettingsProvider")
3367         )
3368     }
3369 
3370     @Test
3371     fun `No warnings about nullability on private constructor getters`() {
3372         check(
3373             expectedIssues = "",
3374             apiLint = "",
3375             sourceFiles = arrayOf(
3376                 kotlin(
3377                     """
3378                         package test.pkg
3379                         class MyClass private constructor(
3380                             val myParameter: Set<Int>
3381                         )
3382                     """
3383                 )
3384             )
3385         )
3386     }
3387 
3388     @Test
3389     fun `Methods returning ListenableFuture end with async`() {
3390         check(
3391             apiLint = "", // enabled
3392             expectedIssues = """
3393                 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]
3394             """,
3395             expectedFail = DefaultLintErrorMessage,
3396             sourceFiles = arrayOf(
3397                 java(
3398                     """
3399                     package android.pkg;
3400 
3401                     import androidx.annotation.Nullable;
3402                     import com.google.common.util.concurrent.ListenableFuture;
3403 
3404                     public final class MyClass {
3405                         public @Nullable ListenableFuture<String> bad() { return null; }
3406                         public @Nullable ListenableFuture<String> goodAsync() { return null; }
3407                     }
3408                     """
3409                 ),
3410                 java(
3411                     """
3412                     package com.google.common.util.concurrent;
3413                     public class ListenableFuture<T> {
3414                     }
3415                     """
3416                 ),
3417                 androidxNullableSource
3418             )
3419         )
3420     }
3421 
3422     @Test
3423     fun `Listener replaceable with OutcomeReceiver or ListenableFuture`() {
3424         check(
3425             apiLint = "", // enabled
3426             expectedIssues = """
3427                 src/android/pkg/Cases.java:7: error: Cases.BadCallback can be replaced with OutcomeReceiver<R,E> (platform) or suspend fun / ListenableFuture (AndroidX). [GenericCallbacks] [See https://s.android.com/api-guidelines#callbacks-sam]
3428                 src/android/pkg/Cases.java:15: error: Cases.BadGenericListener can be replaced with OutcomeReceiver<R,E> (platform) or suspend fun / ListenableFuture (AndroidX). [GenericCallbacks] [See https://s.android.com/api-guidelines#callbacks-sam]
3429                 src/android/pkg/Cases.java:11: error: Cases.BadListener can be replaced with OutcomeReceiver<R,E> (platform) or suspend fun / ListenableFuture (AndroidX). [GenericCallbacks] [See https://s.android.com/api-guidelines#callbacks-sam]
3430             """,
3431             expectedFail = DefaultLintErrorMessage,
3432             sourceFiles = arrayOf(
3433                 java(
3434                     """
3435                     package android.pkg;
3436 
3437                     import androidx.annotation.NonNull;
3438                     import java.io.IOException;
3439 
3440                     public final class Cases {
3441                         public class BadCallback {
3442                             public abstract void onSuccess(@NonNull String result);
3443                             public void onFailure(@NonNull Throwable error) {}
3444                         }
3445                         public interface BadListener {
3446                             void onResult(@NonNull Object result);
3447                             void onError(@NonNull IOException error);
3448                         }
3449                         public interface BadGenericListener<R, E extends Throwable> {
3450                             void onResult(@NonNull R result);
3451                             void onError(@NonNull E error);
3452                         }
3453                         public interface OkListener {
3454                             void onResult(@NonNull Object result);
3455                             void onError(@NonNull int error);
3456                         }
3457                         public interface Ok2Listener {
3458                             void onResult(@NonNull int result);
3459                             void onError(@NonNull Throwable error);
3460                         }
3461                         public interface Ok3Listener {
3462                             void onSuccess(@NonNull String result);
3463                             void onOtherThing(@NonNull String result);
3464                             void onFailure(@NonNull Throwable error);
3465                         }
3466                     }
3467                     """
3468                 ),
3469                 androidxNonNullSource
3470             )
3471         )
3472     }
3473 
3474     @Test
3475     fun `No warning on generic return type`() {
3476         check(
3477             expectedIssues = "",
3478             apiLint = "",
3479             sourceFiles = arrayOf(
3480                 kotlin(
3481                     """
3482                         package test.pkg
3483                         class SimpleArrayMap<K, V> {
3484                             override fun getOrDefault(key: K, defaultValue: V): V {}
3485                         }
3486                     """
3487                 )
3488             )
3489         )
3490     }
3491 
3492     @Test
3493     fun `No crash when setter start with numbers`() {
3494         check(
3495             expectedIssues = "",
3496             apiLint = "",
3497             sourceFiles = arrayOf(
3498                 java(
3499                     """
3500                         package test.pkg;
3501                         import androidx.annotation.NonNull;
3502                         public class Config {
3503                             public boolean is80211mcSupported() { return true; }
3504                             public static final class Builder {
3505                                 @NonNull
3506                                 public Builder set80211mcSupported(boolean supports80211mc) {
3507                                 }
3508                                 @NonNull
3509                                 public Config build() {
3510                                     return Config();
3511                                 }
3512                             }
3513                         }
3514                     """
3515                 ),
3516                 androidxNonNullSource,
3517             )
3518         )
3519     }
3520 
3521     @Test
3522     fun `No error for nullability on synthetic methods`() {
3523         check(
3524             expectedIssues = "",
3525             apiLint = "",
3526             sourceFiles = arrayOf(
3527                 kotlin(
3528                     """
3529                         package test.pkg
3530                         class Foo {
3531                             @JvmSynthetic
3532                             fun bar(): String {}
3533                         }
3534                     """
3535                 )
3536             )
3537         )
3538     }
3539 
3540     @Test
3541     fun `Constructors return types don't require nullability`() {
3542         check(
3543             expectedIssues = "",
3544             apiLint = "",
3545             sourceFiles = arrayOf(
3546                 java(
3547                     """
3548                         package test.pkg;
3549                         public class Foo() {
3550                             // Doesn't require nullability
3551                             public Foo(@NonNull String bar);
3552                             // Requires nullability
3553                             public @NonNull String baz(@NonNull String whatever);
3554                         }
3555                     """
3556                 )
3557             )
3558         )
3559     }
3560 
3561     @Test
No nullability allowed on overrides of unannotated methods or parametersnull3562     fun `No nullability allowed on overrides of unannotated methods or parameters`() {
3563         check(
3564             expectedIssues = """
3565                 src/test/pkg/Foo.java:10: error: Invalid nullability on method `bar` return. Overrides of unannotated super method cannot be Nullable. [InvalidNullabilityOverride] [See https://s.android.com/api-guidelines#annotations-nullability-overrides]
3566                 src/test/pkg/Foo.java:10: error: Invalid nullability on parameter `baz` in method `bar`. Parameters of overrides cannot be NonNull if the super parameter is unannotated. [InvalidNullabilityOverride] [See https://s.android.com/api-guidelines#annotations-nullability-overrides]
3567                 src/test/pkg/Foo.java:5: error: Missing nullability on method `bar` return [MissingNullability] [See https://s.android.com/api-guidelines#annotations]
3568                 src/test/pkg/Foo.java:5: error: Missing nullability on parameter `baz` in method `bar` [MissingNullability] [See https://s.android.com/api-guidelines#annotations]
3569                 """,
3570             apiLint = "",
3571             expectedFail = DefaultLintErrorMessage,
3572             sourceFiles = arrayOf(
3573                 java(
3574                     """
3575                         package test.pkg;
3576 
3577                         public class Foo {
3578                             // Not annotated
3579                             public String bar(String baz);
3580                         }
3581                         // Not allowed to mark override method Nullable if parent is not annotated
3582                         // Not allowed to mark override parameter NonNull if parent is not annotated
3583                         public class Bar extends Foo {
3584                             @Nullable @Override public String bar(@NonNull String baz);
3585                         }
3586                     """
3587                 ),
3588                 androidxNullableSource,
3589                 androidxNonNullSource
3590             )
3591         )
3592     }
3593 
3594     @Test
Override enforcement on kotlin sourced child classnull3595     fun `Override enforcement on kotlin sourced child class`() {
3596 
3597         check(
3598             expectedIssues = """
3599                 src/test/pkg/Bar.kt:5: error: Invalid nullability on parameter `baz` in method `bar`. Parameters of overrides cannot be NonNull if the super parameter is unannotated. [InvalidNullabilityOverride] [See https://s.android.com/api-guidelines#annotations-nullability-overrides]
3600                 src/test/pkg/Foo.java:5: error: Missing nullability on method `bar` return [MissingNullability] [See https://s.android.com/api-guidelines#annotations]
3601                 src/test/pkg/Foo.java:5: error: Missing nullability on parameter `baz` in method `bar` [MissingNullability] [See https://s.android.com/api-guidelines#annotations]
3602                 """,
3603             apiLint = "",
3604             expectedFail = DefaultLintErrorMessage,
3605             sourceFiles = arrayOf(
3606                 java(
3607                     """
3608                         package test.pkg;
3609 
3610                         public class Foo {
3611                             // Not annotated
3612                             public String bar(String baz);
3613                         }
3614                         """
3615                 ),
3616                 kotlin(
3617                     """
3618                         package test.pkg
3619                         // Not allowed to mark override method Nullable if parent is not annotated
3620                         // Not allowed to mark override parameter NonNull if parent is not annotated
3621                         class Bar : Foo {
3622                             override fun bar(baz: String): String
3623                         }
3624                     """
3625                 ),
3626                 androidxNullableSource,
3627                 androidxNonNullSource
3628             )
3629         )
3630     }
3631 
3632     @Test
Overrides of non-null methods cannot be nullablenull3633     fun `Overrides of non-null methods cannot be nullable`() {
3634         check(
3635             expectedIssues = """
3636                 src/test/pkg/Foo.java:9: error: Invalid nullability on method `bar` return. Overrides of NonNull methods cannot be Nullable. [InvalidNullabilityOverride] [See https://s.android.com/api-guidelines#annotations-nullability-overrides]
3637                 """,
3638             apiLint = "",
3639             expectedFail = DefaultLintErrorMessage,
3640             sourceFiles = arrayOf(
3641                 java(
3642                     """
3643                         package test.pkg;
3644 
3645                         public class Foo {
3646                             @NonNull public String bar(@Nullable String baz);
3647                         }
3648 
3649                         // Not allowed to mark override method Nullable if parent is nonNull
3650                         public class Bar extends Foo {
3651                             @Nullable @Override public String bar(@Nullable String baz);
3652                         }
3653                     """
3654                 ),
3655                 androidxNullableSource,
3656                 androidxNonNullSource
3657             )
3658         )
3659     }
3660 
3661     @Test
Overrides of nullable parameters cannot be non-nullnull3662     fun `Overrides of nullable parameters cannot be non-null`() {
3663         check(
3664             expectedIssues = """
3665                 src/test/pkg/Foo.java:10: error: Invalid nullability on parameter `baz` in method `bar`. Parameters of overrides cannot be NonNull if super parameter is Nullable. [InvalidNullabilityOverride] [See https://s.android.com/api-guidelines#annotations-nullability-overrides]
3666                 """,
3667             apiLint = "",
3668             expectedFail = DefaultLintErrorMessage,
3669             sourceFiles = arrayOf(
3670                 java(
3671                     """
3672                         package test.pkg;
3673 
3674                         public class Foo {
3675                             // Not annotated
3676                             @NonNull public String bar(@Nullable String baz);
3677                         }
3678 
3679                         // Not allowed to mark override parameter NonNull if parent is Nullable
3680                         public class Bar extends Foo {
3681                             @NonNull @Override public String bar(@NonNull String baz);
3682                         }
3683                     """
3684                 ),
3685                 androidxNullableSource,
3686                 androidxNonNullSource
3687             )
3688         )
3689     }
3690 
3691     @Test
Unchecked exceptions not allowednull3692     fun `Unchecked exceptions not allowed`() {
3693         check(
3694             expectedIssues = """
3695                 src/test/pkg/Foo.java:22: error: Methods must not throw unchecked exceptions [BannedThrow]
3696                 src/test/pkg/Foo.java:23: error: Methods must not throw unchecked exceptions [BannedThrow]
3697                 src/test/pkg/Foo.java:24: error: Methods must not throw unchecked exceptions [BannedThrow]
3698                 src/test/pkg/Foo.java:25: error: Methods must not throw unchecked exceptions [BannedThrow]
3699                 src/test/pkg/Foo.java:26: error: Methods must not throw unchecked exceptions [BannedThrow]
3700                 src/test/pkg/Foo.java:27: error: Methods must not throw unchecked exceptions [BannedThrow]
3701                 src/test/pkg/Foo.java:28: error: Methods must not throw unchecked exceptions [BannedThrow]
3702                 src/test/pkg/Foo.java:29: error: Methods must not throw unchecked exceptions [BannedThrow]
3703                 src/test/pkg/Foo.java:30: error: Methods must not throw unchecked exceptions [BannedThrow]
3704                 src/test/pkg/Foo.java:31: error: Methods must not throw unchecked exceptions [BannedThrow]
3705                 src/test/pkg/Foo.java:32: error: Methods must not throw unchecked exceptions [BannedThrow]
3706                 src/test/pkg/Foo.java:33: error: Methods must not throw unchecked exceptions [BannedThrow]
3707                 src/test/pkg/Foo.java:34: error: Methods must not throw unchecked exceptions [BannedThrow]
3708                 src/test/pkg/Foo.java:35: error: Methods must not throw unchecked exceptions [BannedThrow]
3709                 src/test/pkg/Foo.java:36: error: Methods must not throw unchecked exceptions [BannedThrow]
3710                 src/test/pkg/Foo.java:37: error: Methods must not throw unchecked exceptions [BannedThrow]
3711                 src/test/pkg/Foo.java:38: error: Methods must not throw unchecked exceptions [BannedThrow]
3712                 src/test/pkg/Foo.java:39: error: Methods must not throw unchecked exceptions [BannedThrow]
3713                 src/test/pkg/Foo.java:40: error: Methods must not throw unchecked exceptions [BannedThrow]
3714                 src/test/pkg/Foo.java:41: error: Methods must not throw unchecked exceptions [BannedThrow]
3715                 src/test/pkg/Foo.java:42: error: Methods must not throw unchecked exceptions [BannedThrow]
3716                 src/test/pkg/Foo.java:43: error: Methods must not throw unchecked exceptions [BannedThrow]
3717                 src/test/pkg/Foo.java:44: error: Methods must not throw unchecked exceptions [BannedThrow]
3718                 src/test/pkg/Foo.java:45: error: Methods must not throw unchecked exceptions [BannedThrow]
3719                 src/test/pkg/Foo.java:46: error: Methods must not throw unchecked exceptions [BannedThrow]
3720                 src/test/pkg/Foo.java:47: error: Methods must not throw unchecked exceptions [BannedThrow]
3721                 src/test/pkg/Foo.java:48: error: Methods must not throw unchecked exceptions [BannedThrow]
3722                 src/test/pkg/Foo.java:49: error: Methods must not throw unchecked exceptions [BannedThrow]
3723                 src/test/pkg/Foo.java:50: error: Methods must not throw unchecked exceptions [BannedThrow]
3724                 src/test/pkg/Foo.java:51: error: Methods must not throw unchecked exceptions [BannedThrow]
3725                 src/test/pkg/Foo.java:52: error: Methods must not throw unchecked exceptions [BannedThrow]
3726                 src/test/pkg/Foo.java:53: error: Methods must not throw unchecked exceptions [BannedThrow]
3727             """,
3728             apiLint = "",
3729             expectedFail = DefaultLintErrorMessage,
3730             sourceFiles = arrayOf(
3731                 java(
3732                     """
3733                         package test.pkg;
3734                         import java.lang.reflect.UndeclaredThrowableException;
3735                         import java.lang.reflect.MalformedParametersException;
3736                         import java.lang.reflect.MalformedParameterizedTypeException;
3737                         import java.lang.invoke.WrongMethodTypeException;
3738                         import java.lang.annotation.AnnotationTypeMismatchException;
3739                         import java.lang.annotation.IncompleteAnnotationException;
3740                         import java.util.MissingResourceException;
3741                         import java.util.EmptyStackException;
3742                         import java.util.concurrent.CompletionException;
3743                         import java.util.concurrent.RejectedExecutionException;
3744                         import java.util.IllformedLocaleException;
3745                         import java.util.ConcurrentModificationException;
3746                         import java.util.NoSuchElementException;
3747                         import java.io.UncheckedIOException;
3748                         import java.time.DateTimeException;
3749                         import java.security.ProviderException;
3750                         import java.nio.BufferUnderflowException;
3751                         import java.nio.BufferOverflowException;
3752                         public class Foo {
3753                             // 32 errors
3754                             public void a() throws NullPointerException;
3755                             public void b() throws ClassCastException;
3756                             public void c() throws IndexOutOfBoundsException;
3757                             public void d() throws UndeclaredThrowableException;
3758                             public void e() throws MalformedParametersException;
3759                             public void f() throws MalformedParameterizedTypeException;
3760                             public void g() throws WrongMethodTypeException;
3761                             public void h() throws EnumConstantNotPresentException;
3762                             public void i() throws IllegalMonitorStateException;
3763                             public void j() throws SecurityException;
3764                             public void k() throws UnsupportedOperationException;
3765                             public void l() throws AnnotationTypeMismatchException;
3766                             public void m() throws IncompleteAnnotationException;
3767                             public void n() throws TypeNotPresentException;
3768                             public void o() throws IllegalStateException;
3769                             public void p() throws ArithmeticException;
3770                             public void q() throws IllegalArgumentException;
3771                             public void r() throws ArrayStoreException;
3772                             public void s() throws NegativeArraySizeException;
3773                             public void t() throws MissingResourceException;
3774                             public void u() throws EmptyStackException;
3775                             public void v() throws CompletionException;
3776                             public void w() throws RejectedExecutionException;
3777                             public void x() throws IllformedLocaleException;
3778                             public void y() throws ConcurrentModificationException;
3779                             public void z() throws NoSuchElementException;
3780                             public void aa() throws UncheckedIOException;
3781                             public void ab() throws DateTimeException;
3782                             public void ac() throws ProviderException;
3783                             public void ad() throws BufferUnderflowException;
3784                             public void ae() throws BufferOverflowException;
3785                             public void af() throws AssertionError;
3786                         }
3787                     """
3788                 ),
3789             )
3790         )
3791     }
3792 
3793     @Test
Nullability overrides in unbounded generics should be allowednull3794     fun `Nullability overrides in unbounded generics should be allowed`() {
3795         check(
3796             apiLint = "",
3797             sourceFiles = arrayOf(
3798                 kotlin(
3799                     """
3800                         package test.pkg;
3801 
3802                         interface Base<T> {
3803                             fun method1(input: T): T
3804                         }
3805 
3806                         class Subject1 : Base<String> {
3807                             override fun method1(input: String): String {
3808                                 TODO()
3809                             }
3810                         }
3811 
3812                         class Subject2 : Base<String?> {
3813                             override fun method1(input: String?): String? {
3814                                 TODO()
3815                             }
3816                         }
3817                     """
3818                 )
3819             )
3820         )
3821     }
3822 
3823     @Test
Nullability overrides in unbounded generics (Object to generic and back)null3824     fun `Nullability overrides in unbounded generics (Object to generic and back)`() {
3825         check(
3826             apiLint = "",
3827             sourceFiles = arrayOf(
3828                 kotlin(
3829                     """
3830                         package test.pkg
3831 
3832                         open class SimpleArrayMap<K, V> {
3833                             open operator fun get(key: K): V? {
3834                                 TODO()
3835                             }
3836                         }
3837                     """
3838                 ),
3839                 java(
3840                     """
3841                         package test.pkg;
3842 
3843                         import java.util.Map;
3844 
3845                         public class ArrayMap<K, V> extends SimpleArrayMap<K, V> implements Map<K, V> {
3846                             @Override
3847                             @Nullable
3848                             public V get(@NonNull Object key) {
3849                                 return super.get((K) key);
3850                             }
3851                         }
3852 
3853                     """
3854                 )
3855             )
3856         )
3857     }
3858 
3859     @Test
Nullability overrides in unbounded generics (one super method lacks nullness info)null3860     fun `Nullability overrides in unbounded generics (one super method lacks nullness info)`() {
3861         check(
3862             apiLint = "",
3863             sourceFiles = arrayOf(
3864                 kotlin(
3865                     """
3866                         package test.pkg
3867 
3868                         open class SimpleArrayMap<K, V> {
3869                             open operator fun get(key: K): V? {
3870                                 TODO()
3871                             }
3872                         }
3873                     """
3874                 ),
3875                 java(
3876                     """
3877                         package test.pkg;
3878 
3879                         import java.util.Map;
3880 
3881                         public class ArrayMap<K, V> extends SimpleArrayMap<K, V> implements Map<K, V> {
3882                             @Override
3883                             @Nullable
3884                             public V get(@Nullable Object key) {
3885                                 return super.get((K) key);
3886                             }
3887                         }
3888 
3889                     """
3890                 )
3891             )
3892         )
3893     }
3894 }
3895