• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.apilevels
18 
19 import org.junit.Assert
20 import kotlin.test.Test
21 import kotlin.test.assertEquals
22 import kotlin.test.assertFailsWith
23 import kotlin.test.assertTrue
24 
25 class ApiToExtensionsMapTest {
26     @Test
empty inputnull27     fun `empty input`() {
28         val xml = """
29             <?xml version="1.0" encoding="utf-8"?>
30             <!-- No rules is a valid (albeit weird). -->
31             <sdk-extensions-info>
32                 <sdk shortname="R-ext" name="R Extensions" id="30" reference="android/os/Build${'$'}VERSION_CODES${'$'}R" />
33                 <sdk shortname="S-ext" name="S Extensions" id="31" reference="android/os/Build${'$'}VERSION_CODES${'$'}S" />
34                 <sdk shortname="T-ext" name="T Extensions" id="33" reference="android/os/Build${'$'}VERSION_CODES${'$'}T" />
35             </sdk-extensions-info>
36         """.trimIndent()
37         val map = ApiToExtensionsMap.fromXml("no-module", xml)
38 
39         assertTrue(map.getExtensions("com.foo.Bar").isEmpty())
40     }
41 
42     @Test
wildcardnull43     fun wildcard() {
44         val xml = """
45             <?xml version="1.0" encoding="utf-8"?>
46             <!-- All APIs will default to extension SDK A. -->
47             <sdk-extensions-info>
48                 <sdk shortname="A" name="A Extensions" id="1" reference="android/os/Build${'$'}VERSION_CODES${'$'}A" />
49                 <symbol jar="mod" pattern="*" sdks="A" />
50             </sdk-extensions-info>
51         """.trimIndent()
52         val map = ApiToExtensionsMap.fromXml("mod", xml)
53 
54         assertEquals(map.getExtensions("com.foo.Bar"), listOf("A"))
55         assertEquals(map.getExtensions("com.foo.SomeOtherBar"), listOf("A"))
56     }
57 
58     @Test
single classnull59     fun `single class`() {
60         val xml = """
61             <?xml version="1.0" encoding="utf-8"?>
62             <!-- A single class. The class, any internal classes, and any methods are allowed;
63                  everything else is denied -->
64             <sdk-extensions-info>
65                 <sdk shortname="A" name="A Extensions" id="1" reference="android/os/Build${'$'}VERSION_CODES${'$'}A" />
66                 <symbol jar="mod" pattern="com.foo.Bar" sdks="A" />
67             </sdk-extensions-info>
68         """.trimIndent()
69         val map = ApiToExtensionsMap.fromXml("mod", xml)
70 
71         assertEquals(map.getExtensions("com.foo.Bar"), listOf("A"))
72         assertEquals(map.getExtensions("com.foo.Bar#FIELD"), listOf("A"))
73         assertEquals(map.getExtensions("com.foo.Bar#method"), listOf("A"))
74         assertEquals(map.getExtensions("com.foo.Bar\$Inner"), listOf("A"))
75         assertEquals(map.getExtensions("com.foo.Bar\$Inner\$InnerInner"), listOf("A"))
76 
77         val clazz = ApiClass("com/foo/Bar", 1, false)
78         val method = ApiElement("method(Ljava.lang.String;I)V", 2, false)
79         assertEquals(map.getExtensions(clazz), listOf("A"))
80         assertEquals(map.getExtensions(clazz, method), listOf("A"))
81 
82         assertTrue(map.getExtensions("com.foo.SomeOtherClass").isEmpty())
83     }
84 
85     @Test
multiple extensionsnull86     fun `multiple extensions`() {
87         val xml = """
88             <?xml version="1.0" encoding="utf-8"?>
89             <!-- Any number of white space separated extension SDKs may be listed. -->
90             <sdk-extensions-info>
91                 <sdk shortname="A" name="A Extensions" id="1" reference="android/os/Build${'$'}VERSION_CODES${'$'}A" />
92                 <sdk shortname="B" name="B Extensions" id="2" reference="android/os/Build${'$'}VERSION_CODES${'$'}B" />
93                 <sdk shortname="FOO" name="FOO Extensions" id="10" reference="android/os/Build${'$'}VERSION_CODES${'$'}FOO" />
94                 <sdk shortname="BAR" name="BAR Extensions" id="11" reference="android/os/Build${'$'}VERSION_CODES${'$'}BAR" />
95                 <symbol jar="mod" pattern="*" sdks="A,B,FOO,BAR" />
96             </sdk-extensions-info>
97         """.trimIndent()
98         val map = ApiToExtensionsMap.fromXml("mod", xml)
99 
100         assertEquals(listOf("A", "B", "FOO", "BAR"), map.getExtensions("com.foo.Bar"))
101     }
102 
103     @Test
precedencenull104     fun precedence() {
105         val xml = """
106             <?xml version="1.0" encoding="utf-8"?>
107             <!-- Multiple classes, and multiple rules with different precedence. -->
108             <sdk-extensions-info>
109                 <sdk shortname="A" name="A Extensions" id="1" reference="android/os/Build${'$'}VERSION_CODES${'$'}A" />
110                 <sdk shortname="B" name="B Extensions" id="2" reference="android/os/Build${'$'}VERSION_CODES${'$'}B" />
111                 <sdk shortname="C" name="C Extensions" id="3" reference="android/os/Build${'$'}VERSION_CODES${'$'}C" />
112                 <sdk shortname="D" name="D Extensions" id="4" reference="android/os/Build${'$'}VERSION_CODES${'$'}D" />
113                 <symbol jar="mod" pattern="*" sdks="A" />
114                 <symbol jar="mod" pattern="com.foo.Bar" sdks="B" />
115                 <symbol jar="mod" pattern="com.foo.Bar${'$'}Inner#method" sdks="C" />
116                 <symbol jar="mod" pattern="com.bar.Foo" sdks="D" />
117             </sdk-extensions-info>
118         """.trimIndent()
119         val map = ApiToExtensionsMap.fromXml("mod", xml)
120 
121         assertEquals(map.getExtensions("anything"), listOf("A"))
122 
123         assertEquals(map.getExtensions("com.foo.Bar"), listOf("B"))
124         assertEquals(map.getExtensions("com.foo.Bar#FIELD"), listOf("B"))
125         assertEquals(map.getExtensions("com.foo.Bar\$Inner"), listOf("B"))
126 
127         assertEquals(map.getExtensions("com.foo.Bar\$Inner#method"), listOf("C"))
128 
129         assertEquals(map.getExtensions("com.bar.Foo"), listOf("D"))
130         assertEquals(map.getExtensions("com.bar.Foo#FIELD"), listOf("D"))
131     }
132 
133     @Test
multiple mainline modulesnull134     fun `multiple mainline modules`() {
135         val xml = """
136             <?xml version="1.0" encoding="utf-8"?>
137             <!-- The allow list will only consider patterns that are marked with the given mainline module -->
138             <sdk-extensions-info>
139                 <sdk shortname="A" name="A Extensions" id="1" reference="android/os/Build${'$'}VERSION_CODES${'$'}A" />
140                 <sdk shortname="B" name="B Extensions" id="2" reference="android/os/Build${'$'}VERSION_CODES${'$'}B" />
141                 <symbol jar="foo" pattern="*" sdks="A" />
142                 <symbol jar="bar" pattern="*" sdks="B" />
143             </sdk-extensions-info>
144         """.trimIndent()
145         val allowListA = ApiToExtensionsMap.fromXml("foo", xml)
146         val allowListB = ApiToExtensionsMap.fromXml("bar", xml)
147         val allowListC = ApiToExtensionsMap.fromXml("baz", xml)
148 
149         assertEquals(allowListA.getExtensions("anything"), listOf("A"))
150         assertEquals(allowListB.getExtensions("anything"), listOf("B"))
151         assertTrue(allowListC.getExtensions("anything").isEmpty())
152     }
153 
154     @Test
declarations and rules can be mixednull155     fun `declarations and rules can be mixed`() {
156         val xml = """
157             <?xml version="1.0" encoding="utf-8"?>
158             <!-- SDK declarations and rule lines can be mixed in any order -->
159             <sdk-extensions-info>
160                 <sdk shortname="A" name="A Extensions" id="1" reference="android/os/Build${'$'}VERSION_CODES${'$'}A" />
161                 <symbol jar="foo" pattern="*" sdks="A,B" />
162                 <sdk shortname="B" name="B Extensions" id="2" reference="android/os/Build${'$'}VERSION_CODES${'$'}B" />
163             </sdk-extensions-info>
164         """.trimIndent()
165         val map = ApiToExtensionsMap.fromXml("foo", xml)
166 
167         assertEquals(map.getExtensions("com.foo.Bar"), listOf("A", "B"))
168     }
169 
170     @Test
bad inputnull171     fun `bad input`() {
172         assertFailsWith<IllegalArgumentException> {
173             ApiToExtensionsMap.fromXml(
174                 "mod",
175                 """
176                     <?xml version="1.0" encoding="utf-8"?>
177                     <!-- Missing root element -->
178                     <sdk shortname="A" name="A Extensions" id="1" reference="android/os/Build${'$'}VERSION_CODES${'$'}A" />
179                     <symbol jar="mod" pattern="com.foo.Bar" sdks="A" />
180                 """.trimIndent()
181             )
182         }
183 
184         assertFailsWith<IllegalArgumentException> {
185             ApiToExtensionsMap.fromXml(
186                 "mod",
187                 """
188                     <?xml version="1.0" encoding="utf-8"?>
189                     <!-- <sdk> tag at unexpected depth  -->
190                     <sdk-extensions-info version="2">
191                         <foo>
192                             <sdk shortname="A" name="A Extensions" id="1" reference="android/os/Build${'$'}VERSION_CODES${'$'}A" >
193                         </foo>
194                         <symbol jar="mod" pattern="com.foo.Bar" sdks="A" />
195                     </sdk-extensions-info>
196                 """.trimIndent()
197             )
198         }
199 
200         assertFailsWith<IllegalArgumentException> {
201             ApiToExtensionsMap.fromXml(
202                 "mod",
203                 """
204                     <?xml version="1.0" encoding="utf-8"?>
205                     <!-- using 0 (reserved for the Android platform SDK) as ID -->
206                     <sdk-extensions-info>
207                         <sdk shortname="A" name="A Extensions" id="0" reference="android/os/Build${'$'}VERSION_CODES${'$'}A" />
208                         <symbol jar="mod" pattern="com.foo.Bar" sdks="A" />
209                     </sdk-extensions-info>
210                 """.trimIndent()
211             )
212         }
213 
214         assertFailsWith<IllegalArgumentException> {
215             ApiToExtensionsMap.fromXml(
216                 "mod",
217                 """
218                     <?xml version="1.0" encoding="utf-8"?>
219                     <!-- missing module attribute -->
220                     <sdk-extensions-info>
221                         <sdk shortname="A" name="A Extensions" id="1" reference="android/os/Build${'$'}VERSION_CODES${'$'}A" />
222                         <symbol pattern="com.foo.Bar" sdks="A" />
223                     </sdk-extensions-info>
224                 """.trimIndent()
225             )
226         }
227 
228         assertFailsWith<IllegalArgumentException> {
229             ApiToExtensionsMap.fromXml(
230                 "mod",
231                 """
232                     <?xml version="1.0" encoding="utf-8"?>
233                     <!-- duplicate module+pattern pairs -->
234                     <sdk-extensions-info>
235                         <sdk shortname="A" name="A Extensions" id="1" reference="android/os/Build${'$'}VERSION_CODES${'$'}A" />
236                         <symbol jar="mod" pattern="com.foo.Bar" sdks="A" />
237                         <symbol jar="mod" pattern="com.foo.Bar" sdks="B" />
238                     </sdk-extensions-info>
239                 """.trimIndent()
240             )
241         }
242 
243         assertFailsWith<IllegalArgumentException> {
244             ApiToExtensionsMap.fromXml(
245                 "mod",
246                 """
247                     <?xml version="1.0" encoding="utf-8"?>
248                     <!-- sdks attribute refer to non-declared SDK -->
249                     <sdk-extensions-info>
250                         <sdk shortname="B" name="A Extensions" id="2" reference="android/os/Build${'$'}VERSION_CODES${'$'}A" />
251                         <symbol jar="mod" pattern="com.foo.Bar" sdks="A" />
252                     </sdk-extensions-info>
253                 """.trimIndent()
254             )
255         }
256 
257         assertFailsWith<IllegalArgumentException> {
258             ApiToExtensionsMap.fromXml(
259                 "mod",
260                 """
261                     <?xml version="1.0" encoding="utf-8"?>
262                     <!-- duplicate numerical ID -->
263                     <sdk-extensions-info>
264                         <sdk shortname="A" name="A Extensions" id="1" reference="android/os/Build${'$'}VERSION_CODES${'$'}A" />
265                         <sdk shortname="B" name="B Extensions" id="1" reference="android/os/Build${'$'}VERSION_CODES${'$'}B" />
266                         <symbol jar="mod" pattern="com.foo.Bar" sdks="A" />
267                     </sdk-extensions-info>
268                 """.trimIndent()
269             )
270         }
271 
272         assertFailsWith<IllegalArgumentException> {
273             ApiToExtensionsMap.fromXml(
274                 "mod",
275                 """
276                     <?xml version="1.0" encoding="utf-8"?>
277                     <!-- duplicate short SDK name -->
278                     <sdk-extensions-info>
279                         <sdk shortname="A" name="A Extensions" id="1" reference="android/os/Build${'$'}VERSION_CODES${'$'}A" />
280                         <sdk shortname="A" name="B Extensions" id="2" reference="android/os/Build${'$'}VERSION_CODES${'$'}B" />
281                         <symbol jar="mod" pattern="com.foo.Bar" sdks="A" />
282                     </sdk-extensions-info>
283                 """.trimIndent()
284             )
285         }
286 
287         assertFailsWith<IllegalArgumentException> {
288             ApiToExtensionsMap.fromXml(
289                 "mod",
290                 """
291                     <?xml version="1.0" encoding="utf-8"?>
292                     <!-- duplicate long SDK name -->
293                     <sdk-extensions-info>
294                         <sdk shortname="A" name="A Extensions" id="1" reference="android/os/Build${'$'}VERSION_CODES${'$'}A" />
295                         <sdk shortname="B" name="A Extensions" id="2" reference="android/os/Build${'$'}VERSION_CODES${'$'}B" />
296                         <symbol jar="mod" pattern="com.foo.Bar" sdks="A" />
297                     </sdk-extensions-info>
298                 """.trimIndent()
299             )
300         }
301 
302         assertFailsWith<IllegalArgumentException> {
303             ApiToExtensionsMap.fromXml(
304                 "mod",
305                 """
306                     <?xml version="1.0" encoding="utf-8"?>
307                     <!-- duplicate SDK reference -->
308                     <sdk-extensions-info version="1">
309                         <sdk shortname="A" name="A Extensions" id="1" reference="android/os/Build${'$'}VERSION_CODES${'$'}A" />
310                         <sdk shortname="B" name="B Extensions" id="2" reference="android/os/Build${'$'}VERSION_CODES${'$'}A" />
311                         <symbol jar="mod" pattern="com.foo.Bar" sdks="A" />
312                     </sdk-extensions-info>
313                 """.trimIndent()
314             )
315         }
316 
317         assertFailsWith<IllegalArgumentException> {
318             ApiToExtensionsMap.fromXml(
319                 "mod",
320                 """
321                     <?xml version="1.0" encoding="utf-8"?>
322                     <!-- duplicate SDK for same symbol -->
323                     <sdk-extensions-info>
324                         <sdk shortname="A" name="A Extensions" id="1" reference="android/os/Build${'$'}VERSION_CODES${'$'}A" />
325                         <sdk shortname="B" name="B Extensions" id="1" reference="android/os/Build${'$'}VERSION_CODES${'$'}B" />
326                         <symbol jar="mod" pattern="com.foo.Bar" sdks="A,B,A" />
327                     </sdk-extensions-info>
328                 """.trimIndent()
329             )
330         }
331     }
332 
333     @Test
calculate sdks xml attributenull334     fun `calculate sdks xml attribute`() {
335         val xml = """
336             <?xml version="1.0" encoding="utf-8"?>
337             <!-- Verify the calculateSdksAttr method -->
338             <sdk-extensions-info>
339                 <sdk shortname="R" name="R Extensions" id="30" reference="android/os/Build${'$'}VERSION_CODES${'$'}R" />
340                 <sdk shortname="S" name="S Extensions" id="31" reference="android/os/Build${'$'}VERSION_CODES${'$'}S" />
341                 <sdk shortname="T" name="T Extensions" id="33" reference="android/os/Build${'$'}VERSION_CODES${'$'}T" />
342                 <sdk shortname="FOO" name="FOO Extensions" id="1000" reference="android/os/Build${'$'}VERSION_CODES${'$'}FOO" />
343                 <sdk shortname="BAR" name="BAR Extensions" id="1001" reference="android/os/Build${'$'}VERSION_CODES${'$'}BAR" />
344             </sdk-extensions-info>
345         """.trimIndent()
346         val filter = ApiToExtensionsMap.fromXml("mod", xml)
347 
348         Assert.assertEquals(
349             "0:34",
350             filter.calculateSdksAttr(34, 34, listOf(), ApiElement.NEVER)
351         )
352 
353         Assert.assertEquals(
354             "30:4",
355             filter.calculateSdksAttr(34, 34, listOf("R"), 4)
356         )
357 
358         Assert.assertEquals(
359             "30:4,31:4",
360             filter.calculateSdksAttr(34, 34, listOf("R", "S"), 4)
361         )
362 
363         Assert.assertEquals(
364             "30:4,31:4,0:33",
365             filter.calculateSdksAttr(33, 34, listOf("R", "S"), 4)
366         )
367 
368         Assert.assertEquals(
369             "30:4,31:4,1000:4,0:33",
370             filter.calculateSdksAttr(33, 34, listOf("R", "S", "FOO"), 4)
371         )
372 
373         Assert.assertEquals(
374             "30:4,31:4,1000:4,1001:4,0:33",
375             filter.calculateSdksAttr(33, 34, listOf("R", "S", "FOO", "BAR"), 4)
376         )
377     }
378 }
379