github.com/kekek/gb@v0.4.5-0.20170222120241-d4ba64b0b297/cmd/gb/testflag.go (about) 1 package main 2 3 import ( 4 "strconv" 5 "strings" 6 7 "github.com/constabulary/gb/internal/debug" 8 "github.com/pkg/errors" 9 ) 10 11 // testFlagSpec defines a flag we know about. 12 type testFlagSpec struct { 13 boolVar bool // True if the flag is type bool 14 passToTest bool // pass to Test 15 passToAll bool // pass to test plugin and test binary 16 present bool // The flag has been seen 17 } 18 19 // testFlagDefn is the set of flags we process. 20 var testFlagDefn = map[string]*testFlagSpec{ 21 // local to the test plugin 22 "cover": {boolVar: true}, 23 "coverpkg": {}, 24 "covermode": {}, 25 "a": {boolVar: true}, 26 "r": {boolVar: true}, 27 "f": {boolVar: true}, 28 "F": {boolVar: true}, 29 "n": {}, 30 "P": {}, 31 "ldflags": {}, 32 "gcflags": {}, 33 "dotfile": {}, 34 "tags": {}, 35 "race": {}, 36 37 // Passed to the test binary 38 "q": {boolVar: true, passToTest: true}, 39 "v": {boolVar: true, passToAll: true}, 40 "bench": {passToTest: true}, 41 "benchmem": {boolVar: true, passToTest: true}, 42 "benchtime": {passToTest: true}, 43 "coverprofile": {passToTest: true}, 44 "cpu": {passToTest: true}, 45 "cpuprofile": {passToTest: true}, 46 "memprofile": {passToTest: true}, 47 "memprofilerate": {passToTest: true}, 48 "blockprofile": {passToTest: true}, 49 "blockprofilerate": {passToTest: true}, 50 "outputdir": {passToTest: true}, 51 "parallel": {passToTest: true}, 52 "run": {passToTest: true}, 53 "short": {boolVar: true, passToTest: true}, 54 "timeout": {passToTest: true}, 55 } 56 57 // TestFlags appends "-test." for flags that are passed to the test binary. 58 func TestFlags(testArgs []string) []string { 59 debug.Debugf("TestFlags: args: %s", testArgs) 60 var targs []string 61 for _, arg := range testArgs { 62 var nArg, nVal, fArg string 63 fArg = arg 64 if !strings.Contains(arg, "-test.") { 65 nArg = strings.TrimPrefix(arg, "-") 66 if strings.Contains(nArg, "=") { 67 nArgVal := strings.Split(nArg, "=") 68 nArg, nVal = nArgVal[0], nArgVal[1] 69 } 70 if val, ok := testFlagDefn[nArg]; ok { 71 // Special handling for -q, needs to be -test.v when passed to the test 72 if nArg == "q" { 73 nArg = "v" 74 } 75 if val.passToTest || val.passToAll { 76 fArg = "-test." + nArg 77 if val.boolVar { 78 // boolean variables can be either -bool, or -bool=true 79 // some code, see issue 605, expects the latter form, so 80 // when present, expand boolean args to their canonical 81 // form. 82 nVal = "true" 83 } 84 if nVal != "" { 85 fArg = fArg + "=" + nVal 86 } 87 } 88 } 89 } 90 targs = append(targs, fArg) 91 } 92 debug.Debugf("testFlags: targs: %s", targs) 93 return targs 94 } 95 96 // TestFlagsExtraParse is used to separate known arguments from unknown 97 // arguments passed on the command line. Returns a string slice of test plugin 98 // arguments (parseArgs), and a slice of string arguments for the test binary 99 // (extraArgs). An error is returned if an argument is used twice, or an 100 // argument value is incorrect. 101 func TestFlagsExtraParse(args []string) (parseArgs []string, extraArgs []string, err error) { 102 argsLen := len(args) 103 104 for x := 0; x < argsLen; x++ { 105 nArg := args[x] 106 107 val, ok := testFlagDefn[strings.TrimPrefix(nArg, "-")] 108 if !strings.HasPrefix(nArg, "-") || (ok && !val.passToTest) { 109 err = setArgFound(nArg) 110 if err != nil { 111 return 112 } 113 if ok && val.passToAll { 114 // passToAll arguments, like -v or -cover, are special. They are handled by gb test 115 // and the test sub process. So move them to the front of the parseArgs list but 116 // the back of the extraArgs list. 117 parseArgs = append([]string{nArg}, parseArgs...) 118 extraArgs = append(extraArgs, nArg) 119 continue 120 } 121 parseArgs = append(parseArgs, nArg) 122 continue 123 } 124 125 var hadTestPrefix bool 126 hasEqual := strings.Contains(nArg, "=") 127 if !hasEqual && (x+1 < argsLen && !strings.HasPrefix(args[x+1], "-")) { 128 if strings.Contains(nArg, "-test.") { 129 hadTestPrefix = true 130 nArg = strings.TrimPrefix(nArg, "-test.") 131 } else { 132 nArg = strings.TrimPrefix(nArg, "-") 133 } 134 err = setArgFound(nArg) 135 if err != nil { 136 return 137 } 138 // Check the spec for arguments that consume the next argument 139 if val, ok := testFlagDefn[nArg]; ok { 140 if !val.boolVar { 141 nArg = nArg + "=" + args[x+1] 142 x++ 143 } 144 } 145 } else if hasEqual { 146 // The argument has an embedded value, here we can do some basic 147 // checking. 148 sArgs := strings.Split(nArg, "=") 149 tArg, tVal := strings.TrimPrefix(sArgs[0], "-"), sArgs[1] 150 if val, ok := testFlagDefn[tArg]; ok { 151 if val.boolVar { 152 if err = checkBoolFlag(tVal); err != nil { 153 return 154 } 155 } 156 if !val.passToTest { 157 parseArgs = append(parseArgs, nArg) 158 continue 159 } 160 } 161 } 162 163 // Append "-" to the argument, and "-test." if "-test." was previously 164 // trimmed. 165 if nArg[0] != '-' { 166 pre := "-" 167 if hadTestPrefix { 168 pre = "-test." 169 } 170 nArg = pre + nArg 171 } 172 extraArgs = append(extraArgs, nArg) 173 } 174 175 return 176 } 177 178 // setArgFound checks the argument spec to see if arg has already been 179 // encountered. If it has, then an error is returned. 180 func setArgFound(arg string) error { 181 var err error 182 nArg := strings.TrimPrefix(arg, "-") 183 if val, ok := testFlagDefn[nArg]; ok { 184 if val.present { 185 err = errors.Errorf("%q flag may be set only once", arg) 186 } else { 187 testFlagDefn[nArg].present = true 188 } 189 } 190 return err 191 } 192 193 // checkBoolFlag checks the value to ensure it is a boolean, if not an error is 194 // returned. 195 func checkBoolFlag(value string) error { 196 var nErr error 197 _, err := strconv.ParseBool(value) 198 if err != nil { 199 nErr = errors.New("illegal bool flag value " + value) 200 } 201 return nErr 202 }