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