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  }