github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/go/script/scripttest/scripttest.go (about) 1 // Copyright 2022 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 scripttest adapts the script engine for use in tests. 6 package scripttest 7 8 import ( 9 "bufio" 10 "errors" 11 "io" 12 "strings" 13 "testing" 14 15 "github.com/go-asm/go/cmd/go/cfg" 16 "github.com/go-asm/go/cmd/go/script" 17 ) 18 19 // DefaultCmds returns a set of broadly useful script commands. 20 // 21 // This set includes all of the commands in script.DefaultCmds, 22 // as well as a "skip" command that halts the script and causes the 23 // testing.TB passed to Run to be skipped. 24 func DefaultCmds() map[string]script.Cmd { 25 cmds := script.DefaultCmds() 26 cmds["skip"] = Skip() 27 return cmds 28 } 29 30 // DefaultConds returns a set of broadly useful script conditions. 31 // 32 // This set includes all of the conditions in script.DefaultConds, 33 // as well as: 34 // 35 // - Conditions of the form "exec:foo" are active when the executable "foo" is 36 // found in the test process's PATH, and inactive when the executable is 37 // not found. 38 // 39 // - "short" is active when testing.Short() is true. 40 // 41 // - "verbose" is active when testing.Verbose() is true. 42 func DefaultConds() map[string]script.Cond { 43 conds := script.DefaultConds() 44 conds["exec"] = CachedExec() 45 conds["short"] = script.BoolCondition("testing.Short()", testing.Short()) 46 conds["verbose"] = script.BoolCondition("testing.Verbose()", testing.Verbose()) 47 return conds 48 } 49 50 // Run runs the script from the given filename starting at the given initial state. 51 // When the script completes, Run closes the state. 52 func Run(t testing.TB, e *script.Engine, s *script.State, filename string, testScript io.Reader) { 53 t.Helper() 54 err := func() (err error) { 55 log := new(strings.Builder) 56 log.WriteString("\n") // Start output on a new line for consistent indentation. 57 58 // Defer writing to the test log in case the script engine panics during execution, 59 // but write the log before we write the final "skip" or "FAIL" line. 60 t.Helper() 61 defer func() { 62 t.Helper() 63 64 if closeErr := s.CloseAndWait(log); err == nil { 65 err = closeErr 66 } 67 68 if log.Len() > 0 { 69 t.Log(strings.TrimSuffix(log.String(), "\n")) 70 } 71 }() 72 73 if testing.Verbose() { 74 // Add the environment to the start of the script log. 75 wait, err := script.Env().Run(s) 76 if err != nil { 77 t.Fatal(err) 78 } 79 if wait != nil { 80 stdout, stderr, err := wait(s) 81 if err != nil { 82 t.Fatalf("env: %v\n%s", err, stderr) 83 } 84 if len(stdout) > 0 { 85 s.Logf("%s\n", stdout) 86 } 87 } 88 } 89 90 return e.Execute(s, filename, bufio.NewReader(testScript), log) 91 }() 92 93 if skip := (skipError{}); errors.As(err, &skip) { 94 if skip.msg == "" { 95 t.Skip("SKIP") 96 } else { 97 t.Skipf("SKIP: %v", skip.msg) 98 } 99 } 100 if err != nil { 101 t.Errorf("FAIL: %v", err) 102 } 103 } 104 105 // Skip returns a sentinel error that causes Run to mark the test as skipped. 106 func Skip() script.Cmd { 107 return script.Command( 108 script.CmdUsage{ 109 Summary: "skip the current test", 110 Args: "[msg]", 111 }, 112 func(_ *script.State, args ...string) (script.WaitFunc, error) { 113 if len(args) > 1 { 114 return nil, script.ErrUsage 115 } 116 if len(args) == 0 { 117 return nil, skipError{""} 118 } 119 return nil, skipError{args[0]} 120 }) 121 } 122 123 type skipError struct { 124 msg string 125 } 126 127 func (s skipError) Error() string { 128 if s.msg == "" { 129 return "skip" 130 } 131 return s.msg 132 } 133 134 // CachedExec returns a Condition that reports whether the PATH of the test 135 // binary itself (not the script's current environment) contains the named 136 // executable. 137 func CachedExec() script.Cond { 138 return script.CachedCondition( 139 "<suffix> names an executable in the test binary's PATH", 140 func(name string) (bool, error) { 141 _, err := cfg.LookPath(name) 142 return err == nil, nil 143 }) 144 }