1// Copyright 2019 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 "reflect" 19 "runtime" 20 "testing" 21 22 "github.com/google/blueprint/proptools" 23) 24 25type Named struct { 26 A *string `android:"arch_variant"` 27 B *string 28} 29 30type NamedAllFiltered struct { 31 A *string 32} 33 34type NamedNoneFiltered struct { 35 A *string `android:"arch_variant"` 36} 37 38func TestFilterArchStruct(t *testing.T) { 39 tests := []struct { 40 name string 41 in interface{} 42 out interface{} 43 filtered bool 44 }{ 45 // Property tests 46 { 47 name: "basic", 48 in: &struct { 49 A *string `android:"arch_variant"` 50 B *string 51 }{}, 52 out: &struct { 53 A *string 54 }{}, 55 filtered: true, 56 }, 57 { 58 name: "tags", 59 in: &struct { 60 A *string `android:"arch_variant"` 61 B *string `android:"arch_variant,path"` 62 C *string `android:"arch_variant,path,variant_prepend"` 63 D *string `android:"path,variant_prepend,arch_variant"` 64 E *string `android:"path"` 65 F *string 66 }{}, 67 out: &struct { 68 A *string 69 B *string 70 C *string 71 D *string 72 }{}, 73 filtered: true, 74 }, 75 { 76 name: "all filtered", 77 in: &struct { 78 A *string 79 }{}, 80 out: nil, 81 filtered: true, 82 }, 83 { 84 name: "none filtered", 85 in: &struct { 86 A *string `android:"arch_variant"` 87 }{}, 88 out: &struct { 89 A *string `android:"arch_variant"` 90 }{}, 91 filtered: false, 92 }, 93 94 // Sub-struct tests 95 { 96 name: "substruct", 97 in: &struct { 98 A struct { 99 A *string `android:"arch_variant"` 100 B *string 101 } `android:"arch_variant"` 102 }{}, 103 out: &struct { 104 A struct { 105 A *string 106 } 107 }{}, 108 filtered: true, 109 }, 110 { 111 name: "substruct all filtered", 112 in: &struct { 113 A struct { 114 A *string 115 } `android:"arch_variant"` 116 }{}, 117 out: nil, 118 filtered: true, 119 }, 120 { 121 name: "substruct none filtered", 122 in: &struct { 123 A struct { 124 A *string `android:"arch_variant"` 125 } `android:"arch_variant"` 126 }{}, 127 out: &struct { 128 A struct { 129 A *string `android:"arch_variant"` 130 } `android:"arch_variant"` 131 }{}, 132 filtered: false, 133 }, 134 135 // Named sub-struct tests 136 { 137 name: "named substruct", 138 in: &struct { 139 A Named `android:"arch_variant"` 140 }{}, 141 out: &struct { 142 A struct { 143 A *string 144 } 145 }{}, 146 filtered: true, 147 }, 148 { 149 name: "substruct all filtered", 150 in: &struct { 151 A NamedAllFiltered `android:"arch_variant"` 152 }{}, 153 out: nil, 154 filtered: true, 155 }, 156 { 157 name: "substruct none filtered", 158 in: &struct { 159 A NamedNoneFiltered `android:"arch_variant"` 160 }{}, 161 out: &struct { 162 A NamedNoneFiltered `android:"arch_variant"` 163 }{}, 164 filtered: false, 165 }, 166 167 // Pointer to sub-struct tests 168 { 169 name: "pointer substruct", 170 in: &struct { 171 A *struct { 172 A *string `android:"arch_variant"` 173 B *string 174 } `android:"arch_variant"` 175 }{}, 176 out: &struct { 177 A *struct { 178 A *string 179 } 180 }{}, 181 filtered: true, 182 }, 183 { 184 name: "pointer substruct all filtered", 185 in: &struct { 186 A *struct { 187 A *string 188 } `android:"arch_variant"` 189 }{}, 190 out: nil, 191 filtered: true, 192 }, 193 { 194 name: "pointer substruct none filtered", 195 in: &struct { 196 A *struct { 197 A *string `android:"arch_variant"` 198 } `android:"arch_variant"` 199 }{}, 200 out: &struct { 201 A *struct { 202 A *string `android:"arch_variant"` 203 } `android:"arch_variant"` 204 }{}, 205 filtered: false, 206 }, 207 208 // Pointer to named sub-struct tests 209 { 210 name: "pointer named substruct", 211 in: &struct { 212 A *Named `android:"arch_variant"` 213 }{}, 214 out: &struct { 215 A *struct { 216 A *string 217 } 218 }{}, 219 filtered: true, 220 }, 221 { 222 name: "pointer substruct all filtered", 223 in: &struct { 224 A *NamedAllFiltered `android:"arch_variant"` 225 }{}, 226 out: nil, 227 filtered: true, 228 }, 229 { 230 name: "pointer substruct none filtered", 231 in: &struct { 232 A *NamedNoneFiltered `android:"arch_variant"` 233 }{}, 234 out: &struct { 235 A *NamedNoneFiltered `android:"arch_variant"` 236 }{}, 237 filtered: false, 238 }, 239 } 240 241 for _, test := range tests { 242 t.Run(test.name, func(t *testing.T) { 243 out, filtered := proptools.FilterPropertyStruct(reflect.TypeOf(test.in), filterArchStruct) 244 if filtered != test.filtered { 245 t.Errorf("expected filtered %v, got %v", test.filtered, filtered) 246 } 247 expected := reflect.TypeOf(test.out) 248 if out != expected { 249 t.Errorf("expected type %v, got %v", expected, out) 250 } 251 }) 252 } 253} 254 255type archTestModule struct { 256 ModuleBase 257 props struct { 258 Deps []string 259 } 260} 261 262func (m *archTestMultiTargetsModule) GenerateAndroidBuildActions(ctx ModuleContext) { 263} 264 265func (m *archTestMultiTargetsModule) DepsMutator(ctx BottomUpMutatorContext) { 266 ctx.AddDependency(ctx.Module(), nil, m.props.Deps...) 267} 268 269func archTestMultiTargetsModuleFactory() Module { 270 m := &archTestMultiTargetsModule{} 271 m.AddProperties(&m.props) 272 InitAndroidMultiTargetsArchModule(m, HostAndDeviceSupported, MultilibCommon) 273 return m 274} 275 276type archTestMultiTargetsModule struct { 277 ModuleBase 278 props struct { 279 Deps []string 280 } 281} 282 283func (m *archTestModule) GenerateAndroidBuildActions(ctx ModuleContext) { 284} 285 286func (m *archTestModule) DepsMutator(ctx BottomUpMutatorContext) { 287 ctx.AddDependency(ctx.Module(), nil, m.props.Deps...) 288} 289 290func archTestModuleFactory() Module { 291 m := &archTestModule{} 292 m.AddProperties(&m.props) 293 InitAndroidArchModule(m, HostAndDeviceSupported, MultilibBoth) 294 return m 295} 296 297var prepareForArchTest = GroupFixturePreparers( 298 PrepareForTestWithArchMutator, 299 FixtureRegisterWithContext(func(ctx RegistrationContext) { 300 ctx.RegisterModuleType("module", archTestModuleFactory) 301 ctx.RegisterModuleType("multi_targets_module", archTestMultiTargetsModuleFactory) 302 }), 303) 304 305func TestArchMutator(t *testing.T) { 306 var buildOSVariants []string 307 var buildOS64Variants []string 308 var buildOS32Variants []string 309 var buildOSCommonVariant string 310 311 switch runtime.GOOS { 312 case "linux": 313 buildOSVariants = []string{"linux_glibc_x86_64", "linux_glibc_x86"} 314 buildOS64Variants = []string{"linux_glibc_x86_64"} 315 buildOS32Variants = []string{"linux_glibc_x86"} 316 buildOSCommonVariant = "linux_glibc_common" 317 case "darwin": 318 buildOSVariants = []string{"darwin_x86_64"} 319 buildOS64Variants = []string{"darwin_x86_64"} 320 buildOS32Variants = nil 321 buildOSCommonVariant = "darwin_common" 322 } 323 324 bp := ` 325 module { 326 name: "foo", 327 } 328 329 module { 330 name: "bar", 331 host_supported: true, 332 } 333 334 module { 335 name: "nohostcross", 336 host_supported: true, 337 host_cross_supported: false, 338 } 339 340 module { 341 name: "baz", 342 device_supported: false, 343 } 344 345 module { 346 name: "qux", 347 host_supported: true, 348 compile_multilib: "32", 349 } 350 351 module { 352 name: "first", 353 host_supported: true, 354 compile_multilib: "first", 355 } 356 357 multi_targets_module { 358 name: "multi_targets", 359 host_supported: true, 360 } 361 ` 362 363 testCases := []struct { 364 name string 365 preparer FixturePreparer 366 fooVariants []string 367 barVariants []string 368 noHostCrossVariants []string 369 bazVariants []string 370 quxVariants []string 371 firstVariants []string 372 373 multiTargetVariants []string 374 multiTargetVariantsMap map[string][]string 375 376 goOS string 377 }{ 378 { 379 name: "normal", 380 preparer: nil, 381 fooVariants: []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"}, 382 barVariants: append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"), 383 noHostCrossVariants: append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"), 384 bazVariants: nil, 385 quxVariants: append(buildOS32Variants, "android_arm_armv7-a-neon"), 386 firstVariants: append(buildOS64Variants, "android_arm64_armv8-a"), 387 multiTargetVariants: []string{buildOSCommonVariant, "android_common"}, 388 multiTargetVariantsMap: map[string][]string{ 389 buildOSCommonVariant: buildOS64Variants, 390 "android_common": {"android_arm64_armv8-a"}, 391 }}, 392 { 393 name: "host-only", 394 preparer: FixtureModifyConfig(func(config Config) { 395 config.BuildOSTarget = Target{} 396 config.BuildOSCommonTarget = Target{} 397 config.Targets[Android] = nil 398 }), 399 fooVariants: nil, 400 barVariants: buildOSVariants, 401 noHostCrossVariants: buildOSVariants, 402 bazVariants: nil, 403 quxVariants: buildOS32Variants, 404 firstVariants: buildOS64Variants, 405 multiTargetVariants: []string{buildOSCommonVariant}, 406 multiTargetVariantsMap: map[string][]string{ 407 buildOSCommonVariant: buildOS64Variants, 408 }, 409 }, 410 { 411 name: "same arch host and host cross", 412 preparer: FixtureModifyConfig(func(config Config) { 413 ModifyTestConfigForMusl(config) 414 modifyTestConfigForMuslArm64HostCross(config) 415 }), 416 fooVariants: []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"}, 417 barVariants: []string{"linux_musl_x86_64", "linux_musl_arm64", "linux_musl_x86", "android_arm64_armv8-a", "android_arm_armv7-a-neon"}, 418 noHostCrossVariants: []string{"linux_musl_x86_64", "linux_musl_x86", "android_arm64_armv8-a", "android_arm_armv7-a-neon"}, 419 bazVariants: nil, 420 quxVariants: []string{"linux_musl_x86", "android_arm_armv7-a-neon"}, 421 firstVariants: []string{"linux_musl_x86_64", "linux_musl_arm64", "android_arm64_armv8-a"}, 422 multiTargetVariants: []string{"linux_musl_common", "android_common"}, 423 multiTargetVariantsMap: map[string][]string{ 424 "linux_musl_common": {"linux_musl_x86_64"}, 425 "android_common": {"android_arm64_armv8-a"}, 426 }, 427 goOS: "linux", 428 }, 429 } 430 431 enabledVariants := func(ctx *TestContext, name string) []string { 432 var ret []string 433 variants := ctx.ModuleVariantsForTests(name) 434 for _, variant := range variants { 435 m := ctx.ModuleForTests(name, variant) 436 if m.Module().Enabled(PanickingConfigAndErrorContext(ctx)) { 437 ret = append(ret, variant) 438 } 439 } 440 return ret 441 } 442 443 moduleMultiTargets := func(ctx *TestContext, name string, variant string) []string { 444 var ret []string 445 targets := ctx.ModuleForTests(name, variant).Module().MultiTargets() 446 for _, t := range targets { 447 ret = append(ret, t.String()) 448 } 449 return ret 450 } 451 452 for _, tt := range testCases { 453 t.Run(tt.name, func(t *testing.T) { 454 if tt.goOS != "" && tt.goOS != runtime.GOOS { 455 t.Skipf("requries runtime.GOOS %s", tt.goOS) 456 } 457 458 result := GroupFixturePreparers( 459 prepareForArchTest, 460 // Test specific preparer 461 OptionalFixturePreparer(tt.preparer), 462 FixtureWithRootAndroidBp(bp), 463 ).RunTest(t) 464 ctx := result.TestContext 465 466 if g, w := enabledVariants(ctx, "foo"), tt.fooVariants; !reflect.DeepEqual(w, g) { 467 t.Errorf("want foo variants:\n%q\ngot:\n%q\n", w, g) 468 } 469 470 if g, w := enabledVariants(ctx, "bar"), tt.barVariants; !reflect.DeepEqual(w, g) { 471 t.Errorf("want bar variants:\n%q\ngot:\n%q\n", w, g) 472 } 473 474 if g, w := enabledVariants(ctx, "nohostcross"), tt.noHostCrossVariants; !reflect.DeepEqual(w, g) { 475 t.Errorf("want nohostcross variants:\n%q\ngot:\n%q\n", w, g) 476 } 477 478 if g, w := enabledVariants(ctx, "baz"), tt.bazVariants; !reflect.DeepEqual(w, g) { 479 t.Errorf("want baz variants:\n%q\ngot:\n%q\n", w, g) 480 } 481 482 if g, w := enabledVariants(ctx, "qux"), tt.quxVariants; !reflect.DeepEqual(w, g) { 483 t.Errorf("want qux variants:\n%q\ngot:\n%q\n", w, g) 484 } 485 if g, w := enabledVariants(ctx, "first"), tt.firstVariants; !reflect.DeepEqual(w, g) { 486 t.Errorf("want first variants:\n%q\ngot:\n%q\n", w, g) 487 } 488 489 if g, w := enabledVariants(ctx, "multi_targets"), tt.multiTargetVariants; !reflect.DeepEqual(w, g) { 490 t.Fatalf("want multi_target variants:\n%q\ngot:\n%q\n", w, g) 491 } 492 493 for _, variant := range tt.multiTargetVariants { 494 targets := moduleMultiTargets(ctx, "multi_targets", variant) 495 if g, w := targets, tt.multiTargetVariantsMap[variant]; !reflect.DeepEqual(w, g) { 496 t.Errorf("want ctx.MultiTarget() for %q:\n%q\ngot:\n%q\n", variant, w, g) 497 } 498 } 499 }) 500 } 501} 502 503func TestArchMutatorNativeBridge(t *testing.T) { 504 bp := ` 505 // This module is only enabled for x86. 506 module { 507 name: "foo", 508 } 509 510 // This module is enabled for x86 and arm (via native bridge). 511 module { 512 name: "bar", 513 native_bridge_supported: true, 514 } 515 516 // This module is enabled for arm (native_bridge) only. 517 module { 518 name: "baz", 519 native_bridge_supported: true, 520 enabled: false, 521 target: { 522 native_bridge: { 523 enabled: true, 524 } 525 } 526 } 527 ` 528 529 testCases := []struct { 530 name string 531 preparer FixturePreparer 532 fooVariants []string 533 barVariants []string 534 bazVariants []string 535 }{ 536 { 537 name: "normal", 538 preparer: nil, 539 fooVariants: []string{"android_x86_64_silvermont", "android_x86_silvermont"}, 540 barVariants: []string{"android_x86_64_silvermont", "android_native_bridge_arm64_armv8-a", "android_x86_silvermont", "android_native_bridge_arm_armv7-a-neon"}, 541 bazVariants: []string{"android_native_bridge_arm64_armv8-a", "android_native_bridge_arm_armv7-a-neon"}, 542 }, 543 } 544 545 enabledVariants := func(ctx *TestContext, name string) []string { 546 var ret []string 547 variants := ctx.ModuleVariantsForTests(name) 548 for _, variant := range variants { 549 m := ctx.ModuleForTests(name, variant) 550 if m.Module().Enabled(PanickingConfigAndErrorContext(ctx)) { 551 ret = append(ret, variant) 552 } 553 } 554 return ret 555 } 556 557 for _, tt := range testCases { 558 t.Run(tt.name, func(t *testing.T) { 559 result := GroupFixturePreparers( 560 prepareForArchTest, 561 // Test specific preparer 562 OptionalFixturePreparer(tt.preparer), 563 PrepareForNativeBridgeEnabled, 564 FixtureWithRootAndroidBp(bp), 565 ).RunTest(t) 566 567 ctx := result.TestContext 568 569 if g, w := enabledVariants(ctx, "foo"), tt.fooVariants; !reflect.DeepEqual(w, g) { 570 t.Errorf("want foo variants:\n%q\ngot:\n%q\n", w, g) 571 } 572 573 if g, w := enabledVariants(ctx, "bar"), tt.barVariants; !reflect.DeepEqual(w, g) { 574 t.Errorf("want bar variants:\n%q\ngot:\n%q\n", w, g) 575 } 576 577 if g, w := enabledVariants(ctx, "baz"), tt.bazVariants; !reflect.DeepEqual(w, g) { 578 t.Errorf("want qux variants:\n%q\ngot:\n%q\n", w, g) 579 } 580 }) 581 } 582} 583 584type testArchPropertiesModule struct { 585 ModuleBase 586 properties struct { 587 A []string `android:"arch_variant"` 588 } 589} 590 591func (testArchPropertiesModule) GenerateAndroidBuildActions(ctx ModuleContext) {} 592 593// Module property "a" does not have "variant_prepend" tag. 594// Expected variant property orders are based on this fact. 595func TestArchProperties(t *testing.T) { 596 bp := ` 597 module { 598 name: "foo", 599 a: ["root"], 600 arch: { 601 arm: { 602 a: ["arm"], 603 }, 604 arm64: { 605 a: ["arm64"], 606 }, 607 riscv64: { a: ["riscv64"] }, 608 x86: { a: ["x86"] }, 609 x86_64: { a: ["x86_64"] }, 610 }, 611 multilib: { 612 lib32: { a: ["lib32"] }, 613 lib64: { a: ["lib64"] }, 614 }, 615 target: { 616 bionic: { a: ["bionic"] }, 617 host: { a: ["host"] }, 618 android: { a: ["android"] }, 619 glibc: { a: ["glibc"] }, 620 musl: { a: ["musl"] }, 621 linux_bionic: { a: ["linux_bionic"] }, 622 linux: { a: ["linux"] }, 623 host_linux: { a: ["host_linux"] }, 624 linux_glibc: { a: ["linux_glibc"] }, 625 linux_musl: { a: ["linux_musl"] }, 626 windows: { a: ["windows"], enabled: true }, 627 darwin: { a: ["darwin"] }, 628 not_windows: { a: ["not_windows"] }, 629 android32: { a: ["android32"] }, 630 android64: { a: ["android64"] }, 631 android_arm: { a: ["android_arm"] }, 632 android_arm64: { a: ["android_arm64"] }, 633 linux_x86: { a: ["linux_x86"] }, 634 linux_x86_64: { a: ["linux_x86_64"] }, 635 linux_glibc_x86: { a: ["linux_glibc_x86"] }, 636 linux_glibc_x86_64: { a: ["linux_glibc_x86_64"] }, 637 linux_musl_x86: { a: ["linux_musl_x86"] }, 638 linux_musl_x86_64: { a: ["linux_musl_x86_64"] }, 639 darwin_x86_64: { a: ["darwin_x86_64"] }, 640 windows_x86: { a: ["windows_x86"] }, 641 windows_x86_64: { a: ["windows_x86_64"] }, 642 }, 643 } 644 ` 645 646 type result struct { 647 module string 648 variant string 649 property []string 650 } 651 652 testCases := []struct { 653 name string 654 goOS string 655 preparer FixturePreparer 656 results []result 657 }{ 658 { 659 name: "default", 660 results: []result{ 661 { 662 module: "foo", 663 variant: "android_arm64_armv8-a", 664 property: []string{"root", "linux", "bionic", "android", "android64", "arm64", "lib64", "android_arm64"}, 665 }, 666 { 667 module: "foo", 668 variant: "android_arm_armv7-a-neon", 669 property: []string{"root", "linux", "bionic", "android", "android64", "arm", "lib32", "android_arm"}, 670 }, 671 }, 672 }, 673 { 674 name: "linux", 675 goOS: "linux", 676 results: []result{ 677 { 678 module: "foo", 679 variant: "linux_glibc_x86_64", 680 property: []string{"root", "host", "linux", "host_linux", "glibc", "linux_glibc", "not_windows", "x86_64", "lib64", "linux_x86_64", "linux_glibc_x86_64"}, 681 }, 682 { 683 module: "foo", 684 variant: "linux_glibc_x86", 685 property: []string{"root", "host", "linux", "host_linux", "glibc", "linux_glibc", "not_windows", "x86", "lib32", "linux_x86", "linux_glibc_x86"}, 686 }, 687 }, 688 }, 689 { 690 name: "windows", 691 goOS: "linux", 692 preparer: FixtureModifyConfig(func(config Config) { 693 config.Targets[Windows] = []Target{ 694 {Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", true}, 695 {Windows, Arch{ArchType: X86}, NativeBridgeDisabled, "", "", true}, 696 } 697 }), 698 results: []result{ 699 { 700 module: "foo", 701 variant: "windows_x86_64", 702 property: []string{"root", "host", "windows", "x86_64", "lib64", "windows_x86_64"}, 703 }, 704 { 705 module: "foo", 706 variant: "windows_x86", 707 property: []string{"root", "host", "windows", "x86", "lib32", "windows_x86"}, 708 }, 709 }, 710 }, 711 { 712 name: "linux_musl", 713 goOS: "linux", 714 preparer: FixtureModifyConfig(ModifyTestConfigForMusl), 715 results: []result{ 716 { 717 module: "foo", 718 variant: "linux_musl_x86_64", 719 property: []string{"root", "host", "linux", "host_linux", "musl", "linux_musl", "not_windows", "x86_64", "lib64", "linux_x86_64", "linux_musl_x86_64"}, 720 }, 721 { 722 module: "foo", 723 variant: "linux_musl_x86", 724 property: []string{"root", "host", "linux", "host_linux", "musl", "linux_musl", "not_windows", "x86", "lib32", "linux_x86", "linux_musl_x86"}, 725 }, 726 }, 727 }, 728 { 729 name: "darwin", 730 goOS: "darwin", 731 results: []result{ 732 { 733 module: "foo", 734 variant: "darwin_x86_64", 735 property: []string{"root", "host", "darwin", "not_windows", "x86_64", "lib64", "darwin_x86_64"}, 736 }, 737 }, 738 }, 739 } 740 741 for _, tt := range testCases { 742 t.Run(tt.name, func(t *testing.T) { 743 if tt.goOS != "" && tt.goOS != runtime.GOOS { 744 t.Skipf("test requires runtime.GOOS==%s, got %s", tt.goOS, runtime.GOOS) 745 } 746 result := GroupFixturePreparers( 747 PrepareForTestWithArchMutator, 748 OptionalFixturePreparer(tt.preparer), 749 FixtureRegisterWithContext(func(ctx RegistrationContext) { 750 ctx.RegisterModuleType("module", func() Module { 751 module := &testArchPropertiesModule{} 752 module.AddProperties(&module.properties) 753 InitAndroidArchModule(module, HostAndDeviceDefault, MultilibBoth) 754 return module 755 }) 756 }), 757 ).RunTestWithBp(t, bp) 758 759 for _, want := range tt.results { 760 t.Run(want.module+"_"+want.variant, func(t *testing.T) { 761 got := result.ModuleForTests(want.module, want.variant).Module().(*testArchPropertiesModule).properties.A 762 AssertArrayString(t, "arch mutator property", want.property, got) 763 }) 764 } 765 }) 766 } 767} 768