| // 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" | |
| ) | |
| // 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: "current", | |
| min_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", | |
| min_sdk_version: "current", | |
| } | |
| ` | |
| testJavaError(t, `"libjni" .*: min_sdk_version\(current\) is higher than min_sdk_version\(29\)`, bp) | |
| } | |
| func TestUpdatableApps_ErrorIfDepMinSdkVersionIsHigher(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", | |
| min_sdk_version: "27", | |
| } | |
| cc_library { | |
| name: "libbar", | |
| stl: "none", | |
| system_shared_libs: [], | |
| sdk_version: "current", | |
| min_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(jniJarOutputPathString) | |
| 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(jniJarOutputPathString) | |
| 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(jniJarOutputPathString) | |
| 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 | |
| allowMissingDependencies bool | |
| certificateOverride string | |
| expectedCertSigningFlags string | |
| expectedCertificate string | |
| }{ | |
| { | |
| name: "default", | |
| bp: ` | |
| android_app { | |
| name: "foo", | |
| srcs: ["a.java"], | |
| sdk_version: "current", | |
| } | |
| `, | |
| certificateOverride: "", | |
| expectedCertSigningFlags: "", | |
| expectedCertificate: "build/make/target/product/security/testkey", | |
| }, | |
| { | |
| 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: "", | |
| expectedCertSigningFlags: "", | |
| expectedCertificate: "cert/new_cert", | |
| }, | |
| { | |
| name: "path certificate property", | |
| bp: ` | |
| android_app { | |
| name: "foo", | |
| srcs: ["a.java"], | |
| certificate: "expiredkey", | |
| sdk_version: "current", | |
| } | |
| `, | |
| certificateOverride: "", | |
| expectedCertSigningFlags: "", | |
| expectedCertificate: "build/make/target/product/security/expiredkey", | |
| }, | |
| { | |
| 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", | |
| expectedCertSigningFlags: "", | |
| expectedCertificate: "cert/new_cert", | |
| }, | |
| { | |
| name: "certificate signing flags", | |
| bp: ` | |
| android_app { | |
| name: "foo", | |
| srcs: ["a.java"], | |
| certificate: ":new_certificate", | |
| lineage: "lineage.bin", | |
| rotationMinSdkVersion: "32", | |
| sdk_version: "current", | |
| } | |
| android_app_certificate { | |
| name: "new_certificate", | |
| certificate: "cert/new_cert", | |
| } | |
| `, | |
| certificateOverride: "", | |
| expectedCertSigningFlags: "--lineage lineage.bin --rotation-min-sdk-version 32", | |
| expectedCertificate: "cert/new_cert", | |
| }, | |
| { | |
| name: "cert signing flags from filegroup", | |
| bp: ` | |
| android_app { | |
| name: "foo", | |
| srcs: ["a.java"], | |
| certificate: ":new_certificate", | |
| lineage: ":lineage_bin", | |
| rotationMinSdkVersion: "32", | |
| sdk_version: "current", | |
| } | |
| android_app_certificate { | |
| name: "new_certificate", | |
| certificate: "cert/new_cert", | |
| } | |
| filegroup { | |
| name: "lineage_bin", | |
| srcs: ["lineage.bin"], | |
| } | |
| `, | |
| certificateOverride: "", | |
| expectedCertSigningFlags: "--lineage lineage.bin --rotation-min-sdk-version 32", | |
| expectedCertificate: "cert/new_cert", | |
| }, | |
| { | |
| name: "missing with AllowMissingDependencies", | |
| bp: ` | |
| android_app { | |
| name: "foo", | |
| srcs: ["a.java"], | |
| certificate: ":new_certificate", | |
| sdk_version: "current", | |
| } | |
| `, | |
| expectedCertificate: "out/soong/.intermediates/foo/android_common/missing", | |
| allowMissingDependencies: true, | |
| }, | |
| } | |
| 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} | |
| } | |
| if test.allowMissingDependencies { | |
| variables.Allow_missing_dependencies = proptools.BoolPtr(true) | |
| } | |
| }), | |
| android.FixtureModifyContext(func(ctx *android.TestContext) { | |
| ctx.SetAllowMissingDependencies(test.allowMissingDependencies) | |
| }), | |
| ).RunTestWithBp(t, test.bp) | |
| foo := result.ModuleForTests("foo", "android_common") | |
| certificate := foo.Module().(*AndroidApp).certificate | |
| android.AssertPathRelativeToTopEquals(t, "certificates key", test.expectedCertificate+".pk8", certificate.Key) | |
| // The sign_target_files_apks and check_target_files_signatures | |
| // tools require that certificates have a .x509.pem extension. | |
| android.AssertPathRelativeToTopEquals(t, "certificates pem", test.expectedCertificate+".x509.pem", certificate.Pem) | |
| signapk := foo.Output("foo.apk") | |
| if signapk.Rule != android.ErrorRule { | |
| signCertificateFlags := signapk.Args["certificates"] | |
| expectedFlags := certificate.Pem.String() + " " + certificate.Key.String() | |
| android.AssertStringEquals(t, "certificates flags", expectedFlags, signCertificateFlags) | |
| certSigningFlags := signapk.Args["flags"] | |
| android.AssertStringEquals(t, "cert signing flags", test.expectedCertSigningFlags, certSigningFlags) | |
| } | |
| }) | |
| } | |
| } | |
| 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 via PRODUCT_PACKAGE_NAME_OVERRIDES", | |
| 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", | |
| }, | |
| }, | |
| { | |
| name: "overridden via stem", | |
| bp: ` | |
| android_app { | |
| name: "foo", | |
| srcs: ["a.java"], | |
| sdk_version: "current", | |
| stem: "bar", | |
| } | |
| `, | |
| packageNameOverride: "", | |
| expected: []string{ | |
| "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", | |
| rotationMinSdkVersion: "32", | |
| 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 | |
| certSigningFlags 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", | |
| certSigningFlags: "", | |
| 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", | |
| certSigningFlags: "--lineage lineage.bin --rotation-min-sdk-version 32", | |
| 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", | |
| certSigningFlags: "", | |
| 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", | |
| certSigningFlags: "", | |
| 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", | |
| certSigningFlags: "", | |
| 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", | |
| certSigningFlags: "", | |
| 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 cert signing flags | |
| certSigningFlags := signapk.Args["flags"] | |
| android.AssertStringEquals(t, "cert signing flags", expected.certSigningFlags, certSigningFlags) | |
| // Check if the overrides field values are correctly aggregated. | |
| mod := variant.Module().(*AndroidApp) | |
| android.AssertDeepEquals(t, "overrides property", expected.overrides, mod.overridableAppProperties.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 TestOverrideAndroidAppOverrides(t *testing.T) { | |
| ctx, _ := testJava( | |
| t, ` | |
| android_app { | |
| name: "foo", | |
| srcs: ["a.java"], | |
| sdk_version: "current", | |
| overrides: ["qux"] | |
| } | |
| android_app { | |
| name: "bar", | |
| srcs: ["b.java"], | |
| sdk_version: "current", | |
| overrides: ["foo"] | |
| } | |
| override_android_app { | |
| name: "foo_override", | |
| base: "foo", | |
| overrides: ["bar"] | |
| } | |
| `) | |
| expectedVariants := []struct { | |
| name string | |
| moduleName string | |
| variantName string | |
| overrides []string | |
| }{ | |
| { | |
| name: "foo", | |
| moduleName: "foo", | |
| variantName: "android_common", | |
| overrides: []string{"qux"}, | |
| }, | |
| { | |
| name: "bar", | |
| moduleName: "bar", | |
| variantName: "android_common", | |
| overrides: []string{"foo"}, | |
| }, | |
| { | |
| name: "foo", | |
| moduleName: "foo_override", | |
| variantName: "android_common_foo_override", | |
| overrides: []string{"bar", "foo"}, | |
| }, | |
| } | |
| for _, expected := range expectedVariants { | |
| variant := ctx.ModuleForTests(expected.name, expected.variantName) | |
| // Check if the overrides field values are correctly aggregated. | |
| mod := variant.Module().(*AndroidApp) | |
| android.AssertDeepEquals(t, "overrides property", expected.overrides, mod.overridableAppProperties.Overrides) | |
| } | |
| } | |
| func TestOverrideAndroidAppWithPrebuilt(t *testing.T) { | |
| result := PrepareForTestWithJavaDefaultModules.RunTestWithBp( | |
| t, ` | |
| android_app { | |
| name: "foo", | |
| srcs: ["a.java"], | |
| sdk_version: "current", | |
| } | |
| override_android_app { | |
| name: "bar", | |
| base: "foo", | |
| } | |
| android_app_import { | |
| name: "bar", | |
| prefer: true, | |
| apk: "bar.apk", | |
| presigned: true, | |
| } | |
| `) | |
| // An app that has an override that also has a prebuilt should not be hidden. | |
| foo := result.ModuleForTests("foo", "android_common") | |
| if foo.Module().IsHideFromMake() { | |
| t.Errorf("expected foo to have HideFromMake false") | |
| } | |
| // An override that also has a prebuilt should be hidden. | |
| barOverride := result.ModuleForTests("foo", "android_common_bar") | |
| if !barOverride.Module().IsHideFromMake() { | |
| t.Errorf("expected bar override variant of foo to have HideFromMake true") | |
| } | |
| } | |
| func TestOverrideAndroidAppStem(t *testing.T) { | |
| ctx, _ := testJava(t, ` | |
| android_app { | |
| name: "foo", | |
| srcs: ["a.java"], | |
| sdk_version: "current", | |
| } | |
| override_android_app { | |
| name: "bar", | |
| base: "foo", | |
| } | |
| override_android_app { | |
| name: "baz", | |
| base: "foo", | |
| stem: "baz_stem", | |
| } | |
| android_app { | |
| name: "foo2", | |
| srcs: ["a.java"], | |
| sdk_version: "current", | |
| stem: "foo2_stem", | |
| } | |
| override_android_app { | |
| name: "bar2", | |
| base: "foo2", | |
| } | |
| override_android_app { | |
| name: "baz2", | |
| base: "foo2", | |
| stem: "baz2_stem", | |
| } | |
| `) | |
| for _, expected := range []struct { | |
| moduleName string | |
| variantName string | |
| apkPath string | |
| }{ | |
| { | |
| moduleName: "foo", | |
| variantName: "android_common", | |
| apkPath: "out/soong/target/product/test_device/system/app/foo/foo.apk", | |
| }, | |
| { | |
| moduleName: "foo", | |
| variantName: "android_common_bar", | |
| apkPath: "out/soong/target/product/test_device/system/app/bar/bar.apk", | |
| }, | |
| { | |
| moduleName: "foo", | |
| variantName: "android_common_baz", | |
| apkPath: "out/soong/target/product/test_device/system/app/baz_stem/baz_stem.apk", | |
| }, | |
| { | |
| moduleName: "foo2", | |
| variantName: "android_common", | |
| apkPath: "out/soong/target/product/test_device/system/app/foo2_stem/foo2_stem.apk", | |
| }, | |
| { | |
| moduleName: "foo2", | |
| variantName: "android_common_bar2", | |
| // Note that this may cause the duplicate output error. | |
| apkPath: "out/soong/target/product/test_device/system/app/foo2_stem/foo2_stem.apk", | |
| }, | |
| { | |
| moduleName: "foo2", | |
| variantName: "android_common_baz2", | |
| apkPath: "out/soong/target/product/test_device/system/app/baz2_stem/baz2_stem.apk", | |
| }, | |
| } { | |
| variant := ctx.ModuleForTests(expected.moduleName, expected.variantName) | |
| variant.Output(expected.apkPath) | |
| } | |
| } | |
| 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.overridableAppProperties.Overrides) { | |
| t.Errorf("Incorrect overrides property value, expected: %q, got: %q", | |
| expected.overrides, mod.overridableAppProperties.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 TestInstrumentationTargetPrebuilt(t *testing.T) { | |
| bp := ` | |
| android_app_import { | |
| name: "foo", | |
| apk: "foo.apk", | |
| presigned: true, | |
| } | |
| android_test { | |
| name: "bar", | |
| srcs: ["a.java"], | |
| instrumentation_for: "foo", | |
| sdk_version: "current", | |
| } | |
| ` | |
| android.GroupFixturePreparers( | |
| PrepareForTestWithJavaDefaultModules, | |
| ).ExtendWithErrorHandler( | |
| android.FixtureExpectsAtLeastOneErrorMatchingPattern( | |
| "instrumentation_for: dependency \"foo\" of type \"android_app_import\" does not provide JavaInfo so is unsuitable for use with this property")). | |
| RunTestWithBp(t, bp) | |
| } | |
| 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(jniJarOutputPathString) | |
| 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. | |
| // These also include explicit `uses_libs`/`optional_uses_libs` entries, as they may be | |
| // propagated from dependencies. | |
| actualManifestFixerArgs := app.Output("manifest_fixer/AndroidManifest.xml").Args["args"] | |
| expectManifestFixerArgs := `--extract-native-libs=true ` + | |
| `--uses-library qux ` + | |
| `--uses-library quuz ` + | |
| `--uses-library foo ` + | |
| `--uses-library com.non.sdk.lib ` + | |
| `--uses-library runtime-library ` + | |
| `--uses-library runtime-required-x ` + | |
| `--uses-library runtime-required-y ` + | |
| `--optional-uses-library bar ` + | |
| `--optional-uses-library runtime-optional-x ` + | |
| `--optional-uses-library runtime-optional-y` | |
| android.AssertStringDoesContain(t, "manifest_fixer args", actualManifestFixerArgs, expectManifestFixerArgs) | |
| // 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 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") | |
| } | |
| } | |
| func TestTargetSdkVersionManifestFixer(t *testing.T) { | |
| platform_sdk_codename := "Tiramisu" | |
| testCases := []struct { | |
| name string | |
| targetSdkVersionInBp string | |
| targetSdkVersionExpected string | |
| unbundledBuild bool | |
| }{ | |
| { | |
| name: "Non-Unbundled build: Android.bp has targetSdkVersion", | |
| targetSdkVersionInBp: "30", | |
| targetSdkVersionExpected: "30", | |
| unbundledBuild: false, | |
| }, | |
| { | |
| name: "Unbundled build: Android.bp has targetSdkVersion", | |
| targetSdkVersionInBp: "30", | |
| targetSdkVersionExpected: "30", | |
| unbundledBuild: true, | |
| }, | |
| { | |
| name: "Non-Unbundled build: Android.bp has targetSdkVersion equal to platform_sdk_codename", | |
| targetSdkVersionInBp: platform_sdk_codename, | |
| targetSdkVersionExpected: platform_sdk_codename, | |
| unbundledBuild: false, | |
| }, | |
| { | |
| name: "Unbundled build: Android.bp has targetSdkVersion equal to platform_sdk_codename", | |
| targetSdkVersionInBp: platform_sdk_codename, | |
| targetSdkVersionExpected: "10000", | |
| unbundledBuild: true, | |
| }, | |
| { | |
| name: "Non-Unbundled build: Android.bp has no targetSdkVersion", | |
| targetSdkVersionExpected: platform_sdk_codename, | |
| unbundledBuild: false, | |
| }, | |
| { | |
| name: "Unbundled build: Android.bp has no targetSdkVersion", | |
| targetSdkVersionExpected: "10000", | |
| unbundledBuild: true, | |
| }, | |
| } | |
| for _, testCase := range testCases { | |
| bp := fmt.Sprintf(` | |
| android_app { | |
| name: "foo", | |
| sdk_version: "current", | |
| target_sdk_version: "%v", | |
| } | |
| `, testCase.targetSdkVersionInBp) | |
| fixture := android.GroupFixturePreparers( | |
| prepareForJavaTest, | |
| android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { | |
| // explicitly set platform_sdk_codename to make the test deterministic | |
| variables.Platform_sdk_codename = &platform_sdk_codename | |
| variables.Platform_version_active_codenames = []string{platform_sdk_codename} | |
| // create a non-empty list if unbundledBuild==true | |
| if testCase.unbundledBuild { | |
| variables.Unbundled_build_apps = []string{"apex_a", "apex_b"} | |
| } | |
| }), | |
| ) | |
| result := fixture.RunTestWithBp(t, bp) | |
| foo := result.ModuleForTests("foo", "android_common") | |
| manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"] | |
| android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion "+testCase.targetSdkVersionExpected) | |
| } | |
| } | |
| func TestDefaultAppTargetSdkVersionForUpdatableModules(t *testing.T) { | |
| platform_sdk_codename := "Tiramisu" | |
| platform_sdk_version := 33 | |
| testCases := []struct { | |
| name string | |
| platform_sdk_final bool | |
| targetSdkVersionInBp *string | |
| targetSdkVersionExpected *string | |
| updatable bool | |
| }{ | |
| { | |
| name: "Non-Updatable Module: Android.bp has older targetSdkVersion", | |
| targetSdkVersionInBp: proptools.StringPtr("29"), | |
| targetSdkVersionExpected: proptools.StringPtr("29"), | |
| updatable: false, | |
| }, | |
| { | |
| name: "Updatable Module: Android.bp has older targetSdkVersion", | |
| targetSdkVersionInBp: proptools.StringPtr("30"), | |
| targetSdkVersionExpected: proptools.StringPtr("30"), | |
| updatable: true, | |
| }, | |
| { | |
| name: "Updatable Module: Android.bp has no targetSdkVersion", | |
| targetSdkVersionExpected: proptools.StringPtr("10000"), | |
| updatable: true, | |
| }, | |
| { | |
| name: "[SDK finalised] Non-Updatable Module: Android.bp has older targetSdkVersion", | |
| platform_sdk_final: true, | |
| targetSdkVersionInBp: proptools.StringPtr("30"), | |
| targetSdkVersionExpected: proptools.StringPtr("30"), | |
| updatable: false, | |
| }, | |
| { | |
| name: "[SDK finalised] Updatable Module: Android.bp has older targetSdkVersion", | |
| platform_sdk_final: true, | |
| targetSdkVersionInBp: proptools.StringPtr("30"), | |
| targetSdkVersionExpected: proptools.StringPtr("30"), | |
| updatable: true, | |
| }, | |
| { | |
| name: "[SDK finalised] Updatable Module: Android.bp has targetSdkVersion as platform sdk codename", | |
| platform_sdk_final: true, | |
| targetSdkVersionInBp: proptools.StringPtr(platform_sdk_codename), | |
| targetSdkVersionExpected: proptools.StringPtr("33"), | |
| updatable: true, | |
| }, | |
| { | |
| name: "[SDK finalised] Updatable Module: Android.bp has no targetSdkVersion", | |
| platform_sdk_final: true, | |
| targetSdkVersionExpected: proptools.StringPtr("33"), | |
| updatable: true, | |
| }, | |
| } | |
| for _, testCase := range testCases { | |
| bp := fmt.Sprintf(` | |
| android_app { | |
| name: "foo", | |
| sdk_version: "current", | |
| min_sdk_version: "29", | |
| target_sdk_version: "%v", | |
| updatable: %t, | |
| enforce_default_target_sdk_version: %t | |
| } | |
| `, proptools.String(testCase.targetSdkVersionInBp), testCase.updatable, testCase.updatable) // enforce default target sdk version if app is updatable | |
| fixture := android.GroupFixturePreparers( | |
| PrepareForTestWithJavaDefaultModules, | |
| android.PrepareForTestWithAllowMissingDependencies, | |
| android.PrepareForTestWithAndroidMk, | |
| android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { | |
| // explicitly set following platform variables to make the test deterministic | |
| variables.Platform_sdk_final = &testCase.platform_sdk_final | |
| variables.Platform_sdk_version = &platform_sdk_version | |
| variables.Platform_sdk_codename = &platform_sdk_codename | |
| variables.Platform_version_active_codenames = []string{platform_sdk_codename} | |
| variables.Unbundled_build = proptools.BoolPtr(true) | |
| variables.Unbundled_build_apps = []string{"sampleModule"} | |
| }), | |
| ) | |
| result := fixture.RunTestWithBp(t, bp) | |
| foo := result.ModuleForTests("foo", "android_common") | |
| manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"] | |
| android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion "+*testCase.targetSdkVersionExpected) | |
| } | |
| } | |
| func TestEnforceDefaultAppTargetSdkVersionFlag(t *testing.T) { | |
| platform_sdk_codename := "Tiramisu" | |
| platform_sdk_version := 33 | |
| testCases := []struct { | |
| name string | |
| enforceDefaultTargetSdkVersion bool | |
| expectedError string | |
| platform_sdk_final bool | |
| targetSdkVersionInBp string | |
| targetSdkVersionExpected string | |
| updatable bool | |
| }{ | |
| { | |
| name: "Not enforcing Target SDK Version: Android.bp has older targetSdkVersion", | |
| enforceDefaultTargetSdkVersion: false, | |
| targetSdkVersionInBp: "29", | |
| targetSdkVersionExpected: "29", | |
| updatable: false, | |
| }, | |
| { | |
| name: "[SDK finalised] Enforce Target SDK Version: Android.bp has current targetSdkVersion", | |
| enforceDefaultTargetSdkVersion: true, | |
| platform_sdk_final: true, | |
| targetSdkVersionInBp: "current", | |
| targetSdkVersionExpected: "33", | |
| updatable: true, | |
| }, | |
| { | |
| name: "Enforce Target SDK Version: Android.bp has current targetSdkVersion", | |
| enforceDefaultTargetSdkVersion: true, | |
| platform_sdk_final: false, | |
| targetSdkVersionInBp: "current", | |
| targetSdkVersionExpected: "10000", | |
| updatable: false, | |
| }, | |
| { | |
| name: "Not enforcing Target SDK Version for Updatable app", | |
| enforceDefaultTargetSdkVersion: false, | |
| expectedError: "Updatable apps must enforce default target sdk version", | |
| targetSdkVersionInBp: "29", | |
| targetSdkVersionExpected: "29", | |
| updatable: true, | |
| }, | |
| } | |
| for _, testCase := range testCases { | |
| errExpected := testCase.expectedError != "" | |
| bp := fmt.Sprintf(` | |
| android_app { | |
| name: "foo", | |
| enforce_default_target_sdk_version: %t, | |
| sdk_version: "current", | |
| min_sdk_version: "29", | |
| target_sdk_version: "%v", | |
| updatable: %t | |
| } | |
| `, testCase.enforceDefaultTargetSdkVersion, testCase.targetSdkVersionInBp, testCase.updatable) | |
| fixture := android.GroupFixturePreparers( | |
| PrepareForTestWithJavaDefaultModules, | |
| android.PrepareForTestWithAllowMissingDependencies, | |
| android.PrepareForTestWithAndroidMk, | |
| android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { | |
| // explicitly set following platform variables to make the test deterministic | |
| variables.Platform_sdk_final = &testCase.platform_sdk_final | |
| variables.Platform_sdk_version = &platform_sdk_version | |
| variables.Platform_sdk_codename = &platform_sdk_codename | |
| variables.Unbundled_build = proptools.BoolPtr(true) | |
| variables.Unbundled_build_apps = []string{"sampleModule"} | |
| }), | |
| ) | |
| errorHandler := android.FixtureExpectsNoErrors | |
| if errExpected { | |
| errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(testCase.expectedError) | |
| } | |
| result := fixture.ExtendWithErrorHandler(errorHandler).RunTestWithBp(t, bp) | |
| if !errExpected { | |
| foo := result.ModuleForTests("foo", "android_common") | |
| manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"] | |
| android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion "+testCase.targetSdkVersionExpected) | |
| } | |
| } | |
| } | |
| func TestEnforceDefaultAppTargetSdkVersionFlagForTests(t *testing.T) { | |
| platform_sdk_codename := "Tiramisu" | |
| platform_sdk_version := 33 | |
| testCases := []struct { | |
| name string | |
| enforceDefaultTargetSdkVersion bool | |
| expectedError string | |
| platform_sdk_final bool | |
| targetSdkVersionInBp string | |
| targetSdkVersionExpected string | |
| }{ | |
| { | |
| name: "Not enforcing Target SDK Version: Android.bp has older targetSdkVersion", | |
| enforceDefaultTargetSdkVersion: false, | |
| targetSdkVersionInBp: "29", | |
| targetSdkVersionExpected: "29", | |
| }, | |
| { | |
| name: "[SDK finalised] Enforce Target SDK Version: Android.bp has current targetSdkVersion", | |
| enforceDefaultTargetSdkVersion: true, | |
| platform_sdk_final: true, | |
| targetSdkVersionInBp: "current", | |
| targetSdkVersionExpected: "33", | |
| }, | |
| { | |
| name: "Enforce Target SDK Version: Android.bp has current targetSdkVersion", | |
| enforceDefaultTargetSdkVersion: true, | |
| platform_sdk_final: false, | |
| targetSdkVersionInBp: "current", | |
| targetSdkVersionExpected: "10000", | |
| }, | |
| } | |
| for _, testCase := range testCases { | |
| errExpected := testCase.expectedError != "" | |
| bp := fmt.Sprintf(` | |
| android_test { | |
| name: "foo", | |
| enforce_default_target_sdk_version: %t, | |
| min_sdk_version: "29", | |
| target_sdk_version: "%v", | |
| } | |
| `, testCase.enforceDefaultTargetSdkVersion, testCase.targetSdkVersionInBp) | |
| fixture := android.GroupFixturePreparers( | |
| PrepareForTestWithJavaDefaultModules, | |
| android.PrepareForTestWithAllowMissingDependencies, | |
| android.PrepareForTestWithAndroidMk, | |
| android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { | |
| // explicitly set following platform variables to make the test deterministic | |
| variables.Platform_sdk_final = &testCase.platform_sdk_final | |
| variables.Platform_sdk_version = &platform_sdk_version | |
| variables.Platform_sdk_codename = &platform_sdk_codename | |
| variables.Unbundled_build = proptools.BoolPtr(true) | |
| variables.Unbundled_build_apps = []string{"sampleModule"} | |
| }), | |
| ) | |
| errorHandler := android.FixtureExpectsNoErrors | |
| if errExpected { | |
| errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(testCase.expectedError) | |
| } | |
| result := fixture.ExtendWithErrorHandler(errorHandler).RunTestWithBp(t, bp) | |
| if !errExpected { | |
| foo := result.ModuleForTests("foo", "android_common") | |
| manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"] | |
| android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion "+testCase.targetSdkVersionExpected) | |
| } | |
| } | |
| } | |
| func TestAppMissingCertificateAllowMissingDependencies(t *testing.T) { | |
| result := android.GroupFixturePreparers( | |
| PrepareForTestWithJavaDefaultModules, | |
| android.PrepareForTestWithAllowMissingDependencies, | |
| android.PrepareForTestWithAndroidMk, | |
| ).RunTestWithBp(t, ` | |
| android_app { | |
| name: "foo", | |
| srcs: ["a.java"], | |
| certificate: ":missing_certificate", | |
| sdk_version: "current", | |
| }`) | |
| foo := result.ModuleForTests("foo", "android_common") | |
| fooApk := foo.Output("foo.apk") | |
| if fooApk.Rule != android.ErrorRule { | |
| t.Fatalf("expected ErrorRule for foo.apk, got %s", fooApk.Rule.String()) | |
| } | |
| android.AssertStringDoesContain(t, "expected error rule message", fooApk.Args["error"], "missing dependencies: missing_certificate\n") | |
| } | |
| func TestAppIncludesJniPackages(t *testing.T) { | |
| ctx := android.GroupFixturePreparers( | |
| PrepareForTestWithJavaDefaultModules, | |
| ).RunTestWithBp(t, ` | |
| android_library_import { | |
| name: "aary-nodeps", | |
| aars: ["aary.aar"], | |
| extract_jni: true, | |
| } | |
| android_library { | |
| name: "aary-lib", | |
| sdk_version: "current", | |
| min_sdk_version: "21", | |
| static_libs: ["aary-nodeps"], | |
| } | |
| android_app { | |
| name: "aary-lib-dep", | |
| sdk_version: "current", | |
| min_sdk_version: "21", | |
| manifest: "AndroidManifest.xml", | |
| static_libs: ["aary-lib"], | |
| use_embedded_native_libs: true, | |
| } | |
| android_app { | |
| name: "aary-import-dep", | |
| sdk_version: "current", | |
| min_sdk_version: "21", | |
| manifest: "AndroidManifest.xml", | |
| static_libs: ["aary-nodeps"], | |
| use_embedded_native_libs: true, | |
| } | |
| android_app { | |
| name: "aary-no-use-embedded", | |
| sdk_version: "current", | |
| min_sdk_version: "21", | |
| manifest: "AndroidManifest.xml", | |
| static_libs: ["aary-nodeps"], | |
| }`) | |
| testCases := []struct { | |
| name string | |
| hasPackage bool | |
| }{ | |
| { | |
| name: "aary-import-dep", | |
| hasPackage: true, | |
| }, | |
| { | |
| name: "aary-lib-dep", | |
| hasPackage: true, | |
| }, | |
| { | |
| name: "aary-no-use-embedded", | |
| hasPackage: false, | |
| }, | |
| } | |
| for _, tc := range testCases { | |
| t.Run(tc.name, func(t *testing.T) { | |
| app := ctx.ModuleForTests(tc.name, "android_common") | |
| outputFile := "jnilibs.zip" | |
| jniOutputLibZip := app.MaybeOutput(outputFile) | |
| if jniOutputLibZip.Rule == nil && !tc.hasPackage { | |
| return | |
| } | |
| jniPackage := "arm64-v8a_jni.zip" | |
| inputs := jniOutputLibZip.Inputs | |
| foundPackage := false | |
| for i := 0; i < len(inputs); i++ { | |
| if strings.Contains(inputs[i].String(), jniPackage) { | |
| foundPackage = true | |
| } | |
| } | |
| if foundPackage != tc.hasPackage { | |
| t.Errorf("expected to find %v in %v inputs; inputs = %v", jniPackage, outputFile, inputs) | |
| } | |
| }) | |
| } | |
| } | |
| func TestTargetSdkVersionMtsTests(t *testing.T) { | |
| platformSdkCodename := "Tiramisu" | |
| android_test := "android_test" | |
| android_test_helper_app := "android_test_helper_app" | |
| bpTemplate := ` | |
| %v { | |
| name: "mytest", | |
| target_sdk_version: "%v", | |
| test_suites: ["othersuite", "%v"], | |
| } | |
| ` | |
| testCases := []struct { | |
| desc string | |
| moduleType string | |
| targetSdkVersionInBp string | |
| targetSdkVersionExpected string | |
| testSuites string | |
| }{ | |
| { | |
| desc: "Non-MTS android_test_apps targeting current should not be upgraded to 10000", | |
| moduleType: android_test, | |
| targetSdkVersionInBp: "current", | |
| targetSdkVersionExpected: platformSdkCodename, | |
| testSuites: "non-mts-suite", | |
| }, | |
| { | |
| desc: "MTS android_test_apps targeting released sdks should not be upgraded to 10000", | |
| moduleType: android_test, | |
| targetSdkVersionInBp: "29", | |
| targetSdkVersionExpected: "29", | |
| testSuites: "mts-suite", | |
| }, | |
| { | |
| desc: "MTS android_test_apps targeting current should be upgraded to 10000", | |
| moduleType: android_test, | |
| targetSdkVersionInBp: "current", | |
| targetSdkVersionExpected: "10000", | |
| testSuites: "mts-suite", | |
| }, | |
| { | |
| desc: "MTS android_test_helper_apps targeting current should be upgraded to 10000", | |
| moduleType: android_test_helper_app, | |
| targetSdkVersionInBp: "current", | |
| targetSdkVersionExpected: "10000", | |
| testSuites: "mts-suite", | |
| }, | |
| } | |
| fixture := android.GroupFixturePreparers( | |
| prepareForJavaTest, | |
| android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { | |
| variables.Platform_sdk_codename = &platformSdkCodename | |
| variables.Platform_version_active_codenames = []string{platformSdkCodename} | |
| }), | |
| ) | |
| for _, testCase := range testCases { | |
| result := fixture.RunTestWithBp(t, fmt.Sprintf(bpTemplate, testCase.moduleType, testCase.targetSdkVersionInBp, testCase.testSuites)) | |
| mytest := result.ModuleForTests("mytest", "android_common") | |
| manifestFixerArgs := mytest.Output("manifest_fixer/AndroidManifest.xml").Args["args"] | |
| android.AssertStringDoesContain(t, testCase.desc, manifestFixerArgs, "--targetSdkVersion "+testCase.targetSdkVersionExpected) | |
| } | |
| } |