1package testshared 2 3import ( 4 "io/ioutil" 5 "os" 6 "os/exec" 7 "strings" 8 "sync" 9 "syscall" 10 "time" 11 12 "github.com/stretchr/testify/assert" 13 14 "github.com/golangci/golangci-lint/pkg/exitcodes" 15 "github.com/golangci/golangci-lint/pkg/logutils" 16) 17 18type LintRunner struct { 19 t assert.TestingT 20 log logutils.Log 21 env []string 22 installOnce sync.Once 23} 24 25func NewLintRunner(t assert.TestingT, environ ...string) *LintRunner { 26 log := logutils.NewStderrLog("test") 27 log.SetLevel(logutils.LogLevelInfo) 28 return &LintRunner{ 29 t: t, 30 log: log, 31 env: environ, 32 } 33} 34 35func (r *LintRunner) Install() { 36 r.installOnce.Do(func() { 37 if os.Getenv("GOLANGCI_LINT_INSTALLED") == "true" { 38 return 39 } 40 41 cmd := exec.Command("make", "-C", "..", "build") 42 assert.NoError(r.t, cmd.Run(), "Can't go install golangci-lint") 43 }) 44} 45 46type RunResult struct { 47 t assert.TestingT 48 49 output string 50 exitCode int 51} 52 53func (r *RunResult) ExpectNoIssues() { 54 assert.Equal(r.t, "", r.output, "exit code is %d", r.exitCode) 55 assert.Equal(r.t, exitcodes.Success, r.exitCode, "output is %s", r.output) 56} 57 58func (r *RunResult) ExpectExitCode(possibleCodes ...int) *RunResult { 59 for _, pc := range possibleCodes { 60 if pc == r.exitCode { 61 return r 62 } 63 } 64 65 assert.Fail(r.t, "invalid exit code", "exit code (%d) must be one of %v: %s", r.exitCode, possibleCodes, r.output) 66 return r 67} 68 69// ExpectOutputRegexp can be called with either a string or compiled regexp 70func (r *RunResult) ExpectOutputRegexp(s interface{}) *RunResult { 71 assert.Regexp(r.t, s, r.output, "exit code is %d", r.exitCode) 72 return r 73} 74 75func (r *RunResult) ExpectOutputContains(s string) *RunResult { 76 assert.Contains(r.t, r.output, s, "exit code is %d", r.exitCode) 77 return r 78} 79 80func (r *RunResult) ExpectOutputEq(s string) *RunResult { 81 assert.Equal(r.t, s, r.output, "exit code is %d", r.exitCode) 82 return r 83} 84 85func (r *RunResult) ExpectHasIssue(issueText string) *RunResult { 86 return r.ExpectExitCode(exitcodes.IssuesFound).ExpectOutputContains(issueText) 87} 88 89func (r *LintRunner) Run(args ...string) *RunResult { 90 newArgs := append([]string{"--allow-parallel-runners"}, args...) 91 return r.RunCommand("run", newArgs...) 92} 93 94func (r *LintRunner) RunCommand(command string, args ...string) *RunResult { 95 r.Install() 96 97 runArgs := append([]string{command}, args...) 98 defer func(startedAt time.Time) { 99 r.log.Infof("ran [../golangci-lint %s] in %s", strings.Join(runArgs, " "), time.Since(startedAt)) 100 }(time.Now()) 101 102 cmd := exec.Command("../golangci-lint", runArgs...) 103 cmd.Env = append(os.Environ(), r.env...) 104 out, err := cmd.CombinedOutput() 105 if err != nil { 106 if exitError, ok := err.(*exec.ExitError); ok { 107 r.log.Infof("stderr: %s", exitError.Stderr) 108 ws := exitError.Sys().(syscall.WaitStatus) 109 return &RunResult{ 110 t: r.t, 111 output: string(out), 112 exitCode: ws.ExitStatus(), 113 } 114 } 115 116 r.t.Errorf("can't get error code from %s", err) 117 return nil 118 } 119 120 // success, exitCode should be 0 if go is ok 121 ws := cmd.ProcessState.Sys().(syscall.WaitStatus) 122 return &RunResult{ 123 t: r.t, 124 output: string(out), 125 exitCode: ws.ExitStatus(), 126 } 127} 128 129func (r *LintRunner) RunWithYamlConfig(cfg string, args ...string) *RunResult { 130 newArgs := append([]string{"--allow-parallel-runners"}, args...) 131 return r.RunCommandWithYamlConfig(cfg, "run", newArgs...) 132} 133 134func (r *LintRunner) RunCommandWithYamlConfig(cfg, command string, args ...string) *RunResult { 135 f, err := ioutil.TempFile("", "golangci_lint_test") 136 assert.NoError(r.t, err) 137 f.Close() 138 139 cfgPath := f.Name() + ".yml" 140 err = os.Rename(f.Name(), cfgPath) 141 assert.NoError(r.t, err) 142 143 if os.Getenv("GL_KEEP_TEMP_FILES") != "1" { 144 defer os.Remove(cfgPath) 145 } 146 147 cfg = strings.TrimSpace(cfg) 148 cfg = strings.Replace(cfg, "\t", " ", -1) 149 150 err = ioutil.WriteFile(cfgPath, []byte(cfg), os.ModePerm) 151 assert.NoError(r.t, err) 152 153 pargs := append([]string{"-c", cfgPath}, args...) 154 return r.RunCommand(command, pargs...) 155} 156