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 "errors" 19 "fmt" 20 "reflect" 21 "strconv" 22 "strings" 23 "testing" 24 25 "github.com/google/blueprint/proptools" 26) 27 28type strsTestCase struct { 29 in []string 30 out string 31 err []error 32} 33 34var commonValidatePathTestCases = []strsTestCase{ 35 { 36 in: []string{""}, 37 out: "", 38 }, 39 { 40 in: []string{"a/b"}, 41 out: "a/b", 42 }, 43 { 44 in: []string{"a/b", "c"}, 45 out: "a/b/c", 46 }, 47 { 48 in: []string{"a/.."}, 49 out: ".", 50 }, 51 { 52 in: []string{"."}, 53 out: ".", 54 }, 55 { 56 in: []string{".."}, 57 out: "", 58 err: []error{errors.New("Path is outside directory: ..")}, 59 }, 60 { 61 in: []string{"../a"}, 62 out: "", 63 err: []error{errors.New("Path is outside directory: ../a")}, 64 }, 65 { 66 in: []string{"b/../../a"}, 67 out: "", 68 err: []error{errors.New("Path is outside directory: ../a")}, 69 }, 70 { 71 in: []string{"/a"}, 72 out: "", 73 err: []error{errors.New("Path is outside directory: /a")}, 74 }, 75 { 76 in: []string{"a", "../b"}, 77 out: "", 78 err: []error{errors.New("Path is outside directory: ../b")}, 79 }, 80 { 81 in: []string{"a", "b/../../c"}, 82 out: "", 83 err: []error{errors.New("Path is outside directory: ../c")}, 84 }, 85 { 86 in: []string{"a", "./.."}, 87 out: "", 88 err: []error{errors.New("Path is outside directory: ..")}, 89 }, 90} 91 92var validateSafePathTestCases = append(commonValidatePathTestCases, []strsTestCase{ 93 { 94 in: []string{"$host/../$a"}, 95 out: "$a", 96 }, 97}...) 98 99var validatePathTestCases = append(commonValidatePathTestCases, []strsTestCase{ 100 { 101 in: []string{"$host/../$a"}, 102 out: "", 103 err: []error{errors.New("Path contains invalid character($): $host/../$a")}, 104 }, 105 { 106 in: []string{"$host/.."}, 107 out: "", 108 err: []error{errors.New("Path contains invalid character($): $host/..")}, 109 }, 110}...) 111 112func TestValidateSafePath(t *testing.T) { 113 for _, testCase := range validateSafePathTestCases { 114 t.Run(strings.Join(testCase.in, ","), func(t *testing.T) { 115 ctx := &configErrorWrapper{} 116 out, err := validateSafePath(testCase.in...) 117 if err != nil { 118 reportPathError(ctx, err) 119 } 120 check(t, "validateSafePath", p(testCase.in), out, ctx.errors, testCase.out, testCase.err) 121 }) 122 } 123} 124 125func TestValidatePath(t *testing.T) { 126 for _, testCase := range validatePathTestCases { 127 t.Run(strings.Join(testCase.in, ","), func(t *testing.T) { 128 ctx := &configErrorWrapper{} 129 out, err := validatePath(testCase.in...) 130 if err != nil { 131 reportPathError(ctx, err) 132 } 133 check(t, "validatePath", p(testCase.in), out, ctx.errors, testCase.out, testCase.err) 134 }) 135 } 136} 137 138func TestOptionalPath(t *testing.T) { 139 var path OptionalPath 140 checkInvalidOptionalPath(t, path) 141 142 path = OptionalPathForPath(nil) 143 checkInvalidOptionalPath(t, path) 144} 145 146func checkInvalidOptionalPath(t *testing.T, path OptionalPath) { 147 t.Helper() 148 if path.Valid() { 149 t.Errorf("Uninitialized OptionalPath should not be valid") 150 } 151 if path.String() != "" { 152 t.Errorf("Uninitialized OptionalPath String() should return \"\", not %q", path.String()) 153 } 154 defer func() { 155 if r := recover(); r == nil { 156 t.Errorf("Expected a panic when calling Path() on an uninitialized OptionalPath") 157 } 158 }() 159 path.Path() 160} 161 162func check(t *testing.T, testType, testString string, 163 got interface{}, err []error, 164 expected interface{}, expectedErr []error) { 165 t.Helper() 166 167 printedTestCase := false 168 e := func(s string, expected, got interface{}) { 169 t.Helper() 170 if !printedTestCase { 171 t.Errorf("test case %s: %s", testType, testString) 172 printedTestCase = true 173 } 174 t.Errorf("incorrect %s", s) 175 t.Errorf(" expected: %s", p(expected)) 176 t.Errorf(" got: %s", p(got)) 177 } 178 179 if !reflect.DeepEqual(expectedErr, err) { 180 e("errors:", expectedErr, err) 181 } 182 183 if !reflect.DeepEqual(expected, got) { 184 e("output:", expected, got) 185 } 186} 187 188func p(in interface{}) string { 189 if v, ok := in.([]interface{}); ok { 190 s := make([]string, len(v)) 191 for i := range v { 192 s[i] = fmt.Sprintf("%#v", v[i]) 193 } 194 return "[" + strings.Join(s, ", ") + "]" 195 } else { 196 return fmt.Sprintf("%#v", in) 197 } 198} 199 200type moduleInstallPathContextImpl struct { 201 baseModuleContext 202 203 inData bool 204 inTestcases bool 205 inSanitizerDir bool 206 inRamdisk bool 207 inRecovery bool 208 inRoot bool 209 forceOS *OsType 210} 211 212func (m moduleInstallPathContextImpl) Config() Config { 213 return m.baseModuleContext.config 214} 215 216func (moduleInstallPathContextImpl) AddNinjaFileDeps(deps ...string) {} 217 218func (m moduleInstallPathContextImpl) InstallInData() bool { 219 return m.inData 220} 221 222func (m moduleInstallPathContextImpl) InstallInTestcases() bool { 223 return m.inTestcases 224} 225 226func (m moduleInstallPathContextImpl) InstallInSanitizerDir() bool { 227 return m.inSanitizerDir 228} 229 230func (m moduleInstallPathContextImpl) InstallInRamdisk() bool { 231 return m.inRamdisk 232} 233 234func (m moduleInstallPathContextImpl) InstallInRecovery() bool { 235 return m.inRecovery 236} 237 238func (m moduleInstallPathContextImpl) InstallInRoot() bool { 239 return m.inRoot 240} 241 242func (m moduleInstallPathContextImpl) InstallBypassMake() bool { 243 return false 244} 245 246func (m moduleInstallPathContextImpl) InstallForceOS() *OsType { 247 return m.forceOS 248} 249 250func pathTestConfig(buildDir string) Config { 251 return TestConfig(buildDir, nil, "", nil) 252} 253 254func TestPathForModuleInstall(t *testing.T) { 255 testConfig := pathTestConfig("") 256 257 hostTarget := Target{Os: Linux} 258 deviceTarget := Target{Os: Android} 259 260 testCases := []struct { 261 name string 262 ctx *moduleInstallPathContextImpl 263 in []string 264 out string 265 }{ 266 { 267 name: "host binary", 268 ctx: &moduleInstallPathContextImpl{ 269 baseModuleContext: baseModuleContext{ 270 os: hostTarget.Os, 271 target: hostTarget, 272 }, 273 }, 274 in: []string{"bin", "my_test"}, 275 out: "host/linux-x86/bin/my_test", 276 }, 277 278 { 279 name: "system binary", 280 ctx: &moduleInstallPathContextImpl{ 281 baseModuleContext: baseModuleContext{ 282 os: deviceTarget.Os, 283 target: deviceTarget, 284 }, 285 }, 286 in: []string{"bin", "my_test"}, 287 out: "target/product/test_device/system/bin/my_test", 288 }, 289 { 290 name: "vendor binary", 291 ctx: &moduleInstallPathContextImpl{ 292 baseModuleContext: baseModuleContext{ 293 os: deviceTarget.Os, 294 target: deviceTarget, 295 earlyModuleContext: earlyModuleContext{ 296 kind: socSpecificModule, 297 }, 298 }, 299 }, 300 in: []string{"bin", "my_test"}, 301 out: "target/product/test_device/vendor/bin/my_test", 302 }, 303 { 304 name: "odm binary", 305 ctx: &moduleInstallPathContextImpl{ 306 baseModuleContext: baseModuleContext{ 307 os: deviceTarget.Os, 308 target: deviceTarget, 309 earlyModuleContext: earlyModuleContext{ 310 kind: deviceSpecificModule, 311 }, 312 }, 313 }, 314 in: []string{"bin", "my_test"}, 315 out: "target/product/test_device/odm/bin/my_test", 316 }, 317 { 318 name: "product binary", 319 ctx: &moduleInstallPathContextImpl{ 320 baseModuleContext: baseModuleContext{ 321 os: deviceTarget.Os, 322 target: deviceTarget, 323 earlyModuleContext: earlyModuleContext{ 324 kind: productSpecificModule, 325 }, 326 }, 327 }, 328 in: []string{"bin", "my_test"}, 329 out: "target/product/test_device/product/bin/my_test", 330 }, 331 { 332 name: "system_ext binary", 333 ctx: &moduleInstallPathContextImpl{ 334 baseModuleContext: baseModuleContext{ 335 os: deviceTarget.Os, 336 target: deviceTarget, 337 earlyModuleContext: earlyModuleContext{ 338 kind: systemExtSpecificModule, 339 }, 340 }, 341 }, 342 in: []string{"bin", "my_test"}, 343 out: "target/product/test_device/system_ext/bin/my_test", 344 }, 345 { 346 name: "root binary", 347 ctx: &moduleInstallPathContextImpl{ 348 baseModuleContext: baseModuleContext{ 349 os: deviceTarget.Os, 350 target: deviceTarget, 351 }, 352 inRoot: true, 353 }, 354 in: []string{"my_test"}, 355 out: "target/product/test_device/root/my_test", 356 }, 357 { 358 name: "recovery binary", 359 ctx: &moduleInstallPathContextImpl{ 360 baseModuleContext: baseModuleContext{ 361 os: deviceTarget.Os, 362 target: deviceTarget, 363 }, 364 inRecovery: true, 365 }, 366 in: []string{"bin/my_test"}, 367 out: "target/product/test_device/recovery/root/system/bin/my_test", 368 }, 369 { 370 name: "recovery root binary", 371 ctx: &moduleInstallPathContextImpl{ 372 baseModuleContext: baseModuleContext{ 373 os: deviceTarget.Os, 374 target: deviceTarget, 375 }, 376 inRecovery: true, 377 inRoot: true, 378 }, 379 in: []string{"my_test"}, 380 out: "target/product/test_device/recovery/root/my_test", 381 }, 382 383 { 384 name: "system native test binary", 385 ctx: &moduleInstallPathContextImpl{ 386 baseModuleContext: baseModuleContext{ 387 os: deviceTarget.Os, 388 target: deviceTarget, 389 }, 390 inData: true, 391 }, 392 in: []string{"nativetest", "my_test"}, 393 out: "target/product/test_device/data/nativetest/my_test", 394 }, 395 { 396 name: "vendor native test binary", 397 ctx: &moduleInstallPathContextImpl{ 398 baseModuleContext: baseModuleContext{ 399 os: deviceTarget.Os, 400 target: deviceTarget, 401 earlyModuleContext: earlyModuleContext{ 402 kind: socSpecificModule, 403 }, 404 }, 405 inData: true, 406 }, 407 in: []string{"nativetest", "my_test"}, 408 out: "target/product/test_device/data/nativetest/my_test", 409 }, 410 { 411 name: "odm native test binary", 412 ctx: &moduleInstallPathContextImpl{ 413 baseModuleContext: baseModuleContext{ 414 os: deviceTarget.Os, 415 target: deviceTarget, 416 earlyModuleContext: earlyModuleContext{ 417 kind: deviceSpecificModule, 418 }, 419 }, 420 inData: true, 421 }, 422 in: []string{"nativetest", "my_test"}, 423 out: "target/product/test_device/data/nativetest/my_test", 424 }, 425 { 426 name: "product native test binary", 427 ctx: &moduleInstallPathContextImpl{ 428 baseModuleContext: baseModuleContext{ 429 os: deviceTarget.Os, 430 target: deviceTarget, 431 earlyModuleContext: earlyModuleContext{ 432 kind: productSpecificModule, 433 }, 434 }, 435 inData: true, 436 }, 437 in: []string{"nativetest", "my_test"}, 438 out: "target/product/test_device/data/nativetest/my_test", 439 }, 440 441 { 442 name: "system_ext native test binary", 443 ctx: &moduleInstallPathContextImpl{ 444 baseModuleContext: baseModuleContext{ 445 os: deviceTarget.Os, 446 target: deviceTarget, 447 earlyModuleContext: earlyModuleContext{ 448 kind: systemExtSpecificModule, 449 }, 450 }, 451 inData: true, 452 }, 453 in: []string{"nativetest", "my_test"}, 454 out: "target/product/test_device/data/nativetest/my_test", 455 }, 456 457 { 458 name: "sanitized system binary", 459 ctx: &moduleInstallPathContextImpl{ 460 baseModuleContext: baseModuleContext{ 461 os: deviceTarget.Os, 462 target: deviceTarget, 463 }, 464 inSanitizerDir: true, 465 }, 466 in: []string{"bin", "my_test"}, 467 out: "target/product/test_device/data/asan/system/bin/my_test", 468 }, 469 { 470 name: "sanitized vendor binary", 471 ctx: &moduleInstallPathContextImpl{ 472 baseModuleContext: baseModuleContext{ 473 os: deviceTarget.Os, 474 target: deviceTarget, 475 earlyModuleContext: earlyModuleContext{ 476 kind: socSpecificModule, 477 }, 478 }, 479 inSanitizerDir: true, 480 }, 481 in: []string{"bin", "my_test"}, 482 out: "target/product/test_device/data/asan/vendor/bin/my_test", 483 }, 484 { 485 name: "sanitized odm binary", 486 ctx: &moduleInstallPathContextImpl{ 487 baseModuleContext: baseModuleContext{ 488 os: deviceTarget.Os, 489 target: deviceTarget, 490 earlyModuleContext: earlyModuleContext{ 491 kind: deviceSpecificModule, 492 }, 493 }, 494 inSanitizerDir: true, 495 }, 496 in: []string{"bin", "my_test"}, 497 out: "target/product/test_device/data/asan/odm/bin/my_test", 498 }, 499 { 500 name: "sanitized product binary", 501 ctx: &moduleInstallPathContextImpl{ 502 baseModuleContext: baseModuleContext{ 503 os: deviceTarget.Os, 504 target: deviceTarget, 505 earlyModuleContext: earlyModuleContext{ 506 kind: productSpecificModule, 507 }, 508 }, 509 inSanitizerDir: true, 510 }, 511 in: []string{"bin", "my_test"}, 512 out: "target/product/test_device/data/asan/product/bin/my_test", 513 }, 514 515 { 516 name: "sanitized system_ext binary", 517 ctx: &moduleInstallPathContextImpl{ 518 baseModuleContext: baseModuleContext{ 519 os: deviceTarget.Os, 520 target: deviceTarget, 521 earlyModuleContext: earlyModuleContext{ 522 kind: systemExtSpecificModule, 523 }, 524 }, 525 inSanitizerDir: true, 526 }, 527 in: []string{"bin", "my_test"}, 528 out: "target/product/test_device/data/asan/system_ext/bin/my_test", 529 }, 530 531 { 532 name: "sanitized system native test binary", 533 ctx: &moduleInstallPathContextImpl{ 534 baseModuleContext: baseModuleContext{ 535 os: deviceTarget.Os, 536 target: deviceTarget, 537 }, 538 inData: true, 539 inSanitizerDir: true, 540 }, 541 in: []string{"nativetest", "my_test"}, 542 out: "target/product/test_device/data/asan/data/nativetest/my_test", 543 }, 544 { 545 name: "sanitized vendor native test binary", 546 ctx: &moduleInstallPathContextImpl{ 547 baseModuleContext: baseModuleContext{ 548 os: deviceTarget.Os, 549 target: deviceTarget, 550 earlyModuleContext: earlyModuleContext{ 551 kind: socSpecificModule, 552 }, 553 }, 554 inData: true, 555 inSanitizerDir: true, 556 }, 557 in: []string{"nativetest", "my_test"}, 558 out: "target/product/test_device/data/asan/data/nativetest/my_test", 559 }, 560 { 561 name: "sanitized odm native test binary", 562 ctx: &moduleInstallPathContextImpl{ 563 baseModuleContext: baseModuleContext{ 564 os: deviceTarget.Os, 565 target: deviceTarget, 566 earlyModuleContext: earlyModuleContext{ 567 kind: deviceSpecificModule, 568 }, 569 }, 570 inData: true, 571 inSanitizerDir: true, 572 }, 573 in: []string{"nativetest", "my_test"}, 574 out: "target/product/test_device/data/asan/data/nativetest/my_test", 575 }, 576 { 577 name: "sanitized product native test binary", 578 ctx: &moduleInstallPathContextImpl{ 579 baseModuleContext: baseModuleContext{ 580 os: deviceTarget.Os, 581 target: deviceTarget, 582 earlyModuleContext: earlyModuleContext{ 583 kind: productSpecificModule, 584 }, 585 }, 586 inData: true, 587 inSanitizerDir: true, 588 }, 589 in: []string{"nativetest", "my_test"}, 590 out: "target/product/test_device/data/asan/data/nativetest/my_test", 591 }, 592 { 593 name: "sanitized system_ext native test binary", 594 ctx: &moduleInstallPathContextImpl{ 595 baseModuleContext: baseModuleContext{ 596 os: deviceTarget.Os, 597 target: deviceTarget, 598 earlyModuleContext: earlyModuleContext{ 599 kind: systemExtSpecificModule, 600 }, 601 }, 602 inData: true, 603 inSanitizerDir: true, 604 }, 605 in: []string{"nativetest", "my_test"}, 606 out: "target/product/test_device/data/asan/data/nativetest/my_test", 607 }, { 608 name: "device testcases", 609 ctx: &moduleInstallPathContextImpl{ 610 baseModuleContext: baseModuleContext{ 611 os: deviceTarget.Os, 612 target: deviceTarget, 613 }, 614 inTestcases: true, 615 }, 616 in: []string{"my_test", "my_test_bin"}, 617 out: "target/product/test_device/testcases/my_test/my_test_bin", 618 }, { 619 name: "host testcases", 620 ctx: &moduleInstallPathContextImpl{ 621 baseModuleContext: baseModuleContext{ 622 os: hostTarget.Os, 623 target: hostTarget, 624 }, 625 inTestcases: true, 626 }, 627 in: []string{"my_test", "my_test_bin"}, 628 out: "host/linux-x86/testcases/my_test/my_test_bin", 629 }, { 630 name: "forced host testcases", 631 ctx: &moduleInstallPathContextImpl{ 632 baseModuleContext: baseModuleContext{ 633 os: deviceTarget.Os, 634 target: deviceTarget, 635 }, 636 inTestcases: true, 637 forceOS: &Linux, 638 }, 639 in: []string{"my_test", "my_test_bin"}, 640 out: "host/linux-x86/testcases/my_test/my_test_bin", 641 }, 642 } 643 644 for _, tc := range testCases { 645 t.Run(tc.name, func(t *testing.T) { 646 tc.ctx.baseModuleContext.config = testConfig 647 output := PathForModuleInstall(tc.ctx, tc.in...) 648 if output.basePath.path != tc.out { 649 t.Errorf("unexpected path:\n got: %q\nwant: %q\n", 650 output.basePath.path, 651 tc.out) 652 } 653 }) 654 } 655} 656 657func TestDirectorySortedPaths(t *testing.T) { 658 config := TestConfig("out", nil, "", map[string][]byte{ 659 "Android.bp": nil, 660 "a.txt": nil, 661 "a/txt": nil, 662 "a/b/c": nil, 663 "a/b/d": nil, 664 "b": nil, 665 "b/b.txt": nil, 666 "a/a.txt": nil, 667 }) 668 669 ctx := PathContextForTesting(config) 670 671 makePaths := func() Paths { 672 return Paths{ 673 PathForSource(ctx, "a.txt"), 674 PathForSource(ctx, "a/txt"), 675 PathForSource(ctx, "a/b/c"), 676 PathForSource(ctx, "a/b/d"), 677 PathForSource(ctx, "b"), 678 PathForSource(ctx, "b/b.txt"), 679 PathForSource(ctx, "a/a.txt"), 680 } 681 } 682 683 expected := []string{ 684 "a.txt", 685 "a/a.txt", 686 "a/b/c", 687 "a/b/d", 688 "a/txt", 689 "b", 690 "b/b.txt", 691 } 692 693 paths := makePaths() 694 reversePaths := ReversePaths(paths) 695 696 sortedPaths := PathsToDirectorySortedPaths(paths) 697 reverseSortedPaths := PathsToDirectorySortedPaths(reversePaths) 698 699 if !reflect.DeepEqual(Paths(sortedPaths).Strings(), expected) { 700 t.Fatalf("sorted paths:\n %#v\n != \n %#v", paths.Strings(), expected) 701 } 702 703 if !reflect.DeepEqual(Paths(reverseSortedPaths).Strings(), expected) { 704 t.Fatalf("sorted reversed paths:\n %#v\n !=\n %#v", reversePaths.Strings(), expected) 705 } 706 707 expectedA := []string{ 708 "a/a.txt", 709 "a/b/c", 710 "a/b/d", 711 "a/txt", 712 } 713 714 inA := sortedPaths.PathsInDirectory("a") 715 if !reflect.DeepEqual(inA.Strings(), expectedA) { 716 t.Errorf("FilesInDirectory(a):\n %#v\n != \n %#v", inA.Strings(), expectedA) 717 } 718 719 expectedA_B := []string{ 720 "a/b/c", 721 "a/b/d", 722 } 723 724 inA_B := sortedPaths.PathsInDirectory("a/b") 725 if !reflect.DeepEqual(inA_B.Strings(), expectedA_B) { 726 t.Errorf("FilesInDirectory(a/b):\n %#v\n != \n %#v", inA_B.Strings(), expectedA_B) 727 } 728 729 expectedB := []string{ 730 "b/b.txt", 731 } 732 733 inB := sortedPaths.PathsInDirectory("b") 734 if !reflect.DeepEqual(inB.Strings(), expectedB) { 735 t.Errorf("FilesInDirectory(b):\n %#v\n != \n %#v", inA.Strings(), expectedA) 736 } 737} 738 739func TestMaybeRel(t *testing.T) { 740 testCases := []struct { 741 name string 742 base string 743 target string 744 out string 745 isRel bool 746 }{ 747 { 748 name: "normal", 749 base: "a/b/c", 750 target: "a/b/c/d", 751 out: "d", 752 isRel: true, 753 }, 754 { 755 name: "parent", 756 base: "a/b/c/d", 757 target: "a/b/c", 758 isRel: false, 759 }, 760 { 761 name: "not relative", 762 base: "a/b", 763 target: "c/d", 764 isRel: false, 765 }, 766 { 767 name: "abs1", 768 base: "/a", 769 target: "a", 770 isRel: false, 771 }, 772 { 773 name: "abs2", 774 base: "a", 775 target: "/a", 776 isRel: false, 777 }, 778 } 779 780 for _, testCase := range testCases { 781 t.Run(testCase.name, func(t *testing.T) { 782 ctx := &configErrorWrapper{} 783 out, isRel := MaybeRel(ctx, testCase.base, testCase.target) 784 if len(ctx.errors) > 0 { 785 t.Errorf("MaybeRel(..., %s, %s) reported unexpected errors %v", 786 testCase.base, testCase.target, ctx.errors) 787 } 788 if isRel != testCase.isRel || out != testCase.out { 789 t.Errorf("MaybeRel(..., %s, %s) want %v, %v got %v, %v", 790 testCase.base, testCase.target, testCase.out, testCase.isRel, out, isRel) 791 } 792 }) 793 } 794} 795 796func TestPathForSource(t *testing.T) { 797 testCases := []struct { 798 name string 799 buildDir string 800 src string 801 err string 802 }{ 803 { 804 name: "normal", 805 buildDir: "out", 806 src: "a/b/c", 807 }, 808 { 809 name: "abs", 810 buildDir: "out", 811 src: "/a/b/c", 812 err: "is outside directory", 813 }, 814 { 815 name: "in out dir", 816 buildDir: "out", 817 src: "out/a/b/c", 818 err: "is in output", 819 }, 820 } 821 822 funcs := []struct { 823 name string 824 f func(ctx PathContext, pathComponents ...string) (SourcePath, error) 825 }{ 826 {"pathForSource", pathForSource}, 827 {"safePathForSource", safePathForSource}, 828 } 829 830 for _, f := range funcs { 831 t.Run(f.name, func(t *testing.T) { 832 for _, test := range testCases { 833 t.Run(test.name, func(t *testing.T) { 834 testConfig := pathTestConfig(test.buildDir) 835 ctx := &configErrorWrapper{config: testConfig} 836 _, err := f.f(ctx, test.src) 837 if len(ctx.errors) > 0 { 838 t.Fatalf("unexpected errors %v", ctx.errors) 839 } 840 if err != nil { 841 if test.err == "" { 842 t.Fatalf("unexpected error %q", err.Error()) 843 } else if !strings.Contains(err.Error(), test.err) { 844 t.Fatalf("incorrect error, want substring %q got %q", test.err, err.Error()) 845 } 846 } else { 847 if test.err != "" { 848 t.Fatalf("missing error %q", test.err) 849 } 850 } 851 }) 852 } 853 }) 854 } 855} 856 857type pathForModuleSrcTestModule struct { 858 ModuleBase 859 props struct { 860 Srcs []string `android:"path"` 861 Exclude_srcs []string `android:"path"` 862 863 Src *string `android:"path"` 864 865 Module_handles_missing_deps bool 866 } 867 868 src string 869 rel string 870 871 srcs []string 872 rels []string 873 874 missingDeps []string 875} 876 877func pathForModuleSrcTestModuleFactory() Module { 878 module := &pathForModuleSrcTestModule{} 879 module.AddProperties(&module.props) 880 InitAndroidModule(module) 881 return module 882} 883 884func (p *pathForModuleSrcTestModule) GenerateAndroidBuildActions(ctx ModuleContext) { 885 var srcs Paths 886 if p.props.Module_handles_missing_deps { 887 srcs, p.missingDeps = PathsAndMissingDepsForModuleSrcExcludes(ctx, p.props.Srcs, p.props.Exclude_srcs) 888 } else { 889 srcs = PathsForModuleSrcExcludes(ctx, p.props.Srcs, p.props.Exclude_srcs) 890 } 891 p.srcs = srcs.Strings() 892 893 for _, src := range srcs { 894 p.rels = append(p.rels, src.Rel()) 895 } 896 897 if p.props.Src != nil { 898 src := PathForModuleSrc(ctx, *p.props.Src) 899 if src != nil { 900 p.src = src.String() 901 p.rel = src.Rel() 902 } 903 } 904 905 if !p.props.Module_handles_missing_deps { 906 p.missingDeps = ctx.GetMissingDependencies() 907 } 908 909 ctx.Build(pctx, BuildParams{ 910 Rule: Touch, 911 Output: PathForModuleOut(ctx, "output"), 912 }) 913} 914 915type pathForModuleSrcOutputFileProviderModule struct { 916 ModuleBase 917 props struct { 918 Outs []string 919 Tagged []string 920 } 921 922 outs Paths 923 tagged Paths 924} 925 926func pathForModuleSrcOutputFileProviderModuleFactory() Module { 927 module := &pathForModuleSrcOutputFileProviderModule{} 928 module.AddProperties(&module.props) 929 InitAndroidModule(module) 930 return module 931} 932 933func (p *pathForModuleSrcOutputFileProviderModule) GenerateAndroidBuildActions(ctx ModuleContext) { 934 for _, out := range p.props.Outs { 935 p.outs = append(p.outs, PathForModuleOut(ctx, out)) 936 } 937 938 for _, tagged := range p.props.Tagged { 939 p.tagged = append(p.tagged, PathForModuleOut(ctx, tagged)) 940 } 941} 942 943func (p *pathForModuleSrcOutputFileProviderModule) OutputFiles(tag string) (Paths, error) { 944 switch tag { 945 case "": 946 return p.outs, nil 947 case ".tagged": 948 return p.tagged, nil 949 default: 950 return nil, fmt.Errorf("unsupported tag %q", tag) 951 } 952} 953 954type pathForModuleSrcTestCase struct { 955 name string 956 bp string 957 srcs []string 958 rels []string 959 src string 960 rel string 961} 962 963func testPathForModuleSrc(t *testing.T, buildDir string, tests []pathForModuleSrcTestCase) { 964 for _, test := range tests { 965 t.Run(test.name, func(t *testing.T) { 966 ctx := NewTestContext() 967 968 ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory) 969 ctx.RegisterModuleType("output_file_provider", pathForModuleSrcOutputFileProviderModuleFactory) 970 ctx.RegisterModuleType("filegroup", FileGroupFactory) 971 972 fgBp := ` 973 filegroup { 974 name: "a", 975 srcs: ["src/a"], 976 } 977 ` 978 979 ofpBp := ` 980 output_file_provider { 981 name: "b", 982 outs: ["gen/b"], 983 tagged: ["gen/c"], 984 } 985 ` 986 987 mockFS := map[string][]byte{ 988 "fg/Android.bp": []byte(fgBp), 989 "foo/Android.bp": []byte(test.bp), 990 "ofp/Android.bp": []byte(ofpBp), 991 "fg/src/a": nil, 992 "foo/src/b": nil, 993 "foo/src/c": nil, 994 "foo/src/d": nil, 995 "foo/src/e/e": nil, 996 "foo/src_special/$": nil, 997 } 998 999 config := TestConfig(buildDir, nil, "", mockFS) 1000 1001 ctx.Register(config) 1002 _, errs := ctx.ParseFileList(".", []string{"fg/Android.bp", "foo/Android.bp", "ofp/Android.bp"}) 1003 FailIfErrored(t, errs) 1004 _, errs = ctx.PrepareBuildActions(config) 1005 FailIfErrored(t, errs) 1006 1007 m := ctx.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule) 1008 1009 if g, w := m.srcs, test.srcs; !reflect.DeepEqual(g, w) { 1010 t.Errorf("want srcs %q, got %q", w, g) 1011 } 1012 1013 if g, w := m.rels, test.rels; !reflect.DeepEqual(g, w) { 1014 t.Errorf("want rels %q, got %q", w, g) 1015 } 1016 1017 if g, w := m.src, test.src; g != w { 1018 t.Errorf("want src %q, got %q", w, g) 1019 } 1020 1021 if g, w := m.rel, test.rel; g != w { 1022 t.Errorf("want rel %q, got %q", w, g) 1023 } 1024 }) 1025 } 1026} 1027 1028func TestPathsForModuleSrc(t *testing.T) { 1029 tests := []pathForModuleSrcTestCase{ 1030 { 1031 name: "path", 1032 bp: ` 1033 test { 1034 name: "foo", 1035 srcs: ["src/b"], 1036 }`, 1037 srcs: []string{"foo/src/b"}, 1038 rels: []string{"src/b"}, 1039 }, 1040 { 1041 name: "glob", 1042 bp: ` 1043 test { 1044 name: "foo", 1045 srcs: [ 1046 "src/*", 1047 "src/e/*", 1048 ], 1049 }`, 1050 srcs: []string{"foo/src/b", "foo/src/c", "foo/src/d", "foo/src/e/e"}, 1051 rels: []string{"src/b", "src/c", "src/d", "src/e/e"}, 1052 }, 1053 { 1054 name: "recursive glob", 1055 bp: ` 1056 test { 1057 name: "foo", 1058 srcs: ["src/**/*"], 1059 }`, 1060 srcs: []string{"foo/src/b", "foo/src/c", "foo/src/d", "foo/src/e/e"}, 1061 rels: []string{"src/b", "src/c", "src/d", "src/e/e"}, 1062 }, 1063 { 1064 name: "filegroup", 1065 bp: ` 1066 test { 1067 name: "foo", 1068 srcs: [":a"], 1069 }`, 1070 srcs: []string{"fg/src/a"}, 1071 rels: []string{"src/a"}, 1072 }, 1073 { 1074 name: "output file provider", 1075 bp: ` 1076 test { 1077 name: "foo", 1078 srcs: [":b"], 1079 }`, 1080 srcs: []string{buildDir + "/.intermediates/ofp/b/gen/b"}, 1081 rels: []string{"gen/b"}, 1082 }, 1083 { 1084 name: "output file provider tagged", 1085 bp: ` 1086 test { 1087 name: "foo", 1088 srcs: [":b{.tagged}"], 1089 }`, 1090 srcs: []string{buildDir + "/.intermediates/ofp/b/gen/c"}, 1091 rels: []string{"gen/c"}, 1092 }, 1093 { 1094 name: "output file provider with exclude", 1095 bp: ` 1096 test { 1097 name: "foo", 1098 srcs: [":b", ":c"], 1099 exclude_srcs: [":c"] 1100 } 1101 output_file_provider { 1102 name: "c", 1103 outs: ["gen/c"], 1104 }`, 1105 srcs: []string{buildDir + "/.intermediates/ofp/b/gen/b"}, 1106 rels: []string{"gen/b"}, 1107 }, 1108 { 1109 name: "special characters glob", 1110 bp: ` 1111 test { 1112 name: "foo", 1113 srcs: ["src_special/*"], 1114 }`, 1115 srcs: []string{"foo/src_special/$"}, 1116 rels: []string{"src_special/$"}, 1117 }, 1118 } 1119 1120 testPathForModuleSrc(t, buildDir, tests) 1121} 1122 1123func TestPathForModuleSrc(t *testing.T) { 1124 tests := []pathForModuleSrcTestCase{ 1125 { 1126 name: "path", 1127 bp: ` 1128 test { 1129 name: "foo", 1130 src: "src/b", 1131 }`, 1132 src: "foo/src/b", 1133 rel: "src/b", 1134 }, 1135 { 1136 name: "glob", 1137 bp: ` 1138 test { 1139 name: "foo", 1140 src: "src/e/*", 1141 }`, 1142 src: "foo/src/e/e", 1143 rel: "src/e/e", 1144 }, 1145 { 1146 name: "filegroup", 1147 bp: ` 1148 test { 1149 name: "foo", 1150 src: ":a", 1151 }`, 1152 src: "fg/src/a", 1153 rel: "src/a", 1154 }, 1155 { 1156 name: "output file provider", 1157 bp: ` 1158 test { 1159 name: "foo", 1160 src: ":b", 1161 }`, 1162 src: buildDir + "/.intermediates/ofp/b/gen/b", 1163 rel: "gen/b", 1164 }, 1165 { 1166 name: "output file provider tagged", 1167 bp: ` 1168 test { 1169 name: "foo", 1170 src: ":b{.tagged}", 1171 }`, 1172 src: buildDir + "/.intermediates/ofp/b/gen/c", 1173 rel: "gen/c", 1174 }, 1175 { 1176 name: "special characters glob", 1177 bp: ` 1178 test { 1179 name: "foo", 1180 src: "src_special/*", 1181 }`, 1182 src: "foo/src_special/$", 1183 rel: "src_special/$", 1184 }, 1185 } 1186 1187 testPathForModuleSrc(t, buildDir, tests) 1188} 1189 1190func TestPathsForModuleSrc_AllowMissingDependencies(t *testing.T) { 1191 bp := ` 1192 test { 1193 name: "foo", 1194 srcs: [":a"], 1195 exclude_srcs: [":b"], 1196 src: ":c", 1197 } 1198 1199 test { 1200 name: "bar", 1201 srcs: [":d"], 1202 exclude_srcs: [":e"], 1203 module_handles_missing_deps: true, 1204 } 1205 ` 1206 1207 config := TestConfig(buildDir, nil, bp, nil) 1208 config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true) 1209 1210 ctx := NewTestContext() 1211 ctx.SetAllowMissingDependencies(true) 1212 1213 ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory) 1214 1215 ctx.Register(config) 1216 1217 _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) 1218 FailIfErrored(t, errs) 1219 _, errs = ctx.PrepareBuildActions(config) 1220 FailIfErrored(t, errs) 1221 1222 foo := ctx.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule) 1223 1224 if g, w := foo.missingDeps, []string{"a", "b", "c"}; !reflect.DeepEqual(g, w) { 1225 t.Errorf("want foo missing deps %q, got %q", w, g) 1226 } 1227 1228 if g, w := foo.srcs, []string{}; !reflect.DeepEqual(g, w) { 1229 t.Errorf("want foo srcs %q, got %q", w, g) 1230 } 1231 1232 if g, w := foo.src, ""; g != w { 1233 t.Errorf("want foo src %q, got %q", w, g) 1234 } 1235 1236 bar := ctx.ModuleForTests("bar", "").Module().(*pathForModuleSrcTestModule) 1237 1238 if g, w := bar.missingDeps, []string{"d", "e"}; !reflect.DeepEqual(g, w) { 1239 t.Errorf("want bar missing deps %q, got %q", w, g) 1240 } 1241 1242 if g, w := bar.srcs, []string{}; !reflect.DeepEqual(g, w) { 1243 t.Errorf("want bar srcs %q, got %q", w, g) 1244 } 1245} 1246 1247func ExampleOutputPath_ReplaceExtension() { 1248 ctx := &configErrorWrapper{ 1249 config: TestConfig("out", nil, "", nil), 1250 } 1251 p := PathForOutput(ctx, "system/framework").Join(ctx, "boot.art") 1252 p2 := p.ReplaceExtension(ctx, "oat") 1253 fmt.Println(p, p2) 1254 fmt.Println(p.Rel(), p2.Rel()) 1255 1256 // Output: 1257 // out/system/framework/boot.art out/system/framework/boot.oat 1258 // boot.art boot.oat 1259} 1260 1261func ExampleOutputPath_FileInSameDir() { 1262 ctx := &configErrorWrapper{ 1263 config: TestConfig("out", nil, "", nil), 1264 } 1265 p := PathForOutput(ctx, "system/framework").Join(ctx, "boot.art") 1266 p2 := p.InSameDir(ctx, "oat", "arm", "boot.vdex") 1267 fmt.Println(p, p2) 1268 fmt.Println(p.Rel(), p2.Rel()) 1269 1270 // Output: 1271 // out/system/framework/boot.art out/system/framework/oat/arm/boot.vdex 1272 // boot.art oat/arm/boot.vdex 1273} 1274 1275func BenchmarkFirstUniquePaths(b *testing.B) { 1276 implementations := []struct { 1277 name string 1278 f func(Paths) Paths 1279 }{ 1280 { 1281 name: "list", 1282 f: firstUniquePathsList, 1283 }, 1284 { 1285 name: "map", 1286 f: firstUniquePathsMap, 1287 }, 1288 } 1289 const maxSize = 1024 1290 uniquePaths := make(Paths, maxSize) 1291 for i := range uniquePaths { 1292 uniquePaths[i] = PathForTesting(strconv.Itoa(i)) 1293 } 1294 samePath := make(Paths, maxSize) 1295 for i := range samePath { 1296 samePath[i] = uniquePaths[0] 1297 } 1298 1299 f := func(b *testing.B, imp func(Paths) Paths, paths Paths) { 1300 for i := 0; i < b.N; i++ { 1301 b.ReportAllocs() 1302 paths = append(Paths(nil), paths...) 1303 imp(paths) 1304 } 1305 } 1306 1307 for n := 1; n <= maxSize; n <<= 1 { 1308 b.Run(strconv.Itoa(n), func(b *testing.B) { 1309 for _, implementation := range implementations { 1310 b.Run(implementation.name, func(b *testing.B) { 1311 b.Run("same", func(b *testing.B) { 1312 f(b, implementation.f, samePath[:n]) 1313 }) 1314 b.Run("unique", func(b *testing.B) { 1315 f(b, implementation.f, uniquePaths[:n]) 1316 }) 1317 }) 1318 } 1319 }) 1320 } 1321} 1322