github.com/SandwichDev/go-internals@v0.0.0-20210605002614-12311ac6b2c5/testenv/testenv.go (about) 1 // Copyright 2015 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 testenv provides information about what functionality 6 // is available in different testing environments run by the Go team. 7 // 8 // It is an internal package because these details are specific 9 // to the Go team's test setup (on build.golang.org) and not 10 // fundamental to tests in general. 11 package testenv 12 13 import ( 14 "errors" 15 "flag" 16 "os" 17 "os/exec" 18 "path/filepath" 19 "runtime" 20 "strconv" 21 "strings" 22 "sync" 23 "testing" 24 25 "github.com/SandwichDev/go-internals/cfg" 26 ) 27 28 // Builder reports the name of the builder running this test 29 // (for example, "linux-amd64" or "windows-386-gce"). 30 // If the test is not running on the build infrastructure, 31 // Builder returns the empty string. 32 func Builder() string { 33 return os.Getenv("GO_BUILDER_NAME") 34 } 35 36 // HasGoBuild reports whether the current system can build programs with ``go build'' 37 // and then run them with os.StartProcess or exec.Command. 38 func HasGoBuild() bool { 39 if os.Getenv("GO_GCFLAGS") != "" { 40 // It's too much work to require every caller of the go command 41 // to pass along "-gcflags="+os.Getenv("GO_GCFLAGS"). 42 // For now, if $GO_GCFLAGS is set, report that we simply can't 43 // run go build. 44 return false 45 } 46 switch runtime.GOOS { 47 case "android", "js", "ios": 48 return false 49 } 50 return true 51 } 52 53 // MustHaveGoBuild checks that the current system can build programs with ``go build'' 54 // and then run them with os.StartProcess or exec.Command. 55 // If not, MustHaveGoBuild calls t.Skip with an explanation. 56 func MustHaveGoBuild(t testing.TB) { 57 if os.Getenv("GO_GCFLAGS") != "" { 58 t.Skipf("skipping test: 'go build' not compatible with setting $GO_GCFLAGS") 59 } 60 if !HasGoBuild() { 61 t.Skipf("skipping test: 'go build' not available on %s/%s", runtime.GOOS, runtime.GOARCH) 62 } 63 } 64 65 // HasGoRun reports whether the current system can run programs with ``go run.'' 66 func HasGoRun() bool { 67 // For now, having go run and having go build are the same. 68 return HasGoBuild() 69 } 70 71 // MustHaveGoRun checks that the current system can run programs with ``go run.'' 72 // If not, MustHaveGoRun calls t.Skip with an explanation. 73 func MustHaveGoRun(t testing.TB) { 74 if !HasGoRun() { 75 t.Skipf("skipping test: 'go run' not available on %s/%s", runtime.GOOS, runtime.GOARCH) 76 } 77 } 78 79 // GoToolPath reports the path to the Go tool. 80 // It is a convenience wrapper around GoTool. 81 // If the tool is unavailable GoToolPath calls t.Skip. 82 // If the tool should be available and isn't, GoToolPath calls t.Fatal. 83 func GoToolPath(t testing.TB) string { 84 MustHaveGoBuild(t) 85 path, err := GoTool() 86 if err != nil { 87 t.Fatal(err) 88 } 89 // Add all environment variables that affect the Go command to test metadata. 90 // Cached test results will be invalidate when these variables change. 91 // See golang.org/issue/32285. 92 for _, envVar := range strings.Fields(cfg.KnownEnv) { 93 os.Getenv(envVar) 94 } 95 return path 96 } 97 98 // GoTool reports the path to the Go tool. 99 func GoTool() (string, error) { 100 if !HasGoBuild() { 101 return "", errors.New("platform cannot run go tool") 102 } 103 var exeSuffix string 104 if runtime.GOOS == "windows" { 105 exeSuffix = ".exe" 106 } 107 path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix) 108 if _, err := os.Stat(path); err == nil { 109 return path, nil 110 } 111 goBin, err := exec.LookPath("go" + exeSuffix) 112 if err != nil { 113 return "", errors.New("cannot find go tool: " + err.Error()) 114 } 115 return goBin, nil 116 } 117 118 // HasExec reports whether the current system can start new processes 119 // using os.StartProcess or (more commonly) exec.Command. 120 func HasExec() bool { 121 switch runtime.GOOS { 122 case "js", "ios": 123 return false 124 } 125 return true 126 } 127 128 // HasSrc reports whether the entire source tree is available under GOROOT. 129 func HasSrc() bool { 130 switch runtime.GOOS { 131 case "ios": 132 return false 133 } 134 return true 135 } 136 137 // MustHaveExec checks that the current system can start new processes 138 // using os.StartProcess or (more commonly) exec.Command. 139 // If not, MustHaveExec calls t.Skip with an explanation. 140 func MustHaveExec(t testing.TB) { 141 if !HasExec() { 142 t.Skipf("skipping test: cannot exec subprocess on %s/%s", runtime.GOOS, runtime.GOARCH) 143 } 144 } 145 146 var execPaths sync.Map // path -> error 147 148 // MustHaveExecPath checks that the current system can start the named executable 149 // using os.StartProcess or (more commonly) exec.Command. 150 // If not, MustHaveExecPath calls t.Skip with an explanation. 151 func MustHaveExecPath(t testing.TB, path string) { 152 MustHaveExec(t) 153 154 err, found := execPaths.Load(path) 155 if !found { 156 _, err = exec.LookPath(path) 157 err, _ = execPaths.LoadOrStore(path, err) 158 } 159 if err != nil { 160 t.Skipf("skipping test: %s: %s", path, err) 161 } 162 } 163 164 // HasExternalNetwork reports whether the current system can use 165 // external (non-localhost) networks. 166 func HasExternalNetwork() bool { 167 return !testing.Short() && runtime.GOOS != "js" 168 } 169 170 // MustHaveExternalNetwork checks that the current system can use 171 // external (non-localhost) networks. 172 // If not, MustHaveExternalNetwork calls t.Skip with an explanation. 173 func MustHaveExternalNetwork(t testing.TB) { 174 if runtime.GOOS == "js" { 175 t.Skipf("skipping test: no external network on %s", runtime.GOOS) 176 } 177 if testing.Short() { 178 t.Skipf("skipping test: no external network in -short mode") 179 } 180 } 181 182 var haveCGO bool 183 184 // HasCGO reports whether the current system can use cgo. 185 func HasCGO() bool { 186 return haveCGO 187 } 188 189 // MustHaveCGO calls t.Skip if cgo is not available. 190 func MustHaveCGO(t testing.TB) { 191 if !haveCGO { 192 t.Skipf("skipping test: no cgo") 193 } 194 } 195 196 // CanInternalLink reports whether the current system can link programs with 197 // internal linking. 198 // (This is the opposite of cmd/internal/sys.MustLinkExternal. Keep them in sync.) 199 func CanInternalLink() bool { 200 switch runtime.GOOS { 201 case "android": 202 if runtime.GOARCH != "arm64" { 203 return false 204 } 205 case "ios": 206 if runtime.GOARCH == "arm64" { 207 return false 208 } 209 } 210 return true 211 } 212 213 // MustInternalLink checks that the current system can link programs with internal 214 // linking. 215 // If not, MustInternalLink calls t.Skip with an explanation. 216 func MustInternalLink(t testing.TB) { 217 if !CanInternalLink() { 218 t.Skipf("skipping test: internal linking on %s/%s is not supported", runtime.GOOS, runtime.GOARCH) 219 } 220 } 221 222 // HasSymlink reports whether the current system can use os.Symlink. 223 func HasSymlink() bool { 224 ok, _ := hasSymlink() 225 return ok 226 } 227 228 // MustHaveSymlink reports whether the current system can use os.Symlink. 229 // If not, MustHaveSymlink calls t.Skip with an explanation. 230 func MustHaveSymlink(t testing.TB) { 231 ok, reason := hasSymlink() 232 if !ok { 233 t.Skipf("skipping test: cannot make symlinks on %s/%s%s", runtime.GOOS, runtime.GOARCH, reason) 234 } 235 } 236 237 // HasLink reports whether the current system can use os.Link. 238 func HasLink() bool { 239 // From Android release M (Marshmallow), hard linking files is blocked 240 // and an attempt to call link() on a file will return EACCES. 241 // - https://code.google.com/p/android-developer-preview/issues/detail?id=3150 242 return runtime.GOOS != "plan9" && runtime.GOOS != "android" 243 } 244 245 // MustHaveLink reports whether the current system can use os.Link. 246 // If not, MustHaveLink calls t.Skip with an explanation. 247 func MustHaveLink(t testing.TB) { 248 if !HasLink() { 249 t.Skipf("skipping test: hardlinks are not supported on %s/%s", runtime.GOOS, runtime.GOARCH) 250 } 251 } 252 253 var flaky = flag.Bool("flaky", false, "run known-flaky tests too") 254 255 func SkipFlaky(t testing.TB, issue int) { 256 t.Helper() 257 if !*flaky { 258 t.Skipf("skipping known flaky test without the -flaky flag; see golang.org/issue/%d", issue) 259 } 260 } 261 262 func SkipFlakyNet(t testing.TB) { 263 t.Helper() 264 if v, _ := strconv.ParseBool(os.Getenv("GO_BUILDER_FLAKY_NET")); v { 265 t.Skip("skipping test on builder known to have frequent network failures") 266 } 267 } 268 269 // CleanCmdEnv will fill cmd.Env with the environment, excluding certain 270 // variables that could modify the behavior of the Go tools such as 271 // GODEBUG and GOTRACEBACK. 272 func CleanCmdEnv(cmd *exec.Cmd) *exec.Cmd { 273 if cmd.Env != nil { 274 panic("environment already set") 275 } 276 for _, env := range os.Environ() { 277 // Exclude GODEBUG from the environment to prevent its output 278 // from breaking tests that are trying to parse other command output. 279 if strings.HasPrefix(env, "GODEBUG=") { 280 continue 281 } 282 // Exclude GOTRACEBACK for the same reason. 283 if strings.HasPrefix(env, "GOTRACEBACK=") { 284 continue 285 } 286 cmd.Env = append(cmd.Env, env) 287 } 288 return cmd 289 } 290 291 // CPUIsSlow reports whether the CPU running the test is suspected to be slow. 292 func CPUIsSlow() bool { 293 switch runtime.GOARCH { 294 case "arm", "mips", "mipsle", "mips64", "mips64le": 295 return true 296 } 297 return false 298 } 299 300 // SkipIfShortAndSlow skips t if -short is set and the CPU running the test is 301 // suspected to be slow. 302 // 303 // (This is useful for CPU-intensive tests that otherwise complete quickly.) 304 func SkipIfShortAndSlow(t testing.TB) { 305 if testing.Short() && CPUIsSlow() { 306 t.Helper() 307 t.Skipf("skipping test in -short mode on %s", runtime.GOARCH) 308 } 309 }