xref: /aosp_15_r20/build/soong/android/module_test.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1// Copyright 2015 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	"path/filepath"
19	"runtime"
20	"testing"
21
22	"github.com/google/blueprint"
23)
24
25func TestSrcIsModule(t *testing.T) {
26	type args struct {
27		s string
28	}
29	tests := []struct {
30		name       string
31		args       args
32		wantModule string
33	}{
34		{
35			name: "file",
36			args: args{
37				s: "foo",
38			},
39			wantModule: "",
40		},
41		{
42			name: "module",
43			args: args{
44				s: ":foo",
45			},
46			wantModule: "foo",
47		},
48		{
49			name: "tag",
50			args: args{
51				s: ":foo{.bar}",
52			},
53			wantModule: "foo{.bar}",
54		},
55		{
56			name: "extra colon",
57			args: args{
58				s: ":foo:bar",
59			},
60			wantModule: "foo:bar",
61		},
62		{
63			name: "fully qualified",
64			args: args{
65				s: "//foo:bar",
66			},
67			wantModule: "//foo:bar",
68		},
69		{
70			name: "fully qualified with tag",
71			args: args{
72				s: "//foo:bar{.tag}",
73			},
74			wantModule: "//foo:bar{.tag}",
75		},
76		{
77			name: "invalid unqualified name",
78			args: args{
79				s: ":foo/bar",
80			},
81			wantModule: "",
82		},
83	}
84	for _, tt := range tests {
85		t.Run(tt.name, func(t *testing.T) {
86			if gotModule := SrcIsModule(tt.args.s); gotModule != tt.wantModule {
87				t.Errorf("SrcIsModule() = %v, want %v", gotModule, tt.wantModule)
88			}
89		})
90	}
91}
92
93func TestSrcIsModuleWithTag(t *testing.T) {
94	type args struct {
95		s string
96	}
97	tests := []struct {
98		name       string
99		args       args
100		wantModule string
101		wantTag    string
102	}{
103		{
104			name: "file",
105			args: args{
106				s: "foo",
107			},
108			wantModule: "",
109			wantTag:    "",
110		},
111		{
112			name: "module",
113			args: args{
114				s: ":foo",
115			},
116			wantModule: "foo",
117			wantTag:    "",
118		},
119		{
120			name: "tag",
121			args: args{
122				s: ":foo{.bar}",
123			},
124			wantModule: "foo",
125			wantTag:    ".bar",
126		},
127		{
128			name: "empty tag",
129			args: args{
130				s: ":foo{}",
131			},
132			wantModule: "foo",
133			wantTag:    "",
134		},
135		{
136			name: "extra colon",
137			args: args{
138				s: ":foo:bar",
139			},
140			wantModule: "foo:bar",
141		},
142		{
143			name: "invalid tag",
144			args: args{
145				s: ":foo{.bar",
146			},
147			wantModule: "foo{.bar",
148		},
149		{
150			name: "invalid tag 2",
151			args: args{
152				s: ":foo.bar}",
153			},
154			wantModule: "foo.bar}",
155		},
156		{
157			name: "fully qualified",
158			args: args{
159				s: "//foo:bar",
160			},
161			wantModule: "//foo:bar",
162		},
163		{
164			name: "fully qualified with tag",
165			args: args{
166				s: "//foo:bar{.tag}",
167			},
168			wantModule: "//foo:bar",
169			wantTag:    ".tag",
170		},
171		{
172			name: "invalid unqualified name",
173			args: args{
174				s: ":foo/bar",
175			},
176			wantModule: "",
177		},
178		{
179			name: "invalid unqualified name with tag",
180			args: args{
181				s: ":foo/bar{.tag}",
182			},
183			wantModule: "",
184		},
185	}
186	for _, tt := range tests {
187		t.Run(tt.name, func(t *testing.T) {
188			gotModule, gotTag := SrcIsModuleWithTag(tt.args.s)
189			if gotModule != tt.wantModule {
190				t.Errorf("SrcIsModuleWithTag() gotModule = %v, want %v", gotModule, tt.wantModule)
191			}
192			if gotTag != tt.wantTag {
193				t.Errorf("SrcIsModuleWithTag() gotTag = %v, want %v", gotTag, tt.wantTag)
194			}
195		})
196	}
197}
198
199type depsModule struct {
200	ModuleBase
201	props struct {
202		Deps []string
203	}
204}
205
206func (m *depsModule) GenerateAndroidBuildActions(ctx ModuleContext) {
207	outputFile := PathForModuleOut(ctx, ctx.ModuleName())
208	ctx.Build(pctx, BuildParams{
209		Rule:   Touch,
210		Output: outputFile,
211	})
212	installFile := ctx.InstallFile(PathForModuleInstall(ctx), ctx.ModuleName(), outputFile)
213	ctx.InstallSymlink(PathForModuleInstall(ctx, "symlinks"), ctx.ModuleName(), installFile)
214}
215
216func (m *depsModule) DepsMutator(ctx BottomUpMutatorContext) {
217	ctx.AddDependency(ctx.Module(), installDepTag{}, m.props.Deps...)
218}
219
220func depsModuleFactory() Module {
221	m := &depsModule{}
222	m.AddProperties(&m.props)
223	InitAndroidArchModule(m, HostAndDeviceDefault, MultilibCommon)
224	return m
225}
226
227var prepareForModuleTests = FixtureRegisterWithContext(func(ctx RegistrationContext) {
228	ctx.RegisterModuleType("deps", depsModuleFactory)
229})
230
231func TestErrorDependsOnDisabledModule(t *testing.T) {
232	bp := `
233		deps {
234			name: "foo",
235			deps: ["bar"],
236		}
237		deps {
238			name: "bar",
239			enabled: false,
240		}
241	`
242
243	prepareForModuleTests.
244		ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo": depends on disabled module "bar"`)).
245		RunTestWithBp(t, bp)
246}
247
248func TestDistErrorChecking(t *testing.T) {
249	bp := `
250		deps {
251			name: "foo",
252      dist: {
253        dest: "../invalid-dest",
254        dir: "../invalid-dir",
255        suffix: "invalid/suffix",
256      },
257      dists: [
258        {
259          dest: "../invalid-dest0",
260          dir: "../invalid-dir0",
261          suffix: "invalid/suffix0",
262        },
263        {
264          dest: "../invalid-dest1",
265          dir: "../invalid-dir1",
266          suffix: "invalid/suffix1",
267        },
268      ],
269 		}
270	`
271
272	expectedErrs := []string{
273		"\\QAndroid.bp:5:13: module \"foo\": dist.dest: Path is outside directory: ../invalid-dest\\E",
274		"\\QAndroid.bp:6:12: module \"foo\": dist.dir: Path is outside directory: ../invalid-dir\\E",
275		"\\QAndroid.bp:7:15: module \"foo\": dist.suffix: Suffix may not contain a '/' character.\\E",
276		"\\QAndroid.bp:11:15: module \"foo\": dists[0].dest: Path is outside directory: ../invalid-dest0\\E",
277		"\\QAndroid.bp:12:14: module \"foo\": dists[0].dir: Path is outside directory: ../invalid-dir0\\E",
278		"\\QAndroid.bp:13:17: module \"foo\": dists[0].suffix: Suffix may not contain a '/' character.\\E",
279		"\\QAndroid.bp:16:15: module \"foo\": dists[1].dest: Path is outside directory: ../invalid-dest1\\E",
280		"\\QAndroid.bp:17:14: module \"foo\": dists[1].dir: Path is outside directory: ../invalid-dir1\\E",
281		"\\QAndroid.bp:18:17: module \"foo\": dists[1].suffix: Suffix may not contain a '/' character.\\E",
282	}
283
284	prepareForModuleTests.
285		ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(expectedErrs)).
286		RunTestWithBp(t, bp)
287}
288
289func TestInstall(t *testing.T) {
290	if runtime.GOOS != "linux" {
291		t.Skip("requires linux")
292	}
293	bp := `
294		deps {
295			name: "foo",
296			deps: ["bar"],
297		}
298
299		deps {
300			name: "bar",
301			deps: ["baz", "qux"],
302		}
303
304		deps {
305			name: "baz",
306			deps: ["qux"],
307		}
308
309		deps {
310			name: "qux",
311		}
312	`
313
314	result := GroupFixturePreparers(
315		prepareForModuleTests,
316		PrepareForTestWithArchMutator,
317	).RunTestWithBp(t, bp)
318
319	module := func(name string, host bool) TestingModule {
320		variant := "android_common"
321		if host {
322			variant = result.Config.BuildOSCommonTarget.String()
323		}
324		return result.ModuleForTests(name, variant)
325	}
326
327	outputRule := func(name string) TestingBuildParams { return module(name, false).Output(name) }
328
329	installRule := func(name string) TestingBuildParams {
330		return module(name, false).Output(filepath.Join("out/soong/target/product/test_device/system", name))
331	}
332
333	symlinkRule := func(name string) TestingBuildParams {
334		return module(name, false).Output(filepath.Join("out/soong/target/product/test_device/system/symlinks", name))
335	}
336
337	hostOutputRule := func(name string) TestingBuildParams { return module(name, true).Output(name) }
338
339	hostInstallRule := func(name string) TestingBuildParams {
340		return module(name, true).Output(filepath.Join("out/soong/host/linux-x86", name))
341	}
342
343	hostSymlinkRule := func(name string) TestingBuildParams {
344		return module(name, true).Output(filepath.Join("out/soong/host/linux-x86/symlinks", name))
345	}
346
347	assertInputs := func(params TestingBuildParams, inputs ...Path) {
348		t.Helper()
349		AssertArrayString(t, "expected inputs", Paths(inputs).Strings(),
350			append(PathsIfNonNil(params.Input), params.Inputs...).Strings())
351	}
352
353	assertImplicits := func(params TestingBuildParams, implicits ...Path) {
354		t.Helper()
355		AssertArrayString(t, "expected implicit dependencies", Paths(implicits).Strings(),
356			append(PathsIfNonNil(params.Implicit), params.Implicits...).Strings())
357	}
358
359	assertOrderOnlys := func(params TestingBuildParams, orderonlys ...Path) {
360		t.Helper()
361		AssertArrayString(t, "expected orderonly dependencies", Paths(orderonlys).Strings(),
362			params.OrderOnly.Strings())
363	}
364
365	// Check host install rule dependencies
366	assertInputs(hostInstallRule("foo"), hostOutputRule("foo").Output)
367	assertImplicits(hostInstallRule("foo"),
368		hostInstallRule("bar").Output,
369		hostSymlinkRule("bar").Output,
370		hostInstallRule("baz").Output,
371		hostSymlinkRule("baz").Output,
372		hostInstallRule("qux").Output,
373		hostSymlinkRule("qux").Output,
374	)
375	assertOrderOnlys(hostInstallRule("foo"))
376
377	// Check host symlink rule dependencies.  Host symlinks must use a normal dependency, not an
378	// order-only dependency, so that the tool gets updated when the symlink is depended on.
379	assertInputs(hostSymlinkRule("foo"), hostInstallRule("foo").Output)
380	assertImplicits(hostSymlinkRule("foo"))
381	assertOrderOnlys(hostSymlinkRule("foo"))
382
383	// Check device install rule dependencies
384	assertInputs(installRule("foo"), outputRule("foo").Output)
385	assertImplicits(installRule("foo"))
386	assertOrderOnlys(installRule("foo"),
387		installRule("bar").Output,
388		symlinkRule("bar").Output,
389		installRule("baz").Output,
390		symlinkRule("baz").Output,
391		installRule("qux").Output,
392		symlinkRule("qux").Output,
393	)
394
395	// Check device symlink rule dependencies.  Device symlinks could use an order-only dependency,
396	// but the current implementation uses a normal dependency.
397	assertInputs(symlinkRule("foo"), installRule("foo").Output)
398	assertImplicits(symlinkRule("foo"))
399	assertOrderOnlys(symlinkRule("foo"))
400}
401
402func TestInstallKatiEnabled(t *testing.T) {
403	if runtime.GOOS != "linux" {
404		t.Skip("requires linux")
405	}
406	bp := `
407		deps {
408			name: "foo",
409			deps: ["bar"],
410		}
411
412		deps {
413			name: "bar",
414			deps: ["baz", "qux"],
415		}
416
417		deps {
418			name: "baz",
419			deps: ["qux"],
420		}
421
422		deps {
423			name: "qux",
424		}
425	`
426
427	result := GroupFixturePreparers(
428		prepareForModuleTests,
429		PrepareForTestWithArchMutator,
430		FixtureModifyConfig(SetKatiEnabledForTests),
431		PrepareForTestWithMakevars,
432	).RunTestWithBp(t, bp)
433
434	rules := result.InstallMakeRulesForTesting(t)
435
436	module := func(name string, host bool) TestingModule {
437		variant := "android_common"
438		if host {
439			variant = result.Config.BuildOSCommonTarget.String()
440		}
441		return result.ModuleForTests(name, variant)
442	}
443
444	outputRule := func(name string) TestingBuildParams { return module(name, false).Output(name) }
445
446	ruleForOutput := func(output string) InstallMakeRule {
447		for _, rule := range rules {
448			if rule.Target == output {
449				return rule
450			}
451		}
452		t.Fatalf("no make install rule for %s", output)
453		return InstallMakeRule{}
454	}
455
456	installRule := func(name string) InstallMakeRule {
457		return ruleForOutput(filepath.Join("out/target/product/test_device/system", name))
458	}
459
460	symlinkRule := func(name string) InstallMakeRule {
461		return ruleForOutput(filepath.Join("out/target/product/test_device/system/symlinks", name))
462	}
463
464	hostOutputRule := func(name string) TestingBuildParams { return module(name, true).Output(name) }
465
466	hostInstallRule := func(name string) InstallMakeRule {
467		return ruleForOutput(filepath.Join("out/host/linux-x86", name))
468	}
469
470	hostSymlinkRule := func(name string) InstallMakeRule {
471		return ruleForOutput(filepath.Join("out/host/linux-x86/symlinks", name))
472	}
473
474	assertDeps := func(rule InstallMakeRule, deps ...string) {
475		t.Helper()
476		AssertArrayString(t, "expected inputs", deps, rule.Deps)
477	}
478
479	assertOrderOnlys := func(rule InstallMakeRule, orderonlys ...string) {
480		t.Helper()
481		AssertArrayString(t, "expected orderonly dependencies", orderonlys, rule.OrderOnlyDeps)
482	}
483
484	// Check host install rule dependencies
485	assertDeps(hostInstallRule("foo"),
486		hostOutputRule("foo").Output.String(),
487		hostInstallRule("bar").Target,
488		hostSymlinkRule("bar").Target,
489		hostInstallRule("baz").Target,
490		hostSymlinkRule("baz").Target,
491		hostInstallRule("qux").Target,
492		hostSymlinkRule("qux").Target,
493	)
494	assertOrderOnlys(hostInstallRule("foo"))
495
496	// Check host symlink rule dependencies.  Host symlinks must use a normal dependency, not an
497	// order-only dependency, so that the tool gets updated when the symlink is depended on.
498	assertDeps(hostSymlinkRule("foo"), hostInstallRule("foo").Target)
499	assertOrderOnlys(hostSymlinkRule("foo"))
500
501	// Check device install rule dependencies
502	assertDeps(installRule("foo"), outputRule("foo").Output.String())
503	assertOrderOnlys(installRule("foo"),
504		installRule("bar").Target,
505		symlinkRule("bar").Target,
506		installRule("baz").Target,
507		symlinkRule("baz").Target,
508		installRule("qux").Target,
509		symlinkRule("qux").Target,
510	)
511
512	// Check device symlink rule dependencies.  Device symlinks could use an order-only dependency,
513	// but the current implementation uses a normal dependency.
514	assertDeps(symlinkRule("foo"), installRule("foo").Target)
515	assertOrderOnlys(symlinkRule("foo"))
516}
517
518type PropsTestModuleEmbedded struct {
519	Embedded_prop *string
520}
521
522type StructInSlice struct {
523	G string
524	H bool
525	I []string
526}
527
528type propsTestModule struct {
529	ModuleBase
530	DefaultableModuleBase
531	props struct {
532		A string `android:"arch_variant"`
533		B *bool
534		C []string
535	}
536	otherProps struct {
537		PropsTestModuleEmbedded
538
539		D      *int64
540		Nested struct {
541			E *string
542		}
543		F *string `blueprint:"mutated"`
544
545		Slice_of_struct []StructInSlice
546	}
547}
548
549func propsTestModuleFactory() Module {
550	module := &propsTestModule{}
551	module.AddProperties(&module.props, &module.otherProps)
552	InitAndroidArchModule(module, HostAndDeviceSupported, MultilibBoth)
553	InitDefaultableModule(module)
554	return module
555}
556
557type propsTestModuleDefaults struct {
558	ModuleBase
559	DefaultsModuleBase
560}
561
562func propsTestModuleDefaultsFactory() Module {
563	defaults := &propsTestModuleDefaults{}
564	module := propsTestModule{}
565	defaults.AddProperties(&module.props, &module.otherProps)
566	InitDefaultsModule(defaults)
567	return defaults
568}
569
570func (p *propsTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
571	str := "abc"
572	p.otherProps.F = &str
573}
574
575func TestUsedProperties(t *testing.T) {
576	testCases := []struct {
577		desc          string
578		bp            string
579		expectedProps []propInfo
580	}{
581		{
582			desc: "only name",
583			bp: `test {
584			name: "foo",
585		}
586	`,
587			expectedProps: []propInfo{
588				propInfo{Name: "Name", Type: "string", Value: "foo"},
589			},
590		},
591		{
592			desc: "some props",
593			bp: `test {
594			name: "foo",
595			a: "abc",
596			b: true,
597			d: 123,
598		}
599	`,
600			expectedProps: []propInfo{
601				propInfo{Name: "A", Type: "string", Value: "abc"},
602				propInfo{Name: "B", Type: "bool", Value: "true"},
603				propInfo{Name: "D", Type: "int64", Value: "123"},
604				propInfo{Name: "Name", Type: "string", Value: "foo"},
605			},
606		},
607		{
608			desc: "unused non-pointer prop",
609			bp: `test {
610			name: "foo",
611			b: true,
612			d: 123,
613		}
614	`,
615			expectedProps: []propInfo{
616				// for non-pointer cannot distinguish between unused and intentionally set to empty
617				propInfo{Name: "A", Type: "string", Value: ""},
618				propInfo{Name: "B", Type: "bool", Value: "true"},
619				propInfo{Name: "D", Type: "int64", Value: "123"},
620				propInfo{Name: "Name", Type: "string", Value: "foo"},
621			},
622		},
623		{
624			desc: "nested props",
625			bp: `test {
626			name: "foo",
627			nested: {
628				e: "abc",
629			}
630		}
631	`,
632			expectedProps: []propInfo{
633				propInfo{Name: "Name", Type: "string", Value: "foo"},
634				propInfo{Name: "Nested.E", Type: "string", Value: "abc"},
635			},
636		},
637		{
638			desc: "arch props",
639			bp: `test {
640			name: "foo",
641			arch: {
642				x86_64: {
643					a: "abc",
644				},
645			}
646		}
647	`,
648			expectedProps: []propInfo{
649				propInfo{Name: "Arch.X86_64.A", Type: "string", Value: "abc"},
650				propInfo{Name: "Name", Type: "string", Value: "foo"},
651			},
652		},
653		{
654			desc: "embedded props",
655			bp: `test {
656			name: "foo",
657			embedded_prop: "a",
658		}
659	`,
660			expectedProps: []propInfo{
661				propInfo{Name: "Embedded_prop", Type: "string", Value: "a"},
662				propInfo{Name: "Name", Type: "string", Value: "foo"},
663			},
664		},
665		{
666			desc: "struct slice",
667			bp: `test {
668			name: "foo",
669			slice_of_struct: [
670				{
671					g: "abc",
672					h: false,
673					i: ["baz"],
674				},
675				{
676					g: "def",
677					h: true,
678					i: [],
679				},
680			]
681		}
682	`,
683			expectedProps: []propInfo{
684				propInfo{Name: "Name", Type: "string", Value: "foo"},
685				propInfo{Name: "Slice_of_struct", Type: "struct slice", Values: []string{
686					`android.StructInSlice{G: abc, H: false, I: [baz]}`,
687					`android.StructInSlice{G: def, H: true, I: []}`,
688				}},
689			},
690		},
691		{
692			desc: "defaults",
693			bp: `
694test_defaults {
695	name: "foo_defaults",
696	a: "a",
697	b: true,
698	c: ["default_c"],
699	embedded_prop:"a",
700	arch: {
701		x86_64: {
702			a: "x86_64 a",
703		},
704	},
705}
706test {
707	name: "foo",
708	defaults: ["foo_defaults"],
709	c: ["c"],
710	nested: {
711		e: "nested e",
712	},
713	target: {
714		linux: {
715			a: "a",
716		},
717	},
718}
719	`,
720			expectedProps: []propInfo{
721				propInfo{Name: "A", Type: "string", Value: "a"},
722				propInfo{Name: "Arch.X86_64.A", Type: "string", Value: "x86_64 a"},
723				propInfo{Name: "B", Type: "bool", Value: "true"},
724				propInfo{Name: "C", Type: "string slice", Values: []string{"default_c", "c"}},
725				propInfo{Name: "Defaults", Type: "string slice", Values: []string{"foo_defaults"}},
726				propInfo{Name: "Embedded_prop", Type: "string", Value: "a"},
727				propInfo{Name: "Name", Type: "string", Value: "foo"},
728				propInfo{Name: "Nested.E", Type: "string", Value: "nested e"},
729				propInfo{Name: "Target.Linux.A", Type: "string", Value: "a"},
730			},
731		},
732	}
733
734	for _, tc := range testCases {
735		t.Run(tc.desc, func(t *testing.T) {
736			result := GroupFixturePreparers(
737				PrepareForTestWithAllowMissingDependencies,
738				PrepareForTestWithDefaults,
739				FixtureRegisterWithContext(func(ctx RegistrationContext) {
740					ctx.RegisterModuleType("test", propsTestModuleFactory)
741					ctx.RegisterModuleType("test_defaults", propsTestModuleDefaultsFactory)
742				}),
743				FixtureWithRootAndroidBp(tc.bp),
744			).RunTest(t)
745
746			foo := result.ModuleForTests("foo", "").Module().base()
747
748			AssertDeepEquals(t, "foo ", tc.expectedProps, foo.propertiesWithValues())
749		})
750	}
751}
752
753func TestSortedUniqueNamedPaths(t *testing.T) {
754	type np struct {
755		path, name string
756	}
757	makePaths := func(l []np) NamedPaths {
758		result := make(NamedPaths, 0, len(l))
759		for _, p := range l {
760			result = append(result, NamedPath{PathForTesting(p.path), p.name})
761		}
762		return result
763	}
764
765	tests := []struct {
766		name        string
767		in          []np
768		expectedOut []np
769	}{
770		{
771			name:        "empty",
772			in:          []np{},
773			expectedOut: []np{},
774		},
775		{
776			name: "all_same",
777			in: []np{
778				{"a.txt", "A"},
779				{"a.txt", "A"},
780				{"a.txt", "A"},
781				{"a.txt", "A"},
782				{"a.txt", "A"},
783			},
784			expectedOut: []np{
785				{"a.txt", "A"},
786			},
787		},
788		{
789			name: "same_path_different_names",
790			in: []np{
791				{"a.txt", "C"},
792				{"a.txt", "A"},
793				{"a.txt", "D"},
794				{"a.txt", "B"},
795				{"a.txt", "E"},
796			},
797			expectedOut: []np{
798				{"a.txt", "A"},
799				{"a.txt", "B"},
800				{"a.txt", "C"},
801				{"a.txt", "D"},
802				{"a.txt", "E"},
803			},
804		},
805		{
806			name: "different_paths_same_name",
807			in: []np{
808				{"b/b.txt", "A"},
809				{"a/a.txt", "A"},
810				{"a/txt", "A"},
811				{"b", "A"},
812				{"a/b/d", "A"},
813			},
814			expectedOut: []np{
815				{"a/a.txt", "A"},
816				{"a/b/d", "A"},
817				{"a/txt", "A"},
818				{"b/b.txt", "A"},
819				{"b", "A"},
820			},
821		},
822		{
823			name: "all_different",
824			in: []np{
825				{"b/b.txt", "A"},
826				{"a/a.txt", "B"},
827				{"a/txt", "D"},
828				{"b", "C"},
829				{"a/b/d", "E"},
830			},
831			expectedOut: []np{
832				{"a/a.txt", "B"},
833				{"a/b/d", "E"},
834				{"a/txt", "D"},
835				{"b/b.txt", "A"},
836				{"b", "C"},
837			},
838		},
839		{
840			name: "some_different",
841			in: []np{
842				{"b/b.txt", "A"},
843				{"a/a.txt", "B"},
844				{"a/txt", "D"},
845				{"a/b/d", "E"},
846				{"b", "C"},
847				{"a/a.txt", "B"},
848				{"a/b/d", "E"},
849			},
850			expectedOut: []np{
851				{"a/a.txt", "B"},
852				{"a/b/d", "E"},
853				{"a/txt", "D"},
854				{"b/b.txt", "A"},
855				{"b", "C"},
856			},
857		},
858	}
859	for _, tt := range tests {
860		t.Run(tt.name, func(t *testing.T) {
861			actual := SortedUniqueNamedPaths(makePaths(tt.in))
862			expected := makePaths(tt.expectedOut)
863			t.Logf("actual: %v", actual)
864			t.Logf("expected: %v", expected)
865			AssertDeepEquals(t, "SortedUniqueNamedPaths ", expected, actual)
866		})
867	}
868}
869
870func TestSetAndroidMkEntriesWithTestOptions(t *testing.T) {
871	tests := []struct {
872		name        string
873		testOptions CommonTestOptions
874		expected    map[string][]string
875	}{
876		{
877			name:        "empty",
878			testOptions: CommonTestOptions{},
879			expected:    map[string][]string{},
880		},
881		{
882			name: "is unit test",
883			testOptions: CommonTestOptions{
884				Unit_test: boolPtr(true),
885			},
886			expected: map[string][]string{
887				"LOCAL_IS_UNIT_TEST": []string{"true"},
888			},
889		},
890		{
891			name: "is not unit test",
892			testOptions: CommonTestOptions{
893				Unit_test: boolPtr(false),
894			},
895			expected: map[string][]string{},
896		},
897		{
898			name: "empty tag",
899			testOptions: CommonTestOptions{
900				Tags: []string{},
901			},
902			expected: map[string][]string{},
903		},
904		{
905			name: "single tag",
906			testOptions: CommonTestOptions{
907				Tags: []string{"tag1"},
908			},
909			expected: map[string][]string{
910				"LOCAL_TEST_OPTIONS_TAGS": []string{"tag1"},
911			},
912		},
913		{
914			name: "multiple tag",
915			testOptions: CommonTestOptions{
916				Tags: []string{"tag1", "tag2", "tag3"},
917			},
918			expected: map[string][]string{
919				"LOCAL_TEST_OPTIONS_TAGS": []string{"tag1", "tag2", "tag3"},
920			},
921		},
922	}
923	for _, tt := range tests {
924		t.Run(tt.name, func(t *testing.T) {
925			actualEntries := AndroidMkEntries{
926				EntryMap: map[string][]string{},
927			}
928			tt.testOptions.SetAndroidMkEntries(&actualEntries)
929			actual := actualEntries.EntryMap
930			t.Logf("actual: %v", actual)
931			t.Logf("expected: %v", tt.expected)
932			AssertDeepEquals(t, "TestProcessCommonTestOptions ", tt.expected, actual)
933		})
934	}
935}
936
937type sourceProducerTestModule struct {
938	ModuleBase
939	props struct {
940		// A represents the source file
941		A string
942	}
943}
944
945func sourceProducerTestModuleFactory() Module {
946	module := &sourceProducerTestModule{}
947	module.AddProperties(&module.props)
948	InitAndroidModule(module)
949	return module
950}
951
952func (s sourceProducerTestModule) GenerateAndroidBuildActions(ModuleContext) {}
953
954func (s sourceProducerTestModule) Srcs() Paths { return PathsForTesting(s.props.A) }
955
956type outputFilesTestModule struct {
957	ModuleBase
958	props struct {
959		// A represents the tag
960		A string
961		// B represents the output file for tag A
962		B string
963	}
964}
965
966func outputFilesTestModuleFactory() Module {
967	module := &outputFilesTestModule{}
968	module.AddProperties(&module.props)
969	InitAndroidModule(module)
970	return module
971}
972
973func (o outputFilesTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
974	if o.props.A != "" || o.props.B != "" {
975		ctx.SetOutputFiles(PathsForTesting(o.props.B), o.props.A)
976	}
977	// This is to simulate the case that some module uses an object to set its
978	// OutputFilesProvider, but the object itself is empty.
979	ctx.SetOutputFiles(Paths{}, "missing")
980}
981
982type pathContextAddMissingDependenciesWrapper struct {
983	PathContext
984	OtherModuleProviderContext
985	missingDeps []string
986}
987
988func (p *pathContextAddMissingDependenciesWrapper) AddMissingDependencies(deps []string) {
989	p.missingDeps = append(p.missingDeps, deps...)
990}
991func (p *pathContextAddMissingDependenciesWrapper) OtherModuleName(module blueprint.Module) string {
992	return module.Name()
993}
994
995func (p *pathContextAddMissingDependenciesWrapper) Module() Module { return nil }
996
997func (p *pathContextAddMissingDependenciesWrapper) GetOutputFiles() OutputFilesInfo {
998	return OutputFilesInfo{}
999}
1000
1001func (p *pathContextAddMissingDependenciesWrapper) EqualModules(m1, m2 Module) bool {
1002	return m1 == m2
1003}
1004
1005func TestOutputFileForModule(t *testing.T) {
1006	testcases := []struct {
1007		name        string
1008		bp          string
1009		tag         string
1010		expected    string
1011		missingDeps []string
1012		env         map[string]string
1013		config      func(*config)
1014	}{
1015		{
1016			name: "SourceFileProducer",
1017			bp: `spt_module {
1018					name: "test_module",
1019					a: "spt.txt",
1020				}
1021			`,
1022			tag:      "",
1023			expected: "spt.txt",
1024		},
1025		{
1026			name: "OutputFileProviderEmptyStringTag",
1027			bp: `oft_module {
1028					name: "test_module",
1029					a: "",
1030					b: "empty.txt",
1031				}
1032		`,
1033			tag:      "",
1034			expected: "empty.txt",
1035		},
1036		{
1037			name: "OutputFileProviderTag",
1038			bp: `oft_module {
1039					name: "test_module",
1040					a: "foo",
1041					b: "foo.txt",
1042				}
1043			`,
1044			tag:      "foo",
1045			expected: "foo.txt",
1046		},
1047		{
1048			name: "OutputFileAllowMissingDependencies",
1049			bp: `oft_module {
1050				name: "test_module",
1051			}
1052		`,
1053			tag:         "missing",
1054			expected:    "missing_output_file/test_module",
1055			missingDeps: []string{"test_module"},
1056			config: func(config *config) {
1057				config.TestProductVariables.Allow_missing_dependencies = boolPtr(true)
1058			},
1059		},
1060	}
1061
1062	for _, tt := range testcases {
1063		t.Run(tt.name, func(t *testing.T) {
1064			result := GroupFixturePreparers(
1065				PrepareForTestWithDefaults,
1066				FixtureRegisterWithContext(func(ctx RegistrationContext) {
1067					ctx.RegisterModuleType("spt_module", sourceProducerTestModuleFactory)
1068					ctx.RegisterModuleType("oft_module", outputFilesTestModuleFactory)
1069				}),
1070				FixtureWithRootAndroidBp(tt.bp),
1071			).RunTest(t)
1072
1073			config := TestConfig(buildDir, tt.env, tt.bp, nil)
1074			if tt.config != nil {
1075				tt.config(config.config)
1076			}
1077			ctx := &pathContextAddMissingDependenciesWrapper{
1078				PathContext:                PathContextForTesting(config),
1079				OtherModuleProviderContext: result.TestContext.OtherModuleProviderAdaptor(),
1080			}
1081			got := OutputFileForModule(ctx, result.ModuleForTests("test_module", "").Module(), tt.tag)
1082			AssertPathRelativeToTopEquals(t, "expected output path", tt.expected, got)
1083			AssertArrayString(t, "expected missing deps", tt.missingDeps, ctx.missingDeps)
1084		})
1085	}
1086}
1087
1088func TestVintfFragmentModulesChecksPartition(t *testing.T) {
1089	bp := `
1090	vintf_fragment {
1091		name: "vintfModA",
1092		src: "test_vintf_file",
1093		vendor: true,
1094	}
1095	deps {
1096		name: "modA",
1097		vintf_fragment_modules: [
1098			"vintfModA",
1099		]
1100	}
1101	`
1102
1103	testPreparer := GroupFixturePreparers(
1104		PrepareForTestWithAndroidBuildComponents,
1105		prepareForModuleTests,
1106	)
1107
1108	testPreparer.
1109		ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(
1110			"Module .+ and Vintf_fragment .+ are installed to different partitions.")).
1111		RunTestWithBp(t, bp)
1112}
1113