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