github.com/nakabonne/golangci-lint@v1.26.1/test/linters_test.go (about) 1 package test 2 3 import ( 4 "bufio" 5 "io/ioutil" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "strings" 10 "testing" 11 12 assert "github.com/stretchr/testify/require" 13 yaml "gopkg.in/yaml.v2" 14 15 "github.com/golangci/golangci-lint/test/testshared" 16 ) 17 18 func runGoErrchk(c *exec.Cmd, files []string, t *testing.T) { 19 output, err := c.CombinedOutput() 20 assert.Error(t, err) 21 _, ok := err.(*exec.ExitError) 22 assert.True(t, ok) 23 24 // TODO: uncomment after deprecating go1.11 25 // assert.Equal(t, exitcodes.IssuesFound, exitErr.ExitCode()) 26 27 fullshort := make([]string, 0, len(files)*2) 28 for _, f := range files { 29 fullshort = append(fullshort, f, filepath.Base(f)) 30 } 31 32 err = errorCheck(string(output), false, fullshort...) 33 assert.NoError(t, err) 34 } 35 36 func testSourcesFromDir(t *testing.T, dir string) { 37 t.Log(filepath.Join(dir, "*.go")) 38 39 findSources := func(pathPatterns ...string) []string { 40 sources, err := filepath.Glob(filepath.Join(pathPatterns...)) 41 assert.NoError(t, err) 42 assert.NotEmpty(t, sources) 43 return sources 44 } 45 sources := findSources(dir, "*.go") 46 47 testshared.NewLintRunner(t).Install() 48 49 for _, s := range sources { 50 s := s 51 t.Run(filepath.Base(s), func(t *testing.T) { 52 t.Parallel() 53 testOneSource(t, s) 54 }) 55 } 56 } 57 58 func TestSourcesFromTestdataWithIssuesDir(t *testing.T) { 59 testSourcesFromDir(t, testdataDir) 60 } 61 62 func TestTypecheck(t *testing.T) { 63 testSourcesFromDir(t, filepath.Join(testdataDir, "notcompiles")) 64 } 65 66 func TestGoimportsLocal(t *testing.T) { 67 sourcePath := filepath.Join(testdataDir, "goimports", "goimports.go") 68 args := []string{ 69 "--disable-all", "--print-issued-lines=false", "--print-linter-name=false", "--out-format=line-number", 70 sourcePath, 71 } 72 rc := extractRunContextFromComments(t, sourcePath) 73 args = append(args, rc.args...) 74 75 cfg, err := yaml.Marshal(rc.config) 76 assert.NoError(t, err) 77 78 testshared.NewLintRunner(t).RunWithYamlConfig(string(cfg), args...). 79 ExpectHasIssue("testdata/goimports/goimports.go:8: File is not `goimports`-ed") 80 } 81 82 func saveConfig(t *testing.T, cfg map[string]interface{}) (cfgPath string, finishFunc func()) { 83 f, err := ioutil.TempFile("", "golangci_lint_test") 84 assert.NoError(t, err) 85 86 cfgPath = f.Name() + ".yml" 87 err = os.Rename(f.Name(), cfgPath) 88 assert.NoError(t, err) 89 90 err = yaml.NewEncoder(f).Encode(cfg) 91 assert.NoError(t, err) 92 93 return cfgPath, func() { 94 assert.NoError(t, f.Close()) 95 if os.Getenv("GL_KEEP_TEMP_FILES") != "1" { 96 assert.NoError(t, os.Remove(cfgPath)) 97 } 98 } 99 } 100 101 func testOneSource(t *testing.T, sourcePath string) { 102 args := []string{ 103 "run", 104 "--disable-all", 105 "--print-issued-lines=false", 106 "--print-linter-name=false", 107 "--out-format=line-number", 108 "--max-same-issues=10", 109 } 110 111 rc := extractRunContextFromComments(t, sourcePath) 112 var cfgPath string 113 114 if rc.config != nil { 115 p, finish := saveConfig(t, rc.config) 116 defer finish() 117 cfgPath = p 118 } else if rc.configPath != "" { 119 cfgPath = rc.configPath 120 } 121 122 for _, addArg := range []string{"", "-Etypecheck"} { 123 caseArgs := append([]string{}, args...) 124 caseArgs = append(caseArgs, rc.args...) 125 if addArg != "" { 126 caseArgs = append(caseArgs, addArg) 127 } 128 if cfgPath == "" { 129 caseArgs = append(caseArgs, "--no-config") 130 } else { 131 caseArgs = append(caseArgs, "-c", cfgPath) 132 } 133 134 caseArgs = append(caseArgs, sourcePath) 135 136 cmd := exec.Command(binName, caseArgs...) 137 t.Log(caseArgs) 138 runGoErrchk(cmd, []string{sourcePath}, t) 139 } 140 } 141 142 type runContext struct { 143 args []string 144 config map[string]interface{} 145 configPath string 146 } 147 148 func buildConfigFromShortRepr(t *testing.T, repr string, config map[string]interface{}) { 149 kv := strings.Split(repr, "=") 150 assert.Len(t, kv, 2) 151 152 keyParts := strings.Split(kv[0], ".") 153 assert.True(t, len(keyParts) >= 2, len(keyParts)) 154 155 lastObj := config 156 for _, k := range keyParts[:len(keyParts)-1] { 157 var v map[string]interface{} 158 if lastObj[k] == nil { 159 v = map[string]interface{}{} 160 } else { 161 v = lastObj[k].(map[string]interface{}) 162 } 163 164 lastObj[k] = v 165 lastObj = v 166 } 167 168 lastObj[keyParts[len(keyParts)-1]] = kv[1] 169 } 170 171 func extractRunContextFromComments(t *testing.T, sourcePath string) *runContext { 172 f, err := os.Open(sourcePath) 173 assert.NoError(t, err) 174 defer f.Close() 175 176 rc := &runContext{} 177 178 scanner := bufio.NewScanner(f) 179 for scanner.Scan() { 180 line := scanner.Text() 181 if !strings.HasPrefix(line, "//") { 182 return rc 183 } 184 185 line = strings.TrimPrefix(line, "//") 186 if strings.HasPrefix(line, "args: ") { 187 assert.Nil(t, rc.args) 188 args := strings.TrimPrefix(line, "args: ") 189 assert.NotEmpty(t, args) 190 rc.args = strings.Split(args, " ") 191 continue 192 } 193 194 if strings.HasPrefix(line, "config: ") { 195 repr := strings.TrimPrefix(line, "config: ") 196 assert.NotEmpty(t, repr) 197 if rc.config == nil { 198 rc.config = map[string]interface{}{} 199 } 200 buildConfigFromShortRepr(t, repr, rc.config) 201 continue 202 } 203 204 if strings.HasPrefix(line, "config_path: ") { 205 configPath := strings.TrimPrefix(line, "config_path: ") 206 assert.NotEmpty(t, configPath) 207 rc.configPath = configPath 208 continue 209 } 210 211 assert.Fail(t, "invalid prefix of comment line %s", line) 212 } 213 214 return rc 215 } 216 217 func TestExtractRunContextFromComments(t *testing.T) { 218 rc := extractRunContextFromComments(t, filepath.Join(testdataDir, "goimports", "goimports.go")) 219 assert.Equal(t, []string{"-Egoimports"}, rc.args) 220 } 221 222 func TestGolintConsumesXTestFiles(t *testing.T) { 223 dir := getTestDataDir("withxtest") 224 const expIssue = "`if` block ends with a `return` statement, so drop this `else` and outdent its block" 225 226 r := testshared.NewLintRunner(t) 227 r.Run("--no-config", "--disable-all", "-Egolint", dir).ExpectHasIssue(expIssue) 228 r.Run("--no-config", "--disable-all", "-Egolint", filepath.Join(dir, "p_test.go")).ExpectHasIssue(expIssue) 229 }