diff options
Diffstat (limited to 'libgo/go/testing/testing.go')
-rw-r--r-- | libgo/go/testing/testing.go | 188 |
1 files changed, 155 insertions, 33 deletions
diff --git a/libgo/go/testing/testing.go b/libgo/go/testing/testing.go index b002aa0..a629742 100644 --- a/libgo/go/testing/testing.go +++ b/libgo/go/testing/testing.go @@ -83,16 +83,30 @@ // ignores leading and trailing space.) These are examples of an example: // // func ExampleHello() { -// fmt.Println("hello") -// // Output: hello +// fmt.Println("hello") +// // Output: hello // } // // func ExampleSalutations() { -// fmt.Println("hello, and") -// fmt.Println("goodbye") -// // Output: -// // hello, and -// // goodbye +// fmt.Println("hello, and") +// fmt.Println("goodbye") +// // Output: +// // hello, and +// // goodbye +// } +// +// The comment prefix "Unordered output:" is like "Output:", but matches any +// line order: +// +// func ExamplePerm() { +// for _, value := range Perm(4) { +// fmt.Println(value) +// } +// // Unordered output: 4 +// // 2 +// // 1 +// // 3 +// // 0 // } // // Example functions without output comments are compiled but not executed. @@ -238,6 +252,7 @@ var ( chatty = flag.Bool("test.v", false, "verbose: print additional output") count = flag.Uint("test.count", 1, "run tests and benchmarks `n` times") coverProfile = flag.String("test.coverprofile", "", "write a coverage profile to `file`") + matchList = flag.String("test.list", "", "list tests, examples, and benchmarch maching `regexp` then exit") match = flag.String("test.run", "", "run only tests and examples matching `regexp`") memProfile = flag.String("test.memprofile", "", "write a memory profile to `file`") memProfileRate = flag.Int("test.memprofilerate", 0, "set memory profiling `rate` (see runtime.MemProfileRate)") @@ -247,7 +262,7 @@ var ( mutexProfile = flag.String("test.mutexprofile", "", "write a mutex contention profile to the named file after execution") mutexProfileFraction = flag.Int("test.mutexprofilefraction", 1, "if >= 0, calls runtime.SetMutexProfileFraction()") traceFile = flag.String("test.trace", "", "write an execution trace to `file`") - timeout = flag.Duration("test.timeout", 0, "fail test binary execution after duration `d` (0 means unlimited)") + timeout = flag.Duration("test.timeout", 0, "panic test binary after duration `d` (0 means unlimited)") cpuListStr = flag.String("test.cpu", "", "comma-separated `list` of cpu counts to run each test with") parallel = flag.Int("test.parallel", runtime.GOMAXPROCS(0), "run at most `n` tests in parallel") @@ -259,17 +274,20 @@ var ( // common holds the elements common between T and B and // captures common methods such as Errorf. type common struct { - mu sync.RWMutex // guards output, failed, and done. - output []byte // Output generated by test or benchmark. - w io.Writer // For flushToParent. - chatty bool // A copy of the chatty flag. - ran bool // Test or benchmark (or one of its subtests) was executed. - failed bool // Test or benchmark has failed. - skipped bool // Test of benchmark has been skipped. - finished bool // Test function has completed. - done bool // Test is finished and all subtests have completed. - hasSub int32 // written atomically - raceErrors int // number of races detected during test + mu sync.RWMutex // guards this group of fields + output []byte // Output generated by test or benchmark. + w io.Writer // For flushToParent. + ran bool // Test or benchmark (or one of its subtests) was executed. + failed bool // Test or benchmark has failed. + skipped bool // Test of benchmark has been skipped. + done bool // Test is finished and all subtests have completed. + helpers map[string]struct{} // functions to be skipped when writing file/line info + + chatty bool // A copy of the chatty flag. + finished bool // Test function has completed. + hasSub int32 // written atomically + raceErrors int // number of races detected during test + runner string // function name of tRunner running the test parent *common level int // Nesting depth of test or benchmark. @@ -298,10 +316,48 @@ func Verbose() bool { return *chatty } +// frameSkip searches, starting after skip frames, for the first caller frame +// in a function not marked as a helper and returns the frames to skip +// to reach that site. The search stops if it finds a tRunner function that +// was the entry point into the test. +// This function must be called with c.mu held. +func (c *common) frameSkip(skip int) int { + if c.helpers == nil { + return skip + } + var pc [50]uintptr + // Skip two extra frames to account for this function + // and runtime.Callers itself. + n := runtime.Callers(skip+2, pc[:]) + if n == 0 { + panic("testing: zero callers found") + } + frames := runtime.CallersFrames(pc[:n]) + var frame runtime.Frame + more := true + for i := 0; more; i++ { + frame, more = frames.Next() + if frame.Function == c.runner { + // We've gone up all the way to the tRunner calling + // the test function (so the user must have + // called tb.Helper from inside that test function). + // Only skip up to the test function itself. + return skip + i - 1 + } + if _, ok := c.helpers[frame.Function]; !ok { + // Found a frame that wasn't inside a helper function. + return skip + i + } + } + return skip +} + // decorate prefixes the string with the file and line of the call site // and inserts the final newline if needed and indentation tabs for formatting. -func decorate(s string) string { - _, file, line, ok := runtime.Caller(3) // decorate + log + public function. +// This function must be called with c.mu held. +func (c *common) decorate(s string) string { + skip := c.frameSkip(3) // decorate + log + public function. + _, file, line, ok := runtime.Caller(skip) if ok { // Truncate file name at last file name separator. if index := strings.LastIndex(file, "/"); index >= 0 { @@ -391,6 +447,7 @@ type TB interface { SkipNow() Skipf(format string, args ...interface{}) Skipped() bool + Helper() // A private method to prevent users implementing the // interface and so future additions to it will not @@ -450,8 +507,9 @@ func (c *common) Fail() { // Failed reports whether the function has failed. func (c *common) Failed() bool { c.mu.RLock() - defer c.mu.RUnlock() - return c.failed + failed := c.failed + c.mu.RUnlock() + return failed || c.raceErrors+race.Errors() > 0 } // FailNow marks the function as having failed and stops its execution. @@ -490,7 +548,7 @@ func (c *common) FailNow() { func (c *common) log(s string) { c.mu.Lock() defer c.mu.Unlock() - c.output = append(c.output, decorate(s)...) + c.output = append(c.output, c.decorate(s)...) } // Log formats its arguments using default formatting, analogous to Println, @@ -568,8 +626,38 @@ func (c *common) Skipped() bool { return c.skipped } +// Helper marks the calling function as a test helper function. +// When printing file and line information, that function will be skipped. +// Helper may be called simultaneously from multiple goroutines. +// Helper has no effect if it is called directly from a TestXxx/BenchmarkXxx +// function or a subtest/sub-benchmark function. +func (c *common) Helper() { + c.mu.Lock() + defer c.mu.Unlock() + if c.helpers == nil { + c.helpers = make(map[string]struct{}) + } + c.helpers[callerName(1)] = struct{}{} +} + +// callerName gives the function name (qualified with a package path) +// for the caller after skip frames (where 0 means the current function). +func callerName(skip int) string { + // Make room for the skip PC. + var pc [2]uintptr + n := runtime.Callers(skip+2, pc[:]) // skip + runtime.Callers + callerName + if n == 0 { + panic("testing: zero callers found") + } + frames := runtime.CallersFrames(pc[:n]) + frame, _ := frames.Next() + return frame.Function +} + // Parallel signals that this test is to be run in parallel with (and only with) -// other parallel tests. +// other parallel tests. When a test is run multiple times due to use of +// -test.count or -test.cpu, multiple instances of a single test never run in +// parallel with each other. func (t *T) Parallel() { if t.isParallel { panic("testing: t.Parallel called multiple times") @@ -600,13 +688,14 @@ type InternalTest struct { } func tRunner(t *T, fn func(t *T)) { + t.runner = callerName(0) + // When this goroutine is done, either because fn(t) // returned normally or because a test failure triggered // a call to runtime.Goexit, record the duration and send // a signal saying that the test is done. defer func() { - t.raceErrors += race.Errors() - if t.raceErrors > 0 { + if t.raceErrors+race.Errors() > 0 { t.Errorf("race detected during execution of test") } @@ -658,14 +747,15 @@ func tRunner(t *T, fn func(t *T)) { t.finished = true } -// Run runs f as a subtest of t called name. It reports whether f succeeded. -// Run will block until all its parallel subtests have completed. +// Run runs f as a subtest of t called name. It reports whether f succeeded. Run +// runs f in a separate goroutine and will block until all its parallel subtests +// have completed. // -// Run may be called simultaneously from multiple goroutines, but all such -// calls must happen before the outer test function for t returns. +// Run may be called simultaneously from multiple goroutines, but all such calls +// must return before the outer test function for t returns. func (t *T) Run(name string, f func(t *T)) bool { atomic.StoreInt32(&t.hasSub, 1) - testName, ok := t.context.match.fullName(&t.common, name) + testName, ok, _ := t.context.match.fullName(&t.common, name) if !ok { return true } @@ -687,7 +777,9 @@ func (t *T) Run(name string, f func(t *T)) bool { root := t.parent for ; root.parent != nil; root = root.parent { } + root.mu.Lock() fmt.Fprintf(root.w, "=== RUN %s\n", t.name) + root.mu.Unlock() } // Instead of reducing the running count of this test before calling the // tRunner and increasing it afterwards, we rely on tRunner keeping the @@ -764,6 +856,7 @@ func (f matchStringOnly) StartCPUProfile(w io.Writer) error { return e func (f matchStringOnly) StopCPUProfile() {} func (f matchStringOnly) WriteHeapProfile(w io.Writer) error { return errMain } func (f matchStringOnly) WriteProfileTo(string, io.Writer, int) error { return errMain } +func (f matchStringOnly) ImportPath() string { return "" } // Main is an internal function, part of the implementation of the "go test" command. // It was exported because it is cross-package and predates "internal" packages. @@ -793,6 +886,7 @@ type testDeps interface { StopCPUProfile() WriteHeapProfile(io.Writer) error WriteProfileTo(string, io.Writer, int) error + ImportPath() string } // MainStart is meant for use by tests generated by 'go test'. @@ -814,6 +908,11 @@ func (m *M) Run() int { flag.Parse() } + if len(*matchList) != 0 { + listTests(m.deps.MatchString, m.tests, m.benchmarks, m.examples) + return 0 + } + parseCpuList() m.before() @@ -825,7 +924,7 @@ func (m *M) Run() int { if !testRan && !exampleRan && *matchBenchmarks == "" { fmt.Fprintln(os.Stderr, "testing: warning: no tests to run") } - if !testOk || !exampleOk || !runBenchmarks(m.deps.MatchString, m.benchmarks) || race.Errors() > 0 { + if !testOk || !exampleOk || !runBenchmarks(m.deps.ImportPath(), m.deps.MatchString, m.benchmarks) || race.Errors() > 0 { fmt.Println("FAIL") m.after() return 1 @@ -853,6 +952,29 @@ func (t *T) report() { } } +func listTests(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) { + if _, err := matchString(*matchList, "non-empty"); err != nil { + fmt.Fprintf(os.Stderr, "testing: invalid regexp in -test.list (%q): %s\n", *matchList, err) + os.Exit(1) + } + + for _, test := range tests { + if ok, _ := matchString(*matchList, test.Name); ok { + fmt.Println(test.Name) + } + } + for _, bench := range benchmarks { + if ok, _ := matchString(*matchList, bench.Name); ok { + fmt.Println(bench.Name) + } + } + for _, example := range examples { + if ok, _ := matchString(*matchList, example.Name); ok { + fmt.Println(example.Name) + } + } +} + // An internal function but exported because it is cross-package; part of the implementation // of the "go test" command. func RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool) { |