Commit 20090df7 authored by Russ Cox's avatar Russ Cox

go: implement test command

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
parent 1338347b
...@@ -16,6 +16,7 @@ GOFILES=\ ...@@ -16,6 +16,7 @@ GOFILES=\
pkg.go\ pkg.go\
run.go\ run.go\
test.go\ test.go\
testflag.go\
version.go\ version.go\
vet.go\ vet.go\
......
...@@ -105,6 +105,7 @@ type builder struct { ...@@ -105,6 +105,7 @@ type builder struct {
nflag bool // the -n flag nflag bool // the -n flag
vflag bool // the -v flag vflag bool // the -v flag
arch string // e.g., "6" arch string // e.g., "6"
goroot string // the $GOROOT
actionCache map[cacheKey]*action // a cache of already-constructed actions actionCache map[cacheKey]*action // a cache of already-constructed actions
} }
...@@ -112,10 +113,12 @@ type builder struct { ...@@ -112,10 +113,12 @@ type builder struct {
type action struct { type action struct {
f func(*builder, *action) error // the action itself f func(*builder, *action) error // the action itself
p *Package // the package this action works on p *Package // the package this action works on
deps []*action // actions that must happen before this one deps []*action // actions that must happen before this one
done bool // whether the action is done (might have failed) done bool // whether the action is done (might have failed)
failed bool // whether the action 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. // Results left for communication with other code.
pkgobj string // the built .a file pkgobj string // the built .a file
...@@ -143,6 +146,7 @@ func (b *builder) init(aflag, nflag, vflag bool) { ...@@ -143,6 +146,7 @@ func (b *builder) init(aflag, nflag, vflag bool) {
b.nflag = nflag b.nflag = nflag
b.vflag = vflag b.vflag = vflag
b.actionCache = make(map[cacheKey]*action) b.actionCache = make(map[cacheKey]*action)
b.goroot = runtime.GOROOT()
b.arch, err = build.ArchChar(build.DefaultContext.GOARCH) b.arch, err = build.ArchChar(build.DefaultContext.GOARCH)
if err != nil { if err != nil {
...@@ -209,12 +213,24 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action ...@@ -209,12 +213,24 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action
return a 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 b.actionCache[key] = a
switch mode { switch mode {
case modeBuild, modeInstall: case modeBuild, modeInstall:
for _, p1 := range p.imports {
a.deps = append(a.deps, b.action(depMode, depMode, p1))
}
if !needInstall(p) && !b.aflag { 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 a.f = (*builder).nop
return a return a
} }
...@@ -238,9 +254,6 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action ...@@ -238,9 +254,6 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action
} }
a.f = (*builder).build a.f = (*builder).build
for _, p1 := range p.imports {
a.deps = append(a.deps, b.action(depMode, depMode, p1))
}
} }
return a return a
...@@ -288,8 +301,10 @@ func (b *builder) do(a *action) { ...@@ -288,8 +301,10 @@ func (b *builder) do(a *action) {
b.do(a1) b.do(a1)
if a1.failed { if a1.failed {
a.failed = true a.failed = true
a.done = true if !a.ignoreFail {
return a.done = true
return
}
} }
} }
if err := a.f(b, a); err != nil { if err := a.f(b, a); err != nil {
...@@ -303,10 +318,12 @@ func (b *builder) nop(a *action) error { ...@@ -303,10 +318,12 @@ func (b *builder) nop(a *action) error {
return nil 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 { func (b *builder) build(a *action) error {
obj := filepath.Join(b.work, filepath.FromSlash(a.p.ImportPath+"/_obj")) + string(filepath.Separator) 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 // make build directory
if err := b.mkdir(obj); err != nil { if err := b.mkdir(obj); err != nil {
...@@ -332,11 +349,10 @@ func (b *builder) build(a *action) error { ...@@ -332,11 +349,10 @@ func (b *builder) build(a *action) error {
inc = append(inc, "-I", b.work) inc = append(inc, "-I", b.work)
incMap := map[string]bool{} incMap := map[string]bool{}
for _, a1 := range a.deps { for _, a1 := range a.deps {
p1 := a1.p pkgdir := a1.pkgdir
if p1.t.Goroot { if pkgdir == build.Path[0].PkgDir() || pkgdir == "" {
continue continue
} }
pkgdir := p1.t.PkgDir()
if !incMap[pkgdir] { if !incMap[pkgdir] {
incMap[pkgdir] = true incMap[pkgdir] = true
inc = append(inc, "-I", pkgdir) inc = append(inc, "-I", pkgdir)
...@@ -386,17 +402,14 @@ func (b *builder) build(a *action) error { ...@@ -386,17 +402,14 @@ func (b *builder) build(a *action) error {
// install is the action for installing a single package. // install is the action for installing a single package.
func (b *builder) install(a *action) error { func (b *builder) install(a *action) error {
if err := b.build(a); err != nil { a1 := a.deps[0]
return err
}
var src string var src string
var perm uint32 var perm uint32
if a.pkgbin != "" { if a1.pkgbin != "" {
src = a.pkgbin src = a1.pkgbin
perm = 0777 perm = 0777
} else { } else {
src = a.pkgobj src = a1.pkgobj
perm = 0666 perm = 0666
} }
......
...@@ -36,6 +36,10 @@ type Command struct { ...@@ -36,6 +36,10 @@ type Command struct {
// Flag is a set of flags specific to this command. // Flag is a set of flags specific to this command.
Flag flag.FlagSet 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. // Name returns the command's name: the first word in the usage line.
...@@ -96,8 +100,12 @@ func main() { ...@@ -96,8 +100,12 @@ func main() {
for _, cmd := range commands { for _, cmd := range commands {
if cmd.Name() == args[0] && cmd.Run != nil { if cmd.Name() == args[0] && cmd.Run != nil {
cmd.Flag.Usage = func() { cmd.Usage() } cmd.Flag.Usage = func() { cmd.Usage() }
cmd.Flag.Parse(args[1:]) if cmd.CustomFlags {
args = cmd.Flag.Args() args = args[1:]
} else {
cmd.Flag.Parse(args[1:])
args = cmd.Flag.Args()
}
cmd.Run(cmd, args) cmd.Run(cmd, args)
exit() exit()
return return
...@@ -209,6 +217,8 @@ func errorf(format string, args ...interface{}) { ...@@ -209,6 +217,8 @@ func errorf(format string, args ...interface{}) {
exitStatus = 1 exitStatus = 1
} }
var logf = log.Printf
func exitIfErrors() { func exitIfErrors() {
if exitStatus != 0 { if exitStatus != 0 {
exit() exit()
......
...@@ -38,6 +38,7 @@ type Package struct { ...@@ -38,6 +38,7 @@ type Package struct {
// Unexported fields are not part of the public API. // Unexported fields are not part of the public API.
t *build.Tree t *build.Tree
pkgdir string
info *build.DirInfo info *build.DirInfo
imports []*Package imports []*Package
gofiles []string // GoFiles+CgoFiles gofiles []string // GoFiles+CgoFiles
......
This diff is collapsed.
// 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
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment