github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/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 // testFlagSpec defines a flag we know about. 20 type testFlagSpec struct { 21 name string 22 boolVar *bool 23 passToTest bool // pass to Test 24 multiOK bool // OK to have multiple instances 25 present bool // flag has been seen 26 } 27 28 // testFlagDefn is the set of flags we process. 29 var testFlagDefn = []*testFlagSpec{ 30 // local. 31 {name: "c", boolVar: &testC}, 32 {name: "cover", boolVar: &testCover}, 33 {name: "covermode"}, 34 {name: "coverpkg"}, 35 {name: "o"}, 36 37 // build flags. 38 {name: "a", boolVar: &buildA}, 39 {name: "n", boolVar: &buildN}, 40 {name: "p"}, 41 {name: "x", boolVar: &buildX}, 42 {name: "i", boolVar: &buildI}, 43 {name: "work", boolVar: &buildWork}, 44 {name: "gcflags"}, 45 {name: "exec"}, 46 {name: "ldflags"}, 47 {name: "gccgoflags"}, 48 {name: "tags"}, 49 {name: "compiler"}, 50 {name: "race", boolVar: &buildRace}, 51 {name: "linkshared", boolVar: &buildLinkshared}, 52 {name: "installsuffix"}, 53 54 // passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v. 55 {name: "bench", passToTest: true}, 56 {name: "benchmem", boolVar: new(bool), passToTest: true}, 57 {name: "benchtime", passToTest: true}, 58 {name: "coverprofile", passToTest: true}, 59 {name: "cpu", passToTest: true}, 60 {name: "cpuprofile", passToTest: true}, 61 {name: "memprofile", passToTest: true}, 62 {name: "memprofilerate", passToTest: true}, 63 {name: "blockprofile", passToTest: true}, 64 {name: "blockprofilerate", passToTest: true}, 65 {name: "outputdir", passToTest: true}, 66 {name: "parallel", passToTest: true}, 67 {name: "run", passToTest: true}, 68 {name: "short", boolVar: new(bool), passToTest: true}, 69 {name: "timeout", passToTest: true}, 70 {name: "trace", passToTest: true}, 71 {name: "v", boolVar: &testV, passToTest: true}, 72 } 73 74 // testFlags processes the command line, grabbing -x and -c, rewriting known flags 75 // to have "test" before them, and reading the command line for the 6.out. 76 // Unfortunately for us, we need to do our own flag processing because go test 77 // grabs some flags but otherwise its command line is just a holding place for 78 // pkg.test's arguments. 79 // We allow known flags both before and after the package name list, 80 // to allow both 81 // go test fmt -custom-flag-for-fmt-test 82 // go test -x math 83 func testFlags(args []string) (packageNames, passToTest []string) { 84 inPkg := false 85 outputDir := "" 86 for i := 0; i < len(args); i++ { 87 if !strings.HasPrefix(args[i], "-") { 88 if !inPkg && packageNames == nil { 89 // First package name we've seen. 90 inPkg = true 91 } 92 if inPkg { 93 packageNames = append(packageNames, args[i]) 94 continue 95 } 96 } 97 98 if inPkg { 99 // Found an argument beginning with "-"; end of package list. 100 inPkg = false 101 } 102 103 f, value, extraWord := testFlag(args, i) 104 if f == nil { 105 // This is a flag we do not know; we must assume 106 // that any args we see after this might be flag 107 // arguments, not package names. 108 inPkg = false 109 if packageNames == nil { 110 // make non-nil: we have seen the empty package list 111 packageNames = []string{} 112 } 113 passToTest = append(passToTest, args[i]) 114 continue 115 } 116 var err error 117 switch f.name { 118 // bool flags. 119 case "a", "c", "i", "n", "x", "v", "race", "cover", "work", "linkshared": 120 setBoolFlag(f.boolVar, value) 121 case "o": 122 testO = value 123 testNeedBinary = true 124 case "p": 125 setIntFlag(&buildP, value) 126 case "exec": 127 execCmd, err = splitQuotedFields(value) 128 if err != nil { 129 fatalf("invalid flag argument for -%s: %v", f.name, err) 130 } 131 case "gcflags": 132 buildGcflags, err = splitQuotedFields(value) 133 if err != nil { 134 fatalf("invalid flag argument for -%s: %v", f.name, err) 135 } 136 case "ldflags": 137 buildLdflags, err = splitQuotedFields(value) 138 if err != nil { 139 fatalf("invalid flag argument for -%s: %v", f.name, err) 140 } 141 case "gccgoflags": 142 buildGccgoflags, err = splitQuotedFields(value) 143 if err != nil { 144 fatalf("invalid flag argument for -%s: %v", f.name, err) 145 } 146 case "tags": 147 buildContext.BuildTags = strings.Fields(value) 148 case "compiler": 149 buildCompiler{}.Set(value) 150 case "bench": 151 // record that we saw the flag; don't care about the value 152 testBench = true 153 case "timeout": 154 testTimeout = value 155 case "blockprofile", "cpuprofile", "memprofile", "trace": 156 testProfile = true 157 testNeedBinary = true 158 case "coverpkg": 159 testCover = true 160 if value == "" { 161 testCoverPaths = nil 162 } else { 163 testCoverPaths = strings.Split(value, ",") 164 } 165 case "coverprofile": 166 testCover = true 167 testProfile = true 168 case "covermode": 169 switch value { 170 case "set", "count", "atomic": 171 testCoverMode = value 172 default: 173 fatalf("invalid flag argument for -covermode: %q", value) 174 } 175 testCover = true 176 case "outputdir": 177 outputDir = value 178 } 179 if extraWord { 180 i++ 181 } 182 if f.passToTest { 183 passToTest = append(passToTest, "-test."+f.name+"="+value) 184 } 185 } 186 187 if testCoverMode == "" { 188 testCoverMode = "set" 189 if buildRace { 190 // Default coverage mode is atomic when -race is set. 191 testCoverMode = "atomic" 192 } 193 } 194 195 // Tell the test what directory we're running in, so it can write the profiles there. 196 if testProfile && outputDir == "" { 197 dir, err := os.Getwd() 198 if err != nil { 199 fatalf("error from os.Getwd: %s", err) 200 } 201 passToTest = append(passToTest, "-test.outputdir", dir) 202 } 203 return 204 } 205 206 // testFlag sees if argument i is a known flag and returns its definition, value, and whether it consumed an extra word. 207 func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool) { 208 arg := args[i] 209 if strings.HasPrefix(arg, "--") { // reduce two minuses to one 210 arg = arg[1:] 211 } 212 switch arg { 213 case "-?", "-h", "-help": 214 usage() 215 } 216 if arg == "" || arg[0] != '-' { 217 return 218 } 219 name := arg[1:] 220 // If there's already "test.", drop it for now. 221 name = strings.TrimPrefix(name, "test.") 222 equals := strings.Index(name, "=") 223 if equals >= 0 { 224 value = name[equals+1:] 225 name = name[:equals] 226 } 227 for _, f = range testFlagDefn { 228 if name == f.name { 229 // Booleans are special because they have modes -x, -x=true, -x=false. 230 if f.boolVar != nil { 231 if equals < 0 { // otherwise, it's been set and will be verified in setBoolFlag 232 value = "true" 233 } else { 234 // verify it parses 235 setBoolFlag(new(bool), value) 236 } 237 } else { // Non-booleans must have a value. 238 extra = equals < 0 239 if extra { 240 if i+1 >= len(args) { 241 testSyntaxError("missing argument for flag " + f.name) 242 } 243 value = args[i+1] 244 } 245 } 246 if f.present && !f.multiOK { 247 testSyntaxError(f.name + " flag may be set only once") 248 } 249 f.present = true 250 return 251 } 252 } 253 f = nil 254 return 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 }