...

Source file src/pkg/cmd/go/internal/test/testflag.go

     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 test
     6	
     7	import (
     8		"flag"
     9		"os"
    10		"strings"
    11	
    12		"cmd/go/internal/base"
    13		"cmd/go/internal/cfg"
    14		"cmd/go/internal/cmdflag"
    15		"cmd/go/internal/str"
    16		"cmd/go/internal/work"
    17	)
    18	
    19	const cmd = "test"
    20	
    21	// The flag handling part of go test is large and distracting.
    22	// We can't use the flag package because some of the flags from
    23	// our command line are for us, and some are for 6.out, and
    24	// some are for both.
    25	
    26	// testFlagDefn is the set of flags we process.
    27	var testFlagDefn = []*cmdflag.Defn{
    28		// local.
    29		{Name: "c", BoolVar: &testC},
    30		{Name: "i", BoolVar: &cfg.BuildI},
    31		{Name: "o"},
    32		{Name: "cover", BoolVar: &testCover},
    33		{Name: "covermode"},
    34		{Name: "coverpkg"},
    35		{Name: "exec"},
    36		{Name: "json", BoolVar: &testJSON},
    37		{Name: "vet"},
    38	
    39		// Passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v.
    40		{Name: "bench", PassToTest: true},
    41		{Name: "benchmem", BoolVar: new(bool), PassToTest: true},
    42		{Name: "benchtime", PassToTest: true},
    43		{Name: "blockprofile", PassToTest: true},
    44		{Name: "blockprofilerate", PassToTest: true},
    45		{Name: "count", PassToTest: true},
    46		{Name: "coverprofile", PassToTest: true},
    47		{Name: "cpu", PassToTest: true},
    48		{Name: "cpuprofile", PassToTest: true},
    49		{Name: "failfast", BoolVar: new(bool), PassToTest: true},
    50		{Name: "list", PassToTest: true},
    51		{Name: "memprofile", PassToTest: true},
    52		{Name: "memprofilerate", PassToTest: true},
    53		{Name: "mutexprofile", PassToTest: true},
    54		{Name: "mutexprofilefraction", PassToTest: true},
    55		{Name: "outputdir", PassToTest: true},
    56		{Name: "parallel", PassToTest: true},
    57		{Name: "run", PassToTest: true},
    58		{Name: "short", BoolVar: new(bool), PassToTest: true},
    59		{Name: "timeout", PassToTest: true},
    60		{Name: "trace", PassToTest: true},
    61		{Name: "v", BoolVar: &testV, PassToTest: true},
    62	}
    63	
    64	// add build flags to testFlagDefn
    65	func init() {
    66		cmdflag.AddKnownFlags("test", testFlagDefn)
    67		var cmd base.Command
    68		work.AddBuildFlags(&cmd)
    69		cmd.Flag.VisitAll(func(f *flag.Flag) {
    70			if f.Name == "v" {
    71				// test overrides the build -v flag
    72				return
    73			}
    74			testFlagDefn = append(testFlagDefn, &cmdflag.Defn{
    75				Name:  f.Name,
    76				Value: f.Value,
    77			})
    78		})
    79	}
    80	
    81	// testFlags processes the command line, grabbing -x and -c, rewriting known flags
    82	// to have "test" before them, and reading the command line for the 6.out.
    83	// Unfortunately for us, we need to do our own flag processing because go test
    84	// grabs some flags but otherwise its command line is just a holding place for
    85	// pkg.test's arguments.
    86	// We allow known flags both before and after the package name list,
    87	// to allow both
    88	//	go test fmt -custom-flag-for-fmt-test
    89	//	go test -x math
    90	func testFlags(usage func(), args []string) (packageNames, passToTest []string) {
    91		args = str.StringList(cmdflag.FindGOFLAGS(testFlagDefn), args)
    92		inPkg := false
    93		var explicitArgs []string
    94		for i := 0; i < len(args); i++ {
    95			if !strings.HasPrefix(args[i], "-") {
    96				if !inPkg && packageNames == nil {
    97					// First package name we've seen.
    98					inPkg = true
    99				}
   100				if inPkg {
   101					packageNames = append(packageNames, args[i])
   102					continue
   103				}
   104			}
   105	
   106			if inPkg {
   107				// Found an argument beginning with "-"; end of package list.
   108				inPkg = false
   109			}
   110	
   111			f, value, extraWord := cmdflag.Parse(cmd, usage, testFlagDefn, args, i)
   112			if f == nil {
   113				// This is a flag we do not know; we must assume
   114				// that any args we see after this might be flag
   115				// arguments, not package names.
   116				inPkg = false
   117				if packageNames == nil {
   118					// make non-nil: we have seen the empty package list
   119					packageNames = []string{}
   120				}
   121				if args[i] == "-args" || args[i] == "--args" {
   122					// -args or --args signals that everything that follows
   123					// should be passed to the test.
   124					explicitArgs = args[i+1:]
   125					break
   126				}
   127				passToTest = append(passToTest, args[i])
   128				continue
   129			}
   130			if f.Value != nil {
   131				if err := f.Value.Set(value); err != nil {
   132					base.Fatalf("invalid flag argument for -%s: %v", f.Name, err)
   133				}
   134			} else {
   135				// Test-only flags.
   136				// Arguably should be handled by f.Value, but aren't.
   137				switch f.Name {
   138				// bool flags.
   139				case "c", "i", "v", "cover", "json":
   140					cmdflag.SetBool(cmd, f.BoolVar, value)
   141					if f.Name == "json" && testJSON {
   142						passToTest = append(passToTest, "-test.v=true")
   143					}
   144				case "o":
   145					testO = value
   146					testNeedBinary = true
   147				case "exec":
   148					xcmd, err := str.SplitQuotedFields(value)
   149					if err != nil {
   150						base.Fatalf("invalid flag argument for -%s: %v", f.Name, err)
   151					}
   152					work.ExecCmd = xcmd
   153				case "bench":
   154					// record that we saw the flag; don't care about the value
   155					testBench = true
   156				case "list":
   157					testList = true
   158				case "timeout":
   159					testTimeout = value
   160				case "blockprofile", "cpuprofile", "memprofile", "mutexprofile":
   161					testProfile = "-" + f.Name
   162					testNeedBinary = true
   163				case "trace":
   164					testProfile = "-trace"
   165				case "coverpkg":
   166					testCover = true
   167					if value == "" {
   168						testCoverPaths = nil
   169					} else {
   170						testCoverPaths = strings.Split(value, ",")
   171					}
   172				case "coverprofile":
   173					testCover = true
   174					testCoverProfile = value
   175				case "covermode":
   176					switch value {
   177					case "set", "count", "atomic":
   178						testCoverMode = value
   179					default:
   180						base.Fatalf("invalid flag argument for -covermode: %q", value)
   181					}
   182					testCover = true
   183				case "outputdir":
   184					testOutputDir = value
   185				case "vet":
   186					testVetList = value
   187				}
   188			}
   189			if extraWord {
   190				i++
   191			}
   192			if f.PassToTest {
   193				passToTest = append(passToTest, "-test."+f.Name+"="+value)
   194			}
   195		}
   196	
   197		if testCoverMode == "" {
   198			testCoverMode = "set"
   199			if cfg.BuildRace {
   200				// Default coverage mode is atomic when -race is set.
   201				testCoverMode = "atomic"
   202			}
   203		}
   204	
   205		testVetExplicit = testVetList != ""
   206		if testVetList != "" && testVetList != "off" {
   207			if strings.Contains(testVetList, "=") {
   208				base.Fatalf("-vet argument cannot contain equal signs")
   209			}
   210			if strings.Contains(testVetList, " ") {
   211				base.Fatalf("-vet argument is comma-separated list, cannot contain spaces")
   212			}
   213			list := strings.Split(testVetList, ",")
   214			for i, arg := range list {
   215				list[i] = "-" + arg
   216			}
   217			testVetFlags = list
   218		}
   219	
   220		if cfg.BuildRace && testCoverMode != "atomic" {
   221			base.Fatalf(`-covermode must be "atomic", not %q, when -race is enabled`, testCoverMode)
   222		}
   223	
   224		// Tell the test what directory we're running in, so it can write the profiles there.
   225		if testProfile != "" && testOutputDir == "" {
   226			dir, err := os.Getwd()
   227			if err != nil {
   228				base.Fatalf("error from os.Getwd: %s", err)
   229			}
   230			passToTest = append(passToTest, "-test.outputdir", dir)
   231		}
   232	
   233		passToTest = append(passToTest, explicitArgs...)
   234		return
   235	}
   236	

View as plain text