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  }