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