// Copyright 2020 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 ( "reflect" "strings" "testing" "android/soong/android" ) func TestRuntimeResourceOverlay(t *testing.T) { fs := map[string][]byte{ "baz/res/res/values/strings.xml": nil, "bar/res/res/values/strings.xml": nil, } bp := ` runtime_resource_overlay { name: "foo", certificate: "platform", lineage: "lineage.bin", product_specific: true, static_libs: ["bar"], resource_libs: ["baz"], aaptflags: ["--keep-raw-values"], } runtime_resource_overlay { name: "foo_themed", certificate: "platform", product_specific: true, theme: "faza", overrides: ["foo"], } android_library { name: "bar", resource_dirs: ["bar/res"], } android_app { name: "baz", sdk_version: "current", resource_dirs: ["baz/res"], } ` config := testAppConfig(nil, bp, fs) ctx := testContext(config) run(t, ctx, config) m := ctx.ModuleForTests("foo", "android_common") // Check AAPT2 link flags. aapt2Flags := m.Output("package-res.apk").Args["flags"] expectedFlags := []string{"--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"} absentFlags := android.RemoveListFromList(expectedFlags, strings.Split(aapt2Flags, " ")) if len(absentFlags) > 0 { t.Errorf("expected values, %q are missing in aapt2 link flags, %q", absentFlags, aapt2Flags) } // Check overlay.list output for static_libs dependency. overlayList := m.Output("aapt2/overlay.list").Inputs.Strings() staticLibPackage := buildDir + "/.intermediates/bar/android_common/package-res.apk" if !inList(staticLibPackage, overlayList) { t.Errorf("Stactic lib res package %q missing in overlay list: %q", staticLibPackage, overlayList) } // Check AAPT2 link flags for resource_libs dependency. resourceLibFlag := "-I " + buildDir + "/.intermediates/baz/android_common/package-res.apk" if !strings.Contains(aapt2Flags, resourceLibFlag) { t.Errorf("Resource lib flag %q missing in aapt2 link flags: %q", resourceLibFlag, aapt2Flags) } // Check cert signing flag. signedApk := m.Output("signed/foo.apk") lineageFlag := signedApk.Args["flags"] expectedLineageFlag := "--lineage lineage.bin" if expectedLineageFlag != lineageFlag { t.Errorf("Incorrect signing lineage flags, expected: %q, got: %q", expectedLineageFlag, lineageFlag) } signingFlag := signedApk.Args["certificates"] expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8" if expected != signingFlag { t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag) } androidMkEntries := android.AndroidMkEntriesForTest(t, ctx, m.Module())[0] path := androidMkEntries.EntryMap["LOCAL_CERTIFICATE"] expectedPath := []string{"build/make/target/product/security/platform.x509.pem"} if !reflect.DeepEqual(path, expectedPath) { t.Errorf("Unexpected LOCAL_CERTIFICATE value: %v, expected: %v", path, expectedPath) } // Check device location. path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"] expectedPath = []string{"/tmp/target/product/test_device/product/overlay"} if !reflect.DeepEqual(path, expectedPath) { t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath) } // A themed module has a different device location m = ctx.ModuleForTests("foo_themed", "android_common") androidMkEntries = android.AndroidMkEntriesForTest(t, ctx, m.Module())[0] path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"] expectedPath = []string{"/tmp/target/product/test_device/product/overlay/faza"} if !reflect.DeepEqual(path, expectedPath) { t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath) } overrides := androidMkEntries.EntryMap["LOCAL_OVERRIDES_PACKAGES"] expectedOverrides := []string{"foo"} if !reflect.DeepEqual(overrides, expectedOverrides) { t.Errorf("Unexpected LOCAL_OVERRIDES_PACKAGES value: %v, expected: %v", overrides, expectedOverrides) } } func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) { ctx, _ := testJava(t, ` java_defaults { name: "rro_defaults", theme: "default_theme", product_specific: true, aaptflags: ["--keep-raw-values"], } runtime_resource_overlay { name: "foo_with_defaults", defaults: ["rro_defaults"], } runtime_resource_overlay { name: "foo_barebones", } `) // // RRO module with defaults // m := ctx.ModuleForTests("foo_with_defaults", "android_common") // Check AAPT2 link flags. aapt2Flags := strings.Split(m.Output("package-res.apk").Args["flags"], " ") expectedFlags := []string{"--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"} absentFlags := android.RemoveListFromList(expectedFlags, aapt2Flags) if len(absentFlags) > 0 { t.Errorf("expected values, %q are missing in aapt2 link flags, %q", absentFlags, aapt2Flags) } // Check device location. path := android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"] expectedPath := []string{"/tmp/target/product/test_device/product/overlay/default_theme"} if !reflect.DeepEqual(path, expectedPath) { t.Errorf("Unexpected LOCAL_MODULE_PATH value: %q, expected: %q", path, expectedPath) } // // RRO module without defaults // m = ctx.ModuleForTests("foo_barebones", "android_common") // Check AAPT2 link flags. aapt2Flags = strings.Split(m.Output("package-res.apk").Args["flags"], " ") unexpectedFlags := "--keep-raw-values" if inList(unexpectedFlags, aapt2Flags) { t.Errorf("unexpected value, %q is present in aapt2 link flags, %q", unexpectedFlags, aapt2Flags) } // Check device location. path = android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"] expectedPath = []string{"/tmp/target/product/test_device/system/overlay"} if !reflect.DeepEqual(path, expectedPath) { t.Errorf("Unexpected LOCAL_MODULE_PATH value: %v, expected: %v", path, expectedPath) } } func TestOverrideRuntimeResourceOverlay(t *testing.T) { ctx, _ := testJava(t, ` runtime_resource_overlay { name: "foo_overlay", certificate: "platform", product_specific: true, sdk_version: "current", } override_runtime_resource_overlay { name: "bar_overlay", base: "foo_overlay", package_name: "com.android.bar.overlay", target_package_name: "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/product/overlay/foo_overlay.apk", overrides: nil, targetVariant: "android_common", packageFlag: "", targetPackageFlag: "", }, { variantName: "android_common_bar_overlay", apkPath: "/target/product/test_device/product/overlay/bar_overlay.apk", overrides: []string{"foo_overlay"}, targetVariant: "android_common_bar", packageFlag: "com.android.bar.overlay", targetPackageFlag: "com.android.bar", }, } for _, expected := range expectedVariants { variant := ctx.ModuleForTests("foo_overlay", expected.variantName) // Check the final apk name outputs := variant.AllOutputs() expectedApkPath := buildDir + expected.apkPath found := false for _, o := range outputs { if o == expectedApkPath { found = true break } } if !found { t.Errorf("Can't find %q in output files.\nAll outputs:%v", expectedApkPath, outputs) } // Check if the overrides field values are correctly aggregated. mod := variant.Module().(*RuntimeResourceOverlay) if !reflect.DeepEqual(expected.overrides, mod.properties.Overrides) { t.Errorf("Incorrect overrides property value, expected: %q, got: %q", expected.overrides, mod.properties.Overrides) } // 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", "") checkAapt2LinkFlag(t, aapt2Flags, "rename-overlay-target-package", expected.targetPackageFlag) } } func TestEnforceRRO_propagatesToDependencies(t *testing.T) { testCases := []struct { name string enforceRROTargets []string rroDirs map[string][]string }{ { name: "no RRO", enforceRROTargets: nil, rroDirs: map[string][]string{ "foo": nil, "bar": nil, }, }, { name: "enforce RRO on all", enforceRROTargets: []string{"*"}, rroDirs: map[string][]string{ "foo": {"product/vendor/blah/overlay/lib2/res"}, "bar": {"product/vendor/blah/overlay/lib2/res"}, }, }, { name: "enforce RRO on foo", enforceRROTargets: []string{"foo"}, rroDirs: map[string][]string{ "foo": {"product/vendor/blah/overlay/lib2/res"}, "bar": {"product/vendor/blah/overlay/lib2/res"}, }, }, } productResourceOverlays := []string{ "product/vendor/blah/overlay", } fs := map[string][]byte{ "lib2/res/values/strings.xml": nil, "product/vendor/blah/overlay/lib2/res/values/strings.xml": nil, } bp := ` android_app { name: "foo", sdk_version: "current", resource_dirs: [], static_libs: ["lib"], } android_app { name: "bar", sdk_version: "current", resource_dirs: [], static_libs: ["lib"], } android_library { name: "lib", sdk_version: "current", resource_dirs: [], static_libs: ["lib2"], } android_library { name: "lib2", sdk_version: "current", resource_dirs: ["lib2/res"], } ` for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { config := testAppConfig(nil, bp, fs) config.TestProductVariables.ProductResourceOverlays = productResourceOverlays if testCase.enforceRROTargets != nil { config.TestProductVariables.EnforceRROTargets = testCase.enforceRROTargets } ctx := testContext(config) run(t, ctx, config) modules := []string{"foo", "bar"} for _, moduleName := range modules { module := ctx.ModuleForTests(moduleName, "android_common") mkEntries := android.AndroidMkEntriesForTest(t, ctx, module.Module())[0] actualRRODirs := mkEntries.EntryMap["LOCAL_SOONG_PRODUCT_RRO_DIRS"] if !reflect.DeepEqual(actualRRODirs, testCase.rroDirs[moduleName]) { t.Errorf("exected %s LOCAL_SOONG_PRODUCT_RRO_DIRS entry: %v\ngot:%q", moduleName, testCase.rroDirs[moduleName], actualRRODirs) } } }) } }