| // Copyright 2017 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" |
| "path/filepath" |
| "reflect" |
| "sort" |
| "strings" |
| "testing" |
| |
| "github.com/google/blueprint/proptools" |
| |
| "android/soong/android" |
| "android/soong/cc" |
| "android/soong/dexpreopt" |
| "android/soong/genrule" |
| ) |
| |
| // testApp runs tests using the prepareForJavaTest |
| // |
| // See testJava for an explanation as to how to stop using this deprecated method. |
| // |
| // deprecated |
| func testApp(t *testing.T, bp string) *android.TestContext { |
| t.Helper() |
| result := prepareForJavaTest.RunTestWithBp(t, bp) |
| return result.TestContext |
| } |
| |
| func TestApp(t *testing.T) { |
| resourceFiles := []string{ |
| "res/layout/layout.xml", |
| "res/values/strings.xml", |
| "res/values-en-rUS/strings.xml", |
| } |
| |
| compiledResourceFiles := []string{ |
| "aapt2/res/layout_layout.xml.flat", |
| "aapt2/res/values_strings.arsc.flat", |
| "aapt2/res/values-en-rUS_strings.arsc.flat", |
| } |
| |
| for _, moduleType := range []string{"android_app", "android_library"} { |
| t.Run(moduleType, func(t *testing.T) { |
| result := android.GroupFixturePreparers( |
| prepareForJavaTest, |
| android.FixtureModifyMockFS(func(fs android.MockFS) { |
| for _, file := range resourceFiles { |
| fs[file] = nil |
| } |
| }), |
| ).RunTestWithBp(t, moduleType+` { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "current" |
| } |
| `) |
| |
| foo := result.ModuleForTests("foo", "android_common") |
| |
| var expectedLinkImplicits []string |
| |
| manifestFixer := foo.Output("manifest_fixer/AndroidManifest.xml") |
| expectedLinkImplicits = append(expectedLinkImplicits, manifestFixer.Output.String()) |
| |
| frameworkRes := result.ModuleForTests("framework-res", "android_common") |
| expectedLinkImplicits = append(expectedLinkImplicits, |
| frameworkRes.Output("package-res.apk").Output.String()) |
| |
| // Test the mapping from input files to compiled output file names |
| compile := foo.Output(compiledResourceFiles[0]) |
| android.AssertDeepEquals(t, "aapt2 compile inputs", resourceFiles, compile.Inputs.Strings()) |
| |
| compiledResourceOutputs := compile.Outputs.Strings() |
| sort.Strings(compiledResourceOutputs) |
| |
| expectedLinkImplicits = append(expectedLinkImplicits, compiledResourceOutputs...) |
| |
| list := foo.Output("aapt2/res.list") |
| expectedLinkImplicits = append(expectedLinkImplicits, list.Output.String()) |
| |
| // Check that the link rule uses |
| res := result.ModuleForTests("foo", "android_common").Output("package-res.apk") |
| android.AssertDeepEquals(t, "aapt2 link implicits", expectedLinkImplicits, res.Implicits.Strings()) |
| }) |
| } |
| } |
| |
| func TestAppSplits(t *testing.T) { |
| ctx := testApp(t, ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| package_splits: ["v4", "v7,hdpi"], |
| sdk_version: "current" |
| }`) |
| |
| foo := ctx.ModuleForTests("foo", "android_common") |
| |
| expectedOutputs := []string{ |
| "out/soong/.intermediates/foo/android_common/foo.apk", |
| "out/soong/.intermediates/foo/android_common/foo_v4.apk", |
| "out/soong/.intermediates/foo/android_common/foo_v7_hdpi.apk", |
| } |
| for _, expectedOutput := range expectedOutputs { |
| foo.Output(expectedOutput) |
| } |
| |
| outputFiles, err := foo.Module().(*AndroidApp).OutputFiles("") |
| if err != nil { |
| t.Fatal(err) |
| } |
| android.AssertPathsRelativeToTopEquals(t, `OutputFiles("")`, expectedOutputs, outputFiles) |
| } |
| |
| func TestPlatformAPIs(t *testing.T) { |
| testJava(t, ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| platform_apis: true, |
| } |
| `) |
| |
| testJava(t, ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "current", |
| } |
| `) |
| |
| testJavaError(t, "This module has conflicting settings. sdk_version is empty, which means that this module is build against platform APIs. However platform_apis is not set to true", ` |
| android_app { |
| name: "bar", |
| srcs: ["b.java"], |
| } |
| `) |
| |
| testJavaError(t, "This module has conflicting settings. sdk_version is not empty, which means this module cannot use platform APIs. However platform_apis is set to true.", ` |
| android_app { |
| name: "bar", |
| srcs: ["b.java"], |
| sdk_version: "system_current", |
| platform_apis: true, |
| } |
| `) |
| } |
| |
| func TestAndroidAppLinkType(t *testing.T) { |
| testJava(t, ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| libs: ["bar"], |
| static_libs: ["baz"], |
| platform_apis: true, |
| } |
| |
| java_library { |
| name: "bar", |
| sdk_version: "current", |
| srcs: ["b.java"], |
| } |
| |
| android_library { |
| name: "baz", |
| sdk_version: "system_current", |
| srcs: ["c.java"], |
| } |
| `) |
| |
| testJavaError(t, "consider adjusting sdk_version: OR platform_apis:", ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| libs: ["bar"], |
| sdk_version: "current", |
| static_libs: ["baz"], |
| } |
| |
| java_library { |
| name: "bar", |
| sdk_version: "current", |
| srcs: ["b.java"], |
| } |
| |
| android_library { |
| name: "baz", |
| sdk_version: "system_current", |
| srcs: ["c.java"], |
| } |
| `) |
| |
| testJava(t, ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| libs: ["bar"], |
| sdk_version: "system_current", |
| static_libs: ["baz"], |
| } |
| |
| java_library { |
| name: "bar", |
| sdk_version: "current", |
| srcs: ["b.java"], |
| } |
| |
| android_library { |
| name: "baz", |
| sdk_version: "system_current", |
| srcs: ["c.java"], |
| } |
| `) |
| |
| testJavaError(t, "consider adjusting sdk_version: OR platform_apis:", ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| libs: ["bar"], |
| sdk_version: "system_current", |
| static_libs: ["baz"], |
| } |
| |
| java_library { |
| name: "bar", |
| sdk_version: "current", |
| srcs: ["b.java"], |
| } |
| |
| android_library { |
| name: "baz", |
| srcs: ["c.java"], |
| } |
| `) |
| } |
| |
| func TestUpdatableApps(t *testing.T) { |
| testCases := []struct { |
| name string |
| bp string |
| expectedError string |
| }{ |
| { |
| name: "Stable public SDK", |
| bp: `android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "29", |
| min_sdk_version: "29", |
| updatable: true, |
| }`, |
| }, |
| { |
| name: "Stable system SDK", |
| bp: `android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "system_29", |
| min_sdk_version: "29", |
| updatable: true, |
| }`, |
| }, |
| { |
| name: "Current public SDK", |
| bp: `android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "current", |
| min_sdk_version: "29", |
| updatable: true, |
| }`, |
| }, |
| { |
| name: "Current system SDK", |
| bp: `android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "system_current", |
| min_sdk_version: "29", |
| updatable: true, |
| }`, |
| }, |
| { |
| name: "Current module SDK", |
| bp: `android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "module_current", |
| min_sdk_version: "29", |
| updatable: true, |
| }`, |
| }, |
| { |
| name: "Current core SDK", |
| bp: `android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "core_current", |
| min_sdk_version: "29", |
| updatable: true, |
| }`, |
| }, |
| { |
| name: "No Platform APIs", |
| bp: `android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| platform_apis: true, |
| min_sdk_version: "29", |
| updatable: true, |
| }`, |
| expectedError: "Updatable apps must use stable SDKs", |
| }, |
| { |
| name: "No Core Platform APIs", |
| bp: `android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "core_platform", |
| min_sdk_version: "29", |
| updatable: true, |
| }`, |
| expectedError: "Updatable apps must use stable SDKs", |
| }, |
| { |
| name: "No unspecified APIs", |
| bp: `android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| updatable: true, |
| min_sdk_version: "29", |
| }`, |
| expectedError: "Updatable apps must use stable SDK", |
| }, |
| { |
| name: "Must specify min_sdk_version", |
| bp: `android_app { |
| name: "app_without_min_sdk_version", |
| srcs: ["a.java"], |
| sdk_version: "29", |
| updatable: true, |
| }`, |
| expectedError: "updatable apps must set min_sdk_version.", |
| }, |
| } |
| |
| for _, test := range testCases { |
| t.Run(test.name, func(t *testing.T) { |
| errorHandler := android.FixtureExpectsNoErrors |
| if test.expectedError != "" { |
| errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(test.expectedError) |
| } |
| android.GroupFixturePreparers( |
| prepareForJavaTest, FixtureWithPrebuiltApis(map[string][]string{ |
| "29": {"foo"}, |
| })). |
| ExtendWithErrorHandler(errorHandler).RunTestWithBp(t, test.bp) |
| }) |
| } |
| } |
| |
| func TestUpdatableApps_TransitiveDepsShouldSetMinSdkVersion(t *testing.T) { |
| testJavaError(t, `module "bar".*: should support min_sdk_version\(29\)`, cc.GatherRequiredDepsForTest(android.Android)+` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| updatable: true, |
| sdk_version: "current", |
| min_sdk_version: "29", |
| static_libs: ["bar"], |
| } |
| |
| java_library { |
| name: "bar", |
| sdk_version: "current", |
| } |
| `) |
| } |
| |
| func TestUpdatableApps_JniLibsShouldShouldSupportMinSdkVersion(t *testing.T) { |
| testJava(t, cc.GatherRequiredDepsForTest(android.Android)+` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| updatable: true, |
| sdk_version: "current", |
| min_sdk_version: "current", |
| jni_libs: ["libjni"], |
| } |
| |
| cc_library { |
| name: "libjni", |
| stl: "none", |
| system_shared_libs: [], |
| sdk_version: "current", |
| } |
| `) |
| } |
| |
| func TestUpdatableApps_JniLibShouldBeBuiltAgainstMinSdkVersion(t *testing.T) { |
| bp := cc.GatherRequiredDepsForTest(android.Android) + ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| updatable: true, |
| sdk_version: "current", |
| min_sdk_version: "29", |
| jni_libs: ["libjni"], |
| } |
| |
| cc_library { |
| name: "libjni", |
| stl: "none", |
| system_shared_libs: [], |
| sdk_version: "29", |
| } |
| ` |
| fs := map[string][]byte{ |
| "prebuilts/ndk/current/platforms/android-29/arch-arm64/usr/lib/crtbegin_so.o": nil, |
| "prebuilts/ndk/current/platforms/android-29/arch-arm64/usr/lib/crtend_so.o": nil, |
| "prebuilts/ndk/current/platforms/android-29/arch-arm/usr/lib/crtbegin_so.o": nil, |
| "prebuilts/ndk/current/platforms/android-29/arch-arm/usr/lib/crtend_so.o": nil, |
| } |
| |
| ctx, _ := testJavaWithFS(t, bp, fs) |
| |
| inputs := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_sdk_shared").Description("link").Implicits |
| var crtbeginFound, crtendFound bool |
| expectedCrtBegin := ctx.ModuleForTests("crtbegin_so", |
| "android_arm64_armv8-a_sdk_29").Rule("partialLd").Output |
| expectedCrtEnd := ctx.ModuleForTests("crtend_so", |
| "android_arm64_armv8-a_sdk_29").Rule("partialLd").Output |
| implicits := []string{} |
| for _, input := range inputs { |
| implicits = append(implicits, input.String()) |
| if strings.HasSuffix(input.String(), expectedCrtBegin.String()) { |
| crtbeginFound = true |
| } else if strings.HasSuffix(input.String(), expectedCrtEnd.String()) { |
| crtendFound = true |
| } |
| } |
| if !crtbeginFound { |
| t.Error(fmt.Sprintf( |
| "expected implicit with suffix %q, have the following implicits:\n%s", |
| expectedCrtBegin, strings.Join(implicits, "\n"))) |
| } |
| if !crtendFound { |
| t.Error(fmt.Sprintf( |
| "expected implicit with suffix %q, have the following implicits:\n%s", |
| expectedCrtEnd, strings.Join(implicits, "\n"))) |
| } |
| } |
| |
| func TestUpdatableApps_ErrorIfJniLibDoesntSupportMinSdkVersion(t *testing.T) { |
| bp := cc.GatherRequiredDepsForTest(android.Android) + ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| updatable: true, |
| sdk_version: "current", |
| min_sdk_version: "29", // this APK should support 29 |
| jni_libs: ["libjni"], |
| } |
| |
| cc_library { |
| name: "libjni", |
| stl: "none", |
| sdk_version: "current", |
| } |
| ` |
| testJavaError(t, `"libjni" .*: sdk_version\(current\) is higher than min_sdk_version\(29\)`, bp) |
| } |
| |
| func TestUpdatableApps_ErrorIfDepSdkVersionIsHigher(t *testing.T) { |
| bp := cc.GatherRequiredDepsForTest(android.Android) + ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| updatable: true, |
| sdk_version: "current", |
| min_sdk_version: "29", // this APK should support 29 |
| jni_libs: ["libjni"], |
| } |
| |
| cc_library { |
| name: "libjni", |
| stl: "none", |
| shared_libs: ["libbar"], |
| system_shared_libs: [], |
| sdk_version: "27", |
| } |
| |
| cc_library { |
| name: "libbar", |
| stl: "none", |
| system_shared_libs: [], |
| sdk_version: "current", |
| } |
| ` |
| testJavaError(t, `"libjni" .*: links "libbar" built against newer API version "current"`, bp) |
| } |
| |
| func TestResourceDirs(t *testing.T) { |
| testCases := []struct { |
| name string |
| prop string |
| resources []string |
| }{ |
| { |
| name: "no resource_dirs", |
| prop: "", |
| resources: []string{"res/res/values/strings.xml"}, |
| }, |
| { |
| name: "resource_dirs", |
| prop: `resource_dirs: ["res"]`, |
| resources: []string{"res/res/values/strings.xml"}, |
| }, |
| { |
| name: "empty resource_dirs", |
| prop: `resource_dirs: []`, |
| resources: nil, |
| }, |
| } |
| |
| fs := android.MockFS{ |
| "res/res/values/strings.xml": nil, |
| } |
| |
| bp := ` |
| android_app { |
| name: "foo", |
| sdk_version: "current", |
| %s |
| } |
| ` |
| |
| for _, testCase := range testCases { |
| t.Run(testCase.name, func(t *testing.T) { |
| result := android.GroupFixturePreparers( |
| PrepareForTestWithJavaDefaultModules, |
| PrepareForTestWithOverlayBuildComponents, |
| fs.AddToFixture(), |
| ).RunTestWithBp(t, fmt.Sprintf(bp, testCase.prop)) |
| |
| module := result.ModuleForTests("foo", "android_common") |
| resourceList := module.MaybeOutput("aapt2/res.list") |
| |
| var resources []string |
| if resourceList.Rule != nil { |
| for _, compiledResource := range resourceList.Inputs.Strings() { |
| resources = append(resources, module.Output(compiledResource).Inputs.Strings()...) |
| } |
| } |
| |
| android.AssertDeepEquals(t, "resource files", testCase.resources, resources) |
| }) |
| } |
| } |
| |
| func TestLibraryAssets(t *testing.T) { |
| bp := ` |
| android_app { |
| name: "foo", |
| sdk_version: "current", |
| static_libs: ["lib1", "lib2", "lib3"], |
| } |
| |
| android_library { |
| name: "lib1", |
| sdk_version: "current", |
| asset_dirs: ["assets_a"], |
| } |
| |
| android_library { |
| name: "lib2", |
| sdk_version: "current", |
| } |
| |
| android_library { |
| name: "lib3", |
| sdk_version: "current", |
| static_libs: ["lib4"], |
| } |
| |
| android_library { |
| name: "lib4", |
| sdk_version: "current", |
| asset_dirs: ["assets_b"], |
| } |
| ` |
| |
| testCases := []struct { |
| name string |
| assetFlag string |
| assetPackages []string |
| }{ |
| { |
| name: "foo", |
| // lib1 has its own asset. lib3 doesn't have any, but provides lib4's transitively. |
| assetPackages: []string{ |
| "out/soong/.intermediates/foo/android_common/aapt2/package-res.apk", |
| "out/soong/.intermediates/lib1/android_common/assets.zip", |
| "out/soong/.intermediates/lib3/android_common/assets.zip", |
| }, |
| }, |
| { |
| name: "lib1", |
| assetFlag: "-A assets_a", |
| }, |
| { |
| name: "lib2", |
| }, |
| { |
| name: "lib3", |
| assetPackages: []string{ |
| "out/soong/.intermediates/lib3/android_common/aapt2/package-res.apk", |
| "out/soong/.intermediates/lib4/android_common/assets.zip", |
| }, |
| }, |
| { |
| name: "lib4", |
| assetFlag: "-A assets_b", |
| }, |
| } |
| ctx := testApp(t, bp) |
| |
| for _, test := range testCases { |
| t.Run(test.name, func(t *testing.T) { |
| m := ctx.ModuleForTests(test.name, "android_common") |
| |
| // Check asset flag in aapt2 link flags |
| var aapt2link android.TestingBuildParams |
| if len(test.assetPackages) > 0 { |
| aapt2link = m.Output("aapt2/package-res.apk") |
| } else { |
| aapt2link = m.Output("package-res.apk") |
| } |
| aapt2link = aapt2link |
| aapt2Flags := aapt2link.Args["flags"] |
| if test.assetFlag != "" { |
| android.AssertStringDoesContain(t, "asset flag", aapt2Flags, test.assetFlag) |
| } else { |
| android.AssertStringDoesNotContain(t, "aapt2 link flags", aapt2Flags, " -A ") |
| } |
| |
| // Check asset merge rule. |
| if len(test.assetPackages) > 0 { |
| mergeAssets := m.Output("package-res.apk") |
| android.AssertPathsRelativeToTopEquals(t, "mergeAssets inputs", test.assetPackages, mergeAssets.Inputs) |
| } |
| }) |
| } |
| } |
| |
| func TestAppJavaResources(t *testing.T) { |
| bp := ` |
| android_app { |
| name: "foo", |
| sdk_version: "current", |
| java_resources: ["resources/a"], |
| srcs: ["a.java"], |
| } |
| |
| android_app { |
| name: "bar", |
| sdk_version: "current", |
| java_resources: ["resources/a"], |
| } |
| ` |
| |
| ctx := testApp(t, bp) |
| |
| foo := ctx.ModuleForTests("foo", "android_common") |
| fooResources := foo.Output("res/foo.jar") |
| fooDexJar := foo.Output("dex-withres/foo.jar") |
| fooDexJarAligned := foo.Output("dex-withres-aligned/foo.jar") |
| fooApk := foo.Rule("combineApk") |
| |
| if g, w := fooDexJar.Inputs.Strings(), fooResources.Output.String(); !android.InList(w, g) { |
| t.Errorf("expected resource jar %q in foo dex jar inputs %q", w, g) |
| } |
| |
| if g, w := fooDexJarAligned.Input.String(), fooDexJar.Output.String(); g != w { |
| t.Errorf("expected dex jar %q in foo aligned dex jar inputs %q", w, g) |
| } |
| |
| if g, w := fooApk.Inputs.Strings(), fooDexJarAligned.Output.String(); !android.InList(w, g) { |
| t.Errorf("expected aligned dex jar %q in foo apk inputs %q", w, g) |
| } |
| |
| bar := ctx.ModuleForTests("bar", "android_common") |
| barResources := bar.Output("res/bar.jar") |
| barApk := bar.Rule("combineApk") |
| |
| if g, w := barApk.Inputs.Strings(), barResources.Output.String(); !android.InList(w, g) { |
| t.Errorf("expected resources jar %q in bar apk inputs %q", w, g) |
| } |
| } |
| |
| func TestAndroidResources(t *testing.T) { |
| testCases := []struct { |
| name string |
| enforceRROTargets []string |
| enforceRROExcludedOverlays []string |
| resourceFiles map[string][]string |
| overlayFiles map[string][]string |
| rroDirs map[string][]string |
| }{ |
| { |
| name: "no RRO", |
| enforceRROTargets: nil, |
| enforceRROExcludedOverlays: nil, |
| resourceFiles: map[string][]string{ |
| "foo": nil, |
| "bar": {"bar/res/res/values/strings.xml"}, |
| "lib": nil, |
| "lib2": {"lib2/res/res/values/strings.xml"}, |
| }, |
| overlayFiles: map[string][]string{ |
| "foo": { |
| "out/soong/.intermediates/lib2/android_common/package-res.apk", |
| "out/soong/.intermediates/lib/android_common/package-res.apk", |
| "out/soong/.intermediates/lib3/android_common/package-res.apk", |
| "foo/res/res/values/strings.xml", |
| "device/vendor/blah/static_overlay/foo/res/values/strings.xml", |
| "device/vendor/blah/overlay/foo/res/values/strings.xml", |
| "product/vendor/blah/overlay/foo/res/values/strings.xml", |
| }, |
| "bar": { |
| "device/vendor/blah/static_overlay/bar/res/values/strings.xml", |
| "device/vendor/blah/overlay/bar/res/values/strings.xml", |
| }, |
| "lib": { |
| "out/soong/.intermediates/lib2/android_common/package-res.apk", |
| "lib/res/res/values/strings.xml", |
| "device/vendor/blah/overlay/lib/res/values/strings.xml", |
| }, |
| }, |
| rroDirs: map[string][]string{ |
| "foo": nil, |
| "bar": nil, |
| }, |
| }, |
| { |
| name: "enforce RRO on foo", |
| enforceRROTargets: []string{"foo"}, |
| enforceRROExcludedOverlays: []string{"device/vendor/blah/static_overlay"}, |
| resourceFiles: map[string][]string{ |
| "foo": nil, |
| "bar": {"bar/res/res/values/strings.xml"}, |
| "lib": nil, |
| "lib2": {"lib2/res/res/values/strings.xml"}, |
| }, |
| overlayFiles: map[string][]string{ |
| "foo": { |
| "out/soong/.intermediates/lib2/android_common/package-res.apk", |
| "out/soong/.intermediates/lib/android_common/package-res.apk", |
| "out/soong/.intermediates/lib3/android_common/package-res.apk", |
| "foo/res/res/values/strings.xml", |
| "device/vendor/blah/static_overlay/foo/res/values/strings.xml", |
| }, |
| "bar": { |
| "device/vendor/blah/static_overlay/bar/res/values/strings.xml", |
| "device/vendor/blah/overlay/bar/res/values/strings.xml", |
| }, |
| "lib": { |
| "out/soong/.intermediates/lib2/android_common/package-res.apk", |
| "lib/res/res/values/strings.xml", |
| }, |
| }, |
| |
| rroDirs: map[string][]string{ |
| "foo": { |
| "device:device/vendor/blah/overlay/foo/res", |
| "product:product/vendor/blah/overlay/foo/res", |
| "device:device/vendor/blah/overlay/lib/res", |
| }, |
| "bar": nil, |
| "lib": {"device:device/vendor/blah/overlay/lib/res"}, |
| }, |
| }, |
| { |
| name: "enforce RRO on all", |
| enforceRROTargets: []string{"*"}, |
| enforceRROExcludedOverlays: []string{ |
| // Excluding specific apps/res directories also allowed. |
| "device/vendor/blah/static_overlay/foo", |
| "device/vendor/blah/static_overlay/bar/res", |
| }, |
| resourceFiles: map[string][]string{ |
| "foo": nil, |
| "bar": {"bar/res/res/values/strings.xml"}, |
| "lib": nil, |
| "lib2": {"lib2/res/res/values/strings.xml"}, |
| }, |
| overlayFiles: map[string][]string{ |
| "foo": { |
| "out/soong/.intermediates/lib2/android_common/package-res.apk", |
| "out/soong/.intermediates/lib/android_common/package-res.apk", |
| "out/soong/.intermediates/lib3/android_common/package-res.apk", |
| "foo/res/res/values/strings.xml", |
| "device/vendor/blah/static_overlay/foo/res/values/strings.xml", |
| }, |
| "bar": {"device/vendor/blah/static_overlay/bar/res/values/strings.xml"}, |
| "lib": { |
| "out/soong/.intermediates/lib2/android_common/package-res.apk", |
| "lib/res/res/values/strings.xml", |
| }, |
| }, |
| rroDirs: map[string][]string{ |
| "foo": { |
| "device:device/vendor/blah/overlay/foo/res", |
| "product:product/vendor/blah/overlay/foo/res", |
| // Lib dep comes after the direct deps |
| "device:device/vendor/blah/overlay/lib/res", |
| }, |
| "bar": {"device:device/vendor/blah/overlay/bar/res"}, |
| "lib": {"device:device/vendor/blah/overlay/lib/res"}, |
| }, |
| }, |
| } |
| |
| deviceResourceOverlays := []string{ |
| "device/vendor/blah/overlay", |
| "device/vendor/blah/overlay2", |
| "device/vendor/blah/static_overlay", |
| } |
| |
| productResourceOverlays := []string{ |
| "product/vendor/blah/overlay", |
| } |
| |
| fs := android.MockFS{ |
| "foo/res/res/values/strings.xml": nil, |
| "bar/res/res/values/strings.xml": nil, |
| "lib/res/res/values/strings.xml": nil, |
| "lib2/res/res/values/strings.xml": nil, |
| "device/vendor/blah/overlay/foo/res/values/strings.xml": nil, |
| "device/vendor/blah/overlay/bar/res/values/strings.xml": nil, |
| "device/vendor/blah/overlay/lib/res/values/strings.xml": nil, |
| "device/vendor/blah/static_overlay/foo/res/values/strings.xml": nil, |
| "device/vendor/blah/static_overlay/bar/res/values/strings.xml": nil, |
| "device/vendor/blah/overlay2/res/values/strings.xml": nil, |
| "product/vendor/blah/overlay/foo/res/values/strings.xml": nil, |
| } |
| |
| bp := ` |
| android_app { |
| name: "foo", |
| sdk_version: "current", |
| resource_dirs: ["foo/res"], |
| static_libs: ["lib", "lib3"], |
| } |
| |
| android_app { |
| name: "bar", |
| sdk_version: "current", |
| resource_dirs: ["bar/res"], |
| } |
| |
| android_library { |
| name: "lib", |
| sdk_version: "current", |
| resource_dirs: ["lib/res"], |
| static_libs: ["lib2"], |
| } |
| |
| android_library { |
| name: "lib2", |
| sdk_version: "current", |
| resource_dirs: ["lib2/res"], |
| } |
| |
| // This library has the same resources as lib (should not lead to dupe RROs) |
| android_library { |
| name: "lib3", |
| sdk_version: "current", |
| resource_dirs: ["lib/res"] |
| } |
| ` |
| |
| for _, testCase := range testCases { |
| t.Run(testCase.name, func(t *testing.T) { |
| result := android.GroupFixturePreparers( |
| PrepareForTestWithJavaDefaultModules, |
| PrepareForTestWithOverlayBuildComponents, |
| fs.AddToFixture(), |
| android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { |
| variables.DeviceResourceOverlays = deviceResourceOverlays |
| variables.ProductResourceOverlays = productResourceOverlays |
| if testCase.enforceRROTargets != nil { |
| variables.EnforceRROTargets = testCase.enforceRROTargets |
| } |
| if testCase.enforceRROExcludedOverlays != nil { |
| variables.EnforceRROExcludedOverlays = testCase.enforceRROExcludedOverlays |
| } |
| }), |
| ).RunTestWithBp(t, bp) |
| |
| resourceListToFiles := func(module android.TestingModule, list []string) (files []string) { |
| for _, o := range list { |
| res := module.MaybeOutput(o) |
| if res.Rule != nil { |
| // If the overlay is compiled as part of this module (i.e. a .arsc.flat file), |
| // verify the inputs to the .arsc.flat rule. |
| files = append(files, res.Inputs.Strings()...) |
| } else { |
| // Otherwise, verify the full path to the output of the other module |
| files = append(files, o) |
| } |
| } |
| return files |
| } |
| |
| getResources := func(moduleName string) (resourceFiles, overlayFiles, rroDirs []string) { |
| module := result.ModuleForTests(moduleName, "android_common") |
| resourceList := module.MaybeOutput("aapt2/res.list") |
| if resourceList.Rule != nil { |
| resourceFiles = resourceListToFiles(module, android.PathsRelativeToTop(resourceList.Inputs)) |
| } |
| overlayList := module.MaybeOutput("aapt2/overlay.list") |
| if overlayList.Rule != nil { |
| overlayFiles = resourceListToFiles(module, android.PathsRelativeToTop(overlayList.Inputs)) |
| } |
| |
| for _, d := range module.Module().(AndroidLibraryDependency).ExportedRRODirs() { |
| var prefix string |
| if d.overlayType == device { |
| prefix = "device:" |
| } else if d.overlayType == product { |
| prefix = "product:" |
| } else { |
| t.Fatalf("Unexpected overlayType %d", d.overlayType) |
| } |
| rroDirs = append(rroDirs, prefix+android.PathRelativeToTop(d.path)) |
| } |
| |
| return resourceFiles, overlayFiles, rroDirs |
| } |
| |
| modules := []string{"foo", "bar", "lib", "lib2"} |
| for _, module := range modules { |
| resourceFiles, overlayFiles, rroDirs := getResources(module) |
| |
| if !reflect.DeepEqual(resourceFiles, testCase.resourceFiles[module]) { |
| t.Errorf("expected %s resource files:\n %#v\n got:\n %#v", |
| module, testCase.resourceFiles[module], resourceFiles) |
| } |
| if !reflect.DeepEqual(overlayFiles, testCase.overlayFiles[module]) { |
| t.Errorf("expected %s overlay files:\n %#v\n got:\n %#v", |
| module, testCase.overlayFiles[module], overlayFiles) |
| } |
| if !reflect.DeepEqual(rroDirs, testCase.rroDirs[module]) { |
| t.Errorf("expected %s rroDirs: %#v\n got:\n %#v", |
| module, testCase.rroDirs[module], rroDirs) |
| } |
| } |
| }) |
| } |
| } |
| |
| func checkSdkVersion(t *testing.T, result *android.TestResult, expectedSdkVersion string) { |
| foo := result.ModuleForTests("foo", "android_common") |
| link := foo.Output("package-res.apk") |
| linkFlags := strings.Split(link.Args["flags"], " ") |
| min := android.IndexList("--min-sdk-version", linkFlags) |
| target := android.IndexList("--target-sdk-version", linkFlags) |
| |
| if min == -1 || target == -1 || min == len(linkFlags)-1 || target == len(linkFlags)-1 { |
| t.Fatalf("missing --min-sdk-version or --target-sdk-version in link flags: %q", linkFlags) |
| } |
| |
| gotMinSdkVersion := linkFlags[min+1] |
| gotTargetSdkVersion := linkFlags[target+1] |
| |
| android.AssertStringEquals(t, "incorrect --min-sdk-version", expectedSdkVersion, gotMinSdkVersion) |
| |
| android.AssertStringEquals(t, "incorrect --target-sdk-version", expectedSdkVersion, gotTargetSdkVersion) |
| } |
| |
| func TestAppSdkVersion(t *testing.T) { |
| testCases := []struct { |
| name string |
| sdkVersion string |
| platformSdkInt int |
| platformSdkCodename string |
| platformSdkFinal bool |
| expectedMinSdkVersion string |
| platformApis bool |
| activeCodenames []string |
| }{ |
| { |
| name: "current final SDK", |
| sdkVersion: "current", |
| platformSdkInt: 27, |
| platformSdkCodename: "REL", |
| platformSdkFinal: true, |
| expectedMinSdkVersion: "27", |
| }, |
| { |
| name: "current non-final SDK", |
| sdkVersion: "current", |
| platformSdkInt: 27, |
| platformSdkCodename: "OMR1", |
| platformSdkFinal: false, |
| expectedMinSdkVersion: "OMR1", |
| activeCodenames: []string{"OMR1"}, |
| }, |
| { |
| name: "default final SDK", |
| sdkVersion: "", |
| platformApis: true, |
| platformSdkInt: 27, |
| platformSdkCodename: "REL", |
| platformSdkFinal: true, |
| expectedMinSdkVersion: "27", |
| }, |
| { |
| name: "default non-final SDK", |
| sdkVersion: "", |
| platformApis: true, |
| platformSdkInt: 27, |
| platformSdkCodename: "OMR1", |
| platformSdkFinal: false, |
| expectedMinSdkVersion: "OMR1", |
| activeCodenames: []string{"OMR1"}, |
| }, |
| { |
| name: "14", |
| sdkVersion: "14", |
| expectedMinSdkVersion: "14", |
| platformSdkCodename: "S", |
| activeCodenames: []string{"S"}, |
| }, |
| } |
| |
| for _, moduleType := range []string{"android_app", "android_library"} { |
| for _, test := range testCases { |
| t.Run(moduleType+" "+test.name, func(t *testing.T) { |
| platformApiProp := "" |
| if test.platformApis { |
| platformApiProp = "platform_apis: true," |
| } |
| bp := fmt.Sprintf(`%s { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "%s", |
| %s |
| }`, moduleType, test.sdkVersion, platformApiProp) |
| |
| result := android.GroupFixturePreparers( |
| prepareForJavaTest, |
| android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { |
| variables.Platform_sdk_version = &test.platformSdkInt |
| variables.Platform_sdk_codename = &test.platformSdkCodename |
| variables.Platform_version_active_codenames = test.activeCodenames |
| variables.Platform_sdk_final = &test.platformSdkFinal |
| }), |
| FixtureWithPrebuiltApis(map[string][]string{ |
| "14": {"foo"}, |
| }), |
| ).RunTestWithBp(t, bp) |
| |
| checkSdkVersion(t, result, test.expectedMinSdkVersion) |
| }) |
| } |
| } |
| } |
| |
| func TestVendorAppSdkVersion(t *testing.T) { |
| testCases := []struct { |
| name string |
| sdkVersion string |
| platformSdkInt int |
| platformSdkCodename string |
| platformSdkFinal bool |
| deviceCurrentApiLevelForVendorModules string |
| expectedMinSdkVersion string |
| }{ |
| { |
| name: "current final SDK", |
| sdkVersion: "current", |
| platformSdkInt: 29, |
| platformSdkCodename: "REL", |
| platformSdkFinal: true, |
| deviceCurrentApiLevelForVendorModules: "29", |
| expectedMinSdkVersion: "29", |
| }, |
| { |
| name: "current final SDK", |
| sdkVersion: "current", |
| platformSdkInt: 29, |
| platformSdkCodename: "REL", |
| platformSdkFinal: true, |
| deviceCurrentApiLevelForVendorModules: "28", |
| expectedMinSdkVersion: "28", |
| }, |
| { |
| name: "current final SDK", |
| sdkVersion: "current", |
| platformSdkInt: 29, |
| platformSdkCodename: "Q", |
| platformSdkFinal: false, |
| deviceCurrentApiLevelForVendorModules: "28", |
| expectedMinSdkVersion: "28", |
| }, |
| } |
| |
| for _, moduleType := range []string{"android_app", "android_library"} { |
| for _, sdkKind := range []string{"", "system_"} { |
| for _, test := range testCases { |
| t.Run(moduleType+" "+test.name, func(t *testing.T) { |
| bp := fmt.Sprintf(`%s { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "%s%s", |
| vendor: true, |
| }`, moduleType, sdkKind, test.sdkVersion) |
| |
| result := android.GroupFixturePreparers( |
| prepareForJavaTest, |
| android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { |
| variables.Platform_sdk_version = &test.platformSdkInt |
| variables.Platform_sdk_codename = &test.platformSdkCodename |
| variables.Platform_sdk_final = &test.platformSdkFinal |
| variables.DeviceCurrentApiLevelForVendorModules = &test.deviceCurrentApiLevelForVendorModules |
| variables.DeviceSystemSdkVersions = []string{"28", "29"} |
| }), |
| FixtureWithPrebuiltApis(map[string][]string{ |
| "28": {"foo"}, |
| "29": {"foo"}, |
| "current": {"foo"}, |
| }), |
| ).RunTestWithBp(t, bp) |
| |
| checkSdkVersion(t, result, test.expectedMinSdkVersion) |
| }) |
| } |
| } |
| } |
| } |
| |
| func TestJNIABI(t *testing.T) { |
| ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+` |
| cc_library { |
| name: "libjni", |
| system_shared_libs: [], |
| sdk_version: "current", |
| stl: "none", |
| } |
| |
| android_test { |
| name: "test", |
| sdk_version: "core_platform", |
| jni_libs: ["libjni"], |
| } |
| |
| android_test { |
| name: "test_first", |
| sdk_version: "core_platform", |
| compile_multilib: "first", |
| jni_libs: ["libjni"], |
| } |
| |
| android_test { |
| name: "test_both", |
| sdk_version: "core_platform", |
| compile_multilib: "both", |
| jni_libs: ["libjni"], |
| } |
| |
| android_test { |
| name: "test_32", |
| sdk_version: "core_platform", |
| compile_multilib: "32", |
| jni_libs: ["libjni"], |
| } |
| |
| android_test { |
| name: "test_64", |
| sdk_version: "core_platform", |
| compile_multilib: "64", |
| jni_libs: ["libjni"], |
| } |
| `) |
| |
| testCases := []struct { |
| name string |
| abis []string |
| }{ |
| {"test", []string{"arm64-v8a"}}, |
| {"test_first", []string{"arm64-v8a"}}, |
| {"test_both", []string{"arm64-v8a", "armeabi-v7a"}}, |
| {"test_32", []string{"armeabi-v7a"}}, |
| {"test_64", []string{"arm64-v8a"}}, |
| } |
| |
| for _, test := range testCases { |
| t.Run(test.name, func(t *testing.T) { |
| app := ctx.ModuleForTests(test.name, "android_common") |
| jniLibZip := app.Output("jnilibs.zip") |
| var abis []string |
| args := strings.Fields(jniLibZip.Args["jarArgs"]) |
| for i := 0; i < len(args); i++ { |
| if args[i] == "-P" { |
| abis = append(abis, filepath.Base(args[i+1])) |
| i++ |
| } |
| } |
| if !reflect.DeepEqual(abis, test.abis) { |
| t.Errorf("want abis %v, got %v", test.abis, abis) |
| } |
| }) |
| } |
| } |
| |
| func TestAppSdkVersionByPartition(t *testing.T) { |
| testJavaError(t, "sdk_version must have a value when the module is located at vendor or product", ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| vendor: true, |
| platform_apis: true, |
| } |
| `) |
| |
| testJava(t, ` |
| android_app { |
| name: "bar", |
| srcs: ["b.java"], |
| platform_apis: true, |
| } |
| `) |
| |
| for _, enforce := range []bool{true, false} { |
| bp := ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| product_specific: true, |
| platform_apis: true, |
| } |
| ` |
| |
| errorHandler := android.FixtureExpectsNoErrors |
| if enforce { |
| errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern("sdk_version must have a value when the module is located at vendor or product") |
| } |
| |
| android.GroupFixturePreparers( |
| PrepareForTestWithJavaDefaultModules, |
| android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { |
| variables.EnforceProductPartitionInterface = proptools.BoolPtr(enforce) |
| }), |
| ). |
| ExtendWithErrorHandler(errorHandler). |
| RunTestWithBp(t, bp) |
| } |
| } |
| |
| func TestJNIPackaging(t *testing.T) { |
| ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+` |
| cc_library { |
| name: "libjni", |
| system_shared_libs: [], |
| stl: "none", |
| sdk_version: "current", |
| } |
| |
| android_app { |
| name: "app", |
| jni_libs: ["libjni"], |
| sdk_version: "current", |
| } |
| |
| android_app { |
| name: "app_noembed", |
| jni_libs: ["libjni"], |
| use_embedded_native_libs: false, |
| sdk_version: "current", |
| } |
| |
| android_app { |
| name: "app_embed", |
| jni_libs: ["libjni"], |
| use_embedded_native_libs: true, |
| sdk_version: "current", |
| } |
| |
| android_test { |
| name: "test", |
| sdk_version: "current", |
| jni_libs: ["libjni"], |
| } |
| |
| android_test { |
| name: "test_noembed", |
| sdk_version: "current", |
| jni_libs: ["libjni"], |
| use_embedded_native_libs: false, |
| } |
| |
| android_test_helper_app { |
| name: "test_helper", |
| sdk_version: "current", |
| jni_libs: ["libjni"], |
| } |
| |
| android_test_helper_app { |
| name: "test_helper_noembed", |
| sdk_version: "current", |
| jni_libs: ["libjni"], |
| use_embedded_native_libs: false, |
| } |
| `) |
| |
| testCases := []struct { |
| name string |
| packaged bool |
| compressed bool |
| }{ |
| {"app", false, false}, |
| {"app_noembed", false, false}, |
| {"app_embed", true, false}, |
| {"test", true, false}, |
| {"test_noembed", true, true}, |
| {"test_helper", true, false}, |
| {"test_helper_noembed", true, true}, |
| } |
| |
| for _, test := range testCases { |
| t.Run(test.name, func(t *testing.T) { |
| app := ctx.ModuleForTests(test.name, "android_common") |
| jniLibZip := app.MaybeOutput("jnilibs.zip") |
| if g, w := (jniLibZip.Rule != nil), test.packaged; g != w { |
| t.Errorf("expected jni packaged %v, got %v", w, g) |
| } |
| |
| if jniLibZip.Rule != nil { |
| if g, w := !strings.Contains(jniLibZip.Args["jarArgs"], "-L 0"), test.compressed; g != w { |
| t.Errorf("expected jni compressed %v, got %v", w, g) |
| } |
| |
| if !strings.Contains(jniLibZip.Implicits[0].String(), "_sdk_") { |
| t.Errorf("expected input %q to use sdk variant", jniLibZip.Implicits[0].String()) |
| } |
| } |
| }) |
| } |
| } |
| |
| func TestJNISDK(t *testing.T) { |
| ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+` |
| cc_library { |
| name: "libjni", |
| system_shared_libs: [], |
| stl: "none", |
| sdk_version: "current", |
| } |
| |
| android_test { |
| name: "app_platform", |
| jni_libs: ["libjni"], |
| platform_apis: true, |
| } |
| |
| android_test { |
| name: "app_sdk", |
| jni_libs: ["libjni"], |
| sdk_version: "current", |
| } |
| |
| android_test { |
| name: "app_force_platform", |
| jni_libs: ["libjni"], |
| sdk_version: "current", |
| jni_uses_platform_apis: true, |
| } |
| |
| android_test { |
| name: "app_force_sdk", |
| jni_libs: ["libjni"], |
| platform_apis: true, |
| jni_uses_sdk_apis: true, |
| } |
| |
| cc_library { |
| name: "libvendorjni", |
| system_shared_libs: [], |
| stl: "none", |
| vendor: true, |
| } |
| |
| android_test { |
| name: "app_vendor", |
| jni_libs: ["libvendorjni"], |
| sdk_version: "current", |
| vendor: true, |
| } |
| `) |
| |
| testCases := []struct { |
| name string |
| sdkJNI bool |
| vendorJNI bool |
| }{ |
| {name: "app_platform"}, |
| {name: "app_sdk", sdkJNI: true}, |
| {name: "app_force_platform"}, |
| {name: "app_force_sdk", sdkJNI: true}, |
| {name: "app_vendor", vendorJNI: true}, |
| } |
| |
| platformJNI := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_shared"). |
| Output("libjni.so").Output.String() |
| sdkJNI := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_sdk_shared"). |
| Output("libjni.so").Output.String() |
| vendorJNI := ctx.ModuleForTests("libvendorjni", "android_arm64_armv8-a_shared"). |
| Output("libvendorjni.so").Output.String() |
| |
| for _, test := range testCases { |
| t.Run(test.name, func(t *testing.T) { |
| app := ctx.ModuleForTests(test.name, "android_common") |
| |
| jniLibZip := app.MaybeOutput("jnilibs.zip") |
| if len(jniLibZip.Implicits) != 1 { |
| t.Fatalf("expected exactly one jni library, got %q", jniLibZip.Implicits.Strings()) |
| } |
| gotJNI := jniLibZip.Implicits[0].String() |
| |
| if test.sdkJNI { |
| if gotJNI != sdkJNI { |
| t.Errorf("expected SDK JNI library %q, got %q", sdkJNI, gotJNI) |
| } |
| } else if test.vendorJNI { |
| if gotJNI != vendorJNI { |
| t.Errorf("expected platform JNI library %q, got %q", vendorJNI, gotJNI) |
| } |
| } else { |
| if gotJNI != platformJNI { |
| t.Errorf("expected platform JNI library %q, got %q", platformJNI, gotJNI) |
| } |
| } |
| }) |
| } |
| |
| t.Run("jni_uses_platform_apis_error", func(t *testing.T) { |
| testJavaError(t, `jni_uses_platform_apis: can only be set for modules that set sdk_version`, ` |
| android_test { |
| name: "app_platform", |
| platform_apis: true, |
| jni_uses_platform_apis: true, |
| } |
| `) |
| }) |
| |
| t.Run("jni_uses_sdk_apis_error", func(t *testing.T) { |
| testJavaError(t, `jni_uses_sdk_apis: can only be set for modules that do not set sdk_version`, ` |
| android_test { |
| name: "app_sdk", |
| sdk_version: "current", |
| jni_uses_sdk_apis: true, |
| } |
| `) |
| }) |
| |
| } |
| |
| func TestCertificates(t *testing.T) { |
| testCases := []struct { |
| name string |
| bp string |
| certificateOverride string |
| expectedLineage string |
| expectedCertificate string |
| }{ |
| { |
| name: "default", |
| bp: ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "current", |
| } |
| `, |
| certificateOverride: "", |
| expectedLineage: "", |
| expectedCertificate: "build/make/target/product/security/testkey.x509.pem build/make/target/product/security/testkey.pk8", |
| }, |
| { |
| name: "module certificate property", |
| bp: ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| certificate: ":new_certificate", |
| sdk_version: "current", |
| } |
| |
| android_app_certificate { |
| name: "new_certificate", |
| certificate: "cert/new_cert", |
| } |
| `, |
| certificateOverride: "", |
| expectedLineage: "", |
| expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8", |
| }, |
| { |
| name: "path certificate property", |
| bp: ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| certificate: "expiredkey", |
| sdk_version: "current", |
| } |
| `, |
| certificateOverride: "", |
| expectedLineage: "", |
| expectedCertificate: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", |
| }, |
| { |
| name: "certificate overrides", |
| bp: ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| certificate: "expiredkey", |
| sdk_version: "current", |
| } |
| |
| android_app_certificate { |
| name: "new_certificate", |
| certificate: "cert/new_cert", |
| } |
| `, |
| certificateOverride: "foo:new_certificate", |
| expectedLineage: "", |
| expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8", |
| }, |
| { |
| name: "certificate lineage", |
| bp: ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| certificate: ":new_certificate", |
| lineage: "lineage.bin", |
| sdk_version: "current", |
| } |
| |
| android_app_certificate { |
| name: "new_certificate", |
| certificate: "cert/new_cert", |
| } |
| `, |
| certificateOverride: "", |
| expectedLineage: "--lineage lineage.bin", |
| expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8", |
| }, |
| { |
| name: "lineage from filegroup", |
| bp: ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| certificate: ":new_certificate", |
| lineage: ":lineage_bin", |
| sdk_version: "current", |
| } |
| |
| android_app_certificate { |
| name: "new_certificate", |
| certificate: "cert/new_cert", |
| } |
| |
| filegroup { |
| name: "lineage_bin", |
| srcs: ["lineage.bin"], |
| } |
| `, |
| certificateOverride: "", |
| expectedLineage: "--lineage lineage.bin", |
| expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8", |
| }, |
| } |
| |
| for _, test := range testCases { |
| t.Run(test.name, func(t *testing.T) { |
| result := android.GroupFixturePreparers( |
| PrepareForTestWithJavaDefaultModules, |
| android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { |
| if test.certificateOverride != "" { |
| variables.CertificateOverrides = []string{test.certificateOverride} |
| } |
| }), |
| ).RunTestWithBp(t, test.bp) |
| |
| foo := result.ModuleForTests("foo", "android_common") |
| |
| signapk := foo.Output("foo.apk") |
| signCertificateFlags := signapk.Args["certificates"] |
| android.AssertStringEquals(t, "certificates flags", test.expectedCertificate, signCertificateFlags) |
| |
| signFlags := signapk.Args["flags"] |
| android.AssertStringEquals(t, "signing flags", test.expectedLineage, signFlags) |
| }) |
| } |
| } |
| |
| func TestRequestV4SigningFlag(t *testing.T) { |
| testCases := []struct { |
| name string |
| bp string |
| expected string |
| }{ |
| { |
| name: "default", |
| bp: ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "current", |
| } |
| `, |
| expected: "", |
| }, |
| { |
| name: "default", |
| bp: ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "current", |
| v4_signature: false, |
| } |
| `, |
| expected: "", |
| }, |
| { |
| name: "module certificate property", |
| bp: ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "current", |
| v4_signature: true, |
| } |
| `, |
| expected: "--enable-v4", |
| }, |
| } |
| |
| for _, test := range testCases { |
| t.Run(test.name, func(t *testing.T) { |
| result := android.GroupFixturePreparers( |
| PrepareForTestWithJavaDefaultModules, |
| ).RunTestWithBp(t, test.bp) |
| |
| foo := result.ModuleForTests("foo", "android_common") |
| |
| signapk := foo.Output("foo.apk") |
| signFlags := signapk.Args["flags"] |
| android.AssertStringEquals(t, "signing flags", test.expected, signFlags) |
| }) |
| } |
| } |
| |
| func TestPackageNameOverride(t *testing.T) { |
| testCases := []struct { |
| name string |
| bp string |
| packageNameOverride string |
| expected []string |
| }{ |
| { |
| name: "default", |
| bp: ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "current", |
| } |
| `, |
| packageNameOverride: "", |
| expected: []string{ |
| "out/soong/.intermediates/foo/android_common/foo.apk", |
| "out/soong/target/product/test_device/system/app/foo/foo.apk", |
| }, |
| }, |
| { |
| name: "overridden", |
| bp: ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "current", |
| } |
| `, |
| packageNameOverride: "foo:bar", |
| expected: []string{ |
| // The package apk should be still be the original name for test dependencies. |
| "out/soong/.intermediates/foo/android_common/bar.apk", |
| "out/soong/target/product/test_device/system/app/bar/bar.apk", |
| }, |
| }, |
| } |
| |
| for _, test := range testCases { |
| t.Run(test.name, func(t *testing.T) { |
| result := android.GroupFixturePreparers( |
| PrepareForTestWithJavaDefaultModules, |
| android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { |
| if test.packageNameOverride != "" { |
| variables.PackageNameOverrides = []string{test.packageNameOverride} |
| } |
| }), |
| ).RunTestWithBp(t, test.bp) |
| |
| foo := result.ModuleForTests("foo", "android_common") |
| |
| outSoongDir := result.Config.SoongOutDir() |
| |
| outputs := foo.AllOutputs() |
| outputMap := make(map[string]bool) |
| for _, o := range outputs { |
| outputMap[android.StringPathRelativeToTop(outSoongDir, o)] = true |
| } |
| for _, e := range test.expected { |
| if _, exist := outputMap[e]; !exist { |
| t.Errorf("Can't find %q in output files.\nAll outputs:%v", e, outputs) |
| } |
| } |
| }) |
| } |
| } |
| |
| func TestInstrumentationTargetOverridden(t *testing.T) { |
| bp := ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "current", |
| } |
| |
| android_test { |
| name: "bar", |
| instrumentation_for: "foo", |
| sdk_version: "current", |
| } |
| ` |
| |
| result := android.GroupFixturePreparers( |
| PrepareForTestWithJavaDefaultModules, |
| android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { |
| variables.ManifestPackageNameOverrides = []string{"foo:org.dandroid.bp"} |
| }), |
| ).RunTestWithBp(t, bp) |
| |
| bar := result.ModuleForTests("bar", "android_common") |
| res := bar.Output("package-res.apk") |
| aapt2Flags := res.Args["flags"] |
| e := "--rename-instrumentation-target-package org.dandroid.bp" |
| if !strings.Contains(aapt2Flags, e) { |
| t.Errorf("target package renaming flag, %q is missing in aapt2 link flags, %q", e, aapt2Flags) |
| } |
| } |
| |
| func TestOverrideAndroidApp(t *testing.T) { |
| result := PrepareForTestWithJavaDefaultModules.RunTestWithBp( |
| t, ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| certificate: "expiredkey", |
| overrides: ["qux"], |
| sdk_version: "current", |
| } |
| |
| override_android_app { |
| name: "bar", |
| base: "foo", |
| certificate: ":new_certificate", |
| lineage: "lineage.bin", |
| logging_parent: "bah", |
| } |
| |
| android_app_certificate { |
| name: "new_certificate", |
| certificate: "cert/new_cert", |
| } |
| |
| override_android_app { |
| name: "baz", |
| base: "foo", |
| package_name: "org.dandroid.bp", |
| } |
| |
| override_android_app { |
| name: "baz_no_rename_resources", |
| base: "foo", |
| package_name: "org.dandroid.bp", |
| rename_resources_package: false, |
| } |
| |
| android_app { |
| name: "foo_no_rename_resources", |
| srcs: ["a.java"], |
| certificate: "expiredkey", |
| overrides: ["qux"], |
| rename_resources_package: false, |
| sdk_version: "current", |
| } |
| |
| override_android_app { |
| name: "baz_base_no_rename_resources", |
| base: "foo_no_rename_resources", |
| package_name: "org.dandroid.bp", |
| } |
| |
| override_android_app { |
| name: "baz_override_base_rename_resources", |
| base: "foo_no_rename_resources", |
| package_name: "org.dandroid.bp", |
| rename_resources_package: true, |
| } |
| `) |
| |
| expectedVariants := []struct { |
| name string |
| moduleName string |
| variantName string |
| apkName string |
| apkPath string |
| certFlag string |
| lineageFlag string |
| overrides []string |
| packageFlag string |
| renameResources bool |
| logging_parent string |
| }{ |
| { |
| name: "foo", |
| moduleName: "foo", |
| variantName: "android_common", |
| apkPath: "out/soong/target/product/test_device/system/app/foo/foo.apk", |
| certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", |
| lineageFlag: "", |
| overrides: []string{"qux"}, |
| packageFlag: "", |
| renameResources: false, |
| logging_parent: "", |
| }, |
| { |
| name: "foo", |
| moduleName: "bar", |
| variantName: "android_common_bar", |
| apkPath: "out/soong/target/product/test_device/system/app/bar/bar.apk", |
| certFlag: "cert/new_cert.x509.pem cert/new_cert.pk8", |
| lineageFlag: "--lineage lineage.bin", |
| overrides: []string{"qux", "foo"}, |
| packageFlag: "", |
| renameResources: false, |
| logging_parent: "bah", |
| }, |
| { |
| name: "foo", |
| moduleName: "baz", |
| variantName: "android_common_baz", |
| apkPath: "out/soong/target/product/test_device/system/app/baz/baz.apk", |
| certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", |
| lineageFlag: "", |
| overrides: []string{"qux", "foo"}, |
| packageFlag: "org.dandroid.bp", |
| renameResources: true, |
| logging_parent: "", |
| }, |
| { |
| name: "foo", |
| moduleName: "baz_no_rename_resources", |
| variantName: "android_common_baz_no_rename_resources", |
| apkPath: "out/soong/target/product/test_device/system/app/baz_no_rename_resources/baz_no_rename_resources.apk", |
| certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", |
| lineageFlag: "", |
| overrides: []string{"qux", "foo"}, |
| packageFlag: "org.dandroid.bp", |
| renameResources: false, |
| logging_parent: "", |
| }, |
| { |
| name: "foo_no_rename_resources", |
| moduleName: "baz_base_no_rename_resources", |
| variantName: "android_common_baz_base_no_rename_resources", |
| apkPath: "out/soong/target/product/test_device/system/app/baz_base_no_rename_resources/baz_base_no_rename_resources.apk", |
| certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", |
| lineageFlag: "", |
| overrides: []string{"qux", "foo_no_rename_resources"}, |
| packageFlag: "org.dandroid.bp", |
| renameResources: false, |
| logging_parent: "", |
| }, |
| { |
| name: "foo_no_rename_resources", |
| moduleName: "baz_override_base_rename_resources", |
| variantName: "android_common_baz_override_base_rename_resources", |
| apkPath: "out/soong/target/product/test_device/system/app/baz_override_base_rename_resources/baz_override_base_rename_resources.apk", |
| certFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", |
| lineageFlag: "", |
| overrides: []string{"qux", "foo_no_rename_resources"}, |
| packageFlag: "org.dandroid.bp", |
| renameResources: true, |
| logging_parent: "", |
| }, |
| } |
| for _, expected := range expectedVariants { |
| variant := result.ModuleForTests(expected.name, expected.variantName) |
| |
| // Check the final apk name |
| variant.Output(expected.apkPath) |
| |
| // Check the certificate paths |
| signapk := variant.Output(expected.moduleName + ".apk") |
| certFlag := signapk.Args["certificates"] |
| android.AssertStringEquals(t, "certificates flags", expected.certFlag, certFlag) |
| |
| // Check the lineage flags |
| lineageFlag := signapk.Args["flags"] |
| android.AssertStringEquals(t, "signing flags", expected.lineageFlag, lineageFlag) |
| |
| // Check if the overrides field values are correctly aggregated. |
| mod := variant.Module().(*AndroidApp) |
| android.AssertDeepEquals(t, "overrides property", expected.overrides, mod.appProperties.Overrides) |
| |
| // Test Overridable property: Logging_parent |
| logging_parent := mod.aapt.LoggingParent |
| android.AssertStringEquals(t, "overrides property value for logging parent", expected.logging_parent, logging_parent) |
| |
| // Check the package renaming flag, if exists. |
| res := variant.Output("package-res.apk") |
| aapt2Flags := res.Args["flags"] |
| checkAapt2LinkFlag(t, aapt2Flags, "rename-manifest-package", expected.packageFlag) |
| expectedPackage := expected.packageFlag |
| if !expected.renameResources { |
| expectedPackage = "" |
| } |
| checkAapt2LinkFlag(t, aapt2Flags, "rename-resources-package", expectedPackage) |
| } |
| } |
| |
| func TestOverrideAndroidAppDependency(t *testing.T) { |
| ctx, _ := testJava(t, ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "current", |
| } |
| |
| override_android_app { |
| name: "bar", |
| base: "foo", |
| package_name: "org.dandroid.bp", |
| } |
| |
| android_test { |
| name: "baz", |
| srcs: ["b.java"], |
| instrumentation_for: "foo", |
| } |
| |
| android_test { |
| name: "qux", |
| srcs: ["b.java"], |
| instrumentation_for: "bar", |
| } |
| `) |
| |
| // Verify baz, which depends on the overridden module foo, has the correct classpath javac arg. |
| javac := ctx.ModuleForTests("baz", "android_common").Rule("javac") |
| fooTurbine := "out/soong/.intermediates/foo/android_common/turbine-combined/foo.jar" |
| if !strings.Contains(javac.Args["classpath"], fooTurbine) { |
| t.Errorf("baz classpath %v does not contain %q", javac.Args["classpath"], fooTurbine) |
| } |
| |
| // Verify qux, which depends on the overriding module bar, has the correct classpath javac arg. |
| javac = ctx.ModuleForTests("qux", "android_common").Rule("javac") |
| barTurbine := "out/soong/.intermediates/foo/android_common_bar/turbine-combined/foo.jar" |
| if !strings.Contains(javac.Args["classpath"], barTurbine) { |
| t.Errorf("qux classpath %v does not contain %q", javac.Args["classpath"], barTurbine) |
| } |
| } |
| |
| func TestOverrideAndroidTest(t *testing.T) { |
| ctx, _ := testJava(t, ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| package_name: "com.android.foo", |
| sdk_version: "current", |
| } |
| |
| override_android_app { |
| name: "bar", |
| base: "foo", |
| package_name: "com.android.bar", |
| } |
| |
| android_test { |
| name: "foo_test", |
| srcs: ["b.java"], |
| instrumentation_for: "foo", |
| } |
| |
| override_android_test { |
| name: "bar_test", |
| base: "foo_test", |
| package_name: "com.android.bar.test", |
| instrumentation_for: "bar", |
| instrumentation_target_package: "com.android.bar", |
| } |
| `) |
| |
| expectedVariants := []struct { |
| moduleName string |
| variantName string |
| apkPath string |
| overrides []string |
| targetVariant string |
| packageFlag string |
| targetPackageFlag string |
| }{ |
| { |
| variantName: "android_common", |
| apkPath: "/target/product/test_device/testcases/foo_test/arm64/foo_test.apk", |
| overrides: nil, |
| targetVariant: "android_common", |
| packageFlag: "", |
| targetPackageFlag: "", |
| }, |
| { |
| variantName: "android_common_bar_test", |
| apkPath: "/target/product/test_device/testcases/bar_test/arm64/bar_test.apk", |
| overrides: []string{"foo_test"}, |
| targetVariant: "android_common_bar", |
| packageFlag: "com.android.bar.test", |
| targetPackageFlag: "com.android.bar", |
| }, |
| } |
| for _, expected := range expectedVariants { |
| variant := ctx.ModuleForTests("foo_test", expected.variantName) |
| |
| // Check the final apk name |
| variant.Output("out/soong" + expected.apkPath) |
| |
| // Check if the overrides field values are correctly aggregated. |
| mod := variant.Module().(*AndroidTest) |
| if !reflect.DeepEqual(expected.overrides, mod.appProperties.Overrides) { |
| t.Errorf("Incorrect overrides property value, expected: %q, got: %q", |
| expected.overrides, mod.appProperties.Overrides) |
| } |
| |
| // Check if javac classpath has the correct jar file path. This checks instrumentation_for overrides. |
| javac := variant.Rule("javac") |
| turbine := filepath.Join("out", "soong", ".intermediates", "foo", expected.targetVariant, "turbine-combined", "foo.jar") |
| if !strings.Contains(javac.Args["classpath"], turbine) { |
| t.Errorf("classpath %q does not contain %q", javac.Args["classpath"], turbine) |
| } |
| |
| // Check aapt2 flags. |
| res := variant.Output("package-res.apk") |
| aapt2Flags := res.Args["flags"] |
| checkAapt2LinkFlag(t, aapt2Flags, "rename-manifest-package", expected.packageFlag) |
| checkAapt2LinkFlag(t, aapt2Flags, "rename-resources-package", expected.packageFlag) |
| checkAapt2LinkFlag(t, aapt2Flags, "rename-instrumentation-target-package", expected.targetPackageFlag) |
| } |
| } |
| |
| func TestAndroidTest_FixTestConfig(t *testing.T) { |
| ctx, _ := testJava(t, ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| package_name: "com.android.foo", |
| sdk_version: "current", |
| } |
| |
| android_test { |
| name: "foo_test", |
| srcs: ["b.java"], |
| instrumentation_for: "foo", |
| } |
| |
| android_test { |
| name: "bar_test", |
| srcs: ["b.java"], |
| package_name: "com.android.bar.test", |
| instrumentation_for: "foo", |
| } |
| |
| override_android_test { |
| name: "baz_test", |
| base: "foo_test", |
| package_name: "com.android.baz.test", |
| } |
| `) |
| |
| testCases := []struct { |
| moduleName string |
| variantName string |
| expectedFlags []string |
| }{ |
| { |
| moduleName: "foo_test", |
| variantName: "android_common", |
| }, |
| { |
| moduleName: "bar_test", |
| variantName: "android_common", |
| expectedFlags: []string{ |
| "--manifest out/soong/.intermediates/bar_test/android_common/manifest_fixer/AndroidManifest.xml", |
| "--package-name com.android.bar.test", |
| }, |
| }, |
| { |
| moduleName: "foo_test", |
| variantName: "android_common_baz_test", |
| expectedFlags: []string{ |
| "--manifest out/soong/.intermediates/foo_test/android_common_baz_test/manifest_fixer/AndroidManifest.xml", |
| "--package-name com.android.baz.test", |
| "--test-file-name baz_test.apk", |
| }, |
| }, |
| } |
| |
| for _, test := range testCases { |
| variant := ctx.ModuleForTests(test.moduleName, test.variantName) |
| params := variant.MaybeOutput("test_config_fixer/AndroidTest.xml") |
| |
| if len(test.expectedFlags) > 0 { |
| if params.Rule == nil { |
| t.Errorf("test_config_fixer was expected to run, but didn't") |
| } else { |
| for _, flag := range test.expectedFlags { |
| if !strings.Contains(params.RuleParams.Command, flag) { |
| t.Errorf("Flag %q was not found in command: %q", flag, params.RuleParams.Command) |
| } |
| } |
| } |
| } else { |
| if params.Rule != nil { |
| t.Errorf("test_config_fixer was not expected to run, but did: %q", params.RuleParams.Command) |
| } |
| } |
| |
| } |
| } |
| |
| func TestStl(t *testing.T) { |
| ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+` |
| cc_library { |
| name: "libjni", |
| sdk_version: "current", |
| stl: "c++_shared", |
| } |
| |
| android_test { |
| name: "stl", |
| jni_libs: ["libjni"], |
| compile_multilib: "both", |
| sdk_version: "current", |
| stl: "c++_shared", |
| } |
| |
| android_test { |
| name: "system", |
| jni_libs: ["libjni"], |
| compile_multilib: "both", |
| sdk_version: "current", |
| } |
| `) |
| |
| testCases := []struct { |
| name string |
| jnis []string |
| }{ |
| {"stl", |
| []string{ |
| "libjni.so", |
| "libc++_shared.so", |
| }, |
| }, |
| {"system", |
| []string{ |
| "libjni.so", |
| }, |
| }, |
| } |
| |
| for _, test := range testCases { |
| t.Run(test.name, func(t *testing.T) { |
| app := ctx.ModuleForTests(test.name, "android_common") |
| jniLibZip := app.Output("jnilibs.zip") |
| var jnis []string |
| args := strings.Fields(jniLibZip.Args["jarArgs"]) |
| for i := 0; i < len(args); i++ { |
| if args[i] == "-f" { |
| jnis = append(jnis, args[i+1]) |
| i += 1 |
| } |
| } |
| jnisJoined := strings.Join(jnis, " ") |
| for _, jni := range test.jnis { |
| if !strings.Contains(jnisJoined, jni) { |
| t.Errorf("missing jni %q in %q", jni, jnis) |
| } |
| } |
| }) |
| } |
| } |
| |
| func TestUsesLibraries(t *testing.T) { |
| bp := ` |
| java_sdk_library { |
| name: "foo", |
| srcs: ["a.java"], |
| api_packages: ["foo"], |
| sdk_version: "current", |
| } |
| |
| java_sdk_library { |
| name: "qux", |
| srcs: ["a.java"], |
| api_packages: ["qux"], |
| sdk_version: "current", |
| } |
| |
| java_sdk_library { |
| name: "quuz", |
| srcs: ["a.java"], |
| api_packages: ["quuz"], |
| sdk_version: "current", |
| } |
| |
| java_sdk_library { |
| name: "fred", |
| srcs: ["a.java"], |
| api_packages: ["fred"], |
| sdk_version: "current", |
| } |
| |
| java_sdk_library { |
| name: "bar", |
| srcs: ["a.java"], |
| api_packages: ["bar"], |
| sdk_version: "current", |
| } |
| |
| java_sdk_library { |
| name: "runtime-library", |
| srcs: ["a.java"], |
| sdk_version: "current", |
| } |
| |
| java_library { |
| name: "static-runtime-helper", |
| srcs: ["a.java"], |
| libs: ["runtime-library"], |
| sdk_version: "current", |
| } |
| |
| java_library { |
| name: "runtime-required-x", |
| srcs: ["a.java"], |
| installable: true, |
| sdk_version: "current", |
| } |
| |
| java_library { |
| name: "runtime-optional-x", |
| srcs: ["a.java"], |
| installable: true, |
| sdk_version: "current", |
| } |
| |
| android_library { |
| name: "static-x", |
| uses_libs: ["runtime-required-x"], |
| optional_uses_libs: ["runtime-optional-x"], |
| sdk_version: "current", |
| } |
| |
| java_library { |
| name: "runtime-required-y", |
| srcs: ["a.java"], |
| installable: true, |
| sdk_version: "current", |
| } |
| |
| java_library { |
| name: "runtime-optional-y", |
| srcs: ["a.java"], |
| installable: true, |
| sdk_version: "current", |
| } |
| |
| java_library { |
| name: "static-y", |
| srcs: ["a.java"], |
| uses_libs: ["runtime-required-y"], |
| optional_uses_libs: ["runtime-optional-y"], |
| sdk_version: "current", |
| } |
| |
| // A library that has to use "provides_uses_lib", because: |
| // - it is not an SDK library |
| // - its library name is different from its module name |
| java_library { |
| name: "non-sdk-lib", |
| provides_uses_lib: "com.non.sdk.lib", |
| installable: true, |
| srcs: ["a.java"], |
| } |
| |
| android_app { |
| name: "app", |
| srcs: ["a.java"], |
| libs: [ |
| "qux", |
| "quuz.stubs" |
| ], |
| static_libs: [ |
| "static-runtime-helper", |
| // statically linked component libraries should not pull their SDK libraries, |
| // so "fred" should not be added to class loader context |
| "fred.stubs", |
| "static-x", |
| "static-y", |
| ], |
| uses_libs: [ |
| "foo", |
| "non-sdk-lib" |
| ], |
| sdk_version: "current", |
| optional_uses_libs: [ |
| "bar", |
| "baz", |
| ], |
| } |
| |
| android_app_import { |
| name: "prebuilt", |
| apk: "prebuilts/apk/app.apk", |
| certificate: "platform", |
| uses_libs: [ |
| "foo", |
| "non-sdk-lib", |
| "android.test.runner" |
| ], |
| optional_uses_libs: [ |
| "bar", |
| "baz", |
| ], |
| } |
| ` |
| |
| result := android.GroupFixturePreparers( |
| prepareForJavaTest, |
| PrepareForTestWithJavaSdkLibraryFiles, |
| FixtureWithLastReleaseApis("runtime-library", "foo", "quuz", "qux", "bar", "fred"), |
| android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { |
| variables.MissingUsesLibraries = []string{"baz"} |
| }), |
| ).RunTestWithBp(t, bp) |
| |
| app := result.ModuleForTests("app", "android_common") |
| prebuilt := result.ModuleForTests("prebuilt", "android_common") |
| |
| // Test that implicit dependencies on java_sdk_library instances are passed to the manifest. |
| // This should not include explicit `uses_libs`/`optional_uses_libs` entries. |
| actualManifestFixerArgs := app.Output("manifest_fixer/AndroidManifest.xml").Args["args"] |
| expectManifestFixerArgs := `--extract-native-libs=true ` + |
| `--uses-library qux ` + |
| `--uses-library quuz ` + |
| `--uses-library runtime-library` |
| android.AssertStringEquals(t, "manifest_fixer args", expectManifestFixerArgs, actualManifestFixerArgs) |
| |
| // Test that all libraries are verified (library order matters). |
| verifyCmd := app.Rule("verify_uses_libraries").RuleParams.Command |
| verifyArgs := `--uses-library foo ` + |
| `--uses-library com.non.sdk.lib ` + |
| `--uses-library qux ` + |
| `--uses-library quuz ` + |
| `--uses-library runtime-library ` + |
| `--uses-library runtime-required-x ` + |
| `--uses-library runtime-required-y ` + |
| `--optional-uses-library bar ` + |
| `--optional-uses-library baz ` + |
| `--optional-uses-library runtime-optional-x ` + |
| `--optional-uses-library runtime-optional-y ` |
| android.AssertStringDoesContain(t, "verify cmd args", verifyCmd, verifyArgs) |
| |
| // Test that all libraries are verified for an APK (library order matters). |
| verifyApkCmd := prebuilt.Rule("verify_uses_libraries").RuleParams.Command |
| verifyApkArgs := `--uses-library foo ` + |
| `--uses-library com.non.sdk.lib ` + |
| `--uses-library android.test.runner ` + |
| `--optional-uses-library bar ` + |
| `--optional-uses-library baz ` |
| android.AssertStringDoesContain(t, "verify apk cmd args", verifyApkCmd, verifyApkArgs) |
| |
| // Test that all present libraries are preopted, including implicit SDK dependencies, possibly stubs |
| cmd := app.Rule("dexpreopt").RuleParams.Command |
| w := `--target-context-for-sdk any ` + |
| `PCL[/system/framework/qux.jar]#` + |
| `PCL[/system/framework/quuz.jar]#` + |
| `PCL[/system/framework/foo.jar]#` + |
| `PCL[/system/framework/non-sdk-lib.jar]#` + |
| `PCL[/system/framework/bar.jar]#` + |
| `PCL[/system/framework/runtime-library.jar]#` + |
| `PCL[/system/framework/runtime-required-x.jar]#` + |
| `PCL[/system/framework/runtime-optional-x.jar]#` + |
| `PCL[/system/framework/runtime-required-y.jar]#` + |
| `PCL[/system/framework/runtime-optional-y.jar] ` |
| android.AssertStringDoesContain(t, "dexpreopt app cmd args", cmd, w) |
| |
| // Test conditional context for target SDK version 28. |
| android.AssertStringDoesContain(t, "dexpreopt app cmd 28", cmd, |
| `--target-context-for-sdk 28`+ |
| ` PCL[/system/framework/org.apache.http.legacy.jar] `) |
| |
| // Test conditional context for target SDK version 29. |
| android.AssertStringDoesContain(t, "dexpreopt app cmd 29", cmd, |
| `--target-context-for-sdk 29`+ |
| ` PCL[/system/framework/android.hidl.manager-V1.0-java.jar]`+ |
| `#PCL[/system/framework/android.hidl.base-V1.0-java.jar] `) |
| |
| // Test conditional context for target SDK version 30. |
| // "android.test.mock" is absent because "android.test.runner" is not used. |
| android.AssertStringDoesContain(t, "dexpreopt app cmd 30", cmd, |
| `--target-context-for-sdk 30`+ |
| ` PCL[/system/framework/android.test.base.jar] `) |
| |
| cmd = prebuilt.Rule("dexpreopt").RuleParams.Command |
| android.AssertStringDoesContain(t, "dexpreopt prebuilt cmd", cmd, |
| `--target-context-for-sdk any`+ |
| ` PCL[/system/framework/foo.jar]`+ |
| `#PCL[/system/framework/non-sdk-lib.jar]`+ |
| `#PCL[/system/framework/android.test.runner.jar]`+ |
| `#PCL[/system/framework/bar.jar] `) |
| |
| // Test conditional context for target SDK version 30. |
| // "android.test.mock" is present because "android.test.runner" is used. |
| android.AssertStringDoesContain(t, "dexpreopt prebuilt cmd 30", cmd, |
| `--target-context-for-sdk 30`+ |
| ` PCL[/system/framework/android.test.base.jar]`+ |
| `#PCL[/system/framework/android.test.mock.jar] `) |
| } |
| |
| func TestDexpreoptBcp(t *testing.T) { |
| bp := ` |
| java_sdk_library { |
| name: "foo", |
| srcs: ["a.java"], |
| api_packages: ["foo"], |
| sdk_version: "current", |
| } |
| |
| java_sdk_library { |
| name: "bar", |
| srcs: ["a.java"], |
| api_packages: ["bar"], |
| permitted_packages: ["bar"], |
| sdk_version: "current", |
| } |
| |
| android_app { |
| name: "app", |
| srcs: ["a.java"], |
| sdk_version: "current", |
| } |
| ` |
| |
| testCases := []struct { |
| name string |
| with bool |
| expect string |
| }{ |
| { |
| name: "with updatable bcp", |
| with: true, |
| expect: "/system/framework/foo.jar:/system/framework/bar.jar", |
| }, |
| { |
| name: "without updatable bcp", |
| with: false, |
| expect: "/system/framework/foo.jar", |
| }, |
| } |
| |
| for _, test := range testCases { |
| t.Run(test.name, func(t *testing.T) { |
| result := android.GroupFixturePreparers( |
| prepareForJavaTest, |
| PrepareForTestWithJavaSdkLibraryFiles, |
| FixtureWithLastReleaseApis("runtime-library", "foo", "bar"), |
| dexpreopt.FixtureSetBootJars("platform:foo"), |
| dexpreopt.FixtureSetApexBootJars("platform:bar"), |
| dexpreopt.FixtureSetPreoptWithUpdatableBcp(test.with), |
| ).RunTestWithBp(t, bp) |
| |
| app := result.ModuleForTests("app", "android_common") |
| cmd := app.Rule("dexpreopt").RuleParams.Command |
| bcp := " -Xbootclasspath-locations:" + test.expect + " " // space at the end matters |
| android.AssertStringDoesContain(t, "dexpreopt app bcp", cmd, bcp) |
| }) |
| } |
| } |
| |
| func TestCodelessApp(t *testing.T) { |
| testCases := []struct { |
| name string |
| bp string |
| noCode bool |
| }{ |
| { |
| name: "normal", |
| bp: ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "current", |
| } |
| `, |
| noCode: false, |
| }, |
| { |
| name: "app without sources", |
| bp: ` |
| android_app { |
| name: "foo", |
| sdk_version: "current", |
| } |
| `, |
| noCode: true, |
| }, |
| { |
| name: "app with libraries", |
| bp: ` |
| android_app { |
| name: "foo", |
| static_libs: ["lib"], |
| sdk_version: "current", |
| } |
| |
| java_library { |
| name: "lib", |
| srcs: ["a.java"], |
| sdk_version: "current", |
| } |
| `, |
| noCode: false, |
| }, |
| { |
| name: "app with sourceless libraries", |
| bp: ` |
| android_app { |
| name: "foo", |
| static_libs: ["lib"], |
| sdk_version: "current", |
| } |
| |
| java_library { |
| name: "lib", |
| sdk_version: "current", |
| } |
| `, |
| // TODO(jungjw): this should probably be true |
| noCode: false, |
| }, |
| } |
| |
| for _, test := range testCases { |
| t.Run(test.name, func(t *testing.T) { |
| ctx := testApp(t, test.bp) |
| |
| foo := ctx.ModuleForTests("foo", "android_common") |
| manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"] |
| if strings.Contains(manifestFixerArgs, "--has-no-code") != test.noCode { |
| t.Errorf("unexpected manifest_fixer args: %q", manifestFixerArgs) |
| } |
| }) |
| } |
| } |
| |
| func TestEmbedNotice(t *testing.T) { |
| result := android.GroupFixturePreparers( |
| PrepareForTestWithJavaDefaultModules, |
| cc.PrepareForTestWithCcDefaultModules, |
| genrule.PrepareForTestWithGenRuleBuildComponents, |
| android.MockFS{ |
| "APP_NOTICE": nil, |
| "GENRULE_NOTICE": nil, |
| "LIB_NOTICE": nil, |
| "TOOL_NOTICE": nil, |
| }.AddToFixture(), |
| ).RunTestWithBp(t, ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| static_libs: ["javalib"], |
| jni_libs: ["libjni"], |
| notice: "APP_NOTICE", |
| embed_notices: true, |
| sdk_version: "current", |
| } |
| |
| // No embed_notice flag |
| android_app { |
| name: "bar", |
| srcs: ["a.java"], |
| jni_libs: ["libjni"], |
| notice: "APP_NOTICE", |
| sdk_version: "current", |
| } |
| |
| // No NOTICE files |
| android_app { |
| name: "baz", |
| srcs: ["a.java"], |
| embed_notices: true, |
| sdk_version: "current", |
| } |
| |
| cc_library { |
| name: "libjni", |
| system_shared_libs: [], |
| stl: "none", |
| notice: "LIB_NOTICE", |
| sdk_version: "current", |
| } |
| |
| java_library { |
| name: "javalib", |
| srcs: [ |
| ":gen", |
| ], |
| sdk_version: "current", |
| } |
| |
| genrule { |
| name: "gen", |
| tools: ["gentool"], |
| out: ["gen.java"], |
| notice: "GENRULE_NOTICE", |
| } |
| |
| java_binary_host { |
| name: "gentool", |
| srcs: ["b.java"], |
| notice: "TOOL_NOTICE", |
| } |
| `) |
| |
| // foo has NOTICE files to process, and embed_notices is true. |
| foo := result.ModuleForTests("foo", "android_common") |
| // verify merge notices rule. |
| mergeNotices := foo.Rule("mergeNoticesRule") |
| noticeInputs := mergeNotices.Inputs.Strings() |
| // TOOL_NOTICE should be excluded as it's a host module. |
| if len(mergeNotices.Inputs) != 3 { |
| t.Errorf("number of input notice files: expected = 3, actual = %q", noticeInputs) |
| } |
| if !inList("APP_NOTICE", noticeInputs) { |
| t.Errorf("APP_NOTICE is missing from notice files, %q", noticeInputs) |
| } |
| if !inList("LIB_NOTICE", noticeInputs) { |
| t.Errorf("LIB_NOTICE is missing from notice files, %q", noticeInputs) |
| } |
| if !inList("GENRULE_NOTICE", noticeInputs) { |
| t.Errorf("GENRULE_NOTICE is missing from notice files, %q", noticeInputs) |
| } |
| // aapt2 flags should include -A <NOTICE dir> so that its contents are put in the APK's /assets. |
| res := foo.Output("package-res.apk") |
| aapt2Flags := res.Args["flags"] |
| e := "-A out/soong/.intermediates/foo/android_common/NOTICE" |
| android.AssertStringDoesContain(t, "expected.apkPath", aapt2Flags, e) |
| |
| // bar has NOTICE files to process, but embed_notices is not set. |
| bar := result.ModuleForTests("bar", "android_common") |
| res = bar.Output("package-res.apk") |
| aapt2Flags = res.Args["flags"] |
| e = "-A out/soong/.intermediates/bar/android_common/NOTICE" |
| android.AssertStringDoesNotContain(t, "bar shouldn't have the asset dir flag for NOTICE", aapt2Flags, e) |
| |
| // baz's embed_notice is true, but it doesn't have any NOTICE files. |
| baz := result.ModuleForTests("baz", "android_common") |
| res = baz.Output("package-res.apk") |
| aapt2Flags = res.Args["flags"] |
| e = "-A out/soong/.intermediates/baz/android_common/NOTICE" |
| if strings.Contains(aapt2Flags, e) { |
| t.Errorf("baz shouldn't have the asset dir flag for NOTICE: %q", e) |
| } |
| } |
| |
| func TestUncompressDex(t *testing.T) { |
| testCases := []struct { |
| name string |
| bp string |
| |
| uncompressedPlatform bool |
| uncompressedUnbundled bool |
| }{ |
| { |
| name: "normal", |
| bp: ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "current", |
| } |
| `, |
| uncompressedPlatform: true, |
| uncompressedUnbundled: false, |
| }, |
| { |
| name: "use_embedded_dex", |
| bp: ` |
| android_app { |
| name: "foo", |
| use_embedded_dex: true, |
| srcs: ["a.java"], |
| sdk_version: "current", |
| } |
| `, |
| uncompressedPlatform: true, |
| uncompressedUnbundled: true, |
| }, |
| { |
| name: "privileged", |
| bp: ` |
| android_app { |
| name: "foo", |
| privileged: true, |
| srcs: ["a.java"], |
| sdk_version: "current", |
| } |
| `, |
| uncompressedPlatform: true, |
| uncompressedUnbundled: true, |
| }, |
| { |
| name: "normal_uncompress_dex_true", |
| bp: ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "current", |
| uncompress_dex: true, |
| } |
| `, |
| uncompressedPlatform: true, |
| uncompressedUnbundled: true, |
| }, |
| { |
| name: "normal_uncompress_dex_false", |
| bp: ` |
| android_app { |
| name: "foo", |
| srcs: ["a.java"], |
| sdk_version: "current", |
| uncompress_dex: false, |
| } |
| `, |
| uncompressedPlatform: false, |
| uncompressedUnbundled: false, |
| }, |
| } |
| |
| test := func(t *testing.T, bp string, want bool, unbundled bool) { |
| t.Helper() |
| |
| result := android.GroupFixturePreparers( |
| prepareForJavaTest, |
| PrepareForTestWithPrebuiltsOfCurrentApi, |
| android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { |
| if unbundled { |
| variables.Unbundled_build = proptools.BoolPtr(true) |
| variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true) |
| } |
| }), |
| ).RunTestWithBp(t, bp) |
| |
| foo := result.ModuleForTests("foo", "android_common") |
| dex := foo.Rule("r8") |
| uncompressedInDexJar := strings.Contains(dex.Args["zipFlags"], "-L 0") |
| aligned := foo.MaybeRule("zipalign").Rule != nil |
| |
| android.AssertBoolEquals(t, "uncompressed in dex", want, uncompressedInDexJar) |
| |
| android.AssertBoolEquals(t, "aligne", want, aligned) |
| } |
| |
| for _, tt := range testCases { |
| t.Run(tt.name, func(t *testing.T) { |
| t.Run("platform", func(t *testing.T) { |
| test(t, tt.bp, tt.uncompressedPlatform, false) |
| }) |
| t.Run("unbundled", func(t *testing.T) { |
| test(t, tt.bp, tt.uncompressedUnbundled, true) |
| }) |
| }) |
| } |
| } |
| |
| func checkAapt2LinkFlag(t *testing.T, aapt2Flags, flagName, expectedValue string) { |
| if expectedValue != "" { |
| expectedFlag := "--" + flagName + " " + expectedValue |
| if !strings.Contains(aapt2Flags, expectedFlag) { |
| t.Errorf("%q is missing in aapt2 link flags, %q", expectedFlag, aapt2Flags) |
| } |
| } else { |
| unexpectedFlag := "--" + flagName |
| if strings.Contains(aapt2Flags, unexpectedFlag) { |
| t.Errorf("unexpected flag, %q is found in aapt2 link flags, %q", unexpectedFlag, aapt2Flags) |
| } |
| } |
| } |
| |
| func TestExportedProguardFlagFiles(t *testing.T) { |
| ctx, _ := testJava(t, ` |
| android_app { |
| name: "foo", |
| sdk_version: "current", |
| static_libs: ["lib1"], |
| } |
| |
| android_library { |
| name: "lib1", |
| sdk_version: "current", |
| optimize: { |
| proguard_flags_files: ["lib1proguard.cfg"], |
| } |
| } |
| `) |
| |
| m := ctx.ModuleForTests("foo", "android_common") |
| hasLib1Proguard := false |
| for _, s := range m.Rule("java.r8").Implicits.Strings() { |
| if s == "lib1proguard.cfg" { |
| hasLib1Proguard = true |
| break |
| } |
| } |
| |
| if !hasLib1Proguard { |
| t.Errorf("App does not use library proguard config") |
| } |
| } |