1// Copyright 2020 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package java
16
17import (
18	"reflect"
19	"strings"
20	"testing"
21
22	"android/soong/android"
23	"android/soong/shared"
24)
25
26func TestRuntimeResourceOverlay(t *testing.T) {
27	fs := android.MockFS{
28		"baz/res/res/values/strings.xml": nil,
29		"bar/res/res/values/strings.xml": nil,
30	}
31	bp := `
32		runtime_resource_overlay {
33			name: "foo",
34			certificate: "platform",
35			lineage: "lineage.bin",
36			product_specific: true,
37			static_libs: ["bar"],
38			resource_libs: ["baz"],
39			aaptflags: ["--keep-raw-values"],
40		}
41
42		runtime_resource_overlay {
43			name: "foo_themed",
44			certificate: "platform",
45			product_specific: true,
46			theme: "faza",
47			overrides: ["foo"],
48		}
49
50		android_library {
51			name: "bar",
52			resource_dirs: ["bar/res"],
53		}
54
55		android_app {
56			name: "baz",
57			sdk_version: "current",
58			resource_dirs: ["baz/res"],
59		}
60	`
61
62	result := android.GroupFixturePreparers(
63		PrepareForTestWithJavaDefaultModules,
64		PrepareForTestWithOverlayBuildComponents,
65		fs.AddToFixture(),
66	).RunTestWithBp(t, bp)
67
68	m := result.ModuleForTests("foo", "android_common")
69
70	// Check AAPT2 link flags.
71	aapt2Flags := m.Output("package-res.apk").Args["flags"]
72	expectedFlags := []string{"--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"}
73	absentFlags := android.RemoveListFromList(expectedFlags, strings.Split(aapt2Flags, " "))
74	if len(absentFlags) > 0 {
75		t.Errorf("expected values, %q are missing in aapt2 link flags, %q", absentFlags, aapt2Flags)
76	}
77
78	// Check overlay.list output for static_libs dependency.
79	overlayList := android.PathsRelativeToTop(m.Output("aapt2/overlay.list").Inputs)
80	staticLibPackage := "out/soong/.intermediates/bar/android_common/package-res.apk"
81	if !inList(staticLibPackage, overlayList) {
82		t.Errorf("Stactic lib res package %q missing in overlay list: %q", staticLibPackage, overlayList)
83	}
84
85	// Check AAPT2 link flags for resource_libs dependency.
86	resourceLibFlag := "-I " + "out/soong/.intermediates/baz/android_common/package-res.apk"
87	if !strings.Contains(aapt2Flags, resourceLibFlag) {
88		t.Errorf("Resource lib flag %q missing in aapt2 link flags: %q", resourceLibFlag, aapt2Flags)
89	}
90
91	// Check cert signing flag.
92	signedApk := m.Output("signed/foo.apk")
93	lineageFlag := signedApk.Args["flags"]
94	expectedLineageFlag := "--lineage lineage.bin"
95	if expectedLineageFlag != lineageFlag {
96		t.Errorf("Incorrect signing lineage flags, expected: %q, got: %q", expectedLineageFlag, lineageFlag)
97	}
98	signingFlag := signedApk.Args["certificates"]
99	expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8"
100	if expected != signingFlag {
101		t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
102	}
103	androidMkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0]
104	path := androidMkEntries.EntryMap["LOCAL_CERTIFICATE"]
105	expectedPath := []string{"build/make/target/product/security/platform.x509.pem"}
106	if !reflect.DeepEqual(path, expectedPath) {
107		t.Errorf("Unexpected LOCAL_CERTIFICATE value: %v, expected: %v", path, expectedPath)
108	}
109
110	// Check device location.
111	path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
112	expectedPath = []string{shared.JoinPath("out/target/product/test_device/product/overlay")}
113	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path)
114
115	// A themed module has a different device location
116	m = result.ModuleForTests("foo_themed", "android_common")
117	androidMkEntries = android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0]
118	path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
119	expectedPath = []string{shared.JoinPath("out/target/product/test_device/product/overlay/faza")}
120	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path)
121
122	overrides := androidMkEntries.EntryMap["LOCAL_OVERRIDES_PACKAGES"]
123	expectedOverrides := []string{"foo"}
124	if !reflect.DeepEqual(overrides, expectedOverrides) {
125		t.Errorf("Unexpected LOCAL_OVERRIDES_PACKAGES value: %v, expected: %v", overrides, expectedOverrides)
126	}
127}
128
129func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) {
130	ctx, config := testJava(t, `
131		java_defaults {
132			name: "rro_defaults",
133			theme: "default_theme",
134			product_specific: true,
135			aaptflags: ["--keep-raw-values"],
136		}
137
138		runtime_resource_overlay {
139			name: "foo_with_defaults",
140			defaults: ["rro_defaults"],
141		}
142
143		runtime_resource_overlay {
144			name: "foo_barebones",
145		}
146		`)
147
148	//
149	// RRO module with defaults
150	//
151	m := ctx.ModuleForTests("foo_with_defaults", "android_common")
152
153	// Check AAPT2 link flags.
154	aapt2Flags := strings.Split(m.Output("package-res.apk").Args["flags"], " ")
155	expectedFlags := []string{"--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"}
156	absentFlags := android.RemoveListFromList(expectedFlags, aapt2Flags)
157	if len(absentFlags) > 0 {
158		t.Errorf("expected values, %q are missing in aapt2 link flags, %q", absentFlags, aapt2Flags)
159	}
160
161	// Check device location.
162	path := android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
163	expectedPath := []string{shared.JoinPath("out/target/product/test_device/product/overlay/default_theme")}
164	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", config, expectedPath, path)
165
166	//
167	// RRO module without defaults
168	//
169	m = ctx.ModuleForTests("foo_barebones", "android_common")
170
171	// Check AAPT2 link flags.
172	aapt2Flags = strings.Split(m.Output("package-res.apk").Args["flags"], " ")
173	unexpectedFlags := "--keep-raw-values"
174	if inList(unexpectedFlags, aapt2Flags) {
175		t.Errorf("unexpected value, %q is present in aapt2 link flags, %q", unexpectedFlags, aapt2Flags)
176	}
177
178	// Check device location.
179	path = android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
180	expectedPath = []string{shared.JoinPath("out/target/product/test_device/system/overlay")}
181	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", config, expectedPath, path)
182}
183
184func TestOverrideRuntimeResourceOverlay(t *testing.T) {
185	ctx, _ := testJava(t, `
186		runtime_resource_overlay {
187			name: "foo_overlay",
188			certificate: "platform",
189			product_specific: true,
190			sdk_version: "current",
191		}
192
193		override_runtime_resource_overlay {
194			name: "bar_overlay",
195			base: "foo_overlay",
196			package_name: "com.android.bar.overlay",
197			target_package_name: "com.android.bar",
198		}
199		`)
200
201	expectedVariants := []struct {
202		moduleName        string
203		variantName       string
204		apkPath           string
205		overrides         []string
206		targetVariant     string
207		packageFlag       string
208		targetPackageFlag string
209	}{
210		{
211			variantName:       "android_common",
212			apkPath:           "out/soong/target/product/test_device/product/overlay/foo_overlay.apk",
213			overrides:         nil,
214			targetVariant:     "android_common",
215			packageFlag:       "",
216			targetPackageFlag: "",
217		},
218		{
219			variantName:       "android_common_bar_overlay",
220			apkPath:           "out/soong/target/product/test_device/product/overlay/bar_overlay.apk",
221			overrides:         []string{"foo_overlay"},
222			targetVariant:     "android_common_bar",
223			packageFlag:       "com.android.bar.overlay",
224			targetPackageFlag: "com.android.bar",
225		},
226	}
227	for _, expected := range expectedVariants {
228		variant := ctx.ModuleForTests("foo_overlay", expected.variantName)
229
230		// Check the final apk name
231		variant.Output(expected.apkPath)
232
233		// Check if the overrides field values are correctly aggregated.
234		mod := variant.Module().(*RuntimeResourceOverlay)
235		if !reflect.DeepEqual(expected.overrides, mod.properties.Overrides) {
236			t.Errorf("Incorrect overrides property value, expected: %q, got: %q",
237				expected.overrides, mod.properties.Overrides)
238		}
239
240		// Check aapt2 flags.
241		res := variant.Output("package-res.apk")
242		aapt2Flags := res.Args["flags"]
243		checkAapt2LinkFlag(t, aapt2Flags, "rename-manifest-package", expected.packageFlag)
244		checkAapt2LinkFlag(t, aapt2Flags, "rename-resources-package", "")
245		checkAapt2LinkFlag(t, aapt2Flags, "rename-overlay-target-package", expected.targetPackageFlag)
246	}
247}
248
249func TestEnforceRRO_propagatesToDependencies(t *testing.T) {
250	testCases := []struct {
251		name              string
252		enforceRROTargets []string
253		rroDirs           map[string][]string
254	}{
255		{
256			name:              "no RRO",
257			enforceRROTargets: nil,
258			rroDirs: map[string][]string{
259				"foo": nil,
260				"bar": nil,
261			},
262		},
263		{
264			name:              "enforce RRO on all",
265			enforceRROTargets: []string{"*"},
266			rroDirs: map[string][]string{
267				"foo": {"product/vendor/blah/overlay/lib2/res"},
268				"bar": {"product/vendor/blah/overlay/lib2/res"},
269			},
270		},
271		{
272			name:              "enforce RRO on foo",
273			enforceRROTargets: []string{"foo"},
274			rroDirs: map[string][]string{
275				"foo": {"product/vendor/blah/overlay/lib2/res"},
276				"bar": {"product/vendor/blah/overlay/lib2/res"},
277			},
278		},
279	}
280
281	productResourceOverlays := []string{
282		"product/vendor/blah/overlay",
283	}
284
285	fs := android.MockFS{
286		"lib2/res/values/strings.xml":                             nil,
287		"product/vendor/blah/overlay/lib2/res/values/strings.xml": nil,
288	}
289
290	bp := `
291			android_app {
292				name: "foo",
293				sdk_version: "current",
294				resource_dirs: [],
295				static_libs: ["lib"],
296			}
297
298			android_app {
299				name: "bar",
300				sdk_version: "current",
301				resource_dirs: [],
302				static_libs: ["lib"],
303			}
304
305			android_library {
306				name: "lib",
307				sdk_version: "current",
308				resource_dirs: [],
309				static_libs: ["lib2"],
310			}
311
312			android_library {
313				name: "lib2",
314				sdk_version: "current",
315				resource_dirs: ["lib2/res"],
316			}
317		`
318
319	for _, testCase := range testCases {
320		t.Run(testCase.name, func(t *testing.T) {
321			result := android.GroupFixturePreparers(
322				PrepareForTestWithJavaDefaultModules,
323				PrepareForTestWithOverlayBuildComponents,
324				fs.AddToFixture(),
325				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
326					variables.ProductResourceOverlays = productResourceOverlays
327					if testCase.enforceRROTargets != nil {
328						variables.EnforceRROTargets = testCase.enforceRROTargets
329					}
330				}),
331			).RunTestWithBp(t, bp)
332
333			modules := []string{"foo", "bar"}
334			for _, moduleName := range modules {
335				module := result.ModuleForTests(moduleName, "android_common")
336				mkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, module.Module())[0]
337				actualRRODirs := mkEntries.EntryMap["LOCAL_SOONG_PRODUCT_RRO_DIRS"]
338				if !reflect.DeepEqual(actualRRODirs, testCase.rroDirs[moduleName]) {
339					t.Errorf("exected %s LOCAL_SOONG_PRODUCT_RRO_DIRS entry: %v\ngot:%q",
340						moduleName, testCase.rroDirs[moduleName], actualRRODirs)
341				}
342			}
343		})
344	}
345}
346