golangci-lint/test/linters_test.go
Denis Isaev a4a7100011 Fix #263: update goimports
The new version of goimports works 100x faster
with go modules. Also it has some new features:

$ git cherry -v 6c7e314b6563 92cdcd90bf52 | fgrep imports
+ 5bbcdc15656ef390fab5dd6e8daf95354f7171e3 imports: redesign fixImports
+ ee45598d2ff288037f53f9e13ae0b1a6e2165ad5 imports: create named imports for name/path mismatches (again)
+ 4c53570e0460bc32468f75bf9dd71c018d03bfa9 imports: ignore globals in different packages
+ 1d424dbce8dd500e9e449fd3ff9d0668c09e2ae1 imports: clean up customization seam
+ 6a3e9aa2ab7749d72d1006ee484271b4a11f96c2 imports: fix renamed sibling imports
+ 5f4a60f04f23ac48e0665f257413ae3eacf339be imports: fix renamed sibling imports more
+ bbccd8cae4a9a47e0f978e03ff4b5df88a9fde1e imports: use go/packages, support modules
+ d4971274fe382404aee0e8c163af974f2bf738e6 imports: don't remove imports that conflict with globals
2018-12-22 15:50:16 +03:00

209 lines
5.0 KiB
Go

package test
import (
"bufio"
"bytes"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
"github.com/golangci/golangci-lint/test/testshared"
assert "github.com/stretchr/testify/require"
yaml "gopkg.in/yaml.v2"
)
func runGoErrchk(c *exec.Cmd, t *testing.T) {
output, err := c.CombinedOutput()
assert.NoError(t, err, "Output:\n%s", output)
// Can't check exit code: tool only prints to output
assert.False(t, bytes.Contains(output, []byte("BUG")), "Output:\n%s", output)
}
func testSourcesFromDir(t *testing.T, dir string) {
t.Log(filepath.Join(dir, "*.go"))
findSources := func(pathPatterns ...string) []string {
sources, err := filepath.Glob(filepath.Join(pathPatterns...))
assert.NoError(t, err)
assert.NotEmpty(t, sources)
return sources
}
sources := findSources(dir, "*.go")
testshared.NewLintRunner(t).Install()
for _, s := range sources {
s := s
t.Run(filepath.Base(s), func(t *testing.T) {
t.Parallel()
testOneSource(t, s)
})
}
}
func TestSourcesFromTestdataWithIssuesDir(t *testing.T) {
testSourcesFromDir(t, testdataDir)
}
func TestTypecheck(t *testing.T) {
testSourcesFromDir(t, filepath.Join(testdataDir, "notcompiles"))
}
func TestGoimportsLocal(t *testing.T) {
sourcePath := filepath.Join(testdataDir, "goimports", "goimports.go")
args := []string{
"--disable-all", "--print-issued-lines=false", "--print-linter-name=false", "--out-format=line-number",
sourcePath,
}
rc := extractRunContextFromComments(t, sourcePath)
args = append(args, rc.args...)
cfg, err := yaml.Marshal(rc.config)
assert.NoError(t, err)
testshared.NewLintRunner(t).RunWithYamlConfig(string(cfg), args...).
ExpectHasIssue("testdata/goimports/goimports.go:8: File is not `goimports`-ed")
}
func saveConfig(t *testing.T, cfg map[string]interface{}) (cfgPath string, finishFunc func()) {
f, err := ioutil.TempFile("", "golangci_lint_test")
assert.NoError(t, err)
cfgPath = f.Name() + ".yml"
err = os.Rename(f.Name(), cfgPath)
assert.NoError(t, err)
err = yaml.NewEncoder(f).Encode(cfg)
assert.NoError(t, err)
return cfgPath, func() {
assert.NoError(t, f.Close())
assert.NoError(t, os.Remove(cfgPath))
}
}
func testOneSource(t *testing.T, sourcePath string) {
goErrchkBin := filepath.Join(runtime.GOROOT(), "test", "errchk")
args := []string{
binName, "run",
"--disable-all",
"--print-issued-lines=false",
"--print-linter-name=false",
"--out-format=line-number",
}
rc := extractRunContextFromComments(t, sourcePath)
var cfgPath string
if rc.config != nil {
p, finish := saveConfig(t, rc.config)
defer finish()
cfgPath = p
}
for _, addArg := range []string{"", "-Etypecheck"} {
caseArgs := append([]string{}, args...)
caseArgs = append(caseArgs, rc.args...)
if addArg != "" {
caseArgs = append(caseArgs, addArg)
}
if cfgPath == "" {
caseArgs = append(caseArgs, "--no-config")
} else {
caseArgs = append(caseArgs, "-c", cfgPath)
}
caseArgs = append(caseArgs, sourcePath)
cmd := exec.Command(goErrchkBin, caseArgs...)
t.Log(caseArgs)
runGoErrchk(cmd, t)
}
}
type runContext struct {
args []string
config map[string]interface{}
}
func buildConfigFromShortRepr(t *testing.T, repr string, config map[string]interface{}) {
kv := strings.Split(repr, "=")
assert.Len(t, kv, 2)
keyParts := strings.Split(kv[0], ".")
assert.True(t, len(keyParts) >= 2, len(keyParts))
lastObj := config
for _, k := range keyParts[:len(keyParts)-1] {
var v map[string]interface{}
if lastObj[k] == nil {
v = map[string]interface{}{}
} else {
v = lastObj[k].(map[string]interface{})
}
lastObj[k] = v
lastObj = v
}
lastObj[keyParts[len(keyParts)-1]] = kv[1]
}
func extractRunContextFromComments(t *testing.T, sourcePath string) *runContext {
f, err := os.Open(sourcePath)
assert.NoError(t, err)
defer f.Close()
rc := &runContext{}
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
if !strings.HasPrefix(line, "//") {
return rc
}
line = strings.TrimPrefix(line, "//")
if strings.HasPrefix(line, "args: ") {
assert.Nil(t, rc.args)
args := strings.TrimPrefix(line, "args: ")
assert.NotEmpty(t, args)
rc.args = strings.Split(args, " ")
continue
}
if strings.HasPrefix(line, "config: ") {
repr := strings.TrimPrefix(line, "config: ")
assert.NotEmpty(t, repr)
if rc.config == nil {
rc.config = map[string]interface{}{}
}
buildConfigFromShortRepr(t, repr, rc.config)
continue
}
}
return rc
}
func TestExtractRunContextFromComments(t *testing.T) {
rc := extractRunContextFromComments(t, filepath.Join(testdataDir, "goimports", "goimports.go"))
assert.Equal(t, []string{"-Egoimports"}, rc.args)
}
func TestGolintConsumesXTestFiles(t *testing.T) {
dir := getTestDataDir("withxtest")
const expIssue = "if block ends with a return statement, so drop this else and outdent its block"
r := testshared.NewLintRunner(t)
r.Run("--no-config", "--disable-all", "-Egolint", dir).ExpectHasIssue(expIssue)
r.Run("--no-config", "--disable-all", "-Egolint", filepath.Join(dir, "p_test.go")).ExpectHasIssue(expIssue)
}