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