diff --git a/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt b/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt index 9e6a6e334a..d2b75d48ec 100644 --- a/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt +++ b/tools/check-flagged-apis/src/com/android/checkflaggedapis/CheckFlaggedApisTest.kt @@ -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 @@ -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 = """ @@ -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 @@ -76,7 +76,7 @@ class CheckFlaggedApisTest : BaseHostJUnit4Test() { @Test fun testParseFlagValues() { val expected: Map = mapOf(Flag("android.flag.foo") to true) - val actual = parseFlagValues(PARSED_FLAGS) + val actual = parseFlagValues(generateFlagsProto(Aconfig.flag_state.ENABLED)) assertEquals(expected, actual) } @@ -86,4 +86,28 @@ class CheckFlaggedApisTest : BaseHostJUnit4Test() { val actual = parseApiVersions(API_VERSIONS.byteInputStream()) assertEquals(expected, actual) } + + @Test + fun testFindErrorsNoErrors() { + val expected = setOf() + 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( + 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) + } } diff --git a/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt b/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt index e7eff176be..84564ba34c 100644 --- a/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt +++ b/tools/check-flagged-apis/src/com/android/checkflaggedapis/Main.kt @@ -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 = @@ -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) } } @@ -185,4 +216,36 @@ internal fun parseApiVersions(input: InputStream): Set { 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>, + flags: Map, + symbolsInOutput: Set +): Set { + val errors = mutableSetOf() + 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) = CheckCommand().main(args)