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  }