• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2017 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	"android/soong/android"
19	"android/soong/cc"
20
21	"fmt"
22	"path/filepath"
23	"reflect"
24	"sort"
25	"strings"
26	"testing"
27
28	"github.com/google/blueprint/proptools"
29)
30
31var (
32	resourceFiles = []string{
33		"res/layout/layout.xml",
34		"res/values/strings.xml",
35		"res/values-en-rUS/strings.xml",
36	}
37
38	compiledResourceFiles = []string{
39		"aapt2/res/layout_layout.xml.flat",
40		"aapt2/res/values_strings.arsc.flat",
41		"aapt2/res/values-en-rUS_strings.arsc.flat",
42	}
43)
44
45func testAppContext(config android.Config, bp string, fs map[string][]byte) *android.TestContext {
46	appFS := map[string][]byte{}
47	for k, v := range fs {
48		appFS[k] = v
49	}
50
51	for _, file := range resourceFiles {
52		appFS[file] = nil
53	}
54
55	return testContext(config, bp, appFS)
56}
57
58func testApp(t *testing.T, bp string) *android.TestContext {
59	config := testConfig(nil)
60
61	ctx := testAppContext(config, bp, nil)
62
63	run(t, ctx, config)
64
65	return ctx
66}
67
68func TestApp(t *testing.T) {
69	for _, moduleType := range []string{"android_app", "android_library"} {
70		t.Run(moduleType, func(t *testing.T) {
71			ctx := testApp(t, moduleType+` {
72					name: "foo",
73					srcs: ["a.java"],
74				}
75			`)
76
77			foo := ctx.ModuleForTests("foo", "android_common")
78
79			var expectedLinkImplicits []string
80
81			manifestFixer := foo.Output("manifest_fixer/AndroidManifest.xml")
82			expectedLinkImplicits = append(expectedLinkImplicits, manifestFixer.Output.String())
83
84			frameworkRes := ctx.ModuleForTests("framework-res", "android_common")
85			expectedLinkImplicits = append(expectedLinkImplicits,
86				frameworkRes.Output("package-res.apk").Output.String())
87
88			// Test the mapping from input files to compiled output file names
89			compile := foo.Output(compiledResourceFiles[0])
90			if !reflect.DeepEqual(resourceFiles, compile.Inputs.Strings()) {
91				t.Errorf("expected aapt2 compile inputs expected:\n  %#v\n got:\n  %#v",
92					resourceFiles, compile.Inputs.Strings())
93			}
94
95			compiledResourceOutputs := compile.Outputs.Strings()
96			sort.Strings(compiledResourceOutputs)
97
98			expectedLinkImplicits = append(expectedLinkImplicits, compiledResourceOutputs...)
99
100			list := foo.Output("aapt2/res.list")
101			expectedLinkImplicits = append(expectedLinkImplicits, list.Output.String())
102
103			// Check that the link rule uses
104			res := ctx.ModuleForTests("foo", "android_common").Output("package-res.apk")
105			if !reflect.DeepEqual(expectedLinkImplicits, res.Implicits.Strings()) {
106				t.Errorf("expected aapt2 link implicits expected:\n  %#v\n got:\n  %#v",
107					expectedLinkImplicits, res.Implicits.Strings())
108			}
109		})
110	}
111}
112
113func TestAppSplits(t *testing.T) {
114	ctx := testApp(t, `
115				android_app {
116					name: "foo",
117					srcs: ["a.java"],
118					package_splits: ["v4", "v7,hdpi"],
119				}`)
120
121	foo := ctx.ModuleForTests("foo", "android_common")
122
123	expectedOutputs := []string{
124		filepath.Join(buildDir, ".intermediates/foo/android_common/foo.apk"),
125		filepath.Join(buildDir, ".intermediates/foo/android_common/foo_v4.apk"),
126		filepath.Join(buildDir, ".intermediates/foo/android_common/foo_v7_hdpi.apk"),
127	}
128	for _, expectedOutput := range expectedOutputs {
129		foo.Output(expectedOutput)
130	}
131
132	if g, w := foo.Module().(*AndroidApp).Srcs().Strings(), expectedOutputs; !reflect.DeepEqual(g, w) {
133		t.Errorf("want Srcs() = %q, got %q", w, g)
134	}
135}
136
137func TestResourceDirs(t *testing.T) {
138	testCases := []struct {
139		name      string
140		prop      string
141		resources []string
142	}{
143		{
144			name:      "no resource_dirs",
145			prop:      "",
146			resources: []string{"res/res/values/strings.xml"},
147		},
148		{
149			name:      "resource_dirs",
150			prop:      `resource_dirs: ["res"]`,
151			resources: []string{"res/res/values/strings.xml"},
152		},
153		{
154			name:      "empty resource_dirs",
155			prop:      `resource_dirs: []`,
156			resources: nil,
157		},
158	}
159
160	fs := map[string][]byte{
161		"res/res/values/strings.xml": nil,
162	}
163
164	bp := `
165			android_app {
166				name: "foo",
167				%s
168			}
169		`
170
171	for _, testCase := range testCases {
172		t.Run(testCase.name, func(t *testing.T) {
173			config := testConfig(nil)
174			ctx := testContext(config, fmt.Sprintf(bp, testCase.prop), fs)
175			run(t, ctx, config)
176
177			module := ctx.ModuleForTests("foo", "android_common")
178			resourceList := module.MaybeOutput("aapt2/res.list")
179
180			var resources []string
181			if resourceList.Rule != nil {
182				for _, compiledResource := range resourceList.Inputs.Strings() {
183					resources = append(resources, module.Output(compiledResource).Inputs.Strings()...)
184				}
185			}
186
187			if !reflect.DeepEqual(resources, testCase.resources) {
188				t.Errorf("expected resource files %q, got %q",
189					testCase.resources, resources)
190			}
191		})
192	}
193}
194
195func TestAndroidResources(t *testing.T) {
196	testCases := []struct {
197		name                       string
198		enforceRROTargets          []string
199		enforceRROExcludedOverlays []string
200		resourceFiles              map[string][]string
201		overlayFiles               map[string][]string
202		rroDirs                    map[string][]string
203	}{
204		{
205			name:                       "no RRO",
206			enforceRROTargets:          nil,
207			enforceRROExcludedOverlays: nil,
208			resourceFiles: map[string][]string{
209				"foo":  nil,
210				"bar":  {"bar/res/res/values/strings.xml"},
211				"lib":  nil,
212				"lib2": {"lib2/res/res/values/strings.xml"},
213			},
214			overlayFiles: map[string][]string{
215				"foo": {
216					buildDir + "/.intermediates/lib2/android_common/package-res.apk",
217					buildDir + "/.intermediates/lib/android_common/package-res.apk",
218					buildDir + "/.intermediates/lib3/android_common/package-res.apk",
219					"foo/res/res/values/strings.xml",
220					"device/vendor/blah/static_overlay/foo/res/values/strings.xml",
221					"device/vendor/blah/overlay/foo/res/values/strings.xml",
222					"product/vendor/blah/overlay/foo/res/values/strings.xml",
223				},
224				"bar": {
225					"device/vendor/blah/static_overlay/bar/res/values/strings.xml",
226					"device/vendor/blah/overlay/bar/res/values/strings.xml",
227				},
228				"lib": {
229					buildDir + "/.intermediates/lib2/android_common/package-res.apk",
230					"lib/res/res/values/strings.xml",
231					"device/vendor/blah/overlay/lib/res/values/strings.xml",
232				},
233			},
234			rroDirs: map[string][]string{
235				"foo": nil,
236				"bar": nil,
237			},
238		},
239		{
240			name:                       "enforce RRO on foo",
241			enforceRROTargets:          []string{"foo"},
242			enforceRROExcludedOverlays: []string{"device/vendor/blah/static_overlay"},
243			resourceFiles: map[string][]string{
244				"foo":  nil,
245				"bar":  {"bar/res/res/values/strings.xml"},
246				"lib":  nil,
247				"lib2": {"lib2/res/res/values/strings.xml"},
248			},
249			overlayFiles: map[string][]string{
250				"foo": {
251					buildDir + "/.intermediates/lib2/android_common/package-res.apk",
252					buildDir + "/.intermediates/lib/android_common/package-res.apk",
253					buildDir + "/.intermediates/lib3/android_common/package-res.apk",
254					"foo/res/res/values/strings.xml",
255					"device/vendor/blah/static_overlay/foo/res/values/strings.xml",
256				},
257				"bar": {
258					"device/vendor/blah/static_overlay/bar/res/values/strings.xml",
259					"device/vendor/blah/overlay/bar/res/values/strings.xml",
260				},
261				"lib": {
262					buildDir + "/.intermediates/lib2/android_common/package-res.apk",
263					"lib/res/res/values/strings.xml",
264					"device/vendor/blah/overlay/lib/res/values/strings.xml",
265				},
266			},
267
268			rroDirs: map[string][]string{
269				"foo": {
270					"device:device/vendor/blah/overlay/foo/res",
271					// Enforce RRO on "foo" could imply RRO on static dependencies, but for now it doesn't.
272					// "device/vendor/blah/overlay/lib/res",
273					"product:product/vendor/blah/overlay/foo/res",
274				},
275				"bar": nil,
276				"lib": nil,
277			},
278		},
279		{
280			name:              "enforce RRO on all",
281			enforceRROTargets: []string{"*"},
282			enforceRROExcludedOverlays: []string{
283				// Excluding specific apps/res directories also allowed.
284				"device/vendor/blah/static_overlay/foo",
285				"device/vendor/blah/static_overlay/bar/res",
286			},
287			resourceFiles: map[string][]string{
288				"foo":  nil,
289				"bar":  {"bar/res/res/values/strings.xml"},
290				"lib":  nil,
291				"lib2": {"lib2/res/res/values/strings.xml"},
292			},
293			overlayFiles: map[string][]string{
294				"foo": {
295					buildDir + "/.intermediates/lib2/android_common/package-res.apk",
296					buildDir + "/.intermediates/lib/android_common/package-res.apk",
297					buildDir + "/.intermediates/lib3/android_common/package-res.apk",
298					"foo/res/res/values/strings.xml",
299					"device/vendor/blah/static_overlay/foo/res/values/strings.xml",
300				},
301				"bar": {"device/vendor/blah/static_overlay/bar/res/values/strings.xml"},
302				"lib": {
303					buildDir + "/.intermediates/lib2/android_common/package-res.apk",
304					"lib/res/res/values/strings.xml",
305				},
306			},
307			rroDirs: map[string][]string{
308				"foo": {
309					"device:device/vendor/blah/overlay/foo/res",
310					"product:product/vendor/blah/overlay/foo/res",
311					// Lib dep comes after the direct deps
312					"device:device/vendor/blah/overlay/lib/res",
313				},
314				"bar": {"device:device/vendor/blah/overlay/bar/res"},
315				"lib": {"device:device/vendor/blah/overlay/lib/res"},
316			},
317		},
318	}
319
320	deviceResourceOverlays := []string{
321		"device/vendor/blah/overlay",
322		"device/vendor/blah/overlay2",
323		"device/vendor/blah/static_overlay",
324	}
325
326	productResourceOverlays := []string{
327		"product/vendor/blah/overlay",
328	}
329
330	fs := map[string][]byte{
331		"foo/res/res/values/strings.xml":                               nil,
332		"bar/res/res/values/strings.xml":                               nil,
333		"lib/res/res/values/strings.xml":                               nil,
334		"lib2/res/res/values/strings.xml":                              nil,
335		"device/vendor/blah/overlay/foo/res/values/strings.xml":        nil,
336		"device/vendor/blah/overlay/bar/res/values/strings.xml":        nil,
337		"device/vendor/blah/overlay/lib/res/values/strings.xml":        nil,
338		"device/vendor/blah/static_overlay/foo/res/values/strings.xml": nil,
339		"device/vendor/blah/static_overlay/bar/res/values/strings.xml": nil,
340		"device/vendor/blah/overlay2/res/values/strings.xml":           nil,
341		"product/vendor/blah/overlay/foo/res/values/strings.xml":       nil,
342	}
343
344	bp := `
345			android_app {
346				name: "foo",
347				resource_dirs: ["foo/res"],
348				static_libs: ["lib", "lib3"],
349			}
350
351			android_app {
352				name: "bar",
353				resource_dirs: ["bar/res"],
354			}
355
356			android_library {
357				name: "lib",
358				resource_dirs: ["lib/res"],
359				static_libs: ["lib2"],
360			}
361
362			android_library {
363				name: "lib2",
364				resource_dirs: ["lib2/res"],
365			}
366
367			// This library has the same resources as lib (should not lead to dupe RROs)
368			android_library {
369				name: "lib3",
370				resource_dirs: ["lib/res"]
371			}
372		`
373
374	for _, testCase := range testCases {
375		t.Run(testCase.name, func(t *testing.T) {
376			config := testConfig(nil)
377			config.TestProductVariables.DeviceResourceOverlays = deviceResourceOverlays
378			config.TestProductVariables.ProductResourceOverlays = productResourceOverlays
379			if testCase.enforceRROTargets != nil {
380				config.TestProductVariables.EnforceRROTargets = testCase.enforceRROTargets
381			}
382			if testCase.enforceRROExcludedOverlays != nil {
383				config.TestProductVariables.EnforceRROExcludedOverlays = testCase.enforceRROExcludedOverlays
384			}
385
386			ctx := testAppContext(config, bp, fs)
387			run(t, ctx, config)
388
389			resourceListToFiles := func(module android.TestingModule, list []string) (files []string) {
390				for _, o := range list {
391					res := module.MaybeOutput(o)
392					if res.Rule != nil {
393						// If the overlay is compiled as part of this module (i.e. a .arsc.flat file),
394						// verify the inputs to the .arsc.flat rule.
395						files = append(files, res.Inputs.Strings()...)
396					} else {
397						// Otherwise, verify the full path to the output of the other module
398						files = append(files, o)
399					}
400				}
401				return files
402			}
403
404			getResources := func(moduleName string) (resourceFiles, overlayFiles, rroDirs []string) {
405				module := ctx.ModuleForTests(moduleName, "android_common")
406				resourceList := module.MaybeOutput("aapt2/res.list")
407				if resourceList.Rule != nil {
408					resourceFiles = resourceListToFiles(module, resourceList.Inputs.Strings())
409				}
410				overlayList := module.MaybeOutput("aapt2/overlay.list")
411				if overlayList.Rule != nil {
412					overlayFiles = resourceListToFiles(module, overlayList.Inputs.Strings())
413				}
414
415				for _, d := range module.Module().(AndroidLibraryDependency).ExportedRRODirs() {
416					var prefix string
417					if d.overlayType == device {
418						prefix = "device:"
419					} else if d.overlayType == product {
420						prefix = "product:"
421					} else {
422						t.Fatalf("Unexpected overlayType %d", d.overlayType)
423					}
424					rroDirs = append(rroDirs, prefix+d.path.String())
425				}
426
427				return resourceFiles, overlayFiles, rroDirs
428			}
429
430			modules := []string{"foo", "bar", "lib", "lib2"}
431			for _, module := range modules {
432				resourceFiles, overlayFiles, rroDirs := getResources(module)
433
434				if !reflect.DeepEqual(resourceFiles, testCase.resourceFiles[module]) {
435					t.Errorf("expected %s resource files:\n  %#v\n got:\n  %#v",
436						module, testCase.resourceFiles[module], resourceFiles)
437				}
438				if !reflect.DeepEqual(overlayFiles, testCase.overlayFiles[module]) {
439					t.Errorf("expected %s overlay files:\n  %#v\n got:\n  %#v",
440						module, testCase.overlayFiles[module], overlayFiles)
441				}
442				if !reflect.DeepEqual(rroDirs, testCase.rroDirs[module]) {
443					t.Errorf("expected %s rroDirs:  %#v\n got:\n  %#v",
444						module, testCase.rroDirs[module], rroDirs)
445				}
446			}
447		})
448	}
449}
450
451func TestAppSdkVersion(t *testing.T) {
452	testCases := []struct {
453		name                  string
454		sdkVersion            string
455		platformSdkInt        int
456		platformSdkCodename   string
457		platformSdkFinal      bool
458		expectedMinSdkVersion string
459	}{
460		{
461			name:                  "current final SDK",
462			sdkVersion:            "current",
463			platformSdkInt:        27,
464			platformSdkCodename:   "REL",
465			platformSdkFinal:      true,
466			expectedMinSdkVersion: "27",
467		},
468		{
469			name:                  "current non-final SDK",
470			sdkVersion:            "current",
471			platformSdkInt:        27,
472			platformSdkCodename:   "OMR1",
473			platformSdkFinal:      false,
474			expectedMinSdkVersion: "OMR1",
475		},
476		{
477			name:                  "default final SDK",
478			sdkVersion:            "",
479			platformSdkInt:        27,
480			platformSdkCodename:   "REL",
481			platformSdkFinal:      true,
482			expectedMinSdkVersion: "27",
483		},
484		{
485			name:                  "default non-final SDK",
486			sdkVersion:            "",
487			platformSdkInt:        27,
488			platformSdkCodename:   "OMR1",
489			platformSdkFinal:      false,
490			expectedMinSdkVersion: "OMR1",
491		},
492		{
493			name:                  "14",
494			sdkVersion:            "14",
495			expectedMinSdkVersion: "14",
496		},
497	}
498
499	for _, moduleType := range []string{"android_app", "android_library"} {
500		for _, test := range testCases {
501			t.Run(moduleType+" "+test.name, func(t *testing.T) {
502				bp := fmt.Sprintf(`%s {
503					name: "foo",
504					srcs: ["a.java"],
505					sdk_version: "%s",
506				}`, moduleType, test.sdkVersion)
507
508				config := testConfig(nil)
509				config.TestProductVariables.Platform_sdk_version = &test.platformSdkInt
510				config.TestProductVariables.Platform_sdk_codename = &test.platformSdkCodename
511				config.TestProductVariables.Platform_sdk_final = &test.platformSdkFinal
512
513				ctx := testAppContext(config, bp, nil)
514
515				run(t, ctx, config)
516
517				foo := ctx.ModuleForTests("foo", "android_common")
518				link := foo.Output("package-res.apk")
519				linkFlags := strings.Split(link.Args["flags"], " ")
520				min := android.IndexList("--min-sdk-version", linkFlags)
521				target := android.IndexList("--target-sdk-version", linkFlags)
522
523				if min == -1 || target == -1 || min == len(linkFlags)-1 || target == len(linkFlags)-1 {
524					t.Fatalf("missing --min-sdk-version or --target-sdk-version in link flags: %q", linkFlags)
525				}
526
527				gotMinSdkVersion := linkFlags[min+1]
528				gotTargetSdkVersion := linkFlags[target+1]
529
530				if gotMinSdkVersion != test.expectedMinSdkVersion {
531					t.Errorf("incorrect --min-sdk-version, expected %q got %q",
532						test.expectedMinSdkVersion, gotMinSdkVersion)
533				}
534
535				if gotTargetSdkVersion != test.expectedMinSdkVersion {
536					t.Errorf("incorrect --target-sdk-version, expected %q got %q",
537						test.expectedMinSdkVersion, gotTargetSdkVersion)
538				}
539			})
540		}
541	}
542}
543
544func TestJNIABI(t *testing.T) {
545	ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
546		cc_library {
547			name: "libjni",
548			system_shared_libs: [],
549			stl: "none",
550		}
551
552		android_test {
553			name: "test",
554			no_framework_libs: true,
555			jni_libs: ["libjni"],
556		}
557
558		android_test {
559			name: "test_first",
560			no_framework_libs: true,
561			compile_multilib: "first",
562			jni_libs: ["libjni"],
563		}
564
565		android_test {
566			name: "test_both",
567			no_framework_libs: true,
568			compile_multilib: "both",
569			jni_libs: ["libjni"],
570		}
571
572		android_test {
573			name: "test_32",
574			no_framework_libs: true,
575			compile_multilib: "32",
576			jni_libs: ["libjni"],
577		}
578
579		android_test {
580			name: "test_64",
581			no_framework_libs: true,
582			compile_multilib: "64",
583			jni_libs: ["libjni"],
584		}
585		`)
586
587	testCases := []struct {
588		name string
589		abis []string
590	}{
591		{"test", []string{"arm64-v8a"}},
592		{"test_first", []string{"arm64-v8a"}},
593		{"test_both", []string{"arm64-v8a", "armeabi-v7a"}},
594		{"test_32", []string{"armeabi-v7a"}},
595		{"test_64", []string{"arm64-v8a"}},
596	}
597
598	for _, test := range testCases {
599		t.Run(test.name, func(t *testing.T) {
600			app := ctx.ModuleForTests(test.name, "android_common")
601			jniLibZip := app.Output("jnilibs.zip")
602			var abis []string
603			args := strings.Fields(jniLibZip.Args["jarArgs"])
604			for i := 0; i < len(args); i++ {
605				if args[i] == "-P" {
606					abis = append(abis, filepath.Base(args[i+1]))
607					i++
608				}
609			}
610			if !reflect.DeepEqual(abis, test.abis) {
611				t.Errorf("want abis %v, got %v", test.abis, abis)
612			}
613		})
614	}
615}
616
617func TestJNIPackaging(t *testing.T) {
618	ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
619		cc_library {
620			name: "libjni",
621			system_shared_libs: [],
622			stl: "none",
623		}
624
625		android_app {
626			name: "app",
627			jni_libs: ["libjni"],
628		}
629
630		android_app {
631			name: "app_noembed",
632			jni_libs: ["libjni"],
633			use_embedded_native_libs: false,
634		}
635
636		android_app {
637			name: "app_embed",
638			jni_libs: ["libjni"],
639			use_embedded_native_libs: true,
640		}
641
642		android_test {
643			name: "test",
644			no_framework_libs: true,
645			jni_libs: ["libjni"],
646		}
647
648		android_test {
649			name: "test_noembed",
650			no_framework_libs: true,
651			jni_libs: ["libjni"],
652			use_embedded_native_libs: false,
653		}
654
655		android_test_helper_app {
656			name: "test_helper",
657			no_framework_libs: true,
658			jni_libs: ["libjni"],
659		}
660
661		android_test_helper_app {
662			name: "test_helper_noembed",
663			no_framework_libs: true,
664			jni_libs: ["libjni"],
665			use_embedded_native_libs: false,
666		}
667		`)
668
669	testCases := []struct {
670		name       string
671		packaged   bool
672		compressed bool
673	}{
674		{"app", false, false},
675		{"app_noembed", false, false},
676		{"app_embed", true, false},
677		{"test", true, false},
678		{"test_noembed", true, true},
679		{"test_helper", true, false},
680		{"test_helper_noembed", true, true},
681	}
682
683	for _, test := range testCases {
684		t.Run(test.name, func(t *testing.T) {
685			app := ctx.ModuleForTests(test.name, "android_common")
686			jniLibZip := app.MaybeOutput("jnilibs.zip")
687			if g, w := (jniLibZip.Rule != nil), test.packaged; g != w {
688				t.Errorf("expected jni packaged %v, got %v", w, g)
689			}
690
691			if jniLibZip.Rule != nil {
692				if g, w := !strings.Contains(jniLibZip.Args["jarArgs"], "-L 0"), test.compressed; g != w {
693					t.Errorf("expected jni compressed %v, got %v", w, g)
694				}
695			}
696		})
697	}
698
699}
700
701func TestCertificates(t *testing.T) {
702	testCases := []struct {
703		name                string
704		bp                  string
705		certificateOverride string
706		expected            string
707	}{
708		{
709			name: "default",
710			bp: `
711				android_app {
712					name: "foo",
713					srcs: ["a.java"],
714				}
715			`,
716			certificateOverride: "",
717			expected:            "build/target/product/security/testkey.x509.pem build/target/product/security/testkey.pk8",
718		},
719		{
720			name: "module certificate property",
721			bp: `
722				android_app {
723					name: "foo",
724					srcs: ["a.java"],
725					certificate: ":new_certificate"
726				}
727
728				android_app_certificate {
729					name: "new_certificate",
730			    certificate: "cert/new_cert",
731				}
732			`,
733			certificateOverride: "",
734			expected:            "cert/new_cert.x509.pem cert/new_cert.pk8",
735		},
736		{
737			name: "path certificate property",
738			bp: `
739				android_app {
740					name: "foo",
741					srcs: ["a.java"],
742					certificate: "expiredkey"
743				}
744			`,
745			certificateOverride: "",
746			expected:            "build/target/product/security/expiredkey.x509.pem build/target/product/security/expiredkey.pk8",
747		},
748		{
749			name: "certificate overrides",
750			bp: `
751				android_app {
752					name: "foo",
753					srcs: ["a.java"],
754					certificate: "expiredkey"
755				}
756
757				android_app_certificate {
758					name: "new_certificate",
759			    certificate: "cert/new_cert",
760				}
761			`,
762			certificateOverride: "foo:new_certificate",
763			expected:            "cert/new_cert.x509.pem cert/new_cert.pk8",
764		},
765	}
766
767	for _, test := range testCases {
768		t.Run(test.name, func(t *testing.T) {
769			config := testConfig(nil)
770			if test.certificateOverride != "" {
771				config.TestProductVariables.CertificateOverrides = []string{test.certificateOverride}
772			}
773			ctx := testAppContext(config, test.bp, nil)
774
775			run(t, ctx, config)
776			foo := ctx.ModuleForTests("foo", "android_common")
777
778			signapk := foo.Output("foo.apk")
779			signFlags := signapk.Args["certificates"]
780			if test.expected != signFlags {
781				t.Errorf("Incorrect signing flags, expected: %q, got: %q", test.expected, signFlags)
782			}
783		})
784	}
785}
786
787func TestPackageNameOverride(t *testing.T) {
788	testCases := []struct {
789		name                string
790		bp                  string
791		packageNameOverride string
792		expected            []string
793	}{
794		{
795			name: "default",
796			bp: `
797				android_app {
798					name: "foo",
799					srcs: ["a.java"],
800				}
801			`,
802			packageNameOverride: "",
803			expected: []string{
804				buildDir + "/.intermediates/foo/android_common/foo.apk",
805				buildDir + "/target/product/test_device/system/app/foo/foo.apk",
806			},
807		},
808		{
809			name: "overridden",
810			bp: `
811				android_app {
812					name: "foo",
813					srcs: ["a.java"],
814				}
815			`,
816			packageNameOverride: "foo:bar",
817			expected: []string{
818				// The package apk should be still be the original name for test dependencies.
819				buildDir + "/.intermediates/foo/android_common/foo.apk",
820				buildDir + "/target/product/test_device/system/app/bar/bar.apk",
821			},
822		},
823	}
824
825	for _, test := range testCases {
826		t.Run(test.name, func(t *testing.T) {
827			config := testConfig(nil)
828			if test.packageNameOverride != "" {
829				config.TestProductVariables.PackageNameOverrides = []string{test.packageNameOverride}
830			}
831			ctx := testAppContext(config, test.bp, nil)
832
833			run(t, ctx, config)
834			foo := ctx.ModuleForTests("foo", "android_common")
835
836			outputs := foo.AllOutputs()
837			outputMap := make(map[string]bool)
838			for _, o := range outputs {
839				outputMap[o] = true
840			}
841			for _, e := range test.expected {
842				if _, exist := outputMap[e]; !exist {
843					t.Errorf("Can't find %q in output files.\nAll outputs:%v", e, outputs)
844				}
845			}
846		})
847	}
848}
849
850func TestInstrumentationTargetOverridden(t *testing.T) {
851	bp := `
852		android_app {
853			name: "foo",
854			srcs: ["a.java"],
855		}
856
857		android_test {
858			name: "bar",
859			instrumentation_for: "foo",
860		}
861		`
862	config := testConfig(nil)
863	config.TestProductVariables.ManifestPackageNameOverrides = []string{"foo:org.dandroid.bp"}
864	ctx := testAppContext(config, bp, nil)
865
866	run(t, ctx, config)
867
868	bar := ctx.ModuleForTests("bar", "android_common")
869	res := bar.Output("package-res.apk")
870	aapt2Flags := res.Args["flags"]
871	e := "--rename-instrumentation-target-package org.dandroid.bp"
872	if !strings.Contains(aapt2Flags, e) {
873		t.Errorf("target package renaming flag, %q is missing in aapt2 link flags, %q", e, aapt2Flags)
874	}
875}
876
877func TestOverrideAndroidApp(t *testing.T) {
878	ctx := testJava(t, `
879		android_app {
880			name: "foo",
881			srcs: ["a.java"],
882			certificate: "expiredkey",
883			overrides: ["baz"],
884		}
885
886		override_android_app {
887			name: "bar",
888			base: "foo",
889			certificate: ":new_certificate",
890		}
891
892		android_app_certificate {
893			name: "new_certificate",
894			certificate: "cert/new_cert",
895		}
896
897		override_android_app {
898			name: "baz",
899			base: "foo",
900			package_name: "org.dandroid.bp",
901		}
902		`)
903
904	expectedVariants := []struct {
905		variantName string
906		apkName     string
907		apkPath     string
908		signFlag    string
909		overrides   []string
910		aaptFlag    string
911	}{
912		{
913			variantName: "android_common",
914			apkPath:     "/target/product/test_device/system/app/foo/foo.apk",
915			signFlag:    "build/target/product/security/expiredkey.x509.pem build/target/product/security/expiredkey.pk8",
916			overrides:   []string{"baz"},
917			aaptFlag:    "",
918		},
919		{
920			variantName: "bar_android_common",
921			apkPath:     "/target/product/test_device/system/app/bar/bar.apk",
922			signFlag:    "cert/new_cert.x509.pem cert/new_cert.pk8",
923			overrides:   []string{"baz", "foo"},
924			aaptFlag:    "",
925		},
926		{
927			variantName: "baz_android_common",
928			apkPath:     "/target/product/test_device/system/app/baz/baz.apk",
929			signFlag:    "build/target/product/security/expiredkey.x509.pem build/target/product/security/expiredkey.pk8",
930			overrides:   []string{"baz", "foo"},
931			aaptFlag:    "--rename-manifest-package org.dandroid.bp",
932		},
933	}
934	for _, expected := range expectedVariants {
935		variant := ctx.ModuleForTests("foo", expected.variantName)
936
937		// Check the final apk name
938		outputs := variant.AllOutputs()
939		expectedApkPath := buildDir + expected.apkPath
940		found := false
941		for _, o := range outputs {
942			if o == expectedApkPath {
943				found = true
944				break
945			}
946		}
947		if !found {
948			t.Errorf("Can't find %q in output files.\nAll outputs:%v", expectedApkPath, outputs)
949		}
950
951		// Check the certificate paths
952		signapk := variant.Output("foo.apk")
953		signFlag := signapk.Args["certificates"]
954		if expected.signFlag != signFlag {
955			t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected.signFlag, signFlag)
956		}
957
958		// Check if the overrides field values are correctly aggregated.
959		mod := variant.Module().(*AndroidApp)
960		if !reflect.DeepEqual(expected.overrides, mod.appProperties.Overrides) {
961			t.Errorf("Incorrect overrides property value, expected: %q, got: %q",
962				expected.overrides, mod.appProperties.Overrides)
963		}
964
965		// Check the package renaming flag, if exists.
966		res := variant.Output("package-res.apk")
967		aapt2Flags := res.Args["flags"]
968		if !strings.Contains(aapt2Flags, expected.aaptFlag) {
969			t.Errorf("package renaming flag, %q is missing in aapt2 link flags, %q", expected.aaptFlag, aapt2Flags)
970		}
971	}
972}
973
974func TestEmbedNotice(t *testing.T) {
975	ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
976		android_app {
977			name: "foo",
978			srcs: ["a.java"],
979			static_libs: ["javalib"],
980			jni_libs: ["libjni"],
981			notice: "APP_NOTICE",
982			embed_notices: true,
983		}
984
985		// No embed_notice flag
986		android_app {
987			name: "bar",
988			srcs: ["a.java"],
989			jni_libs: ["libjni"],
990			notice: "APP_NOTICE",
991		}
992
993		// No NOTICE files
994		android_app {
995			name: "baz",
996			srcs: ["a.java"],
997			embed_notices: true,
998		}
999
1000		cc_library {
1001			name: "libjni",
1002			system_shared_libs: [],
1003			stl: "none",
1004			notice: "LIB_NOTICE",
1005		}
1006
1007		java_library {
1008			name: "javalib",
1009			srcs: [
1010				":gen",
1011			],
1012		}
1013
1014		genrule {
1015			name: "gen",
1016			tools: ["gentool"],
1017			out: ["gen.java"],
1018			notice: "GENRULE_NOTICE",
1019		}
1020
1021		java_binary_host {
1022			name: "gentool",
1023			srcs: ["b.java"],
1024			notice: "TOOL_NOTICE",
1025		}
1026	`)
1027
1028	// foo has NOTICE files to process, and embed_notices is true.
1029	foo := ctx.ModuleForTests("foo", "android_common")
1030	// verify merge notices rule.
1031	mergeNotices := foo.Rule("mergeNoticesRule")
1032	noticeInputs := mergeNotices.Inputs.Strings()
1033	// TOOL_NOTICE should be excluded as it's a host module.
1034	if len(mergeNotices.Inputs) != 3 {
1035		t.Errorf("number of input notice files: expected = 3, actual = %q", noticeInputs)
1036	}
1037	if !inList("APP_NOTICE", noticeInputs) {
1038		t.Errorf("APP_NOTICE is missing from notice files, %q", noticeInputs)
1039	}
1040	if !inList("LIB_NOTICE", noticeInputs) {
1041		t.Errorf("LIB_NOTICE is missing from notice files, %q", noticeInputs)
1042	}
1043	if !inList("GENRULE_NOTICE", noticeInputs) {
1044		t.Errorf("GENRULE_NOTICE is missing from notice files, %q", noticeInputs)
1045	}
1046	// aapt2 flags should include -A <NOTICE dir> so that its contents are put in the APK's /assets.
1047	res := foo.Output("package-res.apk")
1048	aapt2Flags := res.Args["flags"]
1049	e := "-A " + buildDir + "/.intermediates/foo/android_common/NOTICE"
1050	if !strings.Contains(aapt2Flags, e) {
1051		t.Errorf("asset dir flag for NOTICE, %q is missing in aapt2 link flags, %q", e, aapt2Flags)
1052	}
1053
1054	// bar has NOTICE files to process, but embed_notices is not set.
1055	bar := ctx.ModuleForTests("bar", "android_common")
1056	mergeNotices = bar.MaybeRule("mergeNoticesRule")
1057	if mergeNotices.Rule != nil {
1058		t.Errorf("mergeNotices shouldn't have run for bar")
1059	}
1060
1061	// baz's embed_notice is true, but it doesn't have any NOTICE files.
1062	baz := ctx.ModuleForTests("baz", "android_common")
1063	mergeNotices = baz.MaybeRule("mergeNoticesRule")
1064	if mergeNotices.Rule != nil {
1065		t.Errorf("mergeNotices shouldn't have run for baz")
1066	}
1067}
1068
1069func TestUncompressDex(t *testing.T) {
1070	testCases := []struct {
1071		name string
1072		bp   string
1073
1074		uncompressedPlatform  bool
1075		uncompressedUnbundled bool
1076	}{
1077		{
1078			name: "normal",
1079			bp: `
1080				android_app {
1081					name: "foo",
1082					srcs: ["a.java"],
1083				}
1084			`,
1085			uncompressedPlatform:  true,
1086			uncompressedUnbundled: false,
1087		},
1088		{
1089			name: "use_embedded_dex",
1090			bp: `
1091				android_app {
1092					name: "foo",
1093					use_embedded_dex: true,
1094					srcs: ["a.java"],
1095				}
1096			`,
1097			uncompressedPlatform:  true,
1098			uncompressedUnbundled: true,
1099		},
1100		{
1101			name: "privileged",
1102			bp: `
1103				android_app {
1104					name: "foo",
1105					privileged: true,
1106					srcs: ["a.java"],
1107				}
1108			`,
1109			uncompressedPlatform:  true,
1110			uncompressedUnbundled: true,
1111		},
1112	}
1113
1114	test := func(t *testing.T, bp string, want bool, unbundled bool) {
1115		t.Helper()
1116
1117		config := testConfig(nil)
1118		if unbundled {
1119			config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
1120		}
1121
1122		ctx := testAppContext(config, bp, nil)
1123
1124		run(t, ctx, config)
1125
1126		foo := ctx.ModuleForTests("foo", "android_common")
1127		dex := foo.Rule("r8")
1128		uncompressedInDexJar := strings.Contains(dex.Args["zipFlags"], "-L 0")
1129		aligned := foo.MaybeRule("zipalign").Rule != nil
1130
1131		if uncompressedInDexJar != want {
1132			t.Errorf("want uncompressed in dex %v, got %v", want, uncompressedInDexJar)
1133		}
1134
1135		if aligned != want {
1136			t.Errorf("want aligned %v, got %v", want, aligned)
1137		}
1138	}
1139
1140	for _, tt := range testCases {
1141		t.Run(tt.name, func(t *testing.T) {
1142			t.Run("platform", func(t *testing.T) {
1143				test(t, tt.bp, tt.uncompressedPlatform, false)
1144			})
1145			t.Run("unbundled", func(t *testing.T) {
1146				test(t, tt.bp, tt.uncompressedUnbundled, true)
1147			})
1148		})
1149	}
1150}
1151