blob: e07ac1dfd4832c3e262cd08aaa175cb2eac3701d [file] [log] [blame]
Mårten Kongstadf242ec82024-04-16 17:12:26 +02001/*
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 */
16package com.android.checkflaggedapis
17
Mårten Kongstad387ff6c2024-04-16 12:42:14 +020018import android.aconfig.Aconfig
Mårten Kongstad18ff19a2024-04-26 05:48:57 +020019import android.aconfig.Aconfig.flag_state.DISABLED
20import android.aconfig.Aconfig.flag_state.ENABLED
Mårten Kongstad387ff6c2024-04-16 12:42:14 +020021import java.io.ByteArrayInputStream
22import java.io.ByteArrayOutputStream
Mårten Kongstad9238a3a2024-04-16 13:19:50 +020023import java.io.InputStream
Mårten Kongstad20de4052024-04-16 11:33:56 +020024import org.junit.Assert.assertEquals
Mårten Kongstadf242ec82024-04-16 17:12:26 +020025import org.junit.Test
26import org.junit.runner.RunWith
Mårten Kongstad7cc21742024-04-18 10:23:20 +020027import org.junit.runners.JUnit4
Mårten Kongstadf242ec82024-04-16 17:12:26 +020028
Mårten Kongstad20de4052024-04-16 11:33:56 +020029private val API_SIGNATURE =
30 """
31 // Signature format: 2.0
32 package android {
Mårten Kongstad18ff19a2024-04-26 05:48:57 +020033 @FlaggedApi("android.flag.foo") public final class Clazz {
Mårten Kongstad40da9702024-04-27 01:42:51 +020034 ctor @FlaggedApi("android.flag.foo") public Clazz();
Mårten Kongstad20de4052024-04-16 11:33:56 +020035 field @FlaggedApi("android.flag.foo") public static final int FOO = 1; // 0x1
Mårten Kongstad40da9702024-04-27 01:42:51 +020036 method @FlaggedApi("android.flag.foo") public int getErrorCode();
Mårten Kongstadb4a14bf2024-04-28 00:21:11 +020037 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);
Mårten Kongstad20de4052024-04-16 11:33:56 +020040 }
Mårten Kongstad18ff19a2024-04-26 05:48:57 +020041 @FlaggedApi("android.flag.bar") public static class Clazz.Builder {
42 }
Mårten Kongstad20de4052024-04-16 11:33:56 +020043 }
44"""
45 .trim()
46
Mårten Kongstadb673d3b2024-04-16 18:34:20 +020047private val API_VERSIONS =
48 """
49 <?xml version="1.0" encoding="utf-8"?>
50 <api version="3">
51 <class name="android/Clazz" since="1">
Mårten Kongstadc3f05a62024-05-06 21:42:15 +020052 <extends name="java/lang/Object"/>
Mårten Kongstadb673d3b2024-04-16 18:34:20 +020053 <method name="&lt;init>()V"/>
54 <field name="FOO"/>
Mårten Kongstad40da9702024-04-27 01:42:51 +020055 <method name="getErrorCode()I"/>
Mårten Kongstadb4a14bf2024-04-28 00:21:11 +020056 <method name="setData(I[[ILandroid/util/Utility;)Z"/>
57 <method name="setVariableData(I[Landroid/util/Atom;)Z"/>
58 <method name="innerClassArg(Landroid/Clazz${"$"}Builder;)"/>
Mårten Kongstadb673d3b2024-04-16 18:34:20 +020059 </class>
Mårten Kongstad18ff19a2024-04-26 05:48:57 +020060 <class name="android/Clazz${"$"}Builder" since="2">
Mårten Kongstadc3f05a62024-05-06 21:42:15 +020061 <extends name="java/lang/Object"/>
Mårten Kongstad18ff19a2024-04-26 05:48:57 +020062 </class>
Mårten Kongstadb673d3b2024-04-16 18:34:20 +020063 </api>
64"""
65 .trim()
66
Mårten Kongstad18ff19a2024-04-26 05:48:57 +020067private fun generateFlagsProto(
68 fooState: Aconfig.flag_state,
69 barState: Aconfig.flag_state
70): InputStream {
71 val fooFlag =
Mårten Kongstad9238a3a2024-04-16 13:19:50 +020072 Aconfig.parsed_flag
73 .newBuilder()
74 .setPackage("android.flag")
75 .setName("foo")
76 .setState(fooState)
77 .setPermission(Aconfig.flag_permission.READ_ONLY)
78 .build()
Mårten Kongstad18ff19a2024-04-26 05:48:57 +020079 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()
Mårten Kongstad9238a3a2024-04-16 13:19:50 +020089 val binaryProto = ByteArrayOutputStream()
Mårten Kongstad18ff19a2024-04-26 05:48:57 +020090 flags.writeTo(binaryProto)
Mårten Kongstad9238a3a2024-04-16 13:19:50 +020091 return ByteArrayInputStream(binaryProto.toByteArray())
92}
93
Mårten Kongstad7cc21742024-04-18 10:23:20 +020094@RunWith(JUnit4::class)
95class CheckFlaggedApisTest {
Mårten Kongstad20de4052024-04-16 11:33:56 +020096 @Test
97 fun testParseApiSignature() {
Mårten Kongstad18ff19a2024-04-26 05:48:57 +020098 val expected =
99 setOf(
Mårten Kongstadc3f05a62024-05-06 21:42:15 +0200100 Pair(
101 Symbol.createClass("android/Clazz", "java/lang/Object", setOf()),
102 Flag("android.flag.foo")),
Mårten Kongstada1fe3712024-05-06 13:46:21 +0200103 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")),
Mårten Kongstadb4a14bf2024-04-28 00:21:11 +0200106 Pair(
Mårten Kongstada1fe3712024-05-06 13:46:21 +0200107 Symbol.createMethod("android/Clazz", "setData(I[[ILandroid/util/Utility;)"),
Mårten Kongstadb4a14bf2024-04-28 00:21:11 +0200108 Flag("android.flag.foo")),
109 Pair(
Mårten Kongstada1fe3712024-05-06 13:46:21 +0200110 Symbol.createMethod("android/Clazz", "setVariableData(I[Landroid/util/Atom;)"),
Mårten Kongstadb4a14bf2024-04-28 00:21:11 +0200111 Flag("android.flag.foo")),
112 Pair(
Mårten Kongstada1fe3712024-05-06 13:46:21 +0200113 Symbol.createMethod("android/Clazz", "innerClassArg(Landroid/Clazz/Builder;)"),
Mårten Kongstadb4a14bf2024-04-28 00:21:11 +0200114 Flag("android.flag.foo")),
Mårten Kongstadc3f05a62024-05-06 21:42:15 +0200115 Pair(
116 Symbol.createClass("android/Clazz/Builder", "java/lang/Object", setOf()),
117 Flag("android.flag.bar")),
Mårten Kongstad18ff19a2024-04-26 05:48:57 +0200118 )
Mårten Kongstad20de4052024-04-16 11:33:56 +0200119 val actual = parseApiSignature("in-memory", API_SIGNATURE.byteInputStream())
120 assertEquals(expected, actual)
121 }
Mårten Kongstad387ff6c2024-04-16 12:42:14 +0200122
123 @Test
Mårten Kongstadaa41dac2024-05-22 15:13:54 +0200124 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
Mårten Kongstad387ff6c2024-04-16 12:42:14 +0200144 fun testParseFlagValues() {
Mårten Kongstad18ff19a2024-04-26 05:48:57 +0200145 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))
Mårten Kongstad387ff6c2024-04-16 12:42:14 +0200148 assertEquals(expected, actual)
149 }
Mårten Kongstadb673d3b2024-04-16 18:34:20 +0200150
151 @Test
152 fun testParseApiVersions() {
Mårten Kongstad18ff19a2024-04-26 05:48:57 +0200153 val expected: Set<Symbol> =
154 setOf(
Mårten Kongstadc3f05a62024-05-06 21:42:15 +0200155 Symbol.createClass("android/Clazz", "java/lang/Object", setOf()),
Mårten Kongstada1fe3712024-05-06 13:46:21 +0200156 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;)"),
Mårten Kongstadc3f05a62024-05-06 21:42:15 +0200162 Symbol.createClass("android/Clazz/Builder", "java/lang/Object", setOf()),
Mårten Kongstad18ff19a2024-04-26 05:48:57 +0200163 )
Mårten Kongstadb673d3b2024-04-16 18:34:20 +0200164 val actual = parseApiVersions(API_VERSIONS.byteInputStream())
165 assertEquals(expected, actual)
166 }
Mårten Kongstad9238a3a2024-04-16 13:19:50 +0200167
168 @Test
Mårten Kongstad02525a82024-05-06 10:28:02 +0200169 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">
Mårten Kongstadc3f05a62024-05-06 21:42:15 +0200175 <extends name="java/lang/Object"/>
Mårten Kongstad02525a82024-05-06 10:28:02 +0200176 <method name="&lt;init>()V"/>
177 </class>
178 </api>
179 """
180 .trim()
181 val expected: Set<Symbol> =
182 setOf(
Mårten Kongstadc3f05a62024-05-06 21:42:15 +0200183 Symbol.createClass("android/Clazz/Foo/Bar", "java/lang/Object", setOf()),
Mårten Kongstada1fe3712024-05-06 13:46:21 +0200184 Symbol.createMethod("android/Clazz/Foo/Bar", "Bar()"),
Mårten Kongstad02525a82024-05-06 10:28:02 +0200185 )
186 val actual = parseApiVersions(apiVersions.byteInputStream())
187 assertEquals(expected, actual)
188 }
189
190 @Test
Mårten Kongstad9238a3a2024-04-16 13:19:50 +0200191 fun testFindErrorsNoErrors() {
192 val expected = setOf<ApiError>()
193 val actual =
194 findErrors(
195 parseApiSignature("in-memory", API_SIGNATURE.byteInputStream()),
Mårten Kongstad18ff19a2024-04-26 05:48:57 +0200196 parseFlagValues(generateFlagsProto(ENABLED, ENABLED)),
Mårten Kongstad9238a3a2024-04-16 13:19:50 +0200197 parseApiVersions(API_VERSIONS.byteInputStream()))
198 assertEquals(expected, actual)
199 }
200
201 @Test
Mårten Kongstadd2c70762024-05-06 14:58:18 +0200202 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">
Mårten Kongstadc3f05a62024-05-06 21:42:15 +0200223 <extends name="java/lang/Object"/>
Mårten Kongstadd2c70762024-05-06 14:58:18 +0200224 <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
Mårten Kongstade8120392024-05-06 21:32:34 +0200244 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
Mårten Kongstad0d44e722024-05-08 10:00:32 +0200293 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
Mårten Kongstad9238a3a2024-04-16 13:19:50 +0200329 fun testFindErrorsDisabledFlaggedApiIsPresent() {
330 val expected =
331 setOf<ApiError>(
Mårten Kongstad40da9702024-04-27 01:42:51 +0200332 DisabledFlaggedApiIsPresentError(
Mårten Kongstadc3f05a62024-05-06 21:42:15 +0200333 Symbol.createClass("android/Clazz", "java/lang/Object", setOf()),
334 Flag("android.flag.foo")),
Mårten Kongstad18ff19a2024-04-26 05:48:57 +0200335 DisabledFlaggedApiIsPresentError(
Mårten Kongstada1fe3712024-05-06 13:46:21 +0200336 Symbol.createMethod("android/Clazz", "Clazz()"), Flag("android.flag.foo")),
Mårten Kongstad40da9702024-04-27 01:42:51 +0200337 DisabledFlaggedApiIsPresentError(
Mårten Kongstada1fe3712024-05-06 13:46:21 +0200338 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;)"),
Mårten Kongstadb4a14bf2024-04-28 00:21:11 +0200343 Flag("android.flag.foo")),
344 DisabledFlaggedApiIsPresentError(
Mårten Kongstada1fe3712024-05-06 13:46:21 +0200345 Symbol.createMethod("android/Clazz", "setVariableData(I[Landroid/util/Atom;)"),
Mårten Kongstadb4a14bf2024-04-28 00:21:11 +0200346 Flag("android.flag.foo")),
347 DisabledFlaggedApiIsPresentError(
Mårten Kongstada1fe3712024-05-06 13:46:21 +0200348 Symbol.createMethod("android/Clazz", "innerClassArg(Landroid/Clazz/Builder;)"),
Mårten Kongstadb4a14bf2024-04-28 00:21:11 +0200349 Flag("android.flag.foo")),
350 DisabledFlaggedApiIsPresentError(
Mårten Kongstadc3f05a62024-05-06 21:42:15 +0200351 Symbol.createClass("android/Clazz/Builder", "java/lang/Object", setOf()),
352 Flag("android.flag.bar")),
Mårten Kongstad18ff19a2024-04-26 05:48:57 +0200353 )
Mårten Kongstad9238a3a2024-04-16 13:19:50 +0200354 val actual =
355 findErrors(
356 parseApiSignature("in-memory", API_SIGNATURE.byteInputStream()),
Mårten Kongstad18ff19a2024-04-26 05:48:57 +0200357 parseFlagValues(generateFlagsProto(DISABLED, DISABLED)),
Mårten Kongstad9238a3a2024-04-16 13:19:50 +0200358 parseApiVersions(API_VERSIONS.byteInputStream()))
359 assertEquals(expected, actual)
360 }
Mårten Kongstad1692a362024-06-10 16:04:34 +0200361
362 @Test
363 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 }
Mårten Kongstadf242ec82024-04-16 17:12:26 +0200380}