| // Copyright 2021 Google Inc. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package java |
| |
| import ( |
| "fmt" |
| "reflect" |
| "regexp" |
| "strings" |
| "testing" |
| |
| "android/soong/android" |
| |
| "github.com/google/blueprint/proptools" |
| ) |
| |
| func TestDroidstubs(t *testing.T) { |
| ctx, _ := testJavaWithFS(t, ` |
| droiddoc_exported_dir { |
| name: "droiddoc-templates-sdk", |
| path: ".", |
| } |
| |
| droidstubs { |
| name: "bar-stubs", |
| srcs: ["bar-doc/a.java"], |
| api_levels_annotations_dirs: ["droiddoc-templates-sdk"], |
| api_levels_annotations_enabled: true, |
| } |
| |
| droidstubs { |
| name: "bar-stubs-other", |
| srcs: ["bar-doc/a.java"], |
| high_mem: true, |
| api_levels_annotations_dirs: ["droiddoc-templates-sdk"], |
| api_levels_annotations_enabled: true, |
| api_levels_jar_filename: "android.other.jar", |
| } |
| |
| droidstubs { |
| name: "stubs-applying-api-versions", |
| srcs: ["bar-doc/a.java"], |
| api_levels_module: "bar-stubs-other", |
| } |
| `, |
| map[string][]byte{ |
| "bar-doc/a.java": nil, |
| }) |
| testcases := []struct { |
| moduleName string |
| expectedJarFilename string |
| generate_xml bool |
| high_mem bool |
| }{ |
| { |
| moduleName: "bar-stubs", |
| generate_xml: true, |
| expectedJarFilename: "android.jar", |
| high_mem: false, |
| }, |
| { |
| moduleName: "bar-stubs-other", |
| generate_xml: true, |
| expectedJarFilename: "android.other.jar", |
| high_mem: true, |
| }, |
| { |
| moduleName: "stubs-applying-api-versions", |
| generate_xml: false, |
| }, |
| } |
| for _, c := range testcases { |
| m := ctx.ModuleForTests(c.moduleName, "android_common") |
| manifest := m.Output("metalava.sbox.textproto") |
| sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx, manifest) |
| cmdline := String(sboxProto.Commands[0].Command) |
| android.AssertStringContainsEquals(t, "api-versions generation flag", cmdline, "--generate-api-levels", c.generate_xml) |
| if c.expectedJarFilename != "" { |
| expected := "--android-jar-pattern ./%/public/" + c.expectedJarFilename |
| if !strings.Contains(cmdline, expected) { |
| t.Errorf("For %q, expected metalava argument %q, but was not found %q", c.moduleName, expected, cmdline) |
| } |
| } |
| |
| metalava := m.Rule("metalava") |
| rp := metalava.RuleParams |
| if actual := rp.Pool != nil && strings.Contains(rp.Pool.String(), "highmem"); actual != c.high_mem { |
| t.Errorf("Expected %q high_mem to be %v, was %v", c.moduleName, c.high_mem, actual) |
| } |
| } |
| } |
| |
| // runs a test for droidstubs with a customizable sdkType argument and returns |
| // the list of jar patterns that is passed as `--android-jar-pattern` |
| func getAndroidJarPatternsForDroidstubs(t *testing.T, sdkType string) []string { |
| ctx, _ := testJavaWithFS(t, fmt.Sprintf(` |
| droiddoc_exported_dir { |
| name: "some-exported-dir", |
| path: "somedir", |
| } |
| |
| droiddoc_exported_dir { |
| name: "some-other-exported-dir", |
| path: "someotherdir", |
| } |
| |
| droidstubs { |
| name: "foo-stubs", |
| srcs: ["foo-doc/a.java"], |
| api_levels_annotations_dirs: [ |
| "some-exported-dir", |
| "some-other-exported-dir", |
| ], |
| api_levels_annotations_enabled: true, |
| api_levels_sdk_type: "%s", |
| } |
| `, sdkType), |
| map[string][]byte{ |
| "foo-doc/a.java": nil, |
| }) |
| |
| m := ctx.ModuleForTests("foo-stubs", "android_common") |
| manifest := m.Output("metalava.sbox.textproto") |
| cmd := String(android.RuleBuilderSboxProtoForTests(t, ctx, manifest).Commands[0].Command) |
| r := regexp.MustCompile(`--android-jar-pattern [^ ]+/android.jar`) |
| return r.FindAllString(cmd, -1) |
| } |
| |
| func TestPublicDroidstubs(t *testing.T) { |
| patterns := getAndroidJarPatternsForDroidstubs(t, "public") |
| |
| android.AssertArrayString(t, "order of patterns", []string{ |
| "--android-jar-pattern somedir/%/public/android.jar", |
| "--android-jar-pattern someotherdir/%/public/android.jar", |
| }, patterns) |
| } |
| |
| func TestSystemDroidstubs(t *testing.T) { |
| patterns := getAndroidJarPatternsForDroidstubs(t, "system") |
| |
| android.AssertArrayString(t, "order of patterns", []string{ |
| "--android-jar-pattern somedir/%/system/android.jar", |
| "--android-jar-pattern someotherdir/%/system/android.jar", |
| "--android-jar-pattern somedir/%/public/android.jar", |
| "--android-jar-pattern someotherdir/%/public/android.jar", |
| }, patterns) |
| } |
| |
| func TestModuleLibDroidstubs(t *testing.T) { |
| patterns := getAndroidJarPatternsForDroidstubs(t, "module-lib") |
| |
| android.AssertArrayString(t, "order of patterns", []string{ |
| "--android-jar-pattern somedir/%/module-lib/android.jar", |
| "--android-jar-pattern someotherdir/%/module-lib/android.jar", |
| "--android-jar-pattern somedir/%/system/android.jar", |
| "--android-jar-pattern someotherdir/%/system/android.jar", |
| "--android-jar-pattern somedir/%/public/android.jar", |
| "--android-jar-pattern someotherdir/%/public/android.jar", |
| }, patterns) |
| } |
| |
| func TestSystemServerDroidstubs(t *testing.T) { |
| patterns := getAndroidJarPatternsForDroidstubs(t, "system-server") |
| |
| android.AssertArrayString(t, "order of patterns", []string{ |
| "--android-jar-pattern somedir/%/system-server/android.jar", |
| "--android-jar-pattern someotherdir/%/system-server/android.jar", |
| "--android-jar-pattern somedir/%/module-lib/android.jar", |
| "--android-jar-pattern someotherdir/%/module-lib/android.jar", |
| "--android-jar-pattern somedir/%/system/android.jar", |
| "--android-jar-pattern someotherdir/%/system/android.jar", |
| "--android-jar-pattern somedir/%/public/android.jar", |
| "--android-jar-pattern someotherdir/%/public/android.jar", |
| }, patterns) |
| } |
| |
| func TestDroidstubsSandbox(t *testing.T) { |
| ctx, _ := testJavaWithFS(t, ` |
| genrule { |
| name: "foo", |
| out: ["foo.txt"], |
| cmd: "touch $(out)", |
| } |
| |
| droidstubs { |
| name: "bar-stubs", |
| srcs: ["bar-doc/a.java"], |
| |
| args: "--reference $(location :foo)", |
| arg_files: [":foo"], |
| } |
| `, |
| map[string][]byte{ |
| "bar-doc/a.java": nil, |
| }) |
| |
| m := ctx.ModuleForTests("bar-stubs", "android_common") |
| metalava := m.Rule("metalava") |
| if g, w := metalava.Inputs.Strings(), []string{"bar-doc/a.java"}; !reflect.DeepEqual(w, g) { |
| t.Errorf("Expected inputs %q, got %q", w, g) |
| } |
| |
| manifest := android.RuleBuilderSboxProtoForTests(t, ctx, m.Output("metalava.sbox.textproto")) |
| if g, w := manifest.Commands[0].GetCommand(), "reference __SBOX_SANDBOX_DIR__/out/soong/.intermediates/foo/gen/foo.txt"; !strings.Contains(g, w) { |
| t.Errorf("Expected command to contain %q, got %q", w, g) |
| } |
| } |
| |
| func TestDroidstubsWithSystemModules(t *testing.T) { |
| ctx, _ := testJava(t, ` |
| droidstubs { |
| name: "stubs-source-system-modules", |
| srcs: [ |
| "bar-doc/a.java", |
| ], |
| sdk_version: "none", |
| system_modules: "source-system-modules", |
| } |
| |
| java_library { |
| name: "source-jar", |
| srcs: [ |
| "a.java", |
| ], |
| } |
| |
| java_system_modules { |
| name: "source-system-modules", |
| libs: ["source-jar"], |
| } |
| |
| droidstubs { |
| name: "stubs-prebuilt-system-modules", |
| srcs: [ |
| "bar-doc/a.java", |
| ], |
| sdk_version: "none", |
| system_modules: "prebuilt-system-modules", |
| } |
| |
| java_import { |
| name: "prebuilt-jar", |
| jars: ["a.jar"], |
| } |
| |
| java_system_modules_import { |
| name: "prebuilt-system-modules", |
| libs: ["prebuilt-jar"], |
| } |
| `) |
| |
| checkSystemModulesUseByDroidstubs(t, ctx, "stubs-source-system-modules", "source-jar.jar") |
| |
| checkSystemModulesUseByDroidstubs(t, ctx, "stubs-prebuilt-system-modules", "prebuilt-jar.jar") |
| } |
| |
| func checkSystemModulesUseByDroidstubs(t *testing.T, ctx *android.TestContext, moduleName string, systemJar string) { |
| metalavaRule := ctx.ModuleForTests(moduleName, "android_common").Rule("metalava") |
| var systemJars []string |
| for _, i := range metalavaRule.Implicits { |
| systemJars = append(systemJars, i.Base()) |
| } |
| if len(systemJars) < 1 || systemJars[0] != systemJar { |
| t.Errorf("inputs of %q must be []string{%q}, but was %#v.", moduleName, systemJar, systemJars) |
| } |
| } |
| |
| func TestDroidstubsWithSdkExtensions(t *testing.T) { |
| ctx, _ := testJavaWithFS(t, ` |
| droiddoc_exported_dir { |
| name: "sdk-dir", |
| path: "sdk", |
| } |
| |
| droidstubs { |
| name: "baz-stubs", |
| api_levels_annotations_dirs: ["sdk-dir"], |
| api_levels_annotations_enabled: true, |
| extensions_info_file: ":info-file", |
| } |
| |
| filegroup { |
| name: "info-file", |
| srcs: ["sdk/extensions/info.txt"], |
| } |
| `, |
| map[string][]byte{ |
| "sdk/extensions/1/public/some-mainline-module-stubs.jar": nil, |
| "sdk/extensions/info.txt": nil, |
| }) |
| m := ctx.ModuleForTests("baz-stubs", "android_common") |
| manifest := m.Output("metalava.sbox.textproto") |
| cmdline := String(android.RuleBuilderSboxProtoForTests(t, ctx, manifest).Commands[0].Command) |
| android.AssertStringDoesContain(t, "sdk-extensions-root present", cmdline, "--sdk-extensions-root sdk/extensions") |
| android.AssertStringDoesContain(t, "sdk-extensions-info present", cmdline, "--sdk-extensions-info sdk/extensions/info.txt") |
| } |
| |
| func TestDroidStubsApiContributionGeneration(t *testing.T) { |
| ctx, _ := testJavaWithFS(t, ` |
| droidstubs { |
| name: "foo", |
| srcs: ["A/a.java"], |
| api_surface: "public", |
| check_api: { |
| current: { |
| api_file: "A/current.txt", |
| removed_api_file: "A/removed.txt", |
| } |
| } |
| } |
| `, |
| map[string][]byte{ |
| "A/a.java": nil, |
| "A/current.txt": nil, |
| "A/removed.txt": nil, |
| }, |
| ) |
| |
| ctx.ModuleForTests("foo.api.contribution", "") |
| } |
| |
| func TestGeneratedApiContributionVisibilityTest(t *testing.T) { |
| library_bp := ` |
| java_api_library { |
| name: "bar", |
| api_surface: "public", |
| api_contributions: ["foo.api.contribution"], |
| stubs_type: "everything", |
| } |
| ` |
| ctx, _ := testJavaWithFS(t, ` |
| droidstubs { |
| name: "foo", |
| srcs: ["A/a.java"], |
| api_surface: "public", |
| check_api: { |
| current: { |
| api_file: "A/current.txt", |
| removed_api_file: "A/removed.txt", |
| } |
| }, |
| visibility: ["//a", "//b"], |
| } |
| `, |
| map[string][]byte{ |
| "a/a.java": nil, |
| "a/current.txt": nil, |
| "a/removed.txt": nil, |
| "b/Android.bp": []byte(library_bp), |
| }, |
| ) |
| |
| ctx.ModuleForTests("bar", "android_common") |
| } |
| |
| func TestAconfigDeclarations(t *testing.T) { |
| result := android.GroupFixturePreparers( |
| prepareForJavaTest, |
| android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { |
| }), |
| android.FixtureMergeMockFs(map[string][]byte{ |
| "a/A.java": nil, |
| "a/current.txt": nil, |
| "a/removed.txt": nil, |
| }), |
| ).RunTestWithBp(t, ` |
| aconfig_declarations { |
| name: "bar", |
| package: "com.example.package", |
| srcs: [ |
| "bar.aconfig", |
| ], |
| } |
| droidstubs { |
| name: "foo", |
| srcs: ["a/A.java"], |
| api_surface: "public", |
| check_api: { |
| current: { |
| api_file: "a/current.txt", |
| removed_api_file: "a/removed.txt", |
| } |
| }, |
| aconfig_declarations: [ |
| "bar", |
| ], |
| } |
| `) |
| |
| // Check that droidstubs depend on aconfig_declarations |
| android.AssertBoolEquals(t, "foo expected to depend on bar", |
| CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "bar"), true) |
| |
| m := result.ModuleForTests("foo", "android_common") |
| android.AssertStringDoesContain(t, "foo generates revert annotations file", |
| strings.Join(m.AllOutputs(), ""), "revert-annotations-exportable.txt") |
| |
| // revert-annotations.txt passed to exportable stubs generation metalava command |
| manifest := m.Output("metalava_exportable.sbox.textproto") |
| cmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest).Commands[0].Command) |
| android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "revert-annotations-exportable.txt") |
| |
| android.AssertStringDoesContain(t, "foo generates exportable stubs jar", |
| strings.Join(m.AllOutputs(), ""), "exportable/foo-stubs.srcjar") |
| } |
| |
| func TestReleaseExportRuntimeApis(t *testing.T) { |
| result := android.GroupFixturePreparers( |
| prepareForJavaTest, |
| android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { |
| variables.BuildFlags = map[string]string{ |
| "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", |
| } |
| variables.ExportRuntimeApis = proptools.BoolPtr(true) |
| }), |
| android.FixtureMergeMockFs(map[string][]byte{ |
| "a/A.java": nil, |
| "a/current.txt": nil, |
| "a/removed.txt": nil, |
| }), |
| ).RunTestWithBp(t, ` |
| aconfig_declarations { |
| name: "bar", |
| package: "com.example.package", |
| srcs: [ |
| "bar.aconfig", |
| ], |
| } |
| droidstubs { |
| name: "foo", |
| srcs: ["a/A.java"], |
| api_surface: "public", |
| check_api: { |
| current: { |
| api_file: "a/current.txt", |
| removed_api_file: "a/removed.txt", |
| } |
| }, |
| aconfig_declarations: [ |
| "bar", |
| ], |
| } |
| `) |
| |
| m := result.ModuleForTests("foo", "android_common") |
| |
| rule := m.Output("released-flagged-apis-exportable.txt") |
| exposeWritableApisFilter := "--filter='state:ENABLED+permission:READ_ONLY' --filter='permission:READ_WRITE'" |
| android.AssertStringEquals(t, "Filter argument expected to contain READ_WRITE permissions", exposeWritableApisFilter, rule.Args["filter_args"]) |
| } |