github.com/karrick/go@v0.0.0-20170817181416-d5b0ec858b37/src/cmd/vet/vet_test.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main_test 6 7 import ( 8 "bytes" 9 "fmt" 10 "internal/testenv" 11 "os" 12 "os/exec" 13 "path/filepath" 14 "runtime" 15 "sync" 16 "testing" 17 ) 18 19 const ( 20 dataDir = "testdata" 21 binary = "testvet.exe" 22 ) 23 24 // We implement TestMain so remove the test binary when all is done. 25 func TestMain(m *testing.M) { 26 result := m.Run() 27 os.Remove(binary) 28 os.Exit(result) 29 } 30 31 func MustHavePerl(t *testing.T) { 32 switch runtime.GOOS { 33 case "plan9", "windows": 34 t.Skipf("skipping test: perl not available on %s", runtime.GOOS) 35 } 36 if _, err := exec.LookPath("perl"); err != nil { 37 t.Skipf("skipping test: perl not found in path") 38 } 39 } 40 41 var ( 42 buildMu sync.Mutex // guards following 43 built = false // We have built the binary. 44 failed = false // We have failed to build the binary, don't try again. 45 ) 46 47 func Build(t *testing.T) { 48 buildMu.Lock() 49 defer buildMu.Unlock() 50 if built { 51 return 52 } 53 if failed { 54 t.Skip("cannot run on this environment") 55 } 56 testenv.MustHaveGoBuild(t) 57 MustHavePerl(t) 58 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", binary) 59 output, err := cmd.CombinedOutput() 60 if err != nil { 61 failed = true 62 fmt.Fprintf(os.Stderr, "%s\n", output) 63 t.Fatal(err) 64 } 65 built = true 66 } 67 68 func Vet(t *testing.T, files []string) { 69 errchk := filepath.Join(runtime.GOROOT(), "test", "errchk") 70 flags := []string{ 71 "./" + binary, 72 "-printfuncs=Warn:1,Warnf:1", 73 "-all", 74 "-shadow", 75 } 76 cmd := exec.Command(errchk, append(flags, files...)...) 77 if !run(cmd, t) { 78 t.Fatal("vet command failed") 79 } 80 } 81 82 // Run this shell script, but do it in Go so it can be run by "go test". 83 // go build -o testvet 84 // $(GOROOT)/test/errchk ./testvet -shadow -printfuncs='Warn:1,Warnf:1' testdata/*.go testdata/*.s 85 // rm testvet 86 // 87 88 // TestVet tests self-contained files in testdata/*.go. 89 // 90 // If a file contains assembly or has inter-dependencies, it should be 91 // in its own test, like TestVetAsm, TestDivergentPackagesExamples, 92 // etc below. 93 func TestVet(t *testing.T) { 94 Build(t) 95 t.Parallel() 96 97 // errchk ./testvet 98 gos, err := filepath.Glob(filepath.Join(dataDir, "*.go")) 99 if err != nil { 100 t.Fatal(err) 101 } 102 wide := runtime.GOMAXPROCS(0) 103 if wide > len(gos) { 104 wide = len(gos) 105 } 106 batch := make([][]string, wide) 107 for i, file := range gos { 108 batch[i%wide] = append(batch[i%wide], file) 109 } 110 for i, files := range batch { 111 files := files 112 t.Run(fmt.Sprint(i), func(t *testing.T) { 113 t.Parallel() 114 t.Logf("files: %q", files) 115 Vet(t, files) 116 }) 117 } 118 } 119 120 func TestVetAsm(t *testing.T) { 121 Build(t) 122 123 asmDir := filepath.Join(dataDir, "asm") 124 gos, err := filepath.Glob(filepath.Join(asmDir, "*.go")) 125 if err != nil { 126 t.Fatal(err) 127 } 128 asms, err := filepath.Glob(filepath.Join(asmDir, "*.s")) 129 if err != nil { 130 t.Fatal(err) 131 } 132 133 t.Parallel() 134 // errchk ./testvet 135 Vet(t, append(gos, asms...)) 136 } 137 138 func TestVetDirs(t *testing.T) { 139 t.Parallel() 140 Build(t) 141 for _, dir := range []string{ 142 "testingpkg", 143 "divergent", 144 "buildtag", 145 "incomplete", // incomplete examples 146 "cgo", 147 } { 148 dir := dir 149 t.Run(dir, func(t *testing.T) { 150 t.Parallel() 151 gos, err := filepath.Glob(filepath.Join("testdata", dir, "*.go")) 152 if err != nil { 153 t.Fatal(err) 154 } 155 Vet(t, gos) 156 }) 157 } 158 } 159 160 func run(c *exec.Cmd, t *testing.T) bool { 161 output, err := c.CombinedOutput() 162 if err != nil { 163 t.Logf("vet output:\n%s", output) 164 t.Fatal(err) 165 } 166 // Errchk delights by not returning non-zero status if it finds errors, so we look at the output. 167 // It prints "BUG" if there is a failure. 168 if !c.ProcessState.Success() { 169 t.Logf("vet output:\n%s", output) 170 return false 171 } 172 ok := !bytes.Contains(output, []byte("BUG")) 173 if !ok { 174 t.Logf("vet output:\n%s", output) 175 } 176 return ok 177 } 178 179 // TestTags verifies that the -tags argument controls which files to check. 180 func TestTags(t *testing.T) { 181 t.Parallel() 182 Build(t) 183 for _, tag := range []string{"testtag", "x testtag y", "x,testtag,y"} { 184 tag := tag 185 t.Run(tag, func(t *testing.T) { 186 t.Parallel() 187 t.Logf("-tags=%s", tag) 188 args := []string{ 189 "-tags=" + tag, 190 "-v", // We're going to look at the files it examines. 191 "testdata/tagtest", 192 } 193 cmd := exec.Command("./"+binary, args...) 194 output, err := cmd.CombinedOutput() 195 if err != nil { 196 t.Fatal(err) 197 } 198 // file1 has testtag and file2 has !testtag. 199 if !bytes.Contains(output, []byte(filepath.Join("tagtest", "file1.go"))) { 200 t.Error("file1 was excluded, should be included") 201 } 202 if bytes.Contains(output, []byte(filepath.Join("tagtest", "file2.go"))) { 203 t.Error("file2 was included, should be excluded") 204 } 205 }) 206 } 207 } 208 209 // Issue #21188. 210 func TestVetVerbose(t *testing.T) { 211 t.Parallel() 212 Build(t) 213 cmd := exec.Command("./"+binary, "-v", "-all", "testdata/cgo/cgo3.go") 214 out, err := cmd.CombinedOutput() 215 if err != nil { 216 t.Logf("%s", out) 217 t.Error(err) 218 } 219 }