github.com/q45/go@v0.0.0-20151101211701-a4fb8c13db3f/src/cmd/go/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 main
     6  
     7  import (
     8  	"flag"
     9  	"fmt"
    10  	"os"
    11  	"strconv"
    12  	"strings"
    13  )
    14  
    15  // The flag handling part of go test is large and distracting.
    16  // We can't use the flag package because some of the flags from
    17  // our command line are for us, and some are for 6.out, and
    18  // some are for both.
    19  
    20  // testFlagSpec defines a flag we know about.
    21  type testFlagSpec struct {
    22  	name       string
    23  	boolVar    *bool
    24  	flagValue  flag.Value
    25  	passToTest bool // pass to Test
    26  	multiOK    bool // OK to have multiple instances
    27  	present    bool // flag has been seen
    28  }
    29  
    30  // testFlagDefn is the set of flags we process.
    31  var testFlagDefn = []*testFlagSpec{
    32  	// local.
    33  	{name: "c", boolVar: &testC},
    34  	{name: "i", boolVar: &buildI},
    35  	{name: "o"},
    36  	{name: "cover", boolVar: &testCover},
    37  	{name: "covermode"},
    38  	{name: "coverpkg"},
    39  	{name: "exec"},
    40  
    41  	// passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v.
    42  	{name: "bench", passToTest: true},
    43  	{name: "benchmem", boolVar: new(bool), passToTest: true},
    44  	{name: "benchtime", passToTest: true},
    45  	{name: "count", passToTest: true},
    46  	{name: "coverprofile", passToTest: true},
    47  	{name: "cpu", passToTest: true},
    48  	{name: "cpuprofile", passToTest: true},
    49  	{name: "memprofile", passToTest: true},
    50  	{name: "memprofilerate", passToTest: true},
    51  	{name: "blockprofile", passToTest: true},
    52  	{name: "blockprofilerate", passToTest: true},
    53  	{name: "outputdir", passToTest: true},
    54  	{name: "parallel", passToTest: true},
    55  	{name: "run", passToTest: true},
    56  	{name: "short", boolVar: new(bool), passToTest: true},
    57  	{name: "timeout", passToTest: true},
    58  	{name: "trace", passToTest: true},
    59  	{name: "v", boolVar: &testV, passToTest: true},
    60  }
    61  
    62  // add build flags to testFlagDefn
    63  func init() {
    64  	var cmd Command
    65  	addBuildFlags(&cmd)
    66  	cmd.Flag.VisitAll(func(f *flag.Flag) {
    67  		if f.Name == "v" {
    68  			// test overrides the build -v flag
    69  			return
    70  		}
    71  		testFlagDefn = append(testFlagDefn, &testFlagSpec{
    72  			name:      f.Name,
    73  			flagValue: f.Value,
    74  		})
    75  	})
    76  }
    77  
    78  // testFlags processes the command line, grabbing -x and -c, rewriting known flags
    79  // to have "test" before them, and reading the command line for the 6.out.
    80  // Unfortunately for us, we need to do our own flag processing because go test
    81  // grabs some flags but otherwise its command line is just a holding place for
    82  // pkg.test's arguments.
    83  // The usage is:
    84  //	go test [test flags] [packages] [flags for test binary]
    85  // Thus we process test flags (adding -test. to each) until we find a non-flag,
    86  // which introduces the optional list of packages. We collect the package paths
    87  // until we find another -flag, and pass that and the rest of the command line
    88  // to the test binary untouched.
    89  // For backwards compatibility with a poor design, if while processing test
    90  // flags we see an unrecognized flag, we accept it as an argument to the binary.
    91  // For this to work in general, one must say -foo=xxx not -foo xxx or else
    92  // xxx will be taken to be a package path. As said, the design is poor.
    93  func testFlags(args []string) (packageNames, passToTest []string) {
    94  	outputDir := ""
    95  	// Flags.
    96  	var i int
    97  	for i = 0; i < len(args); i++ {
    98  		if !strings.HasPrefix(args[i], "-") {
    99  			break // Start of packages.
   100  		}
   101  
   102  		f, value, extraWord := testFlag(args, i)
   103  		if f == nil {
   104  			// This is a flag we do not know. Pass it to the test but keep
   105  			// processing flags.
   106  			passToTest = append(passToTest, args[i])
   107  			continue
   108  		}
   109  		if f.flagValue != nil {
   110  			if err := f.flagValue.Set(value); err != nil {
   111  				fatalf("invalid flag argument for -%s: %v", f.name, err)
   112  			}
   113  		} else {
   114  			// Test-only flags.
   115  			// Arguably should be handled by f.flagValue, but aren't.
   116  			var err error
   117  			switch f.name {
   118  			// bool flags.
   119  			case "c", "i", "v", "cover":
   120  				setBoolFlag(f.boolVar, value)
   121  			case "o":
   122  				testO = value
   123  				testNeedBinary = true
   124  			case "exec":
   125  				execCmd, err = splitQuotedFields(value)
   126  				if err != nil {
   127  					fatalf("invalid flag argument for -%s: %v", f.name, err)
   128  				}
   129  			case "bench":
   130  				// record that we saw the flag; don't care about the value
   131  				testBench = true
   132  			case "timeout":
   133  				testTimeout = value
   134  			case "blockprofile", "cpuprofile", "memprofile", "trace":
   135  				testProfile = true
   136  				testNeedBinary = true
   137  			case "coverpkg":
   138  				testCover = true
   139  				if value == "" {
   140  					testCoverPaths = nil
   141  				} else {
   142  					testCoverPaths = strings.Split(value, ",")
   143  				}
   144  			case "coverprofile":
   145  				testCover = true
   146  				testProfile = true
   147  			case "covermode":
   148  				switch value {
   149  				case "set", "count", "atomic":
   150  					testCoverMode = value
   151  				default:
   152  					fatalf("invalid flag argument for -covermode: %q", value)
   153  				}
   154  				testCover = true
   155  			case "outputdir":
   156  				outputDir = value
   157  			}
   158  		}
   159  		if extraWord {
   160  			i++
   161  		}
   162  		if f.passToTest {
   163  			passToTest = append(passToTest, "-test."+f.name+"="+value)
   164  		}
   165  	}
   166  	// Package names.
   167  	for ; i < len(args); i++ {
   168  		if strings.HasPrefix(args[i], "-") {
   169  			break // Start of trailing arguments.
   170  		}
   171  		packageNames = append(packageNames, args[i])
   172  	}
   173  	// Trailing arguments.
   174  	passToTest = append(passToTest, args[i:]...)
   175  
   176  	if testCoverMode == "" {
   177  		testCoverMode = "set"
   178  		if buildRace {
   179  			// Default coverage mode is atomic when -race is set.
   180  			testCoverMode = "atomic"
   181  		}
   182  	}
   183  
   184  	// Tell the test what directory we're running in, so it can write the profiles there.
   185  	if testProfile && outputDir == "" {
   186  		dir, err := os.Getwd()
   187  		if err != nil {
   188  			fatalf("error from os.Getwd: %s", err)
   189  		}
   190  		passToTest = append(passToTest, "-test.outputdir", dir)
   191  	}
   192  	return
   193  }
   194  
   195  // testFlag sees if argument i is a known flag and returns its definition, value, and whether it consumed an extra word.
   196  func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool) {
   197  	arg := args[i]
   198  	if strings.HasPrefix(arg, "--") { // reduce two minuses to one
   199  		arg = arg[1:]
   200  	}
   201  	switch arg {
   202  	case "-?", "-h", "-help":
   203  		usage()
   204  	}
   205  	if arg == "" || arg[0] != '-' {
   206  		return
   207  	}
   208  	name := arg[1:]
   209  	// If there's already "test.", drop it for now.
   210  	name = strings.TrimPrefix(name, "test.")
   211  	equals := strings.Index(name, "=")
   212  	if equals >= 0 {
   213  		value = name[equals+1:]
   214  		name = name[:equals]
   215  	}
   216  	for _, f = range testFlagDefn {
   217  		if name == f.name {
   218  			// Booleans are special because they have modes -x, -x=true, -x=false.
   219  			if f.boolVar != nil || isBoolFlag(f.flagValue) {
   220  				if equals < 0 { // otherwise, it's been set and will be verified in setBoolFlag
   221  					value = "true"
   222  				} else {
   223  					// verify it parses
   224  					setBoolFlag(new(bool), value)
   225  				}
   226  			} else { // Non-booleans must have a value.
   227  				extra = equals < 0
   228  				if extra {
   229  					if i+1 >= len(args) {
   230  						testSyntaxError("missing argument for flag " + f.name)
   231  					}
   232  					value = args[i+1]
   233  				}
   234  			}
   235  			if f.present && !f.multiOK {
   236  				testSyntaxError(f.name + " flag may be set only once")
   237  			}
   238  			f.present = true
   239  			return
   240  		}
   241  	}
   242  	f = nil
   243  	return
   244  }
   245  
   246  // isBoolFlag reports whether v is a bool flag.
   247  func isBoolFlag(v flag.Value) bool {
   248  	vv, ok := v.(interface {
   249  		IsBoolFlag() bool
   250  	})
   251  	if ok {
   252  		return vv.IsBoolFlag()
   253  	}
   254  	return false
   255  }
   256  
   257  // setBoolFlag sets the addressed boolean to the value.
   258  func setBoolFlag(flag *bool, value string) {
   259  	x, err := strconv.ParseBool(value)
   260  	if err != nil {
   261  		testSyntaxError("illegal bool flag value " + value)
   262  	}
   263  	*flag = x
   264  }
   265  
   266  // setIntFlag sets the addressed integer to the value.
   267  func setIntFlag(flag *int, value string) {
   268  	x, err := strconv.Atoi(value)
   269  	if err != nil {
   270  		testSyntaxError("illegal int flag value " + value)
   271  	}
   272  	*flag = x
   273  }
   274  
   275  func testSyntaxError(msg string) {
   276  	fmt.Fprintf(os.Stderr, "go test: %s\n", msg)
   277  	fmt.Fprintf(os.Stderr, `run "go help test" or "go help testflag" for more information`+"\n")
   278  	os.Exit(2)
   279  }