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 "android/soong/ui/status" 30) 31 32func testContext() Context { 33 return Context{&ContextImpl{ 34 Context: context.Background(), 35 Logger: logger.New(&bytes.Buffer{}), 36 Writer: &bytes.Buffer{}, 37 Status: &status.Status{}, 38 }} 39} 40 41func TestConfigParseArgsJK(t *testing.T) { 42 ctx := testContext() 43 44 testCases := []struct { 45 args []string 46 47 parallel int 48 keepGoing int 49 remaining []string 50 }{ 51 {nil, -1, -1, nil}, 52 53 {[]string{"-j"}, -1, -1, nil}, 54 {[]string{"-j1"}, 1, -1, nil}, 55 {[]string{"-j1234"}, 1234, -1, nil}, 56 57 {[]string{"-j", "1"}, 1, -1, nil}, 58 {[]string{"-j", "1234"}, 1234, -1, nil}, 59 {[]string{"-j", "1234", "abc"}, 1234, -1, []string{"abc"}}, 60 {[]string{"-j", "abc"}, -1, -1, []string{"abc"}}, 61 {[]string{"-j", "1abc"}, -1, -1, []string{"1abc"}}, 62 63 {[]string{"-k"}, -1, 0, nil}, 64 {[]string{"-k0"}, -1, 0, nil}, 65 {[]string{"-k1"}, -1, 1, nil}, 66 {[]string{"-k1234"}, -1, 1234, nil}, 67 68 {[]string{"-k", "0"}, -1, 0, nil}, 69 {[]string{"-k", "1"}, -1, 1, nil}, 70 {[]string{"-k", "1234"}, -1, 1234, nil}, 71 {[]string{"-k", "1234", "abc"}, -1, 1234, []string{"abc"}}, 72 {[]string{"-k", "abc"}, -1, 0, []string{"abc"}}, 73 {[]string{"-k", "1abc"}, -1, 0, []string{"1abc"}}, 74 75 // TODO: These are supported in Make, should we support them? 76 //{[]string{"-kj"}, -1, 0}, 77 //{[]string{"-kj8"}, 8, 0}, 78 79 // -jk is not valid in Make 80 } 81 82 for _, tc := range testCases { 83 t.Run(strings.Join(tc.args, " "), func(t *testing.T) { 84 defer logger.Recover(func(err error) { 85 t.Fatal(err) 86 }) 87 88 c := &configImpl{ 89 parallel: -1, 90 keepGoing: -1, 91 } 92 c.parseArgs(ctx, tc.args) 93 94 if c.parallel != tc.parallel { 95 t.Errorf("for %q, parallel:\nwant: %d\n got: %d\n", 96 strings.Join(tc.args, " "), 97 tc.parallel, c.parallel) 98 } 99 if c.keepGoing != tc.keepGoing { 100 t.Errorf("for %q, keep going:\nwant: %d\n got: %d\n", 101 strings.Join(tc.args, " "), 102 tc.keepGoing, c.keepGoing) 103 } 104 if !reflect.DeepEqual(c.arguments, tc.remaining) { 105 t.Errorf("for %q, remaining arguments:\nwant: %q\n got: %q\n", 106 strings.Join(tc.args, " "), 107 tc.remaining, c.arguments) 108 } 109 }) 110 } 111} 112 113func TestConfigParseArgsVars(t *testing.T) { 114 ctx := testContext() 115 116 testCases := []struct { 117 env []string 118 args []string 119 120 expectedEnv []string 121 remaining []string 122 }{ 123 {}, 124 { 125 env: []string{"A=bc"}, 126 127 expectedEnv: []string{"A=bc"}, 128 }, 129 { 130 args: []string{"abc"}, 131 132 remaining: []string{"abc"}, 133 }, 134 135 { 136 args: []string{"A=bc"}, 137 138 expectedEnv: []string{"A=bc"}, 139 }, 140 { 141 env: []string{"A=a"}, 142 args: []string{"A=bc"}, 143 144 expectedEnv: []string{"A=bc"}, 145 }, 146 147 { 148 env: []string{"A=a"}, 149 args: []string{"A=", "=b"}, 150 151 expectedEnv: []string{"A="}, 152 remaining: []string{"=b"}, 153 }, 154 } 155 156 for _, tc := range testCases { 157 t.Run(strings.Join(tc.args, " "), func(t *testing.T) { 158 defer logger.Recover(func(err error) { 159 t.Fatal(err) 160 }) 161 162 e := Environment(tc.env) 163 c := &configImpl{ 164 environ: &e, 165 } 166 c.parseArgs(ctx, tc.args) 167 168 if !reflect.DeepEqual([]string(*c.environ), tc.expectedEnv) { 169 t.Errorf("for env=%q args=%q, environment:\nwant: %q\n got: %q\n", 170 tc.env, tc.args, 171 tc.expectedEnv, []string(*c.environ)) 172 } 173 if !reflect.DeepEqual(c.arguments, tc.remaining) { 174 t.Errorf("for env=%q args=%q, remaining arguments:\nwant: %q\n got: %q\n", 175 tc.env, tc.args, 176 tc.remaining, c.arguments) 177 } 178 }) 179 } 180} 181 182func TestConfigCheckTopDir(t *testing.T) { 183 ctx := testContext() 184 buildRootDir := filepath.Dir(srcDirFileCheck) 185 expectedErrStr := fmt.Sprintf("Current working directory must be the source tree. %q not found.", srcDirFileCheck) 186 187 tests := []struct { 188 // ********* Setup ********* 189 // Test description. 190 description string 191 192 // ********* Action ********* 193 // If set to true, the build root file is created. 194 rootBuildFile bool 195 196 // The current path where Soong is being executed. 197 path string 198 199 // ********* Validation ********* 200 // Expecting error and validate the error string against expectedErrStr. 201 wantErr bool 202 }{{ 203 description: "current directory is the root source tree", 204 rootBuildFile: true, 205 path: ".", 206 wantErr: false, 207 }, { 208 description: "one level deep in the source tree", 209 rootBuildFile: true, 210 path: "1", 211 wantErr: true, 212 }, { 213 description: "very deep in the source tree", 214 rootBuildFile: true, 215 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", 216 wantErr: true, 217 }, { 218 description: "outside of source tree", 219 rootBuildFile: false, 220 path: "1/2/3/4/5", 221 wantErr: true, 222 }} 223 224 for _, tt := range tests { 225 t.Run(tt.description, func(t *testing.T) { 226 defer logger.Recover(func(err error) { 227 if !tt.wantErr { 228 t.Fatalf("Got unexpected error: %v", err) 229 } 230 if expectedErrStr != err.Error() { 231 t.Fatalf("expected %s, got %s", expectedErrStr, err.Error()) 232 } 233 }) 234 235 // Create the root source tree. 236 rootDir, err := ioutil.TempDir("", "") 237 if err != nil { 238 t.Fatal(err) 239 } 240 defer os.RemoveAll(rootDir) 241 242 // Create the build root file. This is to test if topDir returns an error if the build root 243 // file does not exist. 244 if tt.rootBuildFile { 245 dir := filepath.Join(rootDir, buildRootDir) 246 if err := os.MkdirAll(dir, 0755); err != nil { 247 t.Errorf("failed to create %s directory: %v", dir, err) 248 } 249 f := filepath.Join(rootDir, srcDirFileCheck) 250 if err := ioutil.WriteFile(f, []byte{}, 0644); err != nil { 251 t.Errorf("failed to create file %s: %v", f, err) 252 } 253 } 254 255 // Next block of code is to set the current directory. 256 dir := rootDir 257 if tt.path != "" { 258 dir = filepath.Join(dir, tt.path) 259 if err := os.MkdirAll(dir, 0755); err != nil { 260 t.Errorf("failed to create %s directory: %v", dir, err) 261 } 262 } 263 curDir, err := os.Getwd() 264 if err != nil { 265 t.Fatalf("failed to get the current directory: %v", err) 266 } 267 defer func() { os.Chdir(curDir) }() 268 269 if err := os.Chdir(dir); err != nil { 270 t.Fatalf("failed to change directory to %s: %v", dir, err) 271 } 272 273 checkTopDir(ctx) 274 }) 275 } 276} 277 278func TestConfigConvertToTarget(t *testing.T) { 279 tests := []struct { 280 // ********* Setup ********* 281 // Test description. 282 description string 283 284 // ********* Action ********* 285 // The current directory where Soong is being executed. 286 dir string 287 288 // The current prefix string to be pre-appended to the target. 289 prefix string 290 291 // ********* Validation ********* 292 // The expected target to be invoked in ninja. 293 expectedTarget string 294 }{{ 295 description: "one level directory in source tree", 296 dir: "test1", 297 prefix: "MODULES-IN-", 298 expectedTarget: "MODULES-IN-test1", 299 }, { 300 description: "multiple level directories in source tree", 301 dir: "test1/test2/test3/test4", 302 prefix: "GET-INSTALL-PATH-IN-", 303 expectedTarget: "GET-INSTALL-PATH-IN-test1-test2-test3-test4", 304 }} 305 for _, tt := range tests { 306 t.Run(tt.description, func(t *testing.T) { 307 target := convertToTarget(tt.dir, tt.prefix) 308 if target != tt.expectedTarget { 309 t.Errorf("expected %s, got %s for target", tt.expectedTarget, target) 310 } 311 }) 312 } 313} 314 315func setTop(t *testing.T, dir string) func() { 316 curDir, err := os.Getwd() 317 if err != nil { 318 t.Fatalf("failed to get current directory: %v", err) 319 } 320 if err := os.Chdir(dir); err != nil { 321 t.Fatalf("failed to change directory to top dir %s: %v", dir, err) 322 } 323 return func() { os.Chdir(curDir) } 324} 325 326func createBuildFiles(t *testing.T, topDir string, buildFiles []string) { 327 for _, buildFile := range buildFiles { 328 buildFile = filepath.Join(topDir, buildFile) 329 if err := ioutil.WriteFile(buildFile, []byte{}, 0644); err != nil { 330 t.Errorf("failed to create file %s: %v", buildFile, err) 331 } 332 } 333} 334 335func createDirectories(t *testing.T, topDir string, dirs []string) { 336 for _, dir := range dirs { 337 dir = filepath.Join(topDir, dir) 338 if err := os.MkdirAll(dir, 0755); err != nil { 339 t.Errorf("failed to create %s directory: %v", dir, err) 340 } 341 } 342} 343 344func TestConfigGetTargets(t *testing.T) { 345 ctx := testContext() 346 tests := []struct { 347 // ********* Setup ********* 348 // Test description. 349 description string 350 351 // Directories that exist in the source tree. 352 dirsInTrees []string 353 354 // Build files that exists in the source tree. 355 buildFiles []string 356 357 // ********* Action ********* 358 // Directories passed in to soong_ui. 359 dirs []string 360 361 // Current directory that the user executed the build action command. 362 curDir string 363 364 // ********* Validation ********* 365 // Expected targets from the function. 366 expectedTargets []string 367 368 // Expecting error from running test case. 369 errStr string 370 }{{ 371 description: "one target dir specified", 372 dirsInTrees: []string{"0/1/2/3"}, 373 buildFiles: []string{"0/1/2/3/Android.bp"}, 374 dirs: []string{"1/2/3"}, 375 curDir: "0", 376 expectedTargets: []string{"MODULES-IN-0-1-2-3"}, 377 }, { 378 description: "one target dir specified, build file does not exist", 379 dirsInTrees: []string{"0/1/2/3"}, 380 buildFiles: []string{}, 381 dirs: []string{"1/2/3"}, 382 curDir: "0", 383 errStr: "Build file not found for 0/1/2/3 directory", 384 }, { 385 description: "one target dir specified, invalid targets specified", 386 dirsInTrees: []string{"0/1/2/3"}, 387 buildFiles: []string{}, 388 dirs: []string{"1/2/3:t1:t2"}, 389 curDir: "0", 390 errStr: "1/2/3:t1:t2 not in proper directory:target1,target2,... format (\":\" was specified more than once)", 391 }, { 392 description: "one target dir specified, no targets specified but has colon", 393 dirsInTrees: []string{"0/1/2/3"}, 394 buildFiles: []string{"0/1/2/3/Android.bp"}, 395 dirs: []string{"1/2/3:"}, 396 curDir: "0", 397 expectedTargets: []string{"MODULES-IN-0-1-2-3"}, 398 }, { 399 description: "one target dir specified, two targets specified", 400 dirsInTrees: []string{"0/1/2/3"}, 401 buildFiles: []string{"0/1/2/3/Android.bp"}, 402 dirs: []string{"1/2/3:t1,t2"}, 403 curDir: "0", 404 expectedTargets: []string{"t1", "t2"}, 405 }, { 406 description: "one target dir specified, no targets and has a comma", 407 dirsInTrees: []string{"0/1/2/3"}, 408 buildFiles: []string{"0/1/2/3/Android.bp"}, 409 dirs: []string{"1/2/3:,"}, 410 curDir: "0", 411 errStr: "0/1/2/3 not in proper directory:target1,target2,... format", 412 }, { 413 description: "one target dir specified, improper targets defined", 414 dirsInTrees: []string{"0/1/2/3"}, 415 buildFiles: []string{"0/1/2/3/Android.bp"}, 416 dirs: []string{"1/2/3:,t1"}, 417 curDir: "0", 418 errStr: "0/1/2/3 not in proper directory:target1,target2,... format", 419 }, { 420 description: "one target dir specified, blank target", 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, many targets specified", 428 dirsInTrees: []string{"0/1/2/3"}, 429 buildFiles: []string{"0/1/2/3/Android.bp"}, 430 dirs: []string{"1/2/3:t1,t2,t3,t4,t5,t6,t7,t8,t9,t10"}, 431 curDir: "0", 432 expectedTargets: []string{"t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", "t10"}, 433 }, { 434 description: "one target dir specified, one target specified, build file does not exist", 435 dirsInTrees: []string{"0/1/2/3"}, 436 buildFiles: []string{}, 437 dirs: []string{"1/2/3:t1"}, 438 curDir: "0", 439 errStr: "Couldn't locate a build file from 0/1/2/3 directory", 440 }, { 441 description: "one target dir specified, one target specified, build file not in target dir", 442 dirsInTrees: []string{"0/1/2/3"}, 443 buildFiles: []string{"0/1/2/Android.mk"}, 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, 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"}, 452 curDir: "0", 453 expectedTargets: []string{"MODULES-IN-0-1-2"}, 454 }, { 455 description: "multiple targets dir specified, targets specified", 456 dirsInTrees: []string{"0/1/2/3", "0/3/4"}, 457 buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"}, 458 dirs: []string{"1/2/3:t1,t2", "3/4:t3,t4,t5"}, 459 curDir: "0", 460 expectedTargets: []string{"t1", "t2", "t3", "t4", "t5"}, 461 }, { 462 description: "multiple targets dir specified, one directory has 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"}, 466 curDir: "0", 467 expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"}, 468 }, { 469 description: "two dirs specified, only one dir exist", 470 dirsInTrees: []string{"0/1/2/3"}, 471 buildFiles: []string{"0/1/2/3/Android.mk"}, 472 dirs: []string{"1/2/3:t1", "3/4"}, 473 curDir: "0", 474 errStr: "couldn't find directory 0/3/4", 475 }, { 476 description: "multiple targets dirs specified at root source tree", 477 dirsInTrees: []string{"0/1/2/3", "0/3/4"}, 478 buildFiles: []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"}, 479 dirs: []string{"0/1/2/3:t1,t2", "0/3/4"}, 480 curDir: ".", 481 expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"}, 482 }, { 483 description: "no directories specified", 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{}, 487 curDir: ".", 488 }} 489 for _, tt := range tests { 490 t.Run(tt.description, func(t *testing.T) { 491 defer logger.Recover(func(err error) { 492 if tt.errStr == "" { 493 t.Fatalf("Got unexpected error: %v", err) 494 } 495 if tt.errStr != err.Error() { 496 t.Errorf("expected %s, got %s", tt.errStr, err.Error()) 497 } 498 }) 499 500 // Create the root source tree. 501 topDir, err := ioutil.TempDir("", "") 502 if err != nil { 503 t.Fatalf("failed to create temp dir: %v", err) 504 } 505 defer os.RemoveAll(topDir) 506 507 createDirectories(t, topDir, tt.dirsInTrees) 508 createBuildFiles(t, topDir, tt.buildFiles) 509 r := setTop(t, topDir) 510 defer r() 511 512 targets := getTargetsFromDirs(ctx, tt.curDir, tt.dirs, "MODULES-IN-") 513 if !reflect.DeepEqual(targets, tt.expectedTargets) { 514 t.Errorf("expected %v, got %v for targets", tt.expectedTargets, targets) 515 } 516 517 // If the execution reached here and there was an expected error code, the unit test case failed. 518 if tt.errStr != "" { 519 t.Errorf("expecting error %s", tt.errStr) 520 } 521 }) 522 } 523} 524 525func TestConfigFindBuildFile(t *testing.T) { 526 ctx := testContext() 527 528 tests := []struct { 529 // ********* Setup ********* 530 // Test description. 531 description string 532 533 // Array of build files to create in dir. 534 buildFiles []string 535 536 // Directories that exist in the source tree. 537 dirsInTrees []string 538 539 // ********* Action ********* 540 // The base directory is where findBuildFile is invoked. 541 dir string 542 543 // ********* Validation ********* 544 // Expected build file path to find. 545 expectedBuildFile string 546 }{{ 547 description: "build file exists at leaf directory", 548 buildFiles: []string{"1/2/3/Android.bp"}, 549 dirsInTrees: []string{"1/2/3"}, 550 dir: "1/2/3", 551 expectedBuildFile: "1/2/3/Android.mk", 552 }, { 553 description: "build file exists in all directory paths", 554 buildFiles: []string{"1/Android.mk", "1/2/Android.mk", "1/2/3/Android.mk"}, 555 dirsInTrees: []string{"1/2/3"}, 556 dir: "1/2/3", 557 expectedBuildFile: "1/2/3/Android.mk", 558 }, { 559 description: "build file does not exist in all directory paths", 560 buildFiles: []string{}, 561 dirsInTrees: []string{"1/2/3"}, 562 dir: "1/2/3", 563 expectedBuildFile: "", 564 }, { 565 description: "build file exists only at top directory", 566 buildFiles: []string{"Android.bp"}, 567 dirsInTrees: []string{"1/2/3"}, 568 dir: "1/2/3", 569 expectedBuildFile: "", 570 }, { 571 description: "build file exist in a subdirectory", 572 buildFiles: []string{"1/2/Android.bp"}, 573 dirsInTrees: []string{"1/2/3"}, 574 dir: "1/2/3", 575 expectedBuildFile: "1/2/Android.mk", 576 }, { 577 description: "build file exists in a subdirectory", 578 buildFiles: []string{"1/Android.mk"}, 579 dirsInTrees: []string{"1/2/3"}, 580 dir: "1/2/3", 581 expectedBuildFile: "1/Android.mk", 582 }, { 583 description: "top directory", 584 buildFiles: []string{"Android.bp"}, 585 dirsInTrees: []string{}, 586 dir: ".", 587 expectedBuildFile: "", 588 }, { 589 description: "build file exists in subdirectory", 590 buildFiles: []string{"1/2/3/Android.bp", "1/2/4/Android.bp"}, 591 dirsInTrees: []string{"1/2/3", "1/2/4"}, 592 dir: "1/2", 593 expectedBuildFile: "1/2/Android.mk", 594 }, { 595 description: "build file exists in parent subdirectory", 596 buildFiles: []string{"1/5/Android.bp"}, 597 dirsInTrees: []string{"1/2/3", "1/2/4", "1/5"}, 598 dir: "1/2", 599 expectedBuildFile: "1/Android.mk", 600 }, { 601 description: "build file exists in deep parent's subdirectory.", 602 buildFiles: []string{"1/5/6/Android.bp"}, 603 dirsInTrees: []string{"1/2/3", "1/2/4", "1/5/6", "1/5/7"}, 604 dir: "1/2", 605 expectedBuildFile: "1/Android.mk", 606 }} 607 608 for _, tt := range tests { 609 t.Run(tt.description, func(t *testing.T) { 610 defer logger.Recover(func(err error) { 611 t.Fatalf("Got unexpected error: %v", err) 612 }) 613 614 topDir, err := ioutil.TempDir("", "") 615 if err != nil { 616 t.Fatalf("failed to create temp dir: %v", err) 617 } 618 defer os.RemoveAll(topDir) 619 620 createDirectories(t, topDir, tt.dirsInTrees) 621 createBuildFiles(t, topDir, tt.buildFiles) 622 623 curDir, err := os.Getwd() 624 if err != nil { 625 t.Fatalf("Could not get working directory: %v", err) 626 } 627 defer func() { os.Chdir(curDir) }() 628 if err := os.Chdir(topDir); err != nil { 629 t.Fatalf("Could not change top dir to %s: %v", topDir, err) 630 } 631 632 buildFile := findBuildFile(ctx, tt.dir) 633 if buildFile != tt.expectedBuildFile { 634 t.Errorf("expected %q, got %q for build file", tt.expectedBuildFile, buildFile) 635 } 636 }) 637 } 638} 639 640func TestConfigSplitArgs(t *testing.T) { 641 tests := []struct { 642 // ********* Setup ********* 643 // Test description. 644 description string 645 646 // ********* Action ********* 647 // Arguments passed in to soong_ui. 648 args []string 649 650 // ********* Validation ********* 651 // Expected newArgs list after extracting the directories. 652 expectedNewArgs []string 653 654 // Expected directories 655 expectedDirs []string 656 }{{ 657 description: "flags but no directories specified", 658 args: []string{"showcommands", "-j", "-k"}, 659 expectedNewArgs: []string{"showcommands", "-j", "-k"}, 660 expectedDirs: []string{}, 661 }, { 662 description: "flags and one directory specified", 663 args: []string{"snod", "-j", "dir:target1,target2"}, 664 expectedNewArgs: []string{"snod", "-j"}, 665 expectedDirs: []string{"dir:target1,target2"}, 666 }, { 667 description: "flags and directories specified", 668 args: []string{"dist", "-k", "dir1", "dir2:target1,target2"}, 669 expectedNewArgs: []string{"dist", "-k"}, 670 expectedDirs: []string{"dir1", "dir2:target1,target2"}, 671 }, { 672 description: "only directories specified", 673 args: []string{"dir1", "dir2", "dir3:target1,target2"}, 674 expectedNewArgs: []string{}, 675 expectedDirs: []string{"dir1", "dir2", "dir3:target1,target2"}, 676 }} 677 for _, tt := range tests { 678 t.Run(tt.description, func(t *testing.T) { 679 args, dirs := splitArgs(tt.args) 680 if !reflect.DeepEqual(tt.expectedNewArgs, args) { 681 t.Errorf("expected %v, got %v for arguments", tt.expectedNewArgs, args) 682 } 683 if !reflect.DeepEqual(tt.expectedDirs, dirs) { 684 t.Errorf("expected %v, got %v for directories", tt.expectedDirs, dirs) 685 } 686 }) 687 } 688} 689 690type envVar struct { 691 name string 692 value string 693} 694 695type buildActionTestCase struct { 696 // ********* Setup ********* 697 // Test description. 698 description string 699 700 // Directories that exist in the source tree. 701 dirsInTrees []string 702 703 // Build files that exists in the source tree. 704 buildFiles []string 705 706 // Create root symlink that points to topDir. 707 rootSymlink bool 708 709 // ********* Action ********* 710 // Arguments passed in to soong_ui. 711 args []string 712 713 // Directory where the build action was invoked. 714 curDir string 715 716 // WITH_TIDY_ONLY environment variable specified. 717 tidyOnly string 718 719 // ********* Validation ********* 720 // Expected arguments to be in Config instance. 721 expectedArgs []string 722 723 // Expecting error from running test case. 724 expectedErrStr string 725} 726 727func testGetConfigArgs(t *testing.T, tt buildActionTestCase, action BuildAction) { 728 ctx := testContext() 729 730 defer logger.Recover(func(err error) { 731 if tt.expectedErrStr == "" { 732 t.Fatalf("Got unexpected error: %v", err) 733 } 734 if tt.expectedErrStr != err.Error() { 735 t.Errorf("expected %s, got %s", tt.expectedErrStr, err.Error()) 736 } 737 }) 738 739 // Environment variables to set it to blank on every test case run. 740 resetEnvVars := []string{ 741 "WITH_TIDY_ONLY", 742 } 743 744 for _, name := range resetEnvVars { 745 if err := os.Unsetenv(name); err != nil { 746 t.Fatalf("failed to unset environment variable %s: %v", name, err) 747 } 748 } 749 if tt.tidyOnly != "" { 750 if err := os.Setenv("WITH_TIDY_ONLY", tt.tidyOnly); err != nil { 751 t.Errorf("failed to set WITH_TIDY_ONLY to %s: %v", tt.tidyOnly, err) 752 } 753 } 754 755 // Create the root source tree. 756 topDir, err := ioutil.TempDir("", "") 757 if err != nil { 758 t.Fatalf("failed to create temp dir: %v", err) 759 } 760 defer os.RemoveAll(topDir) 761 762 createDirectories(t, topDir, tt.dirsInTrees) 763 createBuildFiles(t, topDir, tt.buildFiles) 764 765 if tt.rootSymlink { 766 // Create a secondary root source tree which points to the true root source tree. 767 symlinkTopDir, err := ioutil.TempDir("", "") 768 if err != nil { 769 t.Fatalf("failed to create symlink temp dir: %v", err) 770 } 771 defer os.RemoveAll(symlinkTopDir) 772 773 symlinkTopDir = filepath.Join(symlinkTopDir, "root") 774 err = os.Symlink(topDir, symlinkTopDir) 775 if err != nil { 776 t.Fatalf("failed to create symlink: %v", err) 777 } 778 topDir = symlinkTopDir 779 } 780 781 r := setTop(t, topDir) 782 defer r() 783 784 // The next block is to create the root build file. 785 rootBuildFileDir := filepath.Dir(srcDirFileCheck) 786 if err := os.MkdirAll(rootBuildFileDir, 0755); err != nil { 787 t.Fatalf("Failed to create %s directory: %v", rootBuildFileDir, err) 788 } 789 790 if err := ioutil.WriteFile(srcDirFileCheck, []byte{}, 0644); err != nil { 791 t.Fatalf("failed to create %s file: %v", srcDirFileCheck, err) 792 } 793 794 args := getConfigArgs(action, tt.curDir, ctx, tt.args) 795 if !reflect.DeepEqual(tt.expectedArgs, args) { 796 t.Fatalf("expected %v, got %v for config arguments", tt.expectedArgs, args) 797 } 798 799 // If the execution reached here and there was an expected error code, the unit test case failed. 800 if tt.expectedErrStr != "" { 801 t.Errorf("expecting error %s", tt.expectedErrStr) 802 } 803} 804 805func TestGetConfigArgsBuildModules(t *testing.T) { 806 tests := []buildActionTestCase{{ 807 description: "normal execution from the root source tree directory", 808 dirsInTrees: []string{"0/1/2", "0/2", "0/3"}, 809 buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp", "0/3/Android.mk"}, 810 args: []string{"-j", "fake_module", "fake_module2"}, 811 curDir: ".", 812 tidyOnly: "", 813 expectedArgs: []string{"-j", "fake_module", "fake_module2"}, 814 }, { 815 description: "normal execution in deep directory", 816 dirsInTrees: []string{"0/1/2", "0/2", "0/3", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6"}, 817 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"}, 818 args: []string{"-j", "fake_module", "fake_module2", "-k"}, 819 curDir: "1/2/3/4/5/6/7/8/9", 820 tidyOnly: "", 821 expectedArgs: []string{"-j", "fake_module", "fake_module2", "-k"}, 822 }, { 823 description: "normal execution in deep directory, no targets", 824 dirsInTrees: []string{"0/1/2", "0/2", "0/3", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6"}, 825 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"}, 826 args: []string{"-j", "-k"}, 827 curDir: "1/2/3/4/5/6/7/8/9", 828 tidyOnly: "", 829 expectedArgs: []string{"-j", "-k"}, 830 }, { 831 description: "normal execution in root source tree, no args", 832 dirsInTrees: []string{"0/1/2", "0/2", "0/3"}, 833 buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp"}, 834 args: []string{}, 835 curDir: "0/2", 836 tidyOnly: "", 837 expectedArgs: []string{}, 838 }, { 839 description: "normal execution in symlink root source tree, no args", 840 dirsInTrees: []string{"0/1/2", "0/2", "0/3"}, 841 buildFiles: []string{"0/1/2/Android.mk", "0/2/Android.bp"}, 842 rootSymlink: true, 843 args: []string{}, 844 curDir: "0/2", 845 tidyOnly: "", 846 expectedArgs: []string{}, 847 }} 848 for _, tt := range tests { 849 t.Run("build action BUILD_MODULES with dependencies, "+tt.description, func(t *testing.T) { 850 testGetConfigArgs(t, tt, BUILD_MODULES) 851 }) 852 } 853} 854 855func TestGetConfigArgsBuildModulesInDirectory(t *testing.T) { 856 tests := []buildActionTestCase{{ 857 description: "normal execution in a directory", 858 dirsInTrees: []string{"0/1/2"}, 859 buildFiles: []string{"0/1/2/Android.mk"}, 860 args: []string{"fake-module"}, 861 curDir: "0/1/2", 862 tidyOnly: "", 863 expectedArgs: []string{"fake-module", "MODULES-IN-0-1-2"}, 864 }, { 865 description: "build file in parent directory", 866 dirsInTrees: []string{"0/1/2"}, 867 buildFiles: []string{"0/1/Android.mk"}, 868 args: []string{}, 869 curDir: "0/1/2", 870 tidyOnly: "", 871 expectedArgs: []string{"MODULES-IN-0-1"}, 872 }, 873 { 874 description: "build file in parent directory, multiple module names passed in", 875 dirsInTrees: []string{"0/1/2"}, 876 buildFiles: []string{"0/1/Android.mk"}, 877 args: []string{"fake-module1", "fake-module2", "fake-module3"}, 878 curDir: "0/1/2", 879 tidyOnly: "", 880 expectedArgs: []string{"fake-module1", "fake-module2", "fake-module3", "MODULES-IN-0-1"}, 881 }, { 882 description: "build file in 2nd level parent directory", 883 dirsInTrees: []string{"0/1/2"}, 884 buildFiles: []string{"0/Android.bp"}, 885 args: []string{}, 886 curDir: "0/1/2", 887 tidyOnly: "", 888 expectedArgs: []string{"MODULES-IN-0"}, 889 }, { 890 description: "build action executed at root directory", 891 dirsInTrees: []string{}, 892 buildFiles: []string{}, 893 rootSymlink: false, 894 args: []string{}, 895 curDir: ".", 896 tidyOnly: "", 897 expectedArgs: []string{}, 898 }, { 899 description: "build action executed at root directory in symlink", 900 dirsInTrees: []string{}, 901 buildFiles: []string{}, 902 rootSymlink: true, 903 args: []string{}, 904 curDir: ".", 905 tidyOnly: "", 906 expectedArgs: []string{}, 907 }, { 908 description: "build file not found", 909 dirsInTrees: []string{"0/1/2"}, 910 buildFiles: []string{}, 911 args: []string{}, 912 curDir: "0/1/2", 913 tidyOnly: "", 914 expectedArgs: []string{"MODULES-IN-0-1-2"}, 915 expectedErrStr: "Build file not found for 0/1/2 directory", 916 }, { 917 description: "GET-INSTALL-PATH specified,", 918 dirsInTrees: []string{"0/1/2"}, 919 buildFiles: []string{"0/1/Android.mk"}, 920 args: []string{"GET-INSTALL-PATH", "-j", "-k", "GET-INSTALL-PATH"}, 921 curDir: "0/1/2", 922 tidyOnly: "", 923 expectedArgs: []string{"-j", "-k", "GET-INSTALL-PATH-IN-0-1"}, 924 }, { 925 description: "tidy only environment variable specified,", 926 dirsInTrees: []string{"0/1/2"}, 927 buildFiles: []string{"0/1/Android.mk"}, 928 args: []string{"GET-INSTALL-PATH"}, 929 curDir: "0/1/2", 930 tidyOnly: "true", 931 expectedArgs: []string{"tidy_only"}, 932 }, { 933 description: "normal execution in root directory with args", 934 dirsInTrees: []string{}, 935 buildFiles: []string{}, 936 args: []string{"-j", "-k", "fake_module"}, 937 curDir: "", 938 tidyOnly: "", 939 expectedArgs: []string{"-j", "-k", "fake_module"}, 940 }} 941 for _, tt := range tests { 942 t.Run("build action BUILD_MODULES_IN_DIR, "+tt.description, func(t *testing.T) { 943 testGetConfigArgs(t, tt, BUILD_MODULES_IN_A_DIRECTORY) 944 }) 945 } 946} 947 948func TestGetConfigArgsBuildModulesInDirectories(t *testing.T) { 949 tests := []buildActionTestCase{{ 950 description: "normal execution in a directory", 951 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"}, 952 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"}, 953 args: []string{"3.1/", "3.2/", "3.3/"}, 954 curDir: "0/1/2", 955 tidyOnly: "", 956 expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-2-3.3"}, 957 }, { 958 description: "GET-INSTALL-PATH specified", 959 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3"}, 960 buildFiles: []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/Android.bp"}, 961 args: []string{"GET-INSTALL-PATH", "2/3.1/", "2/3.2", "3"}, 962 curDir: "0/1", 963 tidyOnly: "", 964 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"}, 965 }, { 966 description: "tidy only environment variable specified", 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{"GET-INSTALL-PATH", "3.1/", "3.2/", "3.3"}, 970 curDir: "0/1/2", 971 tidyOnly: "1", 972 expectedArgs: []string{"tidy_only"}, 973 }, { 974 description: "normal execution from top dir directory", 975 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"}, 976 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"}, 977 rootSymlink: false, 978 args: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"}, 979 curDir: ".", 980 tidyOnly: "", 981 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"}, 982 }, { 983 description: "normal execution from top dir directory in symlink", 984 dirsInTrees: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"}, 985 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"}, 986 rootSymlink: true, 987 args: []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"}, 988 curDir: ".", 989 tidyOnly: "", 990 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"}, 991 }} 992 for _, tt := range tests { 993 t.Run("build action BUILD_MODULES_IN_DIRS, "+tt.description, func(t *testing.T) { 994 testGetConfigArgs(t, tt, BUILD_MODULES_IN_DIRECTORIES) 995 }) 996 } 997} 998