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