github.com/gsquire/gb@v0.4.4-0.20161112235727-3982dc872064/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  }