Merge "check-flagged-apis: create list of @FlaggedApi errors" into main
This commit is contained in:
commit
fde34c3b65
2 changed files with 108 additions and 21 deletions
|
@ -20,6 +20,7 @@ import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
|
||||||
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
|
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.InputStream
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
@ -36,22 +37,6 @@ private val API_SIGNATURE =
|
||||||
"""
|
"""
|
||||||
.trim()
|
.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())
|
|
||||||
}()
|
|
||||||
|
|
||||||
private val API_VERSIONS =
|
private val API_VERSIONS =
|
||||||
"""
|
"""
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
@ -64,6 +49,21 @@ private val API_VERSIONS =
|
||||||
"""
|
"""
|
||||||
.trim()
|
.trim()
|
||||||
|
|
||||||
|
private fun generateFlagsProto(fooState: Aconfig.flag_state): InputStream {
|
||||||
|
val parsed_flag =
|
||||||
|
Aconfig.parsed_flag
|
||||||
|
.newBuilder()
|
||||||
|
.setPackage("android.flag")
|
||||||
|
.setName("foo")
|
||||||
|
.setState(fooState)
|
||||||
|
.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)
|
||||||
|
return ByteArrayInputStream(binaryProto.toByteArray())
|
||||||
|
}
|
||||||
|
|
||||||
@RunWith(DeviceJUnit4ClassRunner::class)
|
@RunWith(DeviceJUnit4ClassRunner::class)
|
||||||
class CheckFlaggedApisTest : BaseHostJUnit4Test() {
|
class CheckFlaggedApisTest : BaseHostJUnit4Test() {
|
||||||
@Test
|
@Test
|
||||||
|
@ -76,7 +76,7 @@ class CheckFlaggedApisTest : BaseHostJUnit4Test() {
|
||||||
@Test
|
@Test
|
||||||
fun testParseFlagValues() {
|
fun testParseFlagValues() {
|
||||||
val expected: Map<Flag, Boolean> = mapOf(Flag("android.flag.foo") to true)
|
val expected: Map<Flag, Boolean> = mapOf(Flag("android.flag.foo") to true)
|
||||||
val actual = parseFlagValues(PARSED_FLAGS)
|
val actual = parseFlagValues(generateFlagsProto(Aconfig.flag_state.ENABLED))
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,4 +86,28 @@ class CheckFlaggedApisTest : BaseHostJUnit4Test() {
|
||||||
val actual = parseApiVersions(API_VERSIONS.byteInputStream())
|
val actual = parseApiVersions(API_VERSIONS.byteInputStream())
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFindErrorsNoErrors() {
|
||||||
|
val expected = setOf<ApiError>()
|
||||||
|
val actual =
|
||||||
|
findErrors(
|
||||||
|
parseApiSignature("in-memory", API_SIGNATURE.byteInputStream()),
|
||||||
|
parseFlagValues(generateFlagsProto(Aconfig.flag_state.ENABLED)),
|
||||||
|
parseApiVersions(API_VERSIONS.byteInputStream()))
|
||||||
|
assertEquals(expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFindErrorsDisabledFlaggedApiIsPresent() {
|
||||||
|
val expected =
|
||||||
|
setOf<ApiError>(
|
||||||
|
DisabledFlaggedApiIsPresentError(Symbol("android.Clazz.FOO"), Flag("android.flag.foo")))
|
||||||
|
val actual =
|
||||||
|
findErrors(
|
||||||
|
parseApiSignature("in-memory", API_SIGNATURE.byteInputStream()),
|
||||||
|
parseFlagValues(generateFlagsProto(Aconfig.flag_state.DISABLED)),
|
||||||
|
parseApiVersions(API_VERSIONS.byteInputStream()))
|
||||||
|
assertEquals(expected, actual)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,36 @@ internal value class Flag(val name: String) {
|
||||||
override fun toString(): String = name.toString()
|
override fun toString(): String = name.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal sealed class ApiError {
|
||||||
|
abstract val symbol: Symbol
|
||||||
|
abstract val flag: Flag
|
||||||
|
}
|
||||||
|
|
||||||
|
internal data class EnabledFlaggedApiNotPresentError(
|
||||||
|
override val symbol: Symbol,
|
||||||
|
override val flag: Flag
|
||||||
|
) : ApiError() {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "error: enabled @FlaggedApi not present in built artifact: symbol=$symbol flag=$flag"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal data class DisabledFlaggedApiIsPresentError(
|
||||||
|
override val symbol: Symbol,
|
||||||
|
override val flag: Flag
|
||||||
|
) : ApiError() {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "error: disabled @FlaggedApi is present in built artifact: symbol=$symbol flag=$flag"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal data class UnknownFlagError(override val symbol: Symbol, override val flag: Flag) :
|
||||||
|
ApiError() {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "error: unknown flag: symbol=$symbol flag=$flag"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class CheckCommand :
|
class CheckCommand :
|
||||||
CliktCommand(
|
CliktCommand(
|
||||||
help =
|
help =
|
||||||
|
@ -122,16 +152,17 @@ The tool will exit with a non-zero exit code if any flagged APIs are found to be
|
||||||
.required()
|
.required()
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
@Suppress("UNUSED_VARIABLE")
|
|
||||||
val flaggedSymbols =
|
val flaggedSymbols =
|
||||||
apiSignaturePath.toFile().inputStream().use {
|
apiSignaturePath.toFile().inputStream().use {
|
||||||
parseApiSignature(apiSignaturePath.toString(), it)
|
parseApiSignature(apiSignaturePath.toString(), it)
|
||||||
}
|
}
|
||||||
@Suppress("UNUSED_VARIABLE")
|
|
||||||
val flags = flagValuesPath.toFile().inputStream().use { parseFlagValues(it) }
|
val flags = flagValuesPath.toFile().inputStream().use { parseFlagValues(it) }
|
||||||
@Suppress("UNUSED_VARIABLE")
|
|
||||||
val exportedSymbols = apiVersionsPath.toFile().inputStream().use { parseApiVersions(it) }
|
val exportedSymbols = apiVersionsPath.toFile().inputStream().use { parseApiVersions(it) }
|
||||||
throw ProgramResult(0)
|
val errors = findErrors(flaggedSymbols, flags, exportedSymbols)
|
||||||
|
for (e in errors) {
|
||||||
|
println(e)
|
||||||
|
}
|
||||||
|
throw ProgramResult(errors.size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,4 +216,36 @@ internal fun parseApiVersions(input: InputStream): Set<Symbol> {
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find errors in the given data.
|
||||||
|
*
|
||||||
|
* @param flaggedSymbolsInSource the set of symbols that are flagged in the source code
|
||||||
|
* @param flags the set of flags and their values
|
||||||
|
* @param symbolsInOutput the set of symbols that are present in the output
|
||||||
|
* @return the set of errors found
|
||||||
|
*/
|
||||||
|
internal fun findErrors(
|
||||||
|
flaggedSymbolsInSource: Set<Pair<Symbol, Flag>>,
|
||||||
|
flags: Map<Flag, Boolean>,
|
||||||
|
symbolsInOutput: Set<Symbol>
|
||||||
|
): Set<ApiError> {
|
||||||
|
val errors = mutableSetOf<ApiError>()
|
||||||
|
for ((symbol, flag) in flaggedSymbolsInSource) {
|
||||||
|
try {
|
||||||
|
if (flags.getValue(flag)) {
|
||||||
|
if (!symbolsInOutput.contains(symbol)) {
|
||||||
|
errors.add(EnabledFlaggedApiNotPresentError(symbol, flag))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (symbolsInOutput.contains(symbol)) {
|
||||||
|
errors.add(DisabledFlaggedApiIsPresentError(symbol, flag))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: NoSuchElementException) {
|
||||||
|
errors.add(UnknownFlagError(symbol, flag))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
fun main(args: Array<String>) = CheckCommand().main(args)
|
fun main(args: Array<String>) = CheckCommand().main(args)
|
||||||
|
|
Loading…
Reference in a new issue