Source file
src/cmd/go/script_test.go
1
2
3
4
5
6
7
8
9
10 package main_test
11
12 import (
13 "bufio"
14 "bytes"
15 "context"
16 _ "embed"
17 "flag"
18 "internal/testenv"
19 "internal/txtar"
20 "net/url"
21 "os"
22 "path/filepath"
23 "runtime"
24 "strings"
25 "testing"
26 "time"
27
28 "cmd/go/internal/cfg"
29 "cmd/go/internal/gover"
30 "cmd/go/internal/vcweb/vcstest"
31 "cmd/internal/script"
32 "cmd/internal/script/scripttest"
33
34 "golang.org/x/telemetry/counter/countertest"
35 )
36
37 var testSum = flag.String("testsum", "", `may be tidy, listm, or listall. If set, TestScript generates a go.sum file at the beginning of each test and updates test files if they pass.`)
38
39
40 func TestScript(t *testing.T) {
41 t.Parallel()
42
43 testenv.MustHaveGoBuild(t)
44 testenv.SkipIfShortAndSlow(t)
45
46 if testing.Short() && runtime.GOOS == "plan9" {
47 t.Skipf("skipping test in -short mode on %s", runtime.GOOS)
48 }
49
50 srv, err := vcstest.NewServer()
51 if err != nil {
52 t.Fatal(err)
53 }
54 t.Cleanup(func() {
55 if err := srv.Close(); err != nil {
56 t.Fatal(err)
57 }
58 })
59 certFile, err := srv.WriteCertificateFile()
60 if err != nil {
61 t.Fatal(err)
62 }
63
64 StartProxy()
65
66 var (
67 ctx = context.Background()
68 gracePeriod = 100 * time.Millisecond
69 )
70 if deadline, ok := t.Deadline(); ok {
71 timeout := time.Until(deadline)
72
73
74
75 if gp := timeout / 20; gp > gracePeriod {
76 gracePeriod = gp
77 }
78
79
80
81
82
83
84
85
86 timeout -= 2 * gracePeriod
87
88 var cancel context.CancelFunc
89 ctx, cancel = context.WithTimeout(ctx, timeout)
90 t.Cleanup(cancel)
91 }
92
93 env, err := scriptEnv(srv, certFile)
94 if err != nil {
95 t.Fatal(err)
96 }
97 engine := &script.Engine{
98 Conds: scriptConditions(t),
99 Cmds: scriptCommands(quitSignal(), gracePeriod),
100 Quiet: !testing.Verbose(),
101 }
102
103 t.Run("README", func(t *testing.T) {
104 t.Parallel()
105 checkScriptReadme(t, engine, env)
106 })
107
108 files, err := filepath.Glob("testdata/script/*.txt")
109 if err != nil {
110 t.Fatal(err)
111 }
112 for _, file := range files {
113 name := strings.TrimSuffix(filepath.Base(file), ".txt")
114 t.Run(name, func(t *testing.T) {
115 t.Parallel()
116 StartProxy()
117
118 workdir, err := os.MkdirTemp(testTmpDir, name)
119 if err != nil {
120 t.Fatal(err)
121 }
122 if !*testWork {
123 defer removeAll(workdir)
124 }
125
126 s, err := script.NewState(tbContext(ctx, t), workdir, env)
127 if err != nil {
128 t.Fatal(err)
129 }
130
131
132 a, err := txtar.ParseFile(file)
133 if err != nil {
134 t.Fatal(err)
135 }
136 telemetryDir := initScriptDirs(t, s)
137 if err := s.ExtractFiles(a); err != nil {
138 t.Fatal(err)
139 }
140
141 t.Log(time.Now().UTC().Format(time.RFC3339))
142 work, _ := s.LookupEnv("WORK")
143 t.Logf("$WORK=%s", work)
144
145
146
147 if *testSum != "" {
148 if updateSum(t, engine, s, a) {
149 defer func() {
150 if t.Failed() {
151 return
152 }
153 data := txtar.Format(a)
154 if err := os.WriteFile(file, data, 0666); err != nil {
155 t.Errorf("rewriting test file: %v", err)
156 }
157 }()
158 }
159 }
160
161
162
163
164
165 scripttest.Run(t, engine, s, file, bytes.NewReader(a.Comment))
166 checkCounters(t, telemetryDir)
167 })
168 }
169 }
170
171
172 type testingTBKey struct{}
173
174
175 func tbContext(ctx context.Context, t testing.TB) context.Context {
176 return context.WithValue(ctx, testingTBKey{}, t)
177 }
178
179
180 func tbFromContext(ctx context.Context) (testing.TB, bool) {
181 t := ctx.Value(testingTBKey{})
182 if t == nil {
183 return nil, false
184 }
185 return t.(testing.TB), true
186 }
187
188
189
190 func initScriptDirs(t testing.TB, s *script.State) (telemetryDir string) {
191 must := func(err error) {
192 if err != nil {
193 t.Helper()
194 t.Fatal(err)
195 }
196 }
197
198 work := s.Getwd()
199 must(s.Setenv("WORK", work))
200
201 telemetryDir = filepath.Join(work, "telemetry")
202 must(os.MkdirAll(telemetryDir, 0777))
203 must(s.Setenv("TEST_TELEMETRY_DIR", filepath.Join(work, "telemetry")))
204
205 must(os.MkdirAll(filepath.Join(work, "tmp"), 0777))
206 must(s.Setenv(tempEnvName(), filepath.Join(work, "tmp")))
207
208 gopath := filepath.Join(work, "gopath")
209 must(s.Setenv("GOPATH", gopath))
210 gopathSrc := filepath.Join(gopath, "src")
211 must(os.MkdirAll(gopathSrc, 0777))
212 must(s.Chdir(gopathSrc))
213 return telemetryDir
214 }
215
216 func scriptEnv(srv *vcstest.Server, srvCertFile string) ([]string, error) {
217 httpURL, err := url.Parse(srv.HTTP.URL)
218 if err != nil {
219 return nil, err
220 }
221 httpsURL, err := url.Parse(srv.HTTPS.URL)
222 if err != nil {
223 return nil, err
224 }
225 env := []string{
226 pathEnvName() + "=" + testBin + string(filepath.ListSeparator) + os.Getenv(pathEnvName()),
227 homeEnvName() + "=/no-home",
228 "CCACHE_DISABLE=1",
229 "GOARCH=" + runtime.GOARCH,
230 "TESTGO_GOHOSTARCH=" + goHostArch,
231 "GOCACHE=" + testGOCACHE,
232 "GOCOVERDIR=" + os.Getenv("GOCOVERDIR"),
233 "GODEBUG=" + os.Getenv("GODEBUG"),
234 "GOEXE=" + cfg.ExeSuffix,
235 "GOEXPERIMENT=" + os.Getenv("GOEXPERIMENT"),
236 "GOOS=" + runtime.GOOS,
237 "TESTGO_GOHOSTOS=" + goHostOS,
238 "GOPROXY=" + proxyURL,
239 "GOPRIVATE=",
240 "GOROOT=" + testGOROOT,
241 "GOTRACEBACK=system",
242 "TESTGONETWORK=panic",
243 "TESTGO_GOROOT=" + testGOROOT,
244 "TESTGO_EXE=" + testGo,
245 "TESTGO_VCSTEST_HOST=" + httpURL.Host,
246 "TESTGO_VCSTEST_TLS_HOST=" + httpsURL.Host,
247 "TESTGO_VCSTEST_CERT=" + srvCertFile,
248 "TESTGONETWORK=panic",
249 "GOSUMDB=" + testSumDBVerifierKey,
250 "TESTGO_SUMDB=" + testSumDBName,
251 "GONOPROXY=",
252 "GONOSUMDB=",
253 "GOVCS=*:all",
254 "devnull=" + os.DevNull,
255 "goversion=" + gover.Local(),
256 "CMDGO_TEST_RUN_MAIN=true",
257 "HGRCPATH=",
258 "GOTOOLCHAIN=auto",
259 "newline=\n",
260 }
261
262 if testenv.Builder() != "" || os.Getenv("GIT_TRACE_CURL") == "1" {
263
264
265 env = append(env,
266 "GIT_TRACE_CURL=1",
267 "GIT_TRACE_CURL_NO_DATA=1",
268 "GIT_REDACT_COOKIES=o,SSO,GSSO_Uberproxy")
269 }
270 if testing.Short() {
271
272
273
274
275 env = append(env, "TESTGOVCSREMOTE=panic")
276 }
277 if os.Getenv("CGO_ENABLED") != "" || runtime.GOOS != goHostOS || runtime.GOARCH != goHostArch {
278
279
280
281 env = append(env, "CGO_ENABLED="+cgoEnabled)
282 }
283
284 for _, key := range extraEnvKeys {
285 if val, ok := os.LookupEnv(key); ok {
286 env = append(env, key+"="+val)
287 }
288 }
289
290 return env, nil
291 }
292
293 var extraEnvKeys = []string{
294 "SYSTEMROOT",
295 "WINDIR",
296 "LD_LIBRARY_PATH",
297 "LIBRARY_PATH",
298 "C_INCLUDE_PATH",
299 "CC",
300 "GO_TESTING_GOTOOLS",
301 "GCCGO",
302 "GCCGOTOOLDIR",
303 }
304
305
306
307
308
309 func updateSum(t testing.TB, e *script.Engine, s *script.State, archive *txtar.Archive) (rewrite bool) {
310 gomodIdx, gosumIdx := -1, -1
311 for i := range archive.Files {
312 switch archive.Files[i].Name {
313 case "go.mod":
314 gomodIdx = i
315 case "go.sum":
316 gosumIdx = i
317 }
318 }
319 if gomodIdx < 0 {
320 return false
321 }
322
323 var cmd string
324 switch *testSum {
325 case "tidy":
326 cmd = "go mod tidy"
327 case "listm":
328 cmd = "go list -m -mod=mod all"
329 case "listall":
330 cmd = "go list -mod=mod all"
331 default:
332 t.Fatalf(`unknown value for -testsum %q; may be "tidy", "listm", or "listall"`, *testSum)
333 }
334
335 log := new(strings.Builder)
336 err := e.Execute(s, "updateSum", bufio.NewReader(strings.NewReader(cmd)), log)
337 if log.Len() > 0 {
338 t.Logf("%s", log)
339 }
340 if err != nil {
341 t.Fatal(err)
342 }
343
344 newGomodData, err := os.ReadFile(s.Path("go.mod"))
345 if err != nil {
346 t.Fatalf("reading go.mod after -testsum: %v", err)
347 }
348 if !bytes.Equal(newGomodData, archive.Files[gomodIdx].Data) {
349 archive.Files[gomodIdx].Data = newGomodData
350 rewrite = true
351 }
352
353 newGosumData, err := os.ReadFile(s.Path("go.sum"))
354 if err != nil && !os.IsNotExist(err) {
355 t.Fatalf("reading go.sum after -testsum: %v", err)
356 }
357 switch {
358 case os.IsNotExist(err) && gosumIdx >= 0:
359
360 rewrite = true
361 archive.Files = append(archive.Files[:gosumIdx], archive.Files[gosumIdx+1:]...)
362 case err == nil && gosumIdx < 0:
363
364 rewrite = true
365 gosumIdx = gomodIdx + 1
366 archive.Files = append(archive.Files, txtar.File{})
367 copy(archive.Files[gosumIdx+1:], archive.Files[gosumIdx:])
368 archive.Files[gosumIdx] = txtar.File{Name: "go.sum", Data: newGosumData}
369 case err == nil && gosumIdx >= 0 && !bytes.Equal(newGosumData, archive.Files[gosumIdx].Data):
370
371 rewrite = true
372 archive.Files[gosumIdx].Data = newGosumData
373 }
374 return rewrite
375 }
376
377 func readCounters(t *testing.T, telemetryDir string) map[string]uint64 {
378 localDir := filepath.Join(telemetryDir, "local")
379 dirents, err := os.ReadDir(localDir)
380 if err != nil {
381 if os.IsNotExist(err) {
382 return nil
383 }
384 t.Fatalf("reading telemetry local dir: %v", err)
385 }
386 totals := map[string]uint64{}
387 for _, dirent := range dirents {
388 if dirent.IsDir() || !strings.HasSuffix(dirent.Name(), ".count") {
389
390 continue
391 }
392 counters, _, err := countertest.ReadFile(filepath.Join(localDir, dirent.Name()))
393 if err != nil {
394 t.Fatalf("reading counter file: %v", err)
395 }
396 for k, v := range counters {
397 totals[k] += v
398 }
399 }
400
401 return totals
402 }
403
404 func checkCounters(t *testing.T, telemetryDir string) {
405 counters := readCounters(t, telemetryDir)
406 if _, ok := scriptGoInvoked.Load(testing.TB(t)); ok {
407 if !disabledOnPlatform && len(counters) == 0 {
408 t.Fatal("go was invoked but no counters were incremented")
409 }
410 }
411 }
412
413
414
415
416
417
418 const disabledOnPlatform = false ||
419
420 runtime.GOOS == "openbsd" ||
421 runtime.GOOS == "solaris" ||
422 runtime.GOOS == "android" ||
423 runtime.GOOS == "illumos" ||
424
425 runtime.GOOS == "js" ||
426 runtime.GOOS == "wasip1" ||
427 runtime.GOOS == "plan9"
428
View as plain text