xref: /aosp_15_r20/tools/metalava/metalava/src/test/java/com/android/tools/metalava/AndroidApiChecksTest.kt (revision 115816f9299ab6ddd6b9673b81f34e707f6bacab)
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tools.metalava
18 
19 import com.android.tools.metalava.cli.common.ARG_WARNING
20 import com.android.tools.metalava.lint.DefaultLintErrorMessage
21 import com.android.tools.metalava.reporter.Issues
22 import com.android.tools.metalava.testing.java
23 import org.junit.Test
24 
25 class AndroidApiChecksTest : DriverTest() {
26     @Test
Flag TODO documentationnull27     fun `Flag TODO documentation`() {
28         check(
29             expectedFail = DefaultLintErrorMessage,
30             expectedIssues =
31                 """
32                 src/android/pkg/Test.java:4: error: Documentation mentions 'TODO' [Todo]
33                 src/android/pkg/Test.java:6: error: Documentation mentions 'TODO' [Todo]
34                 """,
35             sourceFiles =
36                 arrayOf(
37                     // Nothing in outside of Android
38                     java(
39                         """
40                     package test.pkg;
41                     /** TODO: Some comment here */
42                     public class Ignored1 {
43                     }
44                     """
45                     ),
46                     // Nothing in android.icu
47                     java(
48                         """
49                     package android.icu;
50                     /** TODO: Some comment here */
51                     public class Ignored2 {
52                     }
53                     """
54                     ),
55                     java(
56                         """
57                     package android.pkg;
58 
59                     /** TODO: Some comment here */
60                     public class Test {
61                        /** TODO(ldap): Some comment here */
62                         public void fun() {
63                             // TODO: Code doesn't count
64                         }
65                     }
66                     """
67                     )
68                 )
69         )
70     }
71 
72     @Test
Document Permissionsnull73     fun `Document Permissions`() {
74         check(
75             expectedFail = DefaultLintErrorMessage,
76             expectedIssues =
77                 """
78                 src/android/pkg/PermissionTest.java:14: error: Method 'test0' documentation mentions permissions without declaring @RequiresPermission [RequiresPermission]
79                 src/android/pkg/PermissionTest.java:21: error: Method 'test1' documentation duplicates auto-generated documentation by @RequiresPermission. If the permissions are only required under certain circumstances use conditional=true to suppress the auto-documentation [RequiresPermission]
80                 src/android/pkg/PermissionTest.java:41: warning: Method 'conditionalBad' documentation does not explain when the conditional permission 'ACCESS_COARSE_LOCATION' is required. [ConditionalRequiresPermissionNotExplained]
81                 """,
82             sourceFiles =
83                 arrayOf(
84                     java(
85                         """
86                     package android.pkg;
87 
88                     import android.Manifest;
89                     import android.annotation.RequiresPermission;
90 
91                     public class PermissionTest {
92 
93                         // Flag methods that talk about permissions in the documentation
94                         // but isn't annotated
95                         /**
96                         * Blah blah.
97                         * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
98                         */
99                         public void test0() {
100                         }
101 
102                         // Flag methods which has permission annotation, but has
103                         // documentation mentioning other permissions that are not listed
104                         /** Blah blah blah ACCESS_COARSE_LOCATION */
105                         @RequiresPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
106                         public void test1() {
107                         }
108 
109                         // TODO: Flag methods which has permission annotation, but where one
110                         // of the permissions is annotated but not mentioned
111                         @RequiresPermission(allOf = Manifest.permission.ACCESS_COARSE_LOCATION)
112                         public void test2() {
113                         }
114 
115                         /**
116                          * Sometimes requires {@link Manifest.permission#ACCESS_COARSE_LOCATION}.
117                          */
118                         @RequiresPermission(allOf = Manifest.permission.ACCESS_COARSE_LOCATION, conditional = true)
119                         public void conditionalOk() {
120                         }
121 
122                         /**
123                          * Not documenting the conditional permission.
124                          */
125                         @RequiresPermission(allOf = Manifest.permission.ACCESS_COARSE_LOCATION, conditional = true)
126                         public void conditionalBad() {
127                         }
128                     }
129                     """
130                     ),
131                     java(
132                         """
133                     package android;
134 
135                     public abstract class Manifest {
136                         public static final class permission {
137                             public static final String ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION";
138                             public static final String ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION";
139                             public static final String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER";
140                         }
141                     }
142                     """
143                     ),
144                     requiresPermissionSource
145                 ),
146             extraArguments =
147                 arrayOf(ARG_WARNING, Issues.CONDITIONAL_REQUIRES_PERMISSION_NOT_EXPLAINED.name),
148         )
149     }
150 
151     @Test
Document Permissions ignore when permission is subset of a wordnull152     fun `Document Permissions ignore when permission is subset of a word`() {
153         check(
154             sourceFiles =
155                 arrayOf(
156                     java(
157                         """
158                             package android;
159 
160                             public abstract class Manifest {
161                                 public static final class permission {
162                                     public static final String PERMISSION = "android.permission.PERMISSION";
163                                 }
164                             }
165                         """
166                     ),
167                     requiresPermissionSource,
168                     java(
169                         """
170                             package android.pkg;
171 
172                             import android.Manifest;
173                             import android.annotation.RequiresPermission;
174 
175                             public class PermissionTest {
176                                 /**
177                                  * While this contains the name of the permission it is not actually
178                                  * referring to the permission ARG_PERMISSION.
179                                  */
180                                 @RequiresPermission(Manifest.permission.PERMISSION)
181                                 public void test0() {
182                                 }
183 
184                                 /**
185                                  * While this contains the name of the permission it is not actually
186                                  * referring to the permission PERMISSION_ARG.
187                                  */
188                                 @RequiresPermission(Manifest.permission.PERMISSION)
189                                 public void test0() {
190                                 }
191                             }
192                         """
193                     ),
194                 ),
195             extraArguments =
196                 arrayOf(ARG_WARNING, Issues.CONDITIONAL_REQUIRES_PERMISSION_NOT_EXPLAINED.name),
197         )
198     }
199 
200     @Test
Document Intent Actionsnull201     fun `Document Intent Actions`() {
202         check(
203             expectedFail = DefaultLintErrorMessage,
204             expectedIssues =
205                 """
206                 src/android/pkg/IntentActionTest.java:19: error: Field 'FOO_BAR_ERROR_ACTION' is missing @BroadcastBehavior [BroadcastBehavior]
207                 src/android/pkg/IntentActionTest.java:19: error: Field 'FOO_BAR_ERROR_ACTION' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) [SdkConstant]
208                 src/android/pkg/IntentActionTest.java:30: error: Field 'BAR_FOO_ERROR_ACTION' is missing @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) [SdkConstant]
209                 """,
210             sourceFiles =
211                 arrayOf(
212                     java(
213                         """
214                     package android.pkg;
215 
216                     import android.Manifest;
217                     import android.annotation.SdkConstant;
218                     import android.annotation.SdkConstant.SdkConstantType;
219                     import android.annotation.BroadcastBehavior;
220 
221                     public class IntentActionTest {
222                         /**
223                          * Broadcast Action: Foo Bar has started.
224                          */
225                         @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
226                         @BroadcastBehavior(includeBackground = true)
227                         public static final String FOO_BAR_OK_ACTION = "android.something.FOO_BAR";
228 
229                         /**
230                          * Broadcast Action: Foo Bar has started.
231                          */
232                         public static final String FOO_BAR_ERROR_ACTION = "android.something.FOO_BAR";
233 
234                         /**
235                          * Activity Action: Bar the Foo.
236                          */
237                         @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
238                         public static final String BAR_FOO_OK_ACTION = "android.something.BAR_FOO";
239 
240                         /**
241                          * Activity Action: Bar the Foo.
242                          */
243                         public static final String BAR_FOO_ERROR_ACTION = "android.something.BAR_FOO";
244                     }
245                     """
246                     ),
247                     sdkConstantSource,
248                     broadcastBehaviorSource
249                 )
250         )
251     }
252 
253     @Test
Check Warnings for missing nullness annotationsnull254     fun `Check Warnings for missing nullness annotations`() {
255         check(
256             expectedIssues =
257                 """
258                 src/android/pkg/NullMentions.java:9: warning: Field 'field2' documentation mentions 'null' without declaring @NonNull or @Nullable [Nullable]
259                 src/android/pkg/NullMentions.java:18: warning: Parameter 'param1' of 'method3' documentation mentions 'null' without declaring @NonNull or @Nullable [Nullable]
260                 src/android/pkg/NullMentions.java:21: warning: Return value of 'method4' documentation mentions 'null' without declaring @NonNull or @Nullable [Nullable]
261                 """,
262             extraArguments = arrayOf(ARG_WARNING, "Nullable"), // Hidden by default
263             sourceFiles =
264                 arrayOf(
265                     java(
266                         """
267                     package android.pkg;
268 
269                     import android.annotation.Nullable;
270 
271                     public class NullMentions {
272                         /** Blah blah */
273                         public Object field1;
274                         /** Blah blah. Never null. */
275                         public Object field2;
276                         /** Blah blah */
277                         public Object method1() { return null; }
278                         /** Blah blah. Sometimes null. */
279                         public Object method2() { return null; }
280                         /** Blah blah. Never null. */
281                         public Object method2(Object param1) { return null; }
282                         /** Blah blah. Never null.
283                          *  @param param1 Sometimes null. */
284                         public Object method3(Object param1) { return null; }
285                         /** Blah blah. Never null.
286                          *  @return Sometimes null. */
287                         public Object method4(Object param1) { return null; }
288                         /** Blah blah. Never null.
289                          *  @param param1 Sometimes null.
290                          *  @return Sometimes null. */
291                         public @Nullable Object method5(@Nullable Object param1) { return null; }
292                     }
293                     """
294                     ),
295                     nullableSource
296                 )
297         )
298     }
299 
300     @Test
Check IntDef Warningsnull301     fun `Check IntDef Warnings`() {
302         check(
303             expectedIssues =
304                 """
305                 src/android/pkg/NullMentions.java:16: warning: Field 'field1' documentation mentions constants without declaring an @IntDef [IntDef]
306                 """,
307             extraArguments = arrayOf(ARG_WARNING, "IntDef"), // Hidden by default
308             sourceFiles =
309                 arrayOf(
310                     java(
311                         """
312                     package android.pkg;
313 
314                     import android.annotation.IntDef;
315                     import android.annotation.Nullable;
316                     import java.lang.annotation.Retention;
317                     import java.lang.annotation.RetentionPolicy;
318 
319                     public class NullMentions {
320                         @IntDef({CONSTANT_ONE, CONSTANT_TWO})
321                         @Retention(RetentionPolicy.SOURCE)
322                         private @interface MyStyle {}
323 
324                         public static final int CONSTANT_ONE = 1;
325                         public static final int CONSTANT_TWO = 12;
326                         /** Should be CONSTANT_ONE or CONSTANT_TWO */
327                         public int field1; // WARN
328 
329                         /** Should be CONSTANT_ONE or CONSTANT_TWO */
330                         @MyStyle
331                         public int field2; // OK
332                     }
333                     """
334                     ),
335                     intDefAnnotationSource,
336                     nullableSource
337                 )
338         )
339     }
340 }
341