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, "unknown") 141 142 path = OptionalPathForPath(nil) 143 checkInvalidOptionalPath(t, path, "unknown") 144 145 path = InvalidOptionalPath("foo") 146 checkInvalidOptionalPath(t, path, "foo") 147 148 path = InvalidOptionalPath("") 149 checkInvalidOptionalPath(t, path, "unknown") 150 151 path = OptionalPathForPath(PathForTesting("path")) 152 checkValidOptionalPath(t, path, "path") 153} 154 155func checkInvalidOptionalPath(t *testing.T, path OptionalPath, expectedInvalidReason string) { 156 t.Helper() 157 if path.Valid() { 158 t.Errorf("Invalid OptionalPath should not be valid") 159 } 160 if path.InvalidReason() != expectedInvalidReason { 161 t.Errorf("Wrong invalid reason: expected %q, got %q", expectedInvalidReason, path.InvalidReason()) 162 } 163 if path.String() != "" { 164 t.Errorf("Invalid OptionalPath String() should return \"\", not %q", path.String()) 165 } 166 paths := path.AsPaths() 167 if len(paths) != 0 { 168 t.Errorf("Invalid OptionalPath AsPaths() should return empty Paths, not %q", paths) 169 } 170 defer func() { 171 if r := recover(); r == nil { 172 t.Errorf("Expected a panic when calling Path() on an uninitialized OptionalPath") 173 } 174 }() 175 path.Path() 176} 177 178func checkValidOptionalPath(t *testing.T, path OptionalPath, expectedString string) { 179 t.Helper() 180 if !path.Valid() { 181 t.Errorf("Initialized OptionalPath should not be invalid") 182 } 183 if path.InvalidReason() != "" { 184 t.Errorf("Initialized OptionalPath should not have an invalid reason, got: %q", path.InvalidReason()) 185 } 186 if path.String() != expectedString { 187 t.Errorf("Initialized OptionalPath String() should return %q, not %q", expectedString, path.String()) 188 } 189 paths := path.AsPaths() 190 if len(paths) != 1 { 191 t.Errorf("Initialized OptionalPath AsPaths() should return Paths with length 1, not %q", paths) 192 } 193 path.Path() 194} 195 196func check(t *testing.T, testType, testString string, 197 got interface{}, err []error, 198 expected interface{}, expectedErr []error) { 199 t.Helper() 200 201 printedTestCase := false 202 e := func(s string, expected, got interface{}) { 203 t.Helper() 204 if !printedTestCase { 205 t.Errorf("test case %s: %s", testType, testString) 206 printedTestCase = true 207 } 208 t.Errorf("incorrect %s", s) 209 t.Errorf(" expected: %s", p(expected)) 210 t.Errorf(" got: %s", p(got)) 211 } 212 213 if !reflect.DeepEqual(expectedErr, err) { 214 e("errors:", expectedErr, err) 215 } 216 217 if !reflect.DeepEqual(expected, got) { 218 e("output:", expected, got) 219 } 220} 221 222func p(in interface{}) string { 223 if v, ok := in.([]interface{}); ok { 224 s := make([]string, len(v)) 225 for i := range v { 226 s[i] = fmt.Sprintf("%#v", v[i]) 227 } 228 return "[" + strings.Join(s, ", ") + "]" 229 } else { 230 return fmt.Sprintf("%#v", in) 231 } 232} 233 234func pathTestConfig(buildDir string) Config { 235 return TestConfig(buildDir, nil, "", nil) 236} 237 238func TestPathForModuleInstall(t *testing.T) { 239 testConfig := pathTestConfig("") 240 241 hostTarget := Target{Os: Linux, Arch: Arch{ArchType: X86}} 242 deviceTarget := Target{Os: Android, Arch: Arch{ArchType: Arm64}} 243 244 testCases := []struct { 245 name string 246 ctx *testModuleInstallPathContext 247 in []string 248 out string 249 partitionDir string 250 }{ 251 { 252 name: "host binary", 253 ctx: &testModuleInstallPathContext{ 254 baseModuleContext: baseModuleContext{ 255 os: hostTarget.Os, 256 target: hostTarget, 257 }, 258 }, 259 in: []string{"bin", "my_test"}, 260 out: "host/linux-x86/bin/my_test", 261 partitionDir: "host/linux-x86", 262 }, 263 264 { 265 name: "system binary", 266 ctx: &testModuleInstallPathContext{ 267 baseModuleContext: baseModuleContext{ 268 os: deviceTarget.Os, 269 target: deviceTarget, 270 }, 271 }, 272 in: []string{"bin", "my_test"}, 273 out: "target/product/test_device/system/bin/my_test", 274 partitionDir: "target/product/test_device/system", 275 }, 276 { 277 name: "vendor binary", 278 ctx: &testModuleInstallPathContext{ 279 baseModuleContext: baseModuleContext{ 280 os: deviceTarget.Os, 281 target: deviceTarget, 282 earlyModuleContext: earlyModuleContext{ 283 kind: socSpecificModule, 284 }, 285 }, 286 }, 287 in: []string{"bin", "my_test"}, 288 out: "target/product/test_device/vendor/bin/my_test", 289 partitionDir: "target/product/test_device/vendor", 290 }, 291 { 292 name: "odm binary", 293 ctx: &testModuleInstallPathContext{ 294 baseModuleContext: baseModuleContext{ 295 os: deviceTarget.Os, 296 target: deviceTarget, 297 earlyModuleContext: earlyModuleContext{ 298 kind: deviceSpecificModule, 299 }, 300 }, 301 }, 302 in: []string{"bin", "my_test"}, 303 out: "target/product/test_device/odm/bin/my_test", 304 partitionDir: "target/product/test_device/odm", 305 }, 306 { 307 name: "product binary", 308 ctx: &testModuleInstallPathContext{ 309 baseModuleContext: baseModuleContext{ 310 os: deviceTarget.Os, 311 target: deviceTarget, 312 earlyModuleContext: earlyModuleContext{ 313 kind: productSpecificModule, 314 }, 315 }, 316 }, 317 in: []string{"bin", "my_test"}, 318 out: "target/product/test_device/product/bin/my_test", 319 partitionDir: "target/product/test_device/product", 320 }, 321 { 322 name: "system_ext binary", 323 ctx: &testModuleInstallPathContext{ 324 baseModuleContext: baseModuleContext{ 325 os: deviceTarget.Os, 326 target: deviceTarget, 327 earlyModuleContext: earlyModuleContext{ 328 kind: systemExtSpecificModule, 329 }, 330 }, 331 }, 332 in: []string{"bin", "my_test"}, 333 out: "target/product/test_device/system_ext/bin/my_test", 334 partitionDir: "target/product/test_device/system_ext", 335 }, 336 { 337 name: "root binary", 338 ctx: &testModuleInstallPathContext{ 339 baseModuleContext: baseModuleContext{ 340 os: deviceTarget.Os, 341 target: deviceTarget, 342 }, 343 inRoot: true, 344 }, 345 in: []string{"my_test"}, 346 out: "target/product/test_device/root/my_test", 347 partitionDir: "target/product/test_device/root", 348 }, 349 { 350 name: "recovery binary", 351 ctx: &testModuleInstallPathContext{ 352 baseModuleContext: baseModuleContext{ 353 os: deviceTarget.Os, 354 target: deviceTarget, 355 }, 356 inRecovery: true, 357 }, 358 in: []string{"bin/my_test"}, 359 out: "target/product/test_device/recovery/root/system/bin/my_test", 360 partitionDir: "target/product/test_device/recovery/root/system", 361 }, 362 { 363 name: "recovery root binary", 364 ctx: &testModuleInstallPathContext{ 365 baseModuleContext: baseModuleContext{ 366 os: deviceTarget.Os, 367 target: deviceTarget, 368 }, 369 inRecovery: true, 370 inRoot: true, 371 }, 372 in: []string{"my_test"}, 373 out: "target/product/test_device/recovery/root/my_test", 374 partitionDir: "target/product/test_device/recovery/root", 375 }, 376 377 { 378 name: "ramdisk binary", 379 ctx: &testModuleInstallPathContext{ 380 baseModuleContext: baseModuleContext{ 381 os: deviceTarget.Os, 382 target: deviceTarget, 383 }, 384 inRamdisk: true, 385 }, 386 in: []string{"my_test"}, 387 out: "target/product/test_device/ramdisk/system/my_test", 388 partitionDir: "target/product/test_device/ramdisk/system", 389 }, 390 { 391 name: "ramdisk root binary", 392 ctx: &testModuleInstallPathContext{ 393 baseModuleContext: baseModuleContext{ 394 os: deviceTarget.Os, 395 target: deviceTarget, 396 }, 397 inRamdisk: true, 398 inRoot: true, 399 }, 400 in: []string{"my_test"}, 401 out: "target/product/test_device/ramdisk/my_test", 402 partitionDir: "target/product/test_device/ramdisk", 403 }, 404 { 405 name: "vendor_ramdisk binary", 406 ctx: &testModuleInstallPathContext{ 407 baseModuleContext: baseModuleContext{ 408 os: deviceTarget.Os, 409 target: deviceTarget, 410 }, 411 inVendorRamdisk: true, 412 }, 413 in: []string{"my_test"}, 414 out: "target/product/test_device/vendor_ramdisk/system/my_test", 415 partitionDir: "target/product/test_device/vendor_ramdisk/system", 416 }, 417 { 418 name: "vendor_ramdisk root binary", 419 ctx: &testModuleInstallPathContext{ 420 baseModuleContext: baseModuleContext{ 421 os: deviceTarget.Os, 422 target: deviceTarget, 423 }, 424 inVendorRamdisk: true, 425 inRoot: true, 426 }, 427 in: []string{"my_test"}, 428 out: "target/product/test_device/vendor_ramdisk/my_test", 429 partitionDir: "target/product/test_device/vendor_ramdisk", 430 }, 431 { 432 name: "debug_ramdisk binary", 433 ctx: &testModuleInstallPathContext{ 434 baseModuleContext: baseModuleContext{ 435 os: deviceTarget.Os, 436 target: deviceTarget, 437 }, 438 inDebugRamdisk: true, 439 }, 440 in: []string{"my_test"}, 441 out: "target/product/test_device/debug_ramdisk/my_test", 442 partitionDir: "target/product/test_device/debug_ramdisk", 443 }, 444 { 445 name: "system native test binary", 446 ctx: &testModuleInstallPathContext{ 447 baseModuleContext: baseModuleContext{ 448 os: deviceTarget.Os, 449 target: deviceTarget, 450 }, 451 inData: true, 452 }, 453 in: []string{"nativetest", "my_test"}, 454 out: "target/product/test_device/data/nativetest/my_test", 455 partitionDir: "target/product/test_device/data", 456 }, 457 { 458 name: "vendor native test binary", 459 ctx: &testModuleInstallPathContext{ 460 baseModuleContext: baseModuleContext{ 461 os: deviceTarget.Os, 462 target: deviceTarget, 463 earlyModuleContext: earlyModuleContext{ 464 kind: socSpecificModule, 465 }, 466 }, 467 inData: true, 468 }, 469 in: []string{"nativetest", "my_test"}, 470 out: "target/product/test_device/data/nativetest/my_test", 471 partitionDir: "target/product/test_device/data", 472 }, 473 { 474 name: "odm native test binary", 475 ctx: &testModuleInstallPathContext{ 476 baseModuleContext: baseModuleContext{ 477 os: deviceTarget.Os, 478 target: deviceTarget, 479 earlyModuleContext: earlyModuleContext{ 480 kind: deviceSpecificModule, 481 }, 482 }, 483 inData: true, 484 }, 485 in: []string{"nativetest", "my_test"}, 486 out: "target/product/test_device/data/nativetest/my_test", 487 partitionDir: "target/product/test_device/data", 488 }, 489 { 490 name: "product native test binary", 491 ctx: &testModuleInstallPathContext{ 492 baseModuleContext: baseModuleContext{ 493 os: deviceTarget.Os, 494 target: deviceTarget, 495 earlyModuleContext: earlyModuleContext{ 496 kind: productSpecificModule, 497 }, 498 }, 499 inData: true, 500 }, 501 in: []string{"nativetest", "my_test"}, 502 out: "target/product/test_device/data/nativetest/my_test", 503 partitionDir: "target/product/test_device/data", 504 }, 505 506 { 507 name: "system_ext native test binary", 508 ctx: &testModuleInstallPathContext{ 509 baseModuleContext: baseModuleContext{ 510 os: deviceTarget.Os, 511 target: deviceTarget, 512 earlyModuleContext: earlyModuleContext{ 513 kind: systemExtSpecificModule, 514 }, 515 }, 516 inData: true, 517 }, 518 in: []string{"nativetest", "my_test"}, 519 out: "target/product/test_device/data/nativetest/my_test", 520 partitionDir: "target/product/test_device/data", 521 }, 522 523 { 524 name: "sanitized system binary", 525 ctx: &testModuleInstallPathContext{ 526 baseModuleContext: baseModuleContext{ 527 os: deviceTarget.Os, 528 target: deviceTarget, 529 }, 530 inSanitizerDir: true, 531 }, 532 in: []string{"bin", "my_test"}, 533 out: "target/product/test_device/data/asan/system/bin/my_test", 534 partitionDir: "target/product/test_device/data/asan/system", 535 }, 536 { 537 name: "sanitized vendor binary", 538 ctx: &testModuleInstallPathContext{ 539 baseModuleContext: baseModuleContext{ 540 os: deviceTarget.Os, 541 target: deviceTarget, 542 earlyModuleContext: earlyModuleContext{ 543 kind: socSpecificModule, 544 }, 545 }, 546 inSanitizerDir: true, 547 }, 548 in: []string{"bin", "my_test"}, 549 out: "target/product/test_device/data/asan/vendor/bin/my_test", 550 partitionDir: "target/product/test_device/data/asan/vendor", 551 }, 552 { 553 name: "sanitized odm binary", 554 ctx: &testModuleInstallPathContext{ 555 baseModuleContext: baseModuleContext{ 556 os: deviceTarget.Os, 557 target: deviceTarget, 558 earlyModuleContext: earlyModuleContext{ 559 kind: deviceSpecificModule, 560 }, 561 }, 562 inSanitizerDir: true, 563 }, 564 in: []string{"bin", "my_test"}, 565 out: "target/product/test_device/data/asan/odm/bin/my_test", 566 partitionDir: "target/product/test_device/data/asan/odm", 567 }, 568 { 569 name: "sanitized product binary", 570 ctx: &testModuleInstallPathContext{ 571 baseModuleContext: baseModuleContext{ 572 os: deviceTarget.Os, 573 target: deviceTarget, 574 earlyModuleContext: earlyModuleContext{ 575 kind: productSpecificModule, 576 }, 577 }, 578 inSanitizerDir: true, 579 }, 580 in: []string{"bin", "my_test"}, 581 out: "target/product/test_device/data/asan/product/bin/my_test", 582 partitionDir: "target/product/test_device/data/asan/product", 583 }, 584 585 { 586 name: "sanitized system_ext binary", 587 ctx: &testModuleInstallPathContext{ 588 baseModuleContext: baseModuleContext{ 589 os: deviceTarget.Os, 590 target: deviceTarget, 591 earlyModuleContext: earlyModuleContext{ 592 kind: systemExtSpecificModule, 593 }, 594 }, 595 inSanitizerDir: true, 596 }, 597 in: []string{"bin", "my_test"}, 598 out: "target/product/test_device/data/asan/system_ext/bin/my_test", 599 partitionDir: "target/product/test_device/data/asan/system_ext", 600 }, 601 602 { 603 name: "sanitized system native test binary", 604 ctx: &testModuleInstallPathContext{ 605 baseModuleContext: baseModuleContext{ 606 os: deviceTarget.Os, 607 target: deviceTarget, 608 }, 609 inData: true, 610 inSanitizerDir: true, 611 }, 612 in: []string{"nativetest", "my_test"}, 613 out: "target/product/test_device/data/asan/data/nativetest/my_test", 614 partitionDir: "target/product/test_device/data/asan/data", 615 }, 616 { 617 name: "sanitized vendor native test binary", 618 ctx: &testModuleInstallPathContext{ 619 baseModuleContext: baseModuleContext{ 620 os: deviceTarget.Os, 621 target: deviceTarget, 622 earlyModuleContext: earlyModuleContext{ 623 kind: socSpecificModule, 624 }, 625 }, 626 inData: true, 627 inSanitizerDir: true, 628 }, 629 in: []string{"nativetest", "my_test"}, 630 out: "target/product/test_device/data/asan/data/nativetest/my_test", 631 partitionDir: "target/product/test_device/data/asan/data", 632 }, 633 { 634 name: "sanitized odm native test binary", 635 ctx: &testModuleInstallPathContext{ 636 baseModuleContext: baseModuleContext{ 637 os: deviceTarget.Os, 638 target: deviceTarget, 639 earlyModuleContext: earlyModuleContext{ 640 kind: deviceSpecificModule, 641 }, 642 }, 643 inData: true, 644 inSanitizerDir: true, 645 }, 646 in: []string{"nativetest", "my_test"}, 647 out: "target/product/test_device/data/asan/data/nativetest/my_test", 648 partitionDir: "target/product/test_device/data/asan/data", 649 }, 650 { 651 name: "sanitized product native test binary", 652 ctx: &testModuleInstallPathContext{ 653 baseModuleContext: baseModuleContext{ 654 os: deviceTarget.Os, 655 target: deviceTarget, 656 earlyModuleContext: earlyModuleContext{ 657 kind: productSpecificModule, 658 }, 659 }, 660 inData: true, 661 inSanitizerDir: true, 662 }, 663 in: []string{"nativetest", "my_test"}, 664 out: "target/product/test_device/data/asan/data/nativetest/my_test", 665 partitionDir: "target/product/test_device/data/asan/data", 666 }, 667 { 668 name: "sanitized system_ext native test binary", 669 ctx: &testModuleInstallPathContext{ 670 baseModuleContext: baseModuleContext{ 671 os: deviceTarget.Os, 672 target: deviceTarget, 673 earlyModuleContext: earlyModuleContext{ 674 kind: systemExtSpecificModule, 675 }, 676 }, 677 inData: true, 678 inSanitizerDir: true, 679 }, 680 in: []string{"nativetest", "my_test"}, 681 out: "target/product/test_device/data/asan/data/nativetest/my_test", 682 partitionDir: "target/product/test_device/data/asan/data", 683 }, { 684 name: "device testcases", 685 ctx: &testModuleInstallPathContext{ 686 baseModuleContext: baseModuleContext{ 687 os: deviceTarget.Os, 688 target: deviceTarget, 689 }, 690 inTestcases: true, 691 }, 692 in: []string{"my_test", "my_test_bin"}, 693 out: "target/product/test_device/testcases/my_test/my_test_bin", 694 partitionDir: "target/product/test_device/testcases", 695 }, { 696 name: "host testcases", 697 ctx: &testModuleInstallPathContext{ 698 baseModuleContext: baseModuleContext{ 699 os: hostTarget.Os, 700 target: hostTarget, 701 }, 702 inTestcases: true, 703 }, 704 in: []string{"my_test", "my_test_bin"}, 705 out: "host/linux-x86/testcases/my_test/my_test_bin", 706 partitionDir: "host/linux-x86/testcases", 707 }, { 708 name: "forced host testcases", 709 ctx: &testModuleInstallPathContext{ 710 baseModuleContext: baseModuleContext{ 711 os: deviceTarget.Os, 712 target: deviceTarget, 713 }, 714 inTestcases: true, 715 forceOS: &Linux, 716 forceArch: &X86, 717 }, 718 in: []string{"my_test", "my_test_bin"}, 719 out: "host/linux-x86/testcases/my_test/my_test_bin", 720 partitionDir: "host/linux-x86/testcases", 721 }, 722 } 723 724 for _, tc := range testCases { 725 t.Run(tc.name, func(t *testing.T) { 726 tc.ctx.baseModuleContext.config = testConfig 727 output := PathForModuleInstall(tc.ctx, tc.in...) 728 if output.basePath.path != tc.out { 729 t.Errorf("unexpected path:\n got: %q\nwant: %q\n", 730 output.basePath.path, 731 tc.out) 732 } 733 if output.partitionDir != tc.partitionDir { 734 t.Errorf("unexpected partitionDir:\n got: %q\nwant: %q\n", 735 output.partitionDir, tc.partitionDir) 736 } 737 }) 738 } 739} 740 741func TestPathForModuleInstallRecoveryAsBoot(t *testing.T) { 742 testConfig := pathTestConfig("") 743 testConfig.TestProductVariables.BoardUsesRecoveryAsBoot = proptools.BoolPtr(true) 744 testConfig.TestProductVariables.BoardMoveRecoveryResourcesToVendorBoot = proptools.BoolPtr(true) 745 deviceTarget := Target{Os: Android, Arch: Arch{ArchType: Arm64}} 746 747 testCases := []struct { 748 name string 749 ctx *testModuleInstallPathContext 750 in []string 751 out string 752 partitionDir string 753 }{ 754 { 755 name: "ramdisk binary", 756 ctx: &testModuleInstallPathContext{ 757 baseModuleContext: baseModuleContext{ 758 os: deviceTarget.Os, 759 target: deviceTarget, 760 }, 761 inRamdisk: true, 762 inRoot: true, 763 }, 764 in: []string{"my_test"}, 765 out: "target/product/test_device/recovery/root/first_stage_ramdisk/my_test", 766 partitionDir: "target/product/test_device/recovery/root/first_stage_ramdisk", 767 }, 768 769 { 770 name: "vendor_ramdisk binary", 771 ctx: &testModuleInstallPathContext{ 772 baseModuleContext: baseModuleContext{ 773 os: deviceTarget.Os, 774 target: deviceTarget, 775 }, 776 inVendorRamdisk: true, 777 inRoot: true, 778 }, 779 in: []string{"my_test"}, 780 out: "target/product/test_device/vendor_ramdisk/first_stage_ramdisk/my_test", 781 partitionDir: "target/product/test_device/vendor_ramdisk/first_stage_ramdisk", 782 }, 783 } 784 785 for _, tc := range testCases { 786 t.Run(tc.name, func(t *testing.T) { 787 tc.ctx.baseModuleContext.config = testConfig 788 output := PathForModuleInstall(tc.ctx, tc.in...) 789 if output.basePath.path != tc.out { 790 t.Errorf("unexpected path:\n got: %q\nwant: %q\n", 791 output.basePath.path, 792 tc.out) 793 } 794 if output.partitionDir != tc.partitionDir { 795 t.Errorf("unexpected partitionDir:\n got: %q\nwant: %q\n", 796 output.partitionDir, tc.partitionDir) 797 } 798 }) 799 } 800} 801 802func TestBaseDirForInstallPath(t *testing.T) { 803 testConfig := pathTestConfig("") 804 deviceTarget := Target{Os: Android, Arch: Arch{ArchType: Arm64}} 805 806 ctx := &testModuleInstallPathContext{ 807 baseModuleContext: baseModuleContext{ 808 os: deviceTarget.Os, 809 target: deviceTarget, 810 }, 811 } 812 ctx.baseModuleContext.config = testConfig 813 814 actual := PathForModuleInstall(ctx, "foo", "bar") 815 expectedBaseDir := "target/product/test_device/system" 816 if actual.partitionDir != expectedBaseDir { 817 t.Errorf("unexpected partitionDir:\n got: %q\nwant: %q\n", actual.partitionDir, expectedBaseDir) 818 } 819 expectedRelPath := "foo/bar" 820 if actual.Rel() != expectedRelPath { 821 t.Errorf("unexpected Rel():\n got: %q\nwant: %q\n", actual.Rel(), expectedRelPath) 822 } 823 824 actualAfterJoin := actual.Join(ctx, "baz") 825 // partitionDir is preserved even after joining 826 if actualAfterJoin.partitionDir != expectedBaseDir { 827 t.Errorf("unexpected partitionDir after joining:\n got: %q\nwant: %q\n", actualAfterJoin.partitionDir, expectedBaseDir) 828 } 829 // Rel() is updated though 830 expectedRelAfterJoin := "baz" 831 if actualAfterJoin.Rel() != expectedRelAfterJoin { 832 t.Errorf("unexpected Rel() after joining:\n got: %q\nwant: %q\n", actualAfterJoin.Rel(), expectedRelAfterJoin) 833 } 834} 835 836func TestDirectorySortedPaths(t *testing.T) { 837 config := TestConfig("out", nil, "", map[string][]byte{ 838 "Android.bp": nil, 839 "a.txt": nil, 840 "a/txt": nil, 841 "a/b/c": nil, 842 "a/b/d": nil, 843 "b": nil, 844 "b/b.txt": nil, 845 "a/a.txt": nil, 846 }) 847 848 ctx := PathContextForTesting(config) 849 850 makePaths := func() Paths { 851 return Paths{ 852 PathForSource(ctx, "a.txt"), 853 PathForSource(ctx, "a/txt"), 854 PathForSource(ctx, "a/b/c"), 855 PathForSource(ctx, "a/b/d"), 856 PathForSource(ctx, "b"), 857 PathForSource(ctx, "b/b.txt"), 858 PathForSource(ctx, "a/a.txt"), 859 } 860 } 861 862 expected := []string{ 863 "a.txt", 864 "a/a.txt", 865 "a/b/c", 866 "a/b/d", 867 "a/txt", 868 "b", 869 "b/b.txt", 870 } 871 872 paths := makePaths() 873 reversePaths := ReversePaths(paths) 874 875 sortedPaths := PathsToDirectorySortedPaths(paths) 876 reverseSortedPaths := PathsToDirectorySortedPaths(reversePaths) 877 878 if !reflect.DeepEqual(Paths(sortedPaths).Strings(), expected) { 879 t.Fatalf("sorted paths:\n %#v\n != \n %#v", paths.Strings(), expected) 880 } 881 882 if !reflect.DeepEqual(Paths(reverseSortedPaths).Strings(), expected) { 883 t.Fatalf("sorted reversed paths:\n %#v\n !=\n %#v", reversePaths.Strings(), expected) 884 } 885 886 expectedA := []string{ 887 "a/a.txt", 888 "a/b/c", 889 "a/b/d", 890 "a/txt", 891 } 892 893 inA := sortedPaths.PathsInDirectory("a") 894 if !reflect.DeepEqual(inA.Strings(), expectedA) { 895 t.Errorf("FilesInDirectory(a):\n %#v\n != \n %#v", inA.Strings(), expectedA) 896 } 897 898 expectedA_B := []string{ 899 "a/b/c", 900 "a/b/d", 901 } 902 903 inA_B := sortedPaths.PathsInDirectory("a/b") 904 if !reflect.DeepEqual(inA_B.Strings(), expectedA_B) { 905 t.Errorf("FilesInDirectory(a/b):\n %#v\n != \n %#v", inA_B.Strings(), expectedA_B) 906 } 907 908 expectedB := []string{ 909 "b/b.txt", 910 } 911 912 inB := sortedPaths.PathsInDirectory("b") 913 if !reflect.DeepEqual(inB.Strings(), expectedB) { 914 t.Errorf("FilesInDirectory(b):\n %#v\n != \n %#v", inA.Strings(), expectedA) 915 } 916} 917 918func TestMaybeRel(t *testing.T) { 919 testCases := []struct { 920 name string 921 base string 922 target string 923 out string 924 isRel bool 925 }{ 926 { 927 name: "normal", 928 base: "a/b/c", 929 target: "a/b/c/d", 930 out: "d", 931 isRel: true, 932 }, 933 { 934 name: "parent", 935 base: "a/b/c/d", 936 target: "a/b/c", 937 isRel: false, 938 }, 939 { 940 name: "not relative", 941 base: "a/b", 942 target: "c/d", 943 isRel: false, 944 }, 945 { 946 name: "abs1", 947 base: "/a", 948 target: "a", 949 isRel: false, 950 }, 951 { 952 name: "abs2", 953 base: "a", 954 target: "/a", 955 isRel: false, 956 }, 957 } 958 959 for _, testCase := range testCases { 960 t.Run(testCase.name, func(t *testing.T) { 961 ctx := &configErrorWrapper{} 962 out, isRel := MaybeRel(ctx, testCase.base, testCase.target) 963 if len(ctx.errors) > 0 { 964 t.Errorf("MaybeRel(..., %s, %s) reported unexpected errors %v", 965 testCase.base, testCase.target, ctx.errors) 966 } 967 if isRel != testCase.isRel || out != testCase.out { 968 t.Errorf("MaybeRel(..., %s, %s) want %v, %v got %v, %v", 969 testCase.base, testCase.target, testCase.out, testCase.isRel, out, isRel) 970 } 971 }) 972 } 973} 974 975func TestPathForSource(t *testing.T) { 976 testCases := []struct { 977 name string 978 buildDir string 979 src string 980 err string 981 }{ 982 { 983 name: "normal", 984 buildDir: "out", 985 src: "a/b/c", 986 }, 987 { 988 name: "abs", 989 buildDir: "out", 990 src: "/a/b/c", 991 err: "is outside directory", 992 }, 993 { 994 name: "in out dir", 995 buildDir: "out", 996 src: "out/soong/a/b/c", 997 err: "is in output", 998 }, 999 } 1000 1001 funcs := []struct { 1002 name string 1003 f func(ctx PathContext, pathComponents ...string) (SourcePath, error) 1004 }{ 1005 {"pathForSource", pathForSource}, 1006 {"safePathForSource", safePathForSource}, 1007 } 1008 1009 for _, f := range funcs { 1010 t.Run(f.name, func(t *testing.T) { 1011 for _, test := range testCases { 1012 t.Run(test.name, func(t *testing.T) { 1013 testConfig := pathTestConfig(test.buildDir) 1014 ctx := &configErrorWrapper{config: testConfig} 1015 _, err := f.f(ctx, test.src) 1016 if len(ctx.errors) > 0 { 1017 t.Fatalf("unexpected errors %v", ctx.errors) 1018 } 1019 if err != nil { 1020 if test.err == "" { 1021 t.Fatalf("unexpected error %q", err.Error()) 1022 } else if !strings.Contains(err.Error(), test.err) { 1023 t.Fatalf("incorrect error, want substring %q got %q", test.err, err.Error()) 1024 } 1025 } else { 1026 if test.err != "" { 1027 t.Fatalf("missing error %q", test.err) 1028 } 1029 } 1030 }) 1031 } 1032 }) 1033 } 1034} 1035 1036type pathForModuleSrcTestModule struct { 1037 ModuleBase 1038 props struct { 1039 Srcs []string `android:"path"` 1040 Exclude_srcs []string `android:"path"` 1041 1042 Src *string `android:"path"` 1043 1044 Module_handles_missing_deps bool 1045 } 1046 1047 src string 1048 rel string 1049 1050 srcs []string 1051 rels []string 1052 1053 missingDeps []string 1054} 1055 1056func pathForModuleSrcTestModuleFactory() Module { 1057 module := &pathForModuleSrcTestModule{} 1058 module.AddProperties(&module.props) 1059 InitAndroidModule(module) 1060 return module 1061} 1062 1063func (p *pathForModuleSrcTestModule) GenerateAndroidBuildActions(ctx ModuleContext) { 1064 var srcs Paths 1065 if p.props.Module_handles_missing_deps { 1066 srcs, p.missingDeps = PathsAndMissingDepsForModuleSrcExcludes(ctx, p.props.Srcs, p.props.Exclude_srcs) 1067 } else { 1068 srcs = PathsForModuleSrcExcludes(ctx, p.props.Srcs, p.props.Exclude_srcs) 1069 } 1070 p.srcs = srcs.Strings() 1071 1072 for _, src := range srcs { 1073 p.rels = append(p.rels, src.Rel()) 1074 } 1075 1076 if p.props.Src != nil { 1077 src := PathForModuleSrc(ctx, *p.props.Src) 1078 if src != nil { 1079 p.src = src.String() 1080 p.rel = src.Rel() 1081 } 1082 } 1083 1084 if !p.props.Module_handles_missing_deps { 1085 p.missingDeps = ctx.GetMissingDependencies() 1086 } 1087 1088 ctx.Build(pctx, BuildParams{ 1089 Rule: Touch, 1090 Output: PathForModuleOut(ctx, "output"), 1091 }) 1092} 1093 1094type pathForModuleSrcOutputFileProviderModule struct { 1095 ModuleBase 1096 props struct { 1097 Outs []string 1098 Tagged []string 1099 } 1100 1101 outs Paths 1102 tagged Paths 1103} 1104 1105func pathForModuleSrcOutputFileProviderModuleFactory() Module { 1106 module := &pathForModuleSrcOutputFileProviderModule{} 1107 module.AddProperties(&module.props) 1108 InitAndroidModule(module) 1109 return module 1110} 1111 1112func (p *pathForModuleSrcOutputFileProviderModule) GenerateAndroidBuildActions(ctx ModuleContext) { 1113 for _, out := range p.props.Outs { 1114 p.outs = append(p.outs, PathForModuleOut(ctx, out)) 1115 } 1116 1117 for _, tagged := range p.props.Tagged { 1118 p.tagged = append(p.tagged, PathForModuleOut(ctx, tagged)) 1119 } 1120} 1121 1122func (p *pathForModuleSrcOutputFileProviderModule) OutputFiles(tag string) (Paths, error) { 1123 switch tag { 1124 case "": 1125 return p.outs, nil 1126 case ".tagged": 1127 return p.tagged, nil 1128 default: 1129 return nil, fmt.Errorf("unsupported tag %q", tag) 1130 } 1131} 1132 1133type pathForModuleSrcTestCase struct { 1134 name string 1135 bp string 1136 srcs []string 1137 rels []string 1138 src string 1139 rel string 1140 1141 // Make test specific preparations to the test fixture. 1142 preparer FixturePreparer 1143 1144 // A test specific error handler. 1145 errorHandler FixtureErrorHandler 1146} 1147 1148func testPathForModuleSrc(t *testing.T, tests []pathForModuleSrcTestCase) { 1149 for _, test := range tests { 1150 t.Run(test.name, func(t *testing.T) { 1151 fgBp := ` 1152 filegroup { 1153 name: "a", 1154 srcs: ["src/a"], 1155 } 1156 ` 1157 1158 ofpBp := ` 1159 output_file_provider { 1160 name: "b", 1161 outs: ["gen/b"], 1162 tagged: ["gen/c"], 1163 } 1164 ` 1165 1166 mockFS := MockFS{ 1167 "fg/Android.bp": []byte(fgBp), 1168 "foo/Android.bp": []byte(test.bp), 1169 "ofp/Android.bp": []byte(ofpBp), 1170 "fg/src/a": nil, 1171 "foo/src/b": nil, 1172 "foo/src/c": nil, 1173 "foo/src/d": nil, 1174 "foo/src/e/e": nil, 1175 "foo/src_special/$": nil, 1176 } 1177 1178 errorHandler := test.errorHandler 1179 if errorHandler == nil { 1180 errorHandler = FixtureExpectsNoErrors 1181 } 1182 1183 result := GroupFixturePreparers( 1184 FixtureRegisterWithContext(func(ctx RegistrationContext) { 1185 ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory) 1186 ctx.RegisterModuleType("output_file_provider", pathForModuleSrcOutputFileProviderModuleFactory) 1187 }), 1188 PrepareForTestWithFilegroup, 1189 PrepareForTestWithNamespace, 1190 mockFS.AddToFixture(), 1191 OptionalFixturePreparer(test.preparer), 1192 ). 1193 ExtendWithErrorHandler(errorHandler). 1194 RunTest(t) 1195 1196 m := result.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule) 1197 1198 AssertStringPathsRelativeToTopEquals(t, "srcs", result.Config, test.srcs, m.srcs) 1199 AssertStringPathsRelativeToTopEquals(t, "rels", result.Config, test.rels, m.rels) 1200 AssertStringPathRelativeToTopEquals(t, "src", result.Config, test.src, m.src) 1201 AssertStringPathRelativeToTopEquals(t, "rel", result.Config, test.rel, m.rel) 1202 }) 1203 } 1204} 1205 1206func TestPathsForModuleSrc(t *testing.T) { 1207 tests := []pathForModuleSrcTestCase{ 1208 { 1209 name: "path", 1210 bp: ` 1211 test { 1212 name: "foo", 1213 srcs: ["src/b"], 1214 }`, 1215 srcs: []string{"foo/src/b"}, 1216 rels: []string{"src/b"}, 1217 }, 1218 { 1219 name: "glob", 1220 bp: ` 1221 test { 1222 name: "foo", 1223 srcs: [ 1224 "src/*", 1225 "src/e/*", 1226 ], 1227 }`, 1228 srcs: []string{"foo/src/b", "foo/src/c", "foo/src/d", "foo/src/e/e"}, 1229 rels: []string{"src/b", "src/c", "src/d", "src/e/e"}, 1230 }, 1231 { 1232 name: "recursive glob", 1233 bp: ` 1234 test { 1235 name: "foo", 1236 srcs: ["src/**/*"], 1237 }`, 1238 srcs: []string{"foo/src/b", "foo/src/c", "foo/src/d", "foo/src/e/e"}, 1239 rels: []string{"src/b", "src/c", "src/d", "src/e/e"}, 1240 }, 1241 { 1242 name: "filegroup", 1243 bp: ` 1244 test { 1245 name: "foo", 1246 srcs: [":a"], 1247 }`, 1248 srcs: []string{"fg/src/a"}, 1249 rels: []string{"src/a"}, 1250 }, 1251 { 1252 name: "output file provider", 1253 bp: ` 1254 test { 1255 name: "foo", 1256 srcs: [":b"], 1257 }`, 1258 srcs: []string{"out/soong/.intermediates/ofp/b/gen/b"}, 1259 rels: []string{"gen/b"}, 1260 }, 1261 { 1262 name: "output file provider tagged", 1263 bp: ` 1264 test { 1265 name: "foo", 1266 srcs: [":b{.tagged}"], 1267 }`, 1268 srcs: []string{"out/soong/.intermediates/ofp/b/gen/c"}, 1269 rels: []string{"gen/c"}, 1270 }, 1271 { 1272 name: "output file provider with exclude", 1273 bp: ` 1274 test { 1275 name: "foo", 1276 srcs: [":b", ":c"], 1277 exclude_srcs: [":c"] 1278 } 1279 output_file_provider { 1280 name: "c", 1281 outs: ["gen/c"], 1282 }`, 1283 srcs: []string{"out/soong/.intermediates/ofp/b/gen/b"}, 1284 rels: []string{"gen/b"}, 1285 }, 1286 { 1287 name: "special characters glob", 1288 bp: ` 1289 test { 1290 name: "foo", 1291 srcs: ["src_special/*"], 1292 }`, 1293 srcs: []string{"foo/src_special/$"}, 1294 rels: []string{"src_special/$"}, 1295 }, 1296 } 1297 1298 testPathForModuleSrc(t, tests) 1299} 1300 1301func TestPathForModuleSrc(t *testing.T) { 1302 tests := []pathForModuleSrcTestCase{ 1303 { 1304 name: "path", 1305 bp: ` 1306 test { 1307 name: "foo", 1308 src: "src/b", 1309 }`, 1310 src: "foo/src/b", 1311 rel: "src/b", 1312 }, 1313 { 1314 name: "glob", 1315 bp: ` 1316 test { 1317 name: "foo", 1318 src: "src/e/*", 1319 }`, 1320 src: "foo/src/e/e", 1321 rel: "src/e/e", 1322 }, 1323 { 1324 name: "filegroup", 1325 bp: ` 1326 test { 1327 name: "foo", 1328 src: ":a", 1329 }`, 1330 src: "fg/src/a", 1331 rel: "src/a", 1332 }, 1333 { 1334 name: "output file provider", 1335 bp: ` 1336 test { 1337 name: "foo", 1338 src: ":b", 1339 }`, 1340 src: "out/soong/.intermediates/ofp/b/gen/b", 1341 rel: "gen/b", 1342 }, 1343 { 1344 name: "output file provider tagged", 1345 bp: ` 1346 test { 1347 name: "foo", 1348 src: ":b{.tagged}", 1349 }`, 1350 src: "out/soong/.intermediates/ofp/b/gen/c", 1351 rel: "gen/c", 1352 }, 1353 { 1354 name: "special characters glob", 1355 bp: ` 1356 test { 1357 name: "foo", 1358 src: "src_special/*", 1359 }`, 1360 src: "foo/src_special/$", 1361 rel: "src_special/$", 1362 }, 1363 { 1364 // This test makes sure that an unqualified module name cannot contain characters that make 1365 // it appear as a qualified module name. 1366 name: "output file provider, invalid fully qualified name", 1367 bp: ` 1368 test { 1369 name: "foo", 1370 src: "://other:b", 1371 srcs: ["://other:c"], 1372 }`, 1373 preparer: FixtureAddTextFile("other/Android.bp", ` 1374 soong_namespace {} 1375 1376 output_file_provider { 1377 name: "b", 1378 outs: ["gen/b"], 1379 } 1380 1381 output_file_provider { 1382 name: "c", 1383 outs: ["gen/c"], 1384 } 1385 `), 1386 src: "foo/:/other:b", 1387 rel: ":/other:b", 1388 srcs: []string{"foo/:/other:c"}, 1389 rels: []string{":/other:c"}, 1390 }, 1391 { 1392 name: "output file provider, missing fully qualified name", 1393 bp: ` 1394 test { 1395 name: "foo", 1396 src: "//other:b", 1397 srcs: ["//other:c"], 1398 }`, 1399 errorHandler: FixtureExpectsAllErrorsToMatchAPattern([]string{ 1400 `"foo" depends on undefined module "//other:b"`, 1401 `"foo" depends on undefined module "//other:c"`, 1402 }), 1403 }, 1404 { 1405 name: "output file provider, fully qualified name", 1406 bp: ` 1407 test { 1408 name: "foo", 1409 src: "//other:b", 1410 srcs: ["//other:c"], 1411 }`, 1412 src: "out/soong/.intermediates/other/b/gen/b", 1413 rel: "gen/b", 1414 srcs: []string{"out/soong/.intermediates/other/c/gen/c"}, 1415 rels: []string{"gen/c"}, 1416 preparer: FixtureAddTextFile("other/Android.bp", ` 1417 soong_namespace {} 1418 1419 output_file_provider { 1420 name: "b", 1421 outs: ["gen/b"], 1422 } 1423 1424 output_file_provider { 1425 name: "c", 1426 outs: ["gen/c"], 1427 } 1428 `), 1429 }, 1430 } 1431 1432 testPathForModuleSrc(t, tests) 1433} 1434 1435func TestPathsForModuleSrc_AllowMissingDependencies(t *testing.T) { 1436 bp := ` 1437 test { 1438 name: "foo", 1439 srcs: [":a"], 1440 exclude_srcs: [":b"], 1441 src: ":c", 1442 } 1443 1444 test { 1445 name: "bar", 1446 srcs: [":d"], 1447 exclude_srcs: [":e"], 1448 module_handles_missing_deps: true, 1449 } 1450 ` 1451 1452 result := GroupFixturePreparers( 1453 PrepareForTestWithAllowMissingDependencies, 1454 FixtureRegisterWithContext(func(ctx RegistrationContext) { 1455 ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory) 1456 }), 1457 FixtureWithRootAndroidBp(bp), 1458 ).RunTest(t) 1459 1460 foo := result.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule) 1461 1462 AssertArrayString(t, "foo missing deps", []string{"a", "b", "c"}, foo.missingDeps) 1463 AssertArrayString(t, "foo srcs", []string{}, foo.srcs) 1464 AssertStringEquals(t, "foo src", "", foo.src) 1465 1466 bar := result.ModuleForTests("bar", "").Module().(*pathForModuleSrcTestModule) 1467 1468 AssertArrayString(t, "bar missing deps", []string{"d", "e"}, bar.missingDeps) 1469 AssertArrayString(t, "bar srcs", []string{}, bar.srcs) 1470} 1471 1472func TestPathRelativeToTop(t *testing.T) { 1473 testConfig := pathTestConfig("/tmp/build/top") 1474 deviceTarget := Target{Os: Android, Arch: Arch{ArchType: Arm64}} 1475 1476 ctx := &testModuleInstallPathContext{ 1477 baseModuleContext: baseModuleContext{ 1478 os: deviceTarget.Os, 1479 target: deviceTarget, 1480 }, 1481 } 1482 ctx.baseModuleContext.config = testConfig 1483 1484 t.Run("install for soong", func(t *testing.T) { 1485 p := PathForModuleInstall(ctx, "install/path") 1486 AssertPathRelativeToTopEquals(t, "install path for soong", "out/soong/target/product/test_device/system/install/path", p) 1487 }) 1488 t.Run("install for make", func(t *testing.T) { 1489 p := PathForModuleInstall(ctx, "install/path") 1490 p.makePath = true 1491 AssertPathRelativeToTopEquals(t, "install path for make", "out/target/product/test_device/system/install/path", p) 1492 }) 1493 t.Run("output", func(t *testing.T) { 1494 p := PathForOutput(ctx, "output/path") 1495 AssertPathRelativeToTopEquals(t, "output path", "out/soong/output/path", p) 1496 }) 1497 t.Run("source", func(t *testing.T) { 1498 p := PathForSource(ctx, "source/path") 1499 AssertPathRelativeToTopEquals(t, "source path", "source/path", p) 1500 }) 1501 t.Run("mixture", func(t *testing.T) { 1502 paths := Paths{ 1503 PathForModuleInstall(ctx, "install/path"), 1504 PathForOutput(ctx, "output/path"), 1505 PathForSource(ctx, "source/path"), 1506 } 1507 1508 expected := []string{ 1509 "out/soong/target/product/test_device/system/install/path", 1510 "out/soong/output/path", 1511 "source/path", 1512 } 1513 AssertPathsRelativeToTopEquals(t, "mixture", expected, paths) 1514 }) 1515} 1516 1517func ExampleOutputPath_ReplaceExtension() { 1518 ctx := &configErrorWrapper{ 1519 config: TestConfig("out", nil, "", nil), 1520 } 1521 p := PathForOutput(ctx, "system/framework").Join(ctx, "boot.art") 1522 p2 := p.ReplaceExtension(ctx, "oat") 1523 fmt.Println(p, p2) 1524 fmt.Println(p.Rel(), p2.Rel()) 1525 1526 // Output: 1527 // out/soong/system/framework/boot.art out/soong/system/framework/boot.oat 1528 // boot.art boot.oat 1529} 1530 1531func ExampleOutputPath_InSameDir() { 1532 ctx := &configErrorWrapper{ 1533 config: TestConfig("out", nil, "", nil), 1534 } 1535 p := PathForOutput(ctx, "system/framework").Join(ctx, "boot.art") 1536 p2 := p.InSameDir(ctx, "oat", "arm", "boot.vdex") 1537 fmt.Println(p, p2) 1538 fmt.Println(p.Rel(), p2.Rel()) 1539 1540 // Output: 1541 // out/soong/system/framework/boot.art out/soong/system/framework/oat/arm/boot.vdex 1542 // boot.art oat/arm/boot.vdex 1543} 1544 1545func BenchmarkFirstUniquePaths(b *testing.B) { 1546 implementations := []struct { 1547 name string 1548 f func(Paths) Paths 1549 }{ 1550 { 1551 name: "list", 1552 f: firstUniquePathsList, 1553 }, 1554 { 1555 name: "map", 1556 f: firstUniquePathsMap, 1557 }, 1558 } 1559 const maxSize = 1024 1560 uniquePaths := make(Paths, maxSize) 1561 for i := range uniquePaths { 1562 uniquePaths[i] = PathForTesting(strconv.Itoa(i)) 1563 } 1564 samePath := make(Paths, maxSize) 1565 for i := range samePath { 1566 samePath[i] = uniquePaths[0] 1567 } 1568 1569 f := func(b *testing.B, imp func(Paths) Paths, paths Paths) { 1570 for i := 0; i < b.N; i++ { 1571 b.ReportAllocs() 1572 paths = append(Paths(nil), paths...) 1573 imp(paths) 1574 } 1575 } 1576 1577 for n := 1; n <= maxSize; n <<= 1 { 1578 b.Run(strconv.Itoa(n), func(b *testing.B) { 1579 for _, implementation := range implementations { 1580 b.Run(implementation.name, func(b *testing.B) { 1581 b.Run("same", func(b *testing.B) { 1582 f(b, implementation.f, samePath[:n]) 1583 }) 1584 b.Run("unique", func(b *testing.B) { 1585 f(b, implementation.f, uniquePaths[:n]) 1586 }) 1587 }) 1588 } 1589 }) 1590 } 1591} 1592