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, &props{ 466 Name: "B", 467 Deps: []string{"D"}, 468 }) 469 470 ctx.CreateModule(newBarModule, &props{ 471 Name: "C", 472 Deps: []string{"D"}, 473 }) 474 475 ctx.CreateModule(newFooModule, &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 ctx := NewContext() 1135 // Register mock FS 1136 ctx.MockFileSystem(mockFs) 1137 // Register module types 1138 ctx.RegisterModuleType("foo_module", newFooModule) 1139 RegisterPackageIncludesModuleType(ctx) 1140 // Add include tags for test case 1141 ctx.AddIncludeTags(tc.includeTags...) 1142 // Run test 1143 _, actualErrs := ctx.ParseFileList(".", []string{"dir1/Android.bp", "dir2/Android.bp"}, nil) 1144 // Evaluate 1145 if !strings.Contains(fmt.Sprintf("%s", actualErrs), fmt.Sprintf("%s", tc.expectedErr)) { 1146 t.Errorf("Expected errors: %s, got errors: %s\n", tc.expectedErr, actualErrs) 1147 } 1148 if tc.expectedErr != "" { 1149 continue // expectedDir check not necessary 1150 } 1151 actualBpFile := ctx.moduleGroupFromName("foo", nil).modules.firstModule().relBlueprintsFile 1152 if tc.expectedDir != filepath.Dir(actualBpFile) { 1153 t.Errorf("Expected foo from %s, got %s\n", tc.expectedDir, filepath.Dir(actualBpFile)) 1154 } 1155 } 1156 1157} 1158