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