diff options
| author | Ondřej Surý <ondrej@sury.org> | 2012-01-30 15:38:19 +0100 |
|---|---|---|
| committer | Ondřej Surý <ondrej@sury.org> | 2012-01-30 15:38:19 +0100 |
| commit | 4cecda6c347bd6902b960c6a35a967add7070b0d (patch) | |
| tree | a462e224ff41ec9f3eb1a0b6e815806f9e8804ad /src/cmd/gotest | |
| parent | 6c7ca6e4d4e26e4c8cbe0d183966011b3b088a0a (diff) | |
| download | golang-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.go | 30 | ||||
| -rw-r--r-- | src/cmd/gotest/flag.go | 34 | ||||
| -rw-r--r-- | src/cmd/gotest/gotest.go | 124 |
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) }` |
