1// Copyright 2017 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 build 16 17import ( 18 "bytes" 19 "context" 20 "fmt" 21 "io/ioutil" 22 "os" 23 "path/filepath" 24 "reflect" 25 "strings" 26 "testing" 27 28 "android/soong/ui/logger" 29 smpb "android/soong/ui/metrics/metrics_proto" 30 "android/soong/ui/status" 31 32 "google.golang.org/protobuf/encoding/prototext" 33 34 "google.golang.org/protobuf/proto" 35) 36 37func testContext() Context { 38 return Context{&ContextImpl{ 39 Context: context.Background(), 40 Logger: logger.New(&bytes.Buffer{}), 41 Writer: &bytes.Buffer{}, 42 Status: &status.Status{}, 43 }} 44} 45 46func TestConfigParseArgsJK(t *testing.T) { 47 ctx := testContext() 48 49 testCases := []struct { 50 args []string 51 52 parallel int 53 keepGoing int 54 remaining []string 55 }{ 56 {nil, -1, -1, nil}, 57 58 {[]string{"-j"}, -1, -1, nil}, 59 {[]string{"-j1"}, 1, -1, nil}, 60 {[]string{"-j1234"}, 1234, -1, nil}, 61 62 {[]string{"-j", "1"}, 1, -1, nil}, 63 {[]string{"-j", "1234"}, 1234, -1, nil}, 64 {[]string{"-j", "1234", "abc"}, 1234, -1, []string{"abc"}}, 65 {[]string{"-j", "abc"}, -1, -1, []string{"abc"}}, 66 {[]string{"-j", "1abc"}, -1, -1, []string{"1abc"}}, 67 68 {[]string{"-k"}, -1, 0, nil}, 69 {[]string{"-k0"}, -1, 0, nil}, 70 {[]string{"-k1"}, -1, 1, nil}, 71 {[]string{"-k1234"}, -1, 1234, nil}, 72 73 {[]string{"-k", "0"}, -1, 0, nil}, 74 {[]string{"-k", "1"}, -1, 1, nil}, 75 {[]string{"-k", "1234"}, -1, 1234, nil}, 76 {[]string{"-k", "1234", "abc"}, -1, 1234, []string{"abc"}}, 77 {[]string{"-k", "abc"}, -1, 0, []string{"abc"}}, 78 {[]string{"-k", "1abc"}, -1, 0, []string{"1abc"}}, 79 80 // TODO: These are supported in Make, should we support them? 81 //{[]string{"-kj"}, -1, 0}, 82 //{[]string{"-kj8"}, 8, 0}, 83 84 // -jk is not valid in Make 85 } 86 87 for _, tc := range testCases { 88 t.Run(strings.Join(tc.args, " "), func(t *testing.T) { 89 defer logger.Recover(func(err error) { 90 t.Fatal(err) 91 }) 92 93 env := Environment([]string{}) 94 c := &configImpl{ 95 environ: &env, 96 parallel: -1, 97 keepGoing: -1, 98 } 99 c.parseArgs(ctx, tc.args) 100 101 if c.parallel != tc.parallel { 102 t.Errorf("for %q, parallel:\nwant: %d\n got: %d\n", 103 strings.Join(tc.args, " "), 104 tc.parallel, c.parallel) 105 } 106 if c.keepGoing != tc.keepGoing { 107 t.Errorf("for %q, keep going:\nwant: %d\n got: %d\n", 108 strings.Join(tc.args, " "), 109 tc.keepGoing, c.keepGoing) 110 } 111 if !reflect.DeepEqual(c.arguments, tc.remaining) { 112 t.Errorf("for %q, remaining arguments:\nwant: %q\n got: %q\n", 113 strings.Join(tc.args, " "), 114 tc.remaining, c.arguments) 115 } 116 }) 117 } 118} 119 120func TestConfigParseArgsVars(t *testing.T) { 121 ctx := testContext() 122 123 testCases := []struct { 124 env []string 125 args []string 126 127 expectedEnv []string 128 remaining []string 129 }{ 130 {}, 131 { 132 env: []string{"A=bc"}, 133 134 expectedEnv: []string{"A=bc"}, 135 }, 136 { 137 args: []string{"abc"}, 138 139 remaining: []string{"abc"}, 140 }, 141 142 { 143 args: []string{"A=bc"}, 144 145 expectedEnv: []string{"A=bc"}, 146 }, 147 { 148 env: []string{"A=a"}, 149 args: []string{"A=bc"}, 150 151 expectedEnv: []string{"A=bc"}, 152 }, 153 154 { 155 env: []string{"A=a"}, 156 args: []string{"A=", "=b"}, 157 158 expectedEnv: []string{"A="}, 159 remaining: []string{"=b"}, 160 }, 161 } 162 163 for _, tc := range testCases { 164 t.Run(strings.Join(tc.args, " "), func(t *testing.T) { 165 defer logger.Recover(func(err error) { 166 t.Fatal(err) 167 }) 168 169 e := Environment(tc.env) 170 c := &configImpl{ 171 environ: &e, 172 } 173 c.parseArgs(ctx, tc.args) 174 175 if !reflect.DeepEqual([]string(*c.environ), tc.expectedEnv) { 176 t.Errorf("for env=%q args=%q, environment:\nwant: %q\n got: %q\n", 177 tc.env, tc.args, 178 tc.expectedEnv, []string(*c.environ)) 179 } 180 if !reflect.DeepEqual(c.arguments, tc.remaining) { 181 t.Errorf("for env=%q args=%q, remaining arguments:\nwant: %q\n got: %q\n", 182 tc.env, tc.args, 183 tc.remaining, c.arguments) 184 } 185 }) 186 } 187} 188 189func TestConfigCheckTopDir(t *testing.T) { 190 ctx := testContext() 191 buildRootDir := filepath.Dir(srcDirFileCheck) 192 expectedErrStr := fmt.Sprintf("Current working directory must be the source tree. %q not found.", srcDirFileCheck) 193 194 tests := []struct { 195 // ********* Setup ********* 196 // Test description. 197 description string 198 199 // ********* Action ********* 200 // If set to true, the build root file is created. 201 rootBuildFile bool 202 203 // The current path where Soong is being executed. 204 path string 205 206 // ********* Validation ********* 207 // Expecting error and validate the error string against expectedErrStr. 208 wantErr bool 209 }{{ 210 description: "current directory is the root source tree", 211 rootBuildFile: true, 212 path: ".", 213 wantErr: false, 214 }, { 215 description: "one level deep in the source tree", 216 rootBuildFile: true, 217 path: "1", 218 wantErr: true, 219 }, { 220 description: "very deep in the source tree", 221 rootBuildFile: true, 222 path: "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/7", 223 wantErr: true, 224 }, { 225 description: "outside of source tree", 226 rootBuildFile: false, 227 path: "1/2/3/4/5", 228 wantErr: true, 229 }} 230 231 for _, tt := range tests { 232 t.Run(tt.description, func(t *testing.T) { 233 defer logger.Recover(func(err error) { 234 if !tt.wantErr { 235 t.Fatalf("Got unexpected error: %v", err) 236 } 237 if expectedErrStr != err.Error() { 238 t.Fatalf("expected %s, got %s", expectedErrStr, err.Error()) 239 } 240 }) 241 242 // Create the root source tree. 243 rootDir, err := ioutil.TempDir("", "") 244 if err != nil { 245 t.Fatal(err) 246 } 247 defer os.RemoveAll(rootDir) 248 249 // Create the build root file. This is to test if topDir returns an error if the build root 250 // file does not exist. 251 if tt.rootBuildFile { 252 dir := filepath.Join(rootDir, buildRootDir) 253 if err := os.MkdirAll(dir, 0755); err != nil { 254 t.Errorf("failed to create %s directory: %v", dir, err) 255 } 256 f := filepath.Join(rootDir, srcDirFileCheck) 257 if err := ioutil.WriteFile(f, []byte{}, 0644); err != nil { 258 t.Errorf("failed to create file %s: %v", f, err) 259 } 260 } 261 262 // Next block of code is to set the current directory. 263 dir := rootDir 264 if tt.path != "" { 265 dir = filepath.Join(dir, tt.path) 266 if err := os.MkdirAll(dir, 0755); err != nil { 267 t.Errorf("failed to create %s directory: %v", dir, err) 268 } 269 } 270 curDir, err := os.Getwd() 271 if err != nil { 272 t.Fatalf("failed to get the current directory: %v", err) 273 } 274 defer func() { os.Chdir(curDir) }() 275 276 if err := os.Chdir(dir); err != nil { 277 t.Fatalf("failed to change directory to %s: %v", dir, err) 278 } 279 280 checkTopDir(ctx) 281 }) 282 } 283} 284 285func TestConfigConvertToTarget(t *testing.T) { 286 tests := []struct { 287 // ********* Setup ********* 288 // Test description. 289 description string 290 291 // ********* Action ********* 292 // The current directory where Soong is being executed. 293 dir string 294 295 // The current prefix string to be pre-appended to the target. 296 prefix string 297 298 // ********* Validation ********* 299 // The expected target to be invoked in ninja. 300 expectedTarget string 301 }{{ 302 description: "one level directory in source tree", 303 dir: "test1", 304 prefix: "MODULES-IN-", 305 expectedTarget: "MODULES-IN-test1", 306 }, { 307 description: "multiple level directories in source tree", 308 dir: "test1/test2/test3/test4", 309 prefix: "GET-INSTALL-PATH-IN-", 310 expectedTarget: "GET-INSTALL-PATH-IN-test1-test2-test3-test4", 311 }} 312 for _, tt := range tests { 313 t.Run(tt.description, func(t *testing.T) { 314 target := convertToTarget(tt.dir, tt.prefix) 315 if target != tt.expectedTarget { 316 t.Errorf("expected %s, got %s for target", tt.expectedTarget, target) 317 } 318 }) 319 } 320} 321 322func setTop(t *testing.T, dir string) func() { 323 curDir, err := os.Getwd() 324 if err != nil { 325 t.Fatalf("failed to get current directory: %v", err) 326 } 327 if err := os.Chdir(dir); err != nil { 328 t.Fatalf("failed to change directory to top dir %s: %v", dir, err) 329 } 330 return func() { os.Chdir(curDir) } 331} 332 333func createBuildFiles(t *testing.T, topDir string, buildFiles []string) { 334 for _, buildFile := range buildFiles { 335 buildFile = filepath.Join(topDir, buildFile) 336 if err := ioutil.WriteFile(buildFile, []byte{}, 0644); err != nil { 337 t.Errorf("failed to create file %s: %v", buildFile, err) 338 } 339 } 340} 341 342func createDirectories(t *testing.T, topDir string, dirs []string) { 343 for _, dir := range dirs { 344 dir = filepath.Join(topDir, dir) 345 if err := os.MkdirAll(dir, 0755); err != nil { 346 t.Errorf("failed to create %s directory: %v", dir, err) 347 } 348 } 349} 350 351func TestConfigGetTargets(t *testing.T) { 352 ctx := testContext() 353 tests := []struct { 354 // ********* Setup ********* 355 // Test description. 356 description string 357 358 // Directories that exist in the source tree. 359 dirsInTrees []string 360 361 // Build files that exists in the source tree. 362 buildFiles []string 363 364 // ********* Action ********* 365 // Directories passed in to soong_ui. 366 dirs []string 367 368 // Current directory that the user executed the build action command. 369 curDir string 370 371 // ********* Validation ********* 372 // Expected targets from the function. 373 expectedTargets []string 374 375 // Expecting error from running test case. 376 errStr string 377 }{{ 378 description: "one target dir specified", 379 dirsInTrees: []string{"0/1/2/3"}, 380 buildFiles: []string{"0/1/2/3/Android.bp"}, 381 dirs: []string{"1/2/3"}, 382 curDir: "0", 383 expectedTargets: []string{"MODULES-IN-0-1-2-3"}, 384 }, { 385 description: "one target dir specified, build file does not exist", 386 dirsInTrees: []string{"0/1/2/3"}, 387 buildFiles: []string{}, 388 dirs: []string{"1/2/3"}, 389 curDir: "0", 390 errStr: "Build file not found for 0/1/2/3 directory", 391 }, { 392 description: "one target dir specified, invalid targets specified", 393 dirsInTrees: []string{"0/1/2/3"}, 394 buildFiles: []string{}, 395 dirs: []string{"1/2/3:t1:t2"}, 396 curDir: "0", 397 errStr: "1/2/3:t1:t2 not in proper directory:target1,target2,... format (\":\" was specified more than once)", 398 }, { 399 description: "one target dir specified, no targets specified but has colon", 400 dirsInTrees: []string{"0/1/2/3"}, 401 buildFiles: []string{"0/1/2/3/Android.bp"}, 402 dirs: []string{"1/2/3:"}, 403 curDir: "0", 404 expectedTargets: []string{"MODULES-IN-0-1-2-3"}, 405 }, { 406 description: "one target dir specified, two targets specified", 407 dirsInTrees: []string{"0/1/2/3"}, 408 buildFiles: []string{"0/1/2/3/Android.bp"}, 409 dirs: []string{"1/2/3:t1,t2"}, 410 curDir: "0", 411 expectedTargets: []string{"t1", "t2"}, 412 }, { 413 description: "one target dir specified, no targets and has a comma", 414 dirsInTrees: []string{"0/1/2/3"}, 415 buildFiles: []string{"0/1/2/3/Android.bp"}, 416 dirs: []string{"1/2/3:,"}, 417 curDir: "0", 418 errStr: "0/1/2/3 not in proper directory:target1,target2,... format", 419 }, { 420 description: "one target dir specified, improper targets defined", 421 dirsInTrees: []string{"0/1/2/3"}, 422 buildFiles: []string{"0/1/2/3/Android.bp"}, 423 dirs: []string{"1/2/3:,t1"}, 424 curDir: "0", 425 errStr: "0/1/2/3 not in proper directory:target1,target2,... format", 426 }, { 427 description: "one target dir specified, blank target", 428 dirsInTrees: []string{"0/1/2/3"}, 429 buildFiles: []string{"0/1/2/3/Android.bp"}, 430 dirs: []string{"1/2/3:t1,"}, 431 curDir: "0", 432 errStr: "0/1/2/3 not in proper directory:target1,target2,... format", 433 }, { 434 description: "one target dir specified, many targets specified", 435 dirsInTrees: []string{"0/1/2/3"}, 436 buildFiles: []string{"0/1/2/3/Android.bp"}, 437 dirs: []string{"1/2/3:t1,t2,t3,t4,t5,t6,t7,t8,t9,t10"}, 438 curDir: "0", 439 expectedTargets: []string{"t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", "t10"}, 440 }, { 441 description: "one target dir specified, one target specified, build file does not exist", 442 dirsInTrees: []string{"0/1/2/3"}, 443 buildFiles: []string{}, 444 dirs: []string{"1/2/3:t1"}, 445 curDir: "0", 446 errStr: "Couldn't locate a build file from 0/1/2/3 directory", 447 }, { 448 description: "one target dir specified, one target specified, build file not in target dir", 449 dirsInTrees: []string{"0/1/2/3"}, 450 buildFiles: []string{"0/1/2/Android.mk"}, 451 dirs: []string{"1/2/3:t1"}, 452 curDir: "0", 453 errStr: "Couldn't locate a build file from 0/1/2/3 directory", 454 }, { 455 description: "one target dir specified, build file not in target dir", 456 dirsInTrees: []string{"0/1/2/3"}, 457 buildFiles: []string{"0/1/2/Android.mk"}, 458 dirs: []string{"1/2/3"}, 459 curDir: "0", 460 expectedTargets: []string{"MODULES-IN-0-1-2"}, 461 }, { 462 description: "multiple targets dir specified, targets specified", 463 dirsInTrees: []string{"0/1/2/3", "0/3/4"}, 464 buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"}, 465 dirs: []string{"1/2/3:t1,t2", "3/4:t3,t4,t5"}, 466 curDir: "0", 467 expectedTargets: []string{"t1", "t2", "t3", "t4", "t5"}, 468 }, { 469 description: "multiple targets dir specified, one directory has targets specified", 470 dirsInTrees: []string{"0/1/2/3", "0/3/4"}, 471 buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"}, 472 dirs: []string{"1/2/3:t1,t2", "3/4"}, 473 curDir: "0", 474 expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"}, 475 }, { 476 description: "two dirs specified, only one dir exist", 477 dirsInTrees: []string{"0/1/2/3"}, 478 buildFiles: []string{"0/1/2/3/Android.mk"}, 479 dirs: []string{"1/2/3:t1", "3/4"}, 480 curDir: "0", 481 errStr: "couldn't find directory 0/3/4", 482 }, { 483 description: "multiple targets dirs specified at root source tree", 484 dirsInTrees: []string{"0/1/2/3", "0/3/4"}, 485 buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"}, 486 dirs: []string{"0/1/2/3:t1,t2", "0/3/4"}, 487 curDir: ".", 488 expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"}, 489 }, { 490 description: "no directories specified", 491 dirsInTrees: []string{"0/1/2/3", "0/3/4"}, 492 buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"}, 493 dirs: []string{}, 494 curDir: ".", 495 }} 496 for _, tt := range tests { 497 t.Run(tt.description, func(t *testing.T) { 498 defer logger.Recover(func(err error) { 499 if tt.errStr == "" { 500 t.Fatalf("Got unexpected error: %v", err) 501 } 502 if tt.errStr != err.Error() { 503 t.Errorf("expected %s, got %s", tt.errStr, err.Error()) 504 } 505 }) 506 507 // Create the root source tree. 508 topDir, err := ioutil.TempDir("", "") 509 if err != nil { 510 t.Fatalf("failed to create temp dir: %v", err) 511 } 512 defer os.RemoveAll(topDir) 513 514 createDirectories(t, topDir, tt.dirsInTrees) 515 createBuildFiles(t, topDir, tt.buildFiles) 516 r := setTop(t, topDir) 517 defer r() 518 519 targets := getTargetsFromDirs(ctx, tt.curDir, tt.dirs, "MODULES-IN-") 520 if !reflect.DeepEqual(targets, tt.expectedTargets) { 521 t.Errorf("expected %v, got %v for targets", tt.expectedTargets, targets) 522 } 523 524 // If the execution reached here and there was an expected error code, the unit test case failed. 525 if tt.errStr != "" { 526 t.Errorf("expecting error %s", tt.errStr) 527 } 528 }) 529 } 530} 531 532func TestConfigFindBuildFile(t *testing.T) { 533 ctx := testContext() 534 535 tests := []struct { 536 // ********* Setup ********* 537 // Test description. 538 description string 539 540 // Array of build files to create in dir. 541 buildFiles []string 542 543 // Directories that exist in the source tree. 544 dirsInTrees []string 545 546 // ********* Action ********* 547 // The base directory is where findBuildFile is invoked. 548 dir string 549 550 // ********* Validation ********* 551 // Expected build file path to find. 552 expectedBuildFile string 553 }{{ 554 description: "build file exists at leaf directory", 555 buildFiles: []string{"1/2/3/Android.bp"}, 556 dirsInTrees: []string{"1/2/3"}, 557 dir: "1/2/3", 558 expectedBuildFile: "1/2/3/Android.mk", 559 }, { 560 description: "build file exists in all directory paths", 561 buildFiles: []string{"1/Android.mk", "1/2/Android.mk", "1/2/3/Android.mk"}, 562 dirsInTrees: []string{"1/2/3"}, 563 dir: "1/2/3", 564 expectedBuildFile: "1/2/3/Android.mk", 565 }, { 566 description: "build file does not exist in all directory paths", 567 buildFiles: []string{}, 568 dirsInTrees: []string{"1/2/3"}, 569 dir: "1/2/3", 570 expectedBuildFile: "", 571 }, { 572 description: "build file exists only at top directory", 573 buildFiles: []string{"Android.bp"}, 574 dirsInTrees: []string{"1/2/3"}, 575 dir: "1/2/3", 576 expectedBuildFile: "", 577 }, { 578 description: "build file exist in a subdirectory", 579 buildFiles: []string{"1/2/Android.bp"}, 580 dirsInTrees: []string{"1/2/3"}, 581 dir: "1/2/3", 582 expectedBuildFile: "1/2/Android.mk", 583 }, { 584 description: "build file exists in a subdirectory", 585 buildFiles: []string{"1/Android.mk"}, 586 dirsInTrees: []string{"1/2/3"}, 587 dir: "1/2/3", 588 expectedBuildFile: "1/Android.mk", 589 }, { 590 description: "top directory", 591 buildFiles: []string{"Android.bp"}, 592 dirsInTrees: []string{}, 593 dir: ".", 594 expectedBuildFile: "", 595 }, { 596 description: "build file exists in subdirectory", 597 buildFiles: []string{"1/2/3/Android.bp", "1/2/4/Android.bp"}, 598 dirsInTrees: []string{"1/2/3", "1/2/4"}, 599 dir: "1/2", 600 expectedBuildFile: "1/2/Android.mk", 601 }, { 602 description: "build file exists in parent subdirectory", 603 buildFiles: []string{"1/5/Android.bp"}, 604 dirsInTrees: []string{"1/2/3", "1/2/4", "1/5"}, 605 dir: "1/2", 606 expectedBuildFile: "1/Android.mk", 607 }, { 608 description: "build file exists in deep parent's subdirectory.", 609 buildFiles: []string{"1/5/6/Android.bp"}, 610 dirsInTrees: []string{"1/2/3", "1/2/4", "1/5/6", "1/5/7"}, 611 dir: "1/2", 612 expectedBuildFile: "1/Android.mk", 613 }} 614 615 for _, tt := range tests { 616 t.Run(tt.description, func(t *testing.T) { 617 defer logger.Recover(func(err error) { 618 t.Fatalf("Got unexpected error: %v", err) 619 }) 620 621 topDir, err := ioutil.TempDir("", "") 622 if err != nil { 623 t.Fatalf("failed to create temp dir: %v", err) 624 } 625 defer os.RemoveAll(topDir) 626 627 createDirectories(t, topDir, tt.dirsInTrees) 628 createBuildFiles(t, topDir, tt.buildFiles) 629 630 curDir, err := os.Getwd() 631 if err != nil { 632 t.Fatalf("Could not get working directory: %v", err) 633 } 634 defer func() { os.Chdir(curDir) }() 635 if err := os.Chdir(topDir); err != nil { 636 t.Fatalf("Could not change top dir to %s: %v", topDir, err) 637 } 638 639 buildFile := findBuildFile(ctx, tt.dir) 640 if buildFile != tt.expectedBuildFile { 641 t.Errorf("expected %q, got %q for build file", tt.expectedBuildFile, buildFile) 642 } 643 }) 644 } 645} 646 647func TestConfigSplitArgs(t *testing.T) { 648 tests := []struct { 649 // ********* Setup ********* 650 // Test description. 651 description string 652 653 // ********* Action ********* 654 // Arguments passed in to soong_ui. 655 args []string 656 657 // ********* Validation ********* 658 // Expected newArgs list after extracting the directories. 659 expectedNewArgs []string 660 661 // Expected directories 662 expectedDirs []string 663 }{{ 664 description: "flags but no directories specified", 665 args: []string{"showcommands", "-j", "-k"}, 666 expectedNewArgs: []string{"showcommands", "-j", "-k"}, 667 expectedDirs: []string{}, 668 }, { 669 description: "flags and one directory specified", 670 args: []string{"snod", "-j", "dir:target1,target2"}, 671 expectedNewArgs: []string{"snod", "-j"}, 672 expectedDirs: []string{"dir:target1,target2"}, 673 }, { 674 description: "flags and directories specified", 675 args: []string{"dist", "-k", "dir1", "dir2:target1,target2"}, 676 expectedNewArgs: []string{"dist", "-k"}, 677 expectedDirs: []string{"dir1", "dir2:target1,target2"}, 678 }, { 679 description: "only directories specified", 680 args: []string{"dir1", "dir2", "dir3:target1,target2"}, 681 expectedNewArgs: []string{}, 682 expectedDirs: []string{"dir1", "dir2", "dir3:target1,target2"}, 683 }} 684 for _, tt := range tests { 685 t.Run(tt.description, func(t *testing.T) { 686 args, dirs := splitArgs(tt.args) 687 if !reflect.DeepEqual(tt.expectedNewArgs, args) { 688 t.Errorf("expected %v, got %v for arguments", tt.expectedNewArgs, args) 689 } 690 if !reflect.DeepEqual(tt.expectedDirs, dirs) { 691 t.Errorf("expected %v, got %v for directories", tt.expectedDirs, dirs) 692 } 693 }) 694 } 695} 696 697type envVar struct { 698 name string 699 value string 700} 701 702type buildActionTestCase struct { 703 // ********* Setup ********* 704 // Test description. 705 description string 706 707 // Directories that exist in the source tree. 708 dirsInTrees []string 709 710 // Build files that exists in the source tree. 711 buildFiles []string 712 713 // Create root symlink that points to topDir. 714 rootSymlink bool 715 716 // ********* Action ********* 717 // Arguments passed in to soong_ui. 718 args []string 719 720 // Directory where the build action was invoked. 721 curDir string 722 723 // WITH_TIDY_ONLY environment variable specified. 724 tidyOnly string 725 726 // ********* Validation ********* 727 // Expected arguments to be in Config instance. 728 expectedArgs []string 729 730 // Expecting error from running test case. 731 expectedErrStr string 732} 733 734func testGetConfigArgs(t *testing.T, tt buildActionTestCase, action BuildAction) { 735 ctx := testContext() 736 737 defer logger.Recover(func(err error) { 738 if tt.expectedErrStr == "" { 739 t.Fatalf("Got unexpected error: %v", err) 740 } 741 if tt.expectedErrStr != err.Error() { 742 t.Errorf("expected %s, got %s", tt.expectedErrStr, err.Error()) 743 } 744 }) 745 746 // Environment variables to set it to blank on every test case run. 747 resetEnvVars := []string{ 748 "WITH_TIDY_ONLY", 749 } 750 751 for _, name := range resetEnvVars { 752 if err := os.Unsetenv(name); err != nil { 753 t.Fatalf("failed to unset environment variable %s: %v", name, err) 754 } 755 } 756 if tt.tidyOnly != "" { 757 if err := os.Setenv("WITH_TIDY_ONLY", tt.tidyOnly); err != nil { 758 t.Errorf("failed to set WITH_TIDY_ONLY to %s: %v", tt.tidyOnly, err) 759 } 760 } 761 762 // Create the root source tree. 763 topDir, err := ioutil.TempDir("", "") 764 if err != nil { 765 t.Fatalf("failed to create temp dir: %v", err) 766 } 767 defer os.RemoveAll(topDir) 768 769 createDirectories(t, topDir, tt.dirsInTrees) 770 createBuildFiles(t, topDir, tt.buildFiles) 771 772 if tt.rootSymlink { 773 // Create a secondary root source tree which points to the true root source tree. 774 symlinkTopDir, err := ioutil.TempDir("", "") 775 if err != nil { 776 t.Fatalf("failed to create symlink temp dir: %v", err) 777 } 778 defer os.RemoveAll(symlinkTopDir) 779 780 symlinkTopDir = filepath.Join(symlinkTopDir, "root") 781 err = os.Symlink(topDir, symlinkTopDir) 782 if err != nil { 783 t.Fatalf("failed to create symlink: %v", err) 784 } 785 topDir = symlinkTopDir 786 } 787 788 r := setTop(t, topDir) 789 defer r() 790 791 // The next block is to create the root build file. 792 rootBuildFileDir := filepath.Dir(srcDirFileCheck) 793 if err := os.MkdirAll(rootBuildFileDir, 0755); err != nil { 794 t.Fatalf("Failed to create %s directory: %v", rootBuildFileDir, err) 795 } 796 797 if err := ioutil.WriteFile(srcDirFileCheck, []byte{}, 0644); err != nil { 798 t.Fatalf("failed to create %s file: %v", srcDirFileCheck, err) 799 } 800 801 args := getConfigArgs(action, tt.curDir, ctx, tt.args) 802 if !reflect.DeepEqual(tt.expectedArgs, args) { 803 t.Fatalf("expected %v, got %v for config arguments", tt.expectedArgs, args) 804 } 805 806 // If the execution reached here and there was an expected error code, the unit test case failed. 807 if tt.expectedErrStr != "" { 808 t.Errorf("expecting error %s", tt.expectedErrStr) 809 } 810} 811 812func TestGetConfigArgsBuildModules(t *testing.T) { 813 tests := []buildActionTestCase{{ 814 description: "normal execution from the root source tree directory", 815 dirsInTrees: []string{"0/1/2", "0/2", "0/3"}, 816 buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp", "0/3/Android.mk"}, 817 args: []string{"-j", "fake_module", "fake_module2"}, 818 curDir: ".", 819 tidyOnly: "", 820 expectedArgs: []string{"-j", "fake_module", "fake_module2"}, 821 }, { 822 description: "normal execution in deep directory", 823 dirsInTrees: []string{"0/1/2", "0/2", "0/3", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6"}, 824 buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/Android.mk"}, 825 args: []string{"-j", "fake_module", "fake_module2", "-k"}, 826 curDir: "1/2/3/4/5/6/7/8/9", 827 tidyOnly: "", 828 expectedArgs: []string{"-j", "fake_module", "fake_module2", "-k"}, 829 }, { 830 description: "normal execution in deep directory, no targets", 831 dirsInTrees: []string{"0/1/2", "0/2", "0/3", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6"}, 832 buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/Android.mk"}, 833 args: []string{"-j", "-k"}, 834 curDir: "1/2/3/4/5/6/7/8/9", 835 tidyOnly: "", 836 expectedArgs: []string{"-j", "-k"}, 837 }, { 838 description: "normal execution in root source tree, no args", 839 dirsInTrees: []string{"0/1/2", "0/2", "0/3"}, 840 buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp"}, 841 args: []string{}, 842 curDir: "0/2", 843 tidyOnly: "", 844 expectedArgs: []string{}, 845 }, { 846 description: "normal execution in symlink root source tree, no args", 847 dirsInTrees: []string{"0/1/2", "0/2", "0/3"}, 848 buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp"}, 849 rootSymlink: true, 850 args: []string{}, 851 curDir: "0/2", 852 tidyOnly: "", 853 expectedArgs: []string{}, 854 }} 855 for _, tt := range tests { 856 t.Run("build action BUILD_MODULES with dependencies, "+tt.description, func(t *testing.T) { 857 testGetConfigArgs(t, tt, BUILD_MODULES) 858 }) 859 } 860} 861 862func TestGetConfigArgsBuildModulesInDirectory(t *testing.T) { 863 tests := []buildActionTestCase{{ 864 description: "normal execution in a directory", 865 dirsInTrees: []string{"0/1/2"}, 866 buildFiles: []string{"0/1/2/Android.mk"}, 867 args: []string{"fake-module"}, 868 curDir: "0/1/2", 869 tidyOnly: "", 870 expectedArgs: []string{"fake-module", "MODULES-IN-0-1-2"}, 871 }, { 872 description: "build file in parent directory", 873 dirsInTrees: []string{"0/1/2"}, 874 buildFiles: []string{"0/1/Android.mk"}, 875 args: []string{}, 876 curDir: "0/1/2", 877 tidyOnly: "", 878 expectedArgs: []string{"MODULES-IN-0-1"}, 879 }, 880 { 881 description: "build file in parent directory, multiple module names passed in", 882 dirsInTrees: []string{"0/1/2"}, 883 buildFiles: []string{"0/1/Android.mk"}, 884 args: []string{"fake-module1", "fake-module2", "fake-module3"}, 885 curDir: "0/1/2", 886 tidyOnly: "", 887 expectedArgs: []string{"fake-module1", "fake-module2", "fake-module3", "MODULES-IN-0-1"}, 888 }, { 889 description: "build file in 2nd level parent directory", 890 dirsInTrees: []string{"0/1/2"}, 891 buildFiles: []string{"0/Android.bp"}, 892 args: []string{}, 893 curDir: "0/1/2", 894 tidyOnly: "", 895 expectedArgs: []string{"MODULES-IN-0"}, 896 }, { 897 description: "build action executed at root directory", 898 dirsInTrees: []string{}, 899 buildFiles: []string{}, 900 rootSymlink: false, 901 args: []string{}, 902 curDir: ".", 903 tidyOnly: "", 904 expectedArgs: []string{}, 905 }, { 906 description: "multitree build action executed at root directory", 907 dirsInTrees: []string{}, 908 buildFiles: []string{}, 909 rootSymlink: false, 910 args: []string{"--multitree-build"}, 911 curDir: ".", 912 tidyOnly: "", 913 expectedArgs: []string{"--multitree-build"}, 914 }, { 915 description: "build action executed at root directory in symlink", 916 dirsInTrees: []string{}, 917 buildFiles: []string{}, 918 rootSymlink: true, 919 args: []string{}, 920 curDir: ".", 921 tidyOnly: "", 922 expectedArgs: []string{}, 923 }, { 924 description: "build file not found", 925 dirsInTrees: []string{"0/1/2"}, 926 buildFiles: []string{}, 927 args: []string{}, 928 curDir: "0/1/2", 929 tidyOnly: "", 930 expectedArgs: []string{"MODULES-IN-0-1-2"}, 931 expectedErrStr: "Build file not found for 0/1/2 directory", 932 }, { 933 description: "GET-INSTALL-PATH specified,", 934 dirsInTrees: []string{"0/1/2"}, 935 buildFiles: []string{"0/1/Android.mk"}, 936 args: []string{"GET-INSTALL-PATH", "-j", "-k", "GET-INSTALL-PATH"}, 937 curDir: "0/1/2", 938 tidyOnly: "", 939 expectedArgs: []string{"-j", "-k", "GET-INSTALL-PATH-IN-0-1"}, 940 }, { 941 description: "tidy only environment variable specified,", 942 dirsInTrees: []string{"0/1/2"}, 943 buildFiles: []string{"0/1/Android.mk"}, 944 args: []string{"GET-INSTALL-PATH"}, 945 curDir: "0/1/2", 946 tidyOnly: "true", 947 expectedArgs: []string{"tidy_only"}, 948 }, { 949 description: "normal execution in root directory with args", 950 dirsInTrees: []string{}, 951 buildFiles: []string{}, 952 args: []string{"-j", "-k", "fake_module"}, 953 curDir: "", 954 tidyOnly: "", 955 expectedArgs: []string{"-j", "-k", "fake_module"}, 956 }} 957 for _, tt := range tests { 958 t.Run("build action BUILD_MODULES_IN_DIR, "+tt.description, func(t *testing.T) { 959 testGetConfigArgs(t, tt, BUILD_MODULES_IN_A_DIRECTORY) 960 }) 961 } 962} 963 964func TestGetConfigArgsBuildModulesInDirectories(t *testing.T) { 965 tests := []buildActionTestCase{{ 966 description: "normal execution in a directory", 967 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"}, 968 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"}, 969 args: []string{"3.1/", "3.2/", "3.3/"}, 970 curDir: "0/1/2", 971 tidyOnly: "", 972 expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-2-3.3"}, 973 }, { 974 description: "GET-INSTALL-PATH specified", 975 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3"}, 976 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/Android.bp"}, 977 args: []string{"GET-INSTALL-PATH", "2/3.1/", "2/3.2", "3"}, 978 curDir: "0/1", 979 tidyOnly: "", 980 expectedArgs: []string{"GET-INSTALL-PATH-IN-0-1-2-3.1", "GET-INSTALL-PATH-IN-0-1-2-3.2", "GET-INSTALL-PATH-IN-0-1"}, 981 }, { 982 description: "tidy only environment variable specified", 983 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"}, 984 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"}, 985 args: []string{"GET-INSTALL-PATH", "3.1/", "3.2/", "3.3"}, 986 curDir: "0/1/2", 987 tidyOnly: "1", 988 expectedArgs: []string{"tidy_only"}, 989 }, { 990 description: "normal execution from top dir directory", 991 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"}, 992 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/3/Android.bp", "0/2/Android.bp"}, 993 rootSymlink: false, 994 args: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"}, 995 curDir: ".", 996 tidyOnly: "", 997 expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-3", "MODULES-IN-0-2"}, 998 }, { 999 description: "normal execution from top dir directory in symlink", 1000 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"}, 1001 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/3/Android.bp", "0/2/Android.bp"}, 1002 rootSymlink: true, 1003 args: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"}, 1004 curDir: ".", 1005 tidyOnly: "", 1006 expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-3", "MODULES-IN-0-2"}, 1007 }} 1008 for _, tt := range tests { 1009 t.Run("build action BUILD_MODULES_IN_DIRS, "+tt.description, func(t *testing.T) { 1010 testGetConfigArgs(t, tt, BUILD_MODULES_IN_DIRECTORIES) 1011 }) 1012 } 1013} 1014 1015func TestBuildConfig(t *testing.T) { 1016 tests := []struct { 1017 name string 1018 environ Environment 1019 arguments []string 1020 useBazel bool 1021 bazelDevMode bool 1022 bazelProdMode bool 1023 bazelStagingMode bool 1024 expectedBuildConfig *smpb.BuildConfig 1025 }{ 1026 { 1027 name: "none set", 1028 environ: Environment{}, 1029 expectedBuildConfig: &smpb.BuildConfig{ 1030 ForceUseGoma: proto.Bool(false), 1031 UseGoma: proto.Bool(false), 1032 UseRbe: proto.Bool(false), 1033 BazelMixedBuild: proto.Bool(false), 1034 ForceDisableBazelMixedBuild: proto.Bool(false), 1035 NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(), 1036 }, 1037 }, 1038 { 1039 name: "force use goma", 1040 environ: Environment{"FORCE_USE_GOMA=1"}, 1041 expectedBuildConfig: &smpb.BuildConfig{ 1042 ForceUseGoma: proto.Bool(true), 1043 UseGoma: proto.Bool(false), 1044 UseRbe: proto.Bool(false), 1045 BazelMixedBuild: proto.Bool(false), 1046 ForceDisableBazelMixedBuild: proto.Bool(false), 1047 NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(), 1048 }, 1049 }, 1050 { 1051 name: "use goma", 1052 environ: Environment{"USE_GOMA=1"}, 1053 expectedBuildConfig: &smpb.BuildConfig{ 1054 ForceUseGoma: proto.Bool(false), 1055 UseGoma: proto.Bool(true), 1056 UseRbe: proto.Bool(false), 1057 BazelMixedBuild: proto.Bool(false), 1058 ForceDisableBazelMixedBuild: proto.Bool(false), 1059 NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(), 1060 }, 1061 }, 1062 { 1063 name: "use rbe", 1064 environ: Environment{"USE_RBE=1"}, 1065 expectedBuildConfig: &smpb.BuildConfig{ 1066 ForceUseGoma: proto.Bool(false), 1067 UseGoma: proto.Bool(false), 1068 UseRbe: proto.Bool(true), 1069 BazelMixedBuild: proto.Bool(false), 1070 ForceDisableBazelMixedBuild: proto.Bool(false), 1071 NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(), 1072 }, 1073 }, 1074 { 1075 name: "disable mixed builds", 1076 environ: Environment{"BUILD_BROKEN_DISABLE_BAZEL=1"}, 1077 expectedBuildConfig: &smpb.BuildConfig{ 1078 ForceUseGoma: proto.Bool(false), 1079 UseGoma: proto.Bool(false), 1080 UseRbe: proto.Bool(false), 1081 BazelMixedBuild: proto.Bool(false), 1082 ForceDisableBazelMixedBuild: proto.Bool(true), 1083 NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(), 1084 }, 1085 }, 1086 { 1087 name: "use bazel as ninja", 1088 environ: Environment{}, 1089 useBazel: true, 1090 expectedBuildConfig: &smpb.BuildConfig{ 1091 ForceUseGoma: proto.Bool(false), 1092 UseGoma: proto.Bool(false), 1093 UseRbe: proto.Bool(false), 1094 BazelMixedBuild: proto.Bool(false), 1095 ForceDisableBazelMixedBuild: proto.Bool(false), 1096 NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(), 1097 }, 1098 }, 1099 { 1100 name: "bazel mixed build from dev mode", 1101 environ: Environment{}, 1102 bazelDevMode: true, 1103 expectedBuildConfig: &smpb.BuildConfig{ 1104 ForceUseGoma: proto.Bool(false), 1105 UseGoma: proto.Bool(false), 1106 UseRbe: proto.Bool(false), 1107 BazelMixedBuild: proto.Bool(true), 1108 ForceDisableBazelMixedBuild: proto.Bool(false), 1109 NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(), 1110 }, 1111 }, 1112 { 1113 name: "bazel mixed build from prod mode", 1114 environ: Environment{}, 1115 bazelProdMode: true, 1116 expectedBuildConfig: &smpb.BuildConfig{ 1117 ForceUseGoma: proto.Bool(false), 1118 UseGoma: proto.Bool(false), 1119 UseRbe: proto.Bool(false), 1120 BazelMixedBuild: proto.Bool(true), 1121 ForceDisableBazelMixedBuild: proto.Bool(false), 1122 NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(), 1123 }, 1124 }, 1125 { 1126 name: "bazel mixed build from staging mode", 1127 environ: Environment{}, 1128 bazelStagingMode: true, 1129 expectedBuildConfig: &smpb.BuildConfig{ 1130 ForceUseGoma: proto.Bool(false), 1131 UseGoma: proto.Bool(false), 1132 UseRbe: proto.Bool(false), 1133 BazelMixedBuild: proto.Bool(true), 1134 ForceDisableBazelMixedBuild: proto.Bool(false), 1135 NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(), 1136 }, 1137 }, 1138 { 1139 name: "specified targets", 1140 environ: Environment{}, 1141 useBazel: true, 1142 arguments: []string{"droid", "dist"}, 1143 expectedBuildConfig: &smpb.BuildConfig{ 1144 ForceUseGoma: proto.Bool(false), 1145 UseGoma: proto.Bool(false), 1146 UseRbe: proto.Bool(false), 1147 BazelMixedBuild: proto.Bool(false), 1148 Targets: []string{"droid", "dist"}, 1149 ForceDisableBazelMixedBuild: proto.Bool(false), 1150 NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(), 1151 }, 1152 }, 1153 { 1154 name: "all set", 1155 environ: Environment{ 1156 "FORCE_USE_GOMA=1", 1157 "USE_GOMA=1", 1158 "USE_RBE=1", 1159 "BUILD_BROKEN_DISABLE_BAZEL=1", 1160 }, 1161 useBazel: true, 1162 bazelDevMode: true, 1163 expectedBuildConfig: &smpb.BuildConfig{ 1164 ForceUseGoma: proto.Bool(true), 1165 UseGoma: proto.Bool(true), 1166 UseRbe: proto.Bool(true), 1167 BazelMixedBuild: proto.Bool(true), 1168 ForceDisableBazelMixedBuild: proto.Bool(true), 1169 NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(), 1170 }, 1171 }, 1172 } 1173 1174 ctx := testContext() 1175 for _, tc := range tests { 1176 t.Run(tc.name, func(t *testing.T) { 1177 c := &configImpl{ 1178 environ: &tc.environ, 1179 bazelDevMode: tc.bazelDevMode, 1180 bazelProdMode: tc.bazelProdMode, 1181 bazelStagingMode: tc.bazelStagingMode, 1182 arguments: tc.arguments, 1183 } 1184 config := Config{c} 1185 checkBazelMode(ctx, config) 1186 actualBuildConfig := buildConfig(config) 1187 if expected := tc.expectedBuildConfig; !proto.Equal(expected, actualBuildConfig) { 1188 t.Errorf("Build config mismatch.\n"+ 1189 "Expected build config: %#v\n"+ 1190 "Actual build config: %#v", prototext.Format(expected), prototext.Format(actualBuildConfig)) 1191 } 1192 }) 1193 } 1194} 1195 1196func TestGetMetricsUploaderApp(t *testing.T) { 1197 1198 metricsUploaderDir := "metrics_uploader_dir" 1199 metricsUploaderBinary := "metrics_uploader_binary" 1200 metricsUploaderPath := filepath.Join(metricsUploaderDir, metricsUploaderBinary) 1201 tests := []struct { 1202 description string 1203 environ Environment 1204 createFiles bool 1205 expected string 1206 }{{ 1207 description: "Uploader binary exist", 1208 environ: Environment{"METRICS_UPLOADER=" + metricsUploaderPath}, 1209 createFiles: true, 1210 expected: metricsUploaderPath, 1211 }, { 1212 description: "Uploader binary not exist", 1213 environ: Environment{"METRICS_UPLOADER=" + metricsUploaderPath}, 1214 createFiles: false, 1215 expected: "", 1216 }, { 1217 description: "Uploader binary variable not set", 1218 createFiles: true, 1219 expected: "", 1220 }} 1221 1222 for _, tt := range tests { 1223 t.Run(tt.description, func(t *testing.T) { 1224 defer logger.Recover(func(err error) { 1225 t.Fatalf("got unexpected error: %v", err) 1226 }) 1227 1228 // Create the root source tree. 1229 topDir, err := ioutil.TempDir("", "") 1230 if err != nil { 1231 t.Fatalf("failed to create temp dir: %v", err) 1232 } 1233 defer os.RemoveAll(topDir) 1234 1235 expected := tt.expected 1236 if len(expected) > 0 { 1237 expected = filepath.Join(topDir, expected) 1238 } 1239 1240 if tt.createFiles { 1241 if err := os.MkdirAll(filepath.Join(topDir, metricsUploaderDir), 0755); err != nil { 1242 t.Errorf("failed to create %s directory: %v", metricsUploaderDir, err) 1243 } 1244 if err := ioutil.WriteFile(filepath.Join(topDir, metricsUploaderPath), []byte{}, 0644); err != nil { 1245 t.Errorf("failed to create file %s: %v", expected, err) 1246 } 1247 } 1248 1249 actual := GetMetricsUploader(topDir, &tt.environ) 1250 1251 if actual != expected { 1252 t.Errorf("expecting: %s, actual: %s", expected, actual) 1253 } 1254 }) 1255 } 1256} 1257