summaryrefslogtreecommitdiff
path: root/src/cmd/gotest
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2012-01-30 15:38:19 +0100
committerOndřej Surý <ondrej@sury.org>2012-01-30 15:38:19 +0100
commit4cecda6c347bd6902b960c6a35a967add7070b0d (patch)
treea462e224ff41ec9f3eb1a0b6e815806f9e8804ad /src/cmd/gotest
parent6c7ca6e4d4e26e4c8cbe0d183966011b3b088a0a (diff)
downloadgolang-4cecda6c347bd6902b960c6a35a967add7070b0d.tar.gz
Imported Upstream version 2012.01.27upstream-weekly/2012.01.27
Diffstat (limited to 'src/cmd/gotest')
-rw-r--r--src/cmd/gotest/doc.go30
-rw-r--r--src/cmd/gotest/flag.go34
-rw-r--r--src/cmd/gotest/gotest.go124
3 files changed, 123 insertions, 65 deletions
diff --git a/src/cmd/gotest/doc.go b/src/cmd/gotest/doc.go
index 5be06f817..8d729f025 100644
--- a/src/cmd/gotest/doc.go
+++ b/src/cmd/gotest/doc.go
@@ -26,6 +26,26 @@ signature,
func BenchmarkXXX(b *testing.B) { ... }
+Example functions may also be written. They are similar to test functions but,
+instead of using *testing.T to report success or failure, their output to
+os.Stdout and os.Stderr is compared against their doc comment.
+
+ // The output of this example function.
+ func ExampleXXX() {
+ fmt.Println("The output of this example function.")
+ }
+
+The following naming conventions are used to declare examples for a function F,
+a type T and method M on type T:
+ func ExampleF() { ... } and func ExampleF_suffix() { ... }
+ func ExampleT() { ... } and func ExampleT_suffix() { ... }
+ func ExampleT_M() { ... } and func ExampleT_M_suffix() { ... }
+
+Multiple example functions may be provided by appending a distinct suffix
+to the name. The suffix must start with a lowercase letter.
+
+Example functions without doc comments are compiled but not executed.
+
See the documentation of the testing package for more information.
By default, gotest needs no arguments. It compiles all the .go files
@@ -54,7 +74,8 @@ Usage:
6.out [-test.v] [-test.run pattern] [-test.bench pattern] \
[-test.cpuprofile=cpu.out] \
[-test.memprofile=mem.out] [-test.memprofilerate=1] \
- [-test.timeout=10] [-test.short] \
+ [-test.parallel=$GOMAXPROCS] \
+ [-test.timeout=10s] [-test.short] \
[-test.benchtime=3] [-test.cpu=1,2,3,4]
The -test.v flag causes the tests to be logged as they run. The
@@ -86,12 +107,17 @@ collection.
Use -test.run or -test.bench to limit profiling to a particular test
or benchmark.
+The -test.parallel flag allows parallel execution of Test functions
+that call test.Parallel. The value of the flag is the maximum
+number of tests to run simultaneously; by default, it is set to the
+value of GOMAXPROCS.
+
The -test.short flag tells long-running tests to shorten their run
time. It is off by default but set by all.bash so installations of
the Go tree can do a sanity check but not spend time running
exhaustive tests.
-The -test.timeout flag sets a timeout for the test in seconds. If the
+The -test.timeout flag sets a timeout for the test. If the
test runs for longer than that, it will panic, dumping a stack trace
of all existing goroutines.
diff --git a/src/cmd/gotest/flag.go b/src/cmd/gotest/flag.go
index c3a28f9a3..b0b0cae5c 100644
--- a/src/cmd/gotest/flag.go
+++ b/src/cmd/gotest/flag.go
@@ -28,6 +28,7 @@ var usageMessage = `Usage of %s:
-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
@@ -52,21 +53,22 @@ type flagSpec struct {
// flagDefn is the set of flags we process.
var flagDefn = []*flagSpec{
// gotest-local.
- &flagSpec{name: "c", isBool: true},
- &flagSpec{name: "file", multiOK: true},
- &flagSpec{name: "x", isBool: true},
+ {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.
- &flagSpec{name: "bench", passToTest: true},
- &flagSpec{name: "benchtime", passToTest: true},
- &flagSpec{name: "cpu", passToTest: true},
- &flagSpec{name: "cpuprofile", passToTest: true},
- &flagSpec{name: "memprofile", passToTest: true},
- &flagSpec{name: "memprofilerate", passToTest: true},
- &flagSpec{name: "run", passToTest: true},
- &flagSpec{name: "short", isBool: true, passToTest: true},
- &flagSpec{name: "timeout", passToTest: true},
- &flagSpec{name: "v", isBool: true, passToTest: true},
+ {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},
}
// flags processes the command line, grabbing -x and -c, rewriting known flags
@@ -105,6 +107,10 @@ func flag(i int) (f *flagSpec, value string, extra bool) {
if strings.HasPrefix(arg, "--") { // reduce two minuses to one
arg = arg[1:]
}
+ switch arg {
+ case "-?", "-h", "-help":
+ usage()
+ }
if arg == "" || arg[0] != '-' {
return
}
@@ -150,7 +156,7 @@ func flag(i int) (f *flagSpec, value string, extra bool) {
// setBoolFlag sets the addressed boolean to the value.
func setBoolFlag(flag *bool, value string) {
- x, err := strconv.Atob(value)
+ x, err := strconv.ParseBool(value)
if err != nil {
fmt.Fprintf(os.Stderr, "gotest: illegal bool flag value %s\n", value)
usage()
diff --git a/src/cmd/gotest/gotest.go b/src/cmd/gotest/gotest.go
index 88c746c1b..6697aeb2a 100644
--- a/src/cmd/gotest/gotest.go
+++ b/src/cmd/gotest/gotest.go
@@ -6,7 +6,6 @@ package main
import (
"bufio"
- "exec"
"fmt"
"go/ast"
"go/build"
@@ -14,12 +13,13 @@ import (
"go/token"
"io/ioutil"
"os"
+ "os/exec"
"runtime"
"sort"
"strings"
"time"
"unicode"
- "utf8"
+ "unicode/utf8"
)
// Environment for commands.
@@ -55,10 +55,10 @@ var (
// elapsed returns the number of seconds since gotest started.
func elapsed() float64 {
- return float64(time.Nanoseconds()-start) / 1e9
+ return time.Now().Sub(start).Seconds()
}
-var start = time.Nanoseconds()
+var start = time.Now()
// File represents a file that contains tests.
type File struct {
@@ -68,6 +68,12 @@ type File struct {
astFile *ast.File
tests []string // The names of the TestXXXs.
benchmarks []string // The names of the BenchmarkXXXs.
+ examples []example
+}
+
+type example struct {
+ name string // The name of the example function (ExampleXXX).
+ output string // The expected output (stdout/stderr) of the function.
}
func main() {
@@ -107,13 +113,6 @@ func Fatalf(s string, args ...interface{}) {
os.Exit(2)
}
-// theChar is the map from architecture to object character.
-var theChar = map[string]string{
- "arm": "5",
- "amd64": "6",
- "386": "8",
-}
-
// addEnv adds a name=value pair to the environment passed to subcommands.
// If the item is already in the environment, addEnv replaces the value.
func addEnv(name, value string) {
@@ -131,14 +130,12 @@ func setEnvironment() {
// Basic environment.
GOROOT = runtime.GOROOT()
addEnv("GOROOT", GOROOT)
- GOARCH = os.Getenv("GOARCH")
- if GOARCH == "" {
- GOARCH = runtime.GOARCH
- }
+ GOARCH = build.DefaultContext.GOARCH
addEnv("GOARCH", GOARCH)
- O = theChar[GOARCH]
- if O == "" {
- Fatalf("unknown architecture %s", GOARCH)
+ var err error
+ O, err = build.ArchChar(GOARCH)
+ if err != nil {
+ Fatalf("unknown architecture: %s", err)
}
// Commands and their flags.
@@ -146,8 +143,12 @@ func setEnvironment() {
if gc == "" {
gc = O + "g"
}
- XGC = []string{gc, "-I", "_test", "-o", "_xtest_." + O}
- GC = []string{gc, "-I", "_test", "_testmain.go"}
+ var gcflags []string
+ if gf := strings.TrimSpace(os.Getenv("GCFLAGS")); gf != "" {
+ gcflags = strings.Fields(gf)
+ }
+ XGC = append([]string{gc, "-I", "_test", "-o", "_xtest_." + O}, gcflags...)
+ GC = append(append([]string{gc, "-I", "_test"}, gcflags...), "_testmain.go")
gl := os.Getenv("GL")
if gl == "" {
gl = O + "l"
@@ -190,7 +191,7 @@ func parseFiles() {
fileSet := token.NewFileSet()
for _, f := range files {
// Report declaration errors so we can abort if the files are incorrect Go.
- file, err := parser.ParseFile(fileSet, f.name, nil, parser.DeclarationErrors)
+ file, err := parser.ParseFile(fileSet, f.name, nil, parser.DeclarationErrors|parser.ParseComments)
if err != nil {
Fatalf("parse error: %s", err)
}
@@ -219,10 +220,20 @@ func getTestNames() {
f.tests = append(f.tests, name)
} else if isTest(name, "Benchmark") {
f.benchmarks = append(f.benchmarks, name)
+ } else if isTest(name, "Example") {
+ output := n.Doc.Text()
+ if output == "" {
+ // Don't run examples with no output.
+ continue
+ }
+ f.examples = append(f.examples, example{
+ name: name,
+ output: output,
+ })
}
// TODO: worth checking the signature? Probably not.
}
- if strings.HasSuffix(f.pkg, "_test") {
+ if isOutsideTest(f.pkg) {
outsideFileNames = append(outsideFileNames, f.name)
} else {
insideFileNames = append(insideFileNames, f.name)
@@ -272,10 +283,10 @@ func runTestWithArgs(binary string) {
func doRun(argv []string, returnStdout bool) string {
if xFlag {
fmt.Printf("gotest %.2fs: %s\n", elapsed(), strings.Join(argv, " "))
- t := -time.Nanoseconds()
+ start := time.Now()
defer func() {
- t += time.Nanoseconds()
- fmt.Printf(" [+%.2fs]\n", float64(t)/1e9)
+ t := time.Now().Sub(start)
+ fmt.Printf(" [+%.2fs]\n", t.Seconds())
}()
}
command := argv[0]
@@ -291,7 +302,7 @@ func doRun(argv []string, returnStdout bool) string {
command = "bash"
argv = []string{"bash", "-c", cmd}
}
- var err os.Error
+ var err error
argv[0], err = exec.LookPath(argv[0])
if err != nil {
Fatalf("can't find %s: %s", command, err)
@@ -356,51 +367,62 @@ func writeTestmainGo() {
insideTests := false
for _, f := range files {
//println(f.name, f.pkg)
- if len(f.tests) == 0 && len(f.benchmarks) == 0 {
+ if len(f.tests) == 0 && len(f.benchmarks) == 0 && len(f.examples) == 0 {
continue
}
- if strings.HasSuffix(f.pkg, "_test") {
+ if isOutsideTest(f.pkg) {
outsideTests = true
} else {
insideTests = true
}
}
+ // Rename the imports for the system under test,
+ // in case the tested package has the same name
+ // as any of the other imports, variables or methods.
if insideTests {
switch importPath {
case "testing":
case "main":
// Import path main is reserved, so import with
// explicit reference to ./_test/main instead.
- // Also, the file we are writing defines a function named main,
- // so rename this import to __main__ to avoid name conflict.
- fmt.Fprintf(b, "import __main__ %q\n", "./_test/main")
+ fmt.Fprintf(b, "import target %q\n", "./_test/main")
default:
- fmt.Fprintf(b, "import %q\n", importPath)
+ fmt.Fprintf(b, "import target %q\n", importPath)
}
}
if outsideTests {
- fmt.Fprintf(b, "import %q\n", "./_xtest_")
+ // It is possible to have both inside and outside tests
+ // at the same time, so a different import name is needed.
+ fmt.Fprintf(b, "import target_test %q\n", "./_xtest_")
}
fmt.Fprintf(b, "import %q\n", "testing")
- fmt.Fprintf(b, "import __os__ %q\n", "os") // rename in case tested package is called os
- fmt.Fprintf(b, "import __regexp__ %q\n", "regexp") // rename in case tested package is called regexp
- fmt.Fprintln(b) // for gofmt
+ fmt.Fprintf(b, "import %q\n", "regexp")
+ fmt.Fprintln(b) // for gofmt
// Tests.
- fmt.Fprintln(b, "var tests = []testing.InternalTest{")
+ fmt.Fprintf(b, "var tests = []testing.InternalTest{\n")
for _, f := range files {
for _, t := range f.tests {
- fmt.Fprintf(b, "\t{\"%s.%s\", %s.%s},\n", f.pkg, t, notMain(f.pkg), t)
+ fmt.Fprintf(b, "\t{\"%s.%s\", %s.%s},\n", f.pkg, t, renamedPackage(f.pkg), t)
}
}
fmt.Fprintln(b, "}")
fmt.Fprintln(b)
// Benchmarks.
- fmt.Fprintf(b, "var benchmarks = []testing.InternalBenchmark{")
+ fmt.Fprintf(b, "var benchmarks = []testing.InternalBenchmark{\n")
for _, f := range files {
for _, bm := range f.benchmarks {
- fmt.Fprintf(b, "\t{\"%s.%s\", %s.%s},\n", f.pkg, bm, notMain(f.pkg), bm)
+ fmt.Fprintf(b, "\t{\"%s.%s\", %s.%s},\n", f.pkg, bm, renamedPackage(f.pkg), bm)
+ }
+ }
+ fmt.Fprintln(b, "}")
+
+ // Examples.
+ fmt.Fprintf(b, "var examples = []testing.InternalExample{")
+ for _, f := range files {
+ for _, eg := range f.examples {
+ fmt.Fprintf(b, "\t{%q, %s.%s, %q},\n", eg.name, renamedPackage(f.pkg), eg.name, eg.output)
}
}
fmt.Fprintln(b, "}")
@@ -409,23 +431,27 @@ func writeTestmainGo() {
fmt.Fprintln(b, testBody)
}
-// notMain returns the package, renaming as appropriate if it's "main".
-func notMain(pkg string) string {
- if pkg == "main" {
- return "__main__"
+// renamedPackage returns the name under which the test package was imported.
+func renamedPackage(pkg string) string {
+ if isOutsideTest(pkg) {
+ return "target_test"
}
- return pkg
+ return "target"
+}
+
+func isOutsideTest(pkg string) bool {
+ return strings.HasSuffix(pkg, "_test")
}
// testBody is just copied to the output. It's the code that runs the tests.
var testBody = `
var matchPat string
-var matchRe *__regexp__.Regexp
+var matchRe *regexp.Regexp
-func matchString(pat, str string) (result bool, err __os__.Error) {
+func matchString(pat, str string) (result bool, err error) {
if matchRe == nil || matchPat != pat {
matchPat = pat
- matchRe, err = __regexp__.Compile(matchPat)
+ matchRe, err = regexp.Compile(matchPat)
if err != nil {
return
}
@@ -434,5 +460,5 @@ func matchString(pat, str string) (result bool, err __os__.Error) {
}
func main() {
- testing.Main(matchString, tests, benchmarks)
+ testing.Main(matchString, tests, benchmarks, examples)
}`