github.com/elek/golangci-lint@v1.42.2-0.20211208090441-c05b7fcb3a9a/test/testshared/testshared.go (about) 1 package testshared 2 3 import ( 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 18 type LintRunner struct { 19 t assert.TestingT 20 log logutils.Log 21 env []string 22 installOnce sync.Once 23 } 24 25 func 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 35 func (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 46 type RunResult struct { 47 t assert.TestingT 48 49 output string 50 exitCode int 51 } 52 53 func (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 58 func (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 70 func (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 75 func (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 80 func (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 85 func (r *RunResult) ExpectHasIssue(issueText string) *RunResult { 86 return r.ExpectExitCode(exitcodes.IssuesFound).ExpectOutputContains(issueText) 87 } 88 89 func (r *LintRunner) Run(args ...string) *RunResult { 90 newArgs := append([]string{"--allow-parallel-runners"}, args...) 91 return r.RunCommand("run", newArgs...) 92 } 93 94 func (r *LintRunner) RunCommand(command string, args ...string) *RunResult { 95 r.Install() 96 97 runArgs := append([]string{command}, "--internal-cmd-test") 98 runArgs = append(runArgs, args...) 99 100 defer func(startedAt time.Time) { 101 r.log.Infof("ran [../golangci-lint %s] in %s", strings.Join(runArgs, " "), time.Since(startedAt)) 102 }(time.Now()) 103 104 cmd := exec.Command("../golangci-lint", runArgs...) 105 cmd.Env = append(os.Environ(), r.env...) 106 out, err := cmd.CombinedOutput() 107 if err != nil { 108 if exitError, ok := err.(*exec.ExitError); ok { 109 r.log.Infof("stderr: %s", exitError.Stderr) 110 ws := exitError.Sys().(syscall.WaitStatus) 111 return &RunResult{ 112 t: r.t, 113 output: string(out), 114 exitCode: ws.ExitStatus(), 115 } 116 } 117 118 r.t.Errorf("can't get error code from %s", err) 119 return nil 120 } 121 122 // success, exitCode should be 0 if go is ok 123 ws := cmd.ProcessState.Sys().(syscall.WaitStatus) 124 return &RunResult{ 125 t: r.t, 126 output: string(out), 127 exitCode: ws.ExitStatus(), 128 } 129 } 130 131 func (r *LintRunner) RunWithYamlConfig(cfg string, args ...string) *RunResult { 132 newArgs := append([]string{"--allow-parallel-runners"}, args...) 133 return r.RunCommandWithYamlConfig(cfg, "run", newArgs...) 134 } 135 136 func (r *LintRunner) RunCommandWithYamlConfig(cfg, command string, args ...string) *RunResult { 137 f, err := ioutil.TempFile("", "golangci_lint_test") 138 assert.NoError(r.t, err) 139 f.Close() 140 141 cfgPath := f.Name() + ".yml" 142 err = os.Rename(f.Name(), cfgPath) 143 assert.NoError(r.t, err) 144 145 if os.Getenv("GL_KEEP_TEMP_FILES") != "1" { 146 defer os.Remove(cfgPath) 147 } 148 149 cfg = strings.TrimSpace(cfg) 150 cfg = strings.Replace(cfg, "\t", " ", -1) 151 152 err = ioutil.WriteFile(cfgPath, []byte(cfg), os.ModePerm) 153 assert.NoError(r.t, err) 154 155 pargs := append([]string{"-c", cfgPath}, args...) 156 return r.RunCommand(command, pargs...) 157 }