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