• 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(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}
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 fakeBlueprintModule struct{}
939
940func (fakeBlueprintModule) Name() string { return "foo" }
941
942func (fakeBlueprintModule) GenerateBuildActions(blueprint.ModuleContext) {}
943
944type sourceProducerTestModule struct {
945	fakeBlueprintModule
946	source Path
947}
948
949func (s sourceProducerTestModule) Srcs() Paths { return Paths{s.source} }
950
951type outputFileProducerTestModule struct {
952	fakeBlueprintModule
953	output map[string]Path
954	error  map[string]error
955}
956
957func (o outputFileProducerTestModule) OutputFiles(tag string) (Paths, error) {
958	return PathsIfNonNil(o.output[tag]), o.error[tag]
959}
960
961type pathContextAddMissingDependenciesWrapper struct {
962	PathContext
963	missingDeps []string
964}
965
966func (p *pathContextAddMissingDependenciesWrapper) AddMissingDependencies(deps []string) {
967	p.missingDeps = append(p.missingDeps, deps...)
968}
969func (p *pathContextAddMissingDependenciesWrapper) OtherModuleName(module blueprint.Module) string {
970	return module.Name()
971}
972
973func TestOutputFileForModule(t *testing.T) {
974	testcases := []struct {
975		name        string
976		module      blueprint.Module
977		tag         string
978		env         map[string]string
979		config      func(*config)
980		expected    string
981		missingDeps []string
982	}{
983		{
984			name:     "SourceFileProducer",
985			module:   &sourceProducerTestModule{source: PathForTesting("foo.txt")},
986			expected: "foo.txt",
987		},
988		{
989			name:     "OutputFileProducer",
990			module:   &outputFileProducerTestModule{output: map[string]Path{"": PathForTesting("foo.txt")}},
991			expected: "foo.txt",
992		},
993		{
994			name:     "OutputFileProducer_tag",
995			module:   &outputFileProducerTestModule{output: map[string]Path{"foo": PathForTesting("foo.txt")}},
996			tag:      "foo",
997			expected: "foo.txt",
998		},
999		{
1000			name: "OutputFileProducer_AllowMissingDependencies",
1001			config: func(config *config) {
1002				config.TestProductVariables.Allow_missing_dependencies = boolPtr(true)
1003			},
1004			module:      &outputFileProducerTestModule{},
1005			missingDeps: []string{"foo"},
1006			expected:    "missing_output_file/foo",
1007		},
1008	}
1009	for _, tt := range testcases {
1010		config := TestConfig(buildDir, tt.env, "", nil)
1011		if tt.config != nil {
1012			tt.config(config.config)
1013		}
1014		ctx := &pathContextAddMissingDependenciesWrapper{
1015			PathContext: PathContextForTesting(config),
1016		}
1017		got := OutputFileForModule(ctx, tt.module, tt.tag)
1018		AssertPathRelativeToTopEquals(t, "expected source path", tt.expected, got)
1019		AssertArrayString(t, "expected missing deps", tt.missingDeps, ctx.missingDeps)
1020	}
1021}
1022