hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

testenv.go (10501B)

    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 	"bytes"
   15 	"errors"
   16 	"flag"
   17 	"github.com/gohugoio/hugo/tpl/internal/go_templates/cfg"
   18 	"os"
   19 	"os/exec"
   20 	"path/filepath"
   21 	"runtime"
   22 	"strconv"
   23 	"strings"
   24 	"sync"
   25 	"testing"
   26 	"time"
   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 // GoTool reports the path to the Go tool.
  100 func GoTool() (string, error) {
  101 	if !HasGoBuild() {
  102 		return "", errors.New("platform cannot run go tool")
  103 	}
  104 	var exeSuffix string
  105 	if runtime.GOOS == "windows" {
  106 		exeSuffix = ".exe"
  107 	}
  108 	path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix)
  109 	if _, err := os.Stat(path); err == nil {
  110 		return path, nil
  111 	}
  112 	goBin, err := exec.LookPath("go" + exeSuffix)
  113 	if err != nil {
  114 		return "", errors.New("cannot find go tool: " + err.Error())
  115 	}
  116 	return goBin, nil
  117 }
  118 
  119 // HasExec reports whether the current system can start new processes
  120 // using os.StartProcess or (more commonly) exec.Command.
  121 func HasExec() bool {
  122 	switch runtime.GOOS {
  123 	case "js", "ios":
  124 		return false
  125 	}
  126 	return true
  127 }
  128 
  129 // HasSrc reports whether the entire source tree is available under GOROOT.
  130 func HasSrc() bool {
  131 	switch runtime.GOOS {
  132 	case "ios":
  133 		return false
  134 	}
  135 	return true
  136 }
  137 
  138 // MustHaveExec checks that the current system can start new processes
  139 // using os.StartProcess or (more commonly) exec.Command.
  140 // If not, MustHaveExec calls t.Skip with an explanation.
  141 func MustHaveExec(t testing.TB) {
  142 	if !HasExec() {
  143 		t.Skipf("skipping test: cannot exec subprocess on %s/%s", runtime.GOOS, runtime.GOARCH)
  144 	}
  145 }
  146 
  147 var execPaths sync.Map // path -> error
  148 
  149 // MustHaveExecPath checks that the current system can start the named executable
  150 // using os.StartProcess or (more commonly) exec.Command.
  151 // If not, MustHaveExecPath calls t.Skip with an explanation.
  152 func MustHaveExecPath(t testing.TB, path string) {
  153 	MustHaveExec(t)
  154 
  155 	err, found := execPaths.Load(path)
  156 	if !found {
  157 		_, err = exec.LookPath(path)
  158 		err, _ = execPaths.LoadOrStore(path, err)
  159 	}
  160 	if err != nil {
  161 		t.Skipf("skipping test: %s: %s", path, err)
  162 	}
  163 }
  164 
  165 // HasExternalNetwork reports whether the current system can use
  166 // external (non-localhost) networks.
  167 func HasExternalNetwork() bool {
  168 	return !testing.Short() && runtime.GOOS != "js"
  169 }
  170 
  171 // MustHaveExternalNetwork checks that the current system can use
  172 // external (non-localhost) networks.
  173 // If not, MustHaveExternalNetwork calls t.Skip with an explanation.
  174 func MustHaveExternalNetwork(t testing.TB) {
  175 	if runtime.GOOS == "js" {
  176 		t.Skipf("skipping test: no external network on %s", runtime.GOOS)
  177 	}
  178 	if testing.Short() {
  179 		t.Skipf("skipping test: no external network in -short mode")
  180 	}
  181 }
  182 
  183 var haveCGO bool
  184 
  185 // HasCGO reports whether the current system can use cgo.
  186 func HasCGO() bool {
  187 	return haveCGO
  188 }
  189 
  190 // MustHaveCGO calls t.Skip if cgo is not available.
  191 func MustHaveCGO(t testing.TB) {
  192 	if !haveCGO {
  193 		t.Skipf("skipping test: no cgo")
  194 	}
  195 }
  196 
  197 // CanInternalLink reports whether the current system can link programs with
  198 // internal linking.
  199 // (This is the opposite of cmd/internal/sys.MustLinkExternal. Keep them in sync.)
  200 func CanInternalLink() bool {
  201 	switch runtime.GOOS {
  202 	case "android":
  203 		if runtime.GOARCH != "arm64" {
  204 			return false
  205 		}
  206 	case "ios":
  207 		if runtime.GOARCH == "arm64" {
  208 			return false
  209 		}
  210 	}
  211 	return true
  212 }
  213 
  214 // MustInternalLink checks that the current system can link programs with internal
  215 // linking.
  216 // If not, MustInternalLink calls t.Skip with an explanation.
  217 func MustInternalLink(t testing.TB) {
  218 	if !CanInternalLink() {
  219 		t.Skipf("skipping test: internal linking on %s/%s is not supported", runtime.GOOS, runtime.GOARCH)
  220 	}
  221 }
  222 
  223 // HasSymlink reports whether the current system can use os.Symlink.
  224 func HasSymlink() bool {
  225 	ok, _ := hasSymlink()
  226 	return ok
  227 }
  228 
  229 // MustHaveSymlink reports whether the current system can use os.Symlink.
  230 // If not, MustHaveSymlink calls t.Skip with an explanation.
  231 func MustHaveSymlink(t testing.TB) {
  232 	ok, reason := hasSymlink()
  233 	if !ok {
  234 		t.Skipf("skipping test: cannot make symlinks on %s/%s%s", runtime.GOOS, runtime.GOARCH, reason)
  235 	}
  236 }
  237 
  238 // HasLink reports whether the current system can use os.Link.
  239 func HasLink() bool {
  240 	// From Android release M (Marshmallow), hard linking files is blocked
  241 	// and an attempt to call link() on a file will return EACCES.
  242 	// - https://code.google.com/p/android-developer-preview/issues/detail?id=3150
  243 	return runtime.GOOS != "plan9" && runtime.GOOS != "android"
  244 }
  245 
  246 // MustHaveLink reports whether the current system can use os.Link.
  247 // If not, MustHaveLink calls t.Skip with an explanation.
  248 func MustHaveLink(t testing.TB) {
  249 	if !HasLink() {
  250 		t.Skipf("skipping test: hardlinks are not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
  251 	}
  252 }
  253 
  254 var flaky = flag.Bool("flaky", false, "run known-flaky tests too")
  255 
  256 func SkipFlaky(t testing.TB, issue int) {
  257 	t.Helper()
  258 	if !*flaky {
  259 		t.Skipf("skipping known flaky test without the -flaky flag; see golang.org/issue/%d", issue)
  260 	}
  261 }
  262 
  263 func SkipFlakyNet(t testing.TB) {
  264 	t.Helper()
  265 	if v, _ := strconv.ParseBool(os.Getenv("GO_BUILDER_FLAKY_NET")); v {
  266 		t.Skip("skipping test on builder known to have frequent network failures")
  267 	}
  268 }
  269 
  270 // CleanCmdEnv will fill cmd.Env with the environment, excluding certain
  271 // variables that could modify the behavior of the Go tools such as
  272 // GODEBUG and GOTRACEBACK.
  273 func CleanCmdEnv(cmd *exec.Cmd) *exec.Cmd {
  274 	if cmd.Env != nil {
  275 		panic("environment already set")
  276 	}
  277 	for _, env := range os.Environ() {
  278 		// Exclude GODEBUG from the environment to prevent its output
  279 		// from breaking tests that are trying to parse other command output.
  280 		if strings.HasPrefix(env, "GODEBUG=") {
  281 			continue
  282 		}
  283 		// Exclude GOTRACEBACK for the same reason.
  284 		if strings.HasPrefix(env, "GOTRACEBACK=") {
  285 			continue
  286 		}
  287 		cmd.Env = append(cmd.Env, env)
  288 	}
  289 	return cmd
  290 }
  291 
  292 // CPUIsSlow reports whether the CPU running the test is suspected to be slow.
  293 func CPUIsSlow() bool {
  294 	switch runtime.GOARCH {
  295 	case "arm", "mips", "mipsle", "mips64", "mips64le":
  296 		return true
  297 	}
  298 	return false
  299 }
  300 
  301 // SkipIfShortAndSlow skips t if -short is set and the CPU running the test is
  302 // suspected to be slow.
  303 //
  304 // (This is useful for CPU-intensive tests that otherwise complete quickly.)
  305 func SkipIfShortAndSlow(t testing.TB) {
  306 	if testing.Short() && CPUIsSlow() {
  307 		t.Helper()
  308 		t.Skipf("skipping test in -short mode on %s", runtime.GOARCH)
  309 	}
  310 }
  311 
  312 // RunWithTimeout runs cmd and returns its combined output. If the
  313 // subprocess exits with a non-zero status, it will log that status
  314 // and return a non-nil error, but this is not considered fatal.
  315 func RunWithTimeout(t testing.TB, cmd *exec.Cmd) ([]byte, error) {
  316 	args := cmd.Args
  317 	if args == nil {
  318 		args = []string{cmd.Path}
  319 	}
  320 
  321 	var b bytes.Buffer
  322 	cmd.Stdout = &b
  323 	cmd.Stderr = &b
  324 	if err := cmd.Start(); err != nil {
  325 		t.Fatalf("starting %s: %v", args, err)
  326 	}
  327 
  328 	// If the process doesn't complete within 1 minute,
  329 	// assume it is hanging and kill it to get a stack trace.
  330 	p := cmd.Process
  331 	done := make(chan bool)
  332 	go func() {
  333 		scale := 1
  334 		// This GOARCH/GOOS test is copied from cmd/dist/test.go.
  335 		// TODO(iant): Have cmd/dist update the environment variable.
  336 		if runtime.GOARCH == "arm" || runtime.GOOS == "windows" {
  337 			scale = 2
  338 		}
  339 		if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
  340 			if sc, err := strconv.Atoi(s); err == nil {
  341 				scale = sc
  342 			}
  343 		}
  344 
  345 		select {
  346 		case <-done:
  347 		case <-time.After(time.Duration(scale) * time.Minute):
  348 			p.Signal(Sigquit)
  349 			// If SIGQUIT doesn't do it after a little
  350 			// while, kill the process.
  351 			select {
  352 			case <-done:
  353 			case <-time.After(time.Duration(scale) * 30 * time.Second):
  354 				p.Signal(os.Kill)
  355 			}
  356 		}
  357 	}()
  358 
  359 	err := cmd.Wait()
  360 	if err != nil {
  361 		t.Logf("%s exit status: %v", args, err)
  362 	}
  363 	close(done)
  364 
  365 	return b.Bytes(), err
  366 }