1// Copyright 2015 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 android 16 17import ( 18 "path/filepath" 19 "runtime" 20 "testing" 21 22 "github.com/google/blueprint" 23) 24 25func TestSrcIsModule(t *testing.T) { 26 type args struct { 27 s string 28 } 29 tests := []struct { 30 name string 31 args args 32 wantModule string 33 }{ 34 { 35 name: "file", 36 args: args{ 37 s: "foo", 38 }, 39 wantModule: "", 40 }, 41 { 42 name: "module", 43 args: args{ 44 s: ":foo", 45 }, 46 wantModule: "foo", 47 }, 48 { 49 name: "tag", 50 args: args{ 51 s: ":foo{.bar}", 52 }, 53 wantModule: "foo{.bar}", 54 }, 55 { 56 name: "extra colon", 57 args: args{ 58 s: ":foo:bar", 59 }, 60 wantModule: "foo:bar", 61 }, 62 { 63 name: "fully qualified", 64 args: args{ 65 s: "//foo:bar", 66 }, 67 wantModule: "//foo:bar", 68 }, 69 { 70 name: "fully qualified with tag", 71 args: args{ 72 s: "//foo:bar{.tag}", 73 }, 74 wantModule: "//foo:bar{.tag}", 75 }, 76 { 77 name: "invalid unqualified name", 78 args: args{ 79 s: ":foo/bar", 80 }, 81 wantModule: "", 82 }, 83 } 84 for _, tt := range tests { 85 t.Run(tt.name, func(t *testing.T) { 86 if gotModule := SrcIsModule(tt.args.s); gotModule != tt.wantModule { 87 t.Errorf("SrcIsModule() = %v, want %v", gotModule, tt.wantModule) 88 } 89 }) 90 } 91} 92 93func TestSrcIsModuleWithTag(t *testing.T) { 94 type args struct { 95 s string 96 } 97 tests := []struct { 98 name string 99 args args 100 wantModule string 101 wantTag string 102 }{ 103 { 104 name: "file", 105 args: args{ 106 s: "foo", 107 }, 108 wantModule: "", 109 wantTag: "", 110 }, 111 { 112 name: "module", 113 args: args{ 114 s: ":foo", 115 }, 116 wantModule: "foo", 117 wantTag: "", 118 }, 119 { 120 name: "tag", 121 args: args{ 122 s: ":foo{.bar}", 123 }, 124 wantModule: "foo", 125 wantTag: ".bar", 126 }, 127 { 128 name: "empty tag", 129 args: args{ 130 s: ":foo{}", 131 }, 132 wantModule: "foo", 133 wantTag: "", 134 }, 135 { 136 name: "extra colon", 137 args: args{ 138 s: ":foo:bar", 139 }, 140 wantModule: "foo:bar", 141 }, 142 { 143 name: "invalid tag", 144 args: args{ 145 s: ":foo{.bar", 146 }, 147 wantModule: "foo{.bar", 148 }, 149 { 150 name: "invalid tag 2", 151 args: args{ 152 s: ":foo.bar}", 153 }, 154 wantModule: "foo.bar}", 155 }, 156 { 157 name: "fully qualified", 158 args: args{ 159 s: "//foo:bar", 160 }, 161 wantModule: "//foo:bar", 162 }, 163 { 164 name: "fully qualified with tag", 165 args: args{ 166 s: "//foo:bar{.tag}", 167 }, 168 wantModule: "//foo:bar", 169 wantTag: ".tag", 170 }, 171 { 172 name: "invalid unqualified name", 173 args: args{ 174 s: ":foo/bar", 175 }, 176 wantModule: "", 177 }, 178 { 179 name: "invalid unqualified name with tag", 180 args: args{ 181 s: ":foo/bar{.tag}", 182 }, 183 wantModule: "", 184 }, 185 } 186 for _, tt := range tests { 187 t.Run(tt.name, func(t *testing.T) { 188 gotModule, gotTag := SrcIsModuleWithTag(tt.args.s) 189 if gotModule != tt.wantModule { 190 t.Errorf("SrcIsModuleWithTag() gotModule = %v, want %v", gotModule, tt.wantModule) 191 } 192 if gotTag != tt.wantTag { 193 t.Errorf("SrcIsModuleWithTag() gotTag = %v, want %v", gotTag, tt.wantTag) 194 } 195 }) 196 } 197} 198 199type depsModule struct { 200 ModuleBase 201 props struct { 202 Deps []string 203 } 204} 205 206func (m *depsModule) GenerateAndroidBuildActions(ctx ModuleContext) { 207 outputFile := PathForModuleOut(ctx, ctx.ModuleName()) 208 ctx.Build(pctx, BuildParams{ 209 Rule: Touch, 210 Output: outputFile, 211 }) 212 installFile := ctx.InstallFile(PathForModuleInstall(ctx), ctx.ModuleName(), outputFile) 213 ctx.InstallSymlink(PathForModuleInstall(ctx, "symlinks"), ctx.ModuleName(), installFile) 214} 215 216func (m *depsModule) DepsMutator(ctx BottomUpMutatorContext) { 217 ctx.AddDependency(ctx.Module(), installDepTag{}, m.props.Deps...) 218} 219 220func depsModuleFactory() Module { 221 m := &depsModule{} 222 m.AddProperties(&m.props) 223 InitAndroidArchModule(m, HostAndDeviceDefault, MultilibCommon) 224 return m 225} 226 227var prepareForModuleTests = FixtureRegisterWithContext(func(ctx RegistrationContext) { 228 ctx.RegisterModuleType("deps", depsModuleFactory) 229}) 230 231func TestErrorDependsOnDisabledModule(t *testing.T) { 232 bp := ` 233 deps { 234 name: "foo", 235 deps: ["bar"], 236 } 237 deps { 238 name: "bar", 239 enabled: false, 240 } 241 ` 242 243 prepareForModuleTests. 244 ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(`module "foo": depends on disabled module "bar"`)). 245 RunTestWithBp(t, bp) 246} 247 248func TestDistErrorChecking(t *testing.T) { 249 bp := ` 250 deps { 251 name: "foo", 252 dist: { 253 dest: "../invalid-dest", 254 dir: "../invalid-dir", 255 suffix: "invalid/suffix", 256 }, 257 dists: [ 258 { 259 dest: "../invalid-dest0", 260 dir: "../invalid-dir0", 261 suffix: "invalid/suffix0", 262 }, 263 { 264 dest: "../invalid-dest1", 265 dir: "../invalid-dir1", 266 suffix: "invalid/suffix1", 267 }, 268 ], 269 } 270 ` 271 272 expectedErrs := []string{ 273 "\\QAndroid.bp:5:13: module \"foo\": dist.dest: Path is outside directory: ../invalid-dest\\E", 274 "\\QAndroid.bp:6:12: module \"foo\": dist.dir: Path is outside directory: ../invalid-dir\\E", 275 "\\QAndroid.bp:7:15: module \"foo\": dist.suffix: Suffix may not contain a '/' character.\\E", 276 "\\QAndroid.bp:11:15: module \"foo\": dists[0].dest: Path is outside directory: ../invalid-dest0\\E", 277 "\\QAndroid.bp:12:14: module \"foo\": dists[0].dir: Path is outside directory: ../invalid-dir0\\E", 278 "\\QAndroid.bp:13:17: module \"foo\": dists[0].suffix: Suffix may not contain a '/' character.\\E", 279 "\\QAndroid.bp:16:15: module \"foo\": dists[1].dest: Path is outside directory: ../invalid-dest1\\E", 280 "\\QAndroid.bp:17:14: module \"foo\": dists[1].dir: Path is outside directory: ../invalid-dir1\\E", 281 "\\QAndroid.bp:18:17: module \"foo\": dists[1].suffix: Suffix may not contain a '/' character.\\E", 282 } 283 284 prepareForModuleTests. 285 ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(expectedErrs)). 286 RunTestWithBp(t, bp) 287} 288 289func TestInstall(t *testing.T) { 290 if runtime.GOOS != "linux" { 291 t.Skip("requires linux") 292 } 293 bp := ` 294 deps { 295 name: "foo", 296 deps: ["bar"], 297 } 298 299 deps { 300 name: "bar", 301 deps: ["baz", "qux"], 302 } 303 304 deps { 305 name: "baz", 306 deps: ["qux"], 307 } 308 309 deps { 310 name: "qux", 311 } 312 ` 313 314 result := GroupFixturePreparers( 315 prepareForModuleTests, 316 PrepareForTestWithArchMutator, 317 ).RunTestWithBp(t, bp) 318 319 module := func(name string, host bool) TestingModule { 320 variant := "android_common" 321 if host { 322 variant = result.Config.BuildOSCommonTarget.String() 323 } 324 return result.ModuleForTests(name, variant) 325 } 326 327 outputRule := func(name string) TestingBuildParams { return module(name, false).Output(name) } 328 329 installRule := func(name string) TestingBuildParams { 330 return module(name, false).Output(filepath.Join("out/soong/target/product/test_device/system", name)) 331 } 332 333 symlinkRule := func(name string) TestingBuildParams { 334 return module(name, false).Output(filepath.Join("out/soong/target/product/test_device/system/symlinks", name)) 335 } 336 337 hostOutputRule := func(name string) TestingBuildParams { return module(name, true).Output(name) } 338 339 hostInstallRule := func(name string) TestingBuildParams { 340 return module(name, true).Output(filepath.Join("out/soong/host/linux-x86", name)) 341 } 342 343 hostSymlinkRule := func(name string) TestingBuildParams { 344 return module(name, true).Output(filepath.Join("out/soong/host/linux-x86/symlinks", name)) 345 } 346 347 assertInputs := func(params TestingBuildParams, inputs ...Path) { 348 t.Helper() 349 AssertArrayString(t, "expected inputs", Paths(inputs).Strings(), 350 append(PathsIfNonNil(params.Input), params.Inputs...).Strings()) 351 } 352 353 assertImplicits := func(params TestingBuildParams, implicits ...Path) { 354 t.Helper() 355 AssertArrayString(t, "expected implicit dependencies", Paths(implicits).Strings(), 356 append(PathsIfNonNil(params.Implicit), params.Implicits...).Strings()) 357 } 358 359 assertOrderOnlys := func(params TestingBuildParams, orderonlys ...Path) { 360 t.Helper() 361 AssertArrayString(t, "expected orderonly dependencies", Paths(orderonlys).Strings(), 362 params.OrderOnly.Strings()) 363 } 364 365 // Check host install rule dependencies 366 assertInputs(hostInstallRule("foo"), hostOutputRule("foo").Output) 367 assertImplicits(hostInstallRule("foo"), 368 hostInstallRule("bar").Output, 369 hostSymlinkRule("bar").Output, 370 hostInstallRule("baz").Output, 371 hostSymlinkRule("baz").Output, 372 hostInstallRule("qux").Output, 373 hostSymlinkRule("qux").Output, 374 ) 375 assertOrderOnlys(hostInstallRule("foo")) 376 377 // Check host symlink rule dependencies. Host symlinks must use a normal dependency, not an 378 // order-only dependency, so that the tool gets updated when the symlink is depended on. 379 assertInputs(hostSymlinkRule("foo"), hostInstallRule("foo").Output) 380 assertImplicits(hostSymlinkRule("foo")) 381 assertOrderOnlys(hostSymlinkRule("foo")) 382 383 // Check device install rule dependencies 384 assertInputs(installRule("foo"), outputRule("foo").Output) 385 assertImplicits(installRule("foo")) 386 assertOrderOnlys(installRule("foo"), 387 installRule("bar").Output, 388 symlinkRule("bar").Output, 389 installRule("baz").Output, 390 symlinkRule("baz").Output, 391 installRule("qux").Output, 392 symlinkRule("qux").Output, 393 ) 394 395 // Check device symlink rule dependencies. Device symlinks could use an order-only dependency, 396 // but the current implementation uses a normal dependency. 397 assertInputs(symlinkRule("foo"), installRule("foo").Output) 398 assertImplicits(symlinkRule("foo")) 399 assertOrderOnlys(symlinkRule("foo")) 400} 401 402func TestInstallKatiEnabled(t *testing.T) { 403 if runtime.GOOS != "linux" { 404 t.Skip("requires linux") 405 } 406 bp := ` 407 deps { 408 name: "foo", 409 deps: ["bar"], 410 } 411 412 deps { 413 name: "bar", 414 deps: ["baz", "qux"], 415 } 416 417 deps { 418 name: "baz", 419 deps: ["qux"], 420 } 421 422 deps { 423 name: "qux", 424 } 425 ` 426 427 result := GroupFixturePreparers( 428 prepareForModuleTests, 429 PrepareForTestWithArchMutator, 430 FixtureModifyConfig(SetKatiEnabledForTests), 431 PrepareForTestWithMakevars, 432 ).RunTestWithBp(t, bp) 433 434 rules := result.InstallMakeRulesForTesting(t) 435 436 module := func(name string, host bool) TestingModule { 437 variant := "android_common" 438 if host { 439 variant = result.Config.BuildOSCommonTarget.String() 440 } 441 return result.ModuleForTests(name, variant) 442 } 443 444 outputRule := func(name string) TestingBuildParams { return module(name, false).Output(name) } 445 446 ruleForOutput := func(output string) InstallMakeRule { 447 for _, rule := range rules { 448 if rule.Target == output { 449 return rule 450 } 451 } 452 t.Fatalf("no make install rule for %s", output) 453 return InstallMakeRule{} 454 } 455 456 installRule := func(name string) InstallMakeRule { 457 return ruleForOutput(filepath.Join("out/target/product/test_device/system", name)) 458 } 459 460 symlinkRule := func(name string) InstallMakeRule { 461 return ruleForOutput(filepath.Join("out/target/product/test_device/system/symlinks", name)) 462 } 463 464 hostOutputRule := func(name string) TestingBuildParams { return module(name, true).Output(name) } 465 466 hostInstallRule := func(name string) InstallMakeRule { 467 return ruleForOutput(filepath.Join("out/host/linux-x86", name)) 468 } 469 470 hostSymlinkRule := func(name string) InstallMakeRule { 471 return ruleForOutput(filepath.Join("out/host/linux-x86/symlinks", name)) 472 } 473 474 assertDeps := func(rule InstallMakeRule, deps ...string) { 475 t.Helper() 476 AssertArrayString(t, "expected inputs", deps, rule.Deps) 477 } 478 479 assertOrderOnlys := func(rule InstallMakeRule, orderonlys ...string) { 480 t.Helper() 481 AssertArrayString(t, "expected orderonly dependencies", orderonlys, rule.OrderOnlyDeps) 482 } 483 484 // Check host install rule dependencies 485 assertDeps(hostInstallRule("foo"), 486 hostOutputRule("foo").Output.String(), 487 hostInstallRule("bar").Target, 488 hostSymlinkRule("bar").Target, 489 hostInstallRule("baz").Target, 490 hostSymlinkRule("baz").Target, 491 hostInstallRule("qux").Target, 492 hostSymlinkRule("qux").Target, 493 ) 494 assertOrderOnlys(hostInstallRule("foo")) 495 496 // Check host symlink rule dependencies. Host symlinks must use a normal dependency, not an 497 // order-only dependency, so that the tool gets updated when the symlink is depended on. 498 assertDeps(hostSymlinkRule("foo"), hostInstallRule("foo").Target) 499 assertOrderOnlys(hostSymlinkRule("foo")) 500 501 // Check device install rule dependencies 502 assertDeps(installRule("foo"), outputRule("foo").Output.String()) 503 assertOrderOnlys(installRule("foo"), 504 installRule("bar").Target, 505 symlinkRule("bar").Target, 506 installRule("baz").Target, 507 symlinkRule("baz").Target, 508 installRule("qux").Target, 509 symlinkRule("qux").Target, 510 ) 511 512 // Check device symlink rule dependencies. Device symlinks could use an order-only dependency, 513 // but the current implementation uses a normal dependency. 514 assertDeps(symlinkRule("foo"), installRule("foo").Target) 515 assertOrderOnlys(symlinkRule("foo")) 516} 517 518type PropsTestModuleEmbedded struct { 519 Embedded_prop *string 520} 521 522type StructInSlice struct { 523 G string 524 H bool 525 I []string 526} 527 528type propsTestModule struct { 529 ModuleBase 530 DefaultableModuleBase 531 props struct { 532 A string `android:"arch_variant"` 533 B *bool 534 C []string 535 } 536 otherProps struct { 537 PropsTestModuleEmbedded 538 539 D *int64 540 Nested struct { 541 E *string 542 } 543 F *string `blueprint:"mutated"` 544 545 Slice_of_struct []StructInSlice 546 } 547} 548 549func propsTestModuleFactory() Module { 550 module := &propsTestModule{} 551 module.AddProperties(&module.props, &module.otherProps) 552 InitAndroidArchModule(module, HostAndDeviceSupported, MultilibBoth) 553 InitDefaultableModule(module) 554 return module 555} 556 557type propsTestModuleDefaults struct { 558 ModuleBase 559 DefaultsModuleBase 560} 561 562func propsTestModuleDefaultsFactory() Module { 563 defaults := &propsTestModuleDefaults{} 564 module := propsTestModule{} 565 defaults.AddProperties(&module.props, &module.otherProps) 566 InitDefaultsModule(defaults) 567 return defaults 568} 569 570func (p *propsTestModule) GenerateAndroidBuildActions(ctx ModuleContext) { 571 str := "abc" 572 p.otherProps.F = &str 573} 574 575func TestUsedProperties(t *testing.T) { 576 testCases := []struct { 577 desc string 578 bp string 579 expectedProps []propInfo 580 }{ 581 { 582 desc: "only name", 583 bp: `test { 584 name: "foo", 585 } 586 `, 587 expectedProps: []propInfo{ 588 propInfo{Name: "Name", Type: "string", Value: "foo"}, 589 }, 590 }, 591 { 592 desc: "some props", 593 bp: `test { 594 name: "foo", 595 a: "abc", 596 b: true, 597 d: 123, 598 } 599 `, 600 expectedProps: []propInfo{ 601 propInfo{Name: "A", Type: "string", Value: "abc"}, 602 propInfo{Name: "B", Type: "bool", Value: "true"}, 603 propInfo{Name: "D", Type: "int64", Value: "123"}, 604 propInfo{Name: "Name", Type: "string", Value: "foo"}, 605 }, 606 }, 607 { 608 desc: "unused non-pointer prop", 609 bp: `test { 610 name: "foo", 611 b: true, 612 d: 123, 613 } 614 `, 615 expectedProps: []propInfo{ 616 // for non-pointer cannot distinguish between unused and intentionally set to empty 617 propInfo{Name: "A", Type: "string", Value: ""}, 618 propInfo{Name: "B", Type: "bool", Value: "true"}, 619 propInfo{Name: "D", Type: "int64", Value: "123"}, 620 propInfo{Name: "Name", Type: "string", Value: "foo"}, 621 }, 622 }, 623 { 624 desc: "nested props", 625 bp: `test { 626 name: "foo", 627 nested: { 628 e: "abc", 629 } 630 } 631 `, 632 expectedProps: []propInfo{ 633 propInfo{Name: "Name", Type: "string", Value: "foo"}, 634 propInfo{Name: "Nested.E", Type: "string", Value: "abc"}, 635 }, 636 }, 637 { 638 desc: "arch props", 639 bp: `test { 640 name: "foo", 641 arch: { 642 x86_64: { 643 a: "abc", 644 }, 645 } 646 } 647 `, 648 expectedProps: []propInfo{ 649 propInfo{Name: "Arch.X86_64.A", Type: "string", Value: "abc"}, 650 propInfo{Name: "Name", Type: "string", Value: "foo"}, 651 }, 652 }, 653 { 654 desc: "embedded props", 655 bp: `test { 656 name: "foo", 657 embedded_prop: "a", 658 } 659 `, 660 expectedProps: []propInfo{ 661 propInfo{Name: "Embedded_prop", Type: "string", Value: "a"}, 662 propInfo{Name: "Name", Type: "string", Value: "foo"}, 663 }, 664 }, 665 { 666 desc: "struct slice", 667 bp: `test { 668 name: "foo", 669 slice_of_struct: [ 670 { 671 g: "abc", 672 h: false, 673 i: ["baz"], 674 }, 675 { 676 g: "def", 677 h: true, 678 i: [], 679 }, 680 ] 681 } 682 `, 683 expectedProps: []propInfo{ 684 propInfo{Name: "Name", Type: "string", Value: "foo"}, 685 propInfo{Name: "Slice_of_struct", Type: "struct slice", Values: []string{ 686 `android.StructInSlice{G: abc, H: false, I: [baz]}`, 687 `android.StructInSlice{G: def, H: true, I: []}`, 688 }}, 689 }, 690 }, 691 { 692 desc: "defaults", 693 bp: ` 694test_defaults { 695 name: "foo_defaults", 696 a: "a", 697 b: true, 698 c: ["default_c"], 699 embedded_prop:"a", 700 arch: { 701 x86_64: { 702 a: "x86_64 a", 703 }, 704 }, 705} 706test { 707 name: "foo", 708 defaults: ["foo_defaults"], 709 c: ["c"], 710 nested: { 711 e: "nested e", 712 }, 713 target: { 714 linux: { 715 a: "a", 716 }, 717 }, 718} 719 `, 720 expectedProps: []propInfo{ 721 propInfo{Name: "A", Type: "string", Value: "a"}, 722 propInfo{Name: "Arch.X86_64.A", Type: "string", Value: "x86_64 a"}, 723 propInfo{Name: "B", Type: "bool", Value: "true"}, 724 propInfo{Name: "C", Type: "string slice", Values: []string{"default_c", "c"}}, 725 propInfo{Name: "Defaults", Type: "string slice", Values: []string{"foo_defaults"}}, 726 propInfo{Name: "Embedded_prop", Type: "string", Value: "a"}, 727 propInfo{Name: "Name", Type: "string", Value: "foo"}, 728 propInfo{Name: "Nested.E", Type: "string", Value: "nested e"}, 729 propInfo{Name: "Target.Linux.A", Type: "string", Value: "a"}, 730 }, 731 }, 732 } 733 734 for _, tc := range testCases { 735 t.Run(tc.desc, func(t *testing.T) { 736 result := GroupFixturePreparers( 737 PrepareForTestWithAllowMissingDependencies, 738 PrepareForTestWithDefaults, 739 FixtureRegisterWithContext(func(ctx RegistrationContext) { 740 ctx.RegisterModuleType("test", propsTestModuleFactory) 741 ctx.RegisterModuleType("test_defaults", propsTestModuleDefaultsFactory) 742 }), 743 FixtureWithRootAndroidBp(tc.bp), 744 ).RunTest(t) 745 746 foo := result.ModuleForTests("foo", "").Module().base() 747 748 AssertDeepEquals(t, "foo ", tc.expectedProps, foo.propertiesWithValues()) 749 }) 750 } 751} 752 753func TestSortedUniqueNamedPaths(t *testing.T) { 754 type np struct { 755 path, name string 756 } 757 makePaths := func(l []np) NamedPaths { 758 result := make(NamedPaths, 0, len(l)) 759 for _, p := range l { 760 result = append(result, NamedPath{PathForTesting(p.path), p.name}) 761 } 762 return result 763 } 764 765 tests := []struct { 766 name string 767 in []np 768 expectedOut []np 769 }{ 770 { 771 name: "empty", 772 in: []np{}, 773 expectedOut: []np{}, 774 }, 775 { 776 name: "all_same", 777 in: []np{ 778 {"a.txt", "A"}, 779 {"a.txt", "A"}, 780 {"a.txt", "A"}, 781 {"a.txt", "A"}, 782 {"a.txt", "A"}, 783 }, 784 expectedOut: []np{ 785 {"a.txt", "A"}, 786 }, 787 }, 788 { 789 name: "same_path_different_names", 790 in: []np{ 791 {"a.txt", "C"}, 792 {"a.txt", "A"}, 793 {"a.txt", "D"}, 794 {"a.txt", "B"}, 795 {"a.txt", "E"}, 796 }, 797 expectedOut: []np{ 798 {"a.txt", "A"}, 799 {"a.txt", "B"}, 800 {"a.txt", "C"}, 801 {"a.txt", "D"}, 802 {"a.txt", "E"}, 803 }, 804 }, 805 { 806 name: "different_paths_same_name", 807 in: []np{ 808 {"b/b.txt", "A"}, 809 {"a/a.txt", "A"}, 810 {"a/txt", "A"}, 811 {"b", "A"}, 812 {"a/b/d", "A"}, 813 }, 814 expectedOut: []np{ 815 {"a/a.txt", "A"}, 816 {"a/b/d", "A"}, 817 {"a/txt", "A"}, 818 {"b/b.txt", "A"}, 819 {"b", "A"}, 820 }, 821 }, 822 { 823 name: "all_different", 824 in: []np{ 825 {"b/b.txt", "A"}, 826 {"a/a.txt", "B"}, 827 {"a/txt", "D"}, 828 {"b", "C"}, 829 {"a/b/d", "E"}, 830 }, 831 expectedOut: []np{ 832 {"a/a.txt", "B"}, 833 {"a/b/d", "E"}, 834 {"a/txt", "D"}, 835 {"b/b.txt", "A"}, 836 {"b", "C"}, 837 }, 838 }, 839 { 840 name: "some_different", 841 in: []np{ 842 {"b/b.txt", "A"}, 843 {"a/a.txt", "B"}, 844 {"a/txt", "D"}, 845 {"a/b/d", "E"}, 846 {"b", "C"}, 847 {"a/a.txt", "B"}, 848 {"a/b/d", "E"}, 849 }, 850 expectedOut: []np{ 851 {"a/a.txt", "B"}, 852 {"a/b/d", "E"}, 853 {"a/txt", "D"}, 854 {"b/b.txt", "A"}, 855 {"b", "C"}, 856 }, 857 }, 858 } 859 for _, tt := range tests { 860 t.Run(tt.name, func(t *testing.T) { 861 actual := SortedUniqueNamedPaths(makePaths(tt.in)) 862 expected := makePaths(tt.expectedOut) 863 t.Logf("actual: %v", actual) 864 t.Logf("expected: %v", expected) 865 AssertDeepEquals(t, "SortedUniqueNamedPaths ", expected, actual) 866 }) 867 } 868} 869 870func TestSetAndroidMkEntriesWithTestOptions(t *testing.T) { 871 tests := []struct { 872 name string 873 testOptions CommonTestOptions 874 expected map[string][]string 875 }{ 876 { 877 name: "empty", 878 testOptions: CommonTestOptions{}, 879 expected: map[string][]string{}, 880 }, 881 { 882 name: "is unit test", 883 testOptions: CommonTestOptions{ 884 Unit_test: boolPtr(true), 885 }, 886 expected: map[string][]string{ 887 "LOCAL_IS_UNIT_TEST": []string{"true"}, 888 }, 889 }, 890 { 891 name: "is not unit test", 892 testOptions: CommonTestOptions{ 893 Unit_test: boolPtr(false), 894 }, 895 expected: map[string][]string{}, 896 }, 897 { 898 name: "empty tag", 899 testOptions: CommonTestOptions{ 900 Tags: []string{}, 901 }, 902 expected: map[string][]string{}, 903 }, 904 { 905 name: "single tag", 906 testOptions: CommonTestOptions{ 907 Tags: []string{"tag1"}, 908 }, 909 expected: map[string][]string{ 910 "LOCAL_TEST_OPTIONS_TAGS": []string{"tag1"}, 911 }, 912 }, 913 { 914 name: "multiple tag", 915 testOptions: CommonTestOptions{ 916 Tags: []string{"tag1", "tag2", "tag3"}, 917 }, 918 expected: map[string][]string{ 919 "LOCAL_TEST_OPTIONS_TAGS": []string{"tag1", "tag2", "tag3"}, 920 }, 921 }, 922 } 923 for _, tt := range tests { 924 t.Run(tt.name, func(t *testing.T) { 925 actualEntries := AndroidMkEntries{ 926 EntryMap: map[string][]string{}, 927 } 928 tt.testOptions.SetAndroidMkEntries(&actualEntries) 929 actual := actualEntries.EntryMap 930 t.Logf("actual: %v", actual) 931 t.Logf("expected: %v", tt.expected) 932 AssertDeepEquals(t, "TestProcessCommonTestOptions ", tt.expected, actual) 933 }) 934 } 935} 936 937type sourceProducerTestModule struct { 938 ModuleBase 939 props struct { 940 // A represents the source file 941 A string 942 } 943} 944 945func sourceProducerTestModuleFactory() Module { 946 module := &sourceProducerTestModule{} 947 module.AddProperties(&module.props) 948 InitAndroidModule(module) 949 return module 950} 951 952func (s sourceProducerTestModule) GenerateAndroidBuildActions(ModuleContext) {} 953 954func (s sourceProducerTestModule) Srcs() Paths { return PathsForTesting(s.props.A) } 955 956type outputFilesTestModule struct { 957 ModuleBase 958 props struct { 959 // A represents the tag 960 A string 961 // B represents the output file for tag A 962 B string 963 } 964} 965 966func outputFilesTestModuleFactory() Module { 967 module := &outputFilesTestModule{} 968 module.AddProperties(&module.props) 969 InitAndroidModule(module) 970 return module 971} 972 973func (o outputFilesTestModule) GenerateAndroidBuildActions(ctx ModuleContext) { 974 if o.props.A != "" || o.props.B != "" { 975 ctx.SetOutputFiles(PathsForTesting(o.props.B), o.props.A) 976 } 977 // This is to simulate the case that some module uses an object to set its 978 // OutputFilesProvider, but the object itself is empty. 979 ctx.SetOutputFiles(Paths{}, "missing") 980} 981 982type pathContextAddMissingDependenciesWrapper struct { 983 PathContext 984 OtherModuleProviderContext 985 missingDeps []string 986} 987 988func (p *pathContextAddMissingDependenciesWrapper) AddMissingDependencies(deps []string) { 989 p.missingDeps = append(p.missingDeps, deps...) 990} 991func (p *pathContextAddMissingDependenciesWrapper) OtherModuleName(module blueprint.Module) string { 992 return module.Name() 993} 994 995func (p *pathContextAddMissingDependenciesWrapper) Module() Module { return nil } 996 997func (p *pathContextAddMissingDependenciesWrapper) GetOutputFiles() OutputFilesInfo { 998 return OutputFilesInfo{} 999} 1000 1001func (p *pathContextAddMissingDependenciesWrapper) EqualModules(m1, m2 Module) bool { 1002 return m1 == m2 1003} 1004 1005func TestOutputFileForModule(t *testing.T) { 1006 testcases := []struct { 1007 name string 1008 bp string 1009 tag string 1010 expected string 1011 missingDeps []string 1012 env map[string]string 1013 config func(*config) 1014 }{ 1015 { 1016 name: "SourceFileProducer", 1017 bp: `spt_module { 1018 name: "test_module", 1019 a: "spt.txt", 1020 } 1021 `, 1022 tag: "", 1023 expected: "spt.txt", 1024 }, 1025 { 1026 name: "OutputFileProviderEmptyStringTag", 1027 bp: `oft_module { 1028 name: "test_module", 1029 a: "", 1030 b: "empty.txt", 1031 } 1032 `, 1033 tag: "", 1034 expected: "empty.txt", 1035 }, 1036 { 1037 name: "OutputFileProviderTag", 1038 bp: `oft_module { 1039 name: "test_module", 1040 a: "foo", 1041 b: "foo.txt", 1042 } 1043 `, 1044 tag: "foo", 1045 expected: "foo.txt", 1046 }, 1047 { 1048 name: "OutputFileAllowMissingDependencies", 1049 bp: `oft_module { 1050 name: "test_module", 1051 } 1052 `, 1053 tag: "missing", 1054 expected: "missing_output_file/test_module", 1055 missingDeps: []string{"test_module"}, 1056 config: func(config *config) { 1057 config.TestProductVariables.Allow_missing_dependencies = boolPtr(true) 1058 }, 1059 }, 1060 } 1061 1062 for _, tt := range testcases { 1063 t.Run(tt.name, func(t *testing.T) { 1064 result := GroupFixturePreparers( 1065 PrepareForTestWithDefaults, 1066 FixtureRegisterWithContext(func(ctx RegistrationContext) { 1067 ctx.RegisterModuleType("spt_module", sourceProducerTestModuleFactory) 1068 ctx.RegisterModuleType("oft_module", outputFilesTestModuleFactory) 1069 }), 1070 FixtureWithRootAndroidBp(tt.bp), 1071 ).RunTest(t) 1072 1073 config := TestConfig(buildDir, tt.env, tt.bp, nil) 1074 if tt.config != nil { 1075 tt.config(config.config) 1076 } 1077 ctx := &pathContextAddMissingDependenciesWrapper{ 1078 PathContext: PathContextForTesting(config), 1079 OtherModuleProviderContext: result.TestContext.OtherModuleProviderAdaptor(), 1080 } 1081 got := OutputFileForModule(ctx, result.ModuleForTests("test_module", "").Module(), tt.tag) 1082 AssertPathRelativeToTopEquals(t, "expected output path", tt.expected, got) 1083 AssertArrayString(t, "expected missing deps", tt.missingDeps, ctx.missingDeps) 1084 }) 1085 } 1086} 1087 1088func TestVintfFragmentModulesChecksPartition(t *testing.T) { 1089 bp := ` 1090 vintf_fragment { 1091 name: "vintfModA", 1092 src: "test_vintf_file", 1093 vendor: true, 1094 } 1095 deps { 1096 name: "modA", 1097 vintf_fragment_modules: [ 1098 "vintfModA", 1099 ] 1100 } 1101 ` 1102 1103 testPreparer := GroupFixturePreparers( 1104 PrepareForTestWithAndroidBuildComponents, 1105 prepareForModuleTests, 1106 ) 1107 1108 testPreparer. 1109 ExtendWithErrorHandler(FixtureExpectsOneErrorPattern( 1110 "Module .+ and Vintf_fragment .+ are installed to different partitions.")). 1111 RunTestWithBp(t, bp) 1112} 1113