1*d57664e9SAndroid Build Coastguard Worker /* 2*d57664e9SAndroid Build Coastguard Worker * Copyright (C) 2024 The Android Open Source Project 3*d57664e9SAndroid Build Coastguard Worker * 4*d57664e9SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*d57664e9SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*d57664e9SAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*d57664e9SAndroid Build Coastguard Worker * 8*d57664e9SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*d57664e9SAndroid Build Coastguard Worker * 10*d57664e9SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*d57664e9SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*d57664e9SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*d57664e9SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*d57664e9SAndroid Build Coastguard Worker * limitations under the License. 15*d57664e9SAndroid Build Coastguard Worker */ 16*d57664e9SAndroid Build Coastguard Worker 17*d57664e9SAndroid Build Coastguard Worker package android.platform.coverage 18*d57664e9SAndroid Build Coastguard Worker 19*d57664e9SAndroid Build Coastguard Worker import com.google.common.truth.extensions.proto.ProtoTruth.assertThat 20*d57664e9SAndroid Build Coastguard Worker import com.google.protobuf.TextFormat 21*d57664e9SAndroid Build Coastguard Worker import java.nio.file.Files 22*d57664e9SAndroid Build Coastguard Worker import java.nio.file.Path 23*d57664e9SAndroid Build Coastguard Worker import java.nio.file.StandardOpenOption 24*d57664e9SAndroid Build Coastguard Worker import org.junit.After 25*d57664e9SAndroid Build Coastguard Worker import org.junit.Before 26*d57664e9SAndroid Build Coastguard Worker import org.junit.Test 27*d57664e9SAndroid Build Coastguard Worker import org.junit.runner.RunWith 28*d57664e9SAndroid Build Coastguard Worker import org.junit.runners.JUnit4 29*d57664e9SAndroid Build Coastguard Worker 30*d57664e9SAndroid Build Coastguard Worker @RunWith(JUnit4::class) 31*d57664e9SAndroid Build Coastguard Worker class ExtractFlaggedApisTest { 32*d57664e9SAndroid Build Coastguard Worker 33*d57664e9SAndroid Build Coastguard Worker companion object { 34*d57664e9SAndroid Build Coastguard Worker const val COMMAND = "java -jar extract-flagged-apis.jar %s %s" 35*d57664e9SAndroid Build Coastguard Worker } 36*d57664e9SAndroid Build Coastguard Worker 37*d57664e9SAndroid Build Coastguard Worker private var apiTextFile: Path = Files.createTempFile("current", ".txt") 38*d57664e9SAndroid Build Coastguard Worker private var flagToApiMap: Path = Files.createTempFile("flag_api_map", ".textproto") 39*d57664e9SAndroid Build Coastguard Worker 40*d57664e9SAndroid Build Coastguard Worker @Before setupnull41*d57664e9SAndroid Build Coastguard Worker fun setup() { 42*d57664e9SAndroid Build Coastguard Worker apiTextFile = Files.createTempFile("current", ".txt") 43*d57664e9SAndroid Build Coastguard Worker flagToApiMap = Files.createTempFile("flag_api_map", ".textproto") 44*d57664e9SAndroid Build Coastguard Worker } 45*d57664e9SAndroid Build Coastguard Worker 46*d57664e9SAndroid Build Coastguard Worker @After cleanupnull47*d57664e9SAndroid Build Coastguard Worker fun cleanup() { 48*d57664e9SAndroid Build Coastguard Worker Files.deleteIfExists(apiTextFile) 49*d57664e9SAndroid Build Coastguard Worker Files.deleteIfExists(flagToApiMap) 50*d57664e9SAndroid Build Coastguard Worker } 51*d57664e9SAndroid Build Coastguard Worker 52*d57664e9SAndroid Build Coastguard Worker @Test extractFlaggedApis_onlyMethodFlag_useMethodFlagnull53*d57664e9SAndroid Build Coastguard Worker fun extractFlaggedApis_onlyMethodFlag_useMethodFlag() { 54*d57664e9SAndroid Build Coastguard Worker val apiText = 55*d57664e9SAndroid Build Coastguard Worker """ 56*d57664e9SAndroid Build Coastguard Worker // Signature format: 2.0 57*d57664e9SAndroid Build Coastguard Worker package android.net.ipsec.ike { 58*d57664e9SAndroid Build Coastguard Worker public final class IkeSession implements java.lang.AutoCloseable { 59*d57664e9SAndroid Build Coastguard Worker method @FlaggedApi("com.android.ipsec.flags.dumpsys_api") public void dump(@NonNull java.io.PrintWriter); 60*d57664e9SAndroid Build Coastguard Worker } 61*d57664e9SAndroid Build Coastguard Worker } 62*d57664e9SAndroid Build Coastguard Worker """ 63*d57664e9SAndroid Build Coastguard Worker .trimIndent() 64*d57664e9SAndroid Build Coastguard Worker Files.write(apiTextFile, apiText.toByteArray(Charsets.UTF_8), StandardOpenOption.APPEND) 65*d57664e9SAndroid Build Coastguard Worker 66*d57664e9SAndroid Build Coastguard Worker val process = Runtime.getRuntime().exec(createCommand()) 67*d57664e9SAndroid Build Coastguard Worker process.waitFor() 68*d57664e9SAndroid Build Coastguard Worker 69*d57664e9SAndroid Build Coastguard Worker val content = Files.readAllBytes(flagToApiMap).toString(Charsets.UTF_8) 70*d57664e9SAndroid Build Coastguard Worker val result = TextFormat.parse(content, FlagApiMap::class.java) 71*d57664e9SAndroid Build Coastguard Worker 72*d57664e9SAndroid Build Coastguard Worker val expected = FlagApiMap.newBuilder() 73*d57664e9SAndroid Build Coastguard Worker val api = 74*d57664e9SAndroid Build Coastguard Worker JavaMethod.newBuilder() 75*d57664e9SAndroid Build Coastguard Worker .setPackageName("android.net.ipsec.ike") 76*d57664e9SAndroid Build Coastguard Worker .setClassName("IkeSession") 77*d57664e9SAndroid Build Coastguard Worker .setMethodName("dump") 78*d57664e9SAndroid Build Coastguard Worker api.addParameters("java.io.PrintWriter") 79*d57664e9SAndroid Build Coastguard Worker addFlaggedApi(expected, api, "com.android.ipsec.flags.dumpsys_api") 80*d57664e9SAndroid Build Coastguard Worker assertThat(result).isEqualTo(expected.build()) 81*d57664e9SAndroid Build Coastguard Worker } 82*d57664e9SAndroid Build Coastguard Worker 83*d57664e9SAndroid Build Coastguard Worker @Test extractFlaggedApis_onlyClassFlag_useClassFlagnull84*d57664e9SAndroid Build Coastguard Worker fun extractFlaggedApis_onlyClassFlag_useClassFlag() { 85*d57664e9SAndroid Build Coastguard Worker val apiText = 86*d57664e9SAndroid Build Coastguard Worker """ 87*d57664e9SAndroid Build Coastguard Worker // Signature format: 2.0 88*d57664e9SAndroid Build Coastguard Worker package android.net.ipsec.ike { 89*d57664e9SAndroid Build Coastguard Worker @FlaggedApi("com.android.ipsec.flags.dumpsys_api") public final class IkeSession implements java.lang.AutoCloseable { 90*d57664e9SAndroid Build Coastguard Worker method public void dump(@NonNull java.io.PrintWriter); 91*d57664e9SAndroid Build Coastguard Worker } 92*d57664e9SAndroid Build Coastguard Worker } 93*d57664e9SAndroid Build Coastguard Worker """ 94*d57664e9SAndroid Build Coastguard Worker .trimIndent() 95*d57664e9SAndroid Build Coastguard Worker Files.write(apiTextFile, apiText.toByteArray(Charsets.UTF_8), StandardOpenOption.APPEND) 96*d57664e9SAndroid Build Coastguard Worker 97*d57664e9SAndroid Build Coastguard Worker val process = Runtime.getRuntime().exec(createCommand()) 98*d57664e9SAndroid Build Coastguard Worker process.waitFor() 99*d57664e9SAndroid Build Coastguard Worker 100*d57664e9SAndroid Build Coastguard Worker val content = Files.readAllBytes(flagToApiMap).toString(Charsets.UTF_8) 101*d57664e9SAndroid Build Coastguard Worker val result = TextFormat.parse(content, FlagApiMap::class.java) 102*d57664e9SAndroid Build Coastguard Worker 103*d57664e9SAndroid Build Coastguard Worker val expected = FlagApiMap.newBuilder() 104*d57664e9SAndroid Build Coastguard Worker val api = 105*d57664e9SAndroid Build Coastguard Worker JavaMethod.newBuilder() 106*d57664e9SAndroid Build Coastguard Worker .setPackageName("android.net.ipsec.ike") 107*d57664e9SAndroid Build Coastguard Worker .setClassName("IkeSession") 108*d57664e9SAndroid Build Coastguard Worker .setMethodName("dump") 109*d57664e9SAndroid Build Coastguard Worker api.addParameters("java.io.PrintWriter") 110*d57664e9SAndroid Build Coastguard Worker addFlaggedApi(expected, api, "com.android.ipsec.flags.dumpsys_api") 111*d57664e9SAndroid Build Coastguard Worker assertThat(result).isEqualTo(expected.build()) 112*d57664e9SAndroid Build Coastguard Worker } 113*d57664e9SAndroid Build Coastguard Worker 114*d57664e9SAndroid Build Coastguard Worker @Test extractFlaggedApis_flaggedConstructorsAreFlaggedApisnull115*d57664e9SAndroid Build Coastguard Worker fun extractFlaggedApis_flaggedConstructorsAreFlaggedApis() { 116*d57664e9SAndroid Build Coastguard Worker val apiText = 117*d57664e9SAndroid Build Coastguard Worker """ 118*d57664e9SAndroid Build Coastguard Worker // Signature format: 2.0 119*d57664e9SAndroid Build Coastguard Worker package android.app.pinner { 120*d57664e9SAndroid Build Coastguard Worker @FlaggedApi("android.app.pinner_service_client_api") public class PinnerServiceClient { 121*d57664e9SAndroid Build Coastguard Worker ctor @FlaggedApi("android.app.pinner_service_client_api") public PinnerServiceClient(); 122*d57664e9SAndroid Build Coastguard Worker } 123*d57664e9SAndroid Build Coastguard Worker } 124*d57664e9SAndroid Build Coastguard Worker """ 125*d57664e9SAndroid Build Coastguard Worker .trimIndent() 126*d57664e9SAndroid Build Coastguard Worker Files.write(apiTextFile, apiText.toByteArray(Charsets.UTF_8), StandardOpenOption.APPEND) 127*d57664e9SAndroid Build Coastguard Worker 128*d57664e9SAndroid Build Coastguard Worker val process = Runtime.getRuntime().exec(createCommand()) 129*d57664e9SAndroid Build Coastguard Worker process.waitFor() 130*d57664e9SAndroid Build Coastguard Worker 131*d57664e9SAndroid Build Coastguard Worker val content = Files.readAllBytes(flagToApiMap).toString(Charsets.UTF_8) 132*d57664e9SAndroid Build Coastguard Worker val result = TextFormat.parse(content, FlagApiMap::class.java) 133*d57664e9SAndroid Build Coastguard Worker 134*d57664e9SAndroid Build Coastguard Worker val expected = FlagApiMap.newBuilder() 135*d57664e9SAndroid Build Coastguard Worker val api = 136*d57664e9SAndroid Build Coastguard Worker JavaMethod.newBuilder() 137*d57664e9SAndroid Build Coastguard Worker .setPackageName("android.app.pinner") 138*d57664e9SAndroid Build Coastguard Worker .setClassName("PinnerServiceClient") 139*d57664e9SAndroid Build Coastguard Worker .setMethodName("PinnerServiceClient") 140*d57664e9SAndroid Build Coastguard Worker addFlaggedApi(expected, api, "android.app.pinner_service_client_api") 141*d57664e9SAndroid Build Coastguard Worker assertThat(result).isEqualTo(expected.build()) 142*d57664e9SAndroid Build Coastguard Worker } 143*d57664e9SAndroid Build Coastguard Worker 144*d57664e9SAndroid Build Coastguard Worker @Test extractFlaggedApis_unflaggedNestedClassShouldUseOuterClassFlagnull145*d57664e9SAndroid Build Coastguard Worker fun extractFlaggedApis_unflaggedNestedClassShouldUseOuterClassFlag() { 146*d57664e9SAndroid Build Coastguard Worker val apiText = 147*d57664e9SAndroid Build Coastguard Worker """ 148*d57664e9SAndroid Build Coastguard Worker // Signature format: 2.0 149*d57664e9SAndroid Build Coastguard Worker package android.location.provider { 150*d57664e9SAndroid Build Coastguard Worker @FlaggedApi(Flags.FLAG_NEW_GEOCODER) public final class ForwardGeocodeRequest implements android.os.Parcelable { 151*d57664e9SAndroid Build Coastguard Worker method public int describeContents(); 152*d57664e9SAndroid Build Coastguard Worker } 153*d57664e9SAndroid Build Coastguard Worker public static final class ForwardGeocodeRequest.Builder { 154*d57664e9SAndroid Build Coastguard Worker method @NonNull public android.location.provider.ForwardGeocodeRequest build(); 155*d57664e9SAndroid Build Coastguard Worker } 156*d57664e9SAndroid Build Coastguard Worker } 157*d57664e9SAndroid Build Coastguard Worker """ 158*d57664e9SAndroid Build Coastguard Worker .trimIndent() 159*d57664e9SAndroid Build Coastguard Worker Files.write(apiTextFile, apiText.toByteArray(Charsets.UTF_8), StandardOpenOption.APPEND) 160*d57664e9SAndroid Build Coastguard Worker 161*d57664e9SAndroid Build Coastguard Worker val process = Runtime.getRuntime().exec(createCommand()) 162*d57664e9SAndroid Build Coastguard Worker process.waitFor() 163*d57664e9SAndroid Build Coastguard Worker 164*d57664e9SAndroid Build Coastguard Worker val content = Files.readAllBytes(flagToApiMap).toString(Charsets.UTF_8) 165*d57664e9SAndroid Build Coastguard Worker val result = TextFormat.parse(content, FlagApiMap::class.java) 166*d57664e9SAndroid Build Coastguard Worker 167*d57664e9SAndroid Build Coastguard Worker val expected = FlagApiMap.newBuilder() 168*d57664e9SAndroid Build Coastguard Worker val api1 = 169*d57664e9SAndroid Build Coastguard Worker JavaMethod.newBuilder() 170*d57664e9SAndroid Build Coastguard Worker .setPackageName("android.location.provider") 171*d57664e9SAndroid Build Coastguard Worker .setClassName("ForwardGeocodeRequest") 172*d57664e9SAndroid Build Coastguard Worker .setMethodName("describeContents") 173*d57664e9SAndroid Build Coastguard Worker addFlaggedApi(expected, api1, "Flags.FLAG_NEW_GEOCODER") 174*d57664e9SAndroid Build Coastguard Worker val api2 = 175*d57664e9SAndroid Build Coastguard Worker JavaMethod.newBuilder() 176*d57664e9SAndroid Build Coastguard Worker .setPackageName("android.location.provider") 177*d57664e9SAndroid Build Coastguard Worker .setClassName("ForwardGeocodeRequest.Builder") 178*d57664e9SAndroid Build Coastguard Worker .setMethodName("build") 179*d57664e9SAndroid Build Coastguard Worker addFlaggedApi(expected, api2, "Flags.FLAG_NEW_GEOCODER") 180*d57664e9SAndroid Build Coastguard Worker assertThat(result).ignoringRepeatedFieldOrder().isEqualTo(expected.build()) 181*d57664e9SAndroid Build Coastguard Worker } 182*d57664e9SAndroid Build Coastguard Worker 183*d57664e9SAndroid Build Coastguard Worker @Test extractFlaggedApis_unflaggedNestedClassShouldUseOuterClassFlag_deeplyNestednull184*d57664e9SAndroid Build Coastguard Worker fun extractFlaggedApis_unflaggedNestedClassShouldUseOuterClassFlag_deeplyNested() { 185*d57664e9SAndroid Build Coastguard Worker val apiText = 186*d57664e9SAndroid Build Coastguard Worker """ 187*d57664e9SAndroid Build Coastguard Worker // Signature format: 2.0 188*d57664e9SAndroid Build Coastguard Worker package android.package.xyz { 189*d57664e9SAndroid Build Coastguard Worker @FlaggedApi(outer_class_flag) public final class OuterClass { 190*d57664e9SAndroid Build Coastguard Worker method public int apiInOuterClass(); 191*d57664e9SAndroid Build Coastguard Worker } 192*d57664e9SAndroid Build Coastguard Worker public final class OuterClass.Deeply.NestedClass { 193*d57664e9SAndroid Build Coastguard Worker method public void apiInNestedClass(); 194*d57664e9SAndroid Build Coastguard Worker } 195*d57664e9SAndroid Build Coastguard Worker } 196*d57664e9SAndroid Build Coastguard Worker """ 197*d57664e9SAndroid Build Coastguard Worker .trimIndent() 198*d57664e9SAndroid Build Coastguard Worker Files.write(apiTextFile, apiText.toByteArray(Charsets.UTF_8), StandardOpenOption.APPEND) 199*d57664e9SAndroid Build Coastguard Worker 200*d57664e9SAndroid Build Coastguard Worker val process = Runtime.getRuntime().exec(createCommand()) 201*d57664e9SAndroid Build Coastguard Worker process.waitFor() 202*d57664e9SAndroid Build Coastguard Worker 203*d57664e9SAndroid Build Coastguard Worker val content = Files.readAllBytes(flagToApiMap).toString(Charsets.UTF_8) 204*d57664e9SAndroid Build Coastguard Worker val result = TextFormat.parse(content, FlagApiMap::class.java) 205*d57664e9SAndroid Build Coastguard Worker 206*d57664e9SAndroid Build Coastguard Worker val expected = FlagApiMap.newBuilder() 207*d57664e9SAndroid Build Coastguard Worker val api1 = 208*d57664e9SAndroid Build Coastguard Worker JavaMethod.newBuilder() 209*d57664e9SAndroid Build Coastguard Worker .setPackageName("android.package.xyz") 210*d57664e9SAndroid Build Coastguard Worker .setClassName("OuterClass") 211*d57664e9SAndroid Build Coastguard Worker .setMethodName("apiInOuterClass") 212*d57664e9SAndroid Build Coastguard Worker addFlaggedApi(expected, api1, "outer_class_flag") 213*d57664e9SAndroid Build Coastguard Worker val api2 = 214*d57664e9SAndroid Build Coastguard Worker JavaMethod.newBuilder() 215*d57664e9SAndroid Build Coastguard Worker .setPackageName("android.package.xyz") 216*d57664e9SAndroid Build Coastguard Worker .setClassName("OuterClass.Deeply.NestedClass") 217*d57664e9SAndroid Build Coastguard Worker .setMethodName("apiInNestedClass") 218*d57664e9SAndroid Build Coastguard Worker addFlaggedApi(expected, api2, "outer_class_flag") 219*d57664e9SAndroid Build Coastguard Worker assertThat(result).ignoringRepeatedFieldOrder().isEqualTo(expected.build()) 220*d57664e9SAndroid Build Coastguard Worker } 221*d57664e9SAndroid Build Coastguard Worker addFlaggedApinull222*d57664e9SAndroid Build Coastguard Worker private fun addFlaggedApi(builder: FlagApiMap.Builder, api: JavaMethod.Builder, flag: String) { 223*d57664e9SAndroid Build Coastguard Worker if (builder.containsFlagToApi(flag)) { 224*d57664e9SAndroid Build Coastguard Worker val updatedApis = 225*d57664e9SAndroid Build Coastguard Worker builder.getFlagToApiOrThrow(flag).toBuilder().addJavaMethods(api).build() 226*d57664e9SAndroid Build Coastguard Worker builder.putFlagToApi(flag, updatedApis) 227*d57664e9SAndroid Build Coastguard Worker } else { 228*d57664e9SAndroid Build Coastguard Worker val apis = FlaggedApis.newBuilder().addJavaMethods(api).build() 229*d57664e9SAndroid Build Coastguard Worker builder.putFlagToApi(flag, apis) 230*d57664e9SAndroid Build Coastguard Worker } 231*d57664e9SAndroid Build Coastguard Worker } 232*d57664e9SAndroid Build Coastguard Worker createCommandnull233*d57664e9SAndroid Build Coastguard Worker private fun createCommand(): Array<String> { 234*d57664e9SAndroid Build Coastguard Worker val command = 235*d57664e9SAndroid Build Coastguard Worker String.format(COMMAND, apiTextFile.toAbsolutePath(), flagToApiMap.toAbsolutePath()) 236*d57664e9SAndroid Build Coastguard Worker return command.split(" ").toTypedArray() 237*d57664e9SAndroid Build Coastguard Worker } 238*d57664e9SAndroid Build Coastguard Worker } 239