xref: /aosp_15_r20/build/make/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
1 /*
2  * Copyright (C) 2024 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 package com.android.checkflaggedapis
17 
18 import android.aconfig.Aconfig
19 import android.aconfig.Aconfig.flag_state.DISABLED
20 import android.aconfig.Aconfig.flag_state.ENABLED
21 import java.io.ByteArrayInputStream
22 import java.io.ByteArrayOutputStream
23 import java.io.InputStream
24 import org.junit.Assert.assertEquals
25 import org.junit.Test
26 import org.junit.runner.RunWith
27 import org.junit.runners.JUnit4
28 
29 private val API_SIGNATURE =
30     """
31       // Signature format: 2.0
32       package android {
33         @FlaggedApi("android.flag.foo") public final class Clazz {
34           ctor @FlaggedApi("android.flag.foo") public Clazz();
35           field @FlaggedApi("android.flag.foo") public static final int FOO = 1; // 0x1
36           method @FlaggedApi("android.flag.foo") public int getErrorCode();
37           method @FlaggedApi("android.flag.foo") public boolean setData(int, int[][], @NonNull android.util.Utility<T, U>);
38           method @FlaggedApi("android.flag.foo") public boolean setVariableData(int, android.util.Atom...);
39           method @FlaggedApi("android.flag.foo") public boolean innerClassArg(android.Clazz.Builder);
40         }
41         @FlaggedApi("android.flag.bar") public static class Clazz.Builder {
42         }
43       }
44 """
45         .trim()
46 
47 private val API_VERSIONS =
48     """
49       <?xml version="1.0" encoding="utf-8"?>
50       <api version="3">
51         <class name="android/Clazz" since="1">
52           <extends name="java/lang/Object"/>
53           <method name="&lt;init>()V"/>
54           <field name="FOO"/>
55           <method name="getErrorCode()I"/>
56           <method name="setData(I[[ILandroid/util/Utility;)Z"/>
57           <method name="setVariableData(I[Landroid/util/Atom;)Z"/>
58           <method name="innerClassArg(Landroid/Clazz${"$"}Builder;)"/>
59         </class>
60         <class name="android/Clazz${"$"}Builder" since="2">
61           <extends name="java/lang/Object"/>
62         </class>
63       </api>
64 """
65         .trim()
66 
generateFlagsProtonull67 private fun generateFlagsProto(
68     fooState: Aconfig.flag_state,
69     barState: Aconfig.flag_state
70 ): InputStream {
71   val fooFlag =
72       Aconfig.parsed_flag
73           .newBuilder()
74           .setPackage("android.flag")
75           .setName("foo")
76           .setState(fooState)
77           .setPermission(Aconfig.flag_permission.READ_ONLY)
78           .build()
79   val barFlag =
80       Aconfig.parsed_flag
81           .newBuilder()
82           .setPackage("android.flag")
83           .setName("bar")
84           .setState(barState)
85           .setPermission(Aconfig.flag_permission.READ_ONLY)
86           .build()
87   val flags =
88       Aconfig.parsed_flags.newBuilder().addParsedFlag(fooFlag).addParsedFlag(barFlag).build()
89   val binaryProto = ByteArrayOutputStream()
90   flags.writeTo(binaryProto)
91   return ByteArrayInputStream(binaryProto.toByteArray())
92 }
93 
94 @RunWith(JUnit4::class)
95 class CheckFlaggedApisTest {
96   @Test
testParseApiSignaturenull97   fun testParseApiSignature() {
98     val expected =
99         setOf(
100             Pair(
101                 Symbol.createClass("android/Clazz", "java/lang/Object", setOf()),
102                 Flag("android.flag.foo")),
103             Pair(Symbol.createMethod("android/Clazz", "Clazz()"), Flag("android.flag.foo")),
104             Pair(Symbol.createField("android/Clazz", "FOO"), Flag("android.flag.foo")),
105             Pair(Symbol.createMethod("android/Clazz", "getErrorCode()"), Flag("android.flag.foo")),
106             Pair(
107                 Symbol.createMethod("android/Clazz", "setData(I[[ILandroid/util/Utility;)"),
108                 Flag("android.flag.foo")),
109             Pair(
110                 Symbol.createMethod("android/Clazz", "setVariableData(I[Landroid/util/Atom;)"),
111                 Flag("android.flag.foo")),
112             Pair(
113                 Symbol.createMethod("android/Clazz", "innerClassArg(Landroid/Clazz/Builder;)"),
114                 Flag("android.flag.foo")),
115             Pair(
116                 Symbol.createClass("android/Clazz/Builder", "java/lang/Object", setOf()),
117                 Flag("android.flag.bar")),
118         )
119     val actual = parseApiSignature("in-memory", API_SIGNATURE.byteInputStream())
120     assertEquals(expected, actual)
121   }
122 
123   @Test
testParseApiSignatureInterfacesInheritFromJavaLangObjectnull124   fun testParseApiSignatureInterfacesInheritFromJavaLangObject() {
125     val apiSignature =
126         """
127           // Signature format: 2.0
128           package android {
129             @FlaggedApi("android.flag.foo") public interface Interface {
130             }
131           }
132         """
133             .trim()
134     val expected =
135         setOf(
136             Pair(
137                 Symbol.createClass("android/Interface", "java/lang/Object", setOf()),
138                 Flag("android.flag.foo")))
139     val actual = parseApiSignature("in-memory", apiSignature.byteInputStream())
140     assertEquals(expected, actual)
141   }
142 
143   @Test
testParseFlagValuesnull144   fun testParseFlagValues() {
145     val expected: Map<Flag, Boolean> =
146         mapOf(Flag("android.flag.foo") to true, Flag("android.flag.bar") to true)
147     val actual = parseFlagValues(generateFlagsProto(ENABLED, ENABLED))
148     assertEquals(expected, actual)
149   }
150 
151   @Test
testParseApiVersionsnull152   fun testParseApiVersions() {
153     val expected: Set<Symbol> =
154         setOf(
155             Symbol.createClass("android/Clazz", "java/lang/Object", setOf()),
156             Symbol.createMethod("android/Clazz", "Clazz()"),
157             Symbol.createField("android/Clazz", "FOO"),
158             Symbol.createMethod("android/Clazz", "getErrorCode()"),
159             Symbol.createMethod("android/Clazz", "setData(I[[ILandroid/util/Utility;)"),
160             Symbol.createMethod("android/Clazz", "setVariableData(I[Landroid/util/Atom;)"),
161             Symbol.createMethod("android/Clazz", "innerClassArg(Landroid/Clazz/Builder;)"),
162             Symbol.createClass("android/Clazz/Builder", "java/lang/Object", setOf()),
163         )
164     val actual = parseApiVersions(API_VERSIONS.byteInputStream())
165     assertEquals(expected, actual)
166   }
167 
168   @Test
testParseApiVersionsNestedClassesnull169   fun testParseApiVersionsNestedClasses() {
170     val apiVersions =
171         """
172           <?xml version="1.0" encoding="utf-8"?>
173           <api version="3">
174             <class name="android/Clazz${'$'}Foo${'$'}Bar" since="1">
175               <extends name="java/lang/Object"/>
176               <method name="&lt;init>()V"/>
177             </class>
178           </api>
179         """
180             .trim()
181     val expected: Set<Symbol> =
182         setOf(
183             Symbol.createClass("android/Clazz/Foo/Bar", "java/lang/Object", setOf()),
184             Symbol.createMethod("android/Clazz/Foo/Bar", "Bar()"),
185         )
186     val actual = parseApiVersions(apiVersions.byteInputStream())
187     assertEquals(expected, actual)
188   }
189 
190   @Test
testFindErrorsNoErrorsnull191   fun testFindErrorsNoErrors() {
192     val expected = setOf<ApiError>()
193     val actual =
194         findErrors(
195             parseApiSignature("in-memory", API_SIGNATURE.byteInputStream()),
196             parseFlagValues(generateFlagsProto(ENABLED, ENABLED)),
197             parseApiVersions(API_VERSIONS.byteInputStream()))
198     assertEquals(expected, actual)
199   }
200 
201   @Test
testFindErrorsVerifyImplementsnull202   fun testFindErrorsVerifyImplements() {
203     val apiSignature =
204         """
205           // Signature format: 2.0
206           package android {
207             @FlaggedApi("android.flag.foo") public final class Clazz implements android.Interface {
208               method @FlaggedApi("android.flag.foo") public boolean foo();
209               method @FlaggedApi("android.flag.foo") public boolean bar();
210             }
211             public interface Interface {
212               method public boolean bar();
213             }
214           }
215         """
216             .trim()
217 
218     val apiVersions =
219         """
220           <?xml version="1.0" encoding="utf-8"?>
221           <api version="3">
222             <class name="android/Clazz" since="1">
223               <extends name="java/lang/Object"/>
224               <implements name="android/Interface"/>
225               <method name="foo()Z"/>
226             </class>
227             <class name="android/Interface" since="1">
228               <method name="bar()Z"/>
229             </class>
230           </api>
231         """
232             .trim()
233 
234     val expected = setOf<ApiError>()
235     val actual =
236         findErrors(
237             parseApiSignature("in-memory", apiSignature.byteInputStream()),
238             parseFlagValues(generateFlagsProto(ENABLED, ENABLED)),
239             parseApiVersions(apiVersions.byteInputStream()))
240     assertEquals(expected, actual)
241   }
242 
243   @Test
testFindErrorsVerifySuperclassnull244   fun testFindErrorsVerifySuperclass() {
245     val apiSignature =
246         """
247           // Signature format: 2.0
248           package android {
249             @FlaggedApi("android.flag.foo") public final class C extends android.B {
250               method @FlaggedApi("android.flag.foo") public boolean c();
251               method @FlaggedApi("android.flag.foo") public boolean b();
252               method @FlaggedApi("android.flag.foo") public boolean a();
253             }
254             public final class B extends android.A {
255               method public boolean b();
256             }
257             public final class A {
258               method public boolean a();
259             }
260           }
261         """
262             .trim()
263 
264     val apiVersions =
265         """
266           <?xml version="1.0" encoding="utf-8"?>
267           <api version="3">
268             <class name="android/C" since="1">
269               <extends name="android/B"/>
270               <method name="c()Z"/>
271             </class>
272             <class name="android/B" since="1">
273               <extends name="android/A"/>
274               <method name="b()Z"/>
275             </class>
276             <class name="android/A" since="1">
277               <method name="a()Z"/>
278             </class>
279           </api>
280         """
281             .trim()
282 
283     val expected = setOf<ApiError>()
284     val actual =
285         findErrors(
286             parseApiSignature("in-memory", apiSignature.byteInputStream()),
287             parseFlagValues(generateFlagsProto(ENABLED, ENABLED)),
288             parseApiVersions(apiVersions.byteInputStream()))
289     assertEquals(expected, actual)
290   }
291 
292   @Test
testNestedFlagsOuterFlagWinsnull293   fun testNestedFlagsOuterFlagWins() {
294     val apiSignature =
295         """
296           // Signature format: 2.0
297           package android {
298             @FlaggedApi("android.flag.foo") public final class A {
299               method @FlaggedApi("android.flag.bar") public boolean method();
300             }
301             @FlaggedApi("android.flag.bar") public final class B {
302               method @FlaggedApi("android.flag.foo") public boolean method();
303             }
304           }
305         """
306             .trim()
307 
308     val apiVersions =
309         """
310           <?xml version="1.0" encoding="utf-8"?>
311           <api version="3">
312             <class name="android/B" since="1">
313             <extends name="java/lang/Object"/>
314             </class>
315           </api>
316         """
317             .trim()
318 
319     val expected = setOf<ApiError>()
320     val actual =
321         findErrors(
322             parseApiSignature("in-memory", apiSignature.byteInputStream()),
323             parseFlagValues(generateFlagsProto(DISABLED, ENABLED)),
324             parseApiVersions(apiVersions.byteInputStream()))
325     assertEquals(expected, actual)
326   }
327 
328   @Test
testFindErrorsDisabledFlaggedApiIsPresentnull329   fun testFindErrorsDisabledFlaggedApiIsPresent() {
330     val expected =
331         setOf<ApiError>(
332             DisabledFlaggedApiIsPresentError(
333                 Symbol.createClass("android/Clazz", "java/lang/Object", setOf()),
334                 Flag("android.flag.foo")),
335             DisabledFlaggedApiIsPresentError(
336                 Symbol.createMethod("android/Clazz", "Clazz()"), Flag("android.flag.foo")),
337             DisabledFlaggedApiIsPresentError(
338                 Symbol.createField("android/Clazz", "FOO"), Flag("android.flag.foo")),
339             DisabledFlaggedApiIsPresentError(
340                 Symbol.createMethod("android/Clazz", "getErrorCode()"), Flag("android.flag.foo")),
341             DisabledFlaggedApiIsPresentError(
342                 Symbol.createMethod("android/Clazz", "setData(I[[ILandroid/util/Utility;)"),
343                 Flag("android.flag.foo")),
344             DisabledFlaggedApiIsPresentError(
345                 Symbol.createMethod("android/Clazz", "setVariableData(I[Landroid/util/Atom;)"),
346                 Flag("android.flag.foo")),
347             DisabledFlaggedApiIsPresentError(
348                 Symbol.createMethod("android/Clazz", "innerClassArg(Landroid/Clazz/Builder;)"),
349                 Flag("android.flag.foo")),
350             DisabledFlaggedApiIsPresentError(
351                 Symbol.createClass("android/Clazz/Builder", "java/lang/Object", setOf()),
352                 Flag("android.flag.bar")),
353         )
354     val actual =
355         findErrors(
356             parseApiSignature("in-memory", API_SIGNATURE.byteInputStream()),
357             parseFlagValues(generateFlagsProto(DISABLED, DISABLED)),
358             parseApiVersions(API_VERSIONS.byteInputStream()))
359     assertEquals(expected, actual)
360   }
361 
362   @Test
testListFlaggedApisnull363   fun testListFlaggedApis() {
364     val expected =
365         listOf(
366             "android.flag.bar DISABLED android/Clazz/Builder",
367             "android.flag.foo ENABLED android/Clazz",
368             "android.flag.foo ENABLED android/Clazz/Clazz()",
369             "android.flag.foo ENABLED android/Clazz/FOO",
370             "android.flag.foo ENABLED android/Clazz/getErrorCode()",
371             "android.flag.foo ENABLED android/Clazz/innerClassArg(Landroid/Clazz/Builder;)",
372             "android.flag.foo ENABLED android/Clazz/setData(I[[ILandroid/util/Utility;)",
373             "android.flag.foo ENABLED android/Clazz/setVariableData(I[Landroid/util/Atom;)")
374     val actual =
375         listFlaggedApis(
376             parseApiSignature("in-memory", API_SIGNATURE.byteInputStream()),
377             parseFlagValues(generateFlagsProto(ENABLED, DISABLED)))
378     assertEquals(expected, actual)
379   }
380 }
381