Loading tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt +41 −17 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import com.android.tradefed.testtype.DeviceJUnit4ClassRunner import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.InputStream import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith Loading @@ -36,22 +37,6 @@ private val API_SIGNATURE = """ .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 = """ <?xml version="1.0" encoding="utf-8"?> Loading @@ -64,6 +49,21 @@ private val API_VERSIONS = """ .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) class CheckFlaggedApisTest : BaseHostJUnit4Test() { @Test Loading @@ -76,7 +76,7 @@ class CheckFlaggedApisTest : BaseHostJUnit4Test() { @Test fun testParseFlagValues() { 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) } Loading @@ -86,4 +86,28 @@ class CheckFlaggedApisTest : BaseHostJUnit4Test() { val actual = parseApiVersions(API_VERSIONS.byteInputStream()) 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) } } tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt +67 −4 Original line number Diff line number Diff line Loading @@ -81,6 +81,36 @@ internal value class Flag(val name: String) { 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 : CliktCommand( help = Loading Loading @@ -122,16 +152,17 @@ The tool will exit with a non-zero exit code if any flagged APIs are found to be .required() override fun run() { @Suppress("UNUSED_VARIABLE") val flaggedSymbols = apiSignaturePath.toFile().inputStream().use { parseApiSignature(apiSignaturePath.toString(), it) } @Suppress("UNUSED_VARIABLE") val flags = flagValuesPath.toFile().inputStream().use { parseFlagValues(it) } @Suppress("UNUSED_VARIABLE") 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) } } Loading Loading @@ -185,4 +216,36 @@ internal fun parseApiVersions(input: InputStream): Set<Symbol> { 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) Loading
tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt +41 −17 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import com.android.tradefed.testtype.DeviceJUnit4ClassRunner import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.InputStream import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith Loading @@ -36,22 +37,6 @@ private val API_SIGNATURE = """ .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 = """ <?xml version="1.0" encoding="utf-8"?> Loading @@ -64,6 +49,21 @@ private val API_VERSIONS = """ .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) class CheckFlaggedApisTest : BaseHostJUnit4Test() { @Test Loading @@ -76,7 +76,7 @@ class CheckFlaggedApisTest : BaseHostJUnit4Test() { @Test fun testParseFlagValues() { 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) } Loading @@ -86,4 +86,28 @@ class CheckFlaggedApisTest : BaseHostJUnit4Test() { val actual = parseApiVersions(API_VERSIONS.byteInputStream()) 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) } }
tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt +67 −4 Original line number Diff line number Diff line Loading @@ -81,6 +81,36 @@ internal value class Flag(val name: String) { 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 : CliktCommand( help = Loading Loading @@ -122,16 +152,17 @@ The tool will exit with a non-zero exit code if any flagged APIs are found to be .required() override fun run() { @Suppress("UNUSED_VARIABLE") val flaggedSymbols = apiSignaturePath.toFile().inputStream().use { parseApiSignature(apiSignaturePath.toString(), it) } @Suppress("UNUSED_VARIABLE") val flags = flagValuesPath.toFile().inputStream().use { parseFlagValues(it) } @Suppress("UNUSED_VARIABLE") 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) } } Loading Loading @@ -185,4 +216,36 @@ internal fun parseApiVersions(input: InputStream): Set<Symbol> { 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)