Files
build_soong/java/droidstubs_test.go
Jihoon Kang 6592e87dbf Add aconfig_declarations property to droidstubs and java_sdk_library
In consideration of the incremental build performance, this change let
droidstubs and java_sdk_library (which generates droidstubs per api
scope) modules to specify `aconfig_declaration` modules where the
dependent flags are defined in via the "aconfig_declarations" property,
opposed to passing uniform "all_aconfig_declaration"-generated flag
arguments to metalava.

When "aconfig_declarations" property is defined for java_sdk_library
modules, the property is passed to the generated droidstubs modules.

When "aconfig_declarations" property is defined for droidstubs modules,
the all aconfig_declaration modules listed in the property are listed as
deps, all cache protobuf files are gathered and metalava-consumable
flags are generated in "revert-annotations.txt".

Although this change introduces scalable implementation to easily
support generation of the "runtime" stubs corresponding flags, actual
support of the runtime flags/stubs will be done in future changes. This
change mostly focuses on the generation of the "exportable" flags.

Utilization of the generated "exportable" flags will be done in future
changes.

Test: go test ./java
Bug: 315485740
Change-Id: I37becd1b9dd9069d7ac4abed130906df30b3fdf4
2024-01-08 07:55:49 +00:00

441 lines
12 KiB
Go

// 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"],
}
`
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 TestDroidstubsHideFlaggedApi(t *testing.T) {
result := android.GroupFixturePreparers(
prepareForJavaTest,
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.NextReleaseHideFlaggedApi = proptools.BoolPtr(true)
variables.Release_expose_flagged_api = proptools.BoolPtr(false)
}),
android.FixtureMergeMockFs(map[string][]byte{
"a/A.java": nil,
"a/current.txt": nil,
"a/removed.txt": nil,
}),
).RunTestWithBp(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",
}
},
}
`)
m := result.ModuleForTests("foo", "android_common")
manifest := m.Output("metalava.sbox.textproto")
cmdline := String(android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest).Commands[0].Command)
android.AssertStringDoesContain(t, "flagged api hide command not included", cmdline, "--revert-annotation android.annotation.FlaggedApi")
}
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")
}