• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(t, 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/target/product/test_device/system", name))
331	}
332
333	symlinkRule := func(name string) TestingBuildParams {
334		return module(name, false).Output(filepath.Join("out/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/host/linux-x86", name))
341	}
342
343	hostSymlinkRule := func(name string) TestingBuildParams {
344		return module(name, true).Output(filepath.Join("out/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		t.Helper()
438		variant := "android_common"
439		if host {
440			variant = result.Config.BuildOSCommonTarget.String()
441		}
442		return result.ModuleForTests(t, name, variant)
443	}
444
445	outputRule := func(name string) TestingBuildParams { return module(name, false).Output(name) }
446
447	ruleForOutput := func(output string) InstallMakeRule {
448		for _, rule := range rules {
449			if rule.Target == output {
450				return rule
451			}
452		}
453		t.Fatalf("no make install rule for %s", output)
454		return InstallMakeRule{}
455	}
456
457	installRule := func(name string) InstallMakeRule {
458		return ruleForOutput(filepath.Join("out/target/product/test_device/system", name))
459	}
460
461	symlinkRule := func(name string) InstallMakeRule {
462		return ruleForOutput(filepath.Join("out/target/product/test_device/system/symlinks", name))
463	}
464
465	hostOutputRule := func(name string) TestingBuildParams { return module(name, true).Output(name) }
466
467	hostInstallRule := func(name string) InstallMakeRule {
468		return ruleForOutput(filepath.Join("out/host/linux-x86", name))
469	}
470
471	hostSymlinkRule := func(name string) InstallMakeRule {
472		return ruleForOutput(filepath.Join("out/host/linux-x86/symlinks", name))
473	}
474
475	assertDeps := func(rule InstallMakeRule, deps ...string) {
476		t.Helper()
477		AssertArrayString(t, "expected inputs", deps, rule.Deps)
478	}
479
480	assertOrderOnlys := func(rule InstallMakeRule, orderonlys ...string) {
481		t.Helper()
482		AssertArrayString(t, "expected orderonly dependencies", orderonlys, rule.OrderOnlyDeps)
483	}
484
485	// Check host install rule dependencies
486	assertDeps(hostInstallRule("foo"),
487		hostOutputRule("foo").Output.String(),
488		hostInstallRule("bar").Target,
489		hostSymlinkRule("bar").Target,
490		hostInstallRule("baz").Target,
491		hostSymlinkRule("baz").Target,
492		hostInstallRule("qux").Target,
493		hostSymlinkRule("qux").Target,
494	)
495	assertOrderOnlys(hostInstallRule("foo"))
496
497	// Check host symlink rule dependencies.  Host symlinks must use a normal dependency, not an
498	// order-only dependency, so that the tool gets updated when the symlink is depended on.
499	assertDeps(hostSymlinkRule("foo"), hostInstallRule("foo").Target)
500	assertOrderOnlys(hostSymlinkRule("foo"))
501
502	// Check device install rule dependencies
503	assertDeps(installRule("foo"), outputRule("foo").Output.String())
504	assertOrderOnlys(installRule("foo"),
505		installRule("bar").Target,
506		symlinkRule("bar").Target,
507		installRule("baz").Target,
508		symlinkRule("baz").Target,
509		installRule("qux").Target,
510		symlinkRule("qux").Target,
511	)
512
513	// Check device symlink rule dependencies.  Device symlinks could use an order-only dependency,
514	// but the current implementation uses a normal dependency.
515	assertDeps(symlinkRule("foo"), installRule("foo").Target)
516	assertOrderOnlys(symlinkRule("foo"))
517}
518
519type PropsTestModuleEmbedded struct {
520	Embedded_prop *string
521}
522
523type StructInSlice struct {
524	G string
525	H bool
526	I []string
527}
528
529type propsTestModule struct {
530	ModuleBase
531	DefaultableModuleBase
532	props struct {
533		A string `android:"arch_variant"`
534		B *bool
535		C []string
536	}
537	otherProps struct {
538		PropsTestModuleEmbedded
539
540		D      *int64
541		Nested struct {
542			E *string
543		}
544		F *string `blueprint:"mutated"`
545
546		Slice_of_struct []StructInSlice
547	}
548}
549
550func propsTestModuleFactory() Module {
551	module := &propsTestModule{}
552	module.AddProperties(&module.props, &module.otherProps)
553	InitAndroidArchModule(module, HostAndDeviceSupported, MultilibBoth)
554	InitDefaultableModule(module)
555	return module
556}
557
558type propsTestModuleDefaults struct {
559	ModuleBase
560	DefaultsModuleBase
561}
562
563func propsTestModuleDefaultsFactory() Module {
564	defaults := &propsTestModuleDefaults{}
565	module := propsTestModule{}
566	defaults.AddProperties(&module.props, &module.otherProps)
567	InitDefaultsModule(defaults)
568	return defaults
569}
570
571func (p *propsTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
572	str := "abc"
573	p.otherProps.F = &str
574}
575
576func TestUsedProperties(t *testing.T) {
577	testCases := []struct {
578		desc          string
579		bp            string
580		expectedProps []propInfo
581	}{
582		{
583			desc: "only name",
584			bp: `test {
585			name: "foo",
586		}
587	`,
588			expectedProps: []propInfo{
589				propInfo{Name: "Name", Type: "string", Value: "foo"},
590			},
591		},
592		{
593			desc: "some props",
594			bp: `test {
595			name: "foo",
596			a: "abc",
597			b: true,
598			d: 123,
599		}
600	`,
601			expectedProps: []propInfo{
602				propInfo{Name: "A", Type: "string", Value: "abc"},
603				propInfo{Name: "B", Type: "bool", Value: "true"},
604				propInfo{Name: "D", Type: "int64", Value: "123"},
605				propInfo{Name: "Name", Type: "string", Value: "foo"},
606			},
607		},
608		{
609			desc: "unused non-pointer prop",
610			bp: `test {
611			name: "foo",
612			b: true,
613			d: 123,
614		}
615	`,
616			expectedProps: []propInfo{
617				// for non-pointer cannot distinguish between unused and intentionally set to empty
618				propInfo{Name: "A", Type: "string", Value: ""},
619				propInfo{Name: "B", Type: "bool", Value: "true"},
620				propInfo{Name: "D", Type: "int64", Value: "123"},
621				propInfo{Name: "Name", Type: "string", Value: "foo"},
622			},
623		},
624		{
625			desc: "nested props",
626			bp: `test {
627			name: "foo",
628			nested: {
629				e: "abc",
630			}
631		}
632	`,
633			expectedProps: []propInfo{
634				propInfo{Name: "Name", Type: "string", Value: "foo"},
635				propInfo{Name: "Nested.E", Type: "string", Value: "abc"},
636			},
637		},
638		{
639			desc: "arch props",
640			bp: `test {
641			name: "foo",
642			arch: {
643				x86_64: {
644					a: "abc",
645				},
646			}
647		}
648	`,
649			expectedProps: []propInfo{
650				propInfo{Name: "Arch.X86_64.A", Type: "string", Value: "abc"},
651				propInfo{Name: "Name", Type: "string", Value: "foo"},
652			},
653		},
654		{
655			desc: "embedded props",
656			bp: `test {
657			name: "foo",
658			embedded_prop: "a",
659		}
660	`,
661			expectedProps: []propInfo{
662				propInfo{Name: "Embedded_prop", Type: "string", Value: "a"},
663				propInfo{Name: "Name", Type: "string", Value: "foo"},
664			},
665		},
666		{
667			desc: "struct slice",
668			bp: `test {
669			name: "foo",
670			slice_of_struct: [
671				{
672					g: "abc",
673					h: false,
674					i: ["baz"],
675				},
676				{
677					g: "def",
678					h: true,
679					i: [],
680				},
681			]
682		}
683	`,
684			expectedProps: []propInfo{
685				propInfo{Name: "Name", Type: "string", Value: "foo"},
686				propInfo{Name: "Slice_of_struct", Type: "struct slice", Values: []string{
687					`android.StructInSlice{G: abc, H: false, I: [baz]}`,
688					`android.StructInSlice{G: def, H: true, I: []}`,
689				}},
690			},
691		},
692		{
693			desc: "defaults",
694			bp: `
695test_defaults {
696	name: "foo_defaults",
697	a: "a",
698	b: true,
699	c: ["default_c"],
700	embedded_prop:"a",
701	arch: {
702		x86_64: {
703			a: "x86_64 a",
704		},
705	},
706}
707test {
708	name: "foo",
709	defaults: ["foo_defaults"],
710	c: ["c"],
711	nested: {
712		e: "nested e",
713	},
714	target: {
715		linux: {
716			a: "a",
717		},
718	},
719}
720	`,
721			expectedProps: []propInfo{
722				propInfo{Name: "A", Type: "string", Value: "a"},
723				propInfo{Name: "Arch.X86_64.A", Type: "string", Value: "x86_64 a"},
724				propInfo{Name: "B", Type: "bool", Value: "true"},
725				propInfo{Name: "C", Type: "string slice", Values: []string{"default_c", "c"}},
726				propInfo{Name: "Defaults", Type: "string slice", Values: []string{"foo_defaults"}},
727				propInfo{Name: "Embedded_prop", Type: "string", Value: "a"},
728				propInfo{Name: "Name", Type: "string", Value: "foo"},
729				propInfo{Name: "Nested.E", Type: "string", Value: "nested e"},
730				propInfo{Name: "Target.Linux.A", Type: "string", Value: "a"},
731			},
732		},
733	}
734
735	for _, tc := range testCases {
736		t.Run(tc.desc, func(t *testing.T) {
737			result := GroupFixturePreparers(
738				PrepareForTestWithAllowMissingDependencies,
739				PrepareForTestWithDefaults,
740				FixtureRegisterWithContext(func(ctx RegistrationContext) {
741					ctx.RegisterModuleType("test", propsTestModuleFactory)
742					ctx.RegisterModuleType("test_defaults", propsTestModuleDefaultsFactory)
743				}),
744				FixtureWithRootAndroidBp(tc.bp),
745			).RunTest(t)
746
747			foo := result.ModuleForTests(t, "foo", "").Module().base()
748
749			AssertDeepEquals(t, "foo ", tc.expectedProps, foo.propertiesWithValues())
750		})
751	}
752}
753
754func TestSortedUniqueNamedPaths(t *testing.T) {
755	type np struct {
756		path, name string
757	}
758	makePaths := func(l []np) NamedPaths {
759		result := make(NamedPaths, 0, len(l))
760		for _, p := range l {
761			result = append(result, NamedPath{PathForTesting(p.path), p.name})
762		}
763		return result
764	}
765
766	tests := []struct {
767		name        string
768		in          []np
769		expectedOut []np
770	}{
771		{
772			name:        "empty",
773			in:          []np{},
774			expectedOut: []np{},
775		},
776		{
777			name: "all_same",
778			in: []np{
779				{"a.txt", "A"},
780				{"a.txt", "A"},
781				{"a.txt", "A"},
782				{"a.txt", "A"},
783				{"a.txt", "A"},
784			},
785			expectedOut: []np{
786				{"a.txt", "A"},
787			},
788		},
789		{
790			name: "same_path_different_names",
791			in: []np{
792				{"a.txt", "C"},
793				{"a.txt", "A"},
794				{"a.txt", "D"},
795				{"a.txt", "B"},
796				{"a.txt", "E"},
797			},
798			expectedOut: []np{
799				{"a.txt", "A"},
800				{"a.txt", "B"},
801				{"a.txt", "C"},
802				{"a.txt", "D"},
803				{"a.txt", "E"},
804			},
805		},
806		{
807			name: "different_paths_same_name",
808			in: []np{
809				{"b/b.txt", "A"},
810				{"a/a.txt", "A"},
811				{"a/txt", "A"},
812				{"b", "A"},
813				{"a/b/d", "A"},
814			},
815			expectedOut: []np{
816				{"a/a.txt", "A"},
817				{"a/b/d", "A"},
818				{"a/txt", "A"},
819				{"b/b.txt", "A"},
820				{"b", "A"},
821			},
822		},
823		{
824			name: "all_different",
825			in: []np{
826				{"b/b.txt", "A"},
827				{"a/a.txt", "B"},
828				{"a/txt", "D"},
829				{"b", "C"},
830				{"a/b/d", "E"},
831			},
832			expectedOut: []np{
833				{"a/a.txt", "B"},
834				{"a/b/d", "E"},
835				{"a/txt", "D"},
836				{"b/b.txt", "A"},
837				{"b", "C"},
838			},
839		},
840		{
841			name: "some_different",
842			in: []np{
843				{"b/b.txt", "A"},
844				{"a/a.txt", "B"},
845				{"a/txt", "D"},
846				{"a/b/d", "E"},
847				{"b", "C"},
848				{"a/a.txt", "B"},
849				{"a/b/d", "E"},
850			},
851			expectedOut: []np{
852				{"a/a.txt", "B"},
853				{"a/b/d", "E"},
854				{"a/txt", "D"},
855				{"b/b.txt", "A"},
856				{"b", "C"},
857			},
858		},
859	}
860	for _, tt := range tests {
861		t.Run(tt.name, func(t *testing.T) {
862			actual := SortedUniqueNamedPaths(makePaths(tt.in))
863			expected := makePaths(tt.expectedOut)
864			t.Logf("actual: %v", actual)
865			t.Logf("expected: %v", expected)
866			AssertDeepEquals(t, "SortedUniqueNamedPaths ", expected, actual)
867		})
868	}
869}
870
871func TestSetAndroidMkEntriesWithTestOptions(t *testing.T) {
872	tests := []struct {
873		name        string
874		testOptions CommonTestOptions
875		expected    map[string][]string
876	}{
877		{
878			name:        "empty",
879			testOptions: CommonTestOptions{},
880			expected:    map[string][]string{},
881		},
882		{
883			name: "is unit test",
884			testOptions: CommonTestOptions{
885				Unit_test: boolPtr(true),
886			},
887			expected: map[string][]string{
888				"LOCAL_IS_UNIT_TEST": []string{"true"},
889			},
890		},
891		{
892			name: "is not unit test",
893			testOptions: CommonTestOptions{
894				Unit_test: boolPtr(false),
895			},
896			expected: map[string][]string{},
897		},
898		{
899			name: "empty tag",
900			testOptions: CommonTestOptions{
901				Tags: []string{},
902			},
903			expected: map[string][]string{},
904		},
905		{
906			name: "single tag",
907			testOptions: CommonTestOptions{
908				Tags: []string{"tag1"},
909			},
910			expected: map[string][]string{
911				"LOCAL_TEST_OPTIONS_TAGS": []string{"tag1"},
912			},
913		},
914		{
915			name: "multiple tag",
916			testOptions: CommonTestOptions{
917				Tags: []string{"tag1", "tag2", "tag3"},
918			},
919			expected: map[string][]string{
920				"LOCAL_TEST_OPTIONS_TAGS": []string{"tag1", "tag2", "tag3"},
921			},
922		},
923	}
924	for _, tt := range tests {
925		t.Run(tt.name, func(t *testing.T) {
926			actualEntries := AndroidMkEntries{
927				EntryMap: map[string][]string{},
928			}
929			tt.testOptions.SetAndroidMkEntries(&actualEntries)
930			actual := actualEntries.EntryMap
931			t.Logf("actual: %v", actual)
932			t.Logf("expected: %v", tt.expected)
933			AssertDeepEquals(t, "TestProcessCommonTestOptions ", tt.expected, actual)
934		})
935	}
936}
937
938type sourceProducerTestModule struct {
939	ModuleBase
940	props struct {
941		// A represents the source file
942		A string
943	}
944}
945
946func sourceProducerTestModuleFactory() Module {
947	module := &sourceProducerTestModule{}
948	module.AddProperties(&module.props)
949	InitAndroidModule(module)
950	return module
951}
952
953func (s sourceProducerTestModule) GenerateAndroidBuildActions(ModuleContext) {}
954
955func (s sourceProducerTestModule) Srcs() Paths { return PathsForTesting(s.props.A) }
956
957type outputFilesTestModule struct {
958	ModuleBase
959	props struct {
960		// A represents the tag
961		A string
962		// B represents the output file for tag A
963		B string
964	}
965}
966
967func outputFilesTestModuleFactory() Module {
968	module := &outputFilesTestModule{}
969	module.AddProperties(&module.props)
970	InitAndroidModule(module)
971	return module
972}
973
974func (o outputFilesTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
975	if o.props.A != "" || o.props.B != "" {
976		ctx.SetOutputFiles(PathsForTesting(o.props.B), o.props.A)
977	}
978	// This is to simulate the case that some module uses an object to set its
979	// OutputFilesProvider, but the object itself is empty.
980	ctx.SetOutputFiles(Paths{}, "missing")
981}
982
983type pathContextAddMissingDependenciesWrapper struct {
984	PathContext
985	OtherModuleProviderContext
986	missingDeps []string
987}
988
989func (p *pathContextAddMissingDependenciesWrapper) AddMissingDependencies(deps []string) {
990	p.missingDeps = append(p.missingDeps, deps...)
991}
992func (p *pathContextAddMissingDependenciesWrapper) OtherModuleName(module blueprint.Module) string {
993	return module.Name()
994}
995
996func (p *pathContextAddMissingDependenciesWrapper) Module() Module { return nil }
997
998func (p *pathContextAddMissingDependenciesWrapper) GetOutputFiles() OutputFilesInfo {
999	return OutputFilesInfo{}
1000}
1001
1002func (p *pathContextAddMissingDependenciesWrapper) EqualModules(m1, m2 Module) bool {
1003	return m1 == m2
1004}
1005
1006func TestOutputFileForModule(t *testing.T) {
1007	testcases := []struct {
1008		name        string
1009		bp          string
1010		tag         string
1011		expected    string
1012		missingDeps []string
1013		env         map[string]string
1014		config      func(*config)
1015	}{
1016		{
1017			name: "SourceFileProducer",
1018			bp: `spt_module {
1019					name: "test_module",
1020					a: "spt.txt",
1021				}
1022			`,
1023			tag:      "",
1024			expected: "spt.txt",
1025		},
1026		{
1027			name: "OutputFileProviderEmptyStringTag",
1028			bp: `oft_module {
1029					name: "test_module",
1030					a: "",
1031					b: "empty.txt",
1032				}
1033		`,
1034			tag:      "",
1035			expected: "empty.txt",
1036		},
1037		{
1038			name: "OutputFileProviderTag",
1039			bp: `oft_module {
1040					name: "test_module",
1041					a: "foo",
1042					b: "foo.txt",
1043				}
1044			`,
1045			tag:      "foo",
1046			expected: "foo.txt",
1047		},
1048		{
1049			name: "OutputFileAllowMissingDependencies",
1050			bp: `oft_module {
1051				name: "test_module",
1052			}
1053		`,
1054			tag:         "missing",
1055			expected:    "missing_output_file/test_module",
1056			missingDeps: []string{"test_module"},
1057			config: func(config *config) {
1058				config.TestProductVariables.Allow_missing_dependencies = boolPtr(true)
1059			},
1060		},
1061	}
1062
1063	for _, tt := range testcases {
1064		t.Run(tt.name, func(t *testing.T) {
1065			result := GroupFixturePreparers(
1066				PrepareForTestWithDefaults,
1067				FixtureRegisterWithContext(func(ctx RegistrationContext) {
1068					ctx.RegisterModuleType("spt_module", sourceProducerTestModuleFactory)
1069					ctx.RegisterModuleType("oft_module", outputFilesTestModuleFactory)
1070				}),
1071				FixtureWithRootAndroidBp(tt.bp),
1072			).RunTest(t)
1073
1074			config := TestConfig(buildDir, tt.env, tt.bp, nil)
1075			if tt.config != nil {
1076				tt.config(config.config)
1077			}
1078			ctx := &pathContextAddMissingDependenciesWrapper{
1079				PathContext:                PathContextForTesting(config),
1080				OtherModuleProviderContext: result.TestContext.OtherModuleProviderAdaptor(),
1081			}
1082			got := OutputFileForModule(ctx, result.ModuleForTests(t, "test_module", "").Module(), tt.tag)
1083			AssertPathRelativeToTopEquals(t, "expected output path", tt.expected, got)
1084			AssertArrayString(t, "expected missing deps", tt.missingDeps, ctx.missingDeps)
1085		})
1086	}
1087}
1088
1089func TestVintfFragmentModulesChecksPartition(t *testing.T) {
1090	bp := `
1091	vintf_fragment {
1092		name: "vintfModA",
1093		src: "test_vintf_file",
1094		vendor: true,
1095	}
1096	deps {
1097		name: "modA",
1098		vintf_fragment_modules: [
1099			"vintfModA",
1100		]
1101	}
1102	`
1103
1104	testPreparer := GroupFixturePreparers(
1105		PrepareForTestWithAndroidBuildComponents,
1106		prepareForModuleTests,
1107	)
1108
1109	testPreparer.
1110		ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(
1111			"Module .+ and Vintf_fragment .+ are installed to different partitions.")).
1112		RunTestWithBp(t, bp)
1113}
1114
1115func TestInvalidModuleName(t *testing.T) {
1116	bp := `
1117		deps {
1118			name: "fo o",
1119		}
1120	`
1121	prepareForModuleTests.
1122		ExtendWithErrorHandler(FixtureExpectsOneErrorPattern(`should use a valid name`)).
1123		RunTestWithBp(t, bp)
1124}
1125