diff options
Diffstat (limited to 'libgo/go/testing/testing.go')
-rw-r--r-- | libgo/go/testing/testing.go | 170 |
1 files changed, 135 insertions, 35 deletions
diff --git a/libgo/go/testing/testing.go b/libgo/go/testing/testing.go index 9f47eb8584f..6f0935850bd 100644 --- a/libgo/go/testing/testing.go +++ b/libgo/go/testing/testing.go @@ -37,17 +37,17 @@ // https://golang.org/cmd/go/#hdr-Testing_flags // // A sample benchmark function looks like this: -// func BenchmarkHello(b *testing.B) { +// func BenchmarkRandInt(b *testing.B) { // for i := 0; i < b.N; i++ { -// fmt.Sprintf("hello") +// rand.Int() // } // } // // The benchmark function must run the target code b.N times. // During benchmark execution, b.N is adjusted until the benchmark function lasts // long enough to be timed reliably. The output -// BenchmarkHello 10000000 282 ns/op -// means that the loop ran 10000000 times at a speed of 282 ns per loop. +// BenchmarkRandInt-8 68453040 17.8 ns/op +// means that the loop ran 68453040 times at a speed of 17.8 ns per loop. // // If a benchmark needs some expensive setup before running, the timer // may be reset: @@ -217,10 +217,14 @@ // // then the generated test will call TestMain(m) instead of running the tests // directly. TestMain runs in the main goroutine and can do whatever setup -// and teardown is necessary around a call to m.Run. It should then call -// os.Exit with the result of m.Run. When TestMain is called, flag.Parse has -// not been run. If TestMain depends on command-line flags, including those -// of the testing package, it should call flag.Parse explicitly. +// and teardown is necessary around a call to m.Run. m.Run will return an exit +// code that may be passed to os.Exit. If TestMain returns, the test wrapper +// will pass the result of m.Run to os.Exit itself. +// +// When TestMain is called, flag.Parse has not been run. If TestMain depends on +// command-line flags, including those of the testing package, it should call +// flag.Parse explicitly. Command line flags are always parsed by the time test +// or benchmark functions run. // // A simple implementation of TestMain is: // @@ -238,6 +242,7 @@ import ( "fmt" "internal/race" "io" + "io/ioutil" "os" "runtime" "runtime/debug" @@ -379,15 +384,17 @@ const maxStackLen = 50 // common holds the elements common between T and B and // captures common methods such as Errorf. type common struct { - 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 - cleanup func() // optional function to be called at the end of the 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 + cleanup func() // optional function to be called at the end of the test + cleanupName string // Name of the cleanup function. + cleanupPc []uintptr // The stack trace at the point where Cleanup was called. chatty bool // A copy of the chatty flag. bench bool // Whether the current test is a benchmark. @@ -405,6 +412,11 @@ type common struct { barrier chan bool // To signal parallel subtests they may start. signal chan bool // To signal a test is done. sub []*T // Queue of subtests to be run in parallel. + + tempDirOnce sync.Once + tempDir string + tempDirErr error + tempDirSeq int32 } // Short reports whether the -test.short flag is set. @@ -464,6 +476,10 @@ func (c *common) frameSkip(skip int) runtime.Frame { var firstFrame, prevFrame, frame runtime.Frame for more := true; more; prevFrame = frame { frame, more = frames.Next() + if frame.Function == c.cleanupName { + frames = runtime.CallersFrames(c.cleanupPc) + continue + } if firstFrame.PC == 0 { firstFrame = frame } @@ -601,6 +617,7 @@ type TB interface { SkipNow() Skipf(format string, args ...interface{}) Skipped() bool + TempDir() string // A private method to prevent users implementing the // interface and so future additions to it will not @@ -866,12 +883,64 @@ func (c *common) Cleanup(f func()) { c.mu.Lock() defer c.mu.Unlock() oldCleanup := c.cleanup + oldCleanupPc := c.cleanupPc c.cleanup = func() { if oldCleanup != nil { - defer oldCleanup() + defer func() { + c.cleanupPc = oldCleanupPc + oldCleanup() + }() } + c.cleanupName = callerName(0) f() } + var pc [maxStackLen]uintptr + // Skip two extra frames to account for this function and runtime.Callers itself. + n := runtime.Callers(2, pc[:]) + c.cleanupPc = pc[:n] +} + +var tempDirReplacer struct { + sync.Once + r *strings.Replacer +} + +// TempDir returns a temporary directory for the test to use. +// The directory is automatically removed by Cleanup when the test and +// all its subtests complete. +// Each subsequent call to t.TempDir returns a unique directory; +// if the directory creation fails, TempDir terminates the test by calling Fatal. +func (c *common) TempDir() string { + // Use a single parent directory for all the temporary directories + // created by a test, each numbered sequentially. + c.tempDirOnce.Do(func() { + c.Helper() + + // ioutil.TempDir doesn't like path separators in its pattern, + // so mangle the name to accommodate subtests. + tempDirReplacer.Do(func() { + tempDirReplacer.r = strings.NewReplacer("/", "_", "\\", "_", ":", "_") + }) + pattern := tempDirReplacer.r.Replace(c.Name()) + + c.tempDir, c.tempDirErr = ioutil.TempDir("", pattern) + if c.tempDirErr == nil { + c.Cleanup(func() { + if err := os.RemoveAll(c.tempDir); err != nil { + c.Errorf("TempDir RemoveAll cleanup: %v", err) + } + }) + } + }) + if c.tempDirErr != nil { + c.Fatalf("TempDir: %v", c.tempDirErr) + } + seq := atomic.AddInt32(&c.tempDirSeq, 1) + dir := fmt.Sprintf("%s%c%03d", c.tempDir, os.PathSeparator, seq) + if err := os.Mkdir(dir, 0777); err != nil { + c.Fatalf("TempDir: %v", err) + } + return dir } // panicHanding is an argument to runCleanup. @@ -1132,10 +1201,20 @@ func (t *T) Run(name string, f func(t *T)) bool { return !t.failed } +// Deadline reports the time at which the test binary will have +// exceeded the timeout specified by the -timeout flag. +// +// The ok result is false if the -timeout flag indicates “no timeout” (0). +func (t *T) Deadline() (deadline time.Time, ok bool) { + deadline = t.context.deadline + return deadline, !deadline.IsZero() +} + // testContext holds all fields that are common to all tests. This includes // synchronization primitives to run at most *parallel tests. type testContext struct { - match *matcher + match *matcher + deadline time.Time mu sync.Mutex @@ -1221,6 +1300,10 @@ type M struct { afterOnce sync.Once numRun int + + // value to pass to os.Exit, the outer test func main + // harness calls os.Exit with this code. See #34129. + exitCode int } // testDeps is an internal interface of functionality that is @@ -1251,7 +1334,11 @@ func MainStart(deps testDeps, tests []InternalTest, benchmarks []InternalBenchma } // Run runs the tests. It returns an exit code to pass to os.Exit. -func (m *M) Run() int { +func (m *M) Run() (code int) { + defer func() { + code = m.exitCode + }() + // Count the number of calls to m.Run. // We only ever expected 1, but we didn't enforce that, // and now there are tests in the wild that call m.Run multiple times. @@ -1268,21 +1355,23 @@ func (m *M) Run() int { if *parallel < 1 { fmt.Fprintln(os.Stderr, "testing: -parallel can only be given a positive integer") flag.Usage() - return 2 + m.exitCode = 2 + return } if len(*matchList) != 0 { listTests(m.deps.MatchString, m.tests, m.benchmarks, m.examples) - return 0 + m.exitCode = 0 + return } parseCpuList() m.before() defer m.after() - m.startAlarm() + deadline := m.startAlarm() haveExamples = len(m.examples) > 0 - testRan, testOk := runTests(m.deps.MatchString, m.tests) + testRan, testOk := runTests(m.deps.MatchString, m.tests, deadline) exampleRan, exampleOk := runExamples(m.deps.MatchString, m.examples) m.stopAlarm() if !testRan && !exampleRan && *matchBenchmarks == "" { @@ -1290,11 +1379,13 @@ func (m *M) Run() int { } if !testOk || !exampleOk || !runBenchmarks(m.deps.ImportPath(), m.deps.MatchString, m.benchmarks) || race.Errors() > 0 { fmt.Println("FAIL") - return 1 + m.exitCode = 1 + return } fmt.Println("PASS") - return 0 + m.exitCode = 0 + return } func (t *T) report() { @@ -1340,14 +1431,18 @@ func listTests(matchString func(pat, str string) (bool, error), tests []Internal // RunTests is an internal function but exported because it is cross-package; // it is part of the implementation of the "go test" command. func RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool) { - ran, ok := runTests(matchString, tests) + var deadline time.Time + if *timeout > 0 { + deadline = time.Now().Add(*timeout) + } + ran, ok := runTests(matchString, tests, deadline) if !ran && !haveExamples { fmt.Fprintln(os.Stderr, "testing: warning: no tests to run") } return ok } -func runTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ran, ok bool) { +func runTests(matchString func(pat, str string) (bool, error), tests []InternalTest, deadline time.Time) (ran, ok bool) { ok = true for _, procs := range cpuList { runtime.GOMAXPROCS(procs) @@ -1356,6 +1451,7 @@ func runTests(matchString func(pat, str string) (bool, error), tests []InternalT break } ctx := newTestContext(*parallel, newMatcher(matchString, *match, "-test.run")) + ctx.deadline = deadline t := &T{ common: common{ signal: make(chan bool), @@ -1537,14 +1633,18 @@ func toOutputDir(path string) string { } // startAlarm starts an alarm if requested. -func (m *M) startAlarm() { - if *timeout > 0 { - m.timer = time.AfterFunc(*timeout, func() { - m.after() - debug.SetTraceback("all") - panic(fmt.Sprintf("test timed out after %v", *timeout)) - }) +func (m *M) startAlarm() time.Time { + if *timeout <= 0 { + return time.Time{} } + + deadline := time.Now().Add(*timeout) + m.timer = time.AfterFunc(*timeout, func() { + m.after() + debug.SetTraceback("all") + panic(fmt.Sprintf("test timed out after %v", *timeout)) + }) + return deadline } // stopAlarm turns off the alarm. |