github.com/jonsyu1/godel@v0.0.0-20171017211503-64567a0cf169/apps/gunit/app_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 gunit_test 16 17 import ( 18 "bytes" 19 "fmt" 20 "io/ioutil" 21 "os" 22 "os/exec" 23 "path" 24 "regexp" 25 "strings" 26 "testing" 27 28 "github.com/nmiyake/pkg/dirs" 29 "github.com/nmiyake/pkg/gofiles" 30 "github.com/palantir/amalgomate/amalgomated" 31 "github.com/palantir/pkg/pkgpath" 32 "github.com/pkg/errors" 33 "github.com/stretchr/testify/assert" 34 "github.com/stretchr/testify/require" 35 36 "github.com/palantir/godel/apps/gunit" 37 ) 38 39 func TestMain(m *testing.M) { 40 code := testHelper(m) 41 os.Exit(code) 42 } 43 44 var supplier amalgomated.CmderSupplier 45 46 func testHelper(m *testing.M) int { 47 tmpDir, cleanup, err := dirs.TempDir("", "") 48 defer cleanup() 49 if err != nil { 50 panic(err) 51 } 52 53 libraries := map[string]string{ 54 "gocover": "./vendor/github.com/nmiyake/gotest", 55 "gojunitreport": "./vendor/github.com/jstemmer/go-junit-report", 56 "gotest": "./vendor/github.com/nmiyake/gotest", 57 "gt": "./vendor/rsc.io/gt", 58 } 59 60 supplier = temporaryBuildRunnerSupplier(tmpDir, libraries) 61 return m.Run() 62 } 63 64 func TestRun(t *testing.T) { 65 wd, err := os.Getwd() 66 require.NoError(t, err) 67 68 tmpDir, cleanup, err := dirs.TempDir(wd, "") 69 defer cleanup() 70 require.NoError(t, err) 71 72 originalWd, err := os.Getwd() 73 require.NoError(t, err) 74 defer func() { 75 if err := os.Chdir(originalWd); err != nil { 76 fmt.Printf("Failed to set wd to %v: %v", originalWd, err) 77 } 78 }() 79 80 for i, currCase := range []struct { 81 name string 82 filesToCreate []gofiles.GoFileSpec 83 config string 84 args []string 85 wantMatch func(currCaseTmpDir string) string 86 wantError string 87 }{ 88 { 89 name: "passing tests", 90 filesToCreate: []gofiles.GoFileSpec{ 91 { 92 RelPath: "foo.go", 93 Src: `package foo 94 import "fmt" 95 func Foo() { 96 fmt.Println("Foo") 97 }`, 98 }, 99 { 100 RelPath: "foo_test.go", 101 Src: `package foo 102 import "testing" 103 func TestFoo(t *testing.T) { 104 Foo() 105 }`, 106 }, 107 { 108 RelPath: "vendor/bar/bar.go", 109 Src: `package bar 110 import "fmt" 111 func Bar() { 112 fmt.Println("Bar") 113 }`, 114 }, 115 }, 116 config: unindent(`exclude: 117 paths: 118 - "vendor" 119 `), 120 wantMatch: func(currCaseTmpDir string) string { 121 return "ok \t" + pkgName(t, currCaseTmpDir) + "\t[0-9.]+s" 122 }, 123 }, 124 { 125 name: "failing tests", 126 filesToCreate: []gofiles.GoFileSpec{ 127 { 128 RelPath: "foo.go", 129 Src: `package foo 130 import "fmt" 131 func Foo() { 132 fmt.Println("Foo") 133 }`, 134 }, 135 { 136 RelPath: "foo_test.go", 137 Src: `package foo 138 import "testing" 139 func TestFoo(t *testing.T) { 140 Foo() 141 t.Errorf("myFail") 142 }`, 143 }, 144 }, 145 wantMatch: func(currCaseTmpDir string) string { 146 return `(?s)` + 147 `Foo\n--- FAIL: TestFoo (.+)\n.+foo_test.go:[0-9]+: myFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + "\t[0-9.]+s" 148 }, 149 wantError: "(?s).+1 package had failing tests:.+", 150 }, 151 { 152 name: "test that does not compile fails", 153 filesToCreate: []gofiles.GoFileSpec{ 154 { 155 RelPath: "foo.go", 156 Src: `package foo 157 import "fmt" 158 func Foo() { 159 fmt.Println("Foo") 160 }`, 161 }, 162 { 163 RelPath: "foo_test.go", 164 Src: `package foo 165 import "testing" 166 import "github.com/palantir/godel/apps/gunit/blah/foo" 167 func TestFoo(t *testing.T) { 168 foo.Foo() 169 t.Errorf("myFail") 170 }`, 171 }, 172 }, 173 wantMatch: func(currCaseTmpDir string) string { 174 return `(?s)` + 175 `foo_test.go:[0-9]+:[0-9]+: cannot find package.+\nFAIL\t` + pkgName(t, currCaseTmpDir) + `\t\[setup failed\]` 176 }, 177 wantError: "(?s).+1 package had failing tests:.+", 178 }, 179 { 180 name: "running with a tag runs only tagged tests", 181 filesToCreate: []gofiles.GoFileSpec{ 182 { 183 RelPath: "foo_test.go", 184 Src: `package foo 185 import "testing" 186 func TestFoo(t *testing.T) { 187 t.Errorf("fooFail") 188 }`, 189 }, 190 { 191 RelPath: "integration/bar_test.go", 192 Src: `package bar 193 import "testing" 194 func TestBar(t *testing.T) { 195 t.Errorf("barFail") 196 }`, 197 }, 198 }, 199 config: unindent(`tags: 200 integration: 201 names: 202 - "integration" 203 exclude: 204 paths: 205 - "vendor" 206 `), 207 args: []string{ 208 "--tags", "integration", 209 }, 210 wantMatch: func(currCaseTmpDir string) string { 211 return `(?s)` + 212 `--- FAIL: TestBar (.+)\n.+bar_test.go:[0-9]+: barFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + "/integration\t[0-9.]+s" 213 }, 214 wantError: "(?s).+1 package had failing tests:.+", 215 }, 216 { 217 name: "union of tags is run when multiple tags are specified", 218 filesToCreate: []gofiles.GoFileSpec{ 219 { 220 RelPath: "foo_test.go", 221 Src: `package foo 222 import "testing" 223 func TestFoo(t *testing.T) { 224 t.Errorf("fooFail") 225 }`, 226 }, 227 { 228 RelPath: "bar/bar_test.go", 229 Src: `package bar 230 import "testing" 231 func TestBar(t *testing.T) { 232 t.Errorf("barFail") 233 }`, 234 }, 235 { 236 RelPath: "baz/baz_test.go", 237 Src: `package baz 238 import "testing" 239 func TestBaz(t *testing.T) { 240 t.Errorf("bazFail") 241 }`, 242 }, 243 }, 244 config: unindent(`tags: 245 bar: 246 paths: 247 - "bar" 248 baz: 249 paths: 250 - "baz" 251 exclude: 252 paths: 253 - "vendor" 254 `), 255 args: []string{ 256 "--tags", "bar,baz", 257 }, 258 wantMatch: func(currCaseTmpDir string) string { 259 return `(?s)` + 260 `--- FAIL: TestBar (.+)\n.+bar_test.go:[0-9]+: barFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + `/bar\t[0-9.]+s.+` + 261 `--- FAIL: TestBaz (.+)\n.+baz_test.go:[0-9]+: bazFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + `/baz\t[0-9.]+s.+` 262 }, 263 wantError: "(?s).+2 packages had failing tests:.+", 264 }, 265 { 266 name: "only non-tagged tests are run if multiple tags are specified and tests are run with none argument", 267 filesToCreate: []gofiles.GoFileSpec{ 268 { 269 RelPath: "foo_test.go", 270 Src: `package foo 271 import "testing" 272 func TestFoo(t *testing.T) { 273 t.Errorf("fooFail") 274 }`, 275 }, 276 { 277 RelPath: "bar/bar_test.go", 278 Src: `package bar 279 import "testing" 280 func TestBar(t *testing.T) { 281 t.Errorf("barFail") 282 }`, 283 }, 284 { 285 RelPath: "baz/baz_test.go", 286 Src: `package baz 287 import "testing" 288 func TestBaz(t *testing.T) { 289 t.Errorf("bazFail") 290 }`, 291 }, 292 }, 293 config: unindent(`tags: 294 bar: 295 paths: 296 - "bar" 297 baz: 298 paths: 299 - "baz" 300 exclude: 301 paths: 302 - "vendor" 303 `), 304 args: []string{ 305 "--tags", "none", 306 }, 307 wantMatch: func(currCaseTmpDir string) string { 308 return `(?s)` + 309 `--- FAIL: TestFoo (.+)\n.+foo_test.go:[0-9]+: fooFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + `\t[0-9.]+s.+` 310 }, 311 wantError: "(?s).+1 package had failing tests:.+", 312 }, 313 { 314 name: "all tests are run if multiple tags are specified and tests are run without arguments", 315 filesToCreate: []gofiles.GoFileSpec{ 316 { 317 RelPath: "foo_test.go", 318 Src: `package foo 319 import "testing" 320 func TestFoo(t *testing.T) { 321 t.Errorf("fooFail") 322 }`, 323 }, 324 { 325 RelPath: "bar/bar_test.go", 326 Src: `package bar 327 import "testing" 328 func TestBar(t *testing.T) { 329 t.Errorf("barFail") 330 }`, 331 }, 332 { 333 RelPath: "baz/baz_test.go", 334 Src: `package baz 335 import "testing" 336 func TestBaz(t *testing.T) { 337 t.Errorf("bazFail") 338 }`, 339 }, 340 }, 341 config: unindent(`tags: 342 bar: 343 paths: 344 - "bar" 345 baz: 346 paths: 347 - "baz" 348 exclude: 349 paths: 350 - "vendor" 351 `), 352 wantMatch: func(currCaseTmpDir string) string { 353 return `(?s)` + 354 `--- FAIL: TestFoo (.+)\n.+foo_test.go:[0-9]+: fooFail.+FAIL\s+` + pkgName(t, currCaseTmpDir) + `\s+[0-9.]+s.+` + 355 `--- FAIL: TestBar (.+)\n.+bar_test.go:[0-9]+: barFail.+FAIL\s+` + pkgName(t, currCaseTmpDir) + `/bar\s+[0-9.]+s.+` + 356 `--- FAIL: TestBaz (.+)\n.+baz_test.go:[0-9]+: bazFail.+FAIL\s+` + pkgName(t, currCaseTmpDir) + `/baz\s+[0-9.]+s.+` 357 }, 358 wantError: "(?s).+3 packages had failing tests:.+", 359 }, 360 { 361 name: "fails if invalid tag is supplied", 362 filesToCreate: []gofiles.GoFileSpec{ 363 { 364 RelPath: "foo_test.go", 365 Src: `package foo 366 import "testing" 367 func TestFoo(t *testing.T) { 368 t.Errorf("fooFail") 369 }`, 370 }, 371 }, 372 config: unindent(`exclude: 373 paths: 374 - "vendor" 375 `), 376 args: []string{ 377 "--tags", "invalid,n!otvalid", 378 }, 379 wantMatch: func(currCaseTmpDir string) string { 380 return `Tags "invalid", "n!otvalid" not defined in configuration. No tags are defined.` 381 }, 382 wantError: `Tags "invalid", "n!otvalid" not defined in configuration. No tags are defined.`, 383 }, 384 { 385 name: "fails if invalid tag is supplied and tag exists", 386 filesToCreate: []gofiles.GoFileSpec{ 387 { 388 RelPath: "foo_test.go", 389 Src: `package foo 390 import "testing" 391 func TestFoo(t *testing.T) { 392 t.Errorf("fooFail") 393 }`, 394 }, 395 }, 396 config: unindent(`tags: 397 bar: 398 paths: 399 - "bar" 400 exclude: 401 paths: 402 - "vendor" 403 other: 404 paths: 405 - "other" 406 `), 407 args: []string{ 408 "--tags", "invalid,n!otvalid", 409 }, 410 wantMatch: func(currCaseTmpDir string) string { 411 return `Tags "invalid", "n!otvalid" not defined in configuration. Valid tags: "bar", "exclude", "other"` 412 }, 413 wantError: `Tags "invalid", "n!otvalid" not defined in configuration. Valid tags: "bar", "exclude", "other"`, 414 }, 415 } { 416 currCaseTmpDir, err := ioutil.TempDir(tmpDir, "") 417 require.NoError(t, err, "Case %d: %s", i, currCase.name) 418 419 _, err = gofiles.Write(currCaseTmpDir, currCase.filesToCreate) 420 require.NoError(t, err, "Case %d: %s", i, currCase.name) 421 422 configFile := path.Join(currCaseTmpDir, "config.yml") 423 err = ioutil.WriteFile(configFile, []byte(currCase.config), 0644) 424 require.NoError(t, err, "Case %d: %s", i, currCase.name) 425 426 outBuf := bytes.Buffer{} 427 428 err = os.Chdir(currCaseTmpDir) 429 require.NoError(t, err) 430 431 app := gunit.App(supplier) 432 app.Stdout = &outBuf 433 app.Stderr = &outBuf 434 435 args := []string{"gunit", "--config", configFile} 436 args = append(args, currCase.args...) 437 args = append(args, "test") 438 439 runTestCode := app.Run(args) 440 output := outBuf.String() 441 442 if runTestCode != 0 { 443 if currCase.wantError == "" { 444 t.Fatalf("Case %d: %s\nunexpected error:\n%v\nOutput: %v", i, currCase.name, err, output) 445 } else if !regexp.MustCompile(currCase.wantError).MatchString(output) { 446 t.Fatalf("Case %d: %s\nexpected error output to contain %v, but was %v", i, currCase.name, currCase.wantError, output) 447 } 448 } else if currCase.wantError != "" { 449 t.Fatalf("Case %d: %s\nexpected error %v, but was none.\nOutput: %v", i, currCase.name, currCase.wantError, output) 450 } 451 452 expectedExpr := currCase.wantMatch(currCaseTmpDir) 453 if !regexp.MustCompile(expectedExpr).MatchString(output) { 454 t.Errorf("Case %d: %s\nOutput did not match expected expression.\nExpected:\n%v\nActual:\n%v", i, currCase.name, expectedExpr, output) 455 } 456 } 457 } 458 459 func TestClean(t *testing.T) { 460 wd, err := os.Getwd() 461 require.NoError(t, err) 462 463 tmpDir, cleanup, err := dirs.TempDir(wd, "") 464 defer cleanup() 465 require.NoError(t, err) 466 467 err = ioutil.WriteFile(path.Join(tmpDir, "tmp_placeholder_test.go"), []byte("package main"), 0644) 468 require.NoError(t, err) 469 470 originalWd, err := os.Getwd() 471 require.NoError(t, err) 472 defer func() { 473 if err := os.Chdir(originalWd); err != nil { 474 fmt.Printf("%+v\n", errors.Wrapf(err, "failed to restore working directory to %s", originalWd)) 475 } 476 }() 477 478 err = os.Chdir(tmpDir) 479 require.NoError(t, err) 480 481 app := gunit.App(supplier) 482 exitCode := app.Run([]string{"gunit", "clean"}) 483 require.Equal(t, 0, exitCode) 484 485 _, err = os.Stat(path.Join(tmpDir, "tmp_placeholder_test.go")) 486 assert.True(t, os.IsNotExist(err)) 487 } 488 489 func temporaryBuildRunnerSupplier(tmpDir string, libraries map[string]string) amalgomated.CmderSupplier { 490 wd, err := os.Getwd() 491 if err != nil { 492 panic(err) 493 } 494 495 // build executables for all commands once 496 for currCmd, currLibrary := range libraries { 497 executable := path.Join(tmpDir, currCmd) 498 cmd := exec.Command("go", "build", "-o", executable) 499 cmd.Dir = path.Join(wd, currLibrary) 500 501 bytes, err := cmd.CombinedOutput() 502 if err != nil { 503 panic(fmt.Errorf("go build failed\nPath: %v\nArgs: %v\nDir: %v\nOutput: %v", cmd.Path, cmd.Args, cmd.Dir, string(bytes))) 504 } 505 } 506 507 // return supplier that points to built executables 508 return func(cmd amalgomated.Cmd) (amalgomated.Cmder, error) { 509 return amalgomated.PathCmder(path.Join(tmpDir, cmd.Name())), nil 510 } 511 } 512 513 func unindent(input string) string { 514 return strings.Replace(input, "\n\t\t\t\t\t", "\n", -1) 515 } 516 517 func pkgName(t *testing.T, path string) string { 518 pkgPath, err := pkgpath.NewAbsPkgPath(path).GoPathSrcRel() 519 require.NoError(t, err) 520 return pkgPath 521 }