github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-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 "os" 10 "strings" 11 12 "github.com/gagliardetto/golang-go/cmd/go/not-internal/base" 13 "github.com/gagliardetto/golang-go/cmd/go/not-internal/cfg" 14 "github.com/gagliardetto/golang-go/cmd/go/not-internal/cmdflag" 15 "github.com/gagliardetto/golang-go/cmd/go/not-internal/str" 16 "github.com/gagliardetto/golang-go/cmd/go/not-internal/work" 17 ) 18 19 const cmd = "test" 20 21 // The flag handling part of go test is large and distracting. 22 // We can't use the flag package because some of the flags from 23 // our command line are for us, and some are for 6.out, and 24 // some are for both. 25 26 // testFlagDefn is the set of flags we process. 27 var testFlagDefn = []*cmdflag.Defn{ 28 // local. 29 {Name: "c", BoolVar: &testC}, 30 {Name: "i", BoolVar: &cfg.BuildI}, 31 {Name: "o"}, 32 {Name: "cover", BoolVar: &testCover}, 33 {Name: "covermode"}, 34 {Name: "coverpkg"}, 35 {Name: "exec"}, 36 {Name: "json", BoolVar: &testJSON}, 37 {Name: "vet"}, 38 39 // Passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v. 40 {Name: "bench", PassToTest: true}, 41 {Name: "benchmem", BoolVar: new(bool), PassToTest: true}, 42 {Name: "benchtime", PassToTest: true}, 43 {Name: "blockprofile", PassToTest: true}, 44 {Name: "blockprofilerate", PassToTest: true}, 45 {Name: "count", PassToTest: true}, 46 {Name: "coverprofile", PassToTest: true}, 47 {Name: "cpu", PassToTest: true}, 48 {Name: "cpuprofile", PassToTest: true}, 49 {Name: "failfast", BoolVar: new(bool), PassToTest: true}, 50 {Name: "list", PassToTest: true}, 51 {Name: "memprofile", PassToTest: true}, 52 {Name: "memprofilerate", 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 cmdflag.AddKnownFlags("test", testFlagDefn) 67 var cmd base.Command 68 work.AddBuildFlags(&cmd, work.DefaultBuildFlags) 69 cmd.Flag.VisitAll(func(f *flag.Flag) { 70 if f.Name == "v" { 71 // test overrides the build -v flag 72 return 73 } 74 testFlagDefn = append(testFlagDefn, &cmdflag.Defn{ 75 Name: f.Name, 76 Value: f.Value, 77 }) 78 }) 79 } 80 81 // testFlags processes the command line, grabbing -x and -c, rewriting known flags 82 // to have "test" before them, and reading the command line for the 6.out. 83 // Unfortunately for us, we need to do our own flag processing because go test 84 // grabs some flags but otherwise its command line is just a holding place for 85 // pkg.test's arguments. 86 // We allow known flags both before and after the package name list, 87 // to allow both 88 // go test fmt -custom-flag-for-fmt-test 89 // go test -x math 90 func testFlags(usage func(), args []string) (packageNames, passToTest []string) { 91 goflags := cmdflag.FindGOFLAGS(testFlagDefn) 92 args = str.StringList(goflags, args) 93 inPkg := false 94 var explicitArgs []string 95 for i := 0; i < len(args); i++ { 96 if !strings.HasPrefix(args[i], "-") { 97 if !inPkg && packageNames == nil { 98 // First package name we've seen. 99 inPkg = true 100 } 101 if inPkg { 102 packageNames = append(packageNames, args[i]) 103 continue 104 } 105 } 106 107 if inPkg { 108 // Found an argument beginning with "-"; end of package list. 109 inPkg = false 110 } 111 112 f, value, extraWord := cmdflag.Parse(cmd, usage, testFlagDefn, args, i) 113 if f == nil { 114 // This is a flag we do not know; we must assume 115 // that any args we see after this might be flag 116 // arguments, not package names. 117 inPkg = false 118 if packageNames == nil { 119 // make non-nil: we have seen the empty package list 120 packageNames = []string{} 121 } 122 if args[i] == "-args" || args[i] == "--args" { 123 // -args or --args signals that everything that follows 124 // should be passed to the test. 125 explicitArgs = args[i+1:] 126 break 127 } 128 passToTest = append(passToTest, args[i]) 129 continue 130 } 131 if i < len(goflags) { 132 f.Present = false // Not actually present on the command line. 133 } 134 if f.Value != nil { 135 if err := f.Value.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.Value, but aren't. 141 switch f.Name { 142 // bool flags. 143 case "c", "i", "v", "cover", "json": 144 cmdflag.SetBool(cmd, f.BoolVar, value) 145 if f.Name == "json" && testJSON { 146 passToTest = append(passToTest, "-test.v=true") 147 } 148 case "o": 149 testO = value 150 testNeedBinary = true 151 case "exec": 152 xcmd, err := str.SplitQuotedFields(value) 153 if err != nil { 154 base.Fatalf("invalid flag argument for -%s: %v", f.Name, err) 155 } 156 work.ExecCmd = xcmd 157 case "bench": 158 // record that we saw the flag; don't care about the value 159 testBench = true 160 case "list": 161 testList = true 162 case "timeout": 163 testTimeout = value 164 case "blockprofile", "cpuprofile", "memprofile", "mutexprofile": 165 testProfile = "-" + f.Name 166 testNeedBinary = true 167 case "trace": 168 testProfile = "-trace" 169 case "coverpkg": 170 testCover = true 171 if value == "" { 172 testCoverPaths = nil 173 } else { 174 testCoverPaths = strings.Split(value, ",") 175 } 176 case "coverprofile": 177 testCover = true 178 testCoverProfile = value 179 case "covermode": 180 switch value { 181 case "set", "count", "atomic": 182 testCoverMode = value 183 default: 184 base.Fatalf("invalid flag argument for -covermode: %q", value) 185 } 186 testCover = true 187 case "outputdir": 188 testOutputDir = value 189 case "vet": 190 testVetList = value 191 } 192 } 193 if extraWord { 194 i++ 195 } 196 if f.PassToTest { 197 passToTest = append(passToTest, "-test."+f.Name+"="+value) 198 } 199 } 200 201 if testCoverMode == "" { 202 testCoverMode = "set" 203 if cfg.BuildRace { 204 // Default coverage mode is atomic when -race is set. 205 testCoverMode = "atomic" 206 } 207 } 208 209 testVetExplicit = testVetList != "" 210 if testVetList != "" && testVetList != "off" { 211 if strings.Contains(testVetList, "=") { 212 base.Fatalf("-vet argument cannot contain equal signs") 213 } 214 if strings.Contains(testVetList, " ") { 215 base.Fatalf("-vet argument is comma-separated list, cannot contain spaces") 216 } 217 list := strings.Split(testVetList, ",") 218 for i, arg := range list { 219 list[i] = "-" + arg 220 } 221 testVetFlags = list 222 } 223 224 if cfg.BuildRace && testCoverMode != "atomic" { 225 base.Fatalf(`-covermode must be "atomic", not %q, when -race is enabled`, testCoverMode) 226 } 227 228 // Tell the test what directory we're running in, so it can write the profiles there. 229 if testProfile != "" && testOutputDir == "" { 230 dir, err := os.Getwd() 231 if err != nil { 232 base.Fatalf("error from os.Getwd: %s", err) 233 } 234 passToTest = append(passToTest, "-test.outputdir", dir) 235 } 236 237 passToTest = append(passToTest, explicitArgs...) 238 return 239 }