github.com/joshdk/godel@v0.0.0-20170529232908-862138a45aee/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)Foo\n--- FAIL: TestFoo (.+)\n.+foo_test.go:[0-9]+: myFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + "\t[0-9.]+s" 147 }, 148 wantError: "(?s).+1 package had failing tests:.+", 149 }, 150 { 151 name: "test that does not compile fails", 152 filesToCreate: []gofiles.GoFileSpec{ 153 { 154 RelPath: "foo.go", 155 Src: `package foo 156 import "fmt" 157 func Foo() { 158 fmt.Println("Foo") 159 }`, 160 }, 161 { 162 RelPath: "foo_test.go", 163 Src: `package foo 164 import "testing" 165 import "github.com/palantir/godel/apps/gunit/blah/foo" 166 func TestFoo(t *testing.T) { 167 foo.Foo() 168 t.Errorf("myFail") 169 }`, 170 }, 171 }, 172 wantMatch: func(currCaseTmpDir string) string { 173 return `(?s)foo_test.go:[0-9]+:[0-9]+: cannot find package.+\nFAIL\t` + pkgName(t, currCaseTmpDir) + `\t\[setup failed\]` 174 }, 175 wantError: "(?s).+1 package had failing tests:.+", 176 }, 177 { 178 name: "running with a tag runs only tagged tests", 179 filesToCreate: []gofiles.GoFileSpec{ 180 { 181 RelPath: "foo_test.go", 182 Src: `package foo 183 import "testing" 184 func TestFoo(t *testing.T) { 185 t.Errorf("fooFail") 186 }`, 187 }, 188 { 189 RelPath: "integration/bar_test.go", 190 Src: `package bar 191 import "testing" 192 func TestBar(t *testing.T) { 193 t.Errorf("barFail") 194 }`, 195 }, 196 }, 197 config: unindent(`tags: 198 integration: 199 names: 200 - "integration" 201 exclude: 202 paths: 203 - "vendor" 204 `), 205 args: []string{ 206 "--tags", "integration", 207 }, 208 wantMatch: func(currCaseTmpDir string) string { 209 return `(?s)--- FAIL: TestBar (.+)\n.+bar_test.go:[0-9]+: barFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + "/integration\t[0-9.]+s" 210 }, 211 wantError: "(?s).+1 package had failing tests:.+", 212 }, 213 { 214 name: "union of tags is run when multiple tags are specified", 215 filesToCreate: []gofiles.GoFileSpec{ 216 { 217 RelPath: "foo_test.go", 218 Src: `package foo 219 import "testing" 220 func TestFoo(t *testing.T) { 221 t.Errorf("fooFail") 222 }`, 223 }, 224 { 225 RelPath: "bar/bar_test.go", 226 Src: `package bar 227 import "testing" 228 func TestBar(t *testing.T) { 229 t.Errorf("barFail") 230 }`, 231 }, 232 { 233 RelPath: "baz/baz_test.go", 234 Src: `package baz 235 import "testing" 236 func TestBaz(t *testing.T) { 237 t.Errorf("bazFail") 238 }`, 239 }, 240 }, 241 config: unindent(`tags: 242 bar: 243 paths: 244 - "bar" 245 baz: 246 paths: 247 - "baz" 248 exclude: 249 paths: 250 - "vendor" 251 `), 252 args: []string{ 253 "--tags", "bar,baz", 254 }, 255 wantMatch: func(currCaseTmpDir string) string { 256 return `(?s)--- FAIL: TestBar (.+)\n.+bar_test.go:[0-9]+: barFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + `/bar\t[0-9.]+s.+` + 257 `--- FAIL: TestBaz (.+)\n.+baz_test.go:[0-9]+: bazFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + `/baz\t[0-9.]+s.+` 258 }, 259 wantError: "(?s).+2 packages had failing tests:.+", 260 }, 261 { 262 name: "only non-tagged tests are run if multiple tags are specified and tests are run without arguments", 263 filesToCreate: []gofiles.GoFileSpec{ 264 { 265 RelPath: "foo_test.go", 266 Src: `package foo 267 import "testing" 268 func TestFoo(t *testing.T) { 269 t.Errorf("fooFail") 270 }`, 271 }, 272 { 273 RelPath: "bar/bar_test.go", 274 Src: `package bar 275 import "testing" 276 func TestBar(t *testing.T) { 277 t.Errorf("barFail") 278 }`, 279 }, 280 { 281 RelPath: "baz/baz_test.go", 282 Src: `package baz 283 import "testing" 284 func TestBaz(t *testing.T) { 285 t.Errorf("bazFail") 286 }`, 287 }, 288 }, 289 config: unindent(`tags: 290 bar: 291 paths: 292 - "bar" 293 baz: 294 paths: 295 - "baz" 296 exclude: 297 paths: 298 - "vendor" 299 `), 300 wantMatch: func(currCaseTmpDir string) string { 301 return `(?s)--- FAIL: TestFoo (.+)\n.+foo_test.go:[0-9]+: fooFail.+FAIL\t` + pkgName(t, currCaseTmpDir) + `\t[0-9.]+s.+` 302 }, 303 wantError: "(?s).+1 package had failing tests:.+", 304 }, 305 { 306 name: "fails if invalid tag is supplied", 307 filesToCreate: []gofiles.GoFileSpec{ 308 { 309 RelPath: "foo_test.go", 310 Src: `package foo 311 import "testing" 312 func TestFoo(t *testing.T) { 313 t.Errorf("fooFail") 314 }`, 315 }, 316 }, 317 config: unindent(`exclude: 318 paths: 319 - "vendor" 320 `), 321 args: []string{ 322 "--tags", "invalid,n!otvalid", 323 }, 324 wantMatch: func(currCaseTmpDir string) string { 325 return `invalid tags: "invalid", "n!otvalid"` 326 }, 327 wantError: `invalid tags: "invalid", "n!otvalid"`, 328 }, 329 } { 330 currCaseTmpDir, err := ioutil.TempDir(tmpDir, "") 331 require.NoError(t, err, "Case %d: %s", i, currCase.name) 332 333 _, err = gofiles.Write(currCaseTmpDir, currCase.filesToCreate) 334 require.NoError(t, err, "Case %d: %s", i, currCase.name) 335 336 configFile := path.Join(currCaseTmpDir, "config.yml") 337 err = ioutil.WriteFile(configFile, []byte(currCase.config), 0644) 338 require.NoError(t, err, "Case %d: %s", i, currCase.name) 339 340 outBuf := bytes.Buffer{} 341 342 err = os.Chdir(currCaseTmpDir) 343 require.NoError(t, err) 344 345 app := gunit.App(supplier) 346 app.Stdout = &outBuf 347 app.Stderr = &outBuf 348 349 args := []string{"gunit", "--config", configFile} 350 args = append(args, currCase.args...) 351 args = append(args, "test") 352 353 runTestCode := app.Run(args) 354 output := outBuf.String() 355 356 if runTestCode != 0 { 357 if currCase.wantError == "" { 358 t.Fatalf("Case %d: %s\nunexpected error:\n%v\nOutput: %v", i, currCase.name, err, output) 359 } else if !regexp.MustCompile(currCase.wantError).MatchString(output) { 360 t.Fatalf("Case %d: %s\nexpected error output to contain %v, but was %v", i, currCase.name, currCase.wantError, output) 361 } 362 } else if currCase.wantError != "" { 363 t.Fatalf("Case %d: %s\nexpected error %v, but was none.\nOutput: %v", i, currCase.name, currCase.wantError, output) 364 } 365 366 expectedExpr := currCase.wantMatch(currCaseTmpDir) 367 if !regexp.MustCompile(expectedExpr).MatchString(output) { 368 t.Errorf("Case %d: %s\nOutput did not match expected expression.\nExpected:\n%v\nActual:\n%v", i, currCase.name, expectedExpr, output) 369 } 370 } 371 } 372 373 func TestClean(t *testing.T) { 374 wd, err := os.Getwd() 375 require.NoError(t, err) 376 377 tmpDir, cleanup, err := dirs.TempDir(wd, "") 378 defer cleanup() 379 require.NoError(t, err) 380 381 err = ioutil.WriteFile(path.Join(tmpDir, "tmp_placeholder_test.go"), []byte("package main"), 0644) 382 require.NoError(t, err) 383 384 originalWd, err := os.Getwd() 385 require.NoError(t, err) 386 defer func() { 387 if err := os.Chdir(originalWd); err != nil { 388 fmt.Printf("%+v\n", errors.Wrapf(err, "failed to restore working directory to %s", originalWd)) 389 } 390 }() 391 392 err = os.Chdir(tmpDir) 393 require.NoError(t, err) 394 395 app := gunit.App(supplier) 396 exitCode := app.Run([]string{"gunit", "clean"}) 397 require.Equal(t, 0, exitCode) 398 399 _, err = os.Stat(path.Join(tmpDir, "tmp_placeholder_test.go")) 400 assert.True(t, os.IsNotExist(err)) 401 } 402 403 func temporaryBuildRunnerSupplier(tmpDir string, libraries map[string]string) amalgomated.CmderSupplier { 404 wd, err := os.Getwd() 405 if err != nil { 406 panic(err) 407 } 408 409 // build executables for all commands once 410 for currCmd, currLibrary := range libraries { 411 executable := path.Join(tmpDir, currCmd) 412 cmd := exec.Command("go", "build", "-o", executable) 413 cmd.Dir = path.Join(wd, currLibrary) 414 415 bytes, err := cmd.CombinedOutput() 416 if err != nil { 417 panic(fmt.Errorf("go build failed\nPath: %v\nArgs: %v\nDir: %v\nOutput: %v", cmd.Path, cmd.Args, cmd.Dir, string(bytes))) 418 } 419 } 420 421 // return supplier that points to built executables 422 return func(cmd amalgomated.Cmd) (amalgomated.Cmder, error) { 423 return amalgomated.PathCmder(path.Join(tmpDir, cmd.Name())), nil 424 } 425 } 426 427 func unindent(input string) string { 428 return strings.Replace(input, "\n\t\t\t\t\t", "\n", -1) 429 } 430 431 func pkgName(t *testing.T, path string) string { 432 pkgPath, err := pkgpath.NewAbsPkgPath(path).GoPathSrcRel() 433 require.NoError(t, err) 434 return pkgPath 435 }