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