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