blob: 07133035e9a25d51cf6f0a59c3739716cfb285ca [file] [log] [blame]
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
// The flag handling part of go test is large and distracting.
// We can't use the flag package because some of the flags from
// our command line are for us, and some are for 6.out, and
// some are for both.
var usageMessage = `Usage of go test:
-c=false: compile but do not run the test binary
-file=file_test.go: specify file to use for tests;
use multiple times for multiple files
-x=false: print command lines as they are executed
// These flags can be passed with or without a "test." prefix: -v or -test.v.
-bench="": passes -test.bench to test
-benchtime=1: passes -test.benchtime to test
-cpu="": passes -test.cpu to test
-cpuprofile="": passes -test.cpuprofile to test
-memprofile="": passes -test.memprofile to test
-memprofilerate=0: passes -test.memprofilerate to test
-parallel=0: passes -test.parallel to test
-run="": passes to test
-short=false: passes -test.short to test
-timeout=0: passes -test.timeout to test
-v=false: passes -test.v to test
// usage prints a usage message and exits.
func testUsage() {
fmt.Fprint(os.Stderr, usageMessage)
exitStatus = 2
// testFlagSpec defines a flag we know about.
type testFlagSpec struct {
name string
isBool bool
passToTest bool // pass to Test
multiOK bool // OK to have multiple instances
present bool // flag has been seen
// testFlagDefn is the set of flags we process.
var testFlagDefn = []*testFlagSpec{
// local.
{name: "c", isBool: true},
{name: "file", multiOK: true},
{name: "x", isBool: true},
// passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v.
{name: "bench", passToTest: true},
{name: "benchtime", passToTest: true},
{name: "cpu", passToTest: true},
{name: "cpuprofile", passToTest: true},
{name: "memprofile", passToTest: true},
{name: "memprofilerate", passToTest: true},
{name: "parallel", passToTest: true},
{name: "run", passToTest: true},
{name: "short", isBool: true, passToTest: true},
{name: "timeout", passToTest: true},
{name: "v", isBool: true, passToTest: true},
// testFlags processes the command line, grabbing -x and -c, rewriting known flags
// to have "test" before them, and reading the command line for the 6.out.
// Unfortunately for us, we need to do our own flag processing because go test
// grabs some flags but otherwise its command line is just a holding place for
// test.out's arguments.
// We allow known flags both before and after the package name list,
// to allow both
// go test fmt -custom-flag-for-fmt-test
// go test -x math
func testFlags(args []string) (packageNames, passToTest []string) {
inPkg := false
for i := 0; i < len(args); i++ {
if !strings.HasPrefix(args[i], "-") {
if !inPkg && packageNames == nil {
// First package name we've seen.
inPkg = true
if inPkg {
packageNames = append(packageNames, args[i])
if inPkg {
// Found an argument beginning with "-"; end of package list.
inPkg = false
f, value, extraWord := testFlag(args, i)
if f == nil {
// This is a flag we do not know; we must assume
// that any args we see after this might be flag
// arguments, not package names.
inPkg = false
if packageNames == nil {
// make non-nil: we have seen the empty package list
packageNames = []string{}
passToTest = append(passToTest, args[i])
switch {
case "c":
setBoolFlag(&testC, value)
case "x":
setBoolFlag(&testX, value)
case "v":
setBoolFlag(&testV, value)
case "file":
testFiles = append(testFiles, value)
if extraWord {
if f.passToTest {
passToTest = append(passToTest, "-test.""="+value)
// testFlag sees if argument i is a known flag and returns its definition, value, and whether it consumed an extra word.
func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool) {
arg := args[i]
if strings.HasPrefix(arg, "--") { // reduce two minuses to one
arg = arg[1:]
switch arg {
case "-?", "-h", "-help":
if arg == "" || arg[0] != '-' {
name := arg[1:]
// If there's already "test.", drop it for now.
if strings.HasPrefix(name, "test.") {
name = name[5:]
equals := strings.Index(name, "=")
if equals >= 0 {
value = name[equals+1:]
name = name[:equals]
for _, f = range testFlagDefn {
if name == {
// Booleans are special because they have modes -x, -x=true, -x=false.
if f.isBool {
if equals < 0 { // otherwise, it's been set and will be verified in setBoolFlag
value = "true"
} else {
// verify it parses
setBoolFlag(new(bool), value)
} else { // Non-booleans must have a value.
extra = equals < 0
if extra {
if i+1 >= len(args) {
value = args[i+1]
if f.present && !f.multiOK {
f.present = true
f = nil
// setBoolFlag sets the addressed boolean to the value.
func setBoolFlag(flag *bool, value string) {
x, err := strconv.ParseBool(value)
if err != nil {
fmt.Fprintf(os.Stderr, "go test: illegal bool flag value %s\n", value)
*flag = x