github.com/aca02djr/gb@v0.4.1/cmd/gb/testflag.go (about) 1 package main 2 3 import ( 4 "errors" 5 "fmt" 6 "strconv" 7 "strings" 8 9 "github.com/constabulary/gb/debug" 10 ) 11 12 // testFlagSpec defines a flag we know about. 13 type testFlagSpec struct { 14 boolVar bool // True if the flag is type bool 15 passToTest bool // pass to Test 16 passToAll bool // pass to test plugin and test binary 17 present bool // The flag has been seen 18 } 19 20 // testFlagDefn is the set of flags we process. 21 var testFlagDefn = map[string]*testFlagSpec{ 22 // local to the test plugin 23 "cover": {boolVar: true}, 24 "coverpkg": {}, 25 "covermode": {}, 26 "a": {boolVar: true}, 27 "r": {boolVar: true}, 28 "f": {boolVar: true}, 29 "F": {boolVar: true}, 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 nVal != "" { 78 fArg = fArg + "=" + nVal 79 } 80 } 81 } 82 } 83 targs = append(targs, fArg) 84 } 85 debug.Debugf("testFlags: targs: %s", targs) 86 return targs 87 } 88 89 // TestFlagsExtraParse is used to separate known arguments from unknown 90 // arguments passed on the command line. Returns a string slice of test plugin 91 // arguments (parseArgs), and a slice of string arguments for the test binary 92 // (extraArgs). An error is returned if an argument is used twice, or an 93 // argument value is incorrect. 94 func TestFlagsExtraParse(args []string) (parseArgs []string, extraArgs []string, err error) { 95 argsLen := len(args) 96 97 for x := 0; x < argsLen; x++ { 98 nArg := args[x] 99 100 val, ok := testFlagDefn[strings.TrimPrefix(nArg, "-")] 101 if !strings.HasPrefix(nArg, "-") || (ok && !val.passToTest) { 102 err = setArgFound(nArg) 103 if err != nil { 104 return 105 } 106 if ok && val.passToAll { 107 // passToAll arguments, like -v or -cover, are special. They are handled by gb test 108 // and the test sub process. So move them to the front of the parseArgs list but 109 // the back of the extraArgs list. 110 parseArgs = append([]string{nArg}, parseArgs...) 111 extraArgs = append(extraArgs, nArg) 112 continue 113 } 114 parseArgs = append(parseArgs, nArg) 115 continue 116 } 117 118 var hadTestPrefix bool 119 hasEqual := strings.Contains(nArg, "=") 120 if !hasEqual && (x+1 < argsLen && !strings.HasPrefix(args[x+1], "-")) { 121 if strings.Contains(nArg, "-test.") { 122 hadTestPrefix = true 123 nArg = strings.TrimPrefix(nArg, "-test.") 124 } else { 125 nArg = strings.TrimPrefix(nArg, "-") 126 } 127 err = setArgFound(nArg) 128 if err != nil { 129 return 130 } 131 // Check the spec for arguments that consume the next argument 132 if val, ok := testFlagDefn[nArg]; ok { 133 if !val.boolVar { 134 nArg = nArg + "=" + args[x+1] 135 x++ 136 } 137 } 138 } else if hasEqual { 139 // The argument has an embedded value, here we can do some basic 140 // checking. 141 sArgs := strings.Split(nArg, "=") 142 tArg, tVal := strings.TrimPrefix(sArgs[0], "-"), sArgs[1] 143 if val, ok := testFlagDefn[tArg]; ok { 144 if val.boolVar { 145 if err = checkBoolFlag(tVal); err != nil { 146 return 147 } 148 } 149 if !val.passToTest { 150 parseArgs = append(parseArgs, nArg) 151 continue 152 } 153 } 154 } 155 156 // Append "-" to the argument, and "-test." if "-test." was previously 157 // trimmed. 158 if nArg[0] != '-' { 159 pre := "-" 160 if hadTestPrefix { 161 pre = "-test." 162 } 163 nArg = pre + nArg 164 } 165 extraArgs = append(extraArgs, nArg) 166 } 167 168 return 169 } 170 171 // setArgFound checks the argument spec to see if arg has already been 172 // encountered. If it has, then an error is returned. 173 func setArgFound(arg string) error { 174 var err error 175 nArg := strings.TrimPrefix(arg, "-") 176 if val, ok := testFlagDefn[nArg]; ok { 177 if val.present { 178 err = fmt.Errorf("%q flag may be set only once", arg) 179 } else { 180 testFlagDefn[nArg].present = true 181 } 182 } 183 return err 184 } 185 186 // checkBoolFlag checks the value to ensure it is a boolean, if not an error is 187 // returned. 188 func checkBoolFlag(value string) error { 189 var nErr error 190 _, err := strconv.ParseBool(value) 191 if err != nil { 192 nErr = errors.New("illegal bool flag value " + value) 193 } 194 return nErr 195 }