github.com/alecthomas/golangci-lint@v1.4.2-0.20180609094924-581a3564ff68/test/run_test.go (about) 1 package test 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "log" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "sort" 11 "strings" 12 "sync" 13 "syscall" 14 "testing" 15 16 "github.com/golangci/golangci-lint/pkg/lint/lintersdb" 17 18 "github.com/stretchr/testify/assert" 19 ) 20 21 var installOnce sync.Once 22 23 func installBinary(t assert.TestingT) { 24 installOnce.Do(func() { 25 cmd := exec.Command("go", "install", filepath.Join("..", "cmd", binName)) 26 assert.NoError(t, cmd.Run(), "Can't go install %s", binName) 27 }) 28 } 29 30 func checkNoIssuesRun(t *testing.T, out string, exitCode int) { 31 assert.Equal(t, 0, exitCode) 32 assert.Equal(t, "Congrats! No issues were found.\n", out) 33 } 34 35 func TestCongratsMessageIfNoIssues(t *testing.T) { 36 out, exitCode := runGolangciLint(t, "../...") 37 checkNoIssuesRun(t, out, exitCode) 38 } 39 40 func TestDeadline(t *testing.T) { 41 out, exitCode := runGolangciLint(t, "--deadline=1ms", "../...") 42 assert.Equal(t, 4, exitCode) 43 assert.Contains(t, out, "deadline exceeded: try increase it by passing --deadline option") 44 assert.NotContains(t, out, "Congrats! No issues were found.") 45 } 46 47 func runGolangciLint(t *testing.T, args ...string) (string, int) { 48 installBinary(t) 49 50 runArgs := append([]string{"run"}, args...) 51 log.Printf("golangci-lint %s", strings.Join(runArgs, " ")) 52 cmd := exec.Command("golangci-lint", runArgs...) 53 out, err := cmd.CombinedOutput() 54 if err != nil { 55 if exitError, ok := err.(*exec.ExitError); ok { 56 t.Logf("stderr: %s", exitError.Stderr) 57 ws := exitError.Sys().(syscall.WaitStatus) 58 return string(out), ws.ExitStatus() 59 } 60 61 t.Fatalf("can't get error code from %s", err) 62 return "", -1 63 } 64 65 // success, exitCode should be 0 if go is ok 66 ws := cmd.ProcessState.Sys().(syscall.WaitStatus) 67 return string(out), ws.ExitStatus() 68 } 69 70 func runGolangciLintWithYamlConfig(t *testing.T, cfg string, args ...string) string { 71 out, ec := runGolangciLintWithYamlConfigWithCode(t, cfg, args...) 72 assert.Equal(t, 0, ec) 73 74 return out 75 } 76 77 func runGolangciLintWithYamlConfigWithCode(t *testing.T, cfg string, args ...string) (string, int) { 78 f, err := ioutil.TempFile("", "golangci_lint_test") 79 assert.NoError(t, err) 80 f.Close() 81 82 cfgPath := f.Name() + ".yml" 83 err = os.Rename(f.Name(), cfgPath) 84 assert.NoError(t, err) 85 86 defer os.Remove(cfgPath) 87 88 cfg = strings.TrimSpace(cfg) 89 cfg = strings.Replace(cfg, "\t", " ", -1) 90 91 err = ioutil.WriteFile(cfgPath, []byte(cfg), os.ModePerm) 92 assert.NoError(t, err) 93 94 pargs := append([]string{"-c", cfgPath}, args...) 95 return runGolangciLint(t, pargs...) 96 } 97 98 func TestTestsAreLintedByDefault(t *testing.T) { 99 out, exitCode := runGolangciLint(t, "./testdata/withtests") 100 assert.Equal(t, 0, exitCode, out) 101 } 102 103 func TestConfigFileIsDetected(t *testing.T) { 104 checkGotConfig := func(out string, exitCode int) { 105 assert.Equal(t, 0, exitCode, out) 106 assert.Equal(t, "test\n", out) // test config contains InternalTest: true, it triggers such output 107 } 108 109 checkGotConfig(runGolangciLint(t, "testdata/withconfig/pkg")) 110 checkGotConfig(runGolangciLint(t, "testdata/withconfig/...")) 111 112 out, exitCode := runGolangciLint(t) // doesn't detect when no args 113 checkNoIssuesRun(t, out, exitCode) 114 } 115 116 func inSlice(s []string, v string) bool { 117 for _, sv := range s { 118 if sv == v { 119 return true 120 } 121 } 122 123 return false 124 } 125 126 func getEnabledByDefaultFastLintersExcept(except ...string) []string { 127 ebdl := lintersdb.GetAllEnabledByDefaultLinters() 128 ret := []string{} 129 for _, linter := range ebdl { 130 if linter.DoesFullImport { 131 continue 132 } 133 134 if !inSlice(except, linter.Linter.Name()) { 135 ret = append(ret, linter.Linter.Name()) 136 } 137 } 138 139 return ret 140 } 141 142 func getAllFastLintersWith(with ...string) []string { 143 linters := lintersdb.GetAllSupportedLinterConfigs() 144 ret := append([]string{}, with...) 145 for _, linter := range linters { 146 if linter.DoesFullImport { 147 continue 148 } 149 ret = append(ret, linter.Linter.Name()) 150 } 151 152 return ret 153 } 154 155 func getEnabledByDefaultLinters() []string { 156 ebdl := lintersdb.GetAllEnabledByDefaultLinters() 157 ret := []string{} 158 for _, linter := range ebdl { 159 ret = append(ret, linter.Linter.Name()) 160 } 161 162 return ret 163 } 164 165 func getEnabledByDefaultFastLintersWith(with ...string) []string { 166 ebdl := lintersdb.GetAllEnabledByDefaultLinters() 167 ret := append([]string{}, with...) 168 for _, linter := range ebdl { 169 if linter.DoesFullImport { 170 continue 171 } 172 173 ret = append(ret, linter.Linter.Name()) 174 } 175 176 return ret 177 } 178 179 func mergeMegacheck(linters []string) []string { 180 if inSlice(linters, "staticcheck") && 181 inSlice(linters, "gosimple") && 182 inSlice(linters, "unused") { 183 ret := []string{"megacheck"} 184 for _, linter := range linters { 185 if !inSlice([]string{"staticcheck", "gosimple", "unused"}, linter) { 186 ret = append(ret, linter) 187 } 188 } 189 190 return ret 191 } 192 193 return linters 194 } 195 196 func TestEnableAllFastAndEnableCanCoexist(t *testing.T) { 197 out, exitCode := runGolangciLint(t, "--fast", "--enable-all", "--enable=typecheck") 198 checkNoIssuesRun(t, out, exitCode) 199 200 _, exitCode = runGolangciLint(t, "--enable-all", "--enable=typecheck") 201 assert.Equal(t, 3, exitCode) 202 203 } 204 205 func TestEnabledLinters(t *testing.T) { 206 type tc struct { 207 name string 208 cfg string 209 el []string 210 args string 211 noImplicitFast bool 212 } 213 214 cases := []tc{ 215 { 216 name: "disable govet in config", 217 cfg: ` 218 linters: 219 disable: 220 - govet 221 `, 222 el: getEnabledByDefaultFastLintersExcept("govet"), 223 }, 224 { 225 name: "enable golint in config", 226 cfg: ` 227 linters: 228 enable: 229 - golint 230 `, 231 el: getEnabledByDefaultFastLintersWith("golint"), 232 }, 233 { 234 name: "disable govet in cmd", 235 args: "-Dgovet", 236 el: getEnabledByDefaultFastLintersExcept("govet"), 237 }, 238 { 239 name: "enable gofmt in cmd and enable golint in config", 240 args: "-Egofmt", 241 cfg: ` 242 linters: 243 enable: 244 - golint 245 `, 246 el: getEnabledByDefaultFastLintersWith("golint", "gofmt"), 247 }, 248 { 249 name: "fast option in config", 250 cfg: ` 251 linters: 252 fast: true 253 `, 254 el: getEnabledByDefaultFastLintersWith(), 255 noImplicitFast: true, 256 }, 257 { 258 name: "explicitly unset fast option in config", 259 cfg: ` 260 linters: 261 fast: false 262 `, 263 el: getEnabledByDefaultLinters(), 264 noImplicitFast: true, 265 }, 266 { 267 name: "set fast option in command-line", 268 args: "--fast", 269 el: getEnabledByDefaultFastLintersWith(), 270 noImplicitFast: true, 271 }, 272 { 273 name: "fast option in command-line has higher priority to enable", 274 cfg: ` 275 linters: 276 fast: false 277 `, 278 args: "--fast", 279 el: getEnabledByDefaultFastLintersWith(), 280 noImplicitFast: true, 281 }, 282 { 283 name: "fast option in command-line has higher priority to disable", 284 cfg: ` 285 linters: 286 fast: true 287 `, 288 args: "--fast=false", 289 el: getEnabledByDefaultLinters(), 290 noImplicitFast: true, 291 }, 292 { 293 name: "fast option combined with enable and enable-all", 294 args: "--enable-all --fast --enable=typecheck", 295 el: getAllFastLintersWith("typecheck"), 296 noImplicitFast: true, 297 }, 298 } 299 300 for _, c := range cases { 301 t.Run(c.name, func(t *testing.T) { 302 runArgs := []string{"-v"} 303 if !c.noImplicitFast { 304 runArgs = append(runArgs, "--fast") 305 } 306 if c.args != "" { 307 runArgs = append(runArgs, strings.Split(c.args, " ")...) 308 } 309 out := runGolangciLintWithYamlConfig(t, c.cfg, runArgs...) 310 el := mergeMegacheck(c.el) 311 sort.StringSlice(el).Sort() 312 expectedLine := fmt.Sprintf("Active %d linters: [%s]", len(el), strings.Join(el, " ")) 313 assert.Contains(t, out, expectedLine) 314 }) 315 } 316 } 317 318 func TestEnabledPresetsAreNotDuplicated(t *testing.T) { 319 out, ec := runGolangciLint(t, "--no-config", "-v", "-p", "style,bugs") 320 assert.Equal(t, 0, ec) 321 assert.Contains(t, out, "Active presets: [bugs style]") 322 } 323 324 func TestDisallowedOptionsInConfig(t *testing.T) { 325 type tc struct { 326 cfg string 327 option string 328 } 329 330 cases := []tc{ 331 { 332 cfg: ` 333 ruN: 334 Args: 335 - 1 336 `, 337 }, 338 { 339 cfg: ` 340 run: 341 CPUProfilePath: path 342 `, 343 option: "--cpu-profile-path=path", 344 }, 345 { 346 cfg: ` 347 run: 348 MemProfilePath: path 349 `, 350 option: "--mem-profile-path=path", 351 }, 352 { 353 cfg: ` 354 run: 355 Verbose: true 356 `, 357 option: "-v", 358 }, 359 } 360 361 for _, c := range cases { 362 // Run with disallowed option set only in config 363 _, ec := runGolangciLintWithYamlConfigWithCode(t, c.cfg) 364 assert.Equal(t, 1, ec) 365 366 if c.option == "" { 367 continue 368 } 369 370 args := []string{c.option, "--fast"} 371 372 // Run with disallowed option set only in command-line 373 _, ec = runGolangciLint(t, args...) 374 assert.Equal(t, 0, ec) 375 376 // Run with disallowed option set both in command-line and in config 377 _, ec = runGolangciLintWithYamlConfigWithCode(t, c.cfg, args...) 378 assert.Equal(t, 1, ec) 379 } 380 }