metalint_test.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. // Copyright (C) 2017 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package meta
  7. import (
  8. "bytes"
  9. "log"
  10. "os/exec"
  11. "strings"
  12. "testing"
  13. )
  14. var (
  15. // fast linters complete in a fraction of a second and might as well be
  16. // run always as part of the build
  17. fastLinters = []string{
  18. "deadcode",
  19. "golint",
  20. "ineffassign",
  21. "vet",
  22. }
  23. // slow linters take several seconds and are run only as part of the
  24. // "metalint" command.
  25. slowLinters = []string{
  26. "gosimple",
  27. "staticcheck",
  28. "structcheck",
  29. "unused",
  30. "varcheck",
  31. }
  32. // Which parts of the tree to lint
  33. lintDirs = []string{
  34. ".",
  35. "../cmd/...",
  36. "../lib/...",
  37. "../script/...",
  38. }
  39. // Messages to ignore
  40. lintExcludes = []string{
  41. ".pb.go",
  42. "should have comment",
  43. "protocol.Vector composite literal uses unkeyed fields",
  44. "cli.Requires composite literal uses unkeyed fields",
  45. "Use DialContext instead", // Go 1.7
  46. "os.SEEK_SET is deprecated", // Go 1.7
  47. "SA4017", // staticcheck "is a pure function but its return value is ignored"
  48. }
  49. )
  50. func TestCheckMetalint(t *testing.T) {
  51. if !isGometalinterInstalled() {
  52. return
  53. }
  54. gometalinter(t, lintDirs, lintExcludes...)
  55. }
  56. func isGometalinterInstalled() bool {
  57. if _, err := runError("gometalinter", "--disable-all"); err != nil {
  58. log.Println("gometalinter is not installed")
  59. return false
  60. }
  61. return true
  62. }
  63. func gometalinter(_ *testing.T, dirs []string, excludes ...string) bool {
  64. params := []string{"--disable-all", "--concurrency=2", "--deadline=300s"}
  65. for _, linter := range fastLinters {
  66. params = append(params, "--enable="+linter)
  67. }
  68. if !testing.Short() {
  69. for _, linter := range slowLinters {
  70. params = append(params, "--enable="+linter)
  71. }
  72. }
  73. for _, exclude := range excludes {
  74. params = append(params, "--exclude="+exclude)
  75. }
  76. params = append(params, dirs...)
  77. bs, _ := runError("gometalinter", params...)
  78. nerr := 0
  79. lines := make(map[string]struct{})
  80. for _, line := range strings.Split(string(bs), "\n") {
  81. if line == "" {
  82. continue
  83. }
  84. if _, ok := lines[line]; ok {
  85. continue
  86. }
  87. log.Println(line)
  88. if strings.Contains(line, "executable file not found") {
  89. log.Println(` - Try "go run build.go setup" to install missing tools`)
  90. }
  91. lines[line] = struct{}{}
  92. nerr++
  93. }
  94. return nerr == 0
  95. }
  96. func runError(cmd string, args ...string) ([]byte, error) {
  97. ecmd := exec.Command(cmd, args...)
  98. bs, err := ecmd.CombinedOutput()
  99. return bytes.TrimSpace(bs), err
  100. }