1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
10 "cmd/go/internal/cmdflag"
11 "cmd/go/internal/work"
22 //go:generate go run ./genflags.go
24 // The flag handling part of go test is large and distracting.
25 // We can't use (*flag.FlagSet).Parse because some of the flags from
26 // our command line are for us, and some are for the test binary, and
30 work.AddBuildFlags(CmdTest, work.OmitVFlag)
31 base.AddWorkfileFlag(&CmdTest.Flag)
34 cf.BoolVar(&testC, "c", false, "")
35 cf.BoolVar(&cfg.BuildI, "i", false, "")
36 cf.StringVar(&testO, "o", "", "")
38 cf.BoolVar(&testCover, "cover", false, "")
39 cf.Var(coverFlag{(*coverModeFlag)(&testCoverMode)}, "covermode", "")
40 cf.Var(coverFlag{commaListFlag{&testCoverPaths}}, "coverpkg", "")
42 cf.Var((*base.StringsFlag)(&work.ExecCmd), "exec", "")
43 cf.BoolVar(&testJSON, "json", false, "")
44 cf.Var(&testVet, "vet", "")
46 // Register flags to be forwarded to the test binary. We retain variables for
47 // some of them so that cmd/go knows what to do with the test output, or knows
48 // to build the test in a way that supports the use of the flag.
50 cf.StringVar(&testBench, "bench", "", "")
51 cf.Bool("benchmem", false, "")
52 cf.String("benchtime", "", "")
53 cf.StringVar(&testBlockProfile, "blockprofile", "", "")
54 cf.String("blockprofilerate", "", "")
55 cf.Int("count", 0, "")
56 cf.Var(coverFlag{stringFlag{&testCoverProfile}}, "coverprofile", "")
57 cf.String("cpu", "", "")
58 cf.StringVar(&testCPUProfile, "cpuprofile", "", "")
59 cf.Bool("failfast", false, "")
60 cf.StringVar(&testFuzz, "fuzz", "", "")
61 cf.StringVar(&testList, "list", "", "")
62 cf.StringVar(&testMemProfile, "memprofile", "", "")
63 cf.String("memprofilerate", "", "")
64 cf.StringVar(&testMutexProfile, "mutexprofile", "", "")
65 cf.String("mutexprofilefraction", "", "")
66 cf.Var(&testOutputDir, "outputdir", "")
67 cf.Int("parallel", 0, "")
68 cf.String("run", "", "")
69 cf.Bool("short", false, "")
70 cf.DurationVar(&testTimeout, "timeout", 10*time.Minute, "")
71 cf.String("fuzztime", "", "")
72 cf.String("fuzzminimizetime", "", "")
73 cf.StringVar(&testTrace, "trace", "", "")
74 cf.BoolVar(&testV, "v", false, "")
75 cf.Var(&testShuffle, "shuffle", "")
77 for name := range passFlagToTest {
78 cf.Var(cf.Lookup(name).Value, "test."+name, "")
82 // A coverFlag is a flag.Value that also implies -cover.
83 type coverFlag struct{ v flag.Value }
85 func (f coverFlag) String() string { return f.v.String() }
87 func (f coverFlag) Set(value string) error {
88 if err := f.v.Set(value); err != nil {
95 type coverModeFlag string
97 func (f *coverModeFlag) String() string { return string(*f) }
98 func (f *coverModeFlag) Set(value string) error {
100 case "", "set", "count", "atomic":
101 *f = coverModeFlag(value)
104 return errors.New(`valid modes are "set", "count", or "atomic"`)
108 // A commaListFlag is a flag.Value representing a comma-separated list.
109 type commaListFlag struct{ vals *[]string }
111 func (f commaListFlag) String() string { return strings.Join(*f.vals, ",") }
113 func (f commaListFlag) Set(value string) error {
117 *f.vals = strings.Split(value, ",")
122 // A stringFlag is a flag.Value representing a single string.
123 type stringFlag struct{ val *string }
125 func (f stringFlag) String() string { return *f.val }
126 func (f stringFlag) Set(value string) error {
131 // outputdirFlag implements the -outputdir flag.
132 // It interprets an empty value as the working directory of the 'go' command.
133 type outputdirFlag struct {
137 func (f *outputdirFlag) String() string {
141 func (f *outputdirFlag) Set(value string) (err error) {
145 f.abs, err = filepath.Abs(value)
150 func (f *outputdirFlag) getAbs() string {
157 // vetFlag implements the special parsing logic for the -vet flag:
158 // a comma-separated list, with distinguished values "all" and
159 // "off", plus a boolean tracking whether it was set explicitly.
161 // "all" is encoded as vetFlag{true, false, nil}, since it will
162 // pass no flags to the vet binary, and by default, it runs all
164 type vetFlag struct {
167 flags []string // passed to vet when invoked automatically during 'go test'
170 func (f *vetFlag) String() string {
172 case !f.off && !f.explicit && len(f.flags) == 0:
178 var buf strings.Builder
179 for i, f := range f.flags {
188 func (f *vetFlag) Set(value string) error {
191 *f = vetFlag{flags: defaultVetFlags}
193 case strings.Contains(value, "="):
194 return fmt.Errorf("-vet argument cannot contain equal signs")
195 case strings.Contains(value, " "):
196 return fmt.Errorf("-vet argument is comma-separated list, cannot contain spaces")
198 *f = vetFlag{explicit: true}
200 for _, arg := range strings.Split(value, ",") {
203 return fmt.Errorf("-vet argument contains empty list element")
206 *f = vetFlag{explicit: true}
216 f.flags = append(f.flags, "-"+arg)
218 if len(f.flags) > 1 && single != "" {
219 return fmt.Errorf("-vet does not accept %q in a list with other analyzers", single)
224 type shuffleFlag struct {
229 func (f *shuffleFlag) String() string {
236 return fmt.Sprintf("%d", *f.seed)
239 func (f *shuffleFlag) Set(value string) error {
241 *f = shuffleFlag{on: false}
246 *f = shuffleFlag{on: true}
250 seed, err := strconv.ParseInt(value, 10, 64)
252 return fmt.Errorf(`-shuffle argument must be "on", "off", or an int64: %v`, err)
255 *f = shuffleFlag{on: true, seed: &seed}
259 // testFlags processes the command line, grabbing -x and -c, rewriting known flags
260 // to have "test" before them, and reading the command line for the test binary.
261 // Unfortunately for us, we need to do our own flag processing because go test
262 // grabs some flags but otherwise its command line is just a holding place for
263 // pkg.test's arguments.
264 // We allow known flags both before and after the package name list,
266 // go test fmt -custom-flag-for-fmt-test
268 func testFlags(args []string) (packageNames, passToTest []string) {
269 base.SetFromGOFLAGS(&CmdTest.Flag)
270 addFromGOFLAGS := map[string]bool{}
271 CmdTest.Flag.Visit(func(f *flag.Flag) {
272 if short := strings.TrimPrefix(f.Name, "test."); passFlagToTest[short] {
273 addFromGOFLAGS[f.Name] = true
277 // firstUnknownFlag helps us report an error when flags not known to 'go
278 // test' are used along with -i or -c.
279 firstUnknownFlag := ""
281 explicitArgs := make([]string, 0, len(args))
283 afterFlagWithoutValue := false
285 f, remainingArgs, err := cmdflag.ParseOne(&CmdTest.Flag, args)
287 wasAfterFlagWithoutValue := afterFlagWithoutValue
288 afterFlagWithoutValue = false // provisionally
290 if errors.Is(err, flag.ErrHelp) {
294 if errors.Is(err, cmdflag.ErrFlagTerminator) {
295 // 'go list' allows package arguments to be named either before or after
296 // the terminator, but 'go test' has historically allowed them only
297 // before. Preserve that behavior and treat all remaining arguments —
298 // including the terminator itself! — as arguments to the test.
299 explicitArgs = append(explicitArgs, args...)
303 if nf := (cmdflag.NonFlagError{}); errors.As(err, &nf) {
304 if !inPkgList && packageNames != nil {
305 // We already saw the package list previously, and this argument is not
306 // a flag, so it — and everything after it — must be either a value for
307 // a preceding flag or a literal argument to the test binary.
308 if wasAfterFlagWithoutValue {
309 // This argument could syntactically be a flag value, so
310 // optimistically assume that it is and keep looking for go command
313 // (If we're wrong, we'll at least be consistent with historical
314 // behavior; see https://golang.org/issue/40763.)
315 explicitArgs = append(explicitArgs, nf.RawArg)
319 // This argument syntactically cannot be a flag value, so it must be a
320 // positional argument, and so must everything after it.
321 explicitArgs = append(explicitArgs, args...)
327 packageNames = append(packageNames, nf.RawArg)
328 args = remainingArgs // Consume the package name.
333 // This argument is syntactically a flag, so if we were in the package
334 // list we're not anymore.
338 if nd := (cmdflag.FlagNotDefinedError{}); errors.As(err, &nd) {
339 // This is a flag we do not know. We must assume that any args we see
340 // after this might be flag arguments, not package names, so make
341 // packageNames non-nil to indicate that the package list is complete.
343 // (Actually, we only strictly need to assume that if the flag is not of
344 // the form -x=value, but making this more precise would be a breaking
345 // change in the command line API.)
346 if packageNames == nil {
347 packageNames = []string{}
350 if nd.RawArg == "-args" || nd.RawArg == "--args" {
351 // -args or --args signals that everything that follows
352 // should be passed to the test.
353 explicitArgs = append(explicitArgs, remainingArgs...)
357 if firstUnknownFlag == "" {
358 firstUnknownFlag = nd.RawArg
361 explicitArgs = append(explicitArgs, nd.RawArg)
364 afterFlagWithoutValue = true
370 fmt.Fprintln(os.Stderr, err)
374 if short := strings.TrimPrefix(f.Name, "test."); passFlagToTest[short] {
375 explicitArgs = append(explicitArgs, fmt.Sprintf("-test.%s=%v", short, f.Value))
377 // This flag has been overridden explicitly, so don't forward its implicit
378 // value from GOFLAGS.
379 delete(addFromGOFLAGS, short)
380 delete(addFromGOFLAGS, "test."+short)
385 if firstUnknownFlag != "" && (testC || cfg.BuildI) {
390 fmt.Fprintf(os.Stderr, "go test: unknown flag %s cannot be used with %s\n", firstUnknownFlag, buildFlag)
394 var injectedFlags []string
396 // If converting to JSON, we need the full output in order to pipe it to
398 injectedFlags = append(injectedFlags, "-test.v=true")
399 delete(addFromGOFLAGS, "v")
400 delete(addFromGOFLAGS, "test.v")
403 // Inject flags from GOFLAGS before the explicit command-line arguments.
404 // (They must appear before the flag terminator or first non-flag argument.)
405 // Also determine whether flags with awkward defaults have already been set.
406 var timeoutSet, outputDirSet bool
407 CmdTest.Flag.Visit(func(f *flag.Flag) {
408 short := strings.TrimPrefix(f.Name, "test.")
409 if addFromGOFLAGS[f.Name] {
410 injectedFlags = append(injectedFlags, fmt.Sprintf("-test.%s=%v", short, f.Value))
420 // 'go test' has a default timeout, but the test binary itself does not.
421 // If the timeout wasn't set (and forwarded) explicitly, add the default
422 // timeout to the command line.
423 if testTimeout > 0 && !timeoutSet {
424 injectedFlags = append(injectedFlags, fmt.Sprintf("-test.timeout=%v", testTimeout))
427 // Similarly, the test binary defaults -test.outputdir to its own working
428 // directory, but 'go test' defaults it to the working directory of the 'go'
429 // command. Set it explicitly if it is needed due to some other flag that
431 if testProfile() != "" && !outputDirSet {
432 injectedFlags = append(injectedFlags, "-test.outputdir="+testOutputDir.getAbs())
435 // If the user is explicitly passing -help or -h, show output
436 // of the test binary so that the help output is displayed
437 // even though the test will exit with success.
438 // This loop is imperfect: it will do the wrong thing for a case
439 // like -args -test.outputdir -help. Such cases are probably rare,
440 // and getting this wrong doesn't do too much harm.
442 for _, arg := range explicitArgs {
446 case "-h", "-help", "--help":
452 // Ensure that -race and -covermode are compatible.
453 if testCoverMode == "" {
454 testCoverMode = "set"
456 // Default coverage mode is atomic when -race is set.
457 testCoverMode = "atomic"
460 if cfg.BuildRace && testCoverMode != "atomic" {
461 base.Fatalf(`-covermode must be "atomic", not %q, when -race is enabled`, testCoverMode)
464 // Forward any unparsed arguments (following --args) to the test binary.
465 return packageNames, append(injectedFlags, explicitArgs...)
468 func exitWithUsage() {
469 fmt.Fprintf(os.Stderr, "usage: %s\n", CmdTest.UsageLine)
470 fmt.Fprintf(os.Stderr, "Run 'go help %s' and 'go help %s' for details.\n", CmdTest.LongName(), HelpTestflag.LongName())
472 base.SetExitStatus(2)