]> Cypherpunks repositories - gostls13.git/commitdiff
go: implement test command
authorRuss Cox <rsc@golang.org>
Fri, 16 Dec 2011 04:51:04 +0000 (23:51 -0500)
committerRuss Cox <rsc@golang.org>
Fri, 16 Dec 2011 04:51:04 +0000 (23:51 -0500)
Gotest tries to build things, for which it invokes make,
and it was too hard to coordinate go invoking gotest
invoking go to build the test binary, so put all the code
here instead.  Gotest will be deleted once we switch.

The only code that really made sense to copy verbatim
was the flag parsing.

This remains a work in progress.  There are still plenty
of things to clean up and make better, but this is a good
checkpoint.  It can run all the tests in the tree (except
runtime, which it can't build yet).

$ go test all -short
ok   archive/tar
ok   archive/zip
ok   bufio
?    builtin [no test files]
ok   bytes
ok   compress/bzip2
ok   compress/flate
ok   compress/gzip
ok   compress/lzw
ok   compress/zlib
ok   container/heap
ok   container/list
ok   container/ring
?    crypto [no test files]
ok   crypto/aes
ok   crypto/bcrypt
ok   crypto/blowfish
ok   crypto/cast5
ok   crypto/cipher
ok   crypto/des
ok   crypto/dsa
ok   crypto/ecdsa
ok   crypto/elliptic
ok   crypto/hmac
ok   crypto/md4
ok   crypto/md5
ok   crypto/ocsp
ok   crypto/openpgp
ok   crypto/openpgp/armor
ok   crypto/openpgp/elgamal
?    crypto/openpgp/error [no test files]
ok   crypto/openpgp/packet
ok   crypto/openpgp/s2k
ok   crypto/rand
ok   crypto/rc4
ok   crypto/ripemd160
ok   crypto/rsa
ok   crypto/sha1
ok   crypto/sha256
ok   crypto/sha512
ok   crypto/subtle
ok   crypto/tls
ok   crypto/twofish
ok   crypto/x509
?    crypto/x509/pkix [no test files]
ok   crypto/xtea
ok   debug/dwarf
ok   debug/elf
ok   debug/gosym
ok   debug/macho
ok   debug/pe
ok   encoding/ascii85
ok   encoding/asn1
ok   encoding/base32
ok   encoding/base64
ok   encoding/binary
ok   encoding/csv
ok   encoding/git85
ok   encoding/gob
ok   encoding/hex
ok   encoding/json
ok   encoding/pem
ok   encoding/xml
ok   errors
ok   exp/ebnf
?    exp/ebnflint [no test files]
ok   exp/gotype
ok   exp/norm
ok   exp/spdy
ok   exp/sql
ok   exp/sql/driver
ok   exp/ssh
ok   exp/types
ok   expvar
ok   flag
ok   fmt
ok   go/ast
ok   go/build
ok   go/doc
ok   go/parser
ok   go/printer
ok   go/scanner
ok   go/token
?    hash [no test files]
ok   hash/adler32
ok   hash/crc32
ok   hash/crc64
ok   hash/fnv
ok   html
ok   html/template
ok   image
?    image/bmp [no test files]
?    image/color [no test files]
ok   image/draw
?    image/gif [no test files]
ok   image/jpeg
ok   image/png
ok   image/tiff
ok   image/ycbcr
ok   index/suffixarray
ok   io
ok   io/ioutil
ok   log
ok   log/syslog
ok   math
ok   math/big
ok   math/cmplx
ok   math/rand
ok   mime
ok   mime/multipart
ok   net
?    net/dict [no test files]
ok   net/http
ok   net/http/cgi
ok   net/http/fcgi
?    net/http/httptest [no test files]
ok   net/http/httputil
?    net/http/pprof [no test files]
ok   net/mail
ok   net/rpc
ok   net/rpc/jsonrpc
ok   net/smtp
ok   net/textproto
ok   net/url
ok   old/netchan
ok   old/regexp
ok   old/template
ok   os
ok   os/exec
ok   os/signal
ok   os/user
ok   patch
ok   path
ok   path/filepath
ok   reflect
ok   regexp
ok   regexp/syntax
# cd /Users/rsc/g/go/src/pkg/runtime; 6g -o /var/folders/mw/qfnx8hhd1_s9mm9wtbng0hw80000gn/T/go-build874847916/runtime_test/_obj/_go_.6 -p runtime_test -I /var/folders/mw/qfnx8hhd1_s9mm9wtbng0hw80000gn/T/go-build874847916 append_test.go chan_test.go closure_test.go gc_test.go mfinal_test.go proc_test.go sema_test.go softfloat64_test.go symtab_test.go
proc_test.go:87: undefined: runtime.Entersyscall
proc_test.go:88: undefined: runtime.Exitsyscall
proc_test.go:111: undefined: runtime.Entersyscall
proc_test.go:116: undefined: runtime.Exitsyscall
softfloat64_test.go:79: undefined: Fadd64
softfloat64_test.go:80: undefined: Fsub64
softfloat64_test.go:82: undefined: Fmul64
softfloat64_test.go:83: undefined: Fdiv64
softfloat64_test.go:94: undefined: F64to32
softfloat64_test.go:99: undefined: F32to64
softfloat64_test.go:99: too many errors

exit status 1
FAIL runtime [build failed]
?    runtime/cgo [no test files]
ok   runtime/debug
ok   runtime/pprof
ok   sort
ok   strconv
ok   strings
ok   sync
ok   sync/atomic
?    syscall [no test files]
?    testing [no test files]
?    testing/iotest [no test files]
ok   testing/quick
ok   testing/script
ok   text/scanner
ok   text/tabwriter
ok   text/template
ok   text/template/parse
ok   time
ok   unicode
ok   unicode/utf16
ok   unicode/utf8
?    unsafe [no test files]
ok   websocket
$

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5495055

src/cmd/go/Makefile
src/cmd/go/build.go
src/cmd/go/main.go
src/cmd/go/pkg.go
src/cmd/go/test.go
src/cmd/go/testflag.go [new file with mode: 0644]

index ba372fe9e901cdd76629a601622f19327d30f4bc..cef0ac2ccdad8f69a411c03f157fde85269f7039 100644 (file)
@@ -16,6 +16,7 @@ GOFILES=\
        pkg.go\
        run.go\
        test.go\
+       testflag.go\
        version.go\
        vet.go\
 
index 4d4b0363d618fbec6d1128af3fa570a7833cd9b4..ba9aa672b604889b1e58661b32d657be4c564742 100644 (file)
@@ -105,6 +105,7 @@ type builder struct {
        nflag       bool                 // the -n flag
        vflag       bool                 // the -v flag
        arch        string               // e.g., "6"
+       goroot      string               // the $GOROOT
        actionCache map[cacheKey]*action // a cache of already-constructed actions
 }
 
@@ -112,10 +113,12 @@ type builder struct {
 type action struct {
        f func(*builder, *action) error // the action itself
 
-       p      *Package  // the package this action works on
-       deps   []*action // actions that must happen before this one
-       done   bool      // whether the action is done (might have failed)
-       failed bool      // whether the action failed
+       p          *Package  // the package this action works on
+       deps       []*action // actions that must happen before this one
+       done       bool      // whether the action is done (might have failed)
+       failed     bool      // whether the action failed
+       pkgdir     string    // the -I or -L argument to use when importing this package
+       ignoreFail bool      // whether to run f even if dependencies fail
 
        // Results left for communication with other code.
        pkgobj string // the built .a file
@@ -143,6 +146,7 @@ func (b *builder) init(aflag, nflag, vflag bool) {
        b.nflag = nflag
        b.vflag = vflag
        b.actionCache = make(map[cacheKey]*action)
+       b.goroot = runtime.GOROOT()
 
        b.arch, err = build.ArchChar(build.DefaultContext.GOARCH)
        if err != nil {
@@ -209,12 +213,24 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action
                return a
        }
 
-       a = &action{p: p}
+       a = &action{p: p, pkgdir: p.t.PkgDir()}
+       if p.pkgdir != "" { // overrides p.t
+               a.pkgdir = p.pkgdir
+       }
+
        b.actionCache[key] = a
 
        switch mode {
        case modeBuild, modeInstall:
+               for _, p1 := range p.imports {
+                       a.deps = append(a.deps, b.action(depMode, depMode, p1))
+               }
+
                if !needInstall(p) && !b.aflag {
+                       // TODO: This is not right if the deps above
+                       // are not all no-ops too.  If fmt is up to date
+                       // wrt its own source files,  but strconv has
+                       // changed, then fmt is not up to date.
                        a.f = (*builder).nop
                        return a
                }
@@ -238,9 +254,6 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action
                }
 
                a.f = (*builder).build
-               for _, p1 := range p.imports {
-                       a.deps = append(a.deps, b.action(depMode, depMode, p1))
-               }
        }
 
        return a
@@ -288,8 +301,10 @@ func (b *builder) do(a *action) {
                b.do(a1)
                if a1.failed {
                        a.failed = true
-                       a.done = true
-                       return
+                       if !a.ignoreFail {
+                               a.done = true
+                               return
+                       }
                }
        }
        if err := a.f(b, a); err != nil {
@@ -303,10 +318,12 @@ func (b *builder) nop(a *action) error {
        return nil
 }
 
-// build is the action for building a single package.
+// build is the action for building a single package or command.
 func (b *builder) build(a *action) error {
        obj := filepath.Join(b.work, filepath.FromSlash(a.p.ImportPath+"/_obj")) + string(filepath.Separator)
-       a.pkgobj = filepath.Join(b.work, filepath.FromSlash(a.p.ImportPath+".a"))
+       if a.pkgobj == "" {
+               a.pkgobj = filepath.Join(b.work, filepath.FromSlash(a.p.ImportPath+".a"))
+       }
 
        // make build directory
        if err := b.mkdir(obj); err != nil {
@@ -332,11 +349,10 @@ func (b *builder) build(a *action) error {
        inc = append(inc, "-I", b.work)
        incMap := map[string]bool{}
        for _, a1 := range a.deps {
-               p1 := a1.p
-               if p1.t.Goroot {
+               pkgdir := a1.pkgdir
+               if pkgdir == build.Path[0].PkgDir() || pkgdir == "" {
                        continue
                }
-               pkgdir := p1.t.PkgDir()
                if !incMap[pkgdir] {
                        incMap[pkgdir] = true
                        inc = append(inc, "-I", pkgdir)
@@ -386,17 +402,14 @@ func (b *builder) build(a *action) error {
 
 // install is the action for installing a single package.
 func (b *builder) install(a *action) error {
-       if err := b.build(a); err != nil {
-               return err
-       }
-
+       a1 := a.deps[0]
        var src string
        var perm uint32
-       if a.pkgbin != "" {
-               src = a.pkgbin
+       if a1.pkgbin != "" {
+               src = a1.pkgbin
                perm = 0777
        } else {
-               src = a.pkgobj
+               src = a1.pkgobj
                perm = 0666
        }
 
index c5941133b6816d34cf7e74995e4bf28a70134b86..97db5abce866ded69d3aa2a09633ac610a9251b4 100644 (file)
@@ -36,6 +36,10 @@ type Command struct {
 
        // Flag is a set of flags specific to this command.
        Flag flag.FlagSet
+
+       // CustomFlags indicates that the command will do its own
+       // flag parsing.
+       CustomFlags bool
 }
 
 // Name returns the command's name: the first word in the usage line.
@@ -96,8 +100,12 @@ func main() {
        for _, cmd := range commands {
                if cmd.Name() == args[0] && cmd.Run != nil {
                        cmd.Flag.Usage = func() { cmd.Usage() }
-                       cmd.Flag.Parse(args[1:])
-                       args = cmd.Flag.Args()
+                       if cmd.CustomFlags {
+                               args = args[1:]
+                       } else {
+                               cmd.Flag.Parse(args[1:])
+                               args = cmd.Flag.Args()
+                       }
                        cmd.Run(cmd, args)
                        exit()
                        return
@@ -209,6 +217,8 @@ func errorf(format string, args ...interface{}) {
        exitStatus = 1
 }
 
+var logf = log.Printf
+
 func exitIfErrors() {
        if exitStatus != 0 {
                exit()
index cc21842e5a965d52b165cc21b8f63b9f2fc3c352..e9fb3bf3c03e50a29ed551b707813d79ea627c44 100644 (file)
@@ -38,6 +38,7 @@ type Package struct {
 
        // Unexported fields are not part of the public API.
        t       *build.Tree
+       pkgdir  string
        info    *build.DirInfo
        imports []*Package
        gofiles []string // GoFiles+CgoFiles
index 1b5ded1e4850c64c7b59cfdd50218edc8e5f0ab4..2f02c0ce1dc404fe7ed888d1d1a7b9cfc9612928 100644 (file)
@@ -4,10 +4,32 @@
 
 package main
 
+import (
+       "bytes"
+       "fmt"
+       "go/ast"
+       "go/build"
+       "go/doc"
+       "go/parser"
+       "go/token"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "strings"
+       "text/template"
+       "unicode"
+       "unicode/utf8"
+)
+
+// Break init loop.
+func init() {
+       cmdTest.Run = runTest
+}
+
 var cmdTest = &Command{
-       Run:       runTest,
-       UsageLine: "test [importpath...] [-file a.go -file b.go ...] [-c] [-x] [flags for test binary]",
-       Short:     "test packages",
+       CustomFlags: true,
+       UsageLine:   "test [importpath...] [-file a.go -file b.go ...] [-c] [-x] [flags for test binary]",
+       Short:       "test packages",
        Long: `
 'Go test' automates testing the packages named by the import paths.
 It prints a summary of the test results in the format:
@@ -158,8 +180,385 @@ See the documentation of the testing package for more information.
                `,
 }
 
+// TODO(rsc): Rethink the flag handling.
+// It might be better to do
+//     go test [go-test-flags] [importpaths] [flags for test binary]
+// If there are no import paths then the two flag sections
+// run together, but we can deal with that.  Right now, 
+//     go test -x  (ok)
+//     go test -x math (NOT OK)
+//     go test math -x (ok)
+// which is weird, because the -x really does apply to go test, not to math.
+// It is also possible that -file can go away.
+// For now just use the gotest code.
+
+var (
+       testC     bool     // -c flag
+       testX     bool     // -x flag
+       testFiles []string // -file flag(s)  TODO: not respected
+       testArgs  []string
+)
+
 func runTest(cmd *Command, args []string) {
-       args = importPaths(args)
-       _ = args
-       panic("test not implemented")
+       // Determine which are the import paths
+       // (leading arguments not starting with -).
+       i := 0
+       for i < len(args) && !strings.HasPrefix(args[i], "-") {
+               i++
+       }
+       pkgs := packages(args[:i])
+       if len(pkgs) == 0 {
+               fatalf("no packages to test")
+       }
+
+       testArgs = testFlags(args[i:])
+       if testC && len(pkgs) != 1 {
+               fatalf("cannot use -c flag with multiple packages")
+       }
+
+       var b builder
+       b.init(false, false, testX)
+
+       var builds, runs []*action
+
+       // Prepare build + run actions for all packages being tested.
+       for _, p := range pkgs {
+               buildTest, runTest, err := b.test(p)
+               if err != nil {
+                       errorf("%s: %s", p, err)
+                       continue
+               }
+               if buildTest == nil {
+                       // no test at all
+                       continue
+               }
+               builds = append(builds, buildTest)
+               runs = append(runs, runTest)
+       }
+
+       // Build+run the tests one at a time in the order
+       // specified on the command line.
+       // May want to revisit when we parallelize things,
+       // although probably not for benchmark runs.
+       for i, a := range builds {
+               if i > 0 {
+                       // Make build of test i depend on
+                       // completing the run of test i-1.
+                       a.deps = append(a.deps, runs[i-1])
+               }
+       }
+
+       allRuns := &action{f: (*builder).nop, deps: runs}
+       b.do(allRuns)
+}
+
+func (b *builder) test(p *Package) (buildAction, runAction *action, err error) {
+       if len(p.info.TestGoFiles)+len(p.info.XTestGoFiles) == 0 {
+               return &action{f: (*builder).nop, p: p}, &action{f: (*builder).notest, p: p}, nil
+       }
+
+       // Build Package structs describing:
+       //      ptest - package + test files
+       //      pxtest - package of external test files
+       //      pmain - test.out binary
+       var ptest, pxtest, pmain *Package
+
+       // go/build does not distinguish the dependencies used
+       // by the TestGoFiles from the dependencies used by the
+       // XTestGoFiles, so we build one list and use it for both
+       // ptest and pxtest.  No harm done.
+       var imports []*Package
+       for _, path := range p.info.TestImports {
+               p1, err := loadPackage(path)
+               if err != nil {
+                       return nil, nil, err
+               }
+               imports = append(imports, p1)
+       }
+
+       // The ptest package needs to be importable under the
+       // same import path that p has, but we cannot put it in
+       // the usual place in the temporary tree, because then
+       // other tests will see it as the real package.
+       // Instead we make a _test directory under the import path
+       // and then repeat the import path there.  We tell the
+       // compiler and linker to look in that _test directory first.
+       //
+       // That is, if the package under test is unicode/utf8,
+       // then the normal place to write the package archive is
+       // $WORK/unicode/utf8.a, but we write the test package archive to
+       // $WORK/unicode/utf8/_test/unicode/utf8.a.
+       // We write the external test package archive to
+       // $WORK/unicode/utf8/_test/unicode/utf8_test.a.
+       testDir := filepath.Join(b.work, filepath.FromSlash(p.ImportPath+"/_test"))
+       ptestObj := filepath.Join(testDir, filepath.FromSlash(p.ImportPath+".a"))
+
+       // Create the directory for the .a files.
+       ptestDir, _ := filepath.Split(ptestObj)
+       if err := b.mkdir(ptestDir); err != nil {
+               return nil, nil, err
+       }
+       if err := writeTestmain(filepath.Join(testDir, "_testmain.go"), p); err != nil {
+               return nil, nil, err
+       }
+
+       // Test package.
+       if len(p.info.TestGoFiles) > 0 {
+               ptest = new(Package)
+               *ptest = *p
+               ptest.GoFiles = nil
+               ptest.GoFiles = append(ptest.GoFiles, p.GoFiles...)
+               ptest.GoFiles = append(ptest.GoFiles, p.info.TestGoFiles...)
+               ptest.targ = "" // must rebuild
+               ptest.Imports = p.info.TestImports
+               ptest.imports = imports
+               ptest.pkgdir = testDir
+       } else {
+               ptest = p
+       }
+
+       // External test package.
+       if len(p.info.XTestGoFiles) > 0 {
+               pxtest = &Package{
+                       Name:       p.Name + "_test",
+                       ImportPath: p.ImportPath + "_test",
+                       Dir:        p.Dir,
+                       GoFiles:    p.info.XTestGoFiles,
+                       Imports:    p.info.TestImports,
+                       t:          p.t,
+                       info:       &build.DirInfo{},
+                       imports:    imports,
+                       pkgdir:     testDir,
+               }
+       }
+
+       // Action for building test.out.
+       pmain = &Package{
+               Name:    "main",
+               Dir:     testDir,
+               GoFiles: []string{"_testmain.go"},
+               t:       p.t,
+               info:    &build.DirInfo{},
+               imports: []*Package{ptest},
+       }
+       if pxtest != nil {
+               pmain.imports = append(pmain.imports, pxtest)
+       }
+       pmainAction := b.action(modeBuild, modeBuild, pmain)
+       pmainAction.pkgbin = filepath.Join(testDir, "test.out")
+
+       if testC {
+               // -c flag: create action to copy binary to ./test.out.
+               pmain.targ = "test.out"
+               runAction = &action{
+                       f:    (*builder).install,
+                       deps: []*action{pmainAction},
+                       p:    pmain,
+               }
+       } else {
+               // run test
+               runAction = &action{
+                       f:          (*builder).runTest,
+                       deps:       []*action{pmainAction},
+                       p:          p,
+                       ignoreFail: true,
+               }
+       }
+
+       return pmainAction, runAction, nil
+}
+
+var pass = []byte("\nPASS\n")
+
+// runTest is the action for running a test binary.
+func (b *builder) runTest(a *action) error {
+       if b.nflag || b.vflag {
+               b.showcmd("%s", strings.Join(append([]string{a.deps[0].pkgbin}, testArgs...), " "))
+               if b.nflag {
+                       return nil
+               }
+       }
+
+       if a.failed {
+               // We were unable to build the binary.
+               a.failed = false
+               fmt.Printf("FAIL\t%s [build failed]\n", a.p.ImportPath)
+               exitStatus = 1
+               return nil
+       }
+
+       cmd := exec.Command(a.deps[0].pkgbin, testArgs...)
+       cmd.Dir = a.p.Dir
+       out, err := cmd.CombinedOutput()
+       if err == nil && (bytes.Equal(out, pass[1:]) || bytes.HasSuffix(out, pass)) {
+               fmt.Printf("ok  \t%s\n", a.p.ImportPath)
+               return nil
+       }
+
+       fmt.Printf("FAIL\t%s\n", a.p.ImportPath)
+       exitStatus = 1
+       if len(out) > 0 {
+               os.Stdout.Write(out)
+               // assume printing the test binary's exit status is superfluous
+       } else {
+               fmt.Printf("%s\n", err)
+       }
+       return nil
+}
+
+// notest is the action for testing a package with no test files.
+func (b *builder) notest(a *action) error {
+       fmt.Printf("?   \t%s [no test files]\n", a.p.ImportPath)
+       return nil
+}
+
+// isTest tells whether name looks like a test (or benchmark, according to prefix).
+// It is a Test (say) if there is a character after Test that is not a lower-case letter.
+// We don't want TesticularCancer.
+func isTest(name, prefix string) bool {
+       if !strings.HasPrefix(name, prefix) {
+               return false
+       }
+       if len(name) == len(prefix) { // "Test" is ok
+               return true
+       }
+       rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
+       return !unicode.IsLower(rune)
+}
+
+// writeTestmain writes the _testmain.go file for package p to
+// the file named out.
+func writeTestmain(out string, p *Package) error {
+       t := &testFuncs{
+               Package: p,
+               Info:    p.info,
+       }
+       for _, file := range p.info.TestGoFiles {
+               if err := t.load(filepath.Join(p.Dir, file), "_test", &t.NeedTest); err != nil {
+                       return err
+               }
+       }
+       for _, file := range p.info.XTestGoFiles {
+               if err := t.load(filepath.Join(p.Dir, file), "_xtest", &t.NeedXtest); err != nil {
+                       return err
+               }
+       }
+
+       f, err := os.Create(out)
+       if err != nil {
+               return err
+       }
+       defer f.Close()
+
+       if err := testmainTmpl.Execute(f, t); err != nil {
+               return err
+       }
+
+       return nil
+}
+
+type testFuncs struct {
+       Tests      []testFunc
+       Benchmarks []testFunc
+       Examples   []testFunc
+       Package    *Package
+       Info       *build.DirInfo
+       NeedTest   bool
+       NeedXtest  bool
+}
+
+type testFunc struct {
+       Package string // imported package name (_test or _xtest)
+       Name    string // function name
+       Output  string // output, for examples
+}
+
+var testFileSet = token.NewFileSet()
+
+func (t *testFuncs) load(filename, pkg string, seen *bool) error {
+       f, err := parser.ParseFile(testFileSet, filename, nil, parser.ParseComments)
+       if err != nil {
+               return err
+       }
+       for _, d := range f.Decls {
+               n, ok := d.(*ast.FuncDecl)
+               if !ok {
+                       continue
+               }
+               if n.Recv != nil {
+                       continue
+               }
+               name := n.Name.String()
+               switch {
+               case isTest(name, "Test"):
+                       t.Tests = append(t.Tests, testFunc{pkg, name, ""})
+                       *seen = true
+               case isTest(name, "Benchmark"):
+                       t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, ""})
+                       *seen = true
+               case isTest(name, "Example"):
+                       output := doc.CommentText(n.Doc)
+                       if output == "" {
+                               // Don't run examples with no output.
+                               continue
+                       }
+                       t.Examples = append(t.Examples, testFunc{pkg, name, output})
+                       *seen = true
+               }
+       }
+
+       return nil
+}
+
+var testmainTmpl = template.Must(template.New("main").Parse(`
+package main
+
+import (
+       "regexp"
+       "testing"
+
+{{if .NeedTest}}
+       _test {{.Package.ImportPath | printf "%q"}}
+{{end}}
+{{if .NeedXtest}}
+       _xtest {{.Package.ImportPath | printf "%s_test" | printf "%q"}}
+{{end}}
+)
+
+var tests = []testing.InternalTest{
+{{range .Tests}}
+       {"{{.Name}}", {{.Package}}.{{.Name}}},
+{{end}}
+}
+
+var benchmarks = []testing.InternalBenchmark{
+{{range .Benchmarks}}
+       {"{{.Name}}", {{.Package}}.{{.Name}}},
+{{end}}
+}
+
+var examples = []testing.InternalExample{
+{{range .Examples}}
+       {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}},
+{{end}}
+}
+
+var matchPat string
+var matchRe *regexp.Regexp
+
+func matchString(pat, str string) (result bool, err error) {
+       if matchRe == nil || matchPat != pat {
+               matchPat = pat
+               matchRe, err = regexp.Compile(matchPat)
+               if err != nil {
+                       return
+               }
+       }
+       return matchRe.MatchString(str), nil
 }
+
+func main() {
+       testing.Main(matchString, tests, benchmarks, examples)
+}
+
+`))
diff --git a/src/cmd/go/testflag.go b/src/cmd/go/testflag.go
new file mode 100644 (file)
index 0000000..c4aa8c6
--- /dev/null
@@ -0,0 +1,168 @@
+// 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 (
+       "fmt"
+       "os"
+       "strconv"
+       "strings"
+)
+
+// 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 -test.run 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
+       exit()
+}
+
+// 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.
+func testFlags(args []string) (passToTest []string) {
+       for i := 0; i < len(args); i++ {
+               arg := args[i]
+               f, value, extraWord := testFlag(args, i)
+               if f == nil {
+                       args = append(args, arg)
+                       continue
+               }
+               switch f.name {
+               case "c":
+                       setBoolFlag(&testC, value)
+               case "x":
+                       setBoolFlag(&testX, value)
+               case "file":
+                       testFiles = append(testFiles, value)
+               }
+               if extraWord {
+                       i++
+               }
+               if f.passToTest {
+                       passToTest = append(passToTest, "-test."+f.name+"="+value)
+               }
+       }
+       return
+}
+
+// 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":
+               usage()
+       }
+       if arg == "" || arg[0] != '-' {
+               return
+       }
+       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 == f.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) {
+                                               usage()
+                                       }
+                                       value = args[i+1]
+                               }
+                       }
+                       if f.present && !f.multiOK {
+                               usage()
+                       }
+                       f.present = true
+                       return
+               }
+       }
+       f = nil
+       return
+}
+
+// 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)
+               usage()
+       }
+       *flag = x
+}