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
141 env = setenv(env, "GOOS", runtime.GOOS)
142 env = setenv(env, "GOARCH", runtime.GOARCH)
143 for _, repl := range repls {
144
145 chunks := strings.Split(repl.EnvVar, "=")
146 if len(chunks) != 2 {
147 t.Fatalf("malformed env var setting: %s", repl.EnvVar)
148 }
149 env = append(env, repl.EnvVar)
150 }
151
152
153 engine := &script.Engine{
154 Conds: conds,
155 Cmds: cmds,
156 Quiet: !testing.Verbose(),
157 }
158
159 t.Run("README", func(t *testing.T) {
160 checkScriptReadme(t, engine, env, scriptsdir, gotool, fixReadme)
161 })
162
163
164 ctx := context.Background()
165 pattern := filepath.Join(scriptsdir, "*.txt")
166 RunTests(t, ctx, engine, env, pattern)
167 }
168
169
170
171
172
173 func RunTests(t *testing.T, ctx context.Context, engine *script.Engine, env []string, pattern string) {
174 gracePeriod := 100 * time.Millisecond
175 if deadline, ok := t.Deadline(); ok {
176 timeout := time.Until(deadline)
177
178
179
180 if gp := timeout / 20; gp > gracePeriod {
181 gracePeriod = gp
182 }
183
184
185
186
187
188
189
190
191
192
193 timeout -= 2 * gracePeriod
194
195 var cancel context.CancelFunc
196 ctx, cancel = context.WithTimeout(ctx, timeout)
197 t.Cleanup(cancel)
198 }
199
200 files, _ := filepath.Glob(pattern)
201 if len(files) == 0 {
202 t.Fatal("no testdata")
203 }
204 for _, file := range files {
205 file := file
206 name := strings.TrimSuffix(filepath.Base(file), ".txt")
207 t.Run(name, func(t *testing.T) {
208 t.Parallel()
209
210 workdir := t.TempDir()
211 s, err := script.NewState(ctx, workdir, env)
212 if err != nil {
213 t.Fatal(err)
214 }
215
216
217 a, err := txtar.ParseFile(file)
218 if err != nil {
219 t.Fatal(err)
220 }
221 initScriptDirs(t, s)
222 if err := s.ExtractFiles(a); err != nil {
223 t.Fatal(err)
224 }
225
226 t.Log(time.Now().UTC().Format(time.RFC3339))
227 work, _ := s.LookupEnv("WORK")
228 t.Logf("$WORK=%s", work)
229
230
231
232
233
234
235 Run(t, engine, s, file, bytes.NewReader(a.Comment))
236 })
237 }
238 }
239
240 func initScriptDirs(t testing.TB, s *script.State) {
241 must := func(err error) {
242 if err != nil {
243 t.Helper()
244 t.Fatal(err)
245 }
246 }
247
248 work := s.Getwd()
249 must(s.Setenv("WORK", work))
250 must(os.MkdirAll(filepath.Join(work, "tmp"), 0777))
251 must(s.Setenv(tempEnvName(), filepath.Join(work, "tmp")))
252 }
253
254 func tempEnvName() string {
255 switch runtime.GOOS {
256 case "windows":
257 return "TMP"
258 case "plan9":
259 return "TMPDIR"
260 default:
261 return "TMPDIR"
262 }
263 }
264
265
266 func scriptCC(cmdExec script.Cmd, ccexe string) script.Cmd {
267 return script.Command(
268 script.CmdUsage{
269 Summary: "run the platform C compiler",
270 Args: "args...",
271 },
272 func(s *script.State, args ...string) (script.WaitFunc, error) {
273 return cmdExec.Run(s, append([]string{ccexe}, args...)...)
274 })
275 }
276
View as plain text