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