1package mage 2 3import ( 4 "bytes" 5 "debug/macho" 6 "debug/pe" 7 "flag" 8 "fmt" 9 "go/build" 10 "go/parser" 11 "go/token" 12 "io" 13 "io/ioutil" 14 "log" 15 "os" 16 "os/exec" 17 "path/filepath" 18 "reflect" 19 "regexp" 20 "runtime" 21 "strconv" 22 "strings" 23 "testing" 24 "time" 25 26 "github.com/magefile/mage/internal" 27 "github.com/magefile/mage/mg" 28) 29 30const testExeEnv = "MAGE_TEST_STRING" 31 32func TestMain(m *testing.M) { 33 if s := os.Getenv(testExeEnv); s != "" { 34 fmt.Fprint(os.Stdout, s) 35 os.Exit(0) 36 } 37 os.Exit(testmain(m)) 38} 39 40func testmain(m *testing.M) int { 41 // ensure we write our temporary binaries to a directory that we'll delete 42 // after running tests. 43 dir, err := ioutil.TempDir("", "") 44 if err != nil { 45 log.Fatal(err) 46 } 47 defer os.RemoveAll(dir) 48 if err := os.Setenv(mg.CacheEnv, dir); err != nil { 49 log.Fatal(err) 50 } 51 if err := os.Unsetenv(mg.VerboseEnv); err != nil { 52 log.Fatal(err) 53 } 54 if err := os.Unsetenv(mg.DebugEnv); err != nil { 55 log.Fatal(err) 56 } 57 if err := os.Unsetenv(mg.IgnoreDefaultEnv); err != nil { 58 log.Fatal(err) 59 } 60 if err := os.Setenv(mg.CacheEnv, dir); err != nil { 61 log.Fatal(err) 62 } 63 if err := os.Unsetenv(mg.EnableColorEnv); err != nil { 64 log.Fatal(err) 65 } 66 if err := os.Unsetenv(mg.TargetColorEnv); err != nil { 67 log.Fatal(err) 68 } 69 resetTerm() 70 return m.Run() 71} 72 73func resetTerm() { 74 if term, exists := os.LookupEnv("TERM"); exists { 75 log.Printf("Current terminal: %s", term) 76 // unset TERM env var in order to disable color output to make the tests simpler 77 // there is a specific test for colorized output, so all the other tests can use non-colorized one 78 if err := os.Unsetenv("TERM"); err != nil { 79 log.Fatal(err) 80 } 81 } 82 os.Setenv(mg.EnableColorEnv, "false") 83} 84 85func TestTransitiveDepCache(t *testing.T) { 86 cache, err := internal.OutputDebug("go", "env", "GOCACHE") 87 if err != nil { 88 t.Fatal(err) 89 } 90 if cache == "" { 91 t.Skip("skipping gocache tests on go version without cache") 92 } 93 // Test that if we change a transitive dep, that we recompile 94 stdout := &bytes.Buffer{} 95 stderr := &bytes.Buffer{} 96 inv := Invocation{ 97 Stderr: stderr, 98 Stdout: stdout, 99 Dir: "testdata/transitiveDeps", 100 Args: []string{"Run"}, 101 } 102 code := Invoke(inv) 103 if code != 0 { 104 t.Fatalf("got code %v, err: %s", code, stderr) 105 } 106 expected := "woof\n" 107 if actual := stdout.String(); actual != expected { 108 t.Fatalf("expected %q but got %q", expected, actual) 109 } 110 // ok, so baseline, the generated and cached binary should do "woof" 111 // now change out the transitive dependency that does the output 112 // so that it produces different output. 113 if err := os.Rename("testdata/transitiveDeps/dep/dog.go", "testdata/transitiveDeps/dep/dog.notgo"); err != nil { 114 t.Fatal(err) 115 } 116 defer os.Rename("testdata/transitiveDeps/dep/dog.notgo", "testdata/transitiveDeps/dep/dog.go") 117 if err := os.Rename("testdata/transitiveDeps/dep/cat.notgo", "testdata/transitiveDeps/dep/cat.go"); err != nil { 118 t.Fatal(err) 119 } 120 defer os.Rename("testdata/transitiveDeps/dep/cat.go", "testdata/transitiveDeps/dep/cat.notgo") 121 stderr.Reset() 122 stdout.Reset() 123 code = Invoke(inv) 124 if code != 0 { 125 t.Fatalf("got code %v, err: %s", code, stderr) 126 } 127 expected = "meow\n" 128 if actual := stdout.String(); actual != expected { 129 t.Fatalf("expected %q but got %q", expected, actual) 130 } 131} 132 133func TestTransitiveHashFast(t *testing.T) { 134 cache, err := internal.OutputDebug("go", "env", "GOCACHE") 135 if err != nil { 136 t.Fatal(err) 137 } 138 if cache == "" { 139 t.Skip("skipping hashfast tests on go version without cache") 140 } 141 142 // Test that if we change a transitive dep, that we don't recompile. 143 // We intentionally run the first time without hashfast to ensure that 144 // we recompile the binary with the current code. 145 stdout := &bytes.Buffer{} 146 stderr := &bytes.Buffer{} 147 inv := Invocation{ 148 Stderr: stderr, 149 Stdout: stdout, 150 Dir: "testdata/transitiveDeps", 151 Args: []string{"Run"}, 152 } 153 code := Invoke(inv) 154 if code != 0 { 155 t.Fatalf("got code %v, err: %s", code, stderr) 156 } 157 expected := "woof\n" 158 if actual := stdout.String(); actual != expected { 159 t.Fatalf("expected %q but got %q", expected, actual) 160 } 161 162 // ok, so baseline, the generated and cached binary should do "woof" 163 // now change out the transitive dependency that does the output 164 // so that it produces different output. 165 if err := os.Rename("testdata/transitiveDeps/dep/dog.go", "testdata/transitiveDeps/dep/dog.notgo"); err != nil { 166 t.Fatal(err) 167 } 168 defer os.Rename("testdata/transitiveDeps/dep/dog.notgo", "testdata/transitiveDeps/dep/dog.go") 169 if err := os.Rename("testdata/transitiveDeps/dep/cat.notgo", "testdata/transitiveDeps/dep/cat.go"); err != nil { 170 t.Fatal(err) 171 } 172 defer os.Rename("testdata/transitiveDeps/dep/cat.go", "testdata/transitiveDeps/dep/cat.notgo") 173 stderr.Reset() 174 stdout.Reset() 175 inv.HashFast = true 176 code = Invoke(inv) 177 if code != 0 { 178 t.Fatalf("got code %v, err: %s", code, stderr) 179 } 180 // we should still get woof, even though the dependency was changed to 181 // return "meow", because we're only hashing the top level magefiles, not 182 // dependencies. 183 if actual := stdout.String(); actual != expected { 184 t.Fatalf("expected %q but got %q", expected, actual) 185 } 186} 187 188func TestListMagefilesMain(t *testing.T) { 189 buf := &bytes.Buffer{} 190 files, err := Magefiles("testdata/mixed_main_files", "", "", "go", buf, false) 191 if err != nil { 192 t.Errorf("error from magefile list: %v: %s", err, buf) 193 } 194 expected := []string{ 195 filepath.FromSlash("testdata/mixed_main_files/mage_helpers.go"), 196 filepath.FromSlash("testdata/mixed_main_files/magefile.go"), 197 } 198 if !reflect.DeepEqual(files, expected) { 199 t.Fatalf("expected %q but got %q", expected, files) 200 } 201} 202 203func TestListMagefilesIgnoresGOOS(t *testing.T) { 204 buf := &bytes.Buffer{} 205 if runtime.GOOS == "windows" { 206 os.Setenv("GOOS", "linux") 207 } else { 208 os.Setenv("GOOS", "windows") 209 } 210 defer os.Setenv("GOOS", runtime.GOOS) 211 files, err := Magefiles("testdata/goos_magefiles", "", "", "go", buf, false) 212 if err != nil { 213 t.Errorf("error from magefile list: %v: %s", err, buf) 214 } 215 var expected []string 216 if runtime.GOOS == "windows" { 217 expected = []string{filepath.FromSlash("testdata/goos_magefiles/magefile_windows.go")} 218 } else { 219 expected = []string{filepath.FromSlash("testdata/goos_magefiles/magefile_nonwindows.go")} 220 } 221 if !reflect.DeepEqual(files, expected) { 222 t.Fatalf("expected %q but got %q", expected, files) 223 } 224} 225 226func TestListMagefilesIgnoresRespectsGOOSArg(t *testing.T) { 227 buf := &bytes.Buffer{} 228 var goos string 229 if runtime.GOOS == "windows" { 230 goos = "linux" 231 } else { 232 goos = "windows" 233 } 234 // Set GOARCH as amd64 because windows is not on all non-x86 architectures. 235 files, err := Magefiles("testdata/goos_magefiles", goos, "amd64", "go", buf, false) 236 if err != nil { 237 t.Errorf("error from magefile list: %v: %s", err, buf) 238 } 239 var expected []string 240 if goos == "windows" { 241 expected = []string{filepath.FromSlash("testdata/goos_magefiles/magefile_windows.go")} 242 } else { 243 expected = []string{filepath.FromSlash("testdata/goos_magefiles/magefile_nonwindows.go")} 244 } 245 if !reflect.DeepEqual(files, expected) { 246 t.Fatalf("expected %q but got %q", expected, files) 247 } 248} 249 250func TestCompileDiffGoosGoarch(t *testing.T) { 251 target, err := ioutil.TempDir("./testdata", "") 252 if err != nil { 253 t.Fatal(err) 254 } 255 defer os.RemoveAll(target) 256 257 // intentionally choose an arch and os to build that are not our current one. 258 259 goos := "windows" 260 if runtime.GOOS == "windows" { 261 goos = "darwin" 262 } 263 goarch := "amd64" 264 if runtime.GOARCH == "amd64" { 265 goarch = "386" 266 } 267 stdout := &bytes.Buffer{} 268 stderr := &bytes.Buffer{} 269 inv := Invocation{ 270 Stderr: stderr, 271 Stdout: stdout, 272 Debug: true, 273 Dir: "testdata", 274 // this is relative to the Dir above 275 CompileOut: filepath.Join(".", filepath.Base(target), "output"), 276 GOOS: goos, 277 GOARCH: goarch, 278 } 279 code := Invoke(inv) 280 if code != 0 { 281 t.Fatalf("got code %v, err: %s", code, stderr) 282 } 283 os, arch, err := fileData(filepath.Join(target, "output")) 284 if err != nil { 285 t.Fatal(err) 286 } 287 if goos == "windows" { 288 if os != winExe { 289 t.Error("ran with GOOS=windows but did not produce a windows exe") 290 } 291 } else { 292 if os != macExe { 293 t.Error("ran with GOOS=darwin but did not a mac exe") 294 } 295 } 296 if goarch == "amd64" { 297 if arch != arch64 { 298 t.Error("ran with GOARCH=amd64 but did not produce a 64 bit exe") 299 } 300 } else { 301 if arch != arch32 { 302 t.Error("rand with GOARCH=386 but did not produce a 32 bit exe") 303 } 304 } 305} 306 307func TestListMagefilesLib(t *testing.T) { 308 buf := &bytes.Buffer{} 309 files, err := Magefiles("testdata/mixed_lib_files", "", "", "go", buf, false) 310 if err != nil { 311 t.Errorf("error from magefile list: %v: %s", err, buf) 312 } 313 expected := []string{ 314 filepath.FromSlash("testdata/mixed_lib_files/mage_helpers.go"), 315 filepath.FromSlash("testdata/mixed_lib_files/magefile.go"), 316 } 317 if !reflect.DeepEqual(files, expected) { 318 t.Fatalf("expected %q but got %q", expected, files) 319 } 320} 321 322func TestMixedMageImports(t *testing.T) { 323 resetTerm() 324 stderr := &bytes.Buffer{} 325 stdout := &bytes.Buffer{} 326 inv := Invocation{ 327 Dir: "./testdata/mixed_lib_files", 328 Stdout: stdout, 329 Stderr: stderr, 330 List: true, 331 } 332 code := Invoke(inv) 333 if code != 0 { 334 t.Errorf("expected to exit with code 0, but got %v, stderr: %s", code, stderr) 335 } 336 expected := "Targets:\n build \n" 337 actual := stdout.String() 338 if actual != expected { 339 t.Fatalf("expected %q but got %q", expected, actual) 340 } 341} 342 343func TestGoRun(t *testing.T) { 344 c := exec.Command("go", "run", "main.go") 345 c.Dir = "./testdata" 346 c.Env = os.Environ() 347 b, err := c.CombinedOutput() 348 if err != nil { 349 t.Error("error:", err) 350 } 351 actual := string(b) 352 expected := "stuff\n" 353 if actual != expected { 354 t.Fatalf("expected %q, but got %q", expected, actual) 355 } 356} 357 358func TestVerbose(t *testing.T) { 359 stderr := &bytes.Buffer{} 360 stdout := &bytes.Buffer{} 361 inv := Invocation{ 362 Dir: "./testdata", 363 Stdout: stdout, 364 Stderr: stderr, 365 Args: []string{"testverbose"}, 366 } 367 368 code := Invoke(inv) 369 if code != 0 { 370 t.Errorf("expected to exit with code 0, but got %v", code) 371 } 372 actual := stdout.String() 373 expected := "" 374 if actual != expected { 375 t.Fatalf("expected %q, but got %q", expected, actual) 376 } 377 stderr.Reset() 378 stdout.Reset() 379 inv.Verbose = true 380 code = Invoke(inv) 381 if code != 0 { 382 t.Errorf("expected to exit with code 0, but got %v", code) 383 } 384 385 actual = stderr.String() 386 expected = "Running target: TestVerbose\nhi!\n" 387 if actual != expected { 388 t.Fatalf("expected %q, but got %q", expected, actual) 389 } 390} 391 392func TestVerboseEnv(t *testing.T) { 393 os.Setenv("MAGEFILE_VERBOSE", "true") 394 defer os.Unsetenv("MAGEFILE_VERBOSE") 395 stdout := &bytes.Buffer{} 396 inv, _, err := Parse(ioutil.Discard, stdout, []string{}) 397 if err != nil { 398 t.Fatal("unexpected error", err) 399 } 400 401 expected := true 402 403 if inv.Verbose != true { 404 t.Fatalf("expected %t, but got %t ", expected, inv.Verbose) 405 } 406} 407func TestVerboseFalseEnv(t *testing.T) { 408 os.Setenv("MAGEFILE_VERBOSE", "0") 409 defer os.Unsetenv("MAGEFILE_VERBOSE") 410 stdout := &bytes.Buffer{} 411 code := ParseAndRun(ioutil.Discard, stdout, nil, []string{"-d", "testdata", "testverbose"}) 412 if code != 0 { 413 t.Fatal("unexpected code", code) 414 } 415 416 if stdout.String() != "" { 417 t.Fatalf("expected no output, but got %s", stdout.String()) 418 } 419} 420 421func TestList(t *testing.T) { 422 stdout := &bytes.Buffer{} 423 inv := Invocation{ 424 Dir: "./testdata/list", 425 Stdout: stdout, 426 Stderr: ioutil.Discard, 427 List: true, 428 } 429 430 code := Invoke(inv) 431 if code != 0 { 432 t.Errorf("expected to exit with code 0, but got %v", code) 433 } 434 actual := stdout.String() 435 expected := ` 436This is a comment on the package which should get turned into output with the list of targets. 437 438Targets: 439 somePig* This is the synopsis for SomePig. 440 testVerbose 441 442* default target 443`[1:] 444 445 if actual != expected { 446 t.Logf("expected: %q", expected) 447 t.Logf(" actual: %q", actual) 448 t.Fatalf("expected:\n%v\n\ngot:\n%v", expected, actual) 449 } 450} 451 452var terminals = []struct { 453 code string 454 supportsColor bool 455}{ 456 {"", true}, 457 {"vt100", false}, 458 {"cygwin", false}, 459 {"xterm-mono", false}, 460 {"xterm", true}, 461 {"xterm-vt220", true}, 462 {"xterm-16color", true}, 463 {"xterm-256color", true}, 464 {"screen-256color", true}, 465} 466 467func TestListWithColor(t *testing.T) { 468 os.Setenv(mg.EnableColorEnv, "true") 469 os.Setenv(mg.TargetColorEnv, mg.Cyan.String()) 470 471 expectedPlainText := ` 472This is a comment on the package which should get turned into output with the list of targets. 473 474Targets: 475 somePig* This is the synopsis for SomePig. 476 testVerbose 477 478* default target 479`[1:] 480 481 // NOTE: using the literal string would be complicated because I would need to break it 482 // in the middle and join with a normal string for the target names, 483 // otherwise the single backslash would be taken literally and encoded as \\ 484 expectedColorizedText := "" + 485 "This is a comment on the package which should get turned into output with the list of targets.\n" + 486 "\n" + 487 "Targets:\n" + 488 " \x1b[36msomePig*\x1b[0m This is the synopsis for SomePig.\n" + 489 " \x1b[36mtestVerbose\x1b[0m \n" + 490 "\n" + 491 "* default target\n" 492 493 for _, terminal := range terminals { 494 t.Run(terminal.code, func(t *testing.T) { 495 os.Setenv("TERM", terminal.code) 496 497 stdout := &bytes.Buffer{} 498 inv := Invocation{ 499 Dir: "./testdata/list", 500 Stdout: stdout, 501 Stderr: ioutil.Discard, 502 List: true, 503 } 504 505 code := Invoke(inv) 506 if code != 0 { 507 t.Errorf("expected to exit with code 0, but got %v", code) 508 } 509 actual := stdout.String() 510 var expected string 511 if terminal.supportsColor { 512 expected = expectedColorizedText 513 } else { 514 expected = expectedPlainText 515 } 516 517 if actual != expected { 518 t.Logf("expected: %q", expected) 519 t.Logf(" actual: %q", actual) 520 t.Fatalf("expected:\n%v\n\ngot:\n%v", expected, actual) 521 } 522 }) 523 } 524} 525 526func TestNoArgNoDefaultList(t *testing.T) { 527 resetTerm() 528 stdout := &bytes.Buffer{} 529 stderr := &bytes.Buffer{} 530 inv := Invocation{ 531 Dir: "testdata/no_default", 532 Stdout: stdout, 533 Stderr: stderr, 534 } 535 code := Invoke(inv) 536 if code != 0 { 537 t.Errorf("expected to exit with code 0, but got %v", code) 538 } 539 if err := stderr.String(); err != "" { 540 t.Errorf("unexpected stderr output:\n%s", err) 541 } 542 actual := stdout.String() 543 expected := ` 544Targets: 545 bazBuz Prints out 'BazBuz'. 546 fooBar Prints out 'FooBar'. 547`[1:] 548 if actual != expected { 549 t.Fatalf("expected:\n%q\n\ngot:\n%q", expected, actual) 550 } 551} 552 553func TestIgnoreDefault(t *testing.T) { 554 stdout := &bytes.Buffer{} 555 stderr := &bytes.Buffer{} 556 inv := Invocation{ 557 Dir: "./testdata/list", 558 Stdout: stdout, 559 Stderr: stderr, 560 } 561 defer os.Unsetenv(mg.IgnoreDefaultEnv) 562 if err := os.Setenv(mg.IgnoreDefaultEnv, "1"); err != nil { 563 t.Fatal(err) 564 } 565 resetTerm() 566 567 code := Invoke(inv) 568 if code != 0 { 569 t.Errorf("expected to exit with code 0, but got %v, stderr:\n%s", code, stderr) 570 } 571 actual := stdout.String() 572 expected := ` 573This is a comment on the package which should get turned into output with the list of targets. 574 575Targets: 576 somePig* This is the synopsis for SomePig. 577 testVerbose 578 579* default target 580`[1:] 581 582 if actual != expected { 583 t.Logf("expected: %q", expected) 584 t.Logf(" actual: %q", actual) 585 t.Fatalf("expected:\n%v\n\ngot:\n%v", expected, actual) 586 } 587} 588 589func TestTargetError(t *testing.T) { 590 stderr := &bytes.Buffer{} 591 inv := Invocation{ 592 Dir: "./testdata", 593 Stdout: ioutil.Discard, 594 Stderr: stderr, 595 Args: []string{"returnsnonnilerror"}, 596 } 597 code := Invoke(inv) 598 if code != 1 { 599 t.Fatalf("expected 1, but got %v", code) 600 } 601 actual := stderr.String() 602 expected := "Error: bang!\n" 603 if actual != expected { 604 t.Fatalf("expected %q, but got %q", expected, actual) 605 } 606} 607 608func TestStdinCopy(t *testing.T) { 609 stdout := &bytes.Buffer{} 610 stdin := strings.NewReader("hi!") 611 inv := Invocation{ 612 Dir: "./testdata", 613 Stderr: ioutil.Discard, 614 Stdout: stdout, 615 Stdin: stdin, 616 Args: []string{"CopyStdin"}, 617 } 618 code := Invoke(inv) 619 if code != 0 { 620 t.Fatalf("expected 0, but got %v", code) 621 } 622 actual := stdout.String() 623 expected := "hi!" 624 if actual != expected { 625 t.Fatalf("expected %q, but got %q", expected, actual) 626 } 627} 628 629func TestTargetPanics(t *testing.T) { 630 stderr := &bytes.Buffer{} 631 inv := Invocation{ 632 Dir: "./testdata", 633 Stdout: ioutil.Discard, 634 Stderr: stderr, 635 Args: []string{"panics"}, 636 } 637 code := Invoke(inv) 638 if code != 1 { 639 t.Fatalf("expected 1, but got %v", code) 640 } 641 actual := stderr.String() 642 expected := "Error: boom!\n" 643 if actual != expected { 644 t.Fatalf("expected %q, but got %q", expected, actual) 645 } 646} 647 648func TestPanicsErr(t *testing.T) { 649 stderr := &bytes.Buffer{} 650 inv := Invocation{ 651 Dir: "./testdata", 652 Stdout: ioutil.Discard, 653 Stderr: stderr, 654 Args: []string{"panicserr"}, 655 } 656 code := Invoke(inv) 657 if code != 1 { 658 t.Fatalf("expected 1, but got %v", code) 659 } 660 actual := stderr.String() 661 expected := "Error: kaboom!\n" 662 if actual != expected { 663 t.Fatalf("expected %q, but got %q", expected, actual) 664 } 665} 666 667// ensure we include the hash of the mainfile template in determining the 668// executable name to run, so we automatically create a new exe if the template 669// changes. 670func TestHashTemplate(t *testing.T) { 671 templ := mageMainfileTplString 672 defer func() { mageMainfileTplString = templ }() 673 name, err := ExeName("go", mg.CacheDir(), []string{"testdata/func.go", "testdata/command.go"}) 674 if err != nil { 675 t.Fatal(err) 676 } 677 mageMainfileTplString = "some other template" 678 changed, err := ExeName("go", mg.CacheDir(), []string{"testdata/func.go", "testdata/command.go"}) 679 if err != nil { 680 t.Fatal(err) 681 } 682 if changed == name { 683 t.Fatal("expected executable name to chage if template changed") 684 } 685} 686 687// Test if the -keep flag does keep the mainfile around after running 688func TestKeepFlag(t *testing.T) { 689 buildFile := fmt.Sprintf("./testdata/keep_flag/%s", mainfile) 690 os.Remove(buildFile) 691 defer os.Remove(buildFile) 692 w := tLogWriter{t} 693 694 inv := Invocation{ 695 Dir: "./testdata/keep_flag", 696 Stdout: w, 697 Stderr: w, 698 List: true, 699 Keep: true, 700 Force: true, // need force so we always regenerate 701 } 702 code := Invoke(inv) 703 if code != 0 { 704 t.Fatalf("expected code 0, but got %v", code) 705 } 706 707 if _, err := os.Stat(buildFile); err != nil { 708 t.Fatalf("expected file %q to exist but got err, %v", buildFile, err) 709 } 710} 711 712type tLogWriter struct { 713 *testing.T 714} 715 716func (t tLogWriter) Write(b []byte) (n int, err error) { 717 t.Log(string(b)) 718 return len(b), nil 719} 720 721// Test if generated mainfile references anything other than the stdlib 722func TestOnlyStdLib(t *testing.T) { 723 buildFile := fmt.Sprintf("./testdata/onlyStdLib/%s", mainfile) 724 os.Remove(buildFile) 725 defer os.Remove(buildFile) 726 727 w := tLogWriter{t} 728 729 inv := Invocation{ 730 Dir: "./testdata/onlyStdLib", 731 Stdout: w, 732 Stderr: w, 733 List: true, 734 Keep: true, 735 Force: true, // need force so we always regenerate 736 Verbose: true, 737 } 738 code := Invoke(inv) 739 if code != 0 { 740 t.Fatalf("expected code 0, but got %v", code) 741 } 742 743 if _, err := os.Stat(buildFile); err != nil { 744 t.Fatalf("expected file %q to exist but got err, %v", buildFile, err) 745 } 746 747 fset := &token.FileSet{} 748 // Parse src but stop after processing the imports. 749 f, err := parser.ParseFile(fset, buildFile, nil, parser.ImportsOnly) 750 if err != nil { 751 fmt.Println(err) 752 return 753 } 754 755 // Print the imports from the file's AST. 756 for _, s := range f.Imports { 757 // the path value comes in as a quoted string, i.e. literally \"context\" 758 path := strings.Trim(s.Path.Value, "\"") 759 pkg, err := build.Default.Import(path, "./testdata/keep_flag", build.FindOnly) 760 if err != nil { 761 t.Fatal(err) 762 } 763 if !filepath.HasPrefix(pkg.Dir, build.Default.GOROOT) { 764 t.Errorf("import of non-stdlib package: %s", s.Path.Value) 765 } 766 } 767} 768 769func TestMultipleTargets(t *testing.T) { 770 var stderr, stdout bytes.Buffer 771 inv := Invocation{ 772 Dir: "./testdata", 773 Stdout: &stdout, 774 Stderr: &stderr, 775 Args: []string{"TestVerbose", "ReturnsNilError"}, 776 Verbose: true, 777 } 778 code := Invoke(inv) 779 if code != 0 { 780 t.Errorf("expected 0, but got %v", code) 781 } 782 actual := stderr.String() 783 expected := "Running target: TestVerbose\nhi!\nRunning target: ReturnsNilError\n" 784 if actual != expected { 785 t.Errorf("expected %q, but got %q", expected, actual) 786 } 787 actual = stdout.String() 788 expected = "stuff\n" 789 if actual != expected { 790 t.Errorf("expected %q, but got %q", expected, actual) 791 } 792} 793 794func TestFirstTargetFails(t *testing.T) { 795 var stderr, stdout bytes.Buffer 796 inv := Invocation{ 797 Dir: "./testdata", 798 Stdout: &stdout, 799 Stderr: &stderr, 800 Args: []string{"ReturnsNonNilError", "ReturnsNilError"}, 801 Verbose: true, 802 } 803 code := Invoke(inv) 804 if code != 1 { 805 t.Errorf("expected 1, but got %v", code) 806 } 807 actual := stderr.String() 808 expected := "Running target: ReturnsNonNilError\nError: bang!\n" 809 if actual != expected { 810 t.Errorf("expected %q, but got %q", expected, actual) 811 } 812 actual = stdout.String() 813 expected = "" 814 if actual != expected { 815 t.Errorf("expected %q, but got %q", expected, actual) 816 } 817} 818 819func TestBadSecondTargets(t *testing.T) { 820 var stderr, stdout bytes.Buffer 821 inv := Invocation{ 822 Dir: "./testdata", 823 Stdout: &stdout, 824 Stderr: &stderr, 825 Args: []string{"TestVerbose", "NotGonnaWork"}, 826 } 827 code := Invoke(inv) 828 if code != 2 { 829 t.Errorf("expected 2, but got %v", code) 830 } 831 actual := stderr.String() 832 expected := "Unknown target specified: \"NotGonnaWork\"\n" 833 if actual != expected { 834 t.Errorf("expected %q, but got %q", expected, actual) 835 } 836 actual = stdout.String() 837 expected = "" 838 if actual != expected { 839 t.Errorf("expected %q, but got %q", expected, actual) 840 } 841} 842 843func TestParse(t *testing.T) { 844 buf := &bytes.Buffer{} 845 inv, cmd, err := Parse(ioutil.Discard, buf, []string{"-v", "-debug", "-gocmd=foo", "-d", "dir", "build", "deploy"}) 846 if err != nil { 847 t.Fatal("unexpected error", err) 848 } 849 if cmd == Init { 850 t.Error("init should be false but was true") 851 } 852 if cmd == Version { 853 t.Error("showVersion should be false but was true") 854 } 855 if inv.Debug != true { 856 t.Error("debug should be true") 857 } 858 if inv.Dir != "dir" { 859 t.Errorf("Expected dir to be \"dir\" but was %q", inv.Dir) 860 } 861 if inv.GoCmd != "foo" { 862 t.Errorf("Expected gocmd to be \"foo\" but was %q", inv.GoCmd) 863 } 864 expected := []string{"build", "deploy"} 865 if !reflect.DeepEqual(inv.Args, expected) { 866 t.Fatalf("expected args to be %q but got %q", expected, inv.Args) 867 } 868 if s := buf.String(); s != "" { 869 t.Fatalf("expected no stdout output but got %q", s) 870 } 871 872} 873 874func TestSetDir(t *testing.T) { 875 stdout := &bytes.Buffer{} 876 stderr := &bytes.Buffer{} 877 code := Invoke(Invocation{ 878 Dir: "testdata/setdir", 879 Stdout: stdout, 880 Stderr: stderr, 881 Args: []string{"TestCurrentDir"}, 882 }) 883 if code != 0 { 884 t.Errorf("expected code 0, but got %d. Stdout:\n%s\nStderr:\n%s", code, stdout, stderr) 885 } 886 expected := "setdir.go\n" 887 if out := stdout.String(); out != expected { 888 t.Fatalf("expected list of files to be %q, but was %q", expected, out) 889 } 890} 891 892func TestSetWorkingDir(t *testing.T) { 893 stdout := &bytes.Buffer{} 894 stderr := &bytes.Buffer{} 895 code := Invoke(Invocation{ 896 Dir: "testdata/setworkdir", 897 WorkDir: "testdata/setworkdir/data", 898 Stdout: stdout, 899 Stderr: stderr, 900 Args: []string{"TestWorkingDir"}, 901 }) 902 903 if code != 0 { 904 t.Errorf( 905 "expected code 0, but got %d. Stdout:\n%s\nStderr:\n%s", 906 code, stdout, stderr, 907 ) 908 } 909 910 expected := "file1.txt, file2.txt\n" 911 if out := stdout.String(); out != expected { 912 t.Fatalf("expected list of files to be %q, but was %q", expected, out) 913 } 914} 915 916// Test the timeout option 917func TestTimeout(t *testing.T) { 918 stderr := &bytes.Buffer{} 919 stdout := &bytes.Buffer{} 920 inv := Invocation{ 921 Dir: "testdata/context", 922 Stdout: stdout, 923 Stderr: stderr, 924 Args: []string{"timeout"}, 925 Timeout: time.Duration(100 * time.Millisecond), 926 } 927 code := Invoke(inv) 928 if code != 1 { 929 t.Fatalf("expected 1, but got %v, stderr: %q, stdout: %q", code, stderr, stdout) 930 } 931 actual := stderr.String() 932 expected := "Error: context deadline exceeded\n" 933 934 if actual != expected { 935 t.Fatalf("expected %q, but got %q", expected, actual) 936 } 937} 938func TestParseHelp(t *testing.T) { 939 buf := &bytes.Buffer{} 940 _, _, err := Parse(ioutil.Discard, buf, []string{"-h"}) 941 if err != flag.ErrHelp { 942 t.Fatal("unexpected error", err) 943 } 944 buf2 := &bytes.Buffer{} 945 _, _, err = Parse(ioutil.Discard, buf2, []string{"--help"}) 946 if err != flag.ErrHelp { 947 t.Fatal("unexpected error", err) 948 } 949 s := buf.String() 950 s2 := buf2.String() 951 if s != s2 { 952 t.Fatalf("expected -h and --help to produce same output, but got different.\n\n-h:\n%s\n\n--help:\n%s", s, s2) 953 } 954} 955 956func TestHelpTarget(t *testing.T) { 957 stdout := &bytes.Buffer{} 958 inv := Invocation{ 959 Dir: "./testdata", 960 Stdout: stdout, 961 Stderr: ioutil.Discard, 962 Args: []string{"panics"}, 963 Help: true, 964 } 965 code := Invoke(inv) 966 if code != 0 { 967 t.Errorf("expected to exit with code 0, but got %v", code) 968 } 969 actual := stdout.String() 970 expected := "Function that panics.\n\nUsage:\n\n\tmage panics\n\n" 971 if actual != expected { 972 t.Fatalf("expected %q, but got %q", expected, actual) 973 } 974} 975 976func TestHelpAlias(t *testing.T) { 977 stdout := &bytes.Buffer{} 978 inv := Invocation{ 979 Dir: "./testdata/alias", 980 Stdout: stdout, 981 Stderr: ioutil.Discard, 982 Args: []string{"status"}, 983 Help: true, 984 } 985 code := Invoke(inv) 986 if code != 0 { 987 t.Errorf("expected to exit with code 0, but got %v", code) 988 } 989 actual := stdout.String() 990 expected := "Prints status.\n\nUsage:\n\n\tmage status\n\nAliases: st, stat\n\n" 991 if actual != expected { 992 t.Fatalf("expected %q, but got %q", expected, actual) 993 } 994 inv = Invocation{ 995 Dir: "./testdata/alias", 996 Stdout: stdout, 997 Stderr: ioutil.Discard, 998 Args: []string{"checkout"}, 999 Help: true, 1000 } 1001 stdout.Reset() 1002 code = Invoke(inv) 1003 if code != 0 { 1004 t.Errorf("expected to exit with code 0, but got %v", code) 1005 } 1006 actual = stdout.String() 1007 expected = "Usage:\n\n\tmage checkout\n\nAliases: co\n\n" 1008 if actual != expected { 1009 t.Fatalf("expected %q, but got %q", expected, actual) 1010 } 1011} 1012 1013func TestAlias(t *testing.T) { 1014 stdout := &bytes.Buffer{} 1015 stderr := &bytes.Buffer{} 1016 debug.SetOutput(stderr) 1017 inv := Invocation{ 1018 Dir: "testdata/alias", 1019 Stdout: stdout, 1020 Stderr: ioutil.Discard, 1021 Args: []string{"status"}, 1022 Debug: true, 1023 } 1024 code := Invoke(inv) 1025 if code != 0 { 1026 t.Errorf("expected to exit with code 0, but got %v\noutput:\n%s\nstderr:\n%s", code, stdout, stderr) 1027 } 1028 actual := stdout.String() 1029 expected := "alias!\n" 1030 if actual != expected { 1031 t.Fatalf("expected %q, but got %q", expected, actual) 1032 } 1033 stdout.Reset() 1034 inv.Args = []string{"st"} 1035 code = Invoke(inv) 1036 if code != 0 { 1037 t.Errorf("expected to exit with code 0, but got %v", code) 1038 } 1039 actual = stdout.String() 1040 if actual != expected { 1041 t.Fatalf("expected %q, but got %q", expected, actual) 1042 } 1043} 1044 1045func TestInvalidAlias(t *testing.T) { 1046 stderr := &bytes.Buffer{} 1047 log.SetOutput(ioutil.Discard) 1048 inv := Invocation{ 1049 Dir: "./testdata/invalid_alias", 1050 Stdout: ioutil.Discard, 1051 Stderr: stderr, 1052 Args: []string{"co"}, 1053 } 1054 code := Invoke(inv) 1055 if code != 2 { 1056 t.Errorf("expected to exit with code 2, but got %v", code) 1057 } 1058 actual := stderr.String() 1059 expected := "Unknown target specified: \"co\"\n" 1060 if actual != expected { 1061 t.Fatalf("expected %q, but got %q", expected, actual) 1062 } 1063} 1064 1065func TestRunCompiledPrintsError(t *testing.T) { 1066 stderr := &bytes.Buffer{} 1067 logger := log.New(stderr, "", 0) 1068 code := RunCompiled(Invocation{}, "thiswon'texist", logger) 1069 if code != 1 { 1070 t.Errorf("expected code 1 but got %v", code) 1071 } 1072 1073 if strings.TrimSpace(stderr.String()) == "" { 1074 t.Fatal("expected to get output to stderr when a run fails, but got nothing.") 1075 } 1076} 1077 1078func TestCompiledFlags(t *testing.T) { 1079 stderr := &bytes.Buffer{} 1080 stdout := &bytes.Buffer{} 1081 dir := "./testdata/compiled" 1082 compileDir, err := ioutil.TempDir(dir, "") 1083 if err != nil { 1084 t.Fatal(err) 1085 } 1086 name := filepath.Join(compileDir, "mage_out") 1087 if runtime.GOOS == "windows" { 1088 name += ".exe" 1089 } 1090 // The CompileOut directory is relative to the 1091 // invocation directory, so chop off the invocation dir. 1092 outName := "./" + name[len(dir)-1:] 1093 defer os.RemoveAll(compileDir) 1094 inv := Invocation{ 1095 Dir: dir, 1096 Stdout: stdout, 1097 Stderr: stderr, 1098 CompileOut: outName, 1099 } 1100 code := Invoke(inv) 1101 if code != 0 { 1102 t.Errorf("expected to exit with code 0, but got %v, stderr: %s", code, stderr) 1103 } 1104 1105 run := func(stdout, stderr *bytes.Buffer, filename string, args ...string) error { 1106 stderr.Reset() 1107 stdout.Reset() 1108 cmd := exec.Command(filename, args...) 1109 cmd.Env = os.Environ() 1110 cmd.Stderr = stderr 1111 cmd.Stdout = stdout 1112 if err := cmd.Run(); err != nil { 1113 return fmt.Errorf("running '%s %s' failed with: %v\nstdout: %s\nstderr: %s", 1114 filename, strings.Join(args, " "), err, stdout, stderr) 1115 } 1116 return nil 1117 } 1118 1119 // get help to target with flag -h target 1120 if err := run(stdout, stderr, name, "-h", "deploy"); err != nil { 1121 t.Fatal(err) 1122 } 1123 got := strings.TrimSpace(stdout.String()) 1124 want := "This is the synopsis for Deploy. This part shouldn't show up.\n\nUsage:\n\n\t" + filepath.Base(name) + " deploy" 1125 if got != want { 1126 t.Errorf("got %q, want %q", got, want) 1127 } 1128 1129 // run target with verbose flag -v 1130 if err := run(stdout, stderr, name, "-v", "testverbose"); err != nil { 1131 t.Fatal(err) 1132 } 1133 got = stderr.String() 1134 want = "hi!" 1135 if strings.Contains(got, want) == false { 1136 t.Errorf("got %q, does not contain %q", got, want) 1137 } 1138 1139 // pass list flag -l 1140 if err := run(stdout, stderr, name, "-l"); err != nil { 1141 t.Fatal(err) 1142 } 1143 got = stdout.String() 1144 want = "This is the synopsis for Deploy" 1145 if strings.Contains(got, want) == false { 1146 t.Errorf("got %q, does not contain %q", got, want) 1147 } 1148 want = "This is very verbose" 1149 if strings.Contains(got, want) == false { 1150 t.Errorf("got %q, does not contain %q", got, want) 1151 } 1152 1153 // pass flag -t 1ms 1154 err = run(stdout, stderr, name, "-t", "1ms", "sleep") 1155 if err == nil { 1156 t.Fatalf("expected an error because of timeout") 1157 } 1158 got = stdout.String() 1159 want = "context deadline exceeded" 1160 if strings.Contains(got, want) == false { 1161 t.Errorf("got %q, does not contain %q", got, want) 1162 } 1163} 1164 1165func TestCompiledEnvironmentVars(t *testing.T) { 1166 stderr := &bytes.Buffer{} 1167 stdout := &bytes.Buffer{} 1168 dir := "./testdata/compiled" 1169 compileDir, err := ioutil.TempDir(dir, "") 1170 if err != nil { 1171 t.Fatal(err) 1172 } 1173 name := filepath.Join(compileDir, "mage_out") 1174 if runtime.GOOS == "windows" { 1175 name += ".exe" 1176 } 1177 // The CompileOut directory is relative to the 1178 // invocation directory, so chop off the invocation dir. 1179 outName := "./" + name[len(dir)-1:] 1180 defer os.RemoveAll(compileDir) 1181 inv := Invocation{ 1182 Dir: dir, 1183 Stdout: stdout, 1184 Stderr: stderr, 1185 CompileOut: outName, 1186 } 1187 code := Invoke(inv) 1188 if code != 0 { 1189 t.Errorf("expected to exit with code 0, but got %v, stderr: %s", code, stderr) 1190 } 1191 1192 run := func(stdout, stderr *bytes.Buffer, filename string, envval string, args ...string) error { 1193 stderr.Reset() 1194 stdout.Reset() 1195 cmd := exec.Command(filename, args...) 1196 cmd.Env = []string{envval} 1197 cmd.Stderr = stderr 1198 cmd.Stdout = stdout 1199 if err := cmd.Run(); err != nil { 1200 return fmt.Errorf("running '%s %s' failed with: %v\nstdout: %s\nstderr: %s", 1201 filename, strings.Join(args, " "), err, stdout, stderr) 1202 } 1203 return nil 1204 } 1205 1206 if err := run(stdout, stderr, name, "MAGEFILE_HELP=1", "deploy"); err != nil { 1207 t.Fatal(err) 1208 } 1209 got := stdout.String() 1210 want := "This is the synopsis for Deploy. This part shouldn't show up.\n\nUsage:\n\n\t" + filepath.Base(name) + " deploy\n\n" 1211 if got != want { 1212 t.Errorf("got %q, want %q", got, want) 1213 } 1214 1215 if err := run(stdout, stderr, name, mg.VerboseEnv+"=1", "testverbose"); err != nil { 1216 t.Fatal(err) 1217 } 1218 got = stderr.String() 1219 want = "hi!" 1220 if strings.Contains(got, want) == false { 1221 t.Errorf("got %q, does not contain %q", got, want) 1222 } 1223 1224 if err := run(stdout, stderr, name, "MAGEFILE_LIST=1"); err != nil { 1225 t.Fatal(err) 1226 } 1227 got = stdout.String() 1228 want = "This is the synopsis for Deploy" 1229 if strings.Contains(got, want) == false { 1230 t.Errorf("got %q, does not contain %q", got, want) 1231 } 1232 want = "This is very verbose" 1233 if strings.Contains(got, want) == false { 1234 t.Errorf("got %q, does not contain %q", got, want) 1235 } 1236 1237 if err := run(stdout, stderr, name, mg.IgnoreDefaultEnv+"=1"); err != nil { 1238 t.Fatal(err) 1239 } 1240 got = stdout.String() 1241 want = "Compiled package description." 1242 if strings.Contains(got, want) == false { 1243 t.Errorf("got %q, does not contain %q", got, want) 1244 } 1245 1246 err = run(stdout, stderr, name, "MAGEFILE_TIMEOUT=1ms", "sleep") 1247 if err == nil { 1248 t.Fatalf("expected an error because of timeout") 1249 } 1250 got = stdout.String() 1251 want = "context deadline exceeded" 1252 if strings.Contains(got, want) == false { 1253 t.Errorf("got %q, does not contain %q", got, want) 1254 } 1255} 1256 1257func TestCompiledVerboseFlag(t *testing.T) { 1258 stderr := &bytes.Buffer{} 1259 stdout := &bytes.Buffer{} 1260 dir := "./testdata/compiled" 1261 compileDir, err := ioutil.TempDir(dir, "") 1262 if err != nil { 1263 t.Fatal(err) 1264 } 1265 filename := filepath.Join(compileDir, "mage_out") 1266 if runtime.GOOS == "windows" { 1267 filename += ".exe" 1268 } 1269 // The CompileOut directory is relative to the 1270 // invocation directory, so chop off the invocation dir. 1271 outName := "./" + filename[len(dir)-1:] 1272 defer os.RemoveAll(compileDir) 1273 inv := Invocation{ 1274 Dir: dir, 1275 Stdout: stdout, 1276 Stderr: stderr, 1277 CompileOut: outName, 1278 } 1279 code := Invoke(inv) 1280 if code != 0 { 1281 t.Errorf("expected to exit with code 0, but got %v, stderr: %s", code, stderr) 1282 } 1283 1284 run := func(verboseEnv string, args ...string) string { 1285 var stdout, stderr bytes.Buffer 1286 args = append(args, "printverboseflag") 1287 cmd := exec.Command(filename, args...) 1288 cmd.Env = []string{verboseEnv} 1289 cmd.Stderr = &stderr 1290 cmd.Stdout = &stdout 1291 if err := cmd.Run(); err != nil { 1292 t.Fatalf("running '%s %s' with env %s failed with: %v\nstdout: %s\nstderr: %s", 1293 filename, strings.Join(args, " "), verboseEnv, err, stdout.String(), stderr.String()) 1294 } 1295 return strings.TrimSpace(stdout.String()) 1296 } 1297 1298 got := run("MAGEFILE_VERBOSE=false") 1299 want := "mg.Verbose()==false" 1300 if got != want { 1301 t.Errorf("got %q, expected %q", got, want) 1302 } 1303 1304 got = run("MAGEFILE_VERBOSE=false", "-v") 1305 want = "mg.Verbose()==true" 1306 if got != want { 1307 t.Errorf("got %q, expected %q", got, want) 1308 } 1309 1310 got = run("MAGEFILE_VERBOSE=true") 1311 want = "mg.Verbose()==true" 1312 if got != want { 1313 t.Errorf("got %q, expected %q", got, want) 1314 } 1315 1316 got = run("MAGEFILE_VERBOSE=true", "-v=false") 1317 want = "mg.Verbose()==false" 1318 if got != want { 1319 t.Errorf("got %q, expected %q", got, want) 1320 } 1321} 1322 1323func TestClean(t *testing.T) { 1324 if err := os.RemoveAll(mg.CacheDir()); err != nil { 1325 t.Error("error removing cache dir:", err) 1326 } 1327 code := ParseAndRun(ioutil.Discard, ioutil.Discard, &bytes.Buffer{}, []string{"-clean"}) 1328 if code != 0 { 1329 t.Errorf("expected 0, but got %v", code) 1330 } 1331 1332 TestAlias(t) // make sure we've got something in the CACHE_DIR 1333 files, err := ioutil.ReadDir(mg.CacheDir()) 1334 if err != nil { 1335 t.Error("issue reading file:", err) 1336 } 1337 1338 if len(files) < 1 { 1339 t.Error("Need at least 1 cached binaries to test --clean") 1340 } 1341 1342 _, cmd, err := Parse(ioutil.Discard, ioutil.Discard, []string{"-clean"}) 1343 if err != nil { 1344 t.Fatal(err) 1345 } 1346 if cmd != Clean { 1347 t.Errorf("Expected 'clean' command but got %v", cmd) 1348 } 1349 buf := &bytes.Buffer{} 1350 code = ParseAndRun(ioutil.Discard, buf, &bytes.Buffer{}, []string{"-clean"}) 1351 if code != 0 { 1352 t.Fatalf("expected 0, but got %v: %s", code, buf) 1353 } 1354 1355 infos, err := ioutil.ReadDir(mg.CacheDir()) 1356 if err != nil { 1357 t.Fatal(err) 1358 } 1359 1360 var names []string 1361 for _, i := range infos { 1362 if !i.IsDir() { 1363 names = append(names, i.Name()) 1364 } 1365 } 1366 1367 if len(names) != 0 { 1368 t.Errorf("expected '-clean' to remove files from CACHE_DIR, but still have %v", names) 1369 } 1370} 1371 1372func TestGoCmd(t *testing.T) { 1373 textOutput := "TestGoCmd" 1374 defer os.Unsetenv(testExeEnv) 1375 if err := os.Setenv(testExeEnv, textOutput); err != nil { 1376 t.Fatal(err) 1377 } 1378 1379 // fake out the compiled file, since the code checks for it. 1380 f, err := ioutil.TempFile("", "") 1381 if err != nil { 1382 t.Fatal(err) 1383 } 1384 name := f.Name() 1385 dir := filepath.Dir(name) 1386 defer os.Remove(name) 1387 f.Close() 1388 1389 buf := &bytes.Buffer{} 1390 stderr := &bytes.Buffer{} 1391 if err := Compile("", "", "", dir, os.Args[0], name, []string{}, false, stderr, buf); err != nil { 1392 t.Log("stderr: ", stderr.String()) 1393 t.Fatal(err) 1394 } 1395 if buf.String() != textOutput { 1396 t.Fatalf("We didn't run the custom go cmd. Expected output %q, but got %q", textOutput, buf) 1397 } 1398} 1399 1400var runtimeVer = regexp.MustCompile(`go1\.([0-9]+)`) 1401 1402func TestGoModules(t *testing.T) { 1403 resetTerm() 1404 matches := runtimeVer.FindStringSubmatch(runtime.Version()) 1405 if len(matches) < 2 || minorVer(t, matches[1]) < 11 { 1406 t.Skipf("Skipping Go modules test because go version %q is less than go1.11", runtime.Version()) 1407 } 1408 dir, err := ioutil.TempDir("", "") 1409 if err != nil { 1410 t.Fatal(err) 1411 } 1412 defer os.RemoveAll(dir) 1413 err = ioutil.WriteFile(filepath.Join(dir, "magefile.go"), []byte(`//+build mage 1414 1415package main 1416 1417import "golang.org/x/text/unicode/norm" 1418 1419func Test() { 1420 print("unicode version: " + norm.Version) 1421} 1422`), 0600) 1423 if err != nil { 1424 t.Fatal(err) 1425 } 1426 1427 stdout := &bytes.Buffer{} 1428 stderr := &bytes.Buffer{} 1429 cmd := exec.Command("go", "mod", "init", "app") 1430 cmd.Dir = dir 1431 cmd.Env = os.Environ() 1432 cmd.Stderr = stderr 1433 cmd.Stdout = stdout 1434 if err := cmd.Run(); err != nil { 1435 t.Fatalf("Error running go mod init: %v\nStdout: %s\nStderr: %s", err, stdout, stderr) 1436 } 1437 stderr.Reset() 1438 stdout.Reset() 1439 code := Invoke(Invocation{ 1440 Dir: dir, 1441 Stderr: stderr, 1442 Stdout: stdout, 1443 }) 1444 if code != 0 { 1445 t.Fatalf("exited with code %d. \nStdout: %s\nStderr: %s", code, stdout, stderr) 1446 } 1447 expected := ` 1448Targets: 1449 test 1450`[1:] 1451 if output := stdout.String(); output != expected { 1452 t.Fatalf("expected output %q, but got %q", expected, output) 1453 } 1454} 1455 1456func minorVer(t *testing.T, v string) int { 1457 a, err := strconv.Atoi(v) 1458 if err != nil { 1459 t.Fatal("unexpected non-numeric version", v) 1460 } 1461 return a 1462} 1463 1464func TestNamespaceDep(t *testing.T) { 1465 stdout := &bytes.Buffer{} 1466 stderr := &bytes.Buffer{} 1467 inv := Invocation{ 1468 Dir: "./testdata/namespaces", 1469 Stderr: stderr, 1470 Stdout: stdout, 1471 Args: []string{"TestNamespaceDep"}, 1472 } 1473 code := Invoke(inv) 1474 if code != 0 { 1475 t.Fatalf("expected 0, but got %v, stderr:\n%s", code, stderr) 1476 } 1477 expected := "hi!\n" 1478 if stdout.String() != expected { 1479 t.Fatalf("expected %q, but got %q", expected, stdout.String()) 1480 } 1481} 1482 1483func TestNamespace(t *testing.T) { 1484 stdout := &bytes.Buffer{} 1485 inv := Invocation{ 1486 Dir: "./testdata/namespaces", 1487 Stderr: ioutil.Discard, 1488 Stdout: stdout, 1489 Args: []string{"ns:error"}, 1490 } 1491 code := Invoke(inv) 1492 if code != 0 { 1493 t.Fatalf("expected 0, but got %v", code) 1494 } 1495 expected := "hi!\n" 1496 if stdout.String() != expected { 1497 t.Fatalf("expected %q, but got %q", expected, stdout.String()) 1498 } 1499} 1500 1501func TestNamespaceDefault(t *testing.T) { 1502 stdout := &bytes.Buffer{} 1503 inv := Invocation{ 1504 Dir: "./testdata/namespaces", 1505 Stderr: ioutil.Discard, 1506 Stdout: stdout, 1507 } 1508 code := Invoke(inv) 1509 if code != 0 { 1510 t.Fatalf("expected 0, but got %v", code) 1511 } 1512 expected := "hi!\n" 1513 if stdout.String() != expected { 1514 t.Fatalf("expected %q, but got %q", expected, stdout.String()) 1515 } 1516} 1517 1518func TestAliasToImport(t *testing.T) { 1519 1520} 1521 1522func TestWrongDependency(t *testing.T) { 1523 stderr := &bytes.Buffer{} 1524 inv := Invocation{ 1525 Dir: "./testdata/wrong_dep", 1526 Stderr: stderr, 1527 Stdout: ioutil.Discard, 1528 } 1529 code := Invoke(inv) 1530 if code != 1 { 1531 t.Fatalf("expected 1, but got %v", code) 1532 } 1533 expected := "Error: argument 0 (complex128), is not a supported argument type\n" 1534 actual := stderr.String() 1535 if actual != expected { 1536 t.Fatalf("expected %q, but got %q", expected, actual) 1537 } 1538} 1539 1540/// This code liberally borrowed from https://github.com/rsc/goversion/blob/master/version/exe.go 1541 1542type exeType int 1543type archSize int 1544 1545const ( 1546 winExe exeType = iota 1547 macExe 1548 1549 arch32 archSize = iota 1550 arch64 1551) 1552 1553// fileData tells us if the given file is mac or windows and if they're 32bit or 1554// 64 bit. Other exe versions are not supported. 1555func fileData(file string) (exeType, archSize, error) { 1556 f, err := os.Open(file) 1557 if err != nil { 1558 return -1, -1, err 1559 } 1560 defer f.Close() 1561 data := make([]byte, 16) 1562 if _, err := io.ReadFull(f, data); err != nil { 1563 return -1, -1, err 1564 } 1565 if bytes.HasPrefix(data, []byte("MZ")) { 1566 // hello windows exe! 1567 e, err := pe.NewFile(f) 1568 if err != nil { 1569 return -1, -1, err 1570 } 1571 if e.Machine == pe.IMAGE_FILE_MACHINE_AMD64 { 1572 return winExe, arch64, nil 1573 } 1574 return winExe, arch32, nil 1575 } 1576 1577 if bytes.HasPrefix(data, []byte("\xFE\xED\xFA")) || bytes.HasPrefix(data[1:], []byte("\xFA\xED\xFE")) { 1578 // hello mac exe! 1579 fe, err := macho.NewFile(f) 1580 if err != nil { 1581 return -1, -1, err 1582 } 1583 if fe.Cpu&0x01000000 != 0 { 1584 return macExe, arch64, nil 1585 } 1586 return macExe, arch32, nil 1587 } 1588 return -1, -1, fmt.Errorf("unrecognized executable format") 1589} 1590