1
2
3
4
5 package test
6
7 import (
8 "cmd/go/internal/base"
9 "cmd/go/internal/cfg"
10 "cmd/go/internal/cmdflag"
11 "cmd/go/internal/work"
12 "errors"
13 "flag"
14 "fmt"
15 "os"
16 "path/filepath"
17 "strconv"
18 "strings"
19 "time"
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(&testFuzz, "fuzz", "", "")
60 cf.StringVar(&testList, "list", "", "")
61 cf.StringVar(&testMemProfile, "memprofile", "", "")
62 cf.String("memprofilerate", "", "")
63 cf.StringVar(&testMutexProfile, "mutexprofile", "", "")
64 cf.String("mutexprofilefraction", "", "")
65 cf.Var(&testOutputDir, "outputdir", "")
66 cf.Int("parallel", 0, "")
67 cf.String("run", "", "")
68 cf.Bool("short", false, "")
69 cf.DurationVar(&testTimeout, "timeout", 10*time.Minute, "")
70 cf.String("fuzztime", "", "")
71 cf.String("fuzzminimizetime", "", "")
72 cf.StringVar(&testTrace, "trace", "", "")
73 cf.BoolVar(&testV, "v", false, "")
74 cf.Var(&testShuffle, "shuffle", "")
75
76 for name := range passFlagToTest {
77 cf.Var(cf.Lookup(name).Value, "test."+name, "")
78 }
79 }
80
81
82 type coverFlag struct{ v flag.Value }
83
84 func (f coverFlag) String() string { return f.v.String() }
85
86 func (f coverFlag) Set(value string) error {
87 if err := f.v.Set(value); err != nil {
88 return err
89 }
90 testCover = true
91 return nil
92 }
93
94 type coverModeFlag string
95
96 func (f *coverModeFlag) String() string { return string(*f) }
97 func (f *coverModeFlag) Set(value string) error {
98 switch value {
99 case "", "set", "count", "atomic":
100 *f = coverModeFlag(value)
101 return nil
102 default:
103 return errors.New(`valid modes are "set", "count", or "atomic"`)
104 }
105 }
106
107
108 type commaListFlag struct{ vals *[]string }
109
110 func (f commaListFlag) String() string { return strings.Join(*f.vals, ",") }
111
112 func (f commaListFlag) Set(value string) error {
113 if value == "" {
114 *f.vals = nil
115 } else {
116 *f.vals = strings.Split(value, ",")
117 }
118 return nil
119 }
120
121
122 type stringFlag struct{ val *string }
123
124 func (f stringFlag) String() string { return *f.val }
125 func (f stringFlag) Set(value string) error {
126 *f.val = value
127 return nil
128 }
129
130
131
132 type outputdirFlag struct {
133 abs string
134 }
135
136 func (f *outputdirFlag) String() string {
137 return f.abs
138 }
139
140 func (f *outputdirFlag) Set(value string) (err error) {
141 if value == "" {
142 f.abs = ""
143 } else {
144 f.abs, err = filepath.Abs(value)
145 }
146 return err
147 }
148
149 func (f *outputdirFlag) getAbs() string {
150 if f.abs == "" {
151 return base.Cwd()
152 }
153 return f.abs
154 }
155
156
157
158
159
160
161
162
163 type vetFlag struct {
164 explicit bool
165 off bool
166 flags []string
167 }
168
169 func (f *vetFlag) String() string {
170 switch {
171 case !f.off && !f.explicit && len(f.flags) == 0:
172 return "all"
173 case f.off:
174 return "off"
175 }
176
177 var buf strings.Builder
178 for i, f := range f.flags {
179 if i > 0 {
180 buf.WriteByte(',')
181 }
182 buf.WriteString(f)
183 }
184 return buf.String()
185 }
186
187 func (f *vetFlag) Set(value string) error {
188 switch {
189 case value == "":
190 *f = vetFlag{flags: defaultVetFlags}
191 return nil
192 case strings.Contains(value, "="):
193 return fmt.Errorf("-vet argument cannot contain equal signs")
194 case strings.Contains(value, " "):
195 return fmt.Errorf("-vet argument is comma-separated list, cannot contain spaces")
196 }
197
198 *f = vetFlag{explicit: true}
199 var single string
200 for _, arg := range strings.Split(value, ",") {
201 switch arg {
202 case "":
203 return fmt.Errorf("-vet argument contains empty list element")
204 case "all":
205 single = arg
206 *f = vetFlag{explicit: true}
207 continue
208 case "off":
209 single = arg
210 *f = vetFlag{
211 explicit: true,
212 off: true,
213 }
214 continue
215 default:
216 if _, ok := passAnalyzersToVet[arg]; !ok {
217 return fmt.Errorf("-vet argument must be a supported analyzer or a distinguished value; found %s", arg)
218 }
219 f.flags = append(f.flags, "-"+arg)
220 }
221 }
222 if len(f.flags) > 1 && single != "" {
223 return fmt.Errorf("-vet does not accept %q in a list with other analyzers", single)
224 }
225 if len(f.flags) > 1 && single != "" {
226 return fmt.Errorf("-vet does not accept %q in a list with other analyzers", single)
227 }
228 return nil
229 }
230
231 type shuffleFlag struct {
232 on bool
233 seed *int64
234 }
235
236 func (f *shuffleFlag) String() string {
237 if !f.on {
238 return "off"
239 }
240 if f.seed == nil {
241 return "on"
242 }
243 return fmt.Sprintf("%d", *f.seed)
244 }
245
246 func (f *shuffleFlag) Set(value string) error {
247 if value == "off" {
248 *f = shuffleFlag{on: false}
249 return nil
250 }
251
252 if value == "on" {
253 *f = shuffleFlag{on: true}
254 return nil
255 }
256
257 seed, err := strconv.ParseInt(value, 10, 64)
258 if err != nil {
259 return fmt.Errorf(`-shuffle argument must be "on", "off", or an int64: %v`, err)
260 }
261
262 *f = shuffleFlag{on: true, seed: &seed}
263 return nil
264 }
265
266
267
268
269
270
271
272
273
274
275 func testFlags(args []string) (packageNames, passToTest []string) {
276 base.SetFromGOFLAGS(&CmdTest.Flag)
277 addFromGOFLAGS := map[string]bool{}
278 CmdTest.Flag.Visit(func(f *flag.Flag) {
279 if short := strings.TrimPrefix(f.Name, "test."); passFlagToTest[short] {
280 addFromGOFLAGS[f.Name] = true
281 }
282 })
283
284
285
286 firstUnknownFlag := ""
287
288 explicitArgs := make([]string, 0, len(args))
289 inPkgList := false
290 afterFlagWithoutValue := false
291 for len(args) > 0 {
292 f, remainingArgs, err := cmdflag.ParseOne(&CmdTest.Flag, args)
293
294 wasAfterFlagWithoutValue := afterFlagWithoutValue
295 afterFlagWithoutValue = false
296
297 if errors.Is(err, flag.ErrHelp) {
298 exitWithUsage()
299 }
300
301 if errors.Is(err, cmdflag.ErrFlagTerminator) {
302
303
304
305
306 explicitArgs = append(explicitArgs, args...)
307 break
308 }
309
310 if nf := (cmdflag.NonFlagError{}); errors.As(err, &nf) {
311 if !inPkgList && packageNames != nil {
312
313
314
315 if wasAfterFlagWithoutValue {
316
317
318
319
320
321
322 explicitArgs = append(explicitArgs, nf.RawArg)
323 args = remainingArgs
324 continue
325 } else {
326
327
328 explicitArgs = append(explicitArgs, args...)
329 break
330 }
331 }
332
333 inPkgList = true
334 packageNames = append(packageNames, nf.RawArg)
335 args = remainingArgs
336 continue
337 }
338
339 if inPkgList {
340
341
342 inPkgList = false
343 }
344
345 if nd := (cmdflag.FlagNotDefinedError{}); errors.As(err, &nd) {
346
347
348
349
350
351
352
353 if packageNames == nil {
354 packageNames = []string{}
355 }
356
357 if nd.RawArg == "-args" || nd.RawArg == "--args" {
358
359
360 explicitArgs = append(explicitArgs, remainingArgs...)
361 break
362 }
363
364 if firstUnknownFlag == "" {
365 firstUnknownFlag = nd.RawArg
366 }
367
368 explicitArgs = append(explicitArgs, nd.RawArg)
369 args = remainingArgs
370 if !nd.HasValue {
371 afterFlagWithoutValue = true
372 }
373 continue
374 }
375
376 if err != nil {
377 fmt.Fprintln(os.Stderr, err)
378 exitWithUsage()
379 }
380
381 if short := strings.TrimPrefix(f.Name, "test."); passFlagToTest[short] {
382 explicitArgs = append(explicitArgs, fmt.Sprintf("-test.%s=%v", short, f.Value))
383
384
385
386 delete(addFromGOFLAGS, short)
387 delete(addFromGOFLAGS, "test."+short)
388 }
389
390 args = remainingArgs
391 }
392 if firstUnknownFlag != "" && (testC || cfg.BuildI) {
393 buildFlag := "-c"
394 if !testC {
395 buildFlag = "-i"
396 }
397 fmt.Fprintf(os.Stderr, "go: unknown flag %s cannot be used with %s\n", firstUnknownFlag, buildFlag)
398 exitWithUsage()
399 }
400
401 var injectedFlags []string
402 if testJSON {
403
404
405 injectedFlags = append(injectedFlags, "-test.v=true")
406 delete(addFromGOFLAGS, "v")
407 delete(addFromGOFLAGS, "test.v")
408 }
409
410
411
412
413 var timeoutSet, outputDirSet bool
414 CmdTest.Flag.Visit(func(f *flag.Flag) {
415 short := strings.TrimPrefix(f.Name, "test.")
416 if addFromGOFLAGS[f.Name] {
417 injectedFlags = append(injectedFlags, fmt.Sprintf("-test.%s=%v", short, f.Value))
418 }
419 switch short {
420 case "timeout":
421 timeoutSet = true
422 case "outputdir":
423 outputDirSet = true
424 }
425 })
426
427
428
429
430 if testTimeout > 0 && !timeoutSet {
431 injectedFlags = append(injectedFlags, fmt.Sprintf("-test.timeout=%v", testTimeout))
432 }
433
434
435
436
437
438 if testProfile() != "" && !outputDirSet {
439 injectedFlags = append(injectedFlags, "-test.outputdir="+testOutputDir.getAbs())
440 }
441
442
443
444
445
446
447
448 helpLoop:
449 for _, arg := range explicitArgs {
450 switch arg {
451 case "--":
452 break helpLoop
453 case "-h", "-help", "--help":
454 testHelp = true
455 break helpLoop
456 }
457 }
458
459
460 if testCoverMode == "" {
461 testCoverMode = "set"
462 if cfg.BuildRace {
463
464 testCoverMode = "atomic"
465 }
466 }
467 if cfg.BuildRace && testCoverMode != "atomic" {
468 base.Fatalf(`-covermode must be "atomic", not %q, when -race is enabled`, testCoverMode)
469 }
470
471
472 return packageNames, append(injectedFlags, explicitArgs...)
473 }
474
475 func exitWithUsage() {
476 fmt.Fprintf(os.Stderr, "usage: %s\n", CmdTest.UsageLine)
477 fmt.Fprintf(os.Stderr, "Run 'go help %s' and 'go help %s' for details.\n", CmdTest.LongName(), HelpTestflag.LongName())
478
479 base.SetExitStatus(2)
480 base.Exit()
481 }
482
View as plain text