• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2014 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 blueprint
16
17import (
18	"bytes"
19	"errors"
20	"fmt"
21	"path/filepath"
22	"reflect"
23	"strings"
24	"sync"
25	"testing"
26	"time"
27
28	"github.com/google/blueprint/parser"
29)
30
31type Walker interface {
32	Walk() bool
33}
34
35func walkDependencyGraph(ctx *Context, topModule *moduleInfo, allowDuplicates bool) (string, string) {
36	var outputDown string
37	var outputUp string
38	ctx.walkDeps(topModule, allowDuplicates,
39		func(dep depInfo, parent *moduleInfo) bool {
40			outputDown += ctx.ModuleName(dep.module.logicModule)
41			if tag, ok := dep.tag.(walkerDepsTag); ok {
42				if !tag.follow {
43					return false
44				}
45			}
46			if dep.module.logicModule.(Walker).Walk() {
47				return true
48			}
49
50			return false
51		},
52		func(dep depInfo, parent *moduleInfo) {
53			outputUp += ctx.ModuleName(dep.module.logicModule)
54		})
55	return outputDown, outputUp
56}
57
58type depsProvider interface {
59	Deps() []string
60	IgnoreDeps() []string
61}
62
63type fooModule struct {
64	SimpleName
65	properties struct {
66		Deps         []string
67		Ignored_deps []string
68		Foo          string
69	}
70}
71
72func newFooModule() (Module, []interface{}) {
73	m := &fooModule{}
74	return m, []interface{}{&m.properties, &m.SimpleName.Properties}
75}
76
77func (f *fooModule) GenerateBuildActions(ModuleContext) {
78}
79
80func (f *fooModule) Deps() []string {
81	return f.properties.Deps
82}
83
84func (f *fooModule) IgnoreDeps() []string {
85	return f.properties.Ignored_deps
86}
87
88func (f *fooModule) Foo() string {
89	return f.properties.Foo
90}
91
92func (f *fooModule) Walk() bool {
93	return true
94}
95
96type barModule struct {
97	SimpleName
98	properties struct {
99		Deps         []string
100		Ignored_deps []string
101		Bar          bool
102	}
103}
104
105func newBarModule() (Module, []interface{}) {
106	m := &barModule{}
107	return m, []interface{}{&m.properties, &m.SimpleName.Properties}
108}
109
110func (b *barModule) Deps() []string {
111	return b.properties.Deps
112}
113
114func (b *barModule) IgnoreDeps() []string {
115	return b.properties.Ignored_deps
116}
117
118func (b *barModule) GenerateBuildActions(ModuleContext) {
119}
120
121func (b *barModule) Bar() bool {
122	return b.properties.Bar
123}
124
125func (b *barModule) Walk() bool {
126	return false
127}
128
129type walkerDepsTag struct {
130	BaseDependencyTag
131	// True if the dependency should be followed, false otherwise.
132	follow bool
133}
134
135func depsMutator(mctx BottomUpMutatorContext) {
136	if m, ok := mctx.Module().(depsProvider); ok {
137		mctx.AddDependency(mctx.Module(), walkerDepsTag{follow: false}, m.IgnoreDeps()...)
138		mctx.AddDependency(mctx.Module(), walkerDepsTag{follow: true}, m.Deps()...)
139	}
140}
141
142func TestContextParse(t *testing.T) {
143	ctx := NewContext()
144	ctx.RegisterModuleType("foo_module", newFooModule)
145	ctx.RegisterModuleType("bar_module", newBarModule)
146
147	r := bytes.NewBufferString(`
148		foo_module {
149	        name: "MyFooModule",
150			deps: ["MyBarModule"],
151		}
152
153		bar_module {
154	        name: "MyBarModule",
155		}
156	`)
157
158	_, _, errs := ctx.parseOne(".", "Blueprint", r, parser.NewScope(nil), nil)
159	if len(errs) > 0 {
160		t.Errorf("unexpected parse errors:")
161		for _, err := range errs {
162			t.Errorf("  %s", err)
163		}
164		t.FailNow()
165	}
166
167	_, errs = ctx.ResolveDependencies(nil)
168	if len(errs) > 0 {
169		t.Errorf("unexpected dep errors:")
170		for _, err := range errs {
171			t.Errorf("  %s", err)
172		}
173		t.FailNow()
174	}
175}
176
177// > |===B---D       - represents a non-walkable edge
178// > A               = represents a walkable edge
179// > |===C===E---G
180// >     |       |   A should not be visited because it's the root node.
181// >     |===F===|   B, D and E should not be walked.
182func TestWalkDeps(t *testing.T) {
183	ctx := NewContext()
184	ctx.MockFileSystem(map[string][]byte{
185		"Android.bp": []byte(`
186			foo_module {
187			    name: "A",
188			    deps: ["B", "C"],
189			}
190
191			bar_module {
192			    name: "B",
193			    deps: ["D"],
194			}
195
196			foo_module {
197			    name: "C",
198			    deps: ["E", "F"],
199			}
200
201			foo_module {
202			    name: "D",
203			}
204
205			bar_module {
206			    name: "E",
207			    deps: ["G"],
208			}
209
210			foo_module {
211			    name: "F",
212			    deps: ["G"],
213			}
214
215			foo_module {
216			    name: "G",
217			}
218		`),
219	})
220
221	ctx.RegisterModuleType("foo_module", newFooModule)
222	ctx.RegisterModuleType("bar_module", newBarModule)
223	ctx.RegisterBottomUpMutator("deps", depsMutator)
224	_, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
225	if len(errs) > 0 {
226		t.Errorf("unexpected parse errors:")
227		for _, err := range errs {
228			t.Errorf("  %s", err)
229		}
230		t.FailNow()
231	}
232
233	_, errs = ctx.ResolveDependencies(nil)
234	if len(errs) > 0 {
235		t.Errorf("unexpected dep errors:")
236		for _, err := range errs {
237			t.Errorf("  %s", err)
238		}
239		t.FailNow()
240	}
241
242	topModule := ctx.moduleGroupFromName("A", nil).modules.firstModule()
243	outputDown, outputUp := walkDependencyGraph(ctx, topModule, false)
244	if outputDown != "BCEFG" {
245		t.Errorf("unexpected walkDeps behaviour: %s\ndown should be: BCEFG", outputDown)
246	}
247	if outputUp != "BEGFC" {
248		t.Errorf("unexpected walkDeps behaviour: %s\nup should be: BEGFC", outputUp)
249	}
250}
251
252// > |===B---D           - represents a non-walkable edge
253// > A                   = represents a walkable edge
254// > |===C===E===\       A should not be visited because it's the root node.
255// >     |       |       B, D should not be walked.
256// >     |===F===G===H   G should be visited multiple times
257// >         \===/       H should only be visited once
258func TestWalkDepsDuplicates(t *testing.T) {
259	ctx := NewContext()
260	ctx.MockFileSystem(map[string][]byte{
261		"Android.bp": []byte(`
262			foo_module {
263			    name: "A",
264			    deps: ["B", "C"],
265			}
266
267			bar_module {
268			    name: "B",
269			    deps: ["D"],
270			}
271
272			foo_module {
273			    name: "C",
274			    deps: ["E", "F"],
275			}
276
277			foo_module {
278			    name: "D",
279			}
280
281			foo_module {
282			    name: "E",
283			    deps: ["G"],
284			}
285
286			foo_module {
287			    name: "F",
288			    deps: ["G", "G"],
289			}
290
291			foo_module {
292			    name: "G",
293				deps: ["H"],
294			}
295
296			foo_module {
297			    name: "H",
298			}
299		`),
300	})
301
302	ctx.RegisterModuleType("foo_module", newFooModule)
303	ctx.RegisterModuleType("bar_module", newBarModule)
304	ctx.RegisterBottomUpMutator("deps", depsMutator)
305	_, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
306	if len(errs) > 0 {
307		t.Errorf("unexpected parse errors:")
308		for _, err := range errs {
309			t.Errorf("  %s", err)
310		}
311		t.FailNow()
312	}
313
314	_, errs = ctx.ResolveDependencies(nil)
315	if len(errs) > 0 {
316		t.Errorf("unexpected dep errors:")
317		for _, err := range errs {
318			t.Errorf("  %s", err)
319		}
320		t.FailNow()
321	}
322
323	topModule := ctx.moduleGroupFromName("A", nil).modules.firstModule()
324	outputDown, outputUp := walkDependencyGraph(ctx, topModule, true)
325	if outputDown != "BCEGHFGG" {
326		t.Errorf("unexpected walkDeps behaviour: %s\ndown should be: BCEGHFGG", outputDown)
327	}
328	if outputUp != "BHGEGGFC" {
329		t.Errorf("unexpected walkDeps behaviour: %s\nup should be: BHGEGGFC", outputUp)
330	}
331}
332
333// >                     - represents a non-walkable edge
334// > A                   = represents a walkable edge
335// > |===B-------\       A should not be visited because it's the root node.
336// >     |       |       B -> D should not be walked.
337// >     |===C===D===E   B -> C -> D -> E should be walked
338func TestWalkDepsDuplicates_IgnoreFirstPath(t *testing.T) {
339	ctx := NewContext()
340	ctx.MockFileSystem(map[string][]byte{
341		"Android.bp": []byte(`
342			foo_module {
343			    name: "A",
344			    deps: ["B"],
345			}
346
347			foo_module {
348			    name: "B",
349			    deps: ["C"],
350			    ignored_deps: ["D"],
351			}
352
353			foo_module {
354			    name: "C",
355			    deps: ["D"],
356			}
357
358			foo_module {
359			    name: "D",
360			    deps: ["E"],
361			}
362
363			foo_module {
364			    name: "E",
365			}
366		`),
367	})
368
369	ctx.RegisterModuleType("foo_module", newFooModule)
370	ctx.RegisterModuleType("bar_module", newBarModule)
371	ctx.RegisterBottomUpMutator("deps", depsMutator)
372	_, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
373	if len(errs) > 0 {
374		t.Errorf("unexpected parse errors:")
375		for _, err := range errs {
376			t.Errorf("  %s", err)
377		}
378		t.FailNow()
379	}
380
381	_, errs = ctx.ResolveDependencies(nil)
382	if len(errs) > 0 {
383		t.Errorf("unexpected dep errors:")
384		for _, err := range errs {
385			t.Errorf("  %s", err)
386		}
387		t.FailNow()
388	}
389
390	topModule := ctx.moduleGroupFromName("A", nil).modules.firstModule()
391	outputDown, outputUp := walkDependencyGraph(ctx, topModule, true)
392	expectedDown := "BDCDE"
393	if outputDown != expectedDown {
394		t.Errorf("unexpected walkDeps behaviour: %s\ndown should be: %s", outputDown, expectedDown)
395	}
396	expectedUp := "DEDCB"
397	if outputUp != expectedUp {
398		t.Errorf("unexpected walkDeps behaviour: %s\nup should be: %s", outputUp, expectedUp)
399	}
400}
401
402func TestCreateModule(t *testing.T) {
403	ctx := newContext()
404	ctx.MockFileSystem(map[string][]byte{
405		"Android.bp": []byte(`
406			foo_module {
407			    name: "A",
408			    deps: ["B", "C"],
409			}
410		`),
411	})
412
413	ctx.RegisterTopDownMutator("create", createTestMutator)
414	ctx.RegisterBottomUpMutator("deps", depsMutator)
415
416	ctx.RegisterModuleType("foo_module", newFooModule)
417	ctx.RegisterModuleType("bar_module", newBarModule)
418	_, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
419	if len(errs) > 0 {
420		t.Errorf("unexpected parse errors:")
421		for _, err := range errs {
422			t.Errorf("  %s", err)
423		}
424		t.FailNow()
425	}
426
427	_, errs = ctx.ResolveDependencies(nil)
428	if len(errs) > 0 {
429		t.Errorf("unexpected dep errors:")
430		for _, err := range errs {
431			t.Errorf("  %s", err)
432		}
433		t.FailNow()
434	}
435
436	a := ctx.moduleGroupFromName("A", nil).modules.firstModule().logicModule.(*fooModule)
437	b := ctx.moduleGroupFromName("B", nil).modules.firstModule().logicModule.(*barModule)
438	c := ctx.moduleGroupFromName("C", nil).modules.firstModule().logicModule.(*barModule)
439	d := ctx.moduleGroupFromName("D", nil).modules.firstModule().logicModule.(*fooModule)
440
441	checkDeps := func(m Module, expected string) {
442		var deps []string
443		ctx.VisitDirectDeps(m, func(m Module) {
444			deps = append(deps, ctx.ModuleName(m))
445		})
446		got := strings.Join(deps, ",")
447		if got != expected {
448			t.Errorf("unexpected %q dependencies, got %q expected %q",
449				ctx.ModuleName(m), got, expected)
450		}
451	}
452
453	checkDeps(a, "B,C")
454	checkDeps(b, "D")
455	checkDeps(c, "D")
456	checkDeps(d, "")
457}
458
459func createTestMutator(ctx TopDownMutatorContext) {
460	type props struct {
461		Name string
462		Deps []string
463	}
464
465	ctx.CreateModule(newBarModule, "new_bar", &props{
466		Name: "B",
467		Deps: []string{"D"},
468	})
469
470	ctx.CreateModule(newBarModule, "new_bar", &props{
471		Name: "C",
472		Deps: []string{"D"},
473	})
474
475	ctx.CreateModule(newFooModule, "new_foo", &props{
476		Name: "D",
477	})
478}
479
480func TestWalkFileOrder(t *testing.T) {
481	// Run the test once to see how long it normally takes
482	start := time.Now()
483	doTestWalkFileOrder(t, time.Duration(0))
484	duration := time.Since(start)
485
486	// Run the test again, but put enough of a sleep into each visitor to detect ordering
487	// problems if they exist
488	doTestWalkFileOrder(t, duration)
489}
490
491// test that WalkBlueprintsFiles calls asyncVisitor in the right order
492func doTestWalkFileOrder(t *testing.T, sleepDuration time.Duration) {
493	// setup mock context
494	ctx := newContext()
495	mockFiles := map[string][]byte{
496		"Android.bp": []byte(`
497			sample_module {
498			    name: "a",
499			}
500		`),
501		"dir1/Android.bp": []byte(`
502			sample_module {
503			    name: "b",
504			}
505		`),
506		"dir1/dir2/Android.bp": []byte(`
507			sample_module {
508			    name: "c",
509			}
510		`),
511	}
512	ctx.MockFileSystem(mockFiles)
513
514	// prepare to monitor the visit order
515	visitOrder := []string{}
516	visitLock := sync.Mutex{}
517	correctVisitOrder := []string{"Android.bp", "dir1/Android.bp", "dir1/dir2/Android.bp"}
518
519	// sleep longer when processing the earlier files
520	chooseSleepDuration := func(fileName string) (duration time.Duration) {
521		duration = time.Duration(0)
522		for i := len(correctVisitOrder) - 1; i >= 0; i-- {
523			if fileName == correctVisitOrder[i] {
524				return duration
525			}
526			duration = duration + sleepDuration
527		}
528		panic("unrecognized file name " + fileName)
529	}
530
531	visitor := func(file *parser.File) {
532		time.Sleep(chooseSleepDuration(file.Name))
533		visitLock.Lock()
534		defer visitLock.Unlock()
535		visitOrder = append(visitOrder, file.Name)
536	}
537	keys := []string{"Android.bp", "dir1/Android.bp", "dir1/dir2/Android.bp"}
538
539	// visit the blueprints files
540	ctx.WalkBlueprintsFiles(".", keys, visitor)
541
542	// check the order
543	if !reflect.DeepEqual(visitOrder, correctVisitOrder) {
544		t.Errorf("Incorrect visit order; expected %v, got %v", correctVisitOrder, visitOrder)
545	}
546}
547
548// test that WalkBlueprintsFiles reports syntax errors
549func TestWalkingWithSyntaxError(t *testing.T) {
550	// setup mock context
551	ctx := newContext()
552	mockFiles := map[string][]byte{
553		"Android.bp": []byte(`
554			sample_module {
555			    name: "a" "b",
556			}
557		`),
558		"dir1/Android.bp": []byte(`
559			sample_module {
560			    name: "b",
561		`),
562		"dir1/dir2/Android.bp": []byte(`
563			sample_module {
564			    name: "c",
565			}
566		`),
567	}
568	ctx.MockFileSystem(mockFiles)
569
570	keys := []string{"Android.bp", "dir1/Android.bp", "dir1/dir2/Android.bp"}
571
572	// visit the blueprints files
573	_, errs := ctx.WalkBlueprintsFiles(".", keys, func(file *parser.File) {})
574
575	expectedErrs := []error{
576		errors.New(`Android.bp:3:18: expected "}", found String`),
577		errors.New(`dir1/Android.bp:4:3: expected "}", found EOF`),
578	}
579	if fmt.Sprintf("%s", expectedErrs) != fmt.Sprintf("%s", errs) {
580		t.Errorf("Incorrect errors; expected:\n%s\ngot:\n%s", expectedErrs, errs)
581	}
582
583}
584
585func TestParseFailsForModuleWithoutName(t *testing.T) {
586	ctx := NewContext()
587	ctx.MockFileSystem(map[string][]byte{
588		"Android.bp": []byte(`
589			foo_module {
590			    name: "A",
591			}
592
593			bar_module {
594			    deps: ["A"],
595			}
596		`),
597	})
598	ctx.RegisterModuleType("foo_module", newFooModule)
599	ctx.RegisterModuleType("bar_module", newBarModule)
600
601	_, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
602
603	expectedErrs := []error{
604		errors.New(`Android.bp:6:4: property 'name' is missing from a module`),
605	}
606	if fmt.Sprintf("%s", expectedErrs) != fmt.Sprintf("%s", errs) {
607		t.Errorf("Incorrect errors; expected:\n%s\ngot:\n%s", expectedErrs, errs)
608	}
609}
610
611func Test_findVariant(t *testing.T) {
612	module := &moduleInfo{
613		variant: variant{
614			name: "normal_local",
615			variations: variationMap{
616				"normal": "normal",
617				"local":  "local",
618			},
619			dependencyVariations: variationMap{
620				"normal": "normal",
621			},
622		},
623	}
624
625	type alias struct {
626		variant variant
627		target  int
628	}
629
630	makeDependencyGroup := func(in ...interface{}) *moduleGroup {
631		group := &moduleGroup{
632			name: "dep",
633		}
634		for _, x := range in {
635			switch m := x.(type) {
636			case *moduleInfo:
637				m.group = group
638				group.modules = append(group.modules, m)
639			case alias:
640				// aliases may need to target modules that haven't been processed
641				// yet, put an empty alias in for now.
642				group.modules = append(group.modules, nil)
643			default:
644				t.Fatalf("unexpected type %T", x)
645			}
646		}
647
648		for i, x := range in {
649			switch m := x.(type) {
650			case *moduleInfo:
651				// already added in the first pass
652			case alias:
653				group.modules[i] = &moduleAlias{
654					variant: m.variant,
655					target:  group.modules[m.target].moduleOrAliasTarget(),
656				}
657			default:
658				t.Fatalf("unexpected type %T", x)
659			}
660		}
661
662		return group
663	}
664
665	tests := []struct {
666		name         string
667		possibleDeps *moduleGroup
668		variations   []Variation
669		far          bool
670		reverse      bool
671		want         string
672	}{
673		{
674			name: "AddVariationDependencies(nil)",
675			// A dependency that matches the non-local variations of the module
676			possibleDeps: makeDependencyGroup(
677				&moduleInfo{
678					variant: variant{
679						name: "normal",
680						variations: variationMap{
681							"normal": "normal",
682						},
683					},
684				},
685			),
686			variations: nil,
687			far:        false,
688			reverse:    false,
689			want:       "normal",
690		},
691		{
692			name: "AddVariationDependencies(nil) to alias",
693			// A dependency with an alias that matches the non-local variations of the module
694			possibleDeps: makeDependencyGroup(
695				alias{
696					variant: variant{
697						name: "normal",
698						variations: variationMap{
699							"normal": "normal",
700						},
701					},
702					target: 1,
703				},
704				&moduleInfo{
705					variant: variant{
706						name: "normal_a",
707						variations: variationMap{
708							"normal": "normal",
709							"a":      "a",
710						},
711					},
712				},
713			),
714			variations: nil,
715			far:        false,
716			reverse:    false,
717			want:       "normal_a",
718		},
719		{
720			name: "AddVariationDependencies(a)",
721			// A dependency with local variations
722			possibleDeps: makeDependencyGroup(
723				&moduleInfo{
724					variant: variant{
725						name: "normal_a",
726						variations: variationMap{
727							"normal": "normal",
728							"a":      "a",
729						},
730					},
731				},
732			),
733			variations: []Variation{{"a", "a"}},
734			far:        false,
735			reverse:    false,
736			want:       "normal_a",
737		},
738		{
739			name: "AddFarVariationDependencies(far)",
740			// A dependency with far variations
741			possibleDeps: makeDependencyGroup(
742				&moduleInfo{
743					variant: variant{
744						name:       "",
745						variations: nil,
746					},
747				},
748				&moduleInfo{
749					variant: variant{
750						name: "far",
751						variations: variationMap{
752							"far": "far",
753						},
754					},
755				},
756			),
757			variations: []Variation{{"far", "far"}},
758			far:        true,
759			reverse:    false,
760			want:       "far",
761		},
762		{
763			name: "AddFarVariationDependencies(far) to alias",
764			// A dependency with far variations and aliases
765			possibleDeps: makeDependencyGroup(
766				alias{
767					variant: variant{
768						name: "far",
769						variations: variationMap{
770							"far": "far",
771						},
772					},
773					target: 2,
774				},
775				&moduleInfo{
776					variant: variant{
777						name: "far_a",
778						variations: variationMap{
779							"far": "far",
780							"a":   "a",
781						},
782					},
783				},
784				&moduleInfo{
785					variant: variant{
786						name: "far_b",
787						variations: variationMap{
788							"far": "far",
789							"b":   "b",
790						},
791					},
792				},
793			),
794			variations: []Variation{{"far", "far"}},
795			far:        true,
796			reverse:    false,
797			want:       "far_b",
798		},
799		{
800			name: "AddFarVariationDependencies(far, b) to missing",
801			// A dependency with far variations and aliases
802			possibleDeps: makeDependencyGroup(
803				alias{
804					variant: variant{
805						name: "far",
806						variations: variationMap{
807							"far": "far",
808						},
809					},
810					target: 1,
811				},
812				&moduleInfo{
813					variant: variant{
814						name: "far_a",
815						variations: variationMap{
816							"far": "far",
817							"a":   "a",
818						},
819					},
820				},
821			),
822			variations: []Variation{{"far", "far"}, {"a", "b"}},
823			far:        true,
824			reverse:    false,
825			want:       "nil",
826		},
827	}
828	for _, tt := range tests {
829		t.Run(tt.name, func(t *testing.T) {
830			got, _ := findVariant(module, tt.possibleDeps, tt.variations, tt.far, tt.reverse)
831			if g, w := got == nil, tt.want == "nil"; g != w {
832				t.Fatalf("findVariant() got = %v, want %v", got, tt.want)
833			}
834			if got != nil {
835				if g, w := got.String(), fmt.Sprintf("module %q variant %q", "dep", tt.want); g != w {
836					t.Errorf("findVariant() got = %v, want %v", g, w)
837				}
838			}
839		})
840	}
841}
842
843func Test_parallelVisit(t *testing.T) {
844	addDep := func(from, to *moduleInfo) {
845		from.directDeps = append(from.directDeps, depInfo{to, nil})
846		from.forwardDeps = append(from.forwardDeps, to)
847		to.reverseDeps = append(to.reverseDeps, from)
848	}
849
850	create := func(name string) *moduleInfo {
851		m := &moduleInfo{
852			group: &moduleGroup{
853				name: name,
854			},
855		}
856		m.group.modules = modulesOrAliases{m}
857		return m
858	}
859	moduleA := create("A")
860	moduleB := create("B")
861	moduleC := create("C")
862	moduleD := create("D")
863	moduleE := create("E")
864	moduleF := create("F")
865	moduleG := create("G")
866
867	// A depends on B, B depends on C.  Nothing depends on D through G, and they don't depend on
868	// anything.
869	addDep(moduleA, moduleB)
870	addDep(moduleB, moduleC)
871
872	t.Run("no modules", func(t *testing.T) {
873		errs := parallelVisit(nil, bottomUpVisitorImpl{}, 1,
874			func(module *moduleInfo, pause chan<- pauseSpec) bool {
875				panic("unexpected call to visitor")
876			})
877		if errs != nil {
878			t.Errorf("expected no errors, got %q", errs)
879		}
880	})
881	t.Run("bottom up", func(t *testing.T) {
882		order := ""
883		errs := parallelVisit([]*moduleInfo{moduleA, moduleB, moduleC}, bottomUpVisitorImpl{}, 1,
884			func(module *moduleInfo, pause chan<- pauseSpec) bool {
885				order += module.group.name
886				return false
887			})
888		if errs != nil {
889			t.Errorf("expected no errors, got %q", errs)
890		}
891		if g, w := order, "CBA"; g != w {
892			t.Errorf("expected order %q, got %q", w, g)
893		}
894	})
895	t.Run("pause", func(t *testing.T) {
896		order := ""
897		errs := parallelVisit([]*moduleInfo{moduleA, moduleB, moduleC, moduleD}, bottomUpVisitorImpl{}, 1,
898			func(module *moduleInfo, pause chan<- pauseSpec) bool {
899				if module == moduleC {
900					// Pause module C on module D
901					unpause := make(chan struct{})
902					pause <- pauseSpec{moduleC, moduleD, unpause}
903					<-unpause
904				}
905				order += module.group.name
906				return false
907			})
908		if errs != nil {
909			t.Errorf("expected no errors, got %q", errs)
910		}
911		if g, w := order, "DCBA"; g != w {
912			t.Errorf("expected order %q, got %q", w, g)
913		}
914	})
915	t.Run("cancel", func(t *testing.T) {
916		order := ""
917		errs := parallelVisit([]*moduleInfo{moduleA, moduleB, moduleC}, bottomUpVisitorImpl{}, 1,
918			func(module *moduleInfo, pause chan<- pauseSpec) bool {
919				order += module.group.name
920				// Cancel in module B
921				return module == moduleB
922			})
923		if errs != nil {
924			t.Errorf("expected no errors, got %q", errs)
925		}
926		if g, w := order, "CB"; g != w {
927			t.Errorf("expected order %q, got %q", w, g)
928		}
929	})
930	t.Run("pause and cancel", func(t *testing.T) {
931		order := ""
932		errs := parallelVisit([]*moduleInfo{moduleA, moduleB, moduleC, moduleD}, bottomUpVisitorImpl{}, 1,
933			func(module *moduleInfo, pause chan<- pauseSpec) bool {
934				if module == moduleC {
935					// Pause module C on module D
936					unpause := make(chan struct{})
937					pause <- pauseSpec{moduleC, moduleD, unpause}
938					<-unpause
939				}
940				order += module.group.name
941				// Cancel in module D
942				return module == moduleD
943			})
944		if errs != nil {
945			t.Errorf("expected no errors, got %q", errs)
946		}
947		if g, w := order, "D"; g != w {
948			t.Errorf("expected order %q, got %q", w, g)
949		}
950	})
951	t.Run("parallel", func(t *testing.T) {
952		order := ""
953		errs := parallelVisit([]*moduleInfo{moduleA, moduleB, moduleC}, bottomUpVisitorImpl{}, 3,
954			func(module *moduleInfo, pause chan<- pauseSpec) bool {
955				order += module.group.name
956				return false
957			})
958		if errs != nil {
959			t.Errorf("expected no errors, got %q", errs)
960		}
961		if g, w := order, "CBA"; g != w {
962			t.Errorf("expected order %q, got %q", w, g)
963		}
964	})
965	t.Run("pause existing", func(t *testing.T) {
966		order := ""
967		errs := parallelVisit([]*moduleInfo{moduleA, moduleB, moduleC}, bottomUpVisitorImpl{}, 3,
968			func(module *moduleInfo, pause chan<- pauseSpec) bool {
969				if module == moduleA {
970					// Pause module A on module B (an existing dependency)
971					unpause := make(chan struct{})
972					pause <- pauseSpec{moduleA, moduleB, unpause}
973					<-unpause
974				}
975				order += module.group.name
976				return false
977			})
978		if errs != nil {
979			t.Errorf("expected no errors, got %q", errs)
980		}
981		if g, w := order, "CBA"; g != w {
982			t.Errorf("expected order %q, got %q", w, g)
983		}
984	})
985	t.Run("cycle", func(t *testing.T) {
986		errs := parallelVisit([]*moduleInfo{moduleA, moduleB, moduleC}, bottomUpVisitorImpl{}, 3,
987			func(module *moduleInfo, pause chan<- pauseSpec) bool {
988				if module == moduleC {
989					// Pause module C on module A (a dependency cycle)
990					unpause := make(chan struct{})
991					pause <- pauseSpec{moduleC, moduleA, unpause}
992					<-unpause
993				}
994				return false
995			})
996		want := []string{
997			`encountered dependency cycle`,
998			`module "C" depends on module "A"`,
999			`module "A" depends on module "B"`,
1000			`module "B" depends on module "C"`,
1001		}
1002		for i := range want {
1003			if len(errs) <= i {
1004				t.Errorf("missing error %s", want[i])
1005			} else if !strings.Contains(errs[i].Error(), want[i]) {
1006				t.Errorf("expected error %s, got %s", want[i], errs[i])
1007			}
1008		}
1009		if len(errs) > len(want) {
1010			for _, err := range errs[len(want):] {
1011				t.Errorf("unexpected error %s", err.Error())
1012			}
1013		}
1014	})
1015	t.Run("pause cycle", func(t *testing.T) {
1016		errs := parallelVisit([]*moduleInfo{moduleA, moduleB, moduleC, moduleD}, bottomUpVisitorImpl{}, 3,
1017			func(module *moduleInfo, pause chan<- pauseSpec) bool {
1018				if module == moduleC {
1019					// Pause module C on module D
1020					unpause := make(chan struct{})
1021					pause <- pauseSpec{moduleC, moduleD, unpause}
1022					<-unpause
1023				}
1024				if module == moduleD {
1025					// Pause module D on module C (a pause cycle)
1026					unpause := make(chan struct{})
1027					pause <- pauseSpec{moduleD, moduleC, unpause}
1028					<-unpause
1029				}
1030				return false
1031			})
1032		want := []string{
1033			`encountered dependency cycle`,
1034			`module "D" depends on module "C"`,
1035			`module "C" depends on module "D"`,
1036		}
1037		for i := range want {
1038			if len(errs) <= i {
1039				t.Errorf("missing error %s", want[i])
1040			} else if !strings.Contains(errs[i].Error(), want[i]) {
1041				t.Errorf("expected error %s, got %s", want[i], errs[i])
1042			}
1043		}
1044		if len(errs) > len(want) {
1045			for _, err := range errs[len(want):] {
1046				t.Errorf("unexpected error %s", err.Error())
1047			}
1048		}
1049	})
1050	t.Run("pause cycle with deps", func(t *testing.T) {
1051		pauseDeps := map[*moduleInfo]*moduleInfo{
1052			// F and G form a pause cycle
1053			moduleF: moduleG,
1054			moduleG: moduleF,
1055			// D depends on E which depends on the pause cycle, making E the first alphabetical
1056			// entry in pauseMap, which is not part of the cycle.
1057			moduleD: moduleE,
1058			moduleE: moduleF,
1059		}
1060		errs := parallelVisit([]*moduleInfo{moduleD, moduleE, moduleF, moduleG}, bottomUpVisitorImpl{}, 4,
1061			func(module *moduleInfo, pause chan<- pauseSpec) bool {
1062				if dep, ok := pauseDeps[module]; ok {
1063					unpause := make(chan struct{})
1064					pause <- pauseSpec{module, dep, unpause}
1065					<-unpause
1066				}
1067				return false
1068			})
1069		want := []string{
1070			`encountered dependency cycle`,
1071			`module "G" depends on module "F"`,
1072			`module "F" depends on module "G"`,
1073		}
1074		for i := range want {
1075			if len(errs) <= i {
1076				t.Errorf("missing error %s", want[i])
1077			} else if !strings.Contains(errs[i].Error(), want[i]) {
1078				t.Errorf("expected error %s, got %s", want[i], errs[i])
1079			}
1080		}
1081		if len(errs) > len(want) {
1082			for _, err := range errs[len(want):] {
1083				t.Errorf("unexpected error %s", err.Error())
1084			}
1085		}
1086	})
1087}
1088
1089func TestPackageIncludes(t *testing.T) {
1090	dir1_foo_bp := `
1091	blueprint_package_includes {
1092		match_all: ["use_dir1"],
1093	}
1094	foo_module {
1095		name: "foo",
1096	}
1097	`
1098	dir2_foo_bp := `
1099	blueprint_package_includes {
1100		match_all: ["use_dir2"],
1101	}
1102	foo_module {
1103		name: "foo",
1104	}
1105	`
1106	mockFs := map[string][]byte{
1107		"dir1/Android.bp": []byte(dir1_foo_bp),
1108		"dir2/Android.bp": []byte(dir2_foo_bp),
1109	}
1110	testCases := []struct {
1111		desc        string
1112		includeTags []string
1113		expectedDir string
1114		expectedErr string
1115	}{
1116		{
1117			desc:        "use_dir1 is set, use dir1 foo",
1118			includeTags: []string{"use_dir1"},
1119			expectedDir: "dir1",
1120		},
1121		{
1122			desc:        "use_dir2 is set, use dir2 foo",
1123			includeTags: []string{"use_dir2"},
1124			expectedDir: "dir2",
1125		},
1126		{
1127			desc:        "duplicate module error if both use_dir1 and use_dir2 are set",
1128			includeTags: []string{"use_dir1", "use_dir2"},
1129			expectedDir: "",
1130			expectedErr: `module "foo" already defined`,
1131		},
1132	}
1133	for _, tc := range testCases {
1134		t.Run(tc.desc, func(t *testing.T) {
1135			ctx := NewContext()
1136			// Register mock FS
1137			ctx.MockFileSystem(mockFs)
1138			// Register module types
1139			ctx.RegisterModuleType("foo_module", newFooModule)
1140			RegisterPackageIncludesModuleType(ctx)
1141			// Add include tags for test case
1142			ctx.AddIncludeTags(tc.includeTags...)
1143			// Run test
1144			_, actualErrs := ctx.ParseFileList(".", []string{"dir1/Android.bp", "dir2/Android.bp"}, nil)
1145			// Evaluate
1146			if !strings.Contains(fmt.Sprintf("%s", actualErrs), fmt.Sprintf("%s", tc.expectedErr)) {
1147				t.Errorf("Expected errors: %s, got errors: %s\n", tc.expectedErr, actualErrs)
1148			}
1149			if tc.expectedErr != "" {
1150				return // expectedDir check not necessary
1151			}
1152			actualBpFile := ctx.moduleGroupFromName("foo", nil).modules.firstModule().relBlueprintsFile
1153			if tc.expectedDir != filepath.Dir(actualBpFile) {
1154				t.Errorf("Expected foo from %s, got %s\n", tc.expectedDir, filepath.Dir(actualBpFile))
1155			}
1156		})
1157	}
1158
1159}
1160
1161func TestDeduplicateOrderOnlyDeps(t *testing.T) {
1162	outputs := func(names ...string) []ninjaString {
1163		r := make([]ninjaString, len(names))
1164		for i, name := range names {
1165			r[i] = literalNinjaString(name)
1166		}
1167		return r
1168	}
1169	b := func(output string, inputs []string, orderOnlyDeps []string) *buildDef {
1170		return &buildDef{
1171			Outputs:   outputs(output),
1172			Inputs:    outputs(inputs...),
1173			OrderOnly: outputs(orderOnlyDeps...),
1174		}
1175	}
1176	m := func(bs ...*buildDef) *moduleInfo {
1177		return &moduleInfo{actionDefs: localBuildActions{buildDefs: bs}}
1178	}
1179	type testcase struct {
1180		modules        []*moduleInfo
1181		expectedPhonys []*buildDef
1182		conversions    map[string][]ninjaString
1183	}
1184	testCases := []testcase{{
1185		modules: []*moduleInfo{
1186			m(b("A", nil, []string{"d"})),
1187			m(b("B", nil, []string{"d"})),
1188		},
1189		expectedPhonys: []*buildDef{
1190			b("dedup-GKw-c0PwFokMUQ6T-TUmEWnZ4_VlQ2Qpgw-vCTT0-OQ", []string{"d"}, nil),
1191		},
1192		conversions: map[string][]ninjaString{
1193			"A": outputs("dedup-GKw-c0PwFokMUQ6T-TUmEWnZ4_VlQ2Qpgw-vCTT0-OQ"),
1194			"B": outputs("dedup-GKw-c0PwFokMUQ6T-TUmEWnZ4_VlQ2Qpgw-vCTT0-OQ"),
1195		},
1196	}, {
1197		modules: []*moduleInfo{
1198			m(b("A", nil, []string{"a"})),
1199			m(b("B", nil, []string{"b"})),
1200		},
1201	}, {
1202		modules: []*moduleInfo{
1203			m(b("A", nil, []string{"a"})),
1204			m(b("B", nil, []string{"b"})),
1205			m(b("C", nil, []string{"a"})),
1206		},
1207		expectedPhonys: []*buildDef{b("dedup-ypeBEsobvcr6wjGzmiPcTaeG7_gUfE5yuYB3ha_uSLs", []string{"a"}, nil)},
1208		conversions: map[string][]ninjaString{
1209			"A": outputs("dedup-ypeBEsobvcr6wjGzmiPcTaeG7_gUfE5yuYB3ha_uSLs"),
1210			"B": outputs("b"),
1211			"C": outputs("dedup-ypeBEsobvcr6wjGzmiPcTaeG7_gUfE5yuYB3ha_uSLs"),
1212		},
1213	}, {
1214		modules: []*moduleInfo{
1215			m(b("A", nil, []string{"a", "b"}),
1216				b("B", nil, []string{"a", "b"})),
1217			m(b("C", nil, []string{"a", "c"}),
1218				b("D", nil, []string{"a", "c"})),
1219		},
1220		expectedPhonys: []*buildDef{
1221			b("dedup--44g_C5MPySMYMOb1lLzwTRymLuXe4tNWQO4UFViBgM", []string{"a", "b"}, nil),
1222			b("dedup-9F3lHN7zCZFVHkHogt17VAR5lkigoAdT9E_JZuYVP8E", []string{"a", "c"}, nil)},
1223		conversions: map[string][]ninjaString{
1224			"A": outputs("dedup--44g_C5MPySMYMOb1lLzwTRymLuXe4tNWQO4UFViBgM"),
1225			"B": outputs("dedup--44g_C5MPySMYMOb1lLzwTRymLuXe4tNWQO4UFViBgM"),
1226			"C": outputs("dedup-9F3lHN7zCZFVHkHogt17VAR5lkigoAdT9E_JZuYVP8E"),
1227			"D": outputs("dedup-9F3lHN7zCZFVHkHogt17VAR5lkigoAdT9E_JZuYVP8E"),
1228		},
1229	}}
1230	for index, tc := range testCases {
1231		t.Run(fmt.Sprintf("TestCase-%d", index), func(t *testing.T) {
1232			ctx := NewContext()
1233			actualPhonys := ctx.deduplicateOrderOnlyDeps(tc.modules)
1234			if len(actualPhonys.variables) != 0 {
1235				t.Errorf("No variables expected but found %v", actualPhonys.variables)
1236			}
1237			if len(actualPhonys.rules) != 0 {
1238				t.Errorf("No rules expected but found %v", actualPhonys.rules)
1239			}
1240			if e, a := len(tc.expectedPhonys), len(actualPhonys.buildDefs); e != a {
1241				t.Errorf("Expected %d build statements but got %d", e, a)
1242			}
1243			for i := 0; i < len(tc.expectedPhonys); i++ {
1244				a := actualPhonys.buildDefs[i]
1245				e := tc.expectedPhonys[i]
1246				if !reflect.DeepEqual(e.Outputs, a.Outputs) {
1247					t.Errorf("phonys expected %v but actualPhonys %v", e.Outputs, a.Outputs)
1248				}
1249				if !reflect.DeepEqual(e.Inputs, a.Inputs) {
1250					t.Errorf("phonys expected %v but actualPhonys %v", e.Inputs, a.Inputs)
1251				}
1252			}
1253			find := func(k string) *buildDef {
1254				for _, m := range tc.modules {
1255					for _, b := range m.actionDefs.buildDefs {
1256						if reflect.DeepEqual(b.Outputs, outputs(k)) {
1257							return b
1258						}
1259					}
1260				}
1261				return nil
1262			}
1263			for k, conversion := range tc.conversions {
1264				actual := find(k)
1265				if actual == nil {
1266					t.Errorf("Couldn't find %s", k)
1267				}
1268				if !reflect.DeepEqual(actual.OrderOnly, conversion) {
1269					t.Errorf("expected %s.OrderOnly = %v but got %v", k, conversion, actual.OrderOnly)
1270				}
1271			}
1272		})
1273	}
1274}
1275
1276func TestSourceRootDirAllowed(t *testing.T) {
1277	type pathCase struct {
1278		path           string
1279		decidingPrefix string
1280		allowed        bool
1281	}
1282	testcases := []struct {
1283		desc      string
1284		rootDirs  []string
1285		pathCases []pathCase
1286	}{
1287		{
1288			desc: "simple case",
1289			rootDirs: []string{
1290				"a",
1291				"b/c/d",
1292				"-c",
1293				"-d/c/a",
1294				"c/some_single_file",
1295			},
1296			pathCases: []pathCase{
1297				{
1298					path:           "a",
1299					decidingPrefix: "a",
1300					allowed:        true,
1301				},
1302				{
1303					path:           "a/b/c",
1304					decidingPrefix: "a",
1305					allowed:        true,
1306				},
1307				{
1308					path:           "b",
1309					decidingPrefix: "",
1310					allowed:        true,
1311				},
1312				{
1313					path:           "b/c/d/a",
1314					decidingPrefix: "b/c/d",
1315					allowed:        true,
1316				},
1317				{
1318					path:           "c",
1319					decidingPrefix: "c",
1320					allowed:        false,
1321				},
1322				{
1323					path:           "c/a/b",
1324					decidingPrefix: "c",
1325					allowed:        false,
1326				},
1327				{
1328					path:           "c/some_single_file",
1329					decidingPrefix: "c/some_single_file",
1330					allowed:        true,
1331				},
1332				{
1333					path:           "d/c/a/abc",
1334					decidingPrefix: "d/c/a",
1335					allowed:        false,
1336				},
1337			},
1338		},
1339		{
1340			desc: "root directory order matters",
1341			rootDirs: []string{
1342				"-a",
1343				"a/c/some_allowed_file",
1344				"a/b/d/some_allowed_file",
1345				"a/b",
1346				"a/c",
1347				"-a/b/d",
1348			},
1349			pathCases: []pathCase{
1350				{
1351					path:           "a",
1352					decidingPrefix: "a",
1353					allowed:        false,
1354				},
1355				{
1356					path:           "a/some_disallowed_file",
1357					decidingPrefix: "a",
1358					allowed:        false,
1359				},
1360				{
1361					path:           "a/c/some_allowed_file",
1362					decidingPrefix: "a/c/some_allowed_file",
1363					allowed:        true,
1364				},
1365				{
1366					path:           "a/b/d/some_allowed_file",
1367					decidingPrefix: "a/b/d/some_allowed_file",
1368					allowed:        true,
1369				},
1370				{
1371					path:           "a/b/c",
1372					decidingPrefix: "a/b",
1373					allowed:        true,
1374				},
1375				{
1376					path:           "a/b/c/some_allowed_file",
1377					decidingPrefix: "a/b",
1378					allowed:        true,
1379				},
1380				{
1381					path:           "a/b/d",
1382					decidingPrefix: "a/b/d",
1383					allowed:        false,
1384				},
1385			},
1386		},
1387	}
1388	for _, tc := range testcases {
1389		dirs := SourceRootDirs{}
1390		dirs.Add(tc.rootDirs...)
1391		for _, pc := range tc.pathCases {
1392			t.Run(fmt.Sprintf("%s: %s", tc.desc, pc.path), func(t *testing.T) {
1393				allowed, decidingPrefix := dirs.SourceRootDirAllowed(pc.path)
1394				if allowed != pc.allowed {
1395					if pc.allowed {
1396						t.Errorf("expected path %q to be allowed, but was not; root allowlist: %q", pc.path, tc.rootDirs)
1397					} else {
1398						t.Errorf("path %q was allowed unexpectedly; root allowlist: %q", pc.path, tc.rootDirs)
1399					}
1400				}
1401				if decidingPrefix != pc.decidingPrefix {
1402					t.Errorf("expected decidingPrefix to be %q, but got %q", pc.decidingPrefix, decidingPrefix)
1403				}
1404			})
1405		}
1406	}
1407}
1408
1409func TestSourceRootDirs(t *testing.T) {
1410	root_foo_bp := `
1411	foo_module {
1412		name: "foo",
1413		deps: ["foo_dir1", "foo_dir_ignored_special_case"],
1414	}
1415	`
1416	dir1_foo_bp := `
1417	foo_module {
1418		name: "foo_dir1",
1419		deps: ["foo_dir_ignored"],
1420	}
1421	`
1422	dir_ignored_foo_bp := `
1423	foo_module {
1424		name: "foo_dir_ignored",
1425	}
1426	`
1427	dir_ignored_special_case_foo_bp := `
1428	foo_module {
1429		name: "foo_dir_ignored_special_case",
1430	}
1431	`
1432	mockFs := map[string][]byte{
1433		"Android.bp":                          []byte(root_foo_bp),
1434		"dir1/Android.bp":                     []byte(dir1_foo_bp),
1435		"dir_ignored/Android.bp":              []byte(dir_ignored_foo_bp),
1436		"dir_ignored/special_case/Android.bp": []byte(dir_ignored_special_case_foo_bp),
1437	}
1438	fileList := []string{}
1439	for f := range mockFs {
1440		fileList = append(fileList, f)
1441	}
1442	testCases := []struct {
1443		sourceRootDirs       []string
1444		expectedModuleDefs   []string
1445		unexpectedModuleDefs []string
1446		expectedErrs         []string
1447	}{
1448		{
1449			sourceRootDirs: []string{},
1450			expectedModuleDefs: []string{
1451				"foo",
1452				"foo_dir1",
1453				"foo_dir_ignored",
1454				"foo_dir_ignored_special_case",
1455			},
1456		},
1457		{
1458			sourceRootDirs: []string{"-", ""},
1459			unexpectedModuleDefs: []string{
1460				"foo",
1461				"foo_dir1",
1462				"foo_dir_ignored",
1463				"foo_dir_ignored_special_case",
1464			},
1465		},
1466		{
1467			sourceRootDirs: []string{"-"},
1468			unexpectedModuleDefs: []string{
1469				"foo",
1470				"foo_dir1",
1471				"foo_dir_ignored",
1472				"foo_dir_ignored_special_case",
1473			},
1474		},
1475		{
1476			sourceRootDirs: []string{"dir1"},
1477			expectedModuleDefs: []string{
1478				"foo",
1479				"foo_dir1",
1480				"foo_dir_ignored",
1481				"foo_dir_ignored_special_case",
1482			},
1483		},
1484		{
1485			sourceRootDirs: []string{"-dir1"},
1486			expectedModuleDefs: []string{
1487				"foo",
1488				"foo_dir_ignored",
1489				"foo_dir_ignored_special_case",
1490			},
1491			unexpectedModuleDefs: []string{
1492				"foo_dir1",
1493			},
1494			expectedErrs: []string{
1495				`Android.bp:2:2: module "foo" depends on skipped module "foo_dir1"; "foo_dir1" was defined in files(s) [dir1/Android.bp], but was skipped for reason(s) ["dir1/Android.bp" is a descendant of "dir1", and that path prefix was not included in PRODUCT_SOURCE_ROOT_DIRS]`,
1496			},
1497		},
1498		{
1499			sourceRootDirs: []string{"-", "dir1"},
1500			expectedModuleDefs: []string{
1501				"foo_dir1",
1502			},
1503			unexpectedModuleDefs: []string{
1504				"foo",
1505				"foo_dir_ignored",
1506				"foo_dir_ignored_special_case",
1507			},
1508			expectedErrs: []string{
1509				`dir1/Android.bp:2:2: module "foo_dir1" depends on skipped module "foo_dir_ignored"; "foo_dir_ignored" was defined in files(s) [dir_ignored/Android.bp], but was skipped for reason(s) ["dir_ignored/Android.bp" is a descendant of "", and that path prefix was not included in PRODUCT_SOURCE_ROOT_DIRS]`,
1510			},
1511		},
1512		{
1513			sourceRootDirs: []string{"-", "dir1", "dir_ignored/special_case/Android.bp"},
1514			expectedModuleDefs: []string{
1515				"foo_dir1",
1516				"foo_dir_ignored_special_case",
1517			},
1518			unexpectedModuleDefs: []string{
1519				"foo",
1520				"foo_dir_ignored",
1521			},
1522			expectedErrs: []string{
1523				"dir1/Android.bp:2:2: module \"foo_dir1\" depends on skipped module \"foo_dir_ignored\"; \"foo_dir_ignored\" was defined in files(s) [dir_ignored/Android.bp], but was skipped for reason(s) [\"dir_ignored/Android.bp\" is a descendant of \"\", and that path prefix was not included in PRODUCT_SOURCE_ROOT_DIRS]",
1524			},
1525		},
1526	}
1527	for _, tc := range testCases {
1528		t.Run(fmt.Sprintf(`source root dirs are %q`, tc.sourceRootDirs), func(t *testing.T) {
1529			ctx := NewContext()
1530			ctx.MockFileSystem(mockFs)
1531			ctx.RegisterModuleType("foo_module", newFooModule)
1532			ctx.RegisterBottomUpMutator("deps", depsMutator)
1533			ctx.AddSourceRootDirs(tc.sourceRootDirs...)
1534			RegisterPackageIncludesModuleType(ctx)
1535			ctx.ParseFileList(".", fileList, nil)
1536			_, actualErrs := ctx.ResolveDependencies(nil)
1537
1538			stringErrs := []string(nil)
1539			for _, err := range actualErrs {
1540				stringErrs = append(stringErrs, err.Error())
1541			}
1542			if !reflect.DeepEqual(tc.expectedErrs, stringErrs) {
1543				t.Errorf("expected to find errors %v; got %v", tc.expectedErrs, stringErrs)
1544			}
1545			for _, modName := range tc.expectedModuleDefs {
1546				allMods := ctx.moduleGroupFromName(modName, nil)
1547				if allMods == nil || len(allMods.modules) != 1 {
1548					mods := modulesOrAliases{}
1549					if allMods != nil {
1550						mods = allMods.modules
1551					}
1552					t.Errorf("expected to find one definition for module %q, but got %v", modName, mods)
1553				}
1554			}
1555
1556			for _, modName := range tc.unexpectedModuleDefs {
1557				allMods := ctx.moduleGroupFromName(modName, nil)
1558				if allMods != nil {
1559					t.Errorf("expected to find no definitions for module %q, but got %v", modName, allMods.modules)
1560				}
1561			}
1562		})
1563	}
1564}
1565