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