1
2
3
4
5
6 package scripttest
7
8 import (
9 "bytes"
10 "cmd/internal/script"
11 "context"
12 "fmt"
13 "internal/testenv"
14 "internal/txtar"
15 "os"
16 "os/exec"
17 "path/filepath"
18 "runtime"
19 "strings"
20 "testing"
21 "time"
22 )
23
24
25
26 type ToolReplacement struct {
27 ToolName string
28 ReplacementPath string
29 EnvVar string
30 }
31
32
33
34
35
36
37 func RunToolScriptTest(t *testing.T, repls []ToolReplacement, scriptsdir string, fixReadme bool) {
38
39
40 testenv.MustHaveGoBuild(t)
41
42
43
44 if runtime.GOOS == "plan9" {
45 t.Skipf("no symlinks on plan9")
46 }
47
48
49 gotool, err := testenv.GoTool()
50 if err != nil {
51 t.Fatalf("locating go tool: %v", err)
52 }
53
54 goEnv := func(name string) string {
55 out, err := exec.Command(gotool, "env", name).CombinedOutput()
56 if err != nil {
57 t.Fatalf("go env %s: %v\n%s", name, err, out)
58 }
59 return strings.TrimSpace(string(out))
60 }
61
62
63
64 cmds := DefaultCmds()
65 conds := DefaultConds()
66
67 addcmd := func(name string, cmd script.Cmd) {
68 if _, ok := cmds[name]; ok {
69 panic(fmt.Sprintf("command %q is already registered", name))
70 }
71 cmds[name] = cmd
72 }
73
74 prependToPath := func(env []string, dir string) {
75 found := false
76 for k := range env {
77 ev := env[k]
78 if !strings.HasPrefix(ev, "PATH=") {
79 continue
80 }
81 oldpath := ev[5:]
82 env[k] = "PATH=" + dir + string(filepath.ListSeparator) + oldpath
83 found = true
84 break
85 }
86 if !found {
87 t.Fatalf("could not update PATH")
88 }
89 }
90
91 setenv := func(env []string, varname, val string) []string {
92 pref := varname + "="
93 found := false
94 for k := range env {
95 if !strings.HasPrefix(env[k], pref) {
96 continue
97 }
98 env[k] = pref + val
99 found = true
100 break
101 }
102 if !found {
103 env = append(env, varname+"="+val)
104 }
105 return env
106 }
107
108 interrupt := func(cmd *exec.Cmd) error {
109 return cmd.Process.Signal(os.Interrupt)
110 }
111 gracePeriod := 60 * time.Second
112
113
114
115
116 goroot := goEnv("GOROOT")
117 tmpdir := t.TempDir()
118 tgr := SetupTestGoRoot(t, tmpdir, goroot)
119
120
121 for _, repl := range repls {
122 ReplaceGoToolInTestGoRoot(t, tgr, repl.ToolName, repl.ReplacementPath)
123 }
124
125
126 testgo := filepath.Join(tgr, "bin", "go")
127 gocmd := script.Program(testgo, interrupt, gracePeriod)
128 addcmd("go", gocmd)
129 cmdExec := cmds["exec"]
130 addcmd("cc", scriptCC(cmdExec, goEnv("CC")))
131
132
133 goHostOS, goHostArch := goEnv("GOHOSTOS"), goEnv("GOHOSTARCH")
134 AddToolChainScriptConditions(t, conds, goHostOS, goHostArch)
135
136
137 env := os.Environ()
138 prependToPath(env, filepath.Join(tgr, "bin"))
139 env = setenv(env, "GOROOT", tgr)
140 for _, repl := range repls {
141
142 chunks := strings.Split(repl.EnvVar, "=")
143 if len(chunks) != 2 {
144 t.Fatalf("malformed env var setting: %s", repl.EnvVar)
145 }
146 env = append(env, repl.EnvVar)
147 }
148
149
150 engine := &script.Engine{
151 Conds: conds,
152 Cmds: cmds,
153 Quiet: !testing.Verbose(),
154 }
155
156 t.Run("README", func(t *testing.T) {
157 checkScriptReadme(t, engine, env, scriptsdir, gotool, fixReadme)
158 })
159
160
161 ctx := context.Background()
162 pattern := filepath.Join(scriptsdir, "*.txt")
163 RunTests(t, ctx, engine, env, pattern)
164 }
165
166
167
168
169
170 func RunTests(t *testing.T, ctx context.Context, engine *script.Engine, env []string, pattern string) {
171 gracePeriod := 100 * time.Millisecond
172 if deadline, ok := t.Deadline(); ok {
173 timeout := time.Until(deadline)
174
175
176
177 if gp := timeout / 20; gp > gracePeriod {
178 gracePeriod = gp
179 }
180
181
182
183
184
185
186
187
188
189
190 timeout -= 2 * gracePeriod
191
192 var cancel context.CancelFunc
193 ctx, cancel = context.WithTimeout(ctx, timeout)
194 t.Cleanup(cancel)
195 }
196
197 files, _ := filepath.Glob(pattern)
198 if len(files) == 0 {
199 t.Fatal("no testdata")
200 }
201 for _, file := range files {
202 file := file
203 name := strings.TrimSuffix(filepath.Base(file), ".txt")
204 t.Run(name, func(t *testing.T) {
205 t.Parallel()
206
207 workdir := t.TempDir()
208 s, err := script.NewState(ctx, workdir, env)
209 if err != nil {
210 t.Fatal(err)
211 }
212
213
214 a, err := txtar.ParseFile(file)
215 if err != nil {
216 t.Fatal(err)
217 }
218 initScriptDirs(t, s)
219 if err := s.ExtractFiles(a); err != nil {
220 t.Fatal(err)
221 }
222
223 t.Log(time.Now().UTC().Format(time.RFC3339))
224 work, _ := s.LookupEnv("WORK")
225 t.Logf("$WORK=%s", work)
226
227
228
229
230
231
232 Run(t, engine, s, file, bytes.NewReader(a.Comment))
233 })
234 }
235 }
236
237 func initScriptDirs(t testing.TB, s *script.State) {
238 must := func(err error) {
239 if err != nil {
240 t.Helper()
241 t.Fatal(err)
242 }
243 }
244
245 work := s.Getwd()
246 must(s.Setenv("WORK", work))
247 must(os.MkdirAll(filepath.Join(work, "tmp"), 0777))
248 must(s.Setenv(tempEnvName(), filepath.Join(work, "tmp")))
249 }
250
251 func tempEnvName() string {
252 switch runtime.GOOS {
253 case "windows":
254 return "TMP"
255 case "plan9":
256 return "TMPDIR"
257 default:
258 return "TMPDIR"
259 }
260 }
261
262
263 func scriptCC(cmdExec script.Cmd, ccexe string) script.Cmd {
264 return script.Command(
265 script.CmdUsage{
266 Summary: "run the platform C compiler",
267 Args: "args...",
268 },
269 func(s *script.State, args ...string) (script.WaitFunc, error) {
270 return cmdExec.Run(s, append([]string{ccexe}, args...)...)
271 })
272 }
273
View as plain text