github.com/LawrenceWoodman/roveralls@v0.0.0-20171119193843-51b78509b607/roveralls_test.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "bytes" 6 "errors" 7 "fmt" 8 "os" 9 "path/filepath" 10 "regexp" 11 "strings" 12 "syscall" 13 "testing" 14 ) 15 16 func TestRun(t *testing.T) { 17 initProgram(os.Args, os.Stdout, os.Stderr, os.Getenv("GOPATH")) 18 cases := []struct { 19 dir string 20 cmdArgs []string 21 wantExitCode int 22 wantOutRegexps []string 23 wantFiles []string 24 }{ 25 {dir: "fixtures", 26 cmdArgs: []string{os.Args[0], "-covermode=count"}, 27 wantExitCode: 0, 28 wantOutRegexps: []string{}, 29 wantFiles: []string{ 30 filepath.Join("fixtures", "good", "good.go"), 31 filepath.Join("fixtures", "good2", "good2.go"), 32 }, 33 }, 34 {dir: "fixtures", 35 cmdArgs: []string{os.Args[0], "-covermode=count", "-v"}, 36 wantExitCode: 0, 37 wantOutRegexps: []string{ 38 "^GOPATH: .*$", 39 "^Working dir: .*$", 40 "^No Go test files in dir: ., skipping$", 41 "^Processing dir: good$", 42 "^Processing: go test -covermode=count -coverprofile=profile.coverprofile -outputdir=.*$", 43 "^Processing dir: good2$", 44 "^Processing: go test -covermode=count -coverprofile=profile.coverprofile -outputdir=.*$", 45 "^No Go test files in dir: no-go-files, skipping$", 46 "^No Go test files in dir: no-test-files, skipping$", 47 "^Processing dir: short$", 48 "^Processing: go test -covermode=count -coverprofile=profile.coverprofile -outputdir=.*$", 49 }, 50 wantFiles: []string{ 51 filepath.Join("fixtures", "good", "good.go"), 52 filepath.Join("fixtures", "good2", "good2.go"), 53 }, 54 }, 55 {dir: "fixtures", 56 cmdArgs: []string{ 57 os.Args[0], 58 "-covermode=count", 59 "-ignore=.git,vendor,good2", 60 "-short", 61 }, 62 wantExitCode: 0, 63 wantOutRegexps: []string{}, 64 wantFiles: []string{ 65 filepath.Join("fixtures", "good", "good.go"), 66 filepath.Join("fixtures", "short", "short.go"), 67 }, 68 }, 69 {dir: "fixtures", 70 cmdArgs: []string{ 71 os.Args[0], 72 "-covermode=count", 73 "-ignore=.git,vendor,good2", 74 "-v", 75 "-short", 76 }, 77 wantExitCode: 0, 78 wantOutRegexps: []string{ 79 "^GOPATH: .*$", 80 "^Working dir: .*$", 81 "^No Go test files in dir: ., skipping$", 82 "^Processing dir: good$", 83 "^Processing: go test -short -covermode=count -coverprofile=profile.coverprofile -outputdir=.*$", 84 "^No Go test files in dir: no-go-files, skipping$", 85 "^No Go test files in dir: no-test-files, skipping$", 86 "^Processing dir: short$", 87 "^Processing: go test -short -covermode=count -coverprofile=profile.coverprofile -outputdir=.*$", 88 }, 89 wantFiles: []string{ 90 filepath.Join("fixtures", "good", "good.go"), 91 filepath.Join("fixtures", "short", "short.go"), 92 }, 93 }, 94 {dir: "fixtures", 95 cmdArgs: []string{ 96 os.Args[0], 97 "-covermode=count", 98 "-short", 99 }, 100 wantExitCode: 0, 101 wantOutRegexps: []string{}, 102 wantFiles: []string{ 103 filepath.Join("fixtures", "good", "good.go"), 104 filepath.Join("fixtures", "good2", "good2.go"), 105 filepath.Join("fixtures", "short", "short.go"), 106 }, 107 }, 108 {dir: "fixtures", 109 cmdArgs: []string{os.Args[0], "-help"}, 110 wantExitCode: 0, 111 wantOutRegexps: makeUsageMsgRegexps(), 112 wantFiles: []string{}, 113 }, 114 } 115 wd, err := os.Getwd() 116 if err != nil { 117 t.Fatal(err) 118 } 119 defer os.Chdir(wd) 120 for _, c := range cases { 121 var gotOut bytes.Buffer 122 var gotErr bytes.Buffer 123 initProgram(c.cmdArgs, &gotOut, &gotErr, os.Getenv("GOPATH")) 124 if err := os.Chdir(wd); err != nil { 125 t.Fatalf("ChDir(%s) err: %s", c.dir, err) 126 } 127 if err := os.Chdir(c.dir); err != nil { 128 t.Fatalf("ChDir(%s) err: %s", c.dir, err) 129 } 130 os.Remove(filepath.Join("roveralls.coverprofile")) 131 exitCode := program.Run() 132 if exitCode != c.wantExitCode { 133 t.Errorf("Run: incorrect exit code, got: %d, want: %d", 134 exitCode, c.wantExitCode) 135 } 136 137 if gotErr.String() != "" { 138 t.Errorf("Run: gotErr: %s", gotErr.String()) 139 } 140 141 if err := checkOutput(c.wantOutRegexps, gotOut.String()); err != nil { 142 t.Errorf("checkOutput: %s", err) 143 } 144 145 gotFiles, err := filesTested(wd, "roveralls.coverprofile") 146 if len(c.wantFiles) != 0 && err != nil { 147 t.Fatalf("filesTested err: %s", err) 148 } 149 if len(gotFiles) != len(c.wantFiles) { 150 t.Errorf("Wrong files tested (cmdArgs: %s). want: %s, got: %v", 151 c.cmdArgs, c.wantFiles, gotFiles) 152 } 153 for _, wantFile := range c.wantFiles { 154 if _, ok := gotFiles[wantFile]; !ok { 155 t.Errorf("No cover entries for file: %s", wantFile) 156 } 157 } 158 } 159 } 160 161 func TestRun_errors(t *testing.T) { 162 initProgram(os.Args, os.Stdout, os.Stderr, os.Getenv("GOPATH")) 163 cases := []struct { 164 dir string 165 cmdArgs []string 166 gopath string 167 wantExitCode int 168 wantOut string 169 wantErr string 170 }{ 171 {dir: "fixtures", 172 cmdArgs: []string{os.Args[0], "-covermode=nothing"}, 173 gopath: os.Getenv("GOPATH"), 174 wantExitCode: 1, 175 wantOut: "", 176 wantErr: "invalid covermode 'nothing'\n" + usageMsg(), 177 }, 178 {dir: "fixtures", 179 cmdArgs: []string{os.Args[0], "-bob"}, 180 gopath: os.Getenv("GOPATH"), 181 wantExitCode: 1, 182 wantOut: "", 183 wantErr: "flag provided but not defined: -bob\n" + usagePartialMsg(), 184 }, 185 {dir: "fixtures", 186 cmdArgs: []string{os.Args[0], "-covermode=count"}, 187 gopath: "", 188 wantExitCode: 1, 189 wantOut: "", 190 wantErr: "invalid GOPATH '.'\n", 191 }, 192 {dir: "fixtures", 193 cmdArgs: []string{os.Args[0], "-covermode=count"}, 194 gopath: ".", 195 wantExitCode: 1, 196 wantOut: "", 197 wantErr: "invalid GOPATH '.'\n", 198 }, 199 } 200 wd, err := os.Getwd() 201 if err != nil { 202 t.Fatal(err) 203 } 204 defer os.Chdir(wd) 205 for _, c := range cases { 206 var gotOut bytes.Buffer 207 var gotErr bytes.Buffer 208 initProgram(c.cmdArgs, &gotOut, &gotErr, c.gopath) 209 if err := os.Chdir(wd); err != nil { 210 t.Fatalf("ChDir(%s) err: %s", c.dir, err) 211 } 212 if err := os.Chdir(c.dir); err != nil { 213 t.Fatalf("ChDir(%s) err: %s", c.dir, err) 214 } 215 exitCode := program.Run() 216 if exitCode != c.wantExitCode { 217 t.Errorf("Run: incorrect exit code, got: %d, want: %d", 218 exitCode, c.wantExitCode) 219 } 220 221 if gotErr.String() != c.wantErr { 222 t.Errorf("Run: gotErr: %s, wantErr: %s", gotErr.String(), c.wantErr) 223 } 224 225 if gotOut.String() != c.wantOut { 226 t.Errorf("Run: gotOut: %s, wantOut: %s", gotOut.String(), c.wantOut) 227 } 228 } 229 } 230 231 func TestProcessDir_errors(t *testing.T) { 232 wd, err := os.Getwd() 233 if err != nil { 234 t.Fatal(err) 235 } 236 defer os.Chdir(wd) 237 cases := []struct { 238 cover string 239 path string 240 wantErr error 241 }{ 242 {cover: "count", 243 path: ".", 244 wantErr: errors.New("can't create relative path"), 245 }, 246 {cover: "count", 247 path: filepath.Join("fixtures", "nonexistant"), 248 wantErr: &os.PathError{ 249 Op: "chdir", 250 Path: filepath.Join("fixtures", "nonexistant"), 251 Err: syscall.ENOENT, 252 }, 253 }, 254 {cover: "bob", 255 path: wd, 256 wantErr: goTestError{ 257 stderr: "invalid flag argument for -covermode: \"bob\"", 258 stdout: "", 259 }, 260 }, 261 } 262 for i, c := range cases { 263 var gotOut bytes.Buffer 264 var pOut bytes.Buffer 265 program := &Program{cover: c.cover, out: &pOut, verbose: true} 266 err := program.processDir(wd, c.path, &gotOut) 267 checkErrorMatch(t, fmt.Sprintf("(%d) processDir: ", i), err, c.wantErr) 268 } 269 } 270 271 func TestUsage(t *testing.T) { 272 var gotErr bytes.Buffer 273 initProgram(os.Args, os.Stdout, &gotErr, os.Getenv("GOPATH")) 274 Usage() 275 want := usageMsg() 276 if gotErr.String() != want { 277 t.Errorf("Usage: got: %s, want: %s", gotErr.String(), want) 278 } 279 } 280 281 func TestGoTestErrorError(t *testing.T) { 282 err := goTestError{ 283 stderr: "this is an error", 284 stdout: "baby did a bad bad thing", 285 } 286 want := "error from go test: this is an error\noutput: baby did a bad bad thing" 287 got := err.Error() 288 if got != want { 289 t.Errorf("Error() got: %s, want: %s", got, want) 290 } 291 } 292 293 func TestWalkingErrorError(t *testing.T) { 294 err := walkingError{ 295 err: errors.New("this is an error"), 296 dir: "/tmp/someplace", 297 } 298 want := "could not walk working directory '/tmp/someplace': this is an error" 299 got := err.Error() 300 if got != want { 301 t.Errorf("Error() got: %s, want: %s", got, want) 302 } 303 } 304 305 /**************************** 306 * Helper functions 307 ****************************/ 308 309 var fileTestedRegexp = regexp.MustCompile("^(.*?)(:\\d.*) (\\d+)$") 310 311 func makeUsageMsgRegexps() []string { 312 lines := strings.Split(usageMsg(), "\n")[:15] 313 r := make([]string, len(lines)) 314 for i, l := range lines { 315 r[i] = regexp.QuoteMeta(l) 316 } 317 return r 318 } 319 320 func checkOutput(wantRegexp []string, gotOut string) error { 321 gotOutStrs := strings.Split(gotOut, "\n") 322 gotOutStrs = gotOutStrs[:len(gotOutStrs)-1] 323 if len(wantRegexp) != len(gotOutStrs) { 324 return fmt.Errorf("wantRegexp has %d lines, gotOut has %d lines", 325 len(wantRegexp), len(gotOutStrs)) 326 } 327 i := 0 328 for _, r := range wantRegexp { 329 compiledRegexp, err := regexp.Compile(r) 330 if err != nil { 331 return err 332 } 333 if !compiledRegexp.MatchString(gotOutStrs[i]) { 334 return fmt.Errorf("line doesn't match got: %s, want: %s", 335 gotOutStrs[i], r) 336 } 337 i++ 338 } 339 return nil 340 } 341 342 func filesTested(wd string, filename string) (map[string]bool, error) { 343 files := map[string]bool{} 344 gopath := filepath.Clean(os.Getenv("GOPATH")) 345 if len(gopath) == 0 { 346 return files, fmt.Errorf("invalid GOPATH '%s'", gopath) 347 } 348 srcpath := filepath.Join(gopath, "src") 349 project, err := filepath.Rel(srcpath, wd) 350 if err != nil { 351 return files, err 352 } 353 project = filepath.Clean(project) 354 f, err := os.Open(filename) 355 if err != nil { 356 return files, err 357 } 358 defer f.Close() 359 scanner := bufio.NewScanner(f) 360 for scanner.Scan() { 361 line := scanner.Text() 362 if fileTestedRegexp.MatchString(line) { 363 file := fileTestedRegexp.ReplaceAllString(line, "$1") 364 file, err = filepath.Rel(project, file) 365 if err != nil { 366 return files, err 367 } 368 count := fileTestedRegexp.ReplaceAllString(line, "$3") 369 if count != "0" { 370 files[file] = true 371 } 372 } 373 } 374 return files, scanner.Err() 375 } 376 377 func checkErrorMatch(t *testing.T, context string, got, want error) { 378 if got == nil && want == nil { 379 return 380 } 381 if got == nil || want == nil { 382 t.Errorf("%s got err: %s, want : %s", context, got, want) 383 return 384 } 385 switch x := want.(type) { 386 case *os.PathError: 387 if err := checkPathErrorMatch(got, x); err != nil { 388 t.Errorf("%s %s", context, err) 389 } 390 return 391 case goTestError: 392 if err := checkGoTestErrorMatch(got, x); err != nil { 393 t.Errorf("%s %s", context, err) 394 } 395 return 396 } 397 if got.Error() != want.Error() { 398 t.Errorf("%s got err: %s, want : %s", context, got, want) 399 } 400 } 401 402 func checkPathErrorMatch(checkErr error, wantErr *os.PathError) error { 403 perr, ok := checkErr.(*os.PathError) 404 if !ok { 405 return fmt.Errorf("got err type: %T, want error type: os.PathError", 406 checkErr) 407 } 408 if perr.Op != wantErr.Op { 409 return fmt.Errorf("got perr.Op: %s, want: %s", perr.Op, wantErr.Op) 410 } 411 if filepath.Clean(perr.Path) != filepath.Clean(wantErr.Path) { 412 return fmt.Errorf("got perr.Path: %s, want: %s", perr.Path, wantErr.Path) 413 } 414 if perr.Err != wantErr.Err { 415 return fmt.Errorf("got perr.Err: %s, want: %s", perr.Err, wantErr.Err) 416 } 417 return nil 418 } 419 420 func checkGoTestErrorMatch(checkErr error, wantErr goTestError) error { 421 gerr, ok := checkErr.(goTestError) 422 if !ok { 423 return fmt.Errorf("got err type: %T, want error type: goTestError", 424 checkErr) 425 } 426 if strings.Trim(gerr.stderr, " \n") != strings.Trim(wantErr.stderr, " \n") { 427 return fmt.Errorf("got gerr.stderr: %s, want: %s", 428 gerr.stderr, wantErr.stderr) 429 } 430 if gerr.stdout != wantErr.stdout { 431 return fmt.Errorf("got gerr.stdout: %s, want: %s", 432 gerr.stdout, wantErr.stdout) 433 } 434 return nil 435 }