Source file
src/cmd/go/scriptreadme_test.go
1
2
3
4
5 package main_test
6
7 import (
8 "bytes"
9 "cmd/go/internal/script"
10 "flag"
11 "internal/diff"
12 "internal/testenv"
13 "os"
14 "strings"
15 "testing"
16 "text/template"
17 )
18
19 var fixReadme = flag.Bool("fixreadme", false, "if true, update ../testdata/script/README")
20
21 func checkScriptReadme(t *testing.T, engine *script.Engine, env []string) {
22 var args struct {
23 Language string
24 Commands string
25 Conditions string
26 }
27
28 cmds := new(strings.Builder)
29 if err := engine.ListCmds(cmds, true); err != nil {
30 t.Fatal(err)
31 }
32 args.Commands = cmds.String()
33
34 conds := new(strings.Builder)
35 if err := engine.ListConds(conds, nil); err != nil {
36 t.Fatal(err)
37 }
38 args.Conditions = conds.String()
39
40 doc := new(strings.Builder)
41 cmd := testenv.Command(t, testGo, "doc", "cmd/go/internal/script")
42 cmd.Env = env
43 cmd.Stdout = doc
44 if err := cmd.Run(); err != nil {
45 t.Fatal(cmd, ":", err)
46 }
47 _, lang, ok := strings.Cut(doc.String(), "# Script Language\n\n")
48 if !ok {
49 t.Fatalf("%q did not include Script Language section", cmd)
50 }
51 lang, _, ok = strings.Cut(lang, "\n\nvar ")
52 if !ok {
53 t.Fatalf("%q did not include vars after Script Language section", cmd)
54 }
55 args.Language = lang
56
57 tmpl := template.Must(template.New("README").Parse(readmeTmpl[1:]))
58 buf := new(bytes.Buffer)
59 if err := tmpl.Execute(buf, args); err != nil {
60 t.Fatal(err)
61 }
62
63 const readmePath = "testdata/script/README"
64 old, err := os.ReadFile(readmePath)
65 if err != nil {
66 t.Fatal(err)
67 }
68 diff := diff.Diff(readmePath, old, "readmeTmpl", buf.Bytes())
69 if diff == nil {
70 t.Logf("%s is up to date.", readmePath)
71 return
72 }
73
74 if *fixReadme {
75 if err := os.WriteFile(readmePath, buf.Bytes(), 0666); err != nil {
76 t.Fatal(err)
77 }
78 t.Logf("wrote %d bytes to %s", buf.Len(), readmePath)
79 } else {
80 t.Logf("\n%s", diff)
81 t.Errorf("%s is stale. To update, run 'go generate cmd/go'.", readmePath)
82 }
83 }
84
85 const readmeTmpl = `
86 This file is generated by 'go generate cmd/go'. DO NOT EDIT.
87
88 This directory holds test scripts *.txt run during 'go test cmd/go'.
89 To run a specific script foo.txt
90
91 go test cmd/go -run=Script/^foo$
92
93 In general script files should have short names: a few words, not whole sentences.
94 The first word should be the general category of behavior being tested,
95 often the name of a go subcommand (list, build, test, ...) or concept (vendor, pattern).
96
97 Each script is a text archive (go doc internal/txtar).
98 The script begins with an actual command script to run
99 followed by the content of zero or more supporting files to
100 create in the script's temporary file system before it starts executing.
101
102 As an example, run_hello.txt says:
103
104 # hello world
105 go run hello.go
106 stderr 'hello world'
107 ! stdout .
108
109 -- hello.go --
110 package main
111 func main() { println("hello world") }
112
113 Each script runs in a fresh temporary work directory tree, available to scripts as $WORK.
114 Scripts also have access to other environment variables, including:
115
116 GOARCH=<target GOARCH>
117 GOCACHE=<actual GOCACHE being used outside the test>
118 GOEXE=<executable file suffix: .exe on Windows, empty on other systems>
119 GOOS=<target GOOS>
120 GOPATH=$WORK/gopath
121 GOPROXY=<local module proxy serving from cmd/go/testdata/mod>
122 GOROOT=<actual GOROOT>
123 TESTGO_GOROOT=<GOROOT used to build cmd/go, for use in tests that may change GOROOT>
124 HOME=/no-home
125 PATH=<actual PATH>
126 TMPDIR=$WORK/tmp
127 GODEBUG=<actual GODEBUG>
128 devnull=<value of os.DevNull>
129 goversion=<current Go version; for example, 1.12>
130
131 On Plan 9, the variables $path and $home are set instead of $PATH and $HOME.
132 On Windows, the variables $USERPROFILE and $TMP are set instead of
133 $HOME and $TMPDIR.
134
135 The lines at the top of the script are a sequence of commands to be executed by
136 a small script engine configured in ../../script_test.go (not the system shell).
137
138 The scripts' supporting files are unpacked relative to $GOPATH/src
139 (aka $WORK/gopath/src) and then the script begins execution in that directory as
140 well. Thus the example above runs in $WORK/gopath/src with GOPATH=$WORK/gopath
141 and $WORK/gopath/src/hello.go containing the listed contents.
142
143 {{.Language}}
144
145 When TestScript runs a script and the script fails, by default TestScript shows
146 the execution of the most recent phase of the script (since the last # comment)
147 and only shows the # comments for earlier phases. For example, here is a
148 multi-phase script with a bug in it:
149
150 # GOPATH with p1 in d2, p2 in d2
151 env GOPATH=$WORK${/}d1${:}$WORK${/}d2
152
153 # build & install p1
154 env
155 go install -i p1
156 ! stale p1
157 ! stale p2
158
159 # modify p2 - p1 should appear stale
160 cp $WORK/p2x.go $WORK/d2/src/p2/p2.go
161 stale p1 p2
162
163 # build & install p1 again
164 go install -i p11
165 ! stale p1
166 ! stale p2
167
168 -- $WORK/d1/src/p1/p1.go --
169 package p1
170 import "p2"
171 func F() { p2.F() }
172 -- $WORK/d2/src/p2/p2.go --
173 package p2
174 func F() {}
175 -- $WORK/p2x.go --
176 package p2
177 func F() {}
178 func G() {}
179
180 The bug is that the final phase installs p11 instead of p1. The test failure looks like:
181
182 $ go test -run=Script
183 --- FAIL: TestScript (3.75s)
184 --- FAIL: TestScript/install_rebuild_gopath (0.16s)
185 script_test.go:223:
186 # GOPATH with p1 in d2, p2 in d2 (0.000s)
187 # build & install p1 (0.087s)
188 # modify p2 - p1 should appear stale (0.029s)
189 # build & install p1 again (0.022s)
190 > go install -i p11
191 [stderr]
192 can't load package: package p11: cannot find package "p11" in any of:
193 /Users/rsc/go/src/p11 (from $GOROOT)
194 $WORK/d1/src/p11 (from $GOPATH)
195 $WORK/d2/src/p11
196 [exit status 1]
197 FAIL: unexpected go command failure
198
199 script_test.go:73: failed at testdata/script/install_rebuild_gopath.txt:15 in $WORK/gopath/src
200
201 FAIL
202 exit status 1
203 FAIL cmd/go 4.875s
204 $
205
206 Note that the commands in earlier phases have been hidden, so that the relevant
207 commands are more easily found, and the elapsed time for a completed phase
208 is shown next to the phase heading. To see the entire execution, use "go test -v",
209 which also adds an initial environment dump to the beginning of the log.
210
211 Note also that in reported output, the actual name of the per-script temporary directory
212 has been consistently replaced with the literal string $WORK.
213
214 The cmd/go test flag -testwork (which must appear on the "go test" command line after
215 standard test flags) causes each test to log the name of its $WORK directory and other
216 environment variable settings and also to leave that directory behind when it exits,
217 for manual debugging of failing tests:
218
219 $ go test -run=Script -work
220 --- FAIL: TestScript (3.75s)
221 --- FAIL: TestScript/install_rebuild_gopath (0.16s)
222 script_test.go:223:
223 WORK=/tmp/cmd-go-test-745953508/script-install_rebuild_gopath
224 GOARCH=
225 GOCACHE=/Users/rsc/Library/Caches/go-build
226 GOOS=
227 GOPATH=$WORK/gopath
228 GOROOT=/Users/rsc/go
229 HOME=/no-home
230 TMPDIR=$WORK/tmp
231 exe=
232
233 # GOPATH with p1 in d2, p2 in d2 (0.000s)
234 # build & install p1 (0.085s)
235 # modify p2 - p1 should appear stale (0.030s)
236 # build & install p1 again (0.019s)
237 > go install -i p11
238 [stderr]
239 can't load package: package p11: cannot find package "p11" in any of:
240 /Users/rsc/go/src/p11 (from $GOROOT)
241 $WORK/d1/src/p11 (from $GOPATH)
242 $WORK/d2/src/p11
243 [exit status 1]
244 FAIL: unexpected go command failure
245
246 script_test.go:73: failed at testdata/script/install_rebuild_gopath.txt:15 in $WORK/gopath/src
247
248 FAIL
249 exit status 1
250 FAIL cmd/go 4.875s
251 $
252
253 $ WORK=/tmp/cmd-go-test-745953508/script-install_rebuild_gopath
254 $ cd $WORK/d1/src/p1
255 $ cat p1.go
256 package p1
257 import "p2"
258 func F() { p2.F() }
259 $
260
261 The available commands are:
262 {{.Commands}}
263
264 The available conditions are:
265 {{.Conditions}}
266 `
267
View as plain text