Merge changes I397b32ae,Ic244b896,I5ccf2a64 into main
* changes: check-flagged-apis: parse flag names and values check-flagged-apis: parse API signature files check-flagged-apis: add unit test infrastructure
This commit is contained in:
commit
e24a1e4ddc
3 changed files with 174 additions and 7 deletions
|
@ -13,16 +13,39 @@
|
|||
// limitations under the License.
|
||||
|
||||
package {
|
||||
default_team: "trendy_team_updatable_sdk_apis",
|
||||
default_applicable_licenses: ["Android-Apache-2.0"],
|
||||
}
|
||||
|
||||
java_defaults {
|
||||
name: "check-flagged-apis-defaults",
|
||||
srcs: [
|
||||
"src/com/android/checkflaggedapis/Main.kt",
|
||||
],
|
||||
static_libs: [
|
||||
"libaconfig_java_proto_lite",
|
||||
"metalava-signature-reader",
|
||||
"metalava-tools-common-m2-deps",
|
||||
],
|
||||
}
|
||||
|
||||
java_binary_host {
|
||||
name: "check-flagged-apis",
|
||||
srcs: [
|
||||
"src/**/*.kt",
|
||||
],
|
||||
static_libs: [
|
||||
"metalava-tools-common-m2-deps",
|
||||
defaults: [
|
||||
"check-flagged-apis-defaults",
|
||||
],
|
||||
main_class: "com.android.checkflaggedapis.Main",
|
||||
}
|
||||
|
||||
java_test_host {
|
||||
name: "check-flagged-apis-test",
|
||||
defaults: [
|
||||
"check-flagged-apis-defaults",
|
||||
],
|
||||
srcs: [
|
||||
"src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt",
|
||||
],
|
||||
static_libs: [
|
||||
"tradefed",
|
||||
],
|
||||
}
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.checkflaggedapis
|
||||
|
||||
import android.aconfig.Aconfig
|
||||
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
|
||||
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
private val API_SIGNATURE =
|
||||
"""
|
||||
// Signature format: 2.0
|
||||
package android {
|
||||
public final class Clazz {
|
||||
ctor public Clazz();
|
||||
field @FlaggedApi("android.flag.foo") public static final int FOO = 1; // 0x1
|
||||
}
|
||||
}
|
||||
"""
|
||||
.trim()
|
||||
|
||||
private val PARSED_FLAGS =
|
||||
{
|
||||
val parsed_flag =
|
||||
Aconfig.parsed_flag
|
||||
.newBuilder()
|
||||
.setPackage("android.flag")
|
||||
.setName("foo")
|
||||
.setState(Aconfig.flag_state.ENABLED)
|
||||
.setPermission(Aconfig.flag_permission.READ_ONLY)
|
||||
.build()
|
||||
val parsed_flags = Aconfig.parsed_flags.newBuilder().addParsedFlag(parsed_flag).build()
|
||||
val binaryProto = ByteArrayOutputStream()
|
||||
parsed_flags.writeTo(binaryProto)
|
||||
ByteArrayInputStream(binaryProto.toByteArray())
|
||||
}()
|
||||
|
||||
@RunWith(DeviceJUnit4ClassRunner::class)
|
||||
class CheckFlaggedApisTest : BaseHostJUnit4Test() {
|
||||
@Test
|
||||
fun testParseApiSignature() {
|
||||
val expected = setOf(Pair(Symbol("android.Clazz.FOO"), Flag("android.flag.foo")))
|
||||
val actual = parseApiSignature("in-memory", API_SIGNATURE.byteInputStream())
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testParseFlagValues() {
|
||||
val expected: Map<Flag, Boolean> = mapOf(Flag("android.flag.foo") to true)
|
||||
val actual = parseFlagValues(PARSED_FLAGS)
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
}
|
|
@ -17,8 +17,17 @@
|
|||
|
||||
package com.android.checkflaggedapis
|
||||
|
||||
import android.aconfig.Aconfig
|
||||
import com.android.tools.metalava.model.BaseItemVisitor
|
||||
import com.android.tools.metalava.model.FieldItem
|
||||
import com.android.tools.metalava.model.text.ApiFile
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import com.github.ajalt.clikt.core.ProgramResult
|
||||
import com.github.ajalt.clikt.parameters.options.help
|
||||
import com.github.ajalt.clikt.parameters.options.option
|
||||
import com.github.ajalt.clikt.parameters.options.required
|
||||
import com.github.ajalt.clikt.parameters.types.path
|
||||
import java.io.InputStream
|
||||
|
||||
/**
|
||||
* Class representing the fully qualified name of a class, method or field.
|
||||
|
@ -70,11 +79,76 @@ internal value class Flag(val name: String) {
|
|||
override fun toString(): String = name.toString()
|
||||
}
|
||||
|
||||
class CheckCommand : CliktCommand() {
|
||||
class CheckCommand :
|
||||
CliktCommand(
|
||||
help =
|
||||
"""
|
||||
Check that all flagged APIs are used in the correct way.
|
||||
|
||||
This tool reads the API signature file and checks that all flagged APIs are used in the correct way.
|
||||
|
||||
The tool will exit with a non-zero exit code if any flagged APIs are found to be used in the incorrect way.
|
||||
""") {
|
||||
private val apiSignaturePath by
|
||||
option("--api-signature")
|
||||
.help(
|
||||
"""
|
||||
Path to API signature file.
|
||||
Usually named *current.txt.
|
||||
Tip: `m frameworks-base-api-current.txt` will generate a file that includes all platform and mainline APIs.
|
||||
""")
|
||||
.path(mustExist = true, canBeDir = false, mustBeReadable = true)
|
||||
.required()
|
||||
private val flagValuesPath by
|
||||
option("--flag-values")
|
||||
.help(
|
||||
"""
|
||||
Path to aconfig parsed_flags binary proto file.
|
||||
Tip: `m all_aconfig_declarations` will generate a file that includes all information about all flags.
|
||||
""")
|
||||
.path(mustExist = true, canBeDir = false, mustBeReadable = true)
|
||||
.required()
|
||||
|
||||
override fun run() {
|
||||
println("hello world")
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
val flaggedSymbols =
|
||||
apiSignaturePath.toFile().inputStream().use {
|
||||
parseApiSignature(apiSignaturePath.toString(), it)
|
||||
}
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
val flags = flagValuesPath.toFile().inputStream().use { parseFlagValues(it) }
|
||||
throw ProgramResult(0)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun parseApiSignature(path: String, input: InputStream): Set<Pair<Symbol, Flag>> {
|
||||
// TODO(334870672): add support for classes and metods
|
||||
val output = mutableSetOf<Pair<Symbol, Flag>>()
|
||||
val visitor =
|
||||
object : BaseItemVisitor() {
|
||||
override fun visitField(field: FieldItem) {
|
||||
val flag =
|
||||
field.modifiers
|
||||
.findAnnotation("android.annotation.FlaggedApi")
|
||||
?.findAttribute("value")
|
||||
?.value
|
||||
?.value() as? String
|
||||
if (flag != null) {
|
||||
val symbol = Symbol.create(field.baselineElementId())
|
||||
output.add(Pair(symbol, Flag(flag)))
|
||||
}
|
||||
}
|
||||
}
|
||||
val codebase = ApiFile.parseApi(path, input)
|
||||
codebase.accept(visitor)
|
||||
return output
|
||||
}
|
||||
|
||||
internal fun parseFlagValues(input: InputStream): Map<Flag, Boolean> {
|
||||
val parsedFlags = Aconfig.parsed_flags.parseFrom(input).getParsedFlagList()
|
||||
return parsedFlags.associateBy(
|
||||
{ Flag("${it.getPackage()}.${it.getName()}") },
|
||||
{ it.getState() == Aconfig.flag_state.ENABLED })
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) = CheckCommand().main(args)
|
||||
|
|
Loading…
Reference in a new issue