1 /* 2 * Copyright (C) 2017 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.model.provider.Capability 20 import com.android.tools.metalava.model.testing.RequiresCapabilities 21 import com.android.tools.metalava.model.text.FileFormat 22 import com.android.tools.metalava.testing.KnownSourceFiles 23 import com.android.tools.metalava.testing.java 24 import com.android.tools.metalava.testing.kotlin 25 import org.junit.Test 26 27 @SuppressWarnings("ALL") // Sample code 28 class ExtractAnnotationsTest : DriverTest() { 29 30 private val sourceFiles1 = 31 arrayOf( 32 java( 33 """ 34 package test.pkg; 35 36 import android.annotation.IntDef; 37 import android.annotation.IntRange; 38 39 import java.lang.annotation.Retention; 40 import java.lang.annotation.RetentionPolicy; 41 42 @SuppressWarnings({"UnusedDeclaration", "WeakerAccess"}) 43 public class IntDefTest { 44 @IntDef({STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT}) 45 @IntRange(from = 20) 46 private @interface DialogStyle {} 47 48 public static final int STYLE_NORMAL = 0; 49 public static final int STYLE_NO_TITLE = 1; 50 public static final int STYLE_NO_FRAME = 2; 51 public static final int STYLE_NO_INPUT = 3; 52 public static final int UNRELATED = 3; 53 54 public void setStyle(@DialogStyle int style, int theme) { 55 } 56 57 public void testIntDef(int arg) { 58 } 59 @IntDef(value = {STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT, 3, 3 + 1}, flag=true) 60 @Retention(RetentionPolicy.SOURCE) 61 private @interface DialogFlags {} 62 63 public void setFlags(Object first, @DialogFlags int flags) { 64 } 65 66 public static final String TYPE_1 = "type1"; 67 public static final String TYPE_2 = "type2"; 68 public static final String UNRELATED_TYPE = "other"; 69 70 public static class Inner { 71 public void setInner(@DialogFlags int flags) { 72 } 73 } 74 } 75 """ 76 ) 77 .indented(), 78 intDefAnnotationSource, 79 intRangeAnnotationSource, 80 // Hide android.annotation classes. 81 KnownSourceFiles.androidAnnotationHide, 82 ) 83 84 @Test Check java typedef extraction and warning about non-source retention of typedefsnull85 fun `Check java typedef extraction and warning about non-source retention of typedefs`() { 86 check( 87 format = FileFormat.V2, 88 sourceFiles = sourceFiles1, 89 expectedIssues = 90 "src/test/pkg/IntDefTest.java:13: error: This typedef annotation class should have @Retention(RetentionPolicy.SOURCE) [AnnotationExtraction]", 91 extractAnnotations = 92 mapOf( 93 "test.pkg" to 94 """ 95 <?xml version="1.0" encoding="UTF-8"?> 96 <root> 97 <item name="test.pkg.IntDefTest void setFlags(java.lang.Object, int) 1"> 98 <annotation name="androidx.annotation.IntDef"> 99 <val name="value" val="{test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT, 3, 4}" /> 100 <val name="flag" val="true" /> 101 </annotation> 102 </item> 103 <item name="test.pkg.IntDefTest void setStyle(int, int) 0"> 104 <annotation name="androidx.annotation.IntDef"> 105 <val name="value" val="{test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT}" /> 106 </annotation> 107 </item> 108 <item name="test.pkg.IntDefTest.Inner void setInner(int) 0"> 109 <annotation name="androidx.annotation.IntDef"> 110 <val name="value" val="{test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT, 3, 4}" /> 111 <val name="flag" val="true" /> 112 </annotation> 113 </item> 114 </root> 115 """ 116 ) 117 ) 118 } 119 120 @RequiresCapabilities(Capability.KOTLIN) 121 @Test Check Kotlin and referencing hidden constants from typedefnull122 fun `Check Kotlin and referencing hidden constants from typedef`() { 123 check( 124 sourceFiles = 125 arrayOf( 126 kotlin( 127 """ 128 @file:Suppress("unused", "UseExpressionBody") 129 130 package test.pkg 131 132 import android.annotation.LongDef 133 134 const val STYLE_NORMAL = 0L 135 const val STYLE_NO_TITLE = 1L 136 const val STYLE_NO_FRAME = 2L 137 const val STYLE_NO_INPUT = 3L 138 const val UNRELATED = 3L 139 private const val HIDDEN = 4 140 141 const val TYPE_1 = "type1" 142 const val TYPE_2 = "type2" 143 const val UNRELATED_TYPE = "other" 144 145 class LongDefTest { 146 147 /** @hide */ 148 @LongDef(STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT, HIDDEN) 149 @Retention(AnnotationRetention.SOURCE) 150 private annotation class DialogStyle 151 152 fun setStyle(@DialogStyle style: Int, theme: Int) {} 153 154 fun testLongDef(arg: Int) { 155 } 156 157 @LongDef(STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT, 3L, 3L + 1L, flag = true) 158 @Retention(AnnotationRetention.SOURCE) 159 private annotation class DialogFlags 160 161 fun setFlags(first: Any, @DialogFlags flags: Int) {} 162 163 class Inner { 164 fun setInner(@DialogFlags flags: Int) {} 165 fun isNull(value: String?): Boolean 166 } 167 }""" 168 ) 169 .indented(), 170 longDefAnnotationSource 171 ), 172 expectedIssues = 173 "src/test/pkg/LongDefTest.kt:12: error: Typedef class references hidden field field LongDefTestKt.HIDDEN: removed from typedef metadata [HiddenTypedefConstant]", 174 extractAnnotations = 175 mapOf( 176 "test.pkg" to 177 """ 178 <?xml version="1.0" encoding="UTF-8"?> 179 <root> 180 <item name="test.pkg.LongDefTest void setFlags(java.lang.Object, int) 1"> 181 <annotation name="androidx.annotation.LongDef"> 182 <val name="value" val="{test.pkg.LongDefTestKt.STYLE_NORMAL, test.pkg.LongDefTestKt.STYLE_NO_TITLE, test.pkg.LongDefTestKt.STYLE_NO_FRAME, test.pkg.LongDefTestKt.STYLE_NO_INPUT, 3, 4L}" /> 183 <val name="flag" val="true" /> 184 </annotation> 185 </item> 186 <item name="test.pkg.LongDefTest void setStyle(int, int) 0"> 187 <annotation name="androidx.annotation.LongDef"> 188 <val name="value" val="{test.pkg.LongDefTestKt.STYLE_NORMAL, test.pkg.LongDefTestKt.STYLE_NO_TITLE, test.pkg.LongDefTestKt.STYLE_NO_FRAME, test.pkg.LongDefTestKt.STYLE_NO_INPUT}" /> 189 </annotation> 190 </item> 191 <item name="test.pkg.LongDefTest.Inner void setInner(int) 0"> 192 <annotation name="androidx.annotation.LongDef"> 193 <val name="value" val="{test.pkg.LongDefTestKt.STYLE_NORMAL, test.pkg.LongDefTestKt.STYLE_NO_TITLE, test.pkg.LongDefTestKt.STYLE_NO_FRAME, test.pkg.LongDefTestKt.STYLE_NO_INPUT, 3, 4L}" /> 194 <val name="flag" val="true" /> 195 </annotation> 196 </item> 197 </root> 198 """ 199 ) 200 ) 201 } 202 203 @RequiresCapabilities(Capability.KOTLIN) 204 @Test Check including only class retention annotations other than typedefsnull205 fun `Check including only class retention annotations other than typedefs`() { 206 check( 207 sourceFiles = 208 arrayOf( 209 kotlin( 210 """ 211 @file:Suppress("unused", "UseExpressionBody") 212 213 package test.pkg 214 215 import android.annotation.LongDef 216 217 const val STYLE_NORMAL = 0L 218 const val STYLE_NO_TITLE = 1L 219 const val STYLE_NO_FRAME = 2L 220 const val STYLE_NO_INPUT = 3L 221 const val UNRELATED = 3L 222 private const val HIDDEN = 4 223 224 const val TYPE_1 = "type1" 225 const val TYPE_2 = "type2" 226 const val UNRELATED_TYPE = "other" 227 228 class LongDefTest { 229 230 /** @hide */ 231 @LongDef(STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT, HIDDEN) 232 @Retention(AnnotationRetention.SOURCE) 233 private annotation class DialogStyle 234 235 fun setStyle(@DialogStyle style: Int, theme: Int) {} 236 237 fun testLongDef(arg: Int) { 238 } 239 240 @LongDef(STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT, 3L, 3L + 1L, flag = true) 241 @Retention(AnnotationRetention.SOURCE) 242 private annotation class DialogFlags 243 244 fun setFlags(first: Any, @DialogFlags flags: Int) {} 245 246 class Inner { 247 fun setInner(@DialogFlags flags: Int) {} 248 fun isNull(value: String?): Boolean 249 } 250 }""" 251 ) 252 .indented(), 253 longDefAnnotationSource 254 ), 255 expectedIssues = 256 "src/test/pkg/LongDefTest.kt:12: error: Typedef class references hidden field field LongDefTestKt.HIDDEN: removed from typedef metadata [HiddenTypedefConstant]", 257 extractAnnotations = 258 mapOf( 259 "test.pkg" to 260 """ 261 <?xml version="1.0" encoding="UTF-8"?> 262 <root> 263 <item name="test.pkg.LongDefTest void setFlags(java.lang.Object, int) 1"> 264 <annotation name="androidx.annotation.LongDef"> 265 <val name="value" val="{test.pkg.LongDefTestKt.STYLE_NORMAL, test.pkg.LongDefTestKt.STYLE_NO_TITLE, test.pkg.LongDefTestKt.STYLE_NO_FRAME, test.pkg.LongDefTestKt.STYLE_NO_INPUT, 3, 4L}" /> 266 <val name="flag" val="true" /> 267 </annotation> 268 </item> 269 <item name="test.pkg.LongDefTest void setStyle(int, int) 0"> 270 <annotation name="androidx.annotation.LongDef"> 271 <val name="value" val="{test.pkg.LongDefTestKt.STYLE_NORMAL, test.pkg.LongDefTestKt.STYLE_NO_TITLE, test.pkg.LongDefTestKt.STYLE_NO_FRAME, test.pkg.LongDefTestKt.STYLE_NO_INPUT}" /> 272 </annotation> 273 </item> 274 <item name="test.pkg.LongDefTest.Inner void setInner(int) 0"> 275 <annotation name="androidx.annotation.LongDef"> 276 <val name="value" val="{test.pkg.LongDefTestKt.STYLE_NORMAL, test.pkg.LongDefTestKt.STYLE_NO_TITLE, test.pkg.LongDefTestKt.STYLE_NO_FRAME, test.pkg.LongDefTestKt.STYLE_NO_INPUT, 3, 4L}" /> 277 <val name="flag" val="true" /> 278 </annotation> 279 </item> 280 </root> 281 """ 282 ) 283 ) 284 } 285 286 @Test Extract permission annotationsnull287 fun `Extract permission annotations`() { 288 check( 289 sourceFiles = 290 arrayOf( 291 java( 292 """ 293 package test.pkg; 294 295 import android.annotation.RequiresPermission; 296 297 public class PermissionsTest { 298 @RequiresPermission(Manifest.permission.MY_PERMISSION) 299 public void myMethod() { 300 } 301 @RequiresPermission(anyOf={Manifest.permission.MY_PERMISSION,Manifest.permission.MY_PERMISSION2}) 302 public void myMethod2() { 303 } 304 305 @RequiresPermission.Read(@RequiresPermission(Manifest.permission.MY_READ_PERMISSION)) 306 @RequiresPermission.Write(@RequiresPermission(Manifest.permission.MY_WRITE_PERMISSION)) 307 public static final String CONTENT_URI = ""; 308 } 309 """ 310 ) 311 .indented(), 312 java( 313 """ 314 package test.pkg; 315 316 public class Manifest { 317 public static final class permission { 318 public static final String MY_PERMISSION = "android.permission.MY_PERMISSION_STRING"; 319 public static final String MY_PERMISSION2 = "android.permission.MY_PERMISSION_STRING2"; 320 public static final String MY_READ_PERMISSION = "android.permission.MY_READ_PERMISSION_STRING"; 321 public static final String MY_WRITE_PERMISSION = "android.permission.MY_WRITE_PERMISSION_STRING"; 322 } 323 } 324 """ 325 ) 326 .indented(), 327 requiresPermissionSource 328 ), 329 extractAnnotations = 330 mapOf( 331 "test.pkg" to 332 """ 333 <?xml version="1.0" encoding="UTF-8"?> 334 <root> 335 <item name="test.pkg.PermissionsTest CONTENT_URI"> 336 <annotation name="androidx.annotation.RequiresPermission.Read"> 337 <val name="value" val=""android.permission.MY_READ_PERMISSION_STRING"" /> 338 </annotation> 339 <annotation name="androidx.annotation.RequiresPermission.Write"> 340 <val name="value" val=""android.permission.MY_WRITE_PERMISSION_STRING"" /> 341 </annotation> 342 </item> 343 <item name="test.pkg.PermissionsTest void myMethod()"> 344 <annotation name="androidx.annotation.RequiresPermission"> 345 <val name="value" val=""android.permission.MY_PERMISSION_STRING"" /> 346 </annotation> 347 </item> 348 <item name="test.pkg.PermissionsTest void myMethod2()"> 349 <annotation name="androidx.annotation.RequiresPermission"> 350 <val name="anyOf" val="{"android.permission.MY_PERMISSION_STRING", "android.permission.MY_PERMISSION_STRING2"}" /> 351 </annotation> 352 </item> 353 </root> 354 """ 355 ) 356 ) 357 } 358 359 @Test Include merged annotations in exported source annotationsnull360 fun `Include merged annotations in exported source annotations`() { 361 check( 362 format = FileFormat.V2, 363 includeSystemApiAnnotations = false, 364 expectedIssues = "error: Unexpected reference to Nonexistent.Field [InternalError]", 365 sourceFiles = 366 arrayOf( 367 java( 368 """ 369 package test.pkg; 370 371 public class MyTest { 372 public int test(int arg) { } 373 }""" 374 ), 375 java( 376 """ 377 package java.util; 378 public class Calendar { 379 public static final int ERA = 1; 380 public static final int YEAR = 2; 381 public static final int MONTH = 3; 382 public static final int WEEK_OF_YEAR = 4; 383 } 384 """ 385 ) 386 ), 387 mergeXmlAnnotations = 388 """<?xml version="1.0" encoding="UTF-8"?> 389 <root> 390 <item name="test.pkg.MyTest int test(int) 0"> 391 <annotation name="org.intellij.lang.annotations.MagicConstant"> 392 <val name="intValues" val="{java.util.Calendar.ERA, java.util.Calendar.YEAR, java.util.Calendar.MONTH, java.util.Calendar.WEEK_OF_YEAR, Nonexistent.Field}" /> 393 </annotation> 394 </item> 395 <item name="test.pkg.MyTest int test(int)"> 396 <annotation name="androidx.annotation.IntDef"> 397 <val name="flag" val="true" /> 398 <val name="value" val="{java.util.Calendar.ERA, java.util.Calendar.YEAR}" /> 399 </annotation> 400 </item> 401 </root> 402 """, 403 extractAnnotations = 404 mapOf( 405 "test.pkg" to 406 """ 407 <?xml version="1.0" encoding="UTF-8"?> 408 <root> 409 <item name="test.pkg.MyTest int test(int)"> 410 <annotation name="androidx.annotation.IntDef"> 411 <val name="value" val="{java.util.Calendar.ERA, java.util.Calendar.YEAR}" /> 412 <val name="flag" val="true" /> 413 </annotation> 414 </item> 415 <item name="test.pkg.MyTest int test(int) 0"> 416 <annotation name="androidx.annotation.IntDef"> 417 <val name="value" val="{java.util.Calendar.ERA, java.util.Calendar.YEAR, java.util.Calendar.MONTH, java.util.Calendar.WEEK_OF_YEAR}" /> 418 </annotation> 419 </item> 420 </root> 421 """ 422 ) 423 ) 424 } 425 426 @Test Only including class retention annotations in stubsnull427 fun `Only including class retention annotations in stubs`() { 428 check( 429 format = FileFormat.V2, 430 includeSystemApiAnnotations = false, 431 sourceFiles = 432 arrayOf( 433 java( 434 """ 435 package test.pkg; 436 import android.annotation.IntRange; 437 import androidx.annotation.RecentlyNullable; 438 public class Test { 439 @RecentlyNullable 440 public static String sayHello(@IntRange(from = 10) int value) { return "hello " + value; } 441 } 442 """ 443 ), 444 intRangeAnnotationSource, 445 recentlyNullableSource 446 ), 447 stubFiles = 448 arrayOf( 449 java( 450 """ 451 package test.pkg; 452 @SuppressWarnings({"unchecked", "deprecation", "all"}) 453 public class Test { 454 public Test() { throw new RuntimeException("Stub!"); } 455 @androidx.annotation.RecentlyNullable 456 public static java.lang.String sayHello(int value) { throw new RuntimeException("Stub!"); } 457 } 458 """ 459 ) 460 ), 461 extractAnnotations = 462 mapOf( 463 "test.pkg" to 464 """ 465 <?xml version="1.0" encoding="UTF-8"?> 466 <root> 467 <item name="test.pkg.Test java.lang.String sayHello(int) 0"> 468 <annotation name="androidx.annotation.IntRange"> 469 <val name="from" val="10" /> 470 </annotation> 471 </item> 472 </root> 473 """ 474 ) 475 ) 476 } 477 478 @Test Check warning about unexpected returns from typedef methodnull479 fun `Check warning about unexpected returns from typedef method`() { 480 check( 481 expectedIssues = 482 "src/test/pkg/IntDefTest.java:36: warning: Returning unexpected constant UNRELATED; is @DialogStyle missing this constant? Expected one of STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT [ReturningUnexpectedConstant]", 483 sourceFiles = 484 arrayOf( 485 java( 486 """ 487 package test.pkg; 488 489 import android.annotation.IntDef; 490 import android.annotation.IntRange; 491 import java.lang.annotation.Retention; 492 import java.lang.annotation.RetentionPolicy; 493 494 @SuppressWarnings({"UnusedDeclaration", "WeakerAccess"}) 495 public class IntDefTest { 496 @IntDef({STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT}) 497 @IntRange(from = 20) 498 @Retention(RetentionPolicy.SOURCE) 499 private @interface DialogStyle { 500 } 501 502 public static final int STYLE_NORMAL = 0; 503 public static final int STYLE_NO_TITLE = 1; 504 public static final int STYLE_NO_FRAME = 2; 505 public static final int STYLE_NO_INPUT = 3; 506 public static final int UNRELATED = 3; 507 public static final int[] EMPTY_ARRAY = new int[0]; 508 509 private int mField1 = 4; 510 private int mField2 = 5; 511 512 @DialogStyle 513 public int getStyle1() { 514 //noinspection ConstantConditions 515 if (mField1 < 1) { 516 return STYLE_NO_TITLE; // OK 517 } else if (mField1 < 2) { 518 return 0; // OK 519 } else if (mField1 < 3) { 520 return mField2; // OK 521 } else { 522 return UNRELATED; // WARN 523 } 524 } 525 526 @DialogStyle 527 public int[] getStyle2() { 528 return EMPTY_ARRAY; // OK 529 } 530 } 531 """ 532 ) 533 .indented(), 534 intDefAnnotationSource, 535 intRangeAnnotationSource 536 ), 537 extractAnnotations = 538 mapOf( 539 "test.pkg" to 540 """ 541 <?xml version="1.0" encoding="UTF-8"?> 542 <root> 543 <item name="test.pkg.IntDefTest int getStyle1()"> 544 <annotation name="androidx.annotation.IntDef"> 545 <val name="value" val="{test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT}" /> 546 </annotation> 547 </item> 548 <item name="test.pkg.IntDefTest int[] getStyle2()"> 549 <annotation name="androidx.annotation.IntDef"> 550 <val name="value" val="{test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT}" /> 551 </annotation> 552 </item> 553 </root> 554 """ 555 ) 556 ) 557 } 558 559 @Test No typedef signatures in api filesnull560 fun `No typedef signatures in api files`() { 561 check( 562 extraArguments = arrayOf(ARG_TYPEDEFS_IN_SIGNATURES, "none"), 563 format = FileFormat.V2, 564 sourceFiles = sourceFiles1, 565 api = 566 """ 567 // Signature format: 2.0 568 package test.pkg { 569 public class IntDefTest { 570 ctor public IntDefTest(); 571 method public void setFlags(Object, int); 572 method public void setStyle(int, int); 573 method public void testIntDef(int); 574 field public static final int STYLE_NORMAL = 0; // 0x0 575 field public static final int STYLE_NO_FRAME = 2; // 0x2 576 field public static final int STYLE_NO_INPUT = 3; // 0x3 577 field public static final int STYLE_NO_TITLE = 1; // 0x1 578 field public static final String TYPE_1 = "type1"; 579 field public static final String TYPE_2 = "type2"; 580 field public static final int UNRELATED = 3; // 0x3 581 field public static final String UNRELATED_TYPE = "other"; 582 } 583 public static class IntDefTest.Inner { 584 ctor public IntDefTest.Inner(); 585 method public void setInner(int); 586 } 587 } 588 """ 589 ) 590 } 591 592 @Test Inlining typedef signatures in api filesnull593 fun `Inlining typedef signatures in api files`() { 594 check( 595 extraArguments = arrayOf(ARG_TYPEDEFS_IN_SIGNATURES, "inline"), 596 format = FileFormat.V2, 597 sourceFiles = sourceFiles1, 598 api = 599 """ 600 // Signature format: 2.0 601 package test.pkg { 602 public class IntDefTest { 603 ctor public IntDefTest(); 604 method public void setFlags(Object, @IntDef(value={test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT, 3, 3 + 1}, flag=true) int); 605 method public void setStyle(@IntDef({test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT}) int, int); 606 method public void testIntDef(int); 607 field public static final int STYLE_NORMAL = 0; // 0x0 608 field public static final int STYLE_NO_FRAME = 2; // 0x2 609 field public static final int STYLE_NO_INPUT = 3; // 0x3 610 field public static final int STYLE_NO_TITLE = 1; // 0x1 611 field public static final String TYPE_1 = "type1"; 612 field public static final String TYPE_2 = "type2"; 613 field public static final int UNRELATED = 3; // 0x3 614 field public static final String UNRELATED_TYPE = "other"; 615 } 616 public static class IntDefTest.Inner { 617 ctor public IntDefTest.Inner(); 618 method public void setInner(@IntDef(value={test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT, 3, 3 + 1}, flag=true) int); 619 } 620 } 621 """ 622 ) 623 } 624 625 @Test Referencing typedef signatures in api filesnull626 fun `Referencing typedef signatures in api files`() { 627 check( 628 extraArguments = arrayOf(ARG_TYPEDEFS_IN_SIGNATURES, "ref"), 629 format = FileFormat.V2, 630 sourceFiles = sourceFiles1, 631 api = 632 """ 633 // Signature format: 2.0 634 package test.pkg { 635 public class IntDefTest { 636 ctor public IntDefTest(); 637 method public void setFlags(Object, @DialogFlags int); 638 method public void setStyle(@DialogStyle int, int); 639 method public void testIntDef(int); 640 field public static final int STYLE_NORMAL = 0; // 0x0 641 field public static final int STYLE_NO_FRAME = 2; // 0x2 642 field public static final int STYLE_NO_INPUT = 3; // 0x3 643 field public static final int STYLE_NO_TITLE = 1; // 0x1 644 field public static final String TYPE_1 = "type1"; 645 field public static final String TYPE_2 = "type2"; 646 field public static final int UNRELATED = 3; // 0x3 647 field public static final String UNRELATED_TYPE = "other"; 648 } 649 public static class IntDefTest.Inner { 650 ctor public IntDefTest.Inner(); 651 method public void setInner(@DialogFlags int); 652 } 653 } 654 """ 655 ) 656 } 657 658 @Test Test generics in XML attributes are encodednull659 fun `Test generics in XML attributes are encoded`() { 660 check( 661 format = FileFormat.V2, 662 includeSystemApiAnnotations = false, 663 sourceFiles = 664 arrayOf( 665 java( 666 """ 667 package test.pkg; 668 669 import android.annotation.IntRange; 670 import java.util.List; 671 672 public class MyTest { 673 public void test(List<Integer> genericArgument, @IntRange(from = 10) int foo) { } 674 }""" 675 ), 676 intRangeAnnotationSource 677 ), 678 extractAnnotations = 679 mapOf( 680 "test.pkg" to 681 """ 682 <?xml version="1.0" encoding="UTF-8"?> 683 <root> 684 <item name="test.pkg.MyTest void test(java.util.List<java.lang.Integer>, int) 1"> 685 <annotation name="androidx.annotation.IntRange"> 686 <val name="from" val="10" /> 687 </annotation> 688 </item> 689 </root> 690 """ 691 ) 692 ) 693 } 694 695 @Test Test string literal encodingnull696 fun `Test string literal encoding`() { 697 check( 698 sourceFiles = 699 arrayOf( 700 java( 701 """ 702 package test.pkg; 703 704 import android.annotation.RequiresPermission; 705 706 public class PermissionsTest { 707 @RequiresPermission("'&\"") 708 public static final String CONTENT_URI = ""; 709 } 710 """ 711 ) 712 .indented(), 713 requiresPermissionSource, 714 ), 715 extractAnnotations = 716 mapOf( 717 "test.pkg" to 718 """ 719 <?xml version="1.0" encoding="UTF-8"?> 720 <root> 721 <item name="test.pkg.PermissionsTest CONTENT_URI"> 722 <annotation name="androidx.annotation.RequiresPermission"> 723 <val name="value" val=""\'&\""" /> 724 </annotation> 725 </item> 726 </root> 727 """ 728 ) 729 ) 730 } 731 732 @Test Test annotations on inherited methodsnull733 fun `Test annotations on inherited methods`() { 734 check( 735 sourceFiles = 736 arrayOf( 737 intDefAnnotationSource, 738 java( 739 """ 740 package test.pkg; 741 742 import android.annotation.IntDef; 743 744 import java.lang.annotation.Retention; 745 import java.lang.annotation.RetentionPolicy; 746 747 @SuppressWarnings({"UnusedDeclaration", "WeakerAccess"}) 748 public class PublicClass { 749 /** @hide */ 750 @IntDef({VALUE1, VALUE2}) 751 @Retention(RetentionPolicy.SOURCE) 752 protected @interface IntDefType {} 753 754 public static final int VALUE1 = 0; 755 public static final int VALUE2 = 1; 756 757 static class HiddenNestedClass { 758 private final int intDefTypeValue = VALUE1; 759 760 public @IntDefType int getIntDefType() { return intDefTypeValue; } 761 } 762 763 public static class PublicNestedClassA extends HiddenNestedClass {} 764 public static class PublicNestedClassB extends HiddenNestedClass {} 765 } 766 """ 767 ), 768 ), 769 extractAnnotations = 770 mapOf( 771 "test.pkg" to 772 // TODO(b/329116156): One of the IntDef annotations should be on 773 // PublicNestedClassB 774 """ 775 <?xml version="1.0" encoding="UTF-8"?> 776 <root> 777 <item name="test.pkg.PublicClass.PublicNestedClassA int getIntDefType()"> 778 <annotation name="androidx.annotation.IntDef"> 779 <val name="value" val="{test.pkg.PublicClass.VALUE1, test.pkg.PublicClass.VALUE2}" /> 780 </annotation> 781 </item> 782 <item name="test.pkg.PublicClass.PublicNestedClassB int getIntDefType()"> 783 <annotation name="androidx.annotation.IntDef"> 784 <val name="value" val="{test.pkg.PublicClass.VALUE1, test.pkg.PublicClass.VALUE2}" /> 785 </annotation> 786 </item> 787 </root> 788 """ 789 ) 790 ) 791 } 792 793 @Test Test generating annotations zip from signature filenull794 fun `Test generating annotations zip from signature file`() { 795 check( 796 signatureSources = 797 arrayOf( 798 """ 799 // Signature format: 2.0 800 package test.pkg { 801 public class Foo { 802 method @RequiresPermission(test.pkg.Permissions.PERMISSION1) public void foo1(); 803 method @RequiresPermission(test.pkg.Permissions.PERMISSION2) public void foo2(); 804 method @RequiresPermission("UnresolvedPermission") public void foo3(); 805 } 806 public class Permissions { 807 field public static final String PERMISSION1 = "Permission1"; 808 field public static final String PERMISSION2 = "Permission2"; 809 } 810 } 811 """, 812 ), 813 extractAnnotations = 814 mapOf( 815 "test.pkg" to 816 // TODO(b/331752084): Add missing annotations 817 """ 818 <?xml version="1.0" encoding="UTF-8"?> 819 <root> 820 <item name="test.pkg.Foo void foo1()"> 821 </item> 822 <item name="test.pkg.Foo void foo2()"> 823 </item> 824 <item name="test.pkg.Foo void foo3()"> 825 </item> 826 </root> 827 """ 828 ) 829 ) 830 } 831 } 832