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