github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/tpl/internal/go_templates/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 "fmt" 17 "os" 18 "os/exec" 19 "path/filepath" 20 "runtime" 21 "strconv" 22 "strings" 23 "sync" 24 "testing" 25 26 "github.com/gohugoio/hugo/tpl/internal/go_templates/cfg" 27 ) 28 29 // Builder reports the name of the builder running this test 30 // (for example, "linux-amd64" or "windows-386-gce"). 31 // If the test is not running on the build infrastructure, 32 // Builder returns the empty string. 33 func Builder() string { 34 return os.Getenv("GO_BUILDER_NAME") 35 } 36 37 // HasGoBuild reports whether the current system can build programs with “go build” 38 // and then run them with os.StartProcess or exec.Command. 39 func HasGoBuild() bool { 40 if os.Getenv("GO_GCFLAGS") != "" { 41 // It's too much work to require every caller of the go command 42 // to pass along "-gcflags="+os.Getenv("GO_GCFLAGS"). 43 // For now, if $GO_GCFLAGS is set, report that we simply can't 44 // run go build. 45 return false 46 } 47 switch runtime.GOOS { 48 case "android", "js", "ios": 49 return false 50 } 51 return true 52 } 53 54 // MustHaveGoBuild checks that the current system can build programs with “go build” 55 // and then run them with os.StartProcess or exec.Command. 56 // If not, MustHaveGoBuild calls t.Skip with an explanation. 57 func MustHaveGoBuild(t testing.TB) { 58 if os.Getenv("GO_GCFLAGS") != "" { 59 t.Skipf("skipping test: 'go build' not compatible with setting $GO_GCFLAGS") 60 } 61 if !HasGoBuild() { 62 t.Skipf("skipping test: 'go build' not available on %s/%s", runtime.GOOS, runtime.GOARCH) 63 } 64 } 65 66 // HasGoRun reports whether the current system can run programs with “go run.” 67 func HasGoRun() bool { 68 // For now, having go run and having go build are the same. 69 return HasGoBuild() 70 } 71 72 // MustHaveGoRun checks that the current system can run programs with “go run.” 73 // If not, MustHaveGoRun calls t.Skip with an explanation. 74 func MustHaveGoRun(t testing.TB) { 75 if !HasGoRun() { 76 t.Skipf("skipping test: 'go run' not available on %s/%s", runtime.GOOS, runtime.GOARCH) 77 } 78 } 79 80 // GoToolPath reports the path to the Go tool. 81 // It is a convenience wrapper around GoTool. 82 // If the tool is unavailable GoToolPath calls t.Skip. 83 // If the tool should be available and isn't, GoToolPath calls t.Fatal. 84 func GoToolPath(t testing.TB) string { 85 MustHaveGoBuild(t) 86 path, err := GoTool() 87 if err != nil { 88 t.Fatal(err) 89 } 90 // Add all environment variables that affect the Go command to test metadata. 91 // Cached test results will be invalidate when these variables change. 92 // See golang.org/issue/32285. 93 for _, envVar := range strings.Fields(cfg.KnownEnv) { 94 os.Getenv(envVar) 95 } 96 return path 97 } 98 99 var ( 100 gorootOnce sync.Once 101 gorootPath string 102 gorootErr error 103 ) 104 105 func findGOROOT() (string, error) { 106 gorootOnce.Do(func() { 107 gorootPath = runtime.GOROOT() 108 if gorootPath != "" { 109 // If runtime.GOROOT() is non-empty, assume that it is valid. 110 // 111 // (It might not be: for example, the user may have explicitly set GOROOT 112 // to the wrong directory, or explicitly set GOROOT_FINAL but not GOROOT 113 // and hasn't moved the tree to GOROOT_FINAL yet. But those cases are 114 // rare, and if that happens the user can fix what they broke.) 115 return 116 } 117 118 // runtime.GOROOT doesn't know where GOROOT is (perhaps because the test 119 // binary was built with -trimpath, or perhaps because GOROOT_FINAL was set 120 // without GOROOT and the tree hasn't been moved there yet). 121 // 122 // Since this is internal/testenv, we can cheat and assume that the caller 123 // is a test of some package in a subdirectory of GOROOT/src. ('go test' 124 // runs the test in the directory containing the packaged under test.) That 125 // means that if we start walking up the tree, we should eventually find 126 // GOROOT/src/go.mod, and we can report the parent directory of that. 127 128 cwd, err := os.Getwd() 129 if err != nil { 130 gorootErr = fmt.Errorf("finding GOROOT: %w", err) 131 return 132 } 133 134 dir := cwd 135 for { 136 parent := filepath.Dir(dir) 137 if parent == dir { 138 // dir is either "." or only a volume name. 139 gorootErr = fmt.Errorf("failed to locate GOROOT/src in any parent directory") 140 return 141 } 142 143 if base := filepath.Base(dir); base != "src" { 144 dir = parent 145 continue // dir cannot be GOROOT/src if it doesn't end in "src". 146 } 147 148 b, err := os.ReadFile(filepath.Join(dir, "go.mod")) 149 if err != nil { 150 if os.IsNotExist(err) { 151 dir = parent 152 continue 153 } 154 gorootErr = fmt.Errorf("finding GOROOT: %w", err) 155 return 156 } 157 goMod := string(b) 158 159 for goMod != "" { 160 var line string 161 line, goMod, _ = strings.Cut(goMod, "\n") 162 fields := strings.Fields(line) 163 if len(fields) >= 2 && fields[0] == "module" && fields[1] == "std" { 164 // Found "module std", which is the module declaration in GOROOT/src! 165 gorootPath = parent 166 return 167 } 168 } 169 } 170 }) 171 172 return gorootPath, gorootErr 173 } 174 175 // GOROOT reports the path to the directory containing the root of the Go 176 // project source tree. This is normally equivalent to runtime.GOROOT, but 177 // works even if the test binary was built with -trimpath. 178 // 179 // If GOROOT cannot be found, GOROOT skips t if t is non-nil, 180 // or panics otherwise. 181 func GOROOT(t testing.TB) string { 182 path, err := findGOROOT() 183 if err != nil { 184 if t == nil { 185 panic(err) 186 } 187 t.Helper() 188 t.Skip(err) 189 } 190 return path 191 } 192 193 // GoTool reports the path to the Go tool. 194 func GoTool() (string, error) { 195 if !HasGoBuild() { 196 return "", errors.New("platform cannot run go tool") 197 } 198 var exeSuffix string 199 if runtime.GOOS == "windows" { 200 exeSuffix = ".exe" 201 } 202 goroot, err := findGOROOT() 203 if err != nil { 204 return "", fmt.Errorf("cannot find go tool: %w", err) 205 } 206 path := filepath.Join(goroot, "bin", "go"+exeSuffix) 207 if _, err := os.Stat(path); err == nil { 208 return path, nil 209 } 210 goBin, err := exec.LookPath("go" + exeSuffix) 211 if err != nil { 212 return "", errors.New("cannot find go tool: " + err.Error()) 213 } 214 return goBin, nil 215 } 216 217 // HasSrc reports whether the entire source tree is available under GOROOT. 218 func HasSrc() bool { 219 switch runtime.GOOS { 220 case "ios": 221 return false 222 } 223 return true 224 } 225 226 // HasExternalNetwork reports whether the current system can use 227 // external (non-localhost) networks. 228 func HasExternalNetwork() bool { 229 return !testing.Short() && runtime.GOOS != "js" 230 } 231 232 // MustHaveExternalNetwork checks that the current system can use 233 // external (non-localhost) networks. 234 // If not, MustHaveExternalNetwork calls t.Skip with an explanation. 235 func MustHaveExternalNetwork(t testing.TB) { 236 if runtime.GOOS == "js" { 237 t.Skipf("skipping test: no external network on %s", runtime.GOOS) 238 } 239 if testing.Short() { 240 t.Skipf("skipping test: no external network in -short mode") 241 } 242 } 243 244 var haveCGO bool 245 246 // HasCGO reports whether the current system can use cgo. 247 func HasCGO() bool { 248 return haveCGO 249 } 250 251 // MustHaveCGO calls t.Skip if cgo is not available. 252 func MustHaveCGO(t testing.TB) { 253 if !haveCGO { 254 t.Skipf("skipping test: no cgo") 255 } 256 } 257 258 // CanInternalLink reports whether the current system can link programs with 259 // internal linking. 260 func CanInternalLink() bool { 261 return false 262 } 263 264 // MustInternalLink checks that the current system can link programs with internal 265 // linking. 266 // If not, MustInternalLink calls t.Skip with an explanation. 267 func MustInternalLink(t testing.TB) { 268 if !CanInternalLink() { 269 t.Skipf("skipping test: internal linking on %s/%s is not supported", runtime.GOOS, runtime.GOARCH) 270 } 271 } 272 273 // HasSymlink reports whether the current system can use os.Symlink. 274 func HasSymlink() bool { 275 ok, _ := hasSymlink() 276 return ok 277 } 278 279 // MustHaveSymlink reports whether the current system can use os.Symlink. 280 // If not, MustHaveSymlink calls t.Skip with an explanation. 281 func MustHaveSymlink(t testing.TB) { 282 ok, reason := hasSymlink() 283 if !ok { 284 t.Skipf("skipping test: cannot make symlinks on %s/%s%s", runtime.GOOS, runtime.GOARCH, reason) 285 } 286 } 287 288 // HasLink reports whether the current system can use os.Link. 289 func HasLink() bool { 290 // From Android release M (Marshmallow), hard linking files is blocked 291 // and an attempt to call link() on a file will return EACCES. 292 // - https://code.google.com/p/android-developer-preview/issues/detail?id=3150 293 return runtime.GOOS != "plan9" && runtime.GOOS != "android" 294 } 295 296 // MustHaveLink reports whether the current system can use os.Link. 297 // If not, MustHaveLink calls t.Skip with an explanation. 298 func MustHaveLink(t testing.TB) { 299 if !HasLink() { 300 t.Skipf("skipping test: hardlinks are not supported on %s/%s", runtime.GOOS, runtime.GOARCH) 301 } 302 } 303 304 var flaky = flag.Bool("flaky", false, "run known-flaky tests too") 305 306 func SkipFlaky(t testing.TB, issue int) { 307 t.Helper() 308 if !*flaky { 309 t.Skipf("skipping known flaky test without the -flaky flag; see golang.org/issue/%d", issue) 310 } 311 } 312 313 func SkipFlakyNet(t testing.TB) { 314 t.Helper() 315 if v, _ := strconv.ParseBool(os.Getenv("GO_BUILDER_FLAKY_NET")); v { 316 t.Skip("skipping test on builder known to have frequent network failures") 317 } 318 } 319 320 // CPUIsSlow reports whether the CPU running the test is suspected to be slow. 321 func CPUIsSlow() bool { 322 switch runtime.GOARCH { 323 case "arm", "mips", "mipsle", "mips64", "mips64le": 324 return true 325 } 326 return false 327 } 328 329 // SkipIfShortAndSlow skips t if -short is set and the CPU running the test is 330 // suspected to be slow. 331 // 332 // (This is useful for CPU-intensive tests that otherwise complete quickly.) 333 func SkipIfShortAndSlow(t testing.TB) { 334 if testing.Short() && CPUIsSlow() { 335 t.Helper() 336 t.Skipf("skipping test in -short mode on %s", runtime.GOARCH) 337 } 338 } 339 340 // SkipIfOptimizationOff skips t if optimization is disabled. 341 func SkipIfOptimizationOff(t testing.TB) { 342 if OptimizationOff() { 343 t.Helper() 344 t.Skip("skipping test with optimization disabled") 345 } 346 } 347 348 // WriteImportcfg writes an importcfg file used by the compiler or linker to 349 // dstPath containing entries for the packages in std and cmd in addition 350 // to the package to package file mappings in additionalPackageFiles. 351 func WriteImportcfg(t testing.TB, dstPath string, additionalPackageFiles map[string]string) { 352 /*importcfg, err := goroot.Importcfg() 353 for k, v := range additionalPackageFiles { 354 importcfg += fmt.Sprintf("\npackagefile %s=%s", k, v) 355 } 356 if err != nil { 357 t.Fatalf("preparing the importcfg failed: %s", err) 358 } 359 err = os.WriteFile(dstPath, []byte(importcfg), 0655) 360 if err != nil { 361 t.Fatalf("writing the importcfg failed: %s", err) 362 }*/ 363 }