github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/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: "outputdir", passToTest: true}, 54 {name: "parallel", passToTest: true}, 55 {name: "run", passToTest: true}, 56 {name: "short", boolVar: new(bool), passToTest: true}, 57 {name: "timeout", passToTest: true}, 58 {name: "trace", passToTest: true}, 59 {name: "v", boolVar: &testV, passToTest: true}, 60 } 61 62 // add build flags to testFlagDefn 63 func init() { 64 var cmd Command 65 addBuildFlags(&cmd) 66 cmd.Flag.VisitAll(func(f *flag.Flag) { 67 if f.Name == "v" { 68 // test overrides the build -v flag 69 return 70 } 71 testFlagDefn = append(testFlagDefn, &testFlagSpec{ 72 name: f.Name, 73 flagValue: f.Value, 74 }) 75 }) 76 } 77 78 // testFlags processes the command line, grabbing -x and -c, rewriting known flags 79 // to have "test" before them, and reading the command line for the 6.out. 80 // Unfortunately for us, we need to do our own flag processing because go test 81 // grabs some flags but otherwise its command line is just a holding place for 82 // pkg.test's arguments. 83 // The usage is: 84 // go test [test flags] [packages] [flags for test binary] 85 // Thus we process test flags (adding -test. to each) until we find a non-flag, 86 // which introduces the optional list of packages. We collect the package paths 87 // until we find another -flag, and pass that and the rest of the command line 88 // to the test binary untouched. 89 // For backwards compatibility with a poor design, if while processing test 90 // flags we see an unrecognized flag, we accept it as an argument to the binary. 91 // For this to work in general, one must say -foo=xxx not -foo xxx or else 92 // xxx will be taken to be a package path. As said, the design is poor. 93 func testFlags(args []string) (packageNames, passToTest []string) { 94 outputDir := "" 95 // Flags. 96 var i int 97 for i = 0; i < len(args); i++ { 98 if !strings.HasPrefix(args[i], "-") { 99 break // Start of packages. 100 } 101 102 f, value, extraWord := testFlag(args, i) 103 if f == nil { 104 // This is a flag we do not know. Pass it to the test but keep 105 // processing flags. 106 passToTest = append(passToTest, args[i]) 107 continue 108 } 109 if f.flagValue != nil { 110 if err := f.flagValue.Set(value); err != nil { 111 fatalf("invalid flag argument for -%s: %v", f.name, err) 112 } 113 } else { 114 // Test-only flags. 115 // Arguably should be handled by f.flagValue, but aren't. 116 var err error 117 switch f.name { 118 // bool flags. 119 case "c", "i", "v", "cover": 120 setBoolFlag(f.boolVar, value) 121 case "o": 122 testO = value 123 testNeedBinary = true 124 case "exec": 125 execCmd, err = splitQuotedFields(value) 126 if err != nil { 127 fatalf("invalid flag argument for -%s: %v", f.name, err) 128 } 129 case "bench": 130 // record that we saw the flag; don't care about the value 131 testBench = true 132 case "timeout": 133 testTimeout = value 134 case "blockprofile", "cpuprofile", "memprofile", "trace": 135 testProfile = true 136 testNeedBinary = true 137 case "coverpkg": 138 testCover = true 139 if value == "" { 140 testCoverPaths = nil 141 } else { 142 testCoverPaths = strings.Split(value, ",") 143 } 144 case "coverprofile": 145 testCover = true 146 testProfile = true 147 case "covermode": 148 switch value { 149 case "set", "count", "atomic": 150 testCoverMode = value 151 default: 152 fatalf("invalid flag argument for -covermode: %q", value) 153 } 154 testCover = true 155 case "outputdir": 156 outputDir = value 157 } 158 } 159 if extraWord { 160 i++ 161 } 162 if f.passToTest { 163 passToTest = append(passToTest, "-test."+f.name+"="+value) 164 } 165 } 166 // Package names. 167 for ; i < len(args); i++ { 168 if strings.HasPrefix(args[i], "-") { 169 break // Start of trailing arguments. 170 } 171 packageNames = append(packageNames, args[i]) 172 } 173 // Trailing arguments. 174 passToTest = append(passToTest, args[i:]...) 175 176 if testCoverMode == "" { 177 testCoverMode = "set" 178 if buildRace { 179 // Default coverage mode is atomic when -race is set. 180 testCoverMode = "atomic" 181 } 182 } 183 184 // Tell the test what directory we're running in, so it can write the profiles there. 185 if testProfile && outputDir == "" { 186 dir, err := os.Getwd() 187 if err != nil { 188 fatalf("error from os.Getwd: %s", err) 189 } 190 passToTest = append(passToTest, "-test.outputdir", dir) 191 } 192 return 193 } 194 195 // testFlag sees if argument i is a known flag and returns its definition, value, and whether it consumed an extra word. 196 func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool) { 197 arg := args[i] 198 if strings.HasPrefix(arg, "--") { // reduce two minuses to one 199 arg = arg[1:] 200 } 201 switch arg { 202 case "-?", "-h", "-help": 203 usage() 204 } 205 if arg == "" || arg[0] != '-' { 206 return 207 } 208 name := arg[1:] 209 // If there's already "test.", drop it for now. 210 name = strings.TrimPrefix(name, "test.") 211 equals := strings.Index(name, "=") 212 if equals >= 0 { 213 value = name[equals+1:] 214 name = name[:equals] 215 } 216 for _, f = range testFlagDefn { 217 if name == f.name { 218 // Booleans are special because they have modes -x, -x=true, -x=false. 219 if f.boolVar != nil || isBoolFlag(f.flagValue) { 220 if equals < 0 { // otherwise, it's been set and will be verified in setBoolFlag 221 value = "true" 222 } else { 223 // verify it parses 224 setBoolFlag(new(bool), value) 225 } 226 } else { // Non-booleans must have a value. 227 extra = equals < 0 228 if extra { 229 if i+1 >= len(args) { 230 testSyntaxError("missing argument for flag " + f.name) 231 } 232 value = args[i+1] 233 } 234 } 235 if f.present && !f.multiOK { 236 testSyntaxError(f.name + " flag may be set only once") 237 } 238 f.present = true 239 return 240 } 241 } 242 f = nil 243 return 244 } 245 246 // isBoolFlag reports whether v is a bool flag. 247 func isBoolFlag(v flag.Value) bool { 248 vv, ok := v.(interface { 249 IsBoolFlag() bool 250 }) 251 if ok { 252 return vv.IsBoolFlag() 253 } 254 return false 255 } 256 257 // setBoolFlag sets the addressed boolean to the value. 258 func setBoolFlag(flag *bool, value string) { 259 x, err := strconv.ParseBool(value) 260 if err != nil { 261 testSyntaxError("illegal bool flag value " + value) 262 } 263 *flag = x 264 } 265 266 // setIntFlag sets the addressed integer to the value. 267 func setIntFlag(flag *int, value string) { 268 x, err := strconv.Atoi(value) 269 if err != nil { 270 testSyntaxError("illegal int flag value " + value) 271 } 272 *flag = x 273 } 274 275 func testSyntaxError(msg string) { 276 fmt.Fprintf(os.Stderr, "go test: %s\n", msg) 277 fmt.Fprintf(os.Stderr, `run "go help test" or "go help testflag" for more information`+"\n") 278 os.Exit(2) 279 }