diff options
Diffstat (limited to 'src/cmd/go/testflag.go')
-rw-r--r-- | src/cmd/go/testflag.go | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/src/cmd/go/testflag.go b/src/cmd/go/testflag.go new file mode 100644 index 000000000..ecf5bf456 --- /dev/null +++ b/src/cmd/go/testflag.go @@ -0,0 +1,235 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "os" + "strconv" + "strings" +) + +// The flag handling part of go test is large and distracting. +// We can't use the flag package because some of the flags from +// our command line are for us, and some are for 6.out, and +// some are for both. + +var usageMessage = `Usage of go test: + -c=false: compile but do not run the test binary + -file=file_test.go: specify file to use for tests; + use multiple times for multiple files + -p=n: build and test up to n packages in parallel + -x=false: print command lines as they are executed + + // These flags can be passed with or without a "test." prefix: -v or -test.v. + -bench="": passes -test.bench to test + -benchtime=1: passes -test.benchtime to test + -cpu="": passes -test.cpu to test + -cpuprofile="": passes -test.cpuprofile to test + -memprofile="": passes -test.memprofile to test + -memprofilerate=0: passes -test.memprofilerate to test + -parallel=0: passes -test.parallel to test + -run="": passes -test.run to test + -short=false: passes -test.short to test + -timeout=0: passes -test.timeout to test + -v=false: passes -test.v to test +` + +// usage prints a usage message and exits. +func testUsage() { + fmt.Fprint(os.Stderr, usageMessage) + setExitStatus(2) + exit() +} + +// testFlagSpec defines a flag we know about. +type testFlagSpec struct { + name string + boolVar *bool + passToTest bool // pass to Test + multiOK bool // OK to have multiple instances + present bool // flag has been seen +} + +// testFlagDefn is the set of flags we process. +var testFlagDefn = []*testFlagSpec{ + // local. + {name: "c", boolVar: &testC}, + {name: "file", multiOK: true}, + {name: "i", boolVar: &testI}, + + // build flags. + {name: "a", boolVar: &buildA}, + {name: "n", boolVar: &buildN}, + {name: "p"}, + {name: "x", boolVar: &buildX}, + {name: "work", boolVar: &buildWork}, + {name: "gcflags"}, + {name: "ldflags"}, + {name: "gccgoflags"}, + {name: "tags"}, + {name: "compiler"}, + + // passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v. + {name: "bench", passToTest: true}, + {name: "benchtime", passToTest: true}, + {name: "cpu", passToTest: true}, + {name: "cpuprofile", passToTest: true}, + {name: "memprofile", passToTest: true}, + {name: "memprofilerate", passToTest: true}, + {name: "parallel", passToTest: true}, + {name: "run", passToTest: true}, + {name: "short", boolVar: new(bool), passToTest: true}, + {name: "timeout", passToTest: true}, + {name: "v", boolVar: &testV, passToTest: true}, +} + +// testFlags processes the command line, grabbing -x and -c, rewriting known flags +// to have "test" before them, and reading the command line for the 6.out. +// Unfortunately for us, we need to do our own flag processing because go test +// grabs some flags but otherwise its command line is just a holding place for +// pkg.test's arguments. +// We allow known flags both before and after the package name list, +// to allow both +// go test fmt -custom-flag-for-fmt-test +// go test -x math +func testFlags(args []string) (packageNames, passToTest []string) { + inPkg := false + for i := 0; i < len(args); i++ { + if !strings.HasPrefix(args[i], "-") { + if !inPkg && packageNames == nil { + // First package name we've seen. + inPkg = true + } + if inPkg { + packageNames = append(packageNames, args[i]) + continue + } + } + + if inPkg { + // Found an argument beginning with "-"; end of package list. + inPkg = false + } + + f, value, extraWord := testFlag(args, i) + if f == nil { + // This is a flag we do not know; we must assume + // that any args we see after this might be flag + // arguments, not package names. + inPkg = false + if packageNames == nil { + // make non-nil: we have seen the empty package list + packageNames = []string{} + } + passToTest = append(passToTest, args[i]) + continue + } + switch f.name { + // bool flags. + case "a", "c", "i", "n", "x", "v", "work": + setBoolFlag(f.boolVar, value) + case "p": + setIntFlag(&buildP, value) + case "gcflags": + buildGcflags = strings.Fields(value) + case "ldflags": + buildLdflags = strings.Fields(value) + case "gccgoflags": + buildGccgoflags = strings.Fields(value) + case "tags": + buildContext.BuildTags = strings.Fields(value) + case "compiler": + buildCompiler{}.Set(value) + case "file": + testFiles = append(testFiles, value) + case "bench": + // record that we saw the flag; don't care about the value + testBench = true + case "timeout": + testTimeout = value + } + if extraWord { + i++ + } + if f.passToTest { + passToTest = append(passToTest, "-test."+f.name+"="+value) + } + } + return +} + +// testFlag sees if argument i is a known flag and returns its definition, value, and whether it consumed an extra word. +func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool) { + arg := args[i] + if strings.HasPrefix(arg, "--") { // reduce two minuses to one + arg = arg[1:] + } + switch arg { + case "-?", "-h", "-help": + usage() + } + if arg == "" || arg[0] != '-' { + return + } + name := arg[1:] + // If there's already "test.", drop it for now. + if strings.HasPrefix(name, "test.") { + name = name[5:] + } + equals := strings.Index(name, "=") + if equals >= 0 { + value = name[equals+1:] + name = name[:equals] + } + for _, f = range testFlagDefn { + if name == f.name { + // Booleans are special because they have modes -x, -x=true, -x=false. + if f.boolVar != nil { + if equals < 0 { // otherwise, it's been set and will be verified in setBoolFlag + value = "true" + } else { + // verify it parses + setBoolFlag(new(bool), value) + } + } else { // Non-booleans must have a value. + extra = equals < 0 + if extra { + if i+1 >= len(args) { + usage() + } + value = args[i+1] + } + } + if f.present && !f.multiOK { + usage() + } + f.present = true + return + } + } + f = nil + return +} + +// setBoolFlag sets the addressed boolean to the value. +func setBoolFlag(flag *bool, value string) { + x, err := strconv.ParseBool(value) + if err != nil { + fmt.Fprintf(os.Stderr, "go test: illegal bool flag value %s\n", value) + usage() + } + *flag = x +} + +// setIntFlag sets the addressed integer to the value. +func setIntFlag(flag *int, value string) { + x, err := strconv.Atoi(value) + if err != nil { + fmt.Fprintf(os.Stderr, "go test: illegal int flag value %s\n", value) + usage() + } + *flag = x +} |