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 android
16
17import (
18	"strings"
19	"testing"
20
21	"github.com/google/blueprint"
22	"github.com/google/blueprint/proptools"
23)
24
25// Module to be packaged
26type componentTestModule struct {
27	ModuleBase
28	props struct {
29		Deps         []string
30		Skip_install *bool
31	}
32}
33
34// dep tag used in this test. All dependencies are considered as installable.
35type installDepTag struct {
36	blueprint.BaseDependencyTag
37	InstallAlwaysNeededDependencyTag
38}
39
40func componentTestModuleFactory() Module {
41	m := &componentTestModule{}
42	m.AddProperties(&m.props)
43	InitAndroidArchModule(m, HostAndDeviceSupported, MultilibBoth)
44	return m
45}
46
47func (m *componentTestModule) DepsMutator(ctx BottomUpMutatorContext) {
48	ctx.AddDependency(ctx.Module(), installDepTag{}, m.props.Deps...)
49}
50
51func (m *componentTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
52	builtFile := PathForModuleOut(ctx, m.Name())
53	dir := ctx.Target().Arch.ArchType.Multilib
54	installDir := PathForModuleInstall(ctx, dir)
55	if proptools.Bool(m.props.Skip_install) {
56		m.SkipInstall()
57	}
58	ctx.InstallFile(installDir, m.Name(), builtFile)
59}
60
61// Module that itself is a package
62type packageTestModule struct {
63	ModuleBase
64	PackagingBase
65	properties struct {
66		Install_deps []string `android:`
67	}
68	entries []string
69}
70
71func packageTestModuleFactory(multiTarget bool, depsCollectFirstTargetOnly bool) Module {
72	module := &packageTestModule{}
73	InitPackageModule(module)
74	module.DepsCollectFirstTargetOnly = depsCollectFirstTargetOnly
75	if multiTarget {
76		InitAndroidMultiTargetsArchModule(module, DeviceSupported, MultilibCommon)
77	} else {
78		InitAndroidArchModule(module, DeviceSupported, MultilibBoth)
79	}
80	module.AddProperties(&module.properties)
81	return module
82}
83
84type packagingDepTag struct {
85	blueprint.BaseDependencyTag
86	PackagingItemAlwaysDepTag
87}
88
89func (m *packageTestModule) DepsMutator(ctx BottomUpMutatorContext) {
90	m.AddDeps(ctx, packagingDepTag{})
91	ctx.AddDependency(ctx.Module(), installDepTag{}, m.properties.Install_deps...)
92}
93
94func (m *packageTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
95	zipFile := PathForModuleOut(ctx, "myzip.zip")
96	m.entries = m.CopyDepsToZip(ctx, m.GatherPackagingSpecs(ctx), zipFile)
97}
98
99type testConfig struct {
100	multiTarget                bool
101	depsCollectFirstTargetOnly bool
102	debuggable                 bool
103}
104
105func runPackagingTest(t *testing.T, config testConfig, bp string, expected []string) {
106	t.Helper()
107
108	var archVariant string
109	if config.multiTarget {
110		archVariant = "android_common"
111	} else {
112		archVariant = "android_arm64_armv8-a"
113	}
114
115	moduleFactory := func() Module {
116		return packageTestModuleFactory(config.multiTarget, config.depsCollectFirstTargetOnly)
117	}
118
119	result := GroupFixturePreparers(
120		PrepareForTestWithArchMutator,
121		FixtureRegisterWithContext(func(ctx RegistrationContext) {
122			ctx.RegisterModuleType("component", componentTestModuleFactory)
123			ctx.RegisterModuleType("package_module", moduleFactory)
124		}),
125		FixtureModifyProductVariables(func(variables FixtureProductVariables) {
126			variables.Debuggable = proptools.BoolPtr(config.debuggable)
127		}),
128		FixtureWithRootAndroidBp(bp),
129	).RunTest(t)
130
131	p := result.Module("package", archVariant).(*packageTestModule)
132	actual := p.entries
133	actual = SortedUniqueStrings(actual)
134	expected = SortedUniqueStrings(expected)
135	AssertDeepEquals(t, "package entries", expected, actual)
136}
137
138func TestPackagingBaseMultiTarget(t *testing.T) {
139	config := testConfig{
140		multiTarget:                true,
141		depsCollectFirstTargetOnly: false,
142	}
143	runPackagingTest(t, config,
144		`
145		component {
146			name: "foo",
147		}
148
149		package_module {
150			name: "package",
151			deps: ["foo"],
152		}
153		`, []string{"lib64/foo"})
154
155	runPackagingTest(t, config,
156		`
157		component {
158			name: "foo",
159			deps: ["bar"],
160		}
161
162		component {
163			name: "bar",
164		}
165
166		package_module {
167			name: "package",
168			deps: ["foo"],
169		}
170		`, []string{"lib64/foo", "lib64/bar"})
171
172	runPackagingTest(t, config,
173		`
174		component {
175			name: "foo",
176			deps: ["bar"],
177		}
178
179		component {
180			name: "bar",
181		}
182
183		package_module {
184			name: "package",
185			deps: ["foo"],
186			compile_multilib: "both",
187		}
188		`, []string{"lib32/foo", "lib32/bar", "lib64/foo", "lib64/bar"})
189
190	runPackagingTest(t, config,
191		`
192		component {
193			name: "foo",
194		}
195
196		component {
197			name: "bar",
198			compile_multilib: "32",
199		}
200
201		package_module {
202			name: "package",
203			deps: ["foo"],
204			multilib: {
205				lib32: {
206					deps: ["bar"],
207				},
208			},
209			compile_multilib: "both",
210		}
211		`, []string{"lib32/foo", "lib32/bar", "lib64/foo"})
212
213	runPackagingTest(t, config,
214		`
215		component {
216			name: "foo",
217		}
218
219		component {
220			name: "bar",
221		}
222
223		package_module {
224			name: "package",
225			deps: ["foo"],
226			multilib: {
227				first: {
228					deps: ["bar"],
229				},
230			},
231			compile_multilib: "both",
232		}
233		`, []string{"lib32/foo", "lib64/foo", "lib64/bar"})
234
235	runPackagingTest(t, config,
236		`
237		component {
238			name: "foo",
239		}
240
241		component {
242			name: "bar",
243		}
244
245		component {
246			name: "baz",
247		}
248
249		package_module {
250			name: "package",
251			deps: ["foo"],
252			arch: {
253				arm64: {
254					deps: ["bar"],
255				},
256				x86_64: {
257					deps: ["baz"],
258				},
259			},
260			compile_multilib: "both",
261		}
262		`, []string{"lib32/foo", "lib64/foo", "lib64/bar"})
263}
264
265func TestPackagingBaseSingleTarget(t *testing.T) {
266	config := testConfig{
267		multiTarget:                false,
268		depsCollectFirstTargetOnly: false,
269	}
270	runPackagingTest(t, config,
271		`
272		component {
273			name: "foo",
274		}
275
276		package_module {
277			name: "package",
278			deps: ["foo"],
279		}
280		`, []string{"lib64/foo"})
281
282	runPackagingTest(t, config,
283		`
284		component {
285			name: "foo",
286			deps: ["bar"],
287		}
288
289		component {
290			name: "bar",
291		}
292
293		package_module {
294			name: "package",
295			deps: ["foo"],
296		}
297		`, []string{"lib64/foo", "lib64/bar"})
298
299	runPackagingTest(t, config,
300		`
301		component {
302			name: "foo",
303		}
304
305		component {
306			name: "bar",
307			compile_multilib: "32",
308		}
309
310		package_module {
311			name: "package",
312			deps: ["foo"],
313			multilib: {
314				lib32: {
315					deps: ["bar"],
316				},
317			},
318		}
319		`, []string{"lib64/foo"})
320
321	runPackagingTest(t, config,
322		`
323		component {
324			name: "foo",
325		}
326
327		component {
328			name: "bar",
329		}
330
331		package_module {
332			name: "package",
333			deps: ["foo"],
334			multilib: {
335				lib64: {
336					deps: ["bar"],
337				},
338			},
339		}
340		`, []string{"lib64/foo", "lib64/bar"})
341
342	runPackagingTest(t, config,
343		`
344		component {
345			name: "foo",
346		}
347
348		component {
349			name: "bar",
350		}
351
352		component {
353			name: "baz",
354		}
355
356		package_module {
357			name: "package",
358			deps: ["foo"],
359			arch: {
360				arm64: {
361					deps: ["bar"],
362				},
363				x86_64: {
364					deps: ["baz"],
365				},
366			},
367		}
368		`, []string{"lib64/foo", "lib64/bar"})
369
370	runPackagingTest(t, config,
371		`
372		component {
373			name: "foo",
374		}
375
376		component {
377			name: "bar",
378		}
379
380		package_module {
381			name: "package",
382			deps: ["foo"],
383			install_deps: ["bar"],
384		}
385		`, []string{"lib64/foo"})
386}
387
388func TestPackagingWithSkipInstallDeps(t *testing.T) {
389	// package -[dep]-> foo -[dep]-> bar      -[dep]-> baz
390	// Packaging should continue transitively through modules that are not installed.
391	config := testConfig{
392		multiTarget:                false,
393		depsCollectFirstTargetOnly: false,
394	}
395	runPackagingTest(t, config,
396		`
397		component {
398			name: "foo",
399			deps: ["bar"],
400		}
401
402		component {
403			name: "bar",
404			deps: ["baz"],
405			skip_install: true,
406		}
407
408		component {
409			name: "baz",
410		}
411
412		package_module {
413			name: "package",
414			deps: ["foo"],
415		}
416		`, []string{"lib64/foo", "lib64/bar", "lib64/baz"})
417}
418
419func TestPackagingWithDepsCollectFirstTargetOnly(t *testing.T) {
420	config := testConfig{
421		multiTarget:                true,
422		depsCollectFirstTargetOnly: true,
423	}
424	runPackagingTest(t, config,
425		`
426		component {
427			name: "foo",
428		}
429
430		package_module {
431			name: "package",
432			deps: ["foo"],
433		}
434		`, []string{"lib64/foo"})
435
436	runPackagingTest(t, config,
437		`
438		component {
439			name: "foo",
440			deps: ["bar"],
441		}
442
443		component {
444			name: "bar",
445		}
446
447		package_module {
448			name: "package",
449			deps: ["foo"],
450		}
451		`, []string{"lib64/foo", "lib64/bar"})
452
453	runPackagingTest(t, config,
454		`
455		component {
456			name: "foo",
457			deps: ["bar"],
458		}
459
460		component {
461			name: "bar",
462		}
463
464		package_module {
465			name: "package",
466			deps: ["foo"],
467			compile_multilib: "both",
468		}
469		`, []string{"lib64/foo", "lib64/bar"})
470
471	runPackagingTest(t, config,
472		`
473		component {
474			name: "foo",
475		}
476
477		component {
478			name: "bar",
479			compile_multilib: "32",
480		}
481
482		package_module {
483			name: "package",
484			deps: ["foo"],
485			multilib: {
486				lib32: {
487					deps: ["bar"],
488				},
489			},
490			compile_multilib: "both",
491		}
492		`, []string{"lib32/bar", "lib64/foo"})
493
494	runPackagingTest(t, config,
495		`
496		component {
497			name: "foo",
498		}
499
500		component {
501			name: "bar",
502		}
503
504		package_module {
505			name: "package",
506			deps: ["foo"],
507			multilib: {
508				both: {
509					deps: ["bar"],
510				},
511			},
512			compile_multilib: "both",
513		}
514		`, []string{"lib64/foo", "lib32/bar", "lib64/bar"})
515
516	runPackagingTest(t, config,
517		`
518		component {
519			name: "foo",
520		}
521
522		component {
523			name: "bar",
524		}
525
526		component {
527			name: "baz",
528		}
529
530		package_module {
531			name: "package",
532			deps: ["foo"],
533			arch: {
534				arm64: {
535					deps: ["bar"],
536				},
537				x86_64: {
538					deps: ["baz"],
539				},
540			},
541			compile_multilib: "both",
542		}
543		`, []string{"lib64/foo", "lib64/bar"})
544}
545
546func TestDebuggableDeps(t *testing.T) {
547	bp := `
548		component {
549			name: "foo",
550		}
551
552		component {
553			name: "bar",
554			deps: ["baz"],
555		}
556
557		component {
558			name: "baz",
559		}
560
561		package_module {
562			name: "package",
563			deps: ["foo"] + select(product_variable("debuggable"), {
564				true: ["bar"],
565				default: [],
566			}),
567		}`
568	testcases := []struct {
569		debuggable bool
570		expected   []string
571	}{
572		{
573			debuggable: true,
574			expected:   []string{"lib64/foo", "lib64/bar", "lib64/baz"},
575		},
576		{
577			debuggable: false,
578			expected:   []string{"lib64/foo"},
579		},
580	}
581	for _, tc := range testcases {
582		config := testConfig{
583			debuggable: tc.debuggable,
584		}
585		runPackagingTest(t, config, bp, tc.expected)
586	}
587}
588
589func TestPrefer32Deps(t *testing.T) {
590	bpTemplate := `
591		component {
592			name: "foo",
593			compile_multilib: "both", // not needed but for clarity
594		}
595
596		component {
597			name: "foo_32only",
598			compile_multilib: "prefer32",
599		}
600
601		component {
602			name: "foo_64only",
603			compile_multilib: "64",
604		}
605
606		package_module {
607			name: "package",
608			compile_multilib: "%COMPILE_MULTILIB%",
609			multilib: {
610				prefer32: {
611					deps: %DEPS%,
612				},
613			},
614		}
615	`
616
617	testcases := []struct {
618		compileMultilib string
619		deps            []string
620		expected        []string
621	}{
622		{
623			compileMultilib: "first",
624			deps:            []string{"foo", "foo_64only"},
625			expected:        []string{"lib64/foo", "lib64/foo_64only"},
626		},
627		{
628			compileMultilib: "64",
629			deps:            []string{"foo", "foo_64only"},
630			expected:        []string{"lib64/foo", "lib64/foo_64only"},
631		},
632		{
633			compileMultilib: "32",
634			deps:            []string{"foo", "foo_32only"},
635			expected:        []string{"lib32/foo", "lib32/foo_32only"},
636		},
637		{
638			compileMultilib: "both",
639			deps:            []string{"foo", "foo_32only", "foo_64only"},
640			expected:        []string{"lib32/foo", "lib32/foo_32only", "lib64/foo_64only"},
641		},
642	}
643	for _, tc := range testcases {
644		config := testConfig{
645			multiTarget:                true,
646			depsCollectFirstTargetOnly: true,
647		}
648		bp := strings.Replace(bpTemplate, "%COMPILE_MULTILIB%", tc.compileMultilib, -1)
649		bp = strings.Replace(bp, "%DEPS%", `["`+strings.Join(tc.deps, `", "`)+`"]`, -1)
650		runPackagingTest(t, config, bp, tc.expected)
651	}
652}
653