summaryrefslogtreecommitdiffstats
path: root/src/cmd/go/internal/script/scripttest
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/go/internal/script/scripttest')
-rw-r--r--src/cmd/go/internal/script/scripttest/scripttest.go143
1 files changed, 143 insertions, 0 deletions
diff --git a/src/cmd/go/internal/script/scripttest/scripttest.go b/src/cmd/go/internal/script/scripttest/scripttest.go
new file mode 100644
index 0000000..6d7bd78
--- /dev/null
+++ b/src/cmd/go/internal/script/scripttest/scripttest.go
@@ -0,0 +1,143 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package scripttest adapts the script engine for use in tests.
+package scripttest
+
+import (
+ "bufio"
+ "cmd/go/internal/cfg"
+ "cmd/go/internal/script"
+ "errors"
+ "io"
+ "strings"
+ "testing"
+)
+
+// DefaultCmds returns a set of broadly useful script commands.
+//
+// This set includes all of the commands in script.DefaultCmds,
+// as well as a "skip" command that halts the script and causes the
+// testing.TB passed to Run to be skipped.
+func DefaultCmds() map[string]script.Cmd {
+ cmds := script.DefaultCmds()
+ cmds["skip"] = Skip()
+ return cmds
+}
+
+// DefaultConds returns a set of broadly useful script conditions.
+//
+// This set includes all of the conditions in script.DefaultConds,
+// as well as:
+//
+// - Conditions of the form "exec:foo" are active when the executable "foo" is
+// found in the test process's PATH, and inactive when the executable is
+// not found.
+//
+// - "short" is active when testing.Short() is true.
+//
+// - "verbose" is active when testing.Verbose() is true.
+func DefaultConds() map[string]script.Cond {
+ conds := script.DefaultConds()
+ conds["exec"] = CachedExec()
+ conds["short"] = script.BoolCondition("testing.Short()", testing.Short())
+ conds["verbose"] = script.BoolCondition("testing.Verbose()", testing.Verbose())
+ return conds
+}
+
+// Run runs the script from the given filename starting at the given initial state.
+// When the script completes, Run closes the state.
+func Run(t testing.TB, e *script.Engine, s *script.State, filename string, testScript io.Reader) {
+ t.Helper()
+ err := func() (err error) {
+ log := new(strings.Builder)
+ log.WriteString("\n") // Start output on a new line for consistent indentation.
+
+ // Defer writing to the test log in case the script engine panics during execution,
+ // but write the log before we write the final "skip" or "FAIL" line.
+ t.Helper()
+ defer func() {
+ t.Helper()
+
+ if closeErr := s.CloseAndWait(log); err == nil {
+ err = closeErr
+ }
+
+ if log.Len() > 0 {
+ t.Log(strings.TrimSuffix(log.String(), "\n"))
+ }
+ }()
+
+ if testing.Verbose() {
+ // Add the environment to the start of the script log.
+ wait, err := script.Env().Run(s)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if wait != nil {
+ stdout, stderr, err := wait(s)
+ if err != nil {
+ t.Fatalf("env: %v\n%s", err, stderr)
+ }
+ if len(stdout) > 0 {
+ s.Logf("%s\n", stdout)
+ }
+ }
+ }
+
+ return e.Execute(s, filename, bufio.NewReader(testScript), log)
+ }()
+
+ if skip := (skipError{}); errors.As(err, &skip) {
+ if skip.msg == "" {
+ t.Skip("SKIP")
+ } else {
+ t.Skipf("SKIP: %v", skip.msg)
+ }
+ }
+ if err != nil {
+ t.Errorf("FAIL: %v", err)
+ }
+}
+
+// Skip returns a sentinel error that causes Run to mark the test as skipped.
+func Skip() script.Cmd {
+ return script.Command(
+ script.CmdUsage{
+ Summary: "skip the current test",
+ Args: "[msg]",
+ },
+ func(_ *script.State, args ...string) (script.WaitFunc, error) {
+ if len(args) > 1 {
+ return nil, script.ErrUsage
+ }
+ if len(args) == 0 {
+ return nil, skipError{""}
+ }
+ return nil, skipError{args[0]}
+ })
+}
+
+type skipError struct {
+ msg string
+}
+
+func (s skipError) Error() string {
+ if s.msg == "" {
+ return "skip"
+ }
+ return s.msg
+}
+
+// CachedExec returns a Condition that reports whether the PATH of the test
+// binary itself (not the script's current environment) contains the named
+// executable.
+func CachedExec() script.Cond {
+ return script.CachedCondition(
+ "<suffix> names an executable in the test binary's PATH",
+ func(name string) (bool, error) {
+ _, err := cfg.LookPath(name)
+ return err == nil, nil
+ })
+}