diff options
-rw-r--r-- | api/coverage/tools/ExtractFlaggedApis.kt | 36 | ||||
-rw-r--r-- | api/coverage/tools/ExtractFlaggedApisTest.kt | 78 |
2 files changed, 101 insertions, 13 deletions
diff --git a/api/coverage/tools/ExtractFlaggedApis.kt b/api/coverage/tools/ExtractFlaggedApis.kt index 5178f09f0301..5efda98a1518 100644 --- a/api/coverage/tools/ExtractFlaggedApis.kt +++ b/api/coverage/tools/ExtractFlaggedApis.kt @@ -17,6 +17,7 @@ package android.platform.coverage import com.android.tools.metalava.model.ClassItem +import com.android.tools.metalava.model.Item import com.android.tools.metalava.model.MethodItem import com.android.tools.metalava.model.text.ApiFile import java.io.File @@ -44,20 +45,9 @@ fun extractFlaggedApisFromClass( builder: FlagApiMap.Builder ) { if (methods.isEmpty()) return - val classFlag = - classItem.modifiers - .findAnnotation("android.annotation.FlaggedApi") - ?.findAttribute("value") - ?.value - ?.value() as? String + val classFlag = getClassFlag(classItem) for (method in methods) { - val methodFlag = - method.modifiers - .findAnnotation("android.annotation.FlaggedApi") - ?.findAttribute("value") - ?.value - ?.value() as? String - ?: classFlag + val methodFlag = getFlagAnnotation(method) ?: classFlag val api = JavaMethod.newBuilder() .setPackageName(packageName) @@ -81,3 +71,23 @@ fun addFlaggedApi(builder: FlagApiMap.Builder, api: JavaMethod.Builder, flag: St builder.putFlagToApi(flag, apis) } } + +fun getClassFlag(classItem: ClassItem): String? { + var classFlag = getFlagAnnotation(classItem) + var cur = classItem + // If a class is not an inner class, use its @FlaggedApi annotation value. + // Otherwise, use the flag value of the closest outer class that is annotated by @FlaggedApi. + while (cur.isInnerClass() && classFlag == null) { + cur = cur.parent() as ClassItem + classFlag = getFlagAnnotation(cur) + } + return classFlag +} + +fun getFlagAnnotation(item: Item): String? { + return item.modifiers + .findAnnotation("android.annotation.FlaggedApi") + ?.findAttribute("value") + ?.value + ?.value() as? String +} diff --git a/api/coverage/tools/ExtractFlaggedApisTest.kt b/api/coverage/tools/ExtractFlaggedApisTest.kt index ee5aaf15cb57..427be36254d3 100644 --- a/api/coverage/tools/ExtractFlaggedApisTest.kt +++ b/api/coverage/tools/ExtractFlaggedApisTest.kt @@ -141,6 +141,84 @@ class ExtractFlaggedApisTest { assertThat(result).isEqualTo(expected.build()) } + @Test + fun extractFlaggedApis_unflaggedNestedClassShouldUseOuterClassFlag() { + val apiText = + """ + // Signature format: 2.0 + package android.location.provider { + @FlaggedApi(Flags.FLAG_NEW_GEOCODER) public final class ForwardGeocodeRequest implements android.os.Parcelable { + method public int describeContents(); + } + public static final class ForwardGeocodeRequest.Builder { + method @NonNull public android.location.provider.ForwardGeocodeRequest build(); + } + } + """ + .trimIndent() + Files.write(apiTextFile, apiText.toByteArray(Charsets.UTF_8), StandardOpenOption.APPEND) + + val process = Runtime.getRuntime().exec(createCommand()) + process.waitFor() + + val content = Files.readAllBytes(flagToApiMap).toString(Charsets.UTF_8) + val result = TextFormat.parse(content, FlagApiMap::class.java) + + val expected = FlagApiMap.newBuilder() + val api1 = + JavaMethod.newBuilder() + .setPackageName("android.location.provider") + .setClassName("ForwardGeocodeRequest") + .setMethodName("describeContents") + addFlaggedApi(expected, api1, "Flags.FLAG_NEW_GEOCODER") + val api2 = + JavaMethod.newBuilder() + .setPackageName("android.location.provider") + .setClassName("ForwardGeocodeRequest.Builder") + .setMethodName("build") + addFlaggedApi(expected, api2, "Flags.FLAG_NEW_GEOCODER") + assertThat(result).ignoringRepeatedFieldOrder().isEqualTo(expected.build()) + } + + @Test + fun extractFlaggedApis_unflaggedNestedClassShouldUseOuterClassFlag_deeplyNested() { + val apiText = + """ + // Signature format: 2.0 + package android.package.xyz { + @FlaggedApi(outer_class_flag) public final class OuterClass { + method public int apiInOuterClass(); + } + public final class OuterClass.Deeply.NestedClass { + method public void apiInNestedClass(); + } + } + """ + .trimIndent() + Files.write(apiTextFile, apiText.toByteArray(Charsets.UTF_8), StandardOpenOption.APPEND) + + val process = Runtime.getRuntime().exec(createCommand()) + process.waitFor() + + val content = Files.readAllBytes(flagToApiMap).toString(Charsets.UTF_8) + val result = TextFormat.parse(content, FlagApiMap::class.java) + + val expected = FlagApiMap.newBuilder() + val api1 = + JavaMethod.newBuilder() + .setPackageName("android.package.xyz") + .setClassName("OuterClass") + .setMethodName("apiInOuterClass") + addFlaggedApi(expected, api1, "outer_class_flag") + val api2 = + JavaMethod.newBuilder() + .setPackageName("android.package.xyz") + .setClassName("OuterClass.Deeply.NestedClass") + .setMethodName("apiInNestedClass") + addFlaggedApi(expected, api2, "outer_class_flag") + assertThat(result).ignoringRepeatedFieldOrder().isEqualTo(expected.build()) + } + private fun addFlaggedApi(builder: FlagApiMap.Builder, api: JavaMethod.Builder, flag: String) { if (builder.containsFlagToApi(flag)) { val updatedApis = |