aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/testing/testing.go
diff options
context:
space:
mode:
authorGiuliano Belinassi <giuliano.belinassi@usp.br>2020-08-22 17:43:43 -0300
committerGiuliano Belinassi <giuliano.belinassi@usp.br>2020-08-22 17:43:43 -0300
commita926878ddbd5a98b272c22171ce58663fc04c3e0 (patch)
tree86af256e5d9a9c06263c00adc90e5fe348008c43 /libgo/go/testing/testing.go
parent542730f087133690b47e036dfd43eb0db8a650ce (diff)
parent07cbaed8ba7d1b6e4ab3a9f44175502a4e1ecdb1 (diff)
downloadgcc-devel/autopar_devel.zip
gcc-devel/autopar_devel.tar.gz
gcc-devel/autopar_devel.tar.bz2
Merge branch 'autopar_rebase2' into autopar_develdevel/autopar_devel
Quickly commit changes in the rebase branch.
Diffstat (limited to 'libgo/go/testing/testing.go')
-rw-r--r--libgo/go/testing/testing.go256
1 files changed, 203 insertions, 53 deletions
diff --git a/libgo/go/testing/testing.go b/libgo/go/testing/testing.go
index 758af74..dee77f7 100644
--- a/libgo/go/testing/testing.go
+++ b/libgo/go/testing/testing.go
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
// Package testing provides support for automated testing of Go packages.
-// It is intended to be used in concert with the ``go test'' command, which automates
+// It is intended to be used in concert with the "go test" command, which automates
// execution of any function of the form
// func TestXxx(*testing.T)
// where Xxx does not start with a lowercase letter. The function name
@@ -14,8 +14,8 @@
// To write a new test suite, create a file whose name ends _test.go that
// contains the TestXxx functions as described here. Put the file in the same
// package as the one being tested. The file will be excluded from regular
-// package builds but will be included when the ``go test'' command is run.
-// For more detail, run ``go help test'' and ``go help testflag''.
+// package builds but will be included when the "go test" command is run.
+// For more detail, run "go help test" and "go help testflag".
//
// A simple test function looks like this:
//
@@ -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"
@@ -320,6 +325,7 @@ var (
cpuListStr *string
parallel *int
testlog *string
+ printer *testPrinter
haveExamples bool // are there examples?
@@ -329,6 +335,48 @@ var (
numFailed uint32 // number of test failures
)
+type testPrinter struct {
+ chatty bool
+
+ lastNameMu sync.Mutex // guards lastName
+ lastName string // last printed test name in chatty mode
+}
+
+func newTestPrinter(chatty bool) *testPrinter {
+ return &testPrinter{
+ chatty: chatty,
+ }
+}
+
+func (p *testPrinter) Print(testName, out string) {
+ p.Fprint(os.Stdout, testName, out)
+}
+
+func (p *testPrinter) Fprint(w io.Writer, testName, out string) {
+ p.lastNameMu.Lock()
+ defer p.lastNameMu.Unlock()
+
+ if !p.chatty ||
+ strings.HasPrefix(out, "--- PASS") ||
+ strings.HasPrefix(out, "--- FAIL") ||
+ strings.HasPrefix(out, "=== CONT") ||
+ strings.HasPrefix(out, "=== RUN") {
+ p.lastName = testName
+ fmt.Fprint(w, out)
+ return
+ }
+
+ if p.lastName == "" {
+ p.lastName = testName
+ } else if p.lastName != testName {
+ // Always printed as-is, with 0 decoration or indentation. So, we skip
+ // printing to w.
+ fmt.Printf("=== CONT %s\n", testName)
+ p.lastName = testName
+ }
+ fmt.Fprint(w, out)
+}
+
// The maximum number of stack frames to go through when skipping helper functions for
// the purpose of decorating log messages.
const maxStackLen = 50
@@ -336,21 +384,24 @@ 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.
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
+ 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.
@@ -361,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.
@@ -420,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
}
@@ -480,9 +540,6 @@ func (c *common) decorate(s string, skip int) string {
buf := new(strings.Builder)
// Every line is indented at least 4 spaces.
buf.WriteString(" ")
- if c.chatty {
- fmt.Fprintf(buf, "%s: ", c.name)
- }
fmt.Fprintf(buf, "%s:%d: ", file, line)
lines := strings.Split(s, "\n")
if l := len(lines); l > 1 && lines[l-1] == "" {
@@ -501,12 +558,12 @@ func (c *common) decorate(s string, skip int) string {
// flushToParent writes c.output to the parent after first writing the header
// with the given format and arguments.
-func (c *common) flushToParent(format string, args ...interface{}) {
+func (c *common) flushToParent(testName, format string, args ...interface{}) {
p := c.parent
p.mu.Lock()
defer p.mu.Unlock()
- fmt.Fprintf(p.w, format, args...)
+ printer.Fprint(p.w, testName, fmt.Sprintf(format, args...))
c.mu.Lock()
defer c.mu.Unlock()
@@ -560,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
@@ -680,7 +738,14 @@ func (c *common) logDepth(s string, depth int) {
panic("Log in goroutine after " + c.name + " has completed")
} else {
if c.chatty {
- fmt.Print(c.decorate(s, depth+1))
+ if c.bench {
+ // Benchmarks don't print === CONT, so we should skip the test
+ // printer and just print straight to stdout.
+ fmt.Print(c.decorate(s, depth+1))
+ } else {
+ printer.Print(c.name, c.decorate(s, depth+1))
+ }
+
return
}
c.output = append(c.output, c.decorate(s, depth+1)...)
@@ -818,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.
@@ -909,7 +1026,7 @@ func (t *T) Parallel() {
for ; root.parent != nil; root = root.parent {
}
root.mu.Lock()
- fmt.Fprintf(root.w, "=== CONT %s\n", t.name)
+ printer.Fprint(root.w, t.name, fmt.Sprintf("=== CONT %s\n", t.name))
root.mu.Unlock()
}
@@ -968,7 +1085,7 @@ func tRunner(t *T, fn func(t *T)) {
root.duration += time.Since(root.start)
d := root.duration
root.mu.Unlock()
- root.flushToParent("--- FAIL: %s (%s)\n", root.name, fmtDuration(d))
+ root.flushToParent(root.name, "--- FAIL: %s (%s)\n", root.name, fmtDuration(d))
if r := root.parent.runCleanup(recoverAndReturnPanic); r != nil {
fmt.Fprintf(root.parent.w, "cleanup panicked with %v", r)
}
@@ -1067,7 +1184,7 @@ func (t *T) Run(name string, f func(t *T)) bool {
for ; root.parent != nil; root = root.parent {
}
root.mu.Lock()
- fmt.Fprintf(root.w, "=== RUN %s\n", t.name)
+ printer.Fprint(root.w, t.name, fmt.Sprintf("=== RUN %s\n", t.name))
root.mu.Unlock()
}
// Instead of reducing the running count of this test before calling the
@@ -1084,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
@@ -1173,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
@@ -1203,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.
@@ -1215,24 +1350,28 @@ func (m *M) Run() int {
flag.Parse()
}
+ printer = newTestPrinter(Verbose())
+
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 == "" {
@@ -1240,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() {
@@ -1254,12 +1395,12 @@ func (t *T) report() {
dstr := fmtDuration(t.duration)
format := "--- %s: %s (%s)\n"
if t.Failed() {
- t.flushToParent(format, "FAIL", t.name, dstr)
+ t.flushToParent(t.name, format, "FAIL", t.name, dstr)
} else if t.chatty {
if t.Skipped() {
- t.flushToParent(format, "SKIP", t.name, dstr)
+ t.flushToParent(t.name, format, "SKIP", t.name, dstr)
} else {
- t.flushToParent(format, "PASS", t.name, dstr)
+ t.flushToParent(t.name, format, "PASS", t.name, dstr)
}
}
}
@@ -1290,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)
@@ -1306,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),
@@ -1487,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.