github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/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    -cpu="": passes -test.cpu to test
    31    -cpuprofile="": passes -test.cpuprofile to test
    32    -memprofile="": passes -test.memprofile to test
    33    -memprofilerate=0: passes -test.memprofilerate to test
    34    -blockprofile="": pases -test.blockprofile to test
    35    -blockprofilerate=0: passes -test.blockprofilerate to test
    36    -parallel=0: passes -test.parallel to test
    37    -run="": passes -test.run to test
    38    -short=false: passes -test.short to test
    39    -timeout=0: passes -test.timeout to test
    40    -v=false: passes -test.v to test
    41  `
    42  
    43  // usage prints a usage message and exits.
    44  func testUsage() {
    45  	fmt.Fprint(os.Stderr, usageMessage)
    46  	setExitStatus(2)
    47  	exit()
    48  }
    49  
    50  // testFlagSpec defines a flag we know about.
    51  type testFlagSpec struct {
    52  	name       string
    53  	boolVar    *bool
    54  	passToTest bool // pass to Test
    55  	multiOK    bool // OK to have multiple instances
    56  	present    bool // flag has been seen
    57  }
    58  
    59  // testFlagDefn is the set of flags we process.
    60  var testFlagDefn = []*testFlagSpec{
    61  	// local.
    62  	{name: "c", boolVar: &testC},
    63  	{name: "file", multiOK: true},
    64  	{name: "i", boolVar: &testI},
    65  
    66  	// build flags.
    67  	{name: "a", boolVar: &buildA},
    68  	{name: "n", boolVar: &buildN},
    69  	{name: "p"},
    70  	{name: "x", boolVar: &buildX},
    71  	{name: "work", boolVar: &buildWork},
    72  	{name: "gcflags"},
    73  	{name: "ldflags"},
    74  	{name: "gccgoflags"},
    75  	{name: "tags"},
    76  	{name: "compiler"},
    77  	{name: "race", boolVar: &buildRace},
    78  
    79  	// passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v.
    80  	{name: "bench", passToTest: true},
    81  	{name: "benchmem", boolVar: new(bool), passToTest: true},
    82  	{name: "benchtime", passToTest: true},
    83  	{name: "cpu", passToTest: true},
    84  	{name: "cpuprofile", passToTest: true},
    85  	{name: "memprofile", passToTest: true},
    86  	{name: "memprofilerate", passToTest: true},
    87  	{name: "blockprofile", passToTest: true},
    88  	{name: "blockprofilerate", passToTest: true},
    89  	{name: "parallel", passToTest: true},
    90  	{name: "run", passToTest: true},
    91  	{name: "short", boolVar: new(bool), passToTest: true},
    92  	{name: "timeout", passToTest: true},
    93  	{name: "v", boolVar: &testV, passToTest: true},
    94  }
    95  
    96  // testFlags processes the command line, grabbing -x and -c, rewriting known flags
    97  // to have "test" before them, and reading the command line for the 6.out.
    98  // Unfortunately for us, we need to do our own flag processing because go test
    99  // grabs some flags but otherwise its command line is just a holding place for
   100  // pkg.test's arguments.
   101  // We allow known flags both before and after the package name list,
   102  // to allow both
   103  //	go test fmt -custom-flag-for-fmt-test
   104  //	go test -x math
   105  func testFlags(args []string) (packageNames, passToTest []string) {
   106  	inPkg := false
   107  	for i := 0; i < len(args); i++ {
   108  		if !strings.HasPrefix(args[i], "-") {
   109  			if !inPkg && packageNames == nil {
   110  				// First package name we've seen.
   111  				inPkg = true
   112  			}
   113  			if inPkg {
   114  				packageNames = append(packageNames, args[i])
   115  				continue
   116  			}
   117  		}
   118  
   119  		if inPkg {
   120  			// Found an argument beginning with "-"; end of package list.
   121  			inPkg = false
   122  		}
   123  
   124  		f, value, extraWord := testFlag(args, i)
   125  		if f == nil {
   126  			// This is a flag we do not know; we must assume
   127  			// that any args we see after this might be flag
   128  			// arguments, not package names.
   129  			inPkg = false
   130  			if packageNames == nil {
   131  				// make non-nil: we have seen the empty package list
   132  				packageNames = []string{}
   133  			}
   134  			passToTest = append(passToTest, args[i])
   135  			continue
   136  		}
   137  		var err error
   138  		switch f.name {
   139  		// bool flags.
   140  		case "a", "c", "i", "n", "x", "v", "work", "race":
   141  			setBoolFlag(f.boolVar, value)
   142  		case "p":
   143  			setIntFlag(&buildP, value)
   144  		case "gcflags":
   145  			buildGcflags, err = splitQuotedFields(value)
   146  			if err != nil {
   147  				fatalf("invalid flag argument for -%s: %v", f.name, err)
   148  			}
   149  		case "ldflags":
   150  			buildLdflags, err = splitQuotedFields(value)
   151  			if err != nil {
   152  				fatalf("invalid flag argument for -%s: %v", f.name, err)
   153  			}
   154  		case "gccgoflags":
   155  			buildGccgoflags, err = splitQuotedFields(value)
   156  			if err != nil {
   157  				fatalf("invalid flag argument for -%s: %v", f.name, err)
   158  			}
   159  		case "tags":
   160  			buildContext.BuildTags = strings.Fields(value)
   161  		case "compiler":
   162  			buildCompiler{}.Set(value)
   163  		case "file":
   164  			testFiles = append(testFiles, value)
   165  		case "bench":
   166  			// record that we saw the flag; don't care about the value
   167  			testBench = true
   168  		case "timeout":
   169  			testTimeout = value
   170  		case "blockprofile", "cpuprofile", "memprofile":
   171  			testProfile = true
   172  		}
   173  		if extraWord {
   174  			i++
   175  		}
   176  		if f.passToTest {
   177  			passToTest = append(passToTest, "-test."+f.name+"="+value)
   178  		}
   179  	}
   180  	return
   181  }
   182  
   183  // testFlag sees if argument i is a known flag and returns its definition, value, and whether it consumed an extra word.
   184  func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool) {
   185  	arg := args[i]
   186  	if strings.HasPrefix(arg, "--") { // reduce two minuses to one
   187  		arg = arg[1:]
   188  	}
   189  	switch arg {
   190  	case "-?", "-h", "-help":
   191  		usage()
   192  	}
   193  	if arg == "" || arg[0] != '-' {
   194  		return
   195  	}
   196  	name := arg[1:]
   197  	// If there's already "test.", drop it for now.
   198  	name = strings.TrimPrefix(name, "test.")
   199  	equals := strings.Index(name, "=")
   200  	if equals >= 0 {
   201  		value = name[equals+1:]
   202  		name = name[:equals]
   203  	}
   204  	for _, f = range testFlagDefn {
   205  		if name == f.name {
   206  			// Booleans are special because they have modes -x, -x=true, -x=false.
   207  			if f.boolVar != nil {
   208  				if equals < 0 { // otherwise, it's been set and will be verified in setBoolFlag
   209  					value = "true"
   210  				} else {
   211  					// verify it parses
   212  					setBoolFlag(new(bool), value)
   213  				}
   214  			} else { // Non-booleans must have a value.
   215  				extra = equals < 0
   216  				if extra {
   217  					if i+1 >= len(args) {
   218  						usage()
   219  					}
   220  					value = args[i+1]
   221  				}
   222  			}
   223  			if f.present && !f.multiOK {
   224  				usage()
   225  			}
   226  			f.present = true
   227  			return
   228  		}
   229  	}
   230  	f = nil
   231  	return
   232  }
   233  
   234  // setBoolFlag sets the addressed boolean to the value.
   235  func setBoolFlag(flag *bool, value string) {
   236  	x, err := strconv.ParseBool(value)
   237  	if err != nil {
   238  		fmt.Fprintf(os.Stderr, "go test: illegal bool flag value %s\n", value)
   239  		usage()
   240  	}
   241  	*flag = x
   242  }
   243  
   244  // setIntFlag sets the addressed integer to the value.
   245  func setIntFlag(flag *int, value string) {
   246  	x, err := strconv.Atoi(value)
   247  	if err != nil {
   248  		fmt.Fprintf(os.Stderr, "go test: illegal int flag value %s\n", value)
   249  		usage()
   250  	}
   251  	*flag = x
   252  }