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