xref: /aosp_15_r20/build/blueprint/transition_test.go (revision 1fa6dee971e1612fa5cc0aa5ca2d35a22e2c34a3)
1// Copyright 2024 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package blueprint
16
17import (
18	"fmt"
19	"regexp"
20	"slices"
21	"strings"
22	"testing"
23)
24
25func testTransitionCommon(bp string, neverFar bool, ctxHook func(*Context)) (*Context, []error) {
26	ctx := newContext()
27	ctx.MockFileSystem(map[string][]byte{
28		"Android.bp": []byte(bp),
29	})
30
31	ctx.RegisterBottomUpMutator("deps", depsMutator)
32	handle := ctx.RegisterTransitionMutator("transition", transitionTestMutator{})
33	if neverFar {
34		handle.NeverFar()
35	}
36	ctx.RegisterBottomUpMutator("post_transition_deps", postTransitionDepsMutator).UsesReverseDependencies()
37
38	ctx.RegisterModuleType("transition_module", newTransitionModule)
39
40	if ctxHook != nil {
41		ctxHook(ctx)
42	}
43
44	_, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
45	if len(errs) > 0 {
46		return nil, errs
47	}
48
49	_, errs = ctx.ResolveDependencies(nil)
50	if len(errs) > 0 {
51		return nil, errs
52	}
53
54	return ctx, nil
55}
56
57func testTransition(bp string) (*Context, []error) {
58	return testTransitionCommon(bp, false, nil)
59}
60
61func testTransitionNeverFar(bp string) (*Context, []error) {
62	return testTransitionCommon(bp, true, nil)
63}
64
65func testTransitionAllowMissingDeps(bp string) (*Context, []error) {
66	return testTransitionCommon(bp, false, func(ctx *Context) {
67		ctx.SetAllowMissingDependencies(true)
68	})
69}
70
71func assertNoErrors(t *testing.T, errs []error) {
72	t.Helper()
73	if len(errs) > 0 {
74		t.Errorf("unexpected errors:")
75		for _, err := range errs {
76			t.Errorf("  %s", err)
77		}
78		t.FailNow()
79	}
80}
81
82func assertOneErrorMatches(t *testing.T, errs []error, re string) {
83	t.Helper()
84	if len(errs) == 0 {
85		t.Fatalf("expected 1 error, but found 0")
86	}
87	if len(errs) > 1 {
88		t.Errorf("expected exactly 1 error, but found:")
89		for _, err := range errs {
90			t.Errorf("  %s", err)
91		}
92		t.FailNow()
93	}
94	if match, err := regexp.MatchString(re, errs[0].Error()); err != nil {
95		t.Fatal(err.Error())
96	} else if !match {
97		t.Fatalf("expected error matching %q, but got: %q", re, errs[0].Error())
98	}
99}
100
101const testTransitionBp = `
102			transition_module {
103			    name: "A",
104			    deps: ["B", "C"],
105				split: ["b", "a"],
106			}
107
108			transition_module {
109				name: "B",
110				deps: ["C"],
111				outgoing: "c",
112				%s
113			}
114
115			transition_module {
116				name: "C",
117				deps: ["D"],
118			}
119
120			transition_module {
121				name: "D",
122				incoming: "d",
123				deps: ["E"],
124			}
125
126			transition_module {
127				name: "E",
128			}
129
130			transition_module {
131				name: "F",
132				incoming: "",
133			}
134
135			transition_module {
136				name: "G",
137				outgoing: "h",
138				%s
139			}
140
141			transition_module {
142				name: "H",
143				split: ["h"],
144			}
145		`
146
147func getTransitionModule(ctx *Context, name, variant string) *transitionModule {
148	group := ctx.moduleGroupFromName(name, nil)
149	module := group.moduleByVariantName(variant)
150	return module.logicModule.(*transitionModule)
151}
152
153func checkTransitionVariants(t *testing.T, ctx *Context, name string, expectedVariants []string) {
154	t.Helper()
155	group := ctx.moduleGroupFromName(name, nil)
156	var gotVariants []string
157	for _, module := range group.modules {
158		gotVariants = append(gotVariants, module.variant.variations.get("transition"))
159	}
160	if !slices.Equal(expectedVariants, gotVariants) {
161		t.Errorf("expected variants of %q to be %q, got %q", name, expectedVariants, gotVariants)
162	}
163}
164
165func checkTransitionDeps(t *testing.T, ctx *Context, m Module, expected ...string) {
166	t.Helper()
167	var got []string
168	ctx.VisitDirectDeps(m, func(m Module) {
169		got = append(got, ctx.ModuleName(m)+"("+ctx.ModuleSubDir(m)+")")
170	})
171	if !slices.Equal(got, expected) {
172		t.Errorf("unexpected %q dependencies, got %q expected %q",
173			ctx.ModuleName(m), got, expected)
174	}
175}
176
177func checkTransitionMutate(t *testing.T, m *transitionModule, variant string) {
178	t.Helper()
179	if m.properties.Mutated != variant {
180		t.Errorf("unexpected mutated property in %q, expected %q got %q", m.Name(), variant, m.properties.Mutated)
181	}
182}
183
184func TestTransition(t *testing.T) {
185	ctx, errs := testTransition(fmt.Sprintf(testTransitionBp, "", ""))
186	assertNoErrors(t, errs)
187
188	// Module A uses Split to create a and b variants
189	checkTransitionVariants(t, ctx, "A", []string{"b", "a"})
190	// Module B inherits a and b variants from A
191	checkTransitionVariants(t, ctx, "B", []string{"", "a", "b"})
192	// Module C inherits a and b variants from A, but gets an outgoing c variant from B
193	checkTransitionVariants(t, ctx, "C", []string{"", "a", "b", "c"})
194	// Module D always has incoming variant d
195	checkTransitionVariants(t, ctx, "D", []string{"", "d"})
196	// Module E inherits d from D
197	checkTransitionVariants(t, ctx, "E", []string{"", "d"})
198	// Module F is untouched
199	checkTransitionVariants(t, ctx, "F", []string{""})
200
201	A_a := getTransitionModule(ctx, "A", "a")
202	A_b := getTransitionModule(ctx, "A", "b")
203	B_a := getTransitionModule(ctx, "B", "a")
204	B_b := getTransitionModule(ctx, "B", "b")
205	C_a := getTransitionModule(ctx, "C", "a")
206	C_b := getTransitionModule(ctx, "C", "b")
207	C_c := getTransitionModule(ctx, "C", "c")
208	D_d := getTransitionModule(ctx, "D", "d")
209	E_d := getTransitionModule(ctx, "E", "d")
210	F := getTransitionModule(ctx, "F", "")
211	G := getTransitionModule(ctx, "G", "")
212	H_h := getTransitionModule(ctx, "H", "h")
213
214	checkTransitionDeps(t, ctx, A_a, "B(a)", "C(a)")
215	checkTransitionDeps(t, ctx, A_b, "B(b)", "C(b)")
216	checkTransitionDeps(t, ctx, B_a, "C(c)")
217	checkTransitionDeps(t, ctx, B_b, "C(c)")
218	checkTransitionDeps(t, ctx, C_a, "D(d)")
219	checkTransitionDeps(t, ctx, C_b, "D(d)")
220	checkTransitionDeps(t, ctx, C_c, "D(d)")
221	checkTransitionDeps(t, ctx, D_d, "E(d)")
222	checkTransitionDeps(t, ctx, E_d)
223	checkTransitionDeps(t, ctx, F)
224	checkTransitionDeps(t, ctx, G)
225	checkTransitionDeps(t, ctx, H_h)
226
227	checkTransitionMutate(t, A_a, "a")
228	checkTransitionMutate(t, A_b, "b")
229	checkTransitionMutate(t, B_a, "a")
230	checkTransitionMutate(t, B_b, "b")
231	checkTransitionMutate(t, C_a, "a")
232	checkTransitionMutate(t, C_b, "b")
233	checkTransitionMutate(t, C_c, "c")
234	checkTransitionMutate(t, D_d, "d")
235	checkTransitionMutate(t, E_d, "d")
236	checkTransitionMutate(t, F, "")
237	checkTransitionMutate(t, G, "")
238	checkTransitionMutate(t, H_h, "h")
239}
240
241func TestPostTransitionDeps(t *testing.T) {
242	ctx, errs := testTransition(fmt.Sprintf(testTransitionBp,
243		`post_transition_deps: ["C", "D:late", "E:d", "F"],`,
244		`post_transition_deps: ["H"],`))
245	assertNoErrors(t, errs)
246
247	// Module A uses Split to create a and b variants
248	checkTransitionVariants(t, ctx, "A", []string{"b", "a"})
249	// Module B inherits a and b variants from A
250	checkTransitionVariants(t, ctx, "B", []string{"", "a", "b"})
251	// Module C inherits a and b variants from A, but gets an outgoing c variant from B
252	checkTransitionVariants(t, ctx, "C", []string{"", "a", "b", "c"})
253	// Module D always has incoming variant d
254	checkTransitionVariants(t, ctx, "D", []string{"", "d"})
255	// Module E inherits d from D
256	checkTransitionVariants(t, ctx, "E", []string{"", "d"})
257	// Module F is untouched
258	checkTransitionVariants(t, ctx, "F", []string{""})
259
260	A_a := getTransitionModule(ctx, "A", "a")
261	A_b := getTransitionModule(ctx, "A", "b")
262	B_a := getTransitionModule(ctx, "B", "a")
263	B_b := getTransitionModule(ctx, "B", "b")
264	C_a := getTransitionModule(ctx, "C", "a")
265	C_b := getTransitionModule(ctx, "C", "b")
266	C_c := getTransitionModule(ctx, "C", "c")
267	D_d := getTransitionModule(ctx, "D", "d")
268	E_d := getTransitionModule(ctx, "E", "d")
269	F := getTransitionModule(ctx, "F", "")
270	G := getTransitionModule(ctx, "G", "")
271	H_h := getTransitionModule(ctx, "H", "h")
272
273	checkTransitionDeps(t, ctx, A_a, "B(a)", "C(a)")
274	checkTransitionDeps(t, ctx, A_b, "B(b)", "C(b)")
275	// Verify post-mutator dependencies added to B.  The first C(c) is a pre-mutator dependency.
276	//  C(c) was added by C and rewritten by OutgoingTransition on B
277	//  D(d) was added by D:late and rewritten by IncomingTransition on D
278	//  E(d) was added by E:d
279	//  F() was added by F and rewritten OutgoingTransition on B and then IncomingTransition on F
280	checkTransitionDeps(t, ctx, B_a, "C(c)", "C(c)", "D(d)", "E(d)", "F()")
281	checkTransitionDeps(t, ctx, B_b, "C(c)", "C(c)", "D(d)", "E(d)", "F()")
282	checkTransitionDeps(t, ctx, C_a, "D(d)")
283	checkTransitionDeps(t, ctx, C_b, "D(d)")
284	checkTransitionDeps(t, ctx, C_c, "D(d)")
285	checkTransitionDeps(t, ctx, D_d, "E(d)")
286	checkTransitionDeps(t, ctx, E_d)
287	checkTransitionDeps(t, ctx, F)
288	checkTransitionDeps(t, ctx, G, "H(h)")
289	checkTransitionDeps(t, ctx, H_h)
290
291	checkTransitionMutate(t, A_a, "a")
292	checkTransitionMutate(t, A_b, "b")
293	checkTransitionMutate(t, B_a, "a")
294	checkTransitionMutate(t, B_b, "b")
295	checkTransitionMutate(t, C_a, "a")
296	checkTransitionMutate(t, C_b, "b")
297	checkTransitionMutate(t, C_c, "c")
298	checkTransitionMutate(t, D_d, "d")
299	checkTransitionMutate(t, E_d, "d")
300	checkTransitionMutate(t, F, "")
301	checkTransitionMutate(t, G, "")
302	checkTransitionMutate(t, H_h, "h")
303}
304
305func TestPostTransitionReverseDeps(t *testing.T) {
306	ctx, errs := testTransition(`
307		transition_module {
308			name: "A",
309			split: ["a1", "a2"],
310		}
311
312		transition_module {
313			name: "B",
314			split: ["a1", "a2"],
315			post_transition_reverse_deps: ["A"],
316		}
317	`)
318	assertNoErrors(t, errs)
319
320	checkTransitionVariants(t, ctx, "A", []string{"a1", "a2"})
321	checkTransitionVariants(t, ctx, "B", []string{"a1", "a2"})
322
323	checkTransitionDeps(t, ctx, getTransitionModule(ctx, "A", "a1"), "B(a1)")
324	checkTransitionDeps(t, ctx, getTransitionModule(ctx, "A", "a2"), "B(a2)")
325	checkTransitionDeps(t, ctx, getTransitionModule(ctx, "B", "a1"))
326	checkTransitionDeps(t, ctx, getTransitionModule(ctx, "B", "a2"))
327}
328
329func TestPostTransitionReverseVariationDeps(t *testing.T) {
330	ctx, errs := testTransition(`
331		transition_module {
332			name: "A",
333			split: ["a"],
334		}
335
336		transition_module {
337			name: "B",
338			split: ["b"],
339			post_transition_reverse_variation_deps: ["A(a)"],
340		}
341	`)
342	assertNoErrors(t, errs)
343
344	checkTransitionVariants(t, ctx, "A", []string{"a"})
345	checkTransitionVariants(t, ctx, "B", []string{"b"})
346
347	checkTransitionDeps(t, ctx, getTransitionModule(ctx, "A", "a"), "B(b)")
348	checkTransitionDeps(t, ctx, getTransitionModule(ctx, "B", "b"))
349}
350
351func TestFarVariationDep(t *testing.T) {
352	ctx, errs := testTransition(`
353		transition_module {
354			name: "A",
355			split: ["a"],
356			deps: ["B"],
357		}
358		transition_module {
359			name: "B",
360			split: ["", "a"],
361		}
362		transition_module {
363			name: "C",
364			split: ["c"],
365			post_transition_far_deps: ["D"],
366		}
367		transition_module {
368			name: "D",
369			split: ["", "c"],
370		}
371	`)
372	assertNoErrors(t, errs)
373
374	checkTransitionVariants(t, ctx, "A", []string{"a"})
375	checkTransitionVariants(t, ctx, "B", []string{"", "a"})
376	checkTransitionVariants(t, ctx, "C", []string{"c"})
377	checkTransitionVariants(t, ctx, "D", []string{"", "c"})
378
379	checkTransitionDeps(t, ctx, getTransitionModule(ctx, "A", "a"), "B(a)")
380	checkTransitionDeps(t, ctx, getTransitionModule(ctx, "C", "c"), "D()")
381}
382
383func TestNeverFarFarVariationDep(t *testing.T) {
384	ctx, errs := testTransitionNeverFar(`
385		transition_module {
386			name: "A",
387			split: ["a"],
388			deps: ["B"],
389		}
390		transition_module {
391			name: "B",
392			split: ["", "a"],
393		}
394		transition_module {
395			name: "C",
396			split: ["c"],
397			post_transition_far_deps: ["D"],
398		}
399		transition_module {
400			name: "D",
401			split: ["", "c"],
402		}
403	`)
404	assertNoErrors(t, errs)
405
406	checkTransitionVariants(t, ctx, "A", []string{"a"})
407	checkTransitionVariants(t, ctx, "B", []string{"", "a"})
408	checkTransitionVariants(t, ctx, "C", []string{"c"})
409	checkTransitionVariants(t, ctx, "D", []string{"", "c"})
410
411	checkTransitionDeps(t, ctx, getTransitionModule(ctx, "A", "a"), "B(a)")
412	checkTransitionDeps(t, ctx, getTransitionModule(ctx, "C", "c"), "D(c)")
413}
414
415func TestPostTransitionReverseDepsErrorOnMissingDep(t *testing.T) {
416	_, errs := testTransition(`
417		transition_module {
418			name: "A",
419			split: ["a"],
420		}
421
422		transition_module {
423			name: "B",
424			split: ["b"],
425			post_transition_reverse_deps: ["A"],
426		}
427	`)
428	assertOneErrorMatches(t, errs, `reverse dependency "A" of "B" missing variant:\s*transition:b\s*available variants:\s*transition:a`)
429}
430
431func TestErrorInIncomingTransition(t *testing.T) {
432	_, errs := testTransition(`
433		transition_module {
434			name: "A",
435			split: ["a"],
436			deps: ["B"],
437		}
438		transition_module {
439			name: "B",
440			split: ["a"],
441			incoming_transition_error: "my incoming transition error",
442		}
443	`)
444	assertOneErrorMatches(t, errs, "my incoming transition error")
445}
446
447func TestErrorInOutgoingTransition(t *testing.T) {
448	_, errs := testTransition(`
449		transition_module {
450			name: "A",
451			split: ["a"],
452			deps: ["B"],
453			outgoing_transition_error: "my outgoing transition error",
454		}
455		transition_module {
456			name: "B",
457			split: ["a"],
458		}
459	`)
460	assertOneErrorMatches(t, errs, "my outgoing transition error")
461}
462
463func TestPostTransitionReverseDepsAllowMissingDeps(t *testing.T) {
464	_, errs := testTransitionAllowMissingDeps(`
465		transition_module {
466			name: "A",
467			split: ["a"],
468		}
469
470		transition_module {
471			name: "B",
472			split: ["b"],
473			post_transition_reverse_deps: ["A"],
474		}
475	`)
476	assertNoErrors(t, errs)
477}
478
479func TestPostTransitionDepsMissingVariant(t *testing.T) {
480	// TODO: eventually this will create the missing variant on demand
481	_, errs := testTransition(fmt.Sprintf(testTransitionBp,
482		`post_transition_deps: ["E:missing"],`, ""))
483	expectedError := `Android.bp:8:4: dependency "E" of "B" missing variant:
484  transition:missing
485available variants:
486  <empty variant>
487  transition:d`
488	if len(errs) != 1 || errs[0].Error() != expectedError {
489		t.Errorf("expected error %q, got %q", expectedError, errs)
490	}
491}
492
493func TestIsAddingDependency(t *testing.T) {
494	ctx, errs := testTransition(`
495		transition_module {
496			name: "A",
497			split: ["a1"],
498			deps: ["C"],
499		}
500
501		transition_module {
502			name: "B",
503			split: ["b1"],
504			post_transition_deps: ["C"],
505		}
506
507		transition_module {
508			name: "C",
509			split: ["c1", "c2"],
510			incoming: "c1",
511			post_transition_incoming: "c2",
512		}
513	`)
514	assertNoErrors(t, errs)
515
516	checkTransitionVariants(t, ctx, "A", []string{"a1"})
517	checkTransitionVariants(t, ctx, "B", []string{"b1"})
518	checkTransitionVariants(t, ctx, "C", []string{"c1", "c2"})
519
520	checkTransitionDeps(t, ctx, getTransitionModule(ctx, "A", "a1"), "C(c1)")
521	checkTransitionDeps(t, ctx, getTransitionModule(ctx, "B", "b1"), "C(c2)")
522}
523
524type transitionTestMutator struct{}
525
526func (transitionTestMutator) Split(ctx BaseModuleContext) []string {
527	if split := ctx.Module().(*transitionModule).properties.Split; len(split) > 0 {
528		return split
529	}
530	return []string{""}
531}
532
533func (transitionTestMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string {
534	if err := ctx.Module().(*transitionModule).properties.Outgoing_transition_error; err != nil {
535		ctx.ModuleErrorf("Error: %s", *err)
536	}
537	if outgoing := ctx.Module().(*transitionModule).properties.Outgoing; outgoing != nil {
538		return *outgoing
539	}
540	return sourceVariation
541}
542
543func (transitionTestMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string {
544	if err := ctx.Module().(*transitionModule).properties.Incoming_transition_error; err != nil {
545		ctx.ModuleErrorf("Error: %s", *err)
546	}
547	if ctx.IsAddingDependency() {
548		if incoming := ctx.Module().(*transitionModule).properties.Post_transition_incoming; incoming != nil {
549			return *incoming
550		}
551	}
552	if incoming := ctx.Module().(*transitionModule).properties.Incoming; incoming != nil {
553		return *incoming
554	}
555	return incomingVariation
556}
557
558func (transitionTestMutator) Mutate(ctx BottomUpMutatorContext, variation string) {
559	ctx.Module().(*transitionModule).properties.Mutated = variation
560}
561
562type transitionModule struct {
563	SimpleName
564	properties struct {
565		Deps                                   []string
566		Post_transition_deps                   []string
567		Post_transition_far_deps               []string
568		Post_transition_reverse_deps           []string
569		Post_transition_reverse_variation_deps []string
570		Split                                  []string
571		Outgoing                               *string
572		Incoming                               *string
573		Post_transition_incoming               *string
574		Outgoing_transition_error              *string
575		Incoming_transition_error              *string
576
577		Mutated string `blueprint:"mutated"`
578	}
579}
580
581func newTransitionModule() (Module, []interface{}) {
582	m := &transitionModule{}
583	return m, []interface{}{&m.properties, &m.SimpleName.Properties}
584}
585
586func (f *transitionModule) GenerateBuildActions(ModuleContext) {
587}
588
589func (f *transitionModule) Deps() []string {
590	return f.properties.Deps
591}
592
593func (f *transitionModule) IgnoreDeps() []string {
594	return nil
595}
596
597var nameAndVariantRegexp = regexp.MustCompile(`([a-zA-Z0-9_]+)\(([a-zA-Z0-9_]+)\)`)
598
599func postTransitionDepsMutator(mctx BottomUpMutatorContext) {
600	if m, ok := mctx.Module().(*transitionModule); ok {
601		for _, dep := range m.properties.Post_transition_deps {
602			module, variation, _ := strings.Cut(dep, ":")
603			var variations []Variation
604			if variation != "" {
605				variations = append(variations, Variation{"transition", variation})
606			}
607			mctx.AddVariationDependencies(variations, walkerDepsTag{follow: true}, module)
608		}
609		for _, dep := range m.properties.Post_transition_far_deps {
610			mctx.AddFarVariationDependencies(nil, walkerDepsTag{follow: true}, dep)
611		}
612		for _, dep := range m.properties.Post_transition_reverse_deps {
613			mctx.AddReverseDependency(m, walkerDepsTag{follow: true}, dep)
614		}
615		for _, dep := range m.properties.Post_transition_reverse_variation_deps {
616			match := nameAndVariantRegexp.FindStringSubmatch(dep)
617			if len(match) == 0 || match[0] != dep {
618				panic(fmt.Sprintf("Invalid Post_transition_reverse_variation_deps: %q. Expected module_name(variant)", dep))
619			}
620			mctx.AddReverseVariationDependency([]Variation{
621				{Mutator: "transition", Variation: match[2]},
622			}, walkerDepsTag{follow: true}, match[1])
623		}
624	}
625}
626