xref: /aosp_15_r20/build/blueprint/context_test.go (revision 1fa6dee971e1612fa5cc0aa5ca2d35a22e2c34a3)
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	"hash/fnv"
22	"os"
23	"reflect"
24	"slices"
25	"strconv"
26	"strings"
27	"sync"
28	"testing"
29	"text/scanner"
30	"time"
31
32	"github.com/google/blueprint/parser"
33	"github.com/google/blueprint/proptools"
34)
35
36type Walker interface {
37	Walk() bool
38}
39
40func walkDependencyGraph(ctx *Context, topModule *moduleInfo, allowDuplicates bool) (string, string) {
41	var outputDown string
42	var outputUp string
43	ctx.walkDeps(topModule, allowDuplicates,
44		func(dep depInfo, parent *moduleInfo) bool {
45			outputDown += ctx.ModuleName(dep.module.logicModule)
46			if tag, ok := dep.tag.(walkerDepsTag); ok {
47				if !tag.follow {
48					return false
49				}
50			}
51			if dep.module.logicModule.(Walker).Walk() {
52				return true
53			}
54
55			return false
56		},
57		func(dep depInfo, parent *moduleInfo) {
58			outputUp += ctx.ModuleName(dep.module.logicModule)
59		})
60	return outputDown, outputUp
61}
62
63type depsProvider interface {
64	Deps() []string
65	IgnoreDeps() []string
66}
67
68type IncrementalTestProvider struct {
69	Value string
70}
71
72var IncrementalTestProviderKey = NewProvider[IncrementalTestProvider]()
73
74type baseTestModule struct {
75	SimpleName
76	properties struct {
77		Deps         []string
78		Ignored_deps []string
79	}
80	GenerateBuildActionsCalled bool
81}
82
83func (b *baseTestModule) Deps() []string {
84	return b.properties.Deps
85}
86
87func (b *baseTestModule) IgnoreDeps() []string {
88	return b.properties.Ignored_deps
89}
90
91var pctx PackageContext
92
93func init() {
94	pctx = NewPackageContext("android/blueprint")
95}
96func (b *baseTestModule) GenerateBuildActions(ctx ModuleContext) {
97	b.GenerateBuildActionsCalled = true
98	outputFile := ctx.ModuleName() + "_phony_output"
99	ctx.Build(pctx, BuildParams{
100		Rule:    Phony,
101		Outputs: []string{outputFile},
102	})
103	SetProvider(ctx, IncrementalTestProviderKey, IncrementalTestProvider{
104		Value: ctx.ModuleName(),
105	})
106}
107
108type fooModule struct {
109	baseTestModule
110}
111
112func newFooModule() (Module, []interface{}) {
113	m := &fooModule{}
114	return m, []interface{}{&m.baseTestModule.properties, &m.SimpleName.Properties}
115}
116
117func (f *fooModule) Walk() bool {
118	return true
119}
120
121type barModule struct {
122	SimpleName
123	baseTestModule
124}
125
126func newBarModule() (Module, []interface{}) {
127	m := &barModule{}
128	return m, []interface{}{&m.baseTestModule.properties, &m.SimpleName.Properties}
129}
130
131func (b *barModule) Walk() bool {
132	return false
133}
134
135type incrementalModule struct {
136	SimpleName
137	baseTestModule
138	IncrementalModule
139}
140
141var _ Incremental = &incrementalModule{}
142
143func newIncrementalModule() (Module, []interface{}) {
144	m := &incrementalModule{}
145	return m, []interface{}{&m.baseTestModule.properties, &m.SimpleName.Properties}
146}
147
148type walkerDepsTag struct {
149	BaseDependencyTag
150	// True if the dependency should be followed, false otherwise.
151	follow bool
152}
153
154func depsMutator(mctx BottomUpMutatorContext) {
155	if m, ok := mctx.Module().(depsProvider); ok {
156		mctx.AddDependency(mctx.Module(), walkerDepsTag{follow: false}, m.IgnoreDeps()...)
157		mctx.AddDependency(mctx.Module(), walkerDepsTag{follow: true}, m.Deps()...)
158	}
159}
160
161func TestContextParse(t *testing.T) {
162	ctx := NewContext()
163	ctx.RegisterModuleType("foo_module", newFooModule)
164	ctx.RegisterModuleType("bar_module", newBarModule)
165
166	r := bytes.NewBufferString(`
167		foo_module {
168	        name: "MyFooModule",
169			deps: ["MyBarModule"],
170		}
171
172		bar_module {
173	        name: "MyBarModule",
174		}
175	`)
176
177	_, _, errs := ctx.parseOne(".", "Blueprint", r, parser.NewScope(nil), nil)
178	if len(errs) > 0 {
179		t.Errorf("unexpected parse errors:")
180		for _, err := range errs {
181			t.Errorf("  %s", err)
182		}
183		t.FailNow()
184	}
185
186	_, errs = ctx.ResolveDependencies(nil)
187	if len(errs) > 0 {
188		t.Errorf("unexpected dep errors:")
189		for _, err := range errs {
190			t.Errorf("  %s", err)
191		}
192		t.FailNow()
193	}
194}
195
196// > |===B---D       - represents a non-walkable edge
197// > A               = represents a walkable edge
198// > |===C===E---G
199// >     |       |   A should not be visited because it's the root node.
200// >     |===F===|   B, D and E should not be walked.
201func TestWalkDeps(t *testing.T) {
202	ctx := NewContext()
203	ctx.MockFileSystem(map[string][]byte{
204		"Android.bp": []byte(`
205			foo_module {
206			    name: "A",
207			    deps: ["B", "C"],
208			}
209
210			bar_module {
211			    name: "B",
212			    deps: ["D"],
213			}
214
215			foo_module {
216			    name: "C",
217			    deps: ["E", "F"],
218			}
219
220			foo_module {
221			    name: "D",
222			}
223
224			bar_module {
225			    name: "E",
226			    deps: ["G"],
227			}
228
229			foo_module {
230			    name: "F",
231			    deps: ["G"],
232			}
233
234			foo_module {
235			    name: "G",
236			}
237		`),
238	})
239
240	ctx.RegisterModuleType("foo_module", newFooModule)
241	ctx.RegisterModuleType("bar_module", newBarModule)
242	ctx.RegisterBottomUpMutator("deps", depsMutator)
243	_, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
244	if len(errs) > 0 {
245		t.Errorf("unexpected parse errors:")
246		for _, err := range errs {
247			t.Errorf("  %s", err)
248		}
249		t.FailNow()
250	}
251
252	_, errs = ctx.ResolveDependencies(nil)
253	if len(errs) > 0 {
254		t.Errorf("unexpected dep errors:")
255		for _, err := range errs {
256			t.Errorf("  %s", err)
257		}
258		t.FailNow()
259	}
260
261	topModule := ctx.moduleGroupFromName("A", nil).modules.firstModule()
262	outputDown, outputUp := walkDependencyGraph(ctx, topModule, false)
263	if outputDown != "BCEFG" {
264		t.Errorf("unexpected walkDeps behaviour: %s\ndown should be: BCEFG", outputDown)
265	}
266	if outputUp != "BEGFC" {
267		t.Errorf("unexpected walkDeps behaviour: %s\nup should be: BEGFC", outputUp)
268	}
269}
270
271// > |===B---D           - represents a non-walkable edge
272// > A                   = represents a walkable edge
273// > |===C===E===\       A should not be visited because it's the root node.
274// >     |       |       B, D should not be walked.
275// >     |===F===G===H   G should be visited multiple times
276// >         \===/       H should only be visited once
277func TestWalkDepsDuplicates(t *testing.T) {
278	ctx := NewContext()
279	ctx.MockFileSystem(map[string][]byte{
280		"Android.bp": []byte(`
281			foo_module {
282			    name: "A",
283			    deps: ["B", "C"],
284			}
285
286			bar_module {
287			    name: "B",
288			    deps: ["D"],
289			}
290
291			foo_module {
292			    name: "C",
293			    deps: ["E", "F"],
294			}
295
296			foo_module {
297			    name: "D",
298			}
299
300			foo_module {
301			    name: "E",
302			    deps: ["G"],
303			}
304
305			foo_module {
306			    name: "F",
307			    deps: ["G", "G"],
308			}
309
310			foo_module {
311			    name: "G",
312				deps: ["H"],
313			}
314
315			foo_module {
316			    name: "H",
317			}
318		`),
319	})
320
321	ctx.RegisterModuleType("foo_module", newFooModule)
322	ctx.RegisterModuleType("bar_module", newBarModule)
323	ctx.RegisterBottomUpMutator("deps", depsMutator)
324	_, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
325	if len(errs) > 0 {
326		t.Errorf("unexpected parse errors:")
327		for _, err := range errs {
328			t.Errorf("  %s", err)
329		}
330		t.FailNow()
331	}
332
333	_, errs = ctx.ResolveDependencies(nil)
334	if len(errs) > 0 {
335		t.Errorf("unexpected dep errors:")
336		for _, err := range errs {
337			t.Errorf("  %s", err)
338		}
339		t.FailNow()
340	}
341
342	topModule := ctx.moduleGroupFromName("A", nil).modules.firstModule()
343	outputDown, outputUp := walkDependencyGraph(ctx, topModule, true)
344	if outputDown != "BCEGHFGG" {
345		t.Errorf("unexpected walkDeps behaviour: %s\ndown should be: BCEGHFGG", outputDown)
346	}
347	if outputUp != "BHGEGGFC" {
348		t.Errorf("unexpected walkDeps behaviour: %s\nup should be: BHGEGGFC", outputUp)
349	}
350}
351
352// >                     - represents a non-walkable edge
353// > A                   = represents a walkable edge
354// > |===B-------\       A should not be visited because it's the root node.
355// >     |       |       B -> D should not be walked.
356// >     |===C===D===E   B -> C -> D -> E should be walked
357func TestWalkDepsDuplicates_IgnoreFirstPath(t *testing.T) {
358	ctx := NewContext()
359	ctx.MockFileSystem(map[string][]byte{
360		"Android.bp": []byte(`
361			foo_module {
362			    name: "A",
363			    deps: ["B"],
364			}
365
366			foo_module {
367			    name: "B",
368			    deps: ["C"],
369			    ignored_deps: ["D"],
370			}
371
372			foo_module {
373			    name: "C",
374			    deps: ["D"],
375			}
376
377			foo_module {
378			    name: "D",
379			    deps: ["E"],
380			}
381
382			foo_module {
383			    name: "E",
384			}
385		`),
386	})
387
388	ctx.RegisterModuleType("foo_module", newFooModule)
389	ctx.RegisterModuleType("bar_module", newBarModule)
390	ctx.RegisterBottomUpMutator("deps", depsMutator)
391	_, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
392	if len(errs) > 0 {
393		t.Errorf("unexpected parse errors:")
394		for _, err := range errs {
395			t.Errorf("  %s", err)
396		}
397		t.FailNow()
398	}
399
400	_, errs = ctx.ResolveDependencies(nil)
401	if len(errs) > 0 {
402		t.Errorf("unexpected dep errors:")
403		for _, err := range errs {
404			t.Errorf("  %s", err)
405		}
406		t.FailNow()
407	}
408
409	topModule := ctx.moduleGroupFromName("A", nil).modules.firstModule()
410	outputDown, outputUp := walkDependencyGraph(ctx, topModule, true)
411	expectedDown := "BDCDE"
412	if outputDown != expectedDown {
413		t.Errorf("unexpected walkDeps behaviour: %s\ndown should be: %s", outputDown, expectedDown)
414	}
415	expectedUp := "DEDCB"
416	if outputUp != expectedUp {
417		t.Errorf("unexpected walkDeps behaviour: %s\nup should be: %s", outputUp, expectedUp)
418	}
419}
420
421func TestCreateModule(t *testing.T) {
422	ctx := newContext()
423	ctx.MockFileSystem(map[string][]byte{
424		"Android.bp": []byte(`
425			foo_module {
426			    name: "A",
427			    deps: ["B", "C"],
428			}
429		`),
430	})
431
432	ctx.RegisterBottomUpMutator("create", createTestMutator).UsesCreateModule()
433	ctx.RegisterBottomUpMutator("deps", depsMutator)
434
435	ctx.RegisterModuleType("foo_module", newFooModule)
436	ctx.RegisterModuleType("bar_module", newBarModule)
437	_, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
438	if len(errs) > 0 {
439		t.Errorf("unexpected parse errors:")
440		for _, err := range errs {
441			t.Errorf("  %s", err)
442		}
443		t.FailNow()
444	}
445
446	_, errs = ctx.ResolveDependencies(nil)
447	if len(errs) > 0 {
448		t.Errorf("unexpected dep errors:")
449		for _, err := range errs {
450			t.Errorf("  %s", err)
451		}
452		t.FailNow()
453	}
454
455	a := ctx.moduleGroupFromName("A", nil).modules.firstModule().logicModule.(*fooModule)
456	b := ctx.moduleGroupFromName("B", nil).modules.firstModule().logicModule.(*barModule)
457	c := ctx.moduleGroupFromName("C", nil).modules.firstModule().logicModule.(*barModule)
458	d := ctx.moduleGroupFromName("D", nil).modules.firstModule().logicModule.(*fooModule)
459
460	checkDeps := func(m Module, expected string) {
461		var deps []string
462		ctx.VisitDirectDeps(m, func(m Module) {
463			deps = append(deps, ctx.ModuleName(m))
464		})
465		got := strings.Join(deps, ",")
466		if got != expected {
467			t.Errorf("unexpected %q dependencies, got %q expected %q",
468				ctx.ModuleName(m), got, expected)
469		}
470	}
471
472	checkDeps(a, "B,C")
473	checkDeps(b, "D")
474	checkDeps(c, "D")
475	checkDeps(d, "")
476}
477
478func createTestMutator(ctx BottomUpMutatorContext) {
479	type props struct {
480		Name string
481		Deps []string
482	}
483
484	ctx.CreateModule(newBarModule, "new_bar", &props{
485		Name: "B",
486		Deps: []string{"D"},
487	})
488
489	ctx.CreateModule(newBarModule, "new_bar", &props{
490		Name: "C",
491		Deps: []string{"D"},
492	})
493
494	ctx.CreateModule(newFooModule, "new_foo", &props{
495		Name: "D",
496	})
497}
498
499func TestWalkFileOrder(t *testing.T) {
500	// Run the test once to see how long it normally takes
501	start := time.Now()
502	doTestWalkFileOrder(t, time.Duration(0))
503	duration := time.Since(start)
504
505	// Run the test again, but put enough of a sleep into each visitor to detect ordering
506	// problems if they exist
507	doTestWalkFileOrder(t, duration)
508}
509
510// test that WalkBlueprintsFiles calls asyncVisitor in the right order
511func doTestWalkFileOrder(t *testing.T, sleepDuration time.Duration) {
512	// setup mock context
513	ctx := newContext()
514	mockFiles := map[string][]byte{
515		"Android.bp": []byte(`
516			sample_module {
517			    name: "a",
518			}
519		`),
520		"dir1/Android.bp": []byte(`
521			sample_module {
522			    name: "b",
523			}
524		`),
525		"dir1/dir2/Android.bp": []byte(`
526			sample_module {
527			    name: "c",
528			}
529		`),
530	}
531	ctx.MockFileSystem(mockFiles)
532
533	// prepare to monitor the visit order
534	visitOrder := []string{}
535	visitLock := sync.Mutex{}
536	correctVisitOrder := []string{"Android.bp", "dir1/Android.bp", "dir1/dir2/Android.bp"}
537
538	// sleep longer when processing the earlier files
539	chooseSleepDuration := func(fileName string) (duration time.Duration) {
540		duration = time.Duration(0)
541		for i := len(correctVisitOrder) - 1; i >= 0; i-- {
542			if fileName == correctVisitOrder[i] {
543				return duration
544			}
545			duration = duration + sleepDuration
546		}
547		panic("unrecognized file name " + fileName)
548	}
549
550	visitor := func(file *parser.File) {
551		time.Sleep(chooseSleepDuration(file.Name))
552		visitLock.Lock()
553		defer visitLock.Unlock()
554		visitOrder = append(visitOrder, file.Name)
555	}
556	keys := []string{"Android.bp", "dir1/Android.bp", "dir1/dir2/Android.bp"}
557
558	// visit the blueprints files
559	ctx.WalkBlueprintsFiles(".", keys, visitor)
560
561	// check the order
562	if !reflect.DeepEqual(visitOrder, correctVisitOrder) {
563		t.Errorf("Incorrect visit order; expected %v, got %v", correctVisitOrder, visitOrder)
564	}
565}
566
567// test that WalkBlueprintsFiles reports syntax errors
568func TestWalkingWithSyntaxError(t *testing.T) {
569	// setup mock context
570	ctx := newContext()
571	mockFiles := map[string][]byte{
572		"Android.bp": []byte(`
573			sample_module {
574			    name: "a" "b",
575			}
576		`),
577		"dir1/Android.bp": []byte(`
578			sample_module {
579			    name: "b",
580		`),
581		"dir1/dir2/Android.bp": []byte(`
582			sample_module {
583			    name: "c",
584			}
585		`),
586	}
587	ctx.MockFileSystem(mockFiles)
588
589	keys := []string{"Android.bp", "dir1/Android.bp", "dir1/dir2/Android.bp"}
590
591	// visit the blueprints files
592	_, errs := ctx.WalkBlueprintsFiles(".", keys, func(file *parser.File) {})
593
594	expectedErrs := []error{
595		errors.New(`Android.bp:3:18: expected "}", found String`),
596		errors.New(`dir1/Android.bp:4:3: expected "}", found EOF`),
597	}
598	if fmt.Sprintf("%s", expectedErrs) != fmt.Sprintf("%s", errs) {
599		t.Errorf("Incorrect errors; expected:\n%s\ngot:\n%s", expectedErrs, errs)
600	}
601
602}
603
604func TestParseFailsForModuleWithoutName(t *testing.T) {
605	ctx := NewContext()
606	ctx.MockFileSystem(map[string][]byte{
607		"Android.bp": []byte(`
608			foo_module {
609			    name: "A",
610			}
611
612			bar_module {
613			    deps: ["A"],
614			}
615		`),
616	})
617	ctx.RegisterModuleType("foo_module", newFooModule)
618	ctx.RegisterModuleType("bar_module", newBarModule)
619
620	_, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
621
622	expectedErrs := []error{
623		errors.New(`Android.bp:6:4: property 'name' is missing from a module`),
624	}
625	if fmt.Sprintf("%s", expectedErrs) != fmt.Sprintf("%s", errs) {
626		t.Errorf("Incorrect errors; expected:\n%s\ngot:\n%s", expectedErrs, errs)
627	}
628}
629
630func Test_findVariant(t *testing.T) {
631	module := &moduleInfo{
632		variant: variant{
633			name: "normal_local",
634			variations: variationMap{
635				map[string]string{
636					"normal": "normal",
637				},
638			},
639		},
640	}
641
642	type alias struct {
643		variant variant
644		target  int
645	}
646
647	makeDependencyGroup := func(in ...*moduleInfo) *moduleGroup {
648		group := &moduleGroup{
649			name: "dep",
650		}
651		for _, m := range in {
652			m.group = group
653			group.modules = append(group.modules, m)
654		}
655		return group
656	}
657
658	tests := []struct {
659		name         string
660		possibleDeps *moduleGroup
661		variations   []Variation
662		far          bool
663		reverse      bool
664		want         string
665	}{
666		{
667			name: "AddVariationDependencies(nil)",
668			// A dependency that matches the non-local variations of the module
669			possibleDeps: makeDependencyGroup(
670				&moduleInfo{
671					variant: variant{
672						name: "normal",
673						variations: variationMap{
674							map[string]string{
675								"normal": "normal",
676							},
677						},
678					},
679				},
680			),
681			variations: nil,
682			far:        false,
683			reverse:    false,
684			want:       "normal",
685		},
686		{
687			name: "AddVariationDependencies(a)",
688			// A dependency with local variations
689			possibleDeps: makeDependencyGroup(
690				&moduleInfo{
691					variant: variant{
692						name: "normal_a",
693						variations: variationMap{
694							map[string]string{
695								"normal": "normal",
696								"a":      "a",
697							},
698						},
699					},
700				},
701			),
702			variations: []Variation{{"a", "a"}},
703			far:        false,
704			reverse:    false,
705			want:       "normal_a",
706		},
707		{
708			name: "AddFarVariationDependencies(far)",
709			// A dependency with far variations
710			possibleDeps: makeDependencyGroup(
711				&moduleInfo{
712					variant: variant{
713						name:       "",
714						variations: variationMap{},
715					},
716				},
717				&moduleInfo{
718					variant: variant{
719						name: "far",
720						variations: variationMap{
721							map[string]string{
722								"far": "far",
723							},
724						},
725					},
726				},
727			),
728			variations: []Variation{{"far", "far"}},
729			far:        true,
730			reverse:    false,
731			want:       "far",
732		},
733	}
734	for _, tt := range tests {
735		t.Run(tt.name, func(t *testing.T) {
736			ctx := NewContext()
737			got, _, errs := ctx.findVariant(module, nil, tt.possibleDeps, tt.variations, tt.far, tt.reverse)
738			if errs != nil {
739				t.Fatal(errs)
740			}
741			if g, w := got == nil, tt.want == "nil"; g != w {
742				t.Fatalf("findVariant() got = %v, want %v", got, tt.want)
743			}
744			if got != nil {
745				if g, w := got.String(), fmt.Sprintf("module %q variant %q", "dep", tt.want); g != w {
746					t.Errorf("findVariant() got = %v, want %v", g, w)
747				}
748			}
749		})
750	}
751}
752
753func Test_parallelVisit(t *testing.T) {
754	addDep := func(from, to *moduleInfo) {
755		from.directDeps = append(from.directDeps, depInfo{to, nil})
756		from.forwardDeps = append(from.forwardDeps, to)
757		to.reverseDeps = append(to.reverseDeps, from)
758	}
759
760	create := func(name string) *moduleInfo {
761		m := &moduleInfo{
762			group: &moduleGroup{
763				name: name,
764			},
765		}
766		m.group.modules = moduleList{m}
767		return m
768	}
769	moduleA := create("A")
770	moduleB := create("B")
771	moduleC := create("C")
772	moduleD := create("D")
773	moduleE := create("E")
774	moduleF := create("F")
775	moduleG := create("G")
776
777	// A depends on B, B depends on C.  Nothing depends on D through G, and they don't depend on
778	// anything.
779	addDep(moduleA, moduleB)
780	addDep(moduleB, moduleC)
781
782	t.Run("no modules", func(t *testing.T) {
783		errs := parallelVisit(slices.Values([]*moduleInfo(nil)), bottomUpVisitorImpl{}, 1,
784			func(module *moduleInfo, pause chan<- pauseSpec) bool {
785				panic("unexpected call to visitor")
786			})
787		if errs != nil {
788			t.Errorf("expected no errors, got %q", errs)
789		}
790	})
791	t.Run("bottom up", func(t *testing.T) {
792		order := ""
793		errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC}), bottomUpVisitorImpl{}, 1,
794			func(module *moduleInfo, pause chan<- pauseSpec) bool {
795				order += module.group.name
796				return false
797			})
798		if errs != nil {
799			t.Errorf("expected no errors, got %q", errs)
800		}
801		if g, w := order, "CBA"; g != w {
802			t.Errorf("expected order %q, got %q", w, g)
803		}
804	})
805	t.Run("pause", func(t *testing.T) {
806		order := ""
807		errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC, moduleD}), bottomUpVisitorImpl{}, 1,
808			func(module *moduleInfo, pause chan<- pauseSpec) bool {
809				if module == moduleC {
810					// Pause module C on module D
811					unpause := make(chan struct{})
812					pause <- pauseSpec{moduleC, moduleD, unpause}
813					<-unpause
814				}
815				order += module.group.name
816				return false
817			})
818		if errs != nil {
819			t.Errorf("expected no errors, got %q", errs)
820		}
821		if g, w := order, "DCBA"; g != w {
822			t.Errorf("expected order %q, got %q", w, g)
823		}
824	})
825	t.Run("cancel", func(t *testing.T) {
826		order := ""
827		errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC}), bottomUpVisitorImpl{}, 1,
828			func(module *moduleInfo, pause chan<- pauseSpec) bool {
829				order += module.group.name
830				// Cancel in module B
831				return module == moduleB
832			})
833		if errs != nil {
834			t.Errorf("expected no errors, got %q", errs)
835		}
836		if g, w := order, "CB"; g != w {
837			t.Errorf("expected order %q, got %q", w, g)
838		}
839	})
840	t.Run("pause and cancel", func(t *testing.T) {
841		order := ""
842		errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC, moduleD}), bottomUpVisitorImpl{}, 1,
843			func(module *moduleInfo, pause chan<- pauseSpec) bool {
844				if module == moduleC {
845					// Pause module C on module D
846					unpause := make(chan struct{})
847					pause <- pauseSpec{moduleC, moduleD, unpause}
848					<-unpause
849				}
850				order += module.group.name
851				// Cancel in module D
852				return module == moduleD
853			})
854		if errs != nil {
855			t.Errorf("expected no errors, got %q", errs)
856		}
857		if g, w := order, "D"; g != w {
858			t.Errorf("expected order %q, got %q", w, g)
859		}
860	})
861	t.Run("parallel", func(t *testing.T) {
862		order := ""
863		errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC}), bottomUpVisitorImpl{}, 3,
864			func(module *moduleInfo, pause chan<- pauseSpec) bool {
865				order += module.group.name
866				return false
867			})
868		if errs != nil {
869			t.Errorf("expected no errors, got %q", errs)
870		}
871		if g, w := order, "CBA"; g != w {
872			t.Errorf("expected order %q, got %q", w, g)
873		}
874	})
875	t.Run("pause existing", func(t *testing.T) {
876		order := ""
877		errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC}), bottomUpVisitorImpl{}, 3,
878			func(module *moduleInfo, pause chan<- pauseSpec) bool {
879				if module == moduleA {
880					// Pause module A on module B (an existing dependency)
881					unpause := make(chan struct{})
882					pause <- pauseSpec{moduleA, moduleB, unpause}
883					<-unpause
884				}
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("cycle", func(t *testing.T) {
896		errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC}), bottomUpVisitorImpl{}, 3,
897			func(module *moduleInfo, pause chan<- pauseSpec) bool {
898				if module == moduleC {
899					// Pause module C on module A (a dependency cycle)
900					unpause := make(chan struct{})
901					pause <- pauseSpec{moduleC, moduleA, unpause}
902					<-unpause
903				}
904				return false
905			})
906		want := []string{
907			`encountered dependency cycle`,
908			`module "C" depends on module "A"`,
909			`module "A" depends on module "B"`,
910			`module "B" depends on module "C"`,
911		}
912		for i := range want {
913			if len(errs) <= i {
914				t.Errorf("missing error %s", want[i])
915			} else if !strings.Contains(errs[i].Error(), want[i]) {
916				t.Errorf("expected error %s, got %s", want[i], errs[i])
917			}
918		}
919		if len(errs) > len(want) {
920			for _, err := range errs[len(want):] {
921				t.Errorf("unexpected error %s", err.Error())
922			}
923		}
924	})
925	t.Run("pause cycle", func(t *testing.T) {
926		errs := parallelVisit(slices.Values([]*moduleInfo{moduleA, moduleB, moduleC, moduleD}), bottomUpVisitorImpl{}, 3,
927			func(module *moduleInfo, pause chan<- pauseSpec) bool {
928				if module == moduleC {
929					// Pause module C on module D
930					unpause := make(chan struct{})
931					pause <- pauseSpec{moduleC, moduleD, unpause}
932					<-unpause
933				}
934				if module == moduleD {
935					// Pause module D on module C (a pause cycle)
936					unpause := make(chan struct{})
937					pause <- pauseSpec{moduleD, moduleC, unpause}
938					<-unpause
939				}
940				return false
941			})
942		want := []string{
943			`encountered dependency cycle`,
944			`module "D" depends on module "C"`,
945			`module "C" depends on module "D"`,
946		}
947		for i := range want {
948			if len(errs) <= i {
949				t.Errorf("missing error %s", want[i])
950			} else if !strings.Contains(errs[i].Error(), want[i]) {
951				t.Errorf("expected error %s, got %s", want[i], errs[i])
952			}
953		}
954		if len(errs) > len(want) {
955			for _, err := range errs[len(want):] {
956				t.Errorf("unexpected error %s", err.Error())
957			}
958		}
959	})
960	t.Run("pause cycle with deps", func(t *testing.T) {
961		pauseDeps := map[*moduleInfo]*moduleInfo{
962			// F and G form a pause cycle
963			moduleF: moduleG,
964			moduleG: moduleF,
965			// D depends on E which depends on the pause cycle, making E the first alphabetical
966			// entry in pauseMap, which is not part of the cycle.
967			moduleD: moduleE,
968			moduleE: moduleF,
969		}
970		errs := parallelVisit(slices.Values([]*moduleInfo{moduleD, moduleE, moduleF, moduleG}), bottomUpVisitorImpl{}, 4,
971			func(module *moduleInfo, pause chan<- pauseSpec) bool {
972				if dep, ok := pauseDeps[module]; ok {
973					unpause := make(chan struct{})
974					pause <- pauseSpec{module, dep, unpause}
975					<-unpause
976				}
977				return false
978			})
979		want := []string{
980			`encountered dependency cycle`,
981			`module "G" depends on module "F"`,
982			`module "F" depends on module "G"`,
983		}
984		for i := range want {
985			if len(errs) <= i {
986				t.Errorf("missing error %s", want[i])
987			} else if !strings.Contains(errs[i].Error(), want[i]) {
988				t.Errorf("expected error %s, got %s", want[i], errs[i])
989			}
990		}
991		if len(errs) > len(want) {
992			for _, err := range errs[len(want):] {
993				t.Errorf("unexpected error %s", err.Error())
994			}
995		}
996	})
997}
998
999func TestDeduplicateOrderOnlyDeps(t *testing.T) {
1000	b := func(output string, inputs []string, orderOnlyDeps []string) *buildDef {
1001		return &buildDef{
1002			OutputStrings:    []string{output},
1003			InputStrings:     inputs,
1004			OrderOnlyStrings: orderOnlyDeps,
1005		}
1006	}
1007	m := func(bs ...*buildDef) *moduleInfo {
1008		return &moduleInfo{actionDefs: localBuildActions{buildDefs: bs}}
1009	}
1010	type testcase struct {
1011		modules        []*moduleInfo
1012		expectedPhonys []*buildDef
1013		conversions    map[string][]string
1014	}
1015	fnvHash := func(s string) string {
1016		hash := fnv.New64a()
1017		hash.Write([]byte(s))
1018		return strconv.FormatUint(hash.Sum64(), 16)
1019	}
1020	testCases := []testcase{{
1021		modules: []*moduleInfo{
1022			m(b("A", nil, []string{"d"})),
1023			m(b("B", nil, []string{"d"})),
1024		},
1025		expectedPhonys: []*buildDef{
1026			b("dedup-"+fnvHash("d"), []string{"d"}, nil),
1027		},
1028		conversions: map[string][]string{
1029			"A": []string{"dedup-" + fnvHash("d")},
1030			"B": []string{"dedup-" + fnvHash("d")},
1031		},
1032	}, {
1033		modules: []*moduleInfo{
1034			m(b("A", nil, []string{"a"})),
1035			m(b("B", nil, []string{"b"})),
1036		},
1037	}, {
1038		modules: []*moduleInfo{
1039			m(b("A", nil, []string{"a"})),
1040			m(b("B", nil, []string{"b"})),
1041			m(b("C", nil, []string{"a"})),
1042		},
1043		expectedPhonys: []*buildDef{b("dedup-"+fnvHash("a"), []string{"a"}, nil)},
1044		conversions: map[string][]string{
1045			"A": []string{"dedup-" + fnvHash("a")},
1046			"B": []string{"b"},
1047			"C": []string{"dedup-" + fnvHash("a")},
1048		},
1049	}, {
1050		modules: []*moduleInfo{
1051			m(b("A", nil, []string{"a", "b"}),
1052				b("B", nil, []string{"a", "b"})),
1053			m(b("C", nil, []string{"a", "c"}),
1054				b("D", nil, []string{"a", "c"})),
1055		},
1056		expectedPhonys: []*buildDef{
1057			b("dedup-"+fnvHash("ab"), []string{"a", "b"}, nil),
1058			b("dedup-"+fnvHash("ac"), []string{"a", "c"}, nil)},
1059		conversions: map[string][]string{
1060			"A": []string{"dedup-" + fnvHash("ab")},
1061			"B": []string{"dedup-" + fnvHash("ab")},
1062			"C": []string{"dedup-" + fnvHash("ac")},
1063			"D": []string{"dedup-" + fnvHash("ac")},
1064		},
1065	}}
1066	for index, tc := range testCases {
1067		t.Run(fmt.Sprintf("TestCase-%d", index), func(t *testing.T) {
1068			ctx := NewContext()
1069			actualPhonys := ctx.deduplicateOrderOnlyDeps(tc.modules)
1070			if len(actualPhonys.variables) != 0 {
1071				t.Errorf("No variables expected but found %v", actualPhonys.variables)
1072			}
1073			if len(actualPhonys.rules) != 0 {
1074				t.Errorf("No rules expected but found %v", actualPhonys.rules)
1075			}
1076			if e, a := len(tc.expectedPhonys), len(actualPhonys.buildDefs); e != a {
1077				t.Errorf("Expected %d build statements but got %d", e, a)
1078			}
1079			for i := 0; i < len(tc.expectedPhonys); i++ {
1080				a := actualPhonys.buildDefs[i]
1081				e := tc.expectedPhonys[i]
1082				if !reflect.DeepEqual(e.Outputs, a.Outputs) {
1083					t.Errorf("phonys expected %v but actualPhonys %v", e.Outputs, a.Outputs)
1084				}
1085				if !reflect.DeepEqual(e.Inputs, a.Inputs) {
1086					t.Errorf("phonys expected %v but actualPhonys %v", e.Inputs, a.Inputs)
1087				}
1088			}
1089			find := func(k string) *buildDef {
1090				for _, m := range tc.modules {
1091					for _, b := range m.actionDefs.buildDefs {
1092						if reflect.DeepEqual(b.OutputStrings, []string{k}) {
1093							return b
1094						}
1095					}
1096				}
1097				return nil
1098			}
1099			for k, conversion := range tc.conversions {
1100				actual := find(k)
1101				if actual == nil {
1102					t.Errorf("Couldn't find %s", k)
1103				}
1104				if !reflect.DeepEqual(actual.OrderOnlyStrings, conversion) {
1105					t.Errorf("expected %s.OrderOnly = %v but got %v", k, conversion, actual.OrderOnly)
1106				}
1107			}
1108		})
1109	}
1110}
1111
1112func TestSourceRootDirAllowed(t *testing.T) {
1113	type pathCase struct {
1114		path           string
1115		decidingPrefix string
1116		allowed        bool
1117	}
1118	testcases := []struct {
1119		desc      string
1120		rootDirs  []string
1121		pathCases []pathCase
1122	}{
1123		{
1124			desc: "simple case",
1125			rootDirs: []string{
1126				"a",
1127				"b/c/d",
1128				"-c",
1129				"-d/c/a",
1130				"c/some_single_file",
1131			},
1132			pathCases: []pathCase{
1133				{
1134					path:           "a",
1135					decidingPrefix: "a",
1136					allowed:        true,
1137				},
1138				{
1139					path:           "a/b/c",
1140					decidingPrefix: "a",
1141					allowed:        true,
1142				},
1143				{
1144					path:           "b",
1145					decidingPrefix: "",
1146					allowed:        true,
1147				},
1148				{
1149					path:           "b/c/d/a",
1150					decidingPrefix: "b/c/d",
1151					allowed:        true,
1152				},
1153				{
1154					path:           "c",
1155					decidingPrefix: "c",
1156					allowed:        false,
1157				},
1158				{
1159					path:           "c/a/b",
1160					decidingPrefix: "c",
1161					allowed:        false,
1162				},
1163				{
1164					path:           "c/some_single_file",
1165					decidingPrefix: "c/some_single_file",
1166					allowed:        true,
1167				},
1168				{
1169					path:           "d/c/a/abc",
1170					decidingPrefix: "d/c/a",
1171					allowed:        false,
1172				},
1173			},
1174		},
1175		{
1176			desc: "root directory order matters",
1177			rootDirs: []string{
1178				"-a",
1179				"a/c/some_allowed_file",
1180				"a/b/d/some_allowed_file",
1181				"a/b",
1182				"a/c",
1183				"-a/b/d",
1184			},
1185			pathCases: []pathCase{
1186				{
1187					path:           "a",
1188					decidingPrefix: "a",
1189					allowed:        false,
1190				},
1191				{
1192					path:           "a/some_disallowed_file",
1193					decidingPrefix: "a",
1194					allowed:        false,
1195				},
1196				{
1197					path:           "a/c/some_allowed_file",
1198					decidingPrefix: "a/c/some_allowed_file",
1199					allowed:        true,
1200				},
1201				{
1202					path:           "a/b/d/some_allowed_file",
1203					decidingPrefix: "a/b/d/some_allowed_file",
1204					allowed:        true,
1205				},
1206				{
1207					path:           "a/b/c",
1208					decidingPrefix: "a/b",
1209					allowed:        true,
1210				},
1211				{
1212					path:           "a/b/c/some_allowed_file",
1213					decidingPrefix: "a/b",
1214					allowed:        true,
1215				},
1216				{
1217					path:           "a/b/d",
1218					decidingPrefix: "a/b/d",
1219					allowed:        false,
1220				},
1221			},
1222		},
1223	}
1224	for _, tc := range testcases {
1225		dirs := SourceRootDirs{}
1226		dirs.Add(tc.rootDirs...)
1227		for _, pc := range tc.pathCases {
1228			t.Run(fmt.Sprintf("%s: %s", tc.desc, pc.path), func(t *testing.T) {
1229				allowed, decidingPrefix := dirs.SourceRootDirAllowed(pc.path)
1230				if allowed != pc.allowed {
1231					if pc.allowed {
1232						t.Errorf("expected path %q to be allowed, but was not; root allowlist: %q", pc.path, tc.rootDirs)
1233					} else {
1234						t.Errorf("path %q was allowed unexpectedly; root allowlist: %q", pc.path, tc.rootDirs)
1235					}
1236				}
1237				if decidingPrefix != pc.decidingPrefix {
1238					t.Errorf("expected decidingPrefix to be %q, but got %q", pc.decidingPrefix, decidingPrefix)
1239				}
1240			})
1241		}
1242	}
1243}
1244
1245func TestSourceRootDirs(t *testing.T) {
1246	root_foo_bp := `
1247	foo_module {
1248		name: "foo",
1249		deps: ["foo_dir1", "foo_dir_ignored_special_case"],
1250	}
1251	`
1252	dir1_foo_bp := `
1253	foo_module {
1254		name: "foo_dir1",
1255		deps: ["foo_dir_ignored"],
1256	}
1257	`
1258	dir_ignored_foo_bp := `
1259	foo_module {
1260		name: "foo_dir_ignored",
1261	}
1262	`
1263	dir_ignored_special_case_foo_bp := `
1264	foo_module {
1265		name: "foo_dir_ignored_special_case",
1266	}
1267	`
1268	mockFs := map[string][]byte{
1269		"Android.bp":                          []byte(root_foo_bp),
1270		"dir1/Android.bp":                     []byte(dir1_foo_bp),
1271		"dir_ignored/Android.bp":              []byte(dir_ignored_foo_bp),
1272		"dir_ignored/special_case/Android.bp": []byte(dir_ignored_special_case_foo_bp),
1273	}
1274	fileList := []string{}
1275	for f := range mockFs {
1276		fileList = append(fileList, f)
1277	}
1278	testCases := []struct {
1279		sourceRootDirs       []string
1280		expectedModuleDefs   []string
1281		unexpectedModuleDefs []string
1282		expectedErrs         []string
1283	}{
1284		{
1285			sourceRootDirs: []string{},
1286			expectedModuleDefs: []string{
1287				"foo",
1288				"foo_dir1",
1289				"foo_dir_ignored",
1290				"foo_dir_ignored_special_case",
1291			},
1292		},
1293		{
1294			sourceRootDirs: []string{"-", ""},
1295			unexpectedModuleDefs: []string{
1296				"foo",
1297				"foo_dir1",
1298				"foo_dir_ignored",
1299				"foo_dir_ignored_special_case",
1300			},
1301		},
1302		{
1303			sourceRootDirs: []string{"-"},
1304			unexpectedModuleDefs: []string{
1305				"foo",
1306				"foo_dir1",
1307				"foo_dir_ignored",
1308				"foo_dir_ignored_special_case",
1309			},
1310		},
1311		{
1312			sourceRootDirs: []string{"dir1"},
1313			expectedModuleDefs: []string{
1314				"foo",
1315				"foo_dir1",
1316				"foo_dir_ignored",
1317				"foo_dir_ignored_special_case",
1318			},
1319		},
1320		{
1321			sourceRootDirs: []string{"-dir1"},
1322			expectedModuleDefs: []string{
1323				"foo",
1324				"foo_dir_ignored",
1325				"foo_dir_ignored_special_case",
1326			},
1327			unexpectedModuleDefs: []string{
1328				"foo_dir1",
1329			},
1330			expectedErrs: []string{
1331				`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]`,
1332			},
1333		},
1334		{
1335			sourceRootDirs: []string{"-", "dir1"},
1336			expectedModuleDefs: []string{
1337				"foo_dir1",
1338			},
1339			unexpectedModuleDefs: []string{
1340				"foo",
1341				"foo_dir_ignored",
1342				"foo_dir_ignored_special_case",
1343			},
1344			expectedErrs: []string{
1345				`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]`,
1346			},
1347		},
1348		{
1349			sourceRootDirs: []string{"-", "dir1", "dir_ignored/special_case/Android.bp"},
1350			expectedModuleDefs: []string{
1351				"foo_dir1",
1352				"foo_dir_ignored_special_case",
1353			},
1354			unexpectedModuleDefs: []string{
1355				"foo",
1356				"foo_dir_ignored",
1357			},
1358			expectedErrs: []string{
1359				"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]",
1360			},
1361		},
1362	}
1363	for _, tc := range testCases {
1364		t.Run(fmt.Sprintf(`source root dirs are %q`, tc.sourceRootDirs), func(t *testing.T) {
1365			ctx := NewContext()
1366			ctx.MockFileSystem(mockFs)
1367			ctx.RegisterModuleType("foo_module", newFooModule)
1368			ctx.RegisterBottomUpMutator("deps", depsMutator)
1369			ctx.AddSourceRootDirs(tc.sourceRootDirs...)
1370			ctx.ParseFileList(".", fileList, nil)
1371			_, actualErrs := ctx.ResolveDependencies(nil)
1372
1373			stringErrs := []string(nil)
1374			for _, err := range actualErrs {
1375				stringErrs = append(stringErrs, err.Error())
1376			}
1377			if !reflect.DeepEqual(tc.expectedErrs, stringErrs) {
1378				t.Errorf("expected to find errors %v; got %v", tc.expectedErrs, stringErrs)
1379			}
1380			for _, modName := range tc.expectedModuleDefs {
1381				allMods := ctx.moduleGroupFromName(modName, nil)
1382				if allMods == nil || len(allMods.modules) != 1 {
1383					mods := moduleList{}
1384					if allMods != nil {
1385						mods = allMods.modules
1386					}
1387					t.Errorf("expected to find one definition for module %q, but got %v", modName, mods)
1388				}
1389			}
1390
1391			for _, modName := range tc.unexpectedModuleDefs {
1392				allMods := ctx.moduleGroupFromName(modName, nil)
1393				if allMods != nil {
1394					t.Errorf("expected to find no definitions for module %q, but got %v", modName, allMods.modules)
1395				}
1396			}
1397		})
1398	}
1399}
1400
1401func incrementalSetup(t *testing.T) *Context {
1402	ctx := NewContext()
1403	fileSystem := map[string][]byte{
1404		"Android.bp": []byte(`
1405			incremental_module {
1406					name: "MyIncrementalModule",
1407					deps: ["MyBarModule"],
1408			}
1409
1410			bar_module {
1411					name: "MyBarModule",
1412			}
1413		`),
1414	}
1415	ctx.MockFileSystem(fileSystem)
1416	ctx.RegisterBottomUpMutator("deps", depsMutator)
1417	ctx.RegisterModuleType("incremental_module", newIncrementalModule)
1418	ctx.RegisterModuleType("bar_module", newBarModule)
1419
1420	_, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
1421	if len(errs) > 0 {
1422		t.Errorf("unexpected parse errors:")
1423		for _, err := range errs {
1424			t.Errorf("  %s", err)
1425		}
1426		t.FailNow()
1427	}
1428
1429	_, errs = ctx.ResolveDependencies(nil)
1430	if len(errs) > 0 {
1431		t.Errorf("unexpected dep errors:")
1432		for _, err := range errs {
1433			t.Errorf("  %s", err)
1434		}
1435		t.FailNow()
1436	}
1437
1438	return ctx
1439}
1440
1441func incrementalSetupForRestore(t *testing.T, orderOnlyStrings []string) (*Context, any) {
1442	ctx := incrementalSetup(t)
1443	incInfo := ctx.moduleGroupFromName("MyIncrementalModule", nil).modules.firstModule()
1444	barInfo := ctx.moduleGroupFromName("MyBarModule", nil).modules.firstModule()
1445
1446	providerHashes := make([]uint64, len(providerRegistry))
1447	// Use fixed value since SetProvider hasn't been called yet, so we can't go
1448	// through the providers of the module.
1449	for k, v := range map[providerKey]any{
1450		IncrementalTestProviderKey.providerKey: IncrementalTestProvider{
1451			Value: barInfo.Name(),
1452		},
1453	} {
1454		hash, err := proptools.CalculateHash(v)
1455		if err != nil {
1456			panic(fmt.Sprintf("Can't hash value of providers"))
1457		}
1458		providerHashes[k.id] = hash
1459	}
1460	cacheKey := calculateHashKey(incInfo, [][]uint64{providerHashes})
1461	var providerValue any = IncrementalTestProvider{Value: "MyIncrementalModule"}
1462	toCache := BuildActionCache{
1463		cacheKey: &BuildActionCachedData{
1464			Pos: &scanner.Position{
1465				Filename: "Android.bp",
1466				Line:     2,
1467				Column:   4,
1468				Offset:   4,
1469			},
1470			Providers: []CachedProvider{{
1471				Id:    &IncrementalTestProviderKey.providerKey,
1472				Value: &providerValue,
1473			}},
1474			OrderOnlyStrings: orderOnlyStrings,
1475		},
1476	}
1477	ctx.SetIncrementalEnabled(true)
1478	ctx.SetIncrementalAnalysis(true)
1479	ctx.buildActionsFromCache = toCache
1480
1481	return ctx, providerValue
1482}
1483
1484func calculateHashKey(m *moduleInfo, providerHashes [][]uint64) BuildActionCacheKey {
1485	hash, err := proptools.CalculateHash(m.properties)
1486	if err != nil {
1487		panic(newPanicErrorf(err, "failed to calculate properties hash"))
1488	}
1489	cacheInput := new(BuildActionCacheInput)
1490	cacheInput.PropertiesHash = hash
1491	cacheInput.ProvidersHash = providerHashes
1492	hash, err = proptools.CalculateHash(&cacheInput)
1493	if err != nil {
1494		panic(newPanicErrorf(err, "failed to calculate cache input hash"))
1495	}
1496	return BuildActionCacheKey{
1497		Id:        m.ModuleCacheKey(),
1498		InputHash: hash,
1499	}
1500}
1501
1502func TestCacheBuildActions(t *testing.T) {
1503	ctx := incrementalSetup(t)
1504	ctx.SetIncrementalEnabled(true)
1505
1506	_, errs := ctx.PrepareBuildActions(nil)
1507	if len(errs) > 0 {
1508		t.Errorf("unexpected errors calling generateModuleBuildActions:")
1509		for _, err := range errs {
1510			t.Errorf("  %s", err)
1511		}
1512		t.FailNow()
1513	}
1514
1515	incInfo := ctx.moduleGroupFromName("MyIncrementalModule", nil).modules.firstModule()
1516	barInfo := ctx.moduleGroupFromName("MyBarModule", nil).modules.firstModule()
1517	if len(ctx.buildActionsToCache) != 1 {
1518		t.Errorf("build actions are not cached for the incremental module")
1519	}
1520	cacheKey := calculateHashKey(incInfo, [][]uint64{barInfo.providerInitialValueHashes})
1521	cache := ctx.buildActionsToCache[cacheKey]
1522	if cache == nil {
1523		t.Errorf("failed to find cached build actions for the incremental module")
1524	}
1525	var providerValue any = IncrementalTestProvider{Value: "MyIncrementalModule"}
1526	expectedCache := BuildActionCachedData{
1527		Pos: &scanner.Position{
1528			Filename: "Android.bp",
1529			Line:     2,
1530			Column:   4,
1531			Offset:   4,
1532		},
1533		Providers: []CachedProvider{{
1534			Id:    &IncrementalTestProviderKey.providerKey,
1535			Value: &providerValue,
1536		}},
1537	}
1538	if !reflect.DeepEqual(expectedCache, *cache) {
1539		t.Errorf("expected: %v actual %v", expectedCache, *cache)
1540	}
1541}
1542
1543func TestRestoreBuildActions(t *testing.T) {
1544	ctx, providerValue := incrementalSetupForRestore(t, nil)
1545	incInfo := ctx.moduleGroupFromName("MyIncrementalModule", nil).modules.firstModule()
1546	barInfo := ctx.moduleGroupFromName("MyBarModule", nil).modules.firstModule()
1547	_, errs := ctx.PrepareBuildActions(nil)
1548	if len(errs) > 0 {
1549		t.Errorf("unexpected errors calling generateModuleBuildActions:")
1550		for _, err := range errs {
1551			t.Errorf("  %s", err)
1552		}
1553		t.FailNow()
1554	}
1555
1556	// Verify that the GenerateBuildActions was skipped for the incremental module
1557	incRerun := incInfo.logicModule.(*incrementalModule).GenerateBuildActionsCalled
1558	barRerun := barInfo.logicModule.(*barModule).GenerateBuildActionsCalled
1559	if incRerun || !barRerun {
1560		t.Errorf("failed to skip/rerun GenerateBuildActions: %t %t", incRerun, barRerun)
1561	}
1562	// Verify that the provider is set correctly for the incremental module
1563	if !reflect.DeepEqual(incInfo.providers[IncrementalTestProviderKey.id], providerValue) {
1564		t.Errorf("provider is not set correctly when restoring from cache")
1565	}
1566}
1567
1568func TestSkipNinjaForCacheHit(t *testing.T) {
1569	ctx, _ := incrementalSetupForRestore(t, nil)
1570	_, errs := ctx.PrepareBuildActions(nil)
1571	if len(errs) > 0 {
1572		t.Errorf("unexpected errors calling generateModuleBuildActions:")
1573		for _, err := range errs {
1574			t.Errorf("  %s", err)
1575		}
1576		t.FailNow()
1577	}
1578
1579	buf := bytes.NewBuffer(nil)
1580	w := newNinjaWriter(buf)
1581	ctx.writeAllModuleActions(w, true, "test.ninja")
1582	// Verify that soong updated the ninja file for the bar module and skipped the
1583	// ninja file writing of the incremental module
1584	file, err := ctx.fs.Open("test.0.ninja")
1585	if err != nil {
1586		t.Errorf("no ninja file for MyBarModule")
1587	}
1588	content := make([]byte, 1024)
1589	file.Read(content)
1590	if !strings.Contains(string(content), "build MyBarModule_phony_output: phony") {
1591		t.Errorf("ninja file doesn't have build statements for MyBarModule: %s", string(content))
1592	}
1593
1594	file, err = ctx.fs.Open("test_incremental_ninja/.-MyIncrementalModule-none-incremental_module.ninja")
1595	if !os.IsNotExist(err) {
1596		t.Errorf("shouldn't generate ninja file for MyIncrementalModule: %s", err.Error())
1597	}
1598}
1599
1600func TestNotSkipNinjaForCacheMiss(t *testing.T) {
1601	ctx := incrementalSetup(t)
1602	ctx.SetIncrementalEnabled(true)
1603	ctx.SetIncrementalAnalysis(true)
1604	_, errs := ctx.PrepareBuildActions(nil)
1605	if len(errs) > 0 {
1606		t.Errorf("unexpected errors calling generateModuleBuildActions:")
1607		for _, err := range errs {
1608			t.Errorf("  %s", err)
1609		}
1610		t.FailNow()
1611	}
1612
1613	buf := bytes.NewBuffer(nil)
1614	w := newNinjaWriter(buf)
1615	ctx.writeAllModuleActions(w, true, "test.ninja")
1616	// Verify that soong updated the ninja files for both the bar module and the
1617	// incremental module
1618	file, err := ctx.fs.Open("test.0.ninja")
1619	if err != nil {
1620		t.Errorf("no ninja file for MyBarModule")
1621	}
1622	content := make([]byte, 1024)
1623	file.Read(content)
1624	if !strings.Contains(string(content), "build MyBarModule_phony_output: phony") {
1625		t.Errorf("ninja file doesn't have build statements for MyBarModule: %s", string(content))
1626	}
1627
1628	file, err = ctx.fs.Open("test_incremental_ninja/.-MyIncrementalModule-none-incremental_module.ninja")
1629	if err != nil {
1630		t.Errorf("no ninja file for MyIncrementalModule")
1631	}
1632	file.Read(content)
1633	if !strings.Contains(string(content), "build MyIncrementalModule_phony_output: phony") {
1634		t.Errorf("ninja file doesn't have build statements for MyIncrementalModule: %s", string(content))
1635	}
1636}
1637
1638func TestOrderOnlyStringsCaching(t *testing.T) {
1639	ctx := incrementalSetup(t)
1640	ctx.SetIncrementalEnabled(true)
1641	_, errs := ctx.PrepareBuildActions(nil)
1642	if len(errs) > 0 {
1643		t.Errorf("unexpected errors calling generateModuleBuildActions:")
1644		for _, err := range errs {
1645			t.Errorf("  %s", err)
1646		}
1647		t.FailNow()
1648	}
1649	incInfo := ctx.moduleGroupFromName("MyIncrementalModule", nil).modules.firstModule()
1650	barInfo := ctx.moduleGroupFromName("MyBarModule", nil).modules.firstModule()
1651	bDef := buildDef{
1652		Rule:             Phony,
1653		OrderOnlyStrings: []string{"test.lib"},
1654	}
1655	incInfo.actionDefs.buildDefs = append(incInfo.actionDefs.buildDefs, &bDef)
1656	barInfo.actionDefs.buildDefs = append(barInfo.actionDefs.buildDefs, &bDef)
1657
1658	buf := bytes.NewBuffer(nil)
1659	w := newNinjaWriter(buf)
1660	ctx.writeAllModuleActions(w, true, "test.ninja")
1661
1662	verifyOrderOnlyStringsCache(t, ctx, incInfo, barInfo)
1663}
1664
1665func TestOrderOnlyStringsRestoring(t *testing.T) {
1666	phony := "dedup-d479e9a8133ff998"
1667	orderOnlyStrings := []string{phony}
1668	ctx, _ := incrementalSetupForRestore(t, orderOnlyStrings)
1669	ctx.orderOnlyStringsFromCache = make(OrderOnlyStringsCache)
1670	ctx.orderOnlyStringsFromCache[phony] = []string{"test.lib"}
1671	_, errs := ctx.PrepareBuildActions(nil)
1672	if len(errs) > 0 {
1673		t.Errorf("unexpected errors calling generateModuleBuildActions:")
1674		for _, err := range errs {
1675			t.Errorf("  %s", err)
1676		}
1677		t.FailNow()
1678	}
1679
1680	buf := bytes.NewBuffer(nil)
1681	w := newNinjaWriter(buf)
1682	ctx.writeAllModuleActions(w, true, "test.ninja")
1683
1684	incInfo := ctx.moduleGroupFromName("MyIncrementalModule", nil).modules.firstModule()
1685	barInfo := ctx.moduleGroupFromName("MyBarModule", nil).modules.firstModule()
1686	verifyOrderOnlyStringsCache(t, ctx, incInfo, barInfo)
1687
1688	// Verify dedup-d479e9a8133ff998 is still written to the common ninja file even
1689	// though MyBarModule no longer uses it.
1690	expected := strings.Join([]string{"build", phony + ":", "phony", "test.lib"}, " ")
1691	if !strings.Contains(buf.String(), expected) {
1692		t.Errorf("phony target not found: %s", buf.String())
1693	}
1694}
1695
1696func verifyOrderOnlyStringsCache(t *testing.T, ctx *Context, incInfo, barInfo *moduleInfo) {
1697	// Verify that soong cache all the order only strings that are used by the
1698	// incremental modules
1699	ok, key := mapContainsValue(ctx.orderOnlyStringsToCache, "test.lib")
1700	if !ok {
1701		t.Errorf("no order only strings used by incremetnal modules cached: %v", ctx.orderOnlyStringsToCache)
1702	}
1703
1704	// Verify that the dedup-* order only strings used by MyIncrementalModule is
1705	// cached along with its other cached values
1706	cacheKey := calculateHashKey(incInfo, [][]uint64{barInfo.providerInitialValueHashes})
1707	cache := ctx.buildActionsToCache[cacheKey]
1708	if cache == nil {
1709		t.Errorf("failed to find cached build actions for the incremental module")
1710	}
1711	if !listContainsValue(cache.OrderOnlyStrings, key) {
1712		t.Errorf("no order only strings cached for MyIncrementalModule: %v", cache.OrderOnlyStrings)
1713	}
1714}
1715
1716func listContainsValue[K comparable](l []K, target K) bool {
1717	for _, value := range l {
1718		if value == target {
1719			return true
1720		}
1721	}
1722	return false
1723}
1724
1725func mapContainsValue[K comparable, V comparable](m map[K][]V, target V) (bool, K) {
1726	for k, v := range m {
1727		if listContainsValue(v, target) {
1728			return true, k
1729		}
1730	}
1731	var key K
1732	return false, key
1733}
1734
1735func TestDisallowedMutatorMethods(t *testing.T) {
1736	testCases := []struct {
1737		name              string
1738		mutatorHandleFunc func(MutatorHandle)
1739		mutatorFunc       func(BottomUpMutatorContext)
1740		expectedPanic     string
1741	}{
1742		{
1743			name:              "rename",
1744			mutatorHandleFunc: func(handle MutatorHandle) { handle.UsesRename() },
1745			mutatorFunc:       func(ctx BottomUpMutatorContext) { ctx.Rename("qux") },
1746			expectedPanic:     "method Rename called from mutator that was not marked UsesRename",
1747		},
1748		{
1749			name:              "replace_dependencies",
1750			mutatorHandleFunc: func(handle MutatorHandle) { handle.UsesReplaceDependencies() },
1751			mutatorFunc:       func(ctx BottomUpMutatorContext) { ctx.ReplaceDependencies("bar") },
1752			expectedPanic:     "method ReplaceDependenciesIf called from mutator that was not marked UsesReplaceDependencies",
1753		},
1754		{
1755			name:              "replace_dependencies_if",
1756			mutatorHandleFunc: func(handle MutatorHandle) { handle.UsesReplaceDependencies() },
1757			mutatorFunc: func(ctx BottomUpMutatorContext) {
1758				ctx.ReplaceDependenciesIf("bar", func(from Module, tag DependencyTag, to Module) bool { return false })
1759			},
1760			expectedPanic: "method ReplaceDependenciesIf called from mutator that was not marked UsesReplaceDependencies",
1761		},
1762		{
1763			name:              "reverse_dependencies",
1764			mutatorHandleFunc: func(handle MutatorHandle) { handle.UsesReverseDependencies() },
1765			mutatorFunc:       func(ctx BottomUpMutatorContext) { ctx.AddReverseDependency(ctx.Module(), nil, "baz") },
1766			expectedPanic:     "method AddReverseDependency called from mutator that was not marked UsesReverseDependencies",
1767		},
1768		{
1769			name:              "create_module",
1770			mutatorHandleFunc: func(handle MutatorHandle) { handle.UsesCreateModule() },
1771			mutatorFunc: func(ctx BottomUpMutatorContext) {
1772				ctx.CreateModule(newFooModule, "create_module",
1773					&struct{ Name string }{Name: "quz"})
1774			},
1775			expectedPanic: "method CreateModule called from mutator that was not marked UsesCreateModule",
1776		},
1777	}
1778
1779	runTest := func(mutatorHandleFunc func(MutatorHandle), mutatorFunc func(ctx BottomUpMutatorContext), expectedPanic string) {
1780		ctx := NewContext()
1781
1782		ctx.MockFileSystem(map[string][]byte{
1783			"Android.bp": []byte(`
1784			foo_module {
1785				name: "foo",
1786			}
1787
1788			foo_module {
1789				name: "bar",
1790				deps: ["foo"],
1791			}
1792
1793			foo_module {
1794				name: "baz",
1795			}
1796		`)})
1797
1798		ctx.RegisterModuleType("foo_module", newFooModule)
1799		ctx.RegisterBottomUpMutator("deps", depsMutator)
1800		handle := ctx.RegisterBottomUpMutator("mutator", func(ctx BottomUpMutatorContext) {
1801			if ctx.ModuleName() == "foo" {
1802				mutatorFunc(ctx)
1803			}
1804		})
1805		mutatorHandleFunc(handle)
1806
1807		_, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
1808		if len(errs) > 0 {
1809			t.Errorf("unexpected parse errors:")
1810			for _, err := range errs {
1811				t.Errorf("  %s", err)
1812			}
1813			t.FailNow()
1814		}
1815
1816		_, errs = ctx.ResolveDependencies(nil)
1817		if expectedPanic != "" {
1818			if len(errs) == 0 {
1819				t.Errorf("missing expected error %q", expectedPanic)
1820			} else if !strings.Contains(errs[0].Error(), expectedPanic) {
1821				t.Errorf("missing expected error %q in %q", expectedPanic, errs[0].Error())
1822			}
1823		} else if len(errs) > 0 {
1824			t.Errorf("unexpected dep errors:")
1825			for _, err := range errs {
1826				t.Errorf("  %s", err)
1827			}
1828			t.FailNow()
1829		}
1830	}
1831
1832	noopMutatorHandleFunc := func(MutatorHandle) {}
1833
1834	for _, testCase := range testCases {
1835		t.Run(testCase.name, func(t *testing.T) {
1836			t.Run("allowed", func(t *testing.T) {
1837				// Test that the method doesn't panic when the handle function is called.
1838				runTest(testCase.mutatorHandleFunc, testCase.mutatorFunc, "")
1839			})
1840			t.Run("disallowed", func(t *testing.T) {
1841				// Test that the method does panic with the expected error when the
1842				// handle function is not called.
1843				runTest(noopMutatorHandleFunc, testCase.mutatorFunc, testCase.expectedPanic)
1844			})
1845		})
1846	}
1847
1848}
1849