Loading api/coverage/tools/ExtractFlaggedApis.kt +23 −13 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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) Loading @@ -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 } api/coverage/tools/ExtractFlaggedApisTest.kt +78 −0 Original line number Diff line number Diff line Loading @@ -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 = Loading Loading
api/coverage/tools/ExtractFlaggedApis.kt +23 −13 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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) Loading @@ -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 }
api/coverage/tools/ExtractFlaggedApisTest.kt +78 −0 Original line number Diff line number Diff line Loading @@ -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 = Loading