xref: /aosp_15_r20/tools/metalava/metalava/src/test/java/com/android/tools/metalava/ExtractAnnotationsTest.kt (revision 115816f9299ab6ddd6b9673b81f34e707f6bacab)
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="&quot;android.permission.MY_READ_PERMISSION_STRING&quot;" />
338                     </annotation>
339                     <annotation name="androidx.annotation.RequiresPermission.Write">
340                       <val name="value" val="&quot;android.permission.MY_WRITE_PERMISSION_STRING&quot;" />
341                     </annotation>
342                   </item>
343                   <item name="test.pkg.PermissionsTest void myMethod()">
344                     <annotation name="androidx.annotation.RequiresPermission">
345                       <val name="value" val="&quot;android.permission.MY_PERMISSION_STRING&quot;" />
346                     </annotation>
347                   </item>
348                   <item name="test.pkg.PermissionsTest void myMethod2()">
349                     <annotation name="androidx.annotation.RequiresPermission">
350                       <val name="anyOf" val="{&quot;android.permission.MY_PERMISSION_STRING&quot;, &quot;android.permission.MY_PERMISSION_STRING2&quot;}" />
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&lt;java.lang.Integer&gt;, 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="&quot;\&apos;&amp;\&quot;&quot;" />
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