diff options
author | Giuliano Belinassi <giuliano.belinassi@usp.br> | 2020-08-22 17:43:43 -0300 |
---|---|---|
committer | Giuliano Belinassi <giuliano.belinassi@usp.br> | 2020-08-22 17:43:43 -0300 |
commit | a926878ddbd5a98b272c22171ce58663fc04c3e0 (patch) | |
tree | 86af256e5d9a9c06263c00adc90e5fe348008c43 /libgo/go/testing/testing.go | |
parent | 542730f087133690b47e036dfd43eb0db8a650ce (diff) | |
parent | 07cbaed8ba7d1b6e4ab3a9f44175502a4e1ecdb1 (diff) | |
download | gcc-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.go | 256 |
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. |