1// Copyright 2021 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package java 16 17import ( 18 "fmt" 19 "reflect" 20 "regexp" 21 "strings" 22 "testing" 23 24 "android/soong/android" 25 26 "github.com/google/blueprint/proptools" 27) 28 29func TestDroidstubs(t *testing.T) { 30 t.Parallel() 31 ctx, _ := testJavaWithFS(t, ` 32 droiddoc_exported_dir { 33 name: "droiddoc-templates-sdk", 34 path: ".", 35 } 36 37 droidstubs { 38 name: "bar-stubs", 39 srcs: ["bar-doc/a.java"], 40 api_levels_annotations_dirs: ["droiddoc-templates-sdk"], 41 api_levels_annotations_enabled: true, 42 } 43 44 droidstubs { 45 name: "bar-stubs-other", 46 srcs: ["bar-doc/a.java"], 47 high_mem: true, 48 api_levels_annotations_dirs: ["droiddoc-templates-sdk"], 49 api_levels_annotations_enabled: true, 50 api_levels_jar_filename: "android.other.jar", 51 } 52 53 droidstubs { 54 name: "stubs-applying-api-versions", 55 srcs: ["bar-doc/a.java"], 56 api_levels_module: "bar-stubs-other", 57 } 58 `, 59 map[string][]byte{ 60 "bar-doc/a.java": nil, 61 }) 62 testcases := []struct { 63 moduleName string 64 expectedJarFilename string 65 generate_xml bool 66 high_mem bool 67 }{ 68 { 69 moduleName: "bar-stubs", 70 generate_xml: true, 71 expectedJarFilename: "android.jar", 72 high_mem: false, 73 }, 74 { 75 moduleName: "bar-stubs-other", 76 generate_xml: true, 77 expectedJarFilename: "android.other.jar", 78 high_mem: true, 79 }, 80 { 81 moduleName: "stubs-applying-api-versions", 82 generate_xml: false, 83 }, 84 } 85 for _, c := range testcases { 86 m := ctx.ModuleForTests(t, c.moduleName, "android_common") 87 manifest := m.Output("metalava.sbox.textproto") 88 sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, manifest) 89 cmdline := String(sboxProto.Commands[0].Command) 90 android.AssertStringContainsEquals(t, "api-versions generation flag", cmdline, "--generate-api-levels", c.generate_xml) 91 if c.expectedJarFilename != "" { 92 expected := "--android-jar-pattern ./{version:major.minor?}/public/" + c.expectedJarFilename 93 if !strings.Contains(cmdline, expected) { 94 t.Errorf("For %q, expected metalava argument %q, but was not found %q", c.moduleName, expected, cmdline) 95 } 96 } 97 98 metalava := m.Rule("metalava") 99 rp := metalava.RuleParams 100 if actual := rp.Pool != nil && strings.Contains(rp.Pool.String(), "highmem"); actual != c.high_mem { 101 t.Errorf("Expected %q high_mem to be %v, was %v", c.moduleName, c.high_mem, actual) 102 } 103 } 104} 105 106// runs a test for droidstubs with a customizable sdkType argument and returns 107// the list of jar patterns that is passed as `--android-jar-pattern` 108func getAndroidJarPatternsForDroidstubs(t *testing.T, sdkType string) []string { 109 ctx, _ := testJavaWithFS(t, fmt.Sprintf(` 110 droiddoc_exported_dir { 111 name: "some-exported-dir", 112 path: "somedir", 113 } 114 115 droiddoc_exported_dir { 116 name: "some-other-exported-dir", 117 path: "someotherdir", 118 } 119 120 droidstubs { 121 name: "foo-stubs", 122 srcs: ["foo-doc/a.java"], 123 api_levels_annotations_dirs: [ 124 "some-exported-dir", 125 "some-other-exported-dir", 126 ], 127 api_levels_annotations_enabled: true, 128 api_levels_sdk_type: "%s", 129 } 130 `, sdkType), 131 map[string][]byte{ 132 "foo-doc/a.java": nil, 133 }) 134 135 m := ctx.ModuleForTests(t, "foo-stubs", "android_common") 136 manifest := m.Output("metalava.sbox.textproto") 137 cmd := String(android.RuleBuilderSboxProtoForTests(t, ctx, manifest).Commands[0].Command) 138 r := regexp.MustCompile(`--android-jar-pattern [^ ]+/android.jar`) 139 return r.FindAllString(cmd, -1) 140} 141 142func TestPublicDroidstubs(t *testing.T) { 143 t.Parallel() 144 patterns := getAndroidJarPatternsForDroidstubs(t, "public") 145 146 android.AssertArrayString(t, "order of patterns", []string{ 147 "--android-jar-pattern somedir/{version:major.minor?}/public/android.jar", 148 "--android-jar-pattern someotherdir/{version:major.minor?}/public/android.jar", 149 }, patterns) 150} 151 152func TestSystemDroidstubs(t *testing.T) { 153 t.Parallel() 154 patterns := getAndroidJarPatternsForDroidstubs(t, "system") 155 156 android.AssertArrayString(t, "order of patterns", []string{ 157 "--android-jar-pattern somedir/{version:major.minor?}/system/android.jar", 158 "--android-jar-pattern someotherdir/{version:major.minor?}/system/android.jar", 159 "--android-jar-pattern somedir/{version:major.minor?}/public/android.jar", 160 "--android-jar-pattern someotherdir/{version:major.minor?}/public/android.jar", 161 }, patterns) 162} 163 164func TestModuleLibDroidstubs(t *testing.T) { 165 t.Parallel() 166 patterns := getAndroidJarPatternsForDroidstubs(t, "module-lib") 167 168 android.AssertArrayString(t, "order of patterns", []string{ 169 "--android-jar-pattern somedir/{version:major.minor?}/module-lib/android.jar", 170 "--android-jar-pattern someotherdir/{version:major.minor?}/module-lib/android.jar", 171 "--android-jar-pattern somedir/{version:major.minor?}/system/android.jar", 172 "--android-jar-pattern someotherdir/{version:major.minor?}/system/android.jar", 173 "--android-jar-pattern somedir/{version:major.minor?}/public/android.jar", 174 "--android-jar-pattern someotherdir/{version:major.minor?}/public/android.jar", 175 }, patterns) 176} 177 178func TestSystemServerDroidstubs(t *testing.T) { 179 patterns := getAndroidJarPatternsForDroidstubs(t, "system-server") 180 181 android.AssertArrayString(t, "order of patterns", []string{ 182 "--android-jar-pattern somedir/{version:major.minor?}/system-server/android.jar", 183 "--android-jar-pattern someotherdir/{version:major.minor?}/system-server/android.jar", 184 "--android-jar-pattern somedir/{version:major.minor?}/module-lib/android.jar", 185 "--android-jar-pattern someotherdir/{version:major.minor?}/module-lib/android.jar", 186 "--android-jar-pattern somedir/{version:major.minor?}/system/android.jar", 187 "--android-jar-pattern someotherdir/{version:major.minor?}/system/android.jar", 188 "--android-jar-pattern somedir/{version:major.minor?}/public/android.jar", 189 "--android-jar-pattern someotherdir/{version:major.minor?}/public/android.jar", 190 }, patterns) 191} 192 193func TestDroidstubsSandbox(t *testing.T) { 194 ctx, _ := testJavaWithFS(t, ` 195 genrule { 196 name: "foo", 197 out: ["foo.txt"], 198 cmd: "touch $(out)", 199 } 200 201 droidstubs { 202 name: "bar-stubs", 203 srcs: ["bar-doc/a.java"], 204 205 args: "--reference $(location :foo)", 206 arg_files: [":foo"], 207 } 208 `, 209 map[string][]byte{ 210 "bar-doc/a.java": nil, 211 }) 212 213 m := ctx.ModuleForTests(t, "bar-stubs", "android_common") 214 metalava := m.Rule("metalava") 215 if g, w := metalava.Inputs.Strings(), []string{"bar-doc/a.java"}; !reflect.DeepEqual(w, g) { 216 t.Errorf("Expected inputs %q, got %q", w, g) 217 } 218 219 manifest := android.RuleBuilderSboxProtoForTests(t, ctx, m.Output("metalava.sbox.textproto")) 220 if g, w := manifest.Commands[0].GetCommand(), "reference __SBOX_SANDBOX_DIR__/out/soong/.intermediates/foo/gen/foo.txt"; !strings.Contains(g, w) { 221 t.Errorf("Expected command to contain %q, got %q", w, g) 222 } 223} 224 225func TestDroidstubsWithSystemModules(t *testing.T) { 226 ctx, _ := testJava(t, ` 227 droidstubs { 228 name: "stubs-source-system-modules", 229 srcs: [ 230 "bar-doc/a.java", 231 ], 232 sdk_version: "none", 233 system_modules: "source-system-modules", 234 } 235 236 java_library { 237 name: "source-jar", 238 srcs: [ 239 "a.java", 240 ], 241 } 242 243 java_system_modules { 244 name: "source-system-modules", 245 libs: ["source-jar"], 246 } 247 248 droidstubs { 249 name: "stubs-prebuilt-system-modules", 250 srcs: [ 251 "bar-doc/a.java", 252 ], 253 sdk_version: "none", 254 system_modules: "prebuilt-system-modules", 255 } 256 257 java_import { 258 name: "prebuilt-jar", 259 jars: ["a.jar"], 260 } 261 262 java_system_modules_import { 263 name: "prebuilt-system-modules", 264 libs: ["prebuilt-jar"], 265 } 266 `) 267 268 checkSystemModulesUseByDroidstubs(t, ctx, "stubs-source-system-modules", "source-jar.jar") 269 270 checkSystemModulesUseByDroidstubs(t, ctx, "stubs-prebuilt-system-modules", "prebuilt-jar.jar") 271} 272 273func checkSystemModulesUseByDroidstubs(t *testing.T, ctx *android.TestContext, moduleName string, systemJar string) { 274 metalavaRule := ctx.ModuleForTests(t, moduleName, "android_common").Rule("metalava") 275 var systemJars []string 276 for _, i := range metalavaRule.Implicits { 277 systemJars = append(systemJars, i.Base()) 278 } 279 if len(systemJars) < 1 || systemJars[0] != systemJar { 280 t.Errorf("inputs of %q must be []string{%q}, but was %#v.", moduleName, systemJar, systemJars) 281 } 282} 283 284func TestDroidstubsWithSdkExtensions(t *testing.T) { 285 ctx, _ := testJavaWithFS(t, ` 286 droiddoc_exported_dir { 287 name: "sdk-dir", 288 path: "sdk", 289 } 290 291 droidstubs { 292 name: "baz-stubs", 293 api_levels_annotations_dirs: ["sdk-dir"], 294 api_levels_annotations_enabled: true, 295 extensions_info_file: ":info-file", 296 } 297 298 filegroup { 299 name: "info-file", 300 srcs: ["sdk/extensions/info.txt"], 301 } 302 `, 303 map[string][]byte{ 304 "sdk/extensions/1/public/some-mainline-module-stubs.jar": nil, 305 "sdk/extensions/info.txt": nil, 306 }) 307 m := ctx.ModuleForTests(t, "baz-stubs", "android_common") 308 manifest := m.Output("metalava.sbox.textproto") 309 cmdline := String(android.RuleBuilderSboxProtoForTests(t, ctx, manifest).Commands[0].Command) 310 android.AssertStringDoesContain(t, "android-jar-pattern present", cmdline, "--android-jar-pattern sdk/extensions/{version:extension}/public/{module}.jar") 311 android.AssertStringDoesContain(t, "sdk-extensions-info present", cmdline, "--sdk-extensions-info sdk/extensions/info.txt") 312} 313 314func TestDroidStubsApiContributionGeneration(t *testing.T) { 315 ctx, _ := testJavaWithFS(t, ` 316 droidstubs { 317 name: "foo", 318 srcs: ["A/a.java"], 319 api_surface: "public", 320 check_api: { 321 current: { 322 api_file: "A/current.txt", 323 removed_api_file: "A/removed.txt", 324 } 325 } 326 } 327 `, 328 map[string][]byte{ 329 "A/a.java": nil, 330 "A/current.txt": nil, 331 "A/removed.txt": nil, 332 }, 333 ) 334 335 ctx.ModuleForTests(t, "foo.api.contribution", "") 336} 337 338func TestGeneratedApiContributionVisibilityTest(t *testing.T) { 339 library_bp := ` 340 java_api_library { 341 name: "bar", 342 api_surface: "public", 343 api_contributions: ["foo.api.contribution"], 344 stubs_type: "everything", 345 } 346 ` 347 ctx, _ := testJavaWithFS(t, ` 348 droidstubs { 349 name: "foo", 350 srcs: ["A/a.java"], 351 api_surface: "public", 352 check_api: { 353 current: { 354 api_file: "A/current.txt", 355 removed_api_file: "A/removed.txt", 356 } 357 }, 358 visibility: ["//a", "//b"], 359 } 360 `, 361 map[string][]byte{ 362 "a/a.java": nil, 363 "a/current.txt": nil, 364 "a/removed.txt": nil, 365 "b/Android.bp": []byte(library_bp), 366 }, 367 ) 368 369 ctx.ModuleForTests(t, "bar", "android_common") 370} 371 372func TestAconfigDeclarations(t *testing.T) { 373 result := android.GroupFixturePreparers( 374 prepareForJavaTest, 375 android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { 376 }), 377 android.FixtureMergeMockFs(map[string][]byte{ 378 "a/A.java": nil, 379 "a/current.txt": nil, 380 "a/removed.txt": nil, 381 }), 382 ).RunTestWithBp(t, ` 383 aconfig_declarations { 384 name: "bar", 385 package: "com.example.package", 386 container: "com.android.foo", 387 srcs: [ 388 "bar.aconfig", 389 ], 390 } 391 droidstubs { 392 name: "foo", 393 srcs: ["a/A.java"], 394 api_surface: "public", 395 check_api: { 396 current: { 397 api_file: "a/current.txt", 398 removed_api_file: "a/removed.txt", 399 } 400 }, 401 aconfig_declarations: [ 402 "bar", 403 ], 404 } 405 `) 406 407 // Check that droidstubs depend on aconfig_declarations 408 android.AssertBoolEquals(t, "foo expected to depend on bar", 409 CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "bar"), true) 410 411 m := result.ModuleForTests(t, "foo", "android_common") 412 android.AssertStringDoesContain(t, "foo generates revert annotations file", 413 strings.Join(m.AllOutputs(), ""), "flags-config-exportable.xml") 414 415 // revert-annotations.txt passed to exportable stubs generation metalava command 416 manifest := m.Output("metalava_exportable.sbox.textproto") 417 cmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest).Commands[0].Command) 418 android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "flags-config-exportable.xml") 419 420 android.AssertStringDoesContain(t, "foo generates exportable stubs jar", 421 strings.Join(m.AllOutputs(), ""), "exportable/foo-stubs.srcjar") 422} 423 424func TestReleaseExportRuntimeApis(t *testing.T) { 425 result := android.GroupFixturePreparers( 426 prepareForJavaTest, 427 android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { 428 variables.ExportRuntimeApis = proptools.BoolPtr(true) 429 }), 430 android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), 431 android.FixtureMergeMockFs(map[string][]byte{ 432 "a/A.java": nil, 433 "a/current.txt": nil, 434 "a/removed.txt": nil, 435 }), 436 ).RunTestWithBp(t, ` 437 aconfig_declarations { 438 name: "bar", 439 package: "com.example.package", 440 container: "com.android.foo", 441 srcs: [ 442 "bar.aconfig", 443 ], 444 } 445 droidstubs { 446 name: "foo", 447 srcs: ["a/A.java"], 448 api_surface: "public", 449 check_api: { 450 current: { 451 api_file: "a/current.txt", 452 removed_api_file: "a/removed.txt", 453 } 454 }, 455 aconfig_declarations: [ 456 "bar", 457 ], 458 } 459 `) 460 461 m := result.ModuleForTests(t, "foo", "android_common") 462 463 rule := m.Output("released-flags-exportable.pb") 464 exposeWritableApisFilter := "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'" 465 android.AssertStringEquals(t, "Filter argument expected to contain READ_WRITE permissions", exposeWritableApisFilter, rule.Args["filter_args"]) 466} 467