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