github.com/joshdk/godel@v0.0.0-20170529232908-862138a45aee/apps/gunit/integration_test/integration_test.go (about) 1 // Copyright 2016 Palantir Technologies, Inc. 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 15 package integration_test 16 17 import ( 18 "io/ioutil" 19 "os" 20 "os/exec" 21 "path" 22 "regexp" 23 "strings" 24 "testing" 25 26 "github.com/nmiyake/pkg/dirs" 27 "github.com/nmiyake/pkg/gofiles" 28 "github.com/palantir/pkg/pkgpath" 29 "github.com/stretchr/testify/assert" 30 "github.com/stretchr/testify/require" 31 32 "github.com/palantir/godel/pkg/products" 33 ) 34 35 func TestRun(t *testing.T) { 36 cli, err := products.Bin("gunit") 37 require.NoError(t, err) 38 39 wd, err := os.Getwd() 40 require.NoError(t, err) 41 42 tmpDir, cleanup, err := dirs.TempDir(wd, "") 43 defer cleanup() 44 require.NoError(t, err) 45 46 originalWd, err := os.Getwd() 47 require.NoError(t, err) 48 defer func() { 49 if err := os.Chdir(originalWd); err != nil { 50 require.NoError(t, err) 51 } 52 }() 53 54 for i, currCase := range []struct { 55 name string 56 filesToCreate []gofiles.GoFileSpec 57 config string 58 args []string 59 wantMatch func(currCaseTmpDir string) string 60 wantError string 61 }{ 62 { 63 name: "passing tests", 64 filesToCreate: []gofiles.GoFileSpec{ 65 { 66 RelPath: "foo.go", 67 Src: `package foo 68 import "fmt" 69 func Foo() { 70 fmt.Println("Foo") 71 }`, 72 }, 73 { 74 RelPath: "foo_test.go", 75 Src: `package foo 76 import "testing" 77 func TestFoo(t *testing.T) { 78 Foo() 79 }`, 80 }, 81 { 82 RelPath: "vendor/bar/bar.go", 83 Src: `package bar 84 import "fmt" 85 func Bar() { 86 fmt.Println("Bar") 87 }`, 88 }, 89 }, 90 config: unindent(`exclude: 91 paths: 92 - "vendor" 93 `), 94 wantMatch: func(currCaseTmpDir string) string { 95 return "ok \t" + pkgName(t, currCaseTmpDir) + "\t[0-9.]+s" 96 }, 97 }, 98 { 99 name: "failing tests", 100 filesToCreate: []gofiles.GoFileSpec{ 101 { 102 RelPath: "foo.go", 103 Src: `package foo 104 import "fmt" 105 func Foo() { 106 fmt.Println("Foo") 107 }`, 108 }, 109 { 110 RelPath: "foo_test.go", 111 Src: `package foo 112 import "testing" 113 func TestFoo(t *testing.T) { 114 Foo() 115 t.Errorf("myFail") 116 }`, 117 }, 118 }, 119 wantMatch: func(currCaseTmpDir string) string { 120 return `(?s)` + 121 `Foo\n--- FAIL: TestFoo (.+)\n.+foo_test.go:[0-9]+: myFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + "\t[0-9.]+s" 122 }, 123 wantError: "(?s).+1 package had failing tests:.+", 124 }, 125 { 126 name: "fails if there are no packages to test (no buildable Go files)", 127 wantMatch: func(currCaseTmpDir string) string { 128 return "^no packages to test\n$" 129 }, 130 wantError: "^no packages to test\n$", 131 }, 132 { 133 name: "test that does not compile fails", 134 filesToCreate: []gofiles.GoFileSpec{ 135 { 136 RelPath: "foo.go", 137 Src: `package foo 138 import "fmt" 139 func Foo() { 140 fmt.Println("Foo") 141 }`, 142 }, 143 { 144 RelPath: "foo_test.go", 145 Src: `package foo 146 import "testing" 147 import "github.com/palantir/godel/apps/gunit/blah/foo" 148 func TestFoo(t *testing.T) { 149 foo.Foo() 150 t.Errorf("myFail") 151 }`, 152 }, 153 }, 154 wantMatch: func(currCaseTmpDir string) string { 155 return `(?s)` + 156 `foo_test.go:[0-9]+:[0-9]+: cannot find package.+\nFAIL\t` + pkgName(t, currCaseTmpDir) + `\t\[setup failed\]` 157 }, 158 wantError: "(?s).+1 package had failing tests:.+", 159 }, 160 { 161 name: "running with a tag runs only tagged tests", 162 filesToCreate: []gofiles.GoFileSpec{ 163 { 164 RelPath: "foo_test.go", 165 Src: `package foo 166 import "testing" 167 func TestFoo(t *testing.T) { 168 t.Errorf("fooFail") 169 }`, 170 }, 171 { 172 RelPath: "integration/bar_test.go", 173 Src: `package bar 174 import "testing" 175 func TestBar(t *testing.T) { 176 t.Errorf("barFail") 177 }`, 178 }, 179 }, 180 config: unindent(`tags: 181 integration: 182 names: 183 - "integration" 184 exclude: 185 paths: 186 - "vendor" 187 `), 188 args: []string{ 189 "--tags", "integration", 190 }, 191 wantMatch: func(currCaseTmpDir string) string { 192 return `(?s)` + 193 `--- FAIL: TestBar (.+)\n.+bar_test.go:[0-9]+: barFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + "/integration\t[0-9.]+s" 194 }, 195 wantError: "(?s).+1 package had failing tests:.+", 196 }, 197 { 198 name: "tag will skip excluded tests", 199 filesToCreate: []gofiles.GoFileSpec{ 200 { 201 RelPath: "foo_test.go", 202 Src: `package foo 203 import "testing" 204 func TestFoo(t *testing.T) { 205 t.Errorf("fooFail") 206 }`, 207 }, 208 { 209 RelPath: "integration/bar_test.go", 210 Src: `package bar 211 import "testing" 212 func TestBar(t *testing.T) { 213 t.Errorf("barFail") 214 }`, 215 }, 216 { 217 RelPath: "integration/baz/baz_test.go", 218 Src: `package baz 219 import "testing" 220 func TestBaz(t *testing.T) { 221 t.Errorf("bazFail") 222 }`, 223 }, 224 { 225 RelPath: "integration/exclude/exclude_test.go", 226 Src: `package exclude 227 import "testing" 228 func TestExclude(t *testing.T) { 229 t.Errorf("exclude") 230 }`, 231 }, 232 }, 233 config: unindent(`tags: 234 integration: 235 names: 236 - "integration" 237 exclude: 238 paths: 239 - "integration/exclude" 240 exclude: 241 paths: 242 - "vendor" 243 `), 244 args: []string{ 245 "--tags", "integration", 246 }, 247 wantMatch: func(currCaseTmpDir string) string { 248 return `(?s)` + 249 `--- FAIL: TestBar (.+)\n.+bar_test.go:[0-9]+: barFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + `/integration\s+[0-9.]+s.+` + 250 `--- FAIL: TestBaz (.+)\n.+baz_test.go:[0-9]+: bazFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + `/integration/baz\s+[0-9.]+s.+` 251 }, 252 wantError: "(?s).+2 packages had failing tests:.+", 253 }, 254 { 255 name: "tags are case-insensitive", 256 filesToCreate: []gofiles.GoFileSpec{ 257 { 258 RelPath: "foo_test.go", 259 Src: `package foo 260 import "testing" 261 func TestFoo(t *testing.T) { 262 t.Errorf("fooFail") 263 }`, 264 }, 265 { 266 RelPath: "integration/bar_test.go", 267 Src: `package bar 268 import "testing" 269 func TestBar(t *testing.T) { 270 t.Errorf("barFail") 271 }`, 272 }, 273 }, 274 config: unindent(`tags: 275 integration: 276 names: 277 - "integration" 278 exclude: 279 paths: 280 - "vendor" 281 `), 282 args: []string{ 283 "--tags", "INTEGRATION", 284 }, 285 wantMatch: func(currCaseTmpDir string) string { 286 return `(?s)` + 287 `--- FAIL: TestBar (.+)\n.+bar_test.go:[0-9]+: barFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + "/integration\t[0-9.]+s" 288 }, 289 wantError: "(?s).+1 package had failing tests:.+", 290 }, 291 { 292 name: "tags can be specified multiple times", 293 filesToCreate: []gofiles.GoFileSpec{ 294 { 295 RelPath: "foo_test.go", 296 Src: `package foo 297 import "testing" 298 func TestFoo(t *testing.T) { 299 t.Errorf("fooFail") 300 }`, 301 }, 302 { 303 RelPath: "integration/bar_test.go", 304 Src: `package bar 305 import "testing" 306 func TestBar(t *testing.T) { 307 t.Errorf("barFail") 308 }`, 309 }, 310 }, 311 config: unindent(`tags: 312 integration: 313 names: 314 - "integration" 315 exclude: 316 paths: 317 - "vendor" 318 `), 319 args: []string{ 320 "--tags", "INTEGRATION,integration", 321 }, 322 wantMatch: func(currCaseTmpDir string) string { 323 return `(?s)` + 324 `--- FAIL: TestBar (.+)\n.+bar_test.go:[0-9]+: barFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + "/integration\t[0-9.]+s" 325 }, 326 wantError: "(?s).+1 package had failing tests:.+", 327 }, 328 { 329 name: "fails if tags do not match any packages", 330 filesToCreate: []gofiles.GoFileSpec{ 331 { 332 RelPath: "foo_test.go", 333 Src: `package foo 334 import "testing" 335 func TestFoo(t *testing.T) { 336 t.Errorf("fooFail") 337 }`, 338 }, 339 }, 340 config: unindent(`tags: 341 integration: 342 names: 343 - "integration" 344 `), 345 args: []string{ 346 "--tags", "integration", 347 }, 348 wantMatch: func(currCaseTmpDir string) string { 349 return "^no packages to test\n$" 350 }, 351 wantError: "^no packages to test\n$", 352 }, 353 { 354 name: "union of tags is run when multiple tags are specified", 355 filesToCreate: []gofiles.GoFileSpec{ 356 { 357 RelPath: "foo_test.go", 358 Src: `package foo 359 import "testing" 360 func TestFoo(t *testing.T) { 361 t.Errorf("fooFail") 362 }`, 363 }, 364 { 365 RelPath: "bar/bar_test.go", 366 Src: `package bar 367 import "testing" 368 func TestBar(t *testing.T) { 369 t.Errorf("barFail") 370 }`, 371 }, 372 { 373 RelPath: "baz/baz_test.go", 374 Src: `package baz 375 import "testing" 376 func TestBaz(t *testing.T) { 377 t.Errorf("bazFail") 378 }`, 379 }, 380 }, 381 config: unindent(`tags: 382 bar: 383 paths: 384 - "bar" 385 baz: 386 paths: 387 - "baz" 388 exclude: 389 paths: 390 - "vendor" 391 `), 392 args: []string{ 393 "--tags", "bar,baz", 394 }, 395 wantMatch: func(currCaseTmpDir string) string { 396 return `(?s)` + 397 `--- FAIL: TestBar (.+)\n.+bar_test.go:[0-9]+: barFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + `/bar\t[0-9.]+s.+` + 398 `--- FAIL: TestBaz (.+)\n.+baz_test.go:[0-9]+: bazFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + `/baz\t[0-9.]+s.+` 399 }, 400 wantError: "(?s).+2 packages had failing tests:.+", 401 }, 402 { 403 name: "only non-tagged tests are run if multiple tags are specified and tests are run without arguments", 404 filesToCreate: []gofiles.GoFileSpec{ 405 { 406 RelPath: "foo_test.go", 407 Src: `package foo 408 import "testing" 409 func TestFoo(t *testing.T) { 410 t.Errorf("fooFail") 411 }`, 412 }, 413 { 414 RelPath: "bar/bar_test.go", 415 Src: `package bar 416 import "testing" 417 func TestBar(t *testing.T) { 418 t.Errorf("barFail") 419 }`, 420 }, 421 { 422 RelPath: "baz/baz_test.go", 423 Src: `package baz 424 import "testing" 425 func TestBaz(t *testing.T) { 426 t.Errorf("bazFail") 427 }`, 428 }, 429 }, 430 config: unindent(`tags: 431 bar: 432 paths: 433 - "bar" 434 baz: 435 paths: 436 - "baz" 437 exclude: 438 paths: 439 - "vendor" 440 `), 441 wantMatch: func(currCaseTmpDir string) string { 442 return `(?s)` + 443 `--- FAIL: TestFoo (.+)\n.+foo_test.go:[0-9]+: fooFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + `\t[0-9.]+s.+` 444 }, 445 wantError: "(?s).+1 package had failing tests:.+", 446 }, 447 { 448 name: "all tests (tagged and non-tagged) are run if 'all' is specified as the tag", 449 filesToCreate: []gofiles.GoFileSpec{ 450 { 451 RelPath: "foo_test.go", 452 Src: `package foo 453 import "testing" 454 func TestFoo(t *testing.T) { 455 t.Errorf("fooFail") 456 }`, 457 }, 458 { 459 RelPath: "bar/bar_test.go", 460 Src: `package bar 461 import "testing" 462 func TestBar(t *testing.T) { 463 t.Errorf("barFail") 464 }`, 465 }, 466 { 467 RelPath: "baz/baz_test.go", 468 Src: `package baz 469 import "testing" 470 func TestBaz(t *testing.T) { 471 t.Errorf("bazFail") 472 }`, 473 }, 474 }, 475 config: unindent(`tags: 476 bar: 477 paths: 478 - "bar" 479 baz: 480 paths: 481 - "baz" 482 exclude: 483 paths: 484 - "vendor" 485 `), 486 args: []string{ 487 "--tags", "all", 488 }, 489 wantMatch: func(currCaseTmpDir string) string { 490 return `(?s)` + 491 `--- FAIL: TestFoo (.+)\n.+foo_test.go:[0-9]+: fooFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + `\s+[0-9.]+s.+` + 492 `--- FAIL: TestBar (.+)\n.+bar_test.go:[0-9]+: barFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + `/bar\s+[0-9.]+s.+` + 493 `--- FAIL: TestBaz (.+)\n.+baz_test.go:[0-9]+: bazFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + `/baz\s+[0-9.]+s.+` 494 }, 495 wantError: "(?s).+3 packages had failing tests:.+", 496 }, 497 { 498 name: "fails if invalid tag is supplied", 499 filesToCreate: []gofiles.GoFileSpec{ 500 { 501 RelPath: "foo_test.go", 502 Src: `package foo 503 import "testing" 504 func TestFoo(t *testing.T) { 505 t.Errorf("fooFail") 506 }`, 507 }, 508 }, 509 config: unindent(`exclude: 510 paths: 511 - "vendor" 512 `), 513 args: []string{ 514 "--tags", "invalid,n!otvalid", 515 }, 516 wantMatch: func(currCaseTmpDir string) string { 517 return `invalid tags: "invalid", "n!otvalid"` 518 }, 519 wantError: `invalid tags: "invalid", "n!otvalid"`, 520 }, 521 { 522 name: "fails if 'all' is supplied as a non-exclusive tag", 523 filesToCreate: []gofiles.GoFileSpec{ 524 { 525 RelPath: "foo_test.go", 526 Src: `package foo 527 import "testing" 528 func TestFoo(t *testing.T) { 529 t.Errorf("fooFail") 530 }`, 531 }, 532 { 533 RelPath: "integration/bar_test.go", 534 Src: `package bar 535 import "testing" 536 func TestBar(t *testing.T) { 537 t.Errorf("barFail") 538 }`, 539 }, 540 }, 541 config: unindent(`tags: 542 integration: 543 names: 544 - "integration" 545 exclude: 546 paths: 547 - "vendor" 548 `), 549 args: []string{ 550 "--tags", "integration,all", 551 }, 552 wantError: regexp.QuoteMeta(`if "all" tag is specified, it must be the only tag specified`), 553 }, 554 { 555 name: "detects race conditions if race flag is supplied", 556 filesToCreate: []gofiles.GoFileSpec{ 557 { 558 RelPath: "foo_test.go", 559 Src: `package foo 560 import ( 561 "fmt" 562 "math/rand" 563 "testing" 564 "time" 565 ) 566 func TestFoo(t *testing.T) { 567 timeTest() 568 } 569 func timeTest() { 570 start := time.Now() 571 var t *time.Timer 572 t = time.AfterFunc(randomDuration(), func() { 573 fmt.Println(time.Now().Sub(start)) 574 t.Reset(randomDuration()) 575 }) 576 time.Sleep(1 * time.Second) 577 } 578 func randomDuration() time.Duration { 579 return time.Duration(rand.Int63n(1e9)) 580 }`, 581 }, 582 }, 583 args: []string{ 584 "--race", 585 }, 586 wantMatch: func(currCaseTmpDir string) string { 587 return `(?s).+WARNING: DATA RACE\n.+` 588 }, 589 wantError: `(?s).+WARNING: DATA RACE\n.+`, 590 }, 591 } { 592 currCaseTmpDir, err := ioutil.TempDir(tmpDir, "") 593 require.NoError(t, err, "Case %d: %s", i, currCase.name) 594 595 _, err = gofiles.Write(currCaseTmpDir, currCase.filesToCreate) 596 require.NoError(t, err, "Case %d: %s", i, currCase.name) 597 598 configFile := path.Join(currCaseTmpDir, "config.yml") 599 err = ioutil.WriteFile(configFile, []byte(currCase.config), 0644) 600 require.NoError(t, err, "Case %d: %s", i, currCase.name) 601 602 err = os.Chdir(currCaseTmpDir) 603 require.NoError(t, err) 604 605 args := []string{"--config", configFile} 606 args = append(args, currCase.args...) 607 args = append(args, "test") 608 609 cmd := exec.Command(cli, args...) 610 outputBytes, err := cmd.CombinedOutput() 611 output := string(outputBytes) 612 613 if err != nil { 614 if currCase.wantError == "" { 615 t.Fatalf("Case %d: %s\nunexpected error:\n%v\nOutput: %v", i, currCase.name, err, output) 616 } else if !regexp.MustCompile(currCase.wantError).MatchString(output) { 617 t.Fatalf("Case %d: %s\nexpected error output to contain %v, but was %v", i, currCase.name, currCase.wantError, output) 618 } 619 } else if currCase.wantError != "" { 620 t.Fatalf("Case %d: %s\nexpected error %v, but was none.\nOutput: %v", i, currCase.name, currCase.wantError, output) 621 } 622 623 if currCase.wantMatch != nil { 624 expectedExpr := currCase.wantMatch(currCaseTmpDir) 625 if !regexp.MustCompile(expectedExpr).MatchString(output) { 626 t.Errorf("Case %d: %s\nOutput did not match expected expression.\nExpected:\n%v\nActual:\n%v", i, currCase.name, expectedExpr, output) 627 } 628 } 629 } 630 } 631 632 func TestClean(t *testing.T) { 633 cli, err := products.Bin("gunit") 634 require.NoError(t, err) 635 636 wd, err := os.Getwd() 637 require.NoError(t, err) 638 639 tmpDir, cleanup, err := dirs.TempDir(wd, "") 640 defer cleanup() 641 require.NoError(t, err) 642 643 files, err := gofiles.Write(tmpDir, []gofiles.GoFileSpec{ 644 { 645 RelPath: "tmp_placeholder_test.go", 646 Src: "package main", 647 }, 648 }) 649 require.NoError(t, err) 650 651 originalWd, err := os.Getwd() 652 require.NoError(t, err) 653 defer func() { 654 if err := os.Chdir(originalWd); err != nil { 655 require.NoError(t, err) 656 } 657 }() 658 659 err = os.Chdir(tmpDir) 660 require.NoError(t, err) 661 662 cmd := exec.Command(cli, "clean") 663 output, err := cmd.CombinedOutput() 664 require.NoError(t, err, "Command %v failed with output:\n%s", cmd.Args, string(output)) 665 666 _, err = os.Stat(files["tmp_placeholder_test.go"].Path) 667 assert.True(t, os.IsNotExist(err)) 668 } 669 670 func unindent(input string) string { 671 return strings.Replace(input, "\n\t\t\t\t\t", "\n", -1) 672 } 673 674 func pkgName(t *testing.T, path string) string { 675 pkgPath, err := pkgpath.NewAbsPkgPath(path).GoPathSrcRel() 676 require.NoError(t, err) 677 return pkgPath 678 }