aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/testing/testing.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/testing/testing.go')
-rw-r--r--libgo/go/testing/testing.go188
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) {