summaryrefslogtreecommitdiff
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.go170
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.