github.com/hbdrawn/golang@v0.0.0-20141214014649-6b835209aba2/src/cmd/go/testflag.go (about) 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. 4 5 package main 6 7 import ( 8 "fmt" 9 "os" 10 "strconv" 11 "strings" 12 ) 13 14 // The flag handling part of go test is large and distracting. 15 // We can't use the flag package because some of the flags from 16 // our command line are for us, and some are for 6.out, and 17 // some are for both. 18 19 var usageMessage = `Usage of go test: 20 -c=false: compile but do not run the test binary 21 -file=file_test.go: specify file to use for tests; 22 use multiple times for multiple files 23 -p=n: build and test up to n packages in parallel 24 -x=false: print command lines as they are executed 25 26 // These flags can be passed with or without a "test." prefix: -v or -test.v. 27 -bench="": passes -test.bench to test 28 -benchmem=false: print memory allocation statistics for benchmarks 29 -benchtime=1s: passes -test.benchtime to test 30 -cover=false: enable coverage analysis 31 -covermode="set": specifies mode for coverage analysis 32 -coverpkg="": comma-separated list of packages for coverage analysis 33 -coverprofile="": passes -test.coverprofile to test if -cover 34 -cpu="": passes -test.cpu to test 35 -cpuprofile="": passes -test.cpuprofile to test 36 -memprofile="": passes -test.memprofile to test 37 -memprofilerate=0: passes -test.memprofilerate to test 38 -blockprofile="": pases -test.blockprofile to test 39 -blockprofilerate=0: passes -test.blockprofilerate to test 40 -outputdir=$PWD: passes -test.outputdir to test 41 -parallel=0: passes -test.parallel to test 42 -run="": passes -test.run to test 43 -short=false: passes -test.short to test 44 -timeout=0: passes -test.timeout to test 45 -v=false: passes -test.v to test 46 ` 47 48 // usage prints a usage message and exits. 49 func testUsage() { 50 fmt.Fprint(os.Stderr, usageMessage) 51 setExitStatus(2) 52 exit() 53 } 54 55 // testFlagSpec defines a flag we know about. 56 type testFlagSpec struct { 57 name string 58 boolVar *bool 59 passToTest bool // pass to Test 60 multiOK bool // OK to have multiple instances 61 present bool // flag has been seen 62 } 63 64 // testFlagDefn is the set of flags we process. 65 var testFlagDefn = []*testFlagSpec{ 66 // local. 67 {name: "c", boolVar: &testC}, 68 {name: "cover", boolVar: &testCover}, 69 {name: "coverpkg"}, 70 {name: "o"}, 71 72 // build flags. 73 {name: "a", boolVar: &buildA}, 74 {name: "n", boolVar: &buildN}, 75 {name: "p"}, 76 {name: "x", boolVar: &buildX}, 77 {name: "i", boolVar: &buildI}, 78 {name: "work", boolVar: &buildWork}, 79 {name: "ccflags"}, 80 {name: "gcflags"}, 81 {name: "exec"}, 82 {name: "ldflags"}, 83 {name: "gccgoflags"}, 84 {name: "tags"}, 85 {name: "compiler"}, 86 {name: "race", boolVar: &buildRace}, 87 {name: "installsuffix"}, 88 89 // passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v. 90 {name: "bench", passToTest: true}, 91 {name: "benchmem", boolVar: new(bool), passToTest: true}, 92 {name: "benchtime", passToTest: true}, 93 {name: "covermode"}, 94 {name: "coverprofile", passToTest: true}, 95 {name: "cpu", passToTest: true}, 96 {name: "cpuprofile", passToTest: true}, 97 {name: "memprofile", passToTest: true}, 98 {name: "memprofilerate", passToTest: true}, 99 {name: "blockprofile", passToTest: true}, 100 {name: "blockprofilerate", passToTest: true}, 101 {name: "outputdir", passToTest: true}, 102 {name: "parallel", passToTest: true}, 103 {name: "run", passToTest: true}, 104 {name: "short", boolVar: new(bool), passToTest: true}, 105 {name: "timeout", passToTest: true}, 106 {name: "v", boolVar: &testV, passToTest: true}, 107 } 108 109 // testFlags processes the command line, grabbing -x and -c, rewriting known flags 110 // to have "test" before them, and reading the command line for the 6.out. 111 // Unfortunately for us, we need to do our own flag processing because go test 112 // grabs some flags but otherwise its command line is just a holding place for 113 // pkg.test's arguments. 114 // We allow known flags both before and after the package name list, 115 // to allow both 116 // go test fmt -custom-flag-for-fmt-test 117 // go test -x math 118 func testFlags(args []string) (packageNames, passToTest []string) { 119 inPkg := false 120 outputDir := "" 121 for i := 0; i < len(args); i++ { 122 if !strings.HasPrefix(args[i], "-") { 123 if !inPkg && packageNames == nil { 124 // First package name we've seen. 125 inPkg = true 126 } 127 if inPkg { 128 packageNames = append(packageNames, args[i]) 129 continue 130 } 131 } 132 133 if inPkg { 134 // Found an argument beginning with "-"; end of package list. 135 inPkg = false 136 } 137 138 f, value, extraWord := testFlag(args, i) 139 if f == nil { 140 // This is a flag we do not know; we must assume 141 // that any args we see after this might be flag 142 // arguments, not package names. 143 inPkg = false 144 if packageNames == nil { 145 // make non-nil: we have seen the empty package list 146 packageNames = []string{} 147 } 148 passToTest = append(passToTest, args[i]) 149 continue 150 } 151 var err error 152 switch f.name { 153 // bool flags. 154 case "a", "c", "i", "n", "x", "v", "race", "cover", "work": 155 setBoolFlag(f.boolVar, value) 156 case "o": 157 testO = value 158 testNeedBinary = true 159 case "p": 160 setIntFlag(&buildP, value) 161 case "exec": 162 execCmd, err = splitQuotedFields(value) 163 if err != nil { 164 fatalf("invalid flag argument for -%s: %v", f.name, err) 165 } 166 case "ccflags": 167 buildCcflags, err = splitQuotedFields(value) 168 if err != nil { 169 fatalf("invalid flag argument for -%s: %v", f.name, err) 170 } 171 case "gcflags": 172 buildGcflags, err = splitQuotedFields(value) 173 if err != nil { 174 fatalf("invalid flag argument for -%s: %v", f.name, err) 175 } 176 case "ldflags": 177 buildLdflags, err = splitQuotedFields(value) 178 if err != nil { 179 fatalf("invalid flag argument for -%s: %v", f.name, err) 180 } 181 case "gccgoflags": 182 buildGccgoflags, err = splitQuotedFields(value) 183 if err != nil { 184 fatalf("invalid flag argument for -%s: %v", f.name, err) 185 } 186 case "tags": 187 buildContext.BuildTags = strings.Fields(value) 188 case "compiler": 189 buildCompiler{}.Set(value) 190 case "bench": 191 // record that we saw the flag; don't care about the value 192 testBench = true 193 case "timeout": 194 testTimeout = value 195 case "blockprofile", "cpuprofile", "memprofile": 196 testProfile = true 197 testNeedBinary = true 198 case "coverpkg": 199 testCover = true 200 if value == "" { 201 testCoverPaths = nil 202 } else { 203 testCoverPaths = strings.Split(value, ",") 204 } 205 case "coverprofile": 206 testCover = true 207 testProfile = true 208 case "covermode": 209 switch value { 210 case "set", "count", "atomic": 211 testCoverMode = value 212 default: 213 fatalf("invalid flag argument for -cover: %q", value) 214 } 215 testCover = true 216 case "outputdir": 217 outputDir = value 218 } 219 if extraWord { 220 i++ 221 } 222 if f.passToTest { 223 passToTest = append(passToTest, "-test."+f.name+"="+value) 224 } 225 } 226 227 if testCoverMode == "" { 228 testCoverMode = "set" 229 if buildRace { 230 // Default coverage mode is atomic when -race is set. 231 testCoverMode = "atomic" 232 } 233 } 234 235 // Tell the test what directory we're running in, so it can write the profiles there. 236 if testProfile && outputDir == "" { 237 dir, err := os.Getwd() 238 if err != nil { 239 fatalf("error from os.Getwd: %s", err) 240 } 241 passToTest = append(passToTest, "-test.outputdir", dir) 242 } 243 return 244 } 245 246 // testFlag sees if argument i is a known flag and returns its definition, value, and whether it consumed an extra word. 247 func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool) { 248 arg := args[i] 249 if strings.HasPrefix(arg, "--") { // reduce two minuses to one 250 arg = arg[1:] 251 } 252 switch arg { 253 case "-?", "-h", "-help": 254 usage() 255 } 256 if arg == "" || arg[0] != '-' { 257 return 258 } 259 name := arg[1:] 260 // If there's already "test.", drop it for now. 261 name = strings.TrimPrefix(name, "test.") 262 equals := strings.Index(name, "=") 263 if equals >= 0 { 264 value = name[equals+1:] 265 name = name[:equals] 266 } 267 for _, f = range testFlagDefn { 268 if name == f.name { 269 // Booleans are special because they have modes -x, -x=true, -x=false. 270 if f.boolVar != nil { 271 if equals < 0 { // otherwise, it's been set and will be verified in setBoolFlag 272 value = "true" 273 } else { 274 // verify it parses 275 setBoolFlag(new(bool), value) 276 } 277 } else { // Non-booleans must have a value. 278 extra = equals < 0 279 if extra { 280 if i+1 >= len(args) { 281 testSyntaxError("missing argument for flag " + f.name) 282 } 283 value = args[i+1] 284 } 285 } 286 if f.present && !f.multiOK { 287 testSyntaxError(f.name + " flag may be set only once") 288 } 289 f.present = true 290 return 291 } 292 } 293 f = nil 294 return 295 } 296 297 // setBoolFlag sets the addressed boolean to the value. 298 func setBoolFlag(flag *bool, value string) { 299 x, err := strconv.ParseBool(value) 300 if err != nil { 301 testSyntaxError("illegal bool flag value " + value) 302 } 303 *flag = x 304 } 305 306 // setIntFlag sets the addressed integer to the value. 307 func setIntFlag(flag *int, value string) { 308 x, err := strconv.Atoi(value) 309 if err != nil { 310 testSyntaxError("illegal int flag value " + value) 311 } 312 *flag = x 313 } 314 315 func testSyntaxError(msg string) { 316 fmt.Fprintf(os.Stderr, "go test: %s\n", msg) 317 fmt.Fprintf(os.Stderr, `run "go help test" or "go help testflag" for more information`+"\n") 318 os.Exit(2) 319 }