github.com/bgentry/go@v0.0.0-20150121062915-6cf5a733d54d/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  	"fmt"
     9  	"os"
    10  	"strconv"
    11  	"strings"
    12  )
    13  
    14  // The flag handling part of go test is large and distracting.
    15  // We can't use the flag package because some of the flags from
    16  // our command line are for us, and some are for 6.out, and
    17  // some are for both.
    18  
    19  var usageMessage = `Usage of go test:
    20    -c=false: compile but do not run the test binary
    21    -file=file_test.go: specify file to use for tests;
    22        use multiple times for multiple files
    23    -p=n: build and test up to n packages in parallel
    24    -x=false: print command lines as they are executed
    25  
    26    // These flags can be passed with or without a "test." prefix: -v or -test.v.
    27    -bench="": passes -test.bench to test
    28    -benchmem=false: print memory allocation statistics for benchmarks
    29    -benchtime=1s: passes -test.benchtime to test
    30    -cover=false: enable coverage analysis
    31    -covermode="set": specifies mode for coverage analysis
    32    -coverpkg="": comma-separated list of packages for coverage analysis
    33    -coverprofile="": passes -test.coverprofile to test if -cover
    34    -cpu="": passes -test.cpu to test
    35    -cpuprofile="": passes -test.cpuprofile to test
    36    -memprofile="": passes -test.memprofile to test
    37    -memprofilerate=0: passes -test.memprofilerate to test
    38    -blockprofile="": pases -test.blockprofile to test
    39    -blockprofilerate=0: passes -test.blockprofilerate to test
    40    -outputdir=$PWD: passes -test.outputdir to test
    41    -parallel=0: passes -test.parallel to test
    42    -run="": passes -test.run to test
    43    -short=false: passes -test.short to test
    44    -timeout=0: passes -test.timeout to test
    45    -v=false: passes -test.v to test
    46  `
    47  
    48  // usage prints a usage message and exits.
    49  func testUsage() {
    50  	fmt.Fprint(os.Stderr, usageMessage)
    51  	setExitStatus(2)
    52  	exit()
    53  }
    54  
    55  // testFlagSpec defines a flag we know about.
    56  type testFlagSpec struct {
    57  	name       string
    58  	boolVar    *bool
    59  	passToTest bool // pass to Test
    60  	multiOK    bool // OK to have multiple instances
    61  	present    bool // flag has been seen
    62  }
    63  
    64  // testFlagDefn is the set of flags we process.
    65  var testFlagDefn = []*testFlagSpec{
    66  	// local.
    67  	{name: "c", boolVar: &testC},
    68  	{name: "cover", boolVar: &testCover},
    69  	{name: "coverpkg"},
    70  	{name: "o"},
    71  
    72  	// build flags.
    73  	{name: "a", boolVar: &buildA},
    74  	{name: "n", boolVar: &buildN},
    75  	{name: "p"},
    76  	{name: "x", boolVar: &buildX},
    77  	{name: "i", boolVar: &buildI},
    78  	{name: "work", boolVar: &buildWork},
    79  	{name: "ccflags"},
    80  	{name: "gcflags"},
    81  	{name: "exec"},
    82  	{name: "ldflags"},
    83  	{name: "gccgoflags"},
    84  	{name: "tags"},
    85  	{name: "compiler"},
    86  	{name: "race", boolVar: &buildRace},
    87  	{name: "installsuffix"},
    88  
    89  	// passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v.
    90  	{name: "bench", passToTest: true},
    91  	{name: "benchmem", boolVar: new(bool), passToTest: true},
    92  	{name: "benchtime", passToTest: true},
    93  	{name: "covermode"},
    94  	{name: "coverprofile", passToTest: true},
    95  	{name: "cpu", passToTest: true},
    96  	{name: "cpuprofile", passToTest: true},
    97  	{name: "memprofile", passToTest: true},
    98  	{name: "memprofilerate", passToTest: true},
    99  	{name: "blockprofile", passToTest: true},
   100  	{name: "blockprofilerate", passToTest: true},
   101  	{name: "outputdir", passToTest: true},
   102  	{name: "parallel", passToTest: true},
   103  	{name: "run", passToTest: true},
   104  	{name: "short", boolVar: new(bool), passToTest: true},
   105  	{name: "timeout", passToTest: true},
   106  	{name: "v", boolVar: &testV, passToTest: true},
   107  }
   108  
   109  // testFlags processes the command line, grabbing -x and -c, rewriting known flags
   110  // to have "test" before them, and reading the command line for the 6.out.
   111  // Unfortunately for us, we need to do our own flag processing because go test
   112  // grabs some flags but otherwise its command line is just a holding place for
   113  // pkg.test's arguments.
   114  // We allow known flags both before and after the package name list,
   115  // to allow both
   116  //	go test fmt -custom-flag-for-fmt-test
   117  //	go test -x math
   118  func testFlags(args []string) (packageNames, passToTest []string) {
   119  	inPkg := false
   120  	outputDir := ""
   121  	for i := 0; i < len(args); i++ {
   122  		if !strings.HasPrefix(args[i], "-") {
   123  			if !inPkg && packageNames == nil {
   124  				// First package name we've seen.
   125  				inPkg = true
   126  			}
   127  			if inPkg {
   128  				packageNames = append(packageNames, args[i])
   129  				continue
   130  			}
   131  		}
   132  
   133  		if inPkg {
   134  			// Found an argument beginning with "-"; end of package list.
   135  			inPkg = false
   136  		}
   137  
   138  		f, value, extraWord := testFlag(args, i)
   139  		if f == nil {
   140  			// This is a flag we do not know; we must assume
   141  			// that any args we see after this might be flag
   142  			// arguments, not package names.
   143  			inPkg = false
   144  			if packageNames == nil {
   145  				// make non-nil: we have seen the empty package list
   146  				packageNames = []string{}
   147  			}
   148  			passToTest = append(passToTest, args[i])
   149  			continue
   150  		}
   151  		var err error
   152  		switch f.name {
   153  		// bool flags.
   154  		case "a", "c", "i", "n", "x", "v", "race", "cover", "work":
   155  			setBoolFlag(f.boolVar, value)
   156  		case "o":
   157  			testO = value
   158  			testNeedBinary = true
   159  		case "p":
   160  			setIntFlag(&buildP, value)
   161  		case "exec":
   162  			execCmd, err = splitQuotedFields(value)
   163  			if err != nil {
   164  				fatalf("invalid flag argument for -%s: %v", f.name, err)
   165  			}
   166  		case "ccflags":
   167  			buildCcflags, err = splitQuotedFields(value)
   168  			if err != nil {
   169  				fatalf("invalid flag argument for -%s: %v", f.name, err)
   170  			}
   171  		case "gcflags":
   172  			buildGcflags, err = splitQuotedFields(value)
   173  			if err != nil {
   174  				fatalf("invalid flag argument for -%s: %v", f.name, err)
   175  			}
   176  		case "ldflags":
   177  			buildLdflags, err = splitQuotedFields(value)
   178  			if err != nil {
   179  				fatalf("invalid flag argument for -%s: %v", f.name, err)
   180  			}
   181  		case "gccgoflags":
   182  			buildGccgoflags, err = splitQuotedFields(value)
   183  			if err != nil {
   184  				fatalf("invalid flag argument for -%s: %v", f.name, err)
   185  			}
   186  		case "tags":
   187  			buildContext.BuildTags = strings.Fields(value)
   188  		case "compiler":
   189  			buildCompiler{}.Set(value)
   190  		case "bench":
   191  			// record that we saw the flag; don't care about the value
   192  			testBench = true
   193  		case "timeout":
   194  			testTimeout = value
   195  		case "blockprofile", "cpuprofile", "memprofile":
   196  			testProfile = true
   197  			testNeedBinary = true
   198  		case "coverpkg":
   199  			testCover = true
   200  			if value == "" {
   201  				testCoverPaths = nil
   202  			} else {
   203  				testCoverPaths = strings.Split(value, ",")
   204  			}
   205  		case "coverprofile":
   206  			testCover = true
   207  			testProfile = true
   208  		case "covermode":
   209  			switch value {
   210  			case "set", "count", "atomic":
   211  				testCoverMode = value
   212  			default:
   213  				fatalf("invalid flag argument for -cover: %q", value)
   214  			}
   215  			testCover = true
   216  		case "outputdir":
   217  			outputDir = value
   218  		}
   219  		if extraWord {
   220  			i++
   221  		}
   222  		if f.passToTest {
   223  			passToTest = append(passToTest, "-test."+f.name+"="+value)
   224  		}
   225  	}
   226  
   227  	if testCoverMode == "" {
   228  		testCoverMode = "set"
   229  		if buildRace {
   230  			// Default coverage mode is atomic when -race is set.
   231  			testCoverMode = "atomic"
   232  		}
   233  	}
   234  
   235  	// Tell the test what directory we're running in, so it can write the profiles there.
   236  	if testProfile && outputDir == "" {
   237  		dir, err := os.Getwd()
   238  		if err != nil {
   239  			fatalf("error from os.Getwd: %s", err)
   240  		}
   241  		passToTest = append(passToTest, "-test.outputdir", dir)
   242  	}
   243  	return
   244  }
   245  
   246  // testFlag sees if argument i is a known flag and returns its definition, value, and whether it consumed an extra word.
   247  func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool) {
   248  	arg := args[i]
   249  	if strings.HasPrefix(arg, "--") { // reduce two minuses to one
   250  		arg = arg[1:]
   251  	}
   252  	switch arg {
   253  	case "-?", "-h", "-help":
   254  		usage()
   255  	}
   256  	if arg == "" || arg[0] != '-' {
   257  		return
   258  	}
   259  	name := arg[1:]
   260  	// If there's already "test.", drop it for now.
   261  	name = strings.TrimPrefix(name, "test.")
   262  	equals := strings.Index(name, "=")
   263  	if equals >= 0 {
   264  		value = name[equals+1:]
   265  		name = name[:equals]
   266  	}
   267  	for _, f = range testFlagDefn {
   268  		if name == f.name {
   269  			// Booleans are special because they have modes -x, -x=true, -x=false.
   270  			if f.boolVar != nil {
   271  				if equals < 0 { // otherwise, it's been set and will be verified in setBoolFlag
   272  					value = "true"
   273  				} else {
   274  					// verify it parses
   275  					setBoolFlag(new(bool), value)
   276  				}
   277  			} else { // Non-booleans must have a value.
   278  				extra = equals < 0
   279  				if extra {
   280  					if i+1 >= len(args) {
   281  						testSyntaxError("missing argument for flag " + f.name)
   282  					}
   283  					value = args[i+1]
   284  				}
   285  			}
   286  			if f.present && !f.multiOK {
   287  				testSyntaxError(f.name + " flag may be set only once")
   288  			}
   289  			f.present = true
   290  			return
   291  		}
   292  	}
   293  	f = nil
   294  	return
   295  }
   296  
   297  // setBoolFlag sets the addressed boolean to the value.
   298  func setBoolFlag(flag *bool, value string) {
   299  	x, err := strconv.ParseBool(value)
   300  	if err != nil {
   301  		testSyntaxError("illegal bool flag value " + value)
   302  	}
   303  	*flag = x
   304  }
   305  
   306  // setIntFlag sets the addressed integer to the value.
   307  func setIntFlag(flag *int, value string) {
   308  	x, err := strconv.Atoi(value)
   309  	if err != nil {
   310  		testSyntaxError("illegal int flag value " + value)
   311  	}
   312  	*flag = x
   313  }
   314  
   315  func testSyntaxError(msg string) {
   316  	fmt.Fprintf(os.Stderr, "go test: %s\n", msg)
   317  	fmt.Fprintf(os.Stderr, `run "go help test" or "go help testflag" for more information`+"\n")
   318  	os.Exit(2)
   319  }