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 org.junit.Ignore
20 import org.junit.Test
21 
22 @SuppressWarnings("ALL") // Sample code
23 class ExtractAnnotationsTest : DriverTest() {
24 
25     @Test
Check java typedef extraction and warning about non-source retention of typedefsnull26     fun `Check java typedef extraction and warning about non-source retention of typedefs`() {
27         check(
28             sourceFiles = *arrayOf(
29                 java(
30             """
31                     package test.pkg;
32 
33                     import android.annotation.IntDef;
34                     import android.annotation.IntRange;
35 
36                     import java.lang.annotation.Retention;
37                     import java.lang.annotation.RetentionPolicy;
38 
39                     @SuppressWarnings({"UnusedDeclaration", "WeakerAccess"})
40                     public class IntDefTest {
41                         @IntDef({STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT})
42                         @IntRange(from = 20)
43                         private @interface DialogStyle {}
44 
45                         public static final int STYLE_NORMAL = 0;
46                         public static final int STYLE_NO_TITLE = 1;
47                         public static final int STYLE_NO_FRAME = 2;
48                         public static final int STYLE_NO_INPUT = 3;
49                         public static final int UNRELATED = 3;
50 
51                         public void setStyle(@DialogStyle int style, int theme) {
52                         }
53 
54                         public void testIntDef(int arg) {
55                         }
56                         @IntDef(value = {STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT, 3, 3 + 1}, flag=true)
57                         @Retention(RetentionPolicy.SOURCE)
58                         private @interface DialogFlags {}
59 
60                         public void setFlags(Object first, @DialogFlags int flags) {
61                         }
62 
63                         public static final String TYPE_1 = "type1";
64                         public static final String TYPE_2 = "type2";
65                         public static final String UNRELATED_TYPE = "other";
66 
67                         public static class Inner {
68                             public void setInner(@DialogFlags int flags) {
69                             }
70                         }
71                     }
72                     """
73                 ).indented(),
74                 intDefAnnotationSource,
75                 intRangeAnnotationSource
76             ),
77             warnings = "src/test/pkg/IntDefTest.java:11: error: This typedef annotation class should have @Retention(RetentionPolicy.SOURCE) [AnnotationExtraction:146]",
78             extractAnnotations = mapOf("test.pkg" to """
79                 <?xml version="1.0" encoding="UTF-8"?>
80                 <root>
81                   <item name="test.pkg.IntDefTest void setFlags(java.lang.Object, int) 1">
82                     <annotation name="androidx.annotation.IntDef">
83                       <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}" />
84                       <val name="flag" val="true" />
85                     </annotation>
86                   </item>
87                   <item name="test.pkg.IntDefTest void setStyle(int, int) 0">
88                     <annotation name="androidx.annotation.IntDef">
89                       <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}" />
90                     </annotation>
91                   </item>
92                   <item name="test.pkg.IntDefTest.Inner void setInner(int) 0">
93                     <annotation name="androidx.annotation.IntDef">
94                       <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}" />
95                       <val name="flag" val="true" />
96                     </annotation>
97                   </item>
98                 </root>
99                 """
100             )
101         )
102     }
103 
104     @Test
Check Kotlin and referencing hidden constants from typedefnull105     fun `Check Kotlin and referencing hidden constants from typedef`() {
106         check(
107             sourceFiles = *arrayOf(
108                 kotlin(
109                     """
110                     @file:Suppress("unused", "UseExpressionBody")
111 
112                     package test.pkg
113 
114                     import android.annotation.LongDef
115 
116                     const val STYLE_NORMAL = 0L
117                     const val STYLE_NO_TITLE = 1L
118                     const val STYLE_NO_FRAME = 2L
119                     const val STYLE_NO_INPUT = 3L
120                     const val UNRELATED = 3L
121                     private const val HIDDEN = 4
122 
123                     const val TYPE_1 = "type1"
124                     const val TYPE_2 = "type2"
125                     const val UNRELATED_TYPE = "other"
126 
127                     class LongDefTest {
128 
129                         /** @hide */
130                         @LongDef(STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT, HIDDEN)
131                         @Retention(AnnotationRetention.SOURCE)
132                         private annotation class DialogStyle
133 
134                         fun setStyle(@DialogStyle style: Int, theme: Int) {}
135 
136                         fun testLongDef(arg: Int) {
137                         }
138 
139                         @LongDef(STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT, 3L, 3L + 1L, flag = true)
140                         @Retention(AnnotationRetention.SOURCE)
141                         private annotation class DialogFlags
142 
143                         fun setFlags(first: Any, @DialogFlags flags: Int) {}
144 
145                         class Inner {
146                             fun setInner(@DialogFlags flags: Int) {}
147                             fun isNull(value: String?): Boolean
148                         }
149                     }"""
150                 ).indented(),
151                 longDefAnnotationSource
152             ),
153             warnings = "src/test/pkg/LongDefTest.kt:12: error: Typedef class references hidden field field LongDefTestKt.HIDDEN: removed from typedef metadata [HiddenTypedefConstant:148]",
154             extractAnnotations = mapOf("test.pkg" to """
155                 <?xml version="1.0" encoding="UTF-8"?>
156                 <root>
157                   <item name="test.pkg.LongDefTest void setFlags(java.lang.Object, int) 1">
158                     <annotation name="androidx.annotation.LongDef">
159                       <val name="flag" val="true" />
160                       <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, 4}" />
161                     </annotation>
162                   </item>
163                   <item name="test.pkg.LongDefTest void setStyle(int, int) 0">
164                     <annotation name="androidx.annotation.LongDef">
165                       <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}" />
166                     </annotation>
167                   </item>
168                   <item name="test.pkg.LongDefTest.Inner void setInner(int) 0">
169                     <annotation name="androidx.annotation.LongDef">
170                       <val name="flag" val="true" />
171                       <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, 4}" />
172                     </annotation>
173                   </item>
174                 </root>
175                 """
176             )
177         )
178     }
179 
180     @Ignore("Not working reliably")
181     @Test
Include merged annotations in exported source annotationsnull182     fun `Include merged annotations in exported source annotations`() {
183         check(
184             compatibilityMode = false,
185             outputKotlinStyleNulls = false,
186             includeSystemApiAnnotations = false,
187             omitCommonPackages = false,
188             warnings = "error: Unexpected reference to Nonexistent.Field [AnnotationExtraction:146]",
189             sourceFiles = *arrayOf(
190                 java(
191                     """
192                     package test.pkg;
193 
194                     public class MyTest {
195                         public void test(int arg) { }
196                     }"""
197                 )
198             ),
199             mergeXmlAnnotations = """<?xml version="1.0" encoding="UTF-8"?>
200                 <root>
201                   <item name="test.pkg.MyTest void test(int) 0">
202                     <annotation name="org.intellij.lang.annotations.MagicConstant">
203                       <val name="intValues" val="{java.util.Calendar.ERA, java.util.Calendar.YEAR, java.util.Calendar.MONTH, java.util.Calendar.WEEK_OF_YEAR, Nonexistent.Field}" />
204                     </annotation>
205                   </item>
206                 </root>
207                 """,
208             extractAnnotations = mapOf("test.pkg" to """
209                 <?xml version="1.0" encoding="UTF-8"?>
210                 <root>
211                   <item name="test.pkg.MyTest void test(int) 0">
212                     <annotation name="androidx.annotation.IntDef">
213                       <val name="value" val="{java.util.Calendar.ERA, java.util.Calendar.YEAR, java.util.Calendar.MONTH, java.util.Calendar.WEEK_OF_YEAR}" />
214                     </annotation>
215                   </item>
216                 </root>
217                 """
218             )
219         )
220     }
221 }