1
2
3
4
5 package test
6
7 import (
8 "errors"
9 "flag"
10 "fmt"
11 "os"
12 "path/filepath"
13 "strings"
14 "time"
15
16 "cmd/go/internal/base"
17 "cmd/go/internal/cfg"
18 "cmd/go/internal/cmdflag"
19 "cmd/go/internal/work"
20 )
21
22
23
24
25
26
27
28
29 func init() {
30 work.AddBuildFlags(CmdTest, work.OmitVFlag)
31
32 cf := CmdTest.Flag
33 cf.BoolVar(&testC, "c", false, "")
34 cf.BoolVar(&cfg.BuildI, "i", false, "")
35 cf.StringVar(&testO, "o", "", "")
36
37 cf.BoolVar(&testCover, "cover", false, "")
38 cf.Var(coverFlag{(*coverModeFlag)(&testCoverMode)}, "covermode", "")
39 cf.Var(coverFlag{commaListFlag{&testCoverPaths}}, "coverpkg", "")
40
41 cf.Var((*base.StringsFlag)(&work.ExecCmd), "exec", "")
42 cf.BoolVar(&testJSON, "json", false, "")
43 cf.Var(&testVet, "vet", "")
44
45
46
47
48
49 cf.StringVar(&testBench, "bench", "", "")
50 cf.Bool("benchmem", false, "")
51 cf.String("benchtime", "", "")
52 cf.StringVar(&testBlockProfile, "blockprofile", "", "")
53 cf.String("blockprofilerate", "", "")
54 cf.Int("count", 0, "")
55 cf.Var(coverFlag{stringFlag{&testCoverProfile}}, "coverprofile", "")
56 cf.String("cpu", "", "")
57 cf.StringVar(&testCPUProfile, "cpuprofile", "", "")
58 cf.Bool("failfast", false, "")
59 cf.StringVar(&testList, "list", "", "")
60 cf.StringVar(&testMemProfile, "memprofile", "", "")
61 cf.String("memprofilerate", "", "")
62 cf.StringVar(&testMutexProfile, "mutexprofile", "", "")
63 cf.String("mutexprofilefraction", "", "")
64 cf.Var(outputdirFlag{&testOutputDir}, "outputdir", "")
65 cf.Int("parallel", 0, "")
66 cf.String("run", "", "")
67 cf.Bool("short", false, "")
68 cf.DurationVar(&testTimeout, "timeout", 10*time.Minute, "")
69 cf.StringVar(&testTrace, "trace", "", "")
70 cf.BoolVar(&testV, "v", false, "")
71
72 for name, _ := range passFlagToTest {
73 cf.Var(cf.Lookup(name).Value, "test."+name, "")
74 }
75 }
76
77
78 type coverFlag struct{ v flag.Value }
79
80 func (f coverFlag) String() string { return f.v.String() }
81
82 func (f coverFlag) Set(value string) error {
83 if err := f.v.Set(value); err != nil {
84 return err
85 }
86 testCover = true
87 return nil
88 }
89
90 type coverModeFlag string
91
92 func (f *coverModeFlag) String() string { return string(*f) }
93 func (f *coverModeFlag) Set(value string) error {
94 switch value {
95 case "", "set", "count", "atomic":
96 *f = coverModeFlag(value)
97 return nil
98 default:
99 return errors.New(`valid modes are "set", "count", or "atomic"`)
100 }
101 }
102
103
104 type commaListFlag struct{ vals *[]string }
105
106 func (f commaListFlag) String() string { return strings.Join(*f.vals, ",") }
107
108 func (f commaListFlag) Set(value string) error {
109 if value == "" {
110 *f.vals = nil
111 } else {
112 *f.vals = strings.Split(value, ",")
113 }
114 return nil
115 }
116
117
118 type stringFlag struct{ val *string }
119
120 func (f stringFlag) String() string { return *f.val }
121 func (f stringFlag) Set(value string) error {
122 *f.val = value
123 return nil
124 }
125
126
127
128 type outputdirFlag struct {
129 resolved *string
130 }
131
132 func (f outputdirFlag) String() string { return *f.resolved }
133 func (f outputdirFlag) Set(value string) (err error) {
134 if value == "" {
135
136 *f.resolved = base.Cwd
137 } else {
138 *f.resolved, err = filepath.Abs(value)
139 }
140 return err
141 }
142
143
144
145
146 type vetFlag struct {
147 explicit bool
148 off bool
149 flags []string
150 }
151
152 func (f *vetFlag) String() string {
153 if f.off {
154 return "off"
155 }
156
157 var buf strings.Builder
158 for i, f := range f.flags {
159 if i > 0 {
160 buf.WriteByte(',')
161 }
162 buf.WriteString(f)
163 }
164 return buf.String()
165 }
166
167 func (f *vetFlag) Set(value string) error {
168 if value == "" {
169 *f = vetFlag{flags: defaultVetFlags}
170 return nil
171 }
172
173 if value == "off" {
174 *f = vetFlag{
175 explicit: true,
176 off: true,
177 }
178 return nil
179 }
180
181 if strings.Contains(value, "=") {
182 return fmt.Errorf("-vet argument cannot contain equal signs")
183 }
184 if strings.Contains(value, " ") {
185 return fmt.Errorf("-vet argument is comma-separated list, cannot contain spaces")
186 }
187 *f = vetFlag{explicit: true}
188 for _, arg := range strings.Split(value, ",") {
189 if arg == "" {
190 return fmt.Errorf("-vet argument contains empty list element")
191 }
192 f.flags = append(f.flags, "-"+arg)
193 }
194 return nil
195 }
196
197
198
199
200
201
202
203
204
205
206 func testFlags(args []string) (packageNames, passToTest []string) {
207 base.SetFromGOFLAGS(&CmdTest.Flag)
208 addFromGOFLAGS := map[string]bool{}
209 CmdTest.Flag.Visit(func(f *flag.Flag) {
210 if short := strings.TrimPrefix(f.Name, "test."); passFlagToTest[short] {
211 addFromGOFLAGS[f.Name] = true
212 }
213 })
214
215
216
217 firstUnknownFlag := ""
218
219 explicitArgs := make([]string, 0, len(args))
220 inPkgList := false
221 afterFlagWithoutValue := false
222 for len(args) > 0 {
223 f, remainingArgs, err := cmdflag.ParseOne(&CmdTest.Flag, args)
224
225 wasAfterFlagWithoutValue := afterFlagWithoutValue
226 afterFlagWithoutValue = false
227
228 if errors.Is(err, flag.ErrHelp) {
229 exitWithUsage()
230 }
231
232 if errors.Is(err, cmdflag.ErrFlagTerminator) {
233
234
235
236
237 explicitArgs = append(explicitArgs, args...)
238 break
239 }
240
241 if nf := (cmdflag.NonFlagError{}); errors.As(err, &nf) {
242 if !inPkgList && packageNames != nil {
243
244
245
246 if wasAfterFlagWithoutValue {
247
248
249
250
251
252
253 explicitArgs = append(explicitArgs, nf.RawArg)
254 args = remainingArgs
255 continue
256 } else {
257
258
259 explicitArgs = append(explicitArgs, args...)
260 break
261 }
262 }
263
264 inPkgList = true
265 packageNames = append(packageNames, nf.RawArg)
266 args = remainingArgs
267 continue
268 }
269
270 if inPkgList {
271
272
273 inPkgList = false
274 }
275
276 if nd := (cmdflag.FlagNotDefinedError{}); errors.As(err, &nd) {
277
278
279
280
281
282
283
284 if packageNames == nil {
285 packageNames = []string{}
286 }
287
288 if nd.RawArg == "-args" || nd.RawArg == "--args" {
289
290
291 explicitArgs = append(explicitArgs, remainingArgs...)
292 break
293 }
294
295 if firstUnknownFlag == "" {
296 firstUnknownFlag = nd.RawArg
297 }
298
299 explicitArgs = append(explicitArgs, nd.RawArg)
300 args = remainingArgs
301 if !nd.HasValue {
302 afterFlagWithoutValue = true
303 }
304 continue
305 }
306
307 if err != nil {
308 fmt.Fprintln(os.Stderr, err)
309 exitWithUsage()
310 }
311
312 if short := strings.TrimPrefix(f.Name, "test."); passFlagToTest[short] {
313 explicitArgs = append(explicitArgs, fmt.Sprintf("-test.%s=%v", short, f.Value))
314
315
316
317 delete(addFromGOFLAGS, short)
318 delete(addFromGOFLAGS, "test."+short)
319 }
320
321 args = remainingArgs
322 }
323 if firstUnknownFlag != "" && (testC || cfg.BuildI) {
324 buildFlag := "-c"
325 if !testC {
326 buildFlag = "-i"
327 }
328 fmt.Fprintf(os.Stderr, "go test: unknown flag %s cannot be used with %s\n", firstUnknownFlag, buildFlag)
329 exitWithUsage()
330 }
331
332 var injectedFlags []string
333 if testJSON {
334
335
336 injectedFlags = append(injectedFlags, "-test.v=true")
337 delete(addFromGOFLAGS, "v")
338 delete(addFromGOFLAGS, "test.v")
339 }
340
341
342
343
344 var timeoutSet, outputDirSet bool
345 CmdTest.Flag.Visit(func(f *flag.Flag) {
346 short := strings.TrimPrefix(f.Name, "test.")
347 if addFromGOFLAGS[f.Name] {
348 injectedFlags = append(injectedFlags, fmt.Sprintf("-test.%s=%v", short, f.Value))
349 }
350 switch short {
351 case "timeout":
352 timeoutSet = true
353 case "outputdir":
354 outputDirSet = true
355 }
356 })
357
358
359
360
361 if testTimeout > 0 && !timeoutSet {
362 injectedFlags = append(injectedFlags, fmt.Sprintf("-test.timeout=%v", testTimeout))
363 }
364
365
366
367
368
369 if testProfile() != "" && !outputDirSet {
370 injectedFlags = append(injectedFlags, "-test.outputdir="+testOutputDir)
371 }
372
373
374
375
376
377
378
379 helpLoop:
380 for _, arg := range explicitArgs {
381 switch arg {
382 case "--":
383 break helpLoop
384 case "-h", "-help", "--help":
385 testHelp = true
386 break helpLoop
387 }
388 }
389
390
391 if testCoverMode == "" {
392 testCoverMode = "set"
393 if cfg.BuildRace {
394
395 testCoverMode = "atomic"
396 }
397 }
398 if cfg.BuildRace && testCoverMode != "atomic" {
399 base.Fatalf(`-covermode must be "atomic", not %q, when -race is enabled`, testCoverMode)
400 }
401
402
403 return packageNames, append(injectedFlags, explicitArgs...)
404 }
405
406 func exitWithUsage() {
407 fmt.Fprintf(os.Stderr, "usage: %s\n", CmdTest.UsageLine)
408 fmt.Fprintf(os.Stderr, "Run 'go help %s' and 'go help %s' for details.\n", CmdTest.LongName(), HelpTestflag.LongName())
409
410 base.SetExitStatus(2)
411 base.Exit()
412 }
413
View as plain text