diff options
author | Ian Lance Taylor <iant@golang.org> | 2018-09-24 21:46:21 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2018-09-24 21:46:21 +0000 |
commit | dd931d9b48647e898dc80927c532ae93cc09e192 (patch) | |
tree | 71be2295cd79b8a182f6130611658db8628772d5 /libgo/go/testing | |
parent | 779d8a5ad09b01428726ea5a0e6c87bd9ac3c0e4 (diff) | |
download | gcc-dd931d9b48647e898dc80927c532ae93cc09e192.zip gcc-dd931d9b48647e898dc80927c532ae93cc09e192.tar.gz gcc-dd931d9b48647e898dc80927c532ae93cc09e192.tar.bz2 |
libgo: update to Go 1.11
Reviewed-on: https://go-review.googlesource.com/136435
gotools/:
* Makefile.am (mostlyclean-local): Run chmod on check-go-dir to
make sure it is writable.
(check-go-tools): Likewise.
(check-vet): Copy internal/objabi to check-vet-dir.
* Makefile.in: Rebuild.
From-SVN: r264546
Diffstat (limited to 'libgo/go/testing')
-rw-r--r-- | libgo/go/testing/benchmark.go | 15 | ||||
-rw-r--r-- | libgo/go/testing/cover.go | 13 | ||||
-rw-r--r-- | libgo/go/testing/example.go | 3 | ||||
-rw-r--r-- | libgo/go/testing/helper_test.go | 8 | ||||
-rw-r--r-- | libgo/go/testing/helperfuncs_test.go | 18 | ||||
-rw-r--r-- | libgo/go/testing/internal/testdeps/deps.go | 4 | ||||
-rw-r--r-- | libgo/go/testing/match.go | 2 | ||||
-rw-r--r-- | libgo/go/testing/sub_test.go | 90 | ||||
-rw-r--r-- | libgo/go/testing/testing.go | 132 |
9 files changed, 210 insertions, 75 deletions
diff --git a/libgo/go/testing/benchmark.go b/libgo/go/testing/benchmark.go index 4d569b7..9c7b1be 100644 --- a/libgo/go/testing/benchmark.go +++ b/libgo/go/testing/benchmark.go @@ -489,14 +489,17 @@ func (b *B) Run(name string, f func(b *B)) bool { if !ok { return true } + var pc [maxStackLen]uintptr + n := runtime.Callers(2, pc[:]) sub := &B{ common: common{ - signal: make(chan bool), - name: benchName, - parent: &b.common, - level: b.level + 1, - w: b.w, - chatty: b.chatty, + signal: make(chan bool), + name: benchName, + parent: &b.common, + level: b.level + 1, + creator: pc[:n], + w: b.w, + chatty: b.chatty, }, importPath: b.importPath, benchFunc: f, diff --git a/libgo/go/testing/cover.go b/libgo/go/testing/cover.go index a4ce37f..17c03f5 100644 --- a/libgo/go/testing/cover.go +++ b/libgo/go/testing/cover.go @@ -13,14 +13,17 @@ import ( ) // CoverBlock records the coverage data for a single basic block. +// The fields are 1-indexed, as in an editor: The opening line of +// the file is number 1, for example. Columns are measured +// in bytes. // NOTE: This struct is internal to the testing infrastructure and may change. // It is not covered (yet) by the Go 1 compatibility guidelines. type CoverBlock struct { - Line0 uint32 - Col0 uint16 - Line1 uint32 - Col1 uint16 - Stmts uint16 + Line0 uint32 // Line number for block start. + Col0 uint16 // Column number for block start. + Line1 uint32 // Line number for block end. + Col1 uint16 // Column number for block end. + Stmts uint16 // Number of statements included in this block. } var cover Cover diff --git a/libgo/go/testing/example.go b/libgo/go/testing/example.go index b995550..f4beb76 100644 --- a/libgo/go/testing/example.go +++ b/libgo/go/testing/example.go @@ -5,7 +5,6 @@ package testing import ( - "bytes" "fmt" "io" "os" @@ -72,7 +71,7 @@ func runExample(eg InternalExample) (ok bool) { os.Stdout = w outC := make(chan string) go func() { - var buf bytes.Buffer + var buf strings.Builder _, err := io.Copy(&buf, r) r.Close() if err != nil { diff --git a/libgo/go/testing/helper_test.go b/libgo/go/testing/helper_test.go index f5cb27c..fe8ff05 100644 --- a/libgo/go/testing/helper_test.go +++ b/libgo/go/testing/helper_test.go @@ -28,11 +28,11 @@ helperfuncs_test.go:33: 1 helperfuncs_test.go:21: 2 helperfuncs_test.go:35: 3 helperfuncs_test.go:42: 4 -helperfuncs_test.go:47: 5 --- FAIL: Test/sub (?s) -helperfuncs_test.go:50: 6 -helperfuncs_test.go:21: 7 -helperfuncs_test.go:53: 8 +helperfuncs_test.go:45: 5 +helperfuncs_test.go:21: 6 +helperfuncs_test.go:44: 7 +helperfuncs_test.go:56: 8 ` lines := strings.Split(buf.String(), "\n") durationRE := regexp.MustCompile(`\(.*\)$`) diff --git a/libgo/go/testing/helperfuncs_test.go b/libgo/go/testing/helperfuncs_test.go index 7cb2e2c..f2d54b3 100644 --- a/libgo/go/testing/helperfuncs_test.go +++ b/libgo/go/testing/helperfuncs_test.go @@ -41,17 +41,19 @@ func testHelper(t *T) { } fn("4") - // Check that calling Helper from inside this test entry function - // doesn't have an effect. - t.Helper() - t.Error("5") - t.Run("sub", func(t *T) { - helper(t, "6") - notHelperCallingHelper(t, "7") + helper(t, "5") + notHelperCallingHelper(t, "6") + // Check that calling Helper from inside a subtest entry function + // works as if it were in an ordinary function call. t.Helper() - t.Error("8") + t.Error("7") }) + + // Check that calling Helper from inside a top-level test function + // has no effect. + t.Helper() + t.Error("8") } func parallelTestHelper(t *T) { diff --git a/libgo/go/testing/internal/testdeps/deps.go b/libgo/go/testing/internal/testdeps/deps.go index 4986898..14512e9 100644 --- a/libgo/go/testing/internal/testdeps/deps.go +++ b/libgo/go/testing/internal/testdeps/deps.go @@ -46,10 +46,6 @@ func (TestDeps) StopCPUProfile() { pprof.StopCPUProfile() } -func (TestDeps) WriteHeapProfile(w io.Writer) error { - return pprof.WriteHeapProfile(w) -} - func (TestDeps) WriteProfileTo(name string, w io.Writer, debug int) error { return pprof.Lookup(name).WriteTo(w, debug) } diff --git a/libgo/go/testing/match.go b/libgo/go/testing/match.go index 89e30d0..b18c6e7 100644 --- a/libgo/go/testing/match.go +++ b/libgo/go/testing/match.go @@ -110,7 +110,7 @@ func splitRegexp(s string) []string { } // unique creates a unique name for the given parent and subname by affixing it -// with one ore more counts, if necessary. +// with one or more counts, if necessary. func (m *matcher) unique(parent, subname string) string { name := fmt.Sprintf("%s/%s", parent, subname) empty := subname == "" diff --git a/libgo/go/testing/sub_test.go b/libgo/go/testing/sub_test.go index acf5dea..9af3909 100644 --- a/libgo/go/testing/sub_test.go +++ b/libgo/go/testing/sub_test.go @@ -168,7 +168,7 @@ func TestTRun(t *T) { --- FAIL: failure in parallel test propagates upwards (N.NNs) --- FAIL: failure in parallel test propagates upwards/#00 (N.NNs) --- FAIL: failure in parallel test propagates upwards/#00/par (N.NNs) - `, + `, f: func(t *T) { t.Run("", func(t *T) { t.Parallel() @@ -210,8 +210,8 @@ func TestTRun(t *T) { desc: "skipping after error", output: ` --- FAIL: skipping after error (N.NNs) - sub_test.go:NNN: an error - sub_test.go:NNN: skipped`, + sub_test.go:NNN: an error + sub_test.go:NNN: skipped`, f: func(t *T) { t.Error("an error") t.Skip("skipped") @@ -316,6 +316,81 @@ func TestTRun(t *T) { t.Skip() }, }, { + desc: "subtest calls error on parent", + ok: false, + output: ` +--- FAIL: subtest calls error on parent (N.NNs) + sub_test.go:NNN: first this + sub_test.go:NNN: and now this! + sub_test.go:NNN: oh, and this too`, + maxPar: 1, + f: func(t *T) { + t.Errorf("first this") + outer := t + t.Run("", func(t *T) { + outer.Errorf("and now this!") + }) + t.Errorf("oh, and this too") + }, + }, { + desc: "subtest calls fatal on parent", + ok: false, + output: ` +--- FAIL: subtest calls fatal on parent (N.NNs) + sub_test.go:NNN: first this + sub_test.go:NNN: and now this! + --- FAIL: subtest calls fatal on parent/#00 (N.NNs) + testing.go:NNN: test executed panic(nil) or runtime.Goexit: subtest may have called FailNow on a parent test`, + maxPar: 1, + f: func(t *T) { + outer := t + t.Errorf("first this") + t.Run("", func(t *T) { + outer.Fatalf("and now this!") + }) + t.Errorf("Should not reach here.") + }, + }, { + desc: "subtest calls error on ancestor", + ok: false, + output: ` +--- FAIL: subtest calls error on ancestor (N.NNs) + sub_test.go:NNN: Report to ancestor + --- FAIL: subtest calls error on ancestor/#00 (N.NNs) + sub_test.go:NNN: Still do this + sub_test.go:NNN: Also do this`, + maxPar: 1, + f: func(t *T) { + outer := t + t.Run("", func(t *T) { + t.Run("", func(t *T) { + outer.Errorf("Report to ancestor") + }) + t.Errorf("Still do this") + }) + t.Errorf("Also do this") + }, + }, { + desc: "subtest calls fatal on ancestor", + ok: false, + output: ` +--- FAIL: subtest calls fatal on ancestor (N.NNs) + sub_test.go:NNN: Nope`, + maxPar: 1, + f: func(t *T) { + outer := t + t.Run("", func(t *T) { + for i := 0; i < 4; i++ { + t.Run("", func(t *T) { + outer.Fatalf("Nope") + }) + t.Errorf("Don't do this") + } + t.Errorf("And neither do this") + }) + t.Errorf("Nor this") + }, + }, { desc: "panic on goroutine fail after test exit", ok: false, maxPar: 4, @@ -428,7 +503,7 @@ func TestBRun(t *T) { chatty: true, output: ` --- SKIP: root - sub_test.go:NNN: skipping`, + sub_test.go:NNN: skipping`, f: func(b *B) { b.Skip("skipping") }, }, { desc: "chatty with recursion", @@ -446,8 +521,8 @@ func TestBRun(t *T) { failed: true, output: ` --- FAIL: root - sub_test.go:NNN: an error - sub_test.go:NNN: skipped`, + sub_test.go:NNN: an error + sub_test.go:NNN: skipped`, f: func(b *B) { b.Error("an error") b.Skip("skipped") @@ -518,8 +593,9 @@ func TestBRun(t *T) { } func makeRegexp(s string) string { + s = regexp.QuoteMeta(s) s = strings.Replace(s, ":NNN:", `:\d\d\d:`, -1) - s = strings.Replace(s, "(N.NNs)", `\(\d*\.\d*s\)`, -1) + s = strings.Replace(s, "N\\.NNs", `\d*\.\d*s`, -1) return s } diff --git a/libgo/go/testing/testing.go b/libgo/go/testing/testing.go index f56dbf8..a552b36 100644 --- a/libgo/go/testing/testing.go +++ b/libgo/go/testing/testing.go @@ -34,7 +34,7 @@ // its -bench flag is provided. Benchmarks are run sequentially. // // For a description of the testing flags, see -// https://golang.org/cmd/go/#hdr-Description_of_testing_flags. +// https://golang.org/cmd/go/#hdr-Testing_flags // // A sample benchmark function looks like this: // func BenchmarkHello(b *testing.B) { @@ -178,6 +178,9 @@ // } // } // +// The race detector kills the program if it exceeds 8192 concurrent goroutines, +// so use care when running parallel tests with the -race flag set. +// // Run does not return until parallel subtests have completed, providing a way // to clean up after a group of parallel tests: // @@ -257,8 +260,8 @@ var ( coverProfile = flag.String("test.coverprofile", "", "write a coverage profile to `file`") matchList = flag.String("test.list", "", "list tests, examples, and benchmarks matching `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)") + memProfile = flag.String("test.memprofile", "", "write an allocation profile to `file`") + memProfileRate = flag.Int("test.memprofilerate", 0, "set memory allocation profiling `rate` (see runtime.MemProfileRate)") cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to `file`") blockProfile = flag.String("test.blockprofile", "", "write a goroutine blocking profile to `file`") blockProfileRate = flag.Int("test.blockprofilerate", 1, "set blocking profile `rate` (see runtime.SetBlockProfileRate)") @@ -278,6 +281,10 @@ var ( numFailed uint32 // number of test failures ) +// The maximum number of stack frames to go through when skipping helper functions for +// the purpose of decorating log messages. +const maxStackLen = 50 + // common holds the elements common between T and B and // captures common methods such as Errorf. type common struct { @@ -298,6 +305,7 @@ type common struct { parent *common level int // Nesting depth of test or benchmark. + creator []uintptr // If level > 0, the stack trace at the point where the parent called t.Run. name string // Name of test or benchmark. start time.Time // Time test or benchmark started duration time.Duration @@ -324,15 +332,20 @@ func Verbose() bool { } // 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. +// in a function not marked as a helper and returns that frame. +// The search stops if it finds a tRunner function that +// was the entry point into the test and the test is not a subtest. // 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 +func (c *common) frameSkip(skip int) runtime.Frame { + // If the search continues into the parent test, we'll have to hold + // its mu temporarily. If we then return, we need to unlock it. + shouldUnlock := false + defer func() { + if shouldUnlock { + c.mu.Unlock() + } + }() + var pc [maxStackLen]uintptr // Skip two extra frames to account for this function // and runtime.Callers itself. n := runtime.Callers(skip+2, pc[:]) @@ -340,32 +353,54 @@ func (c *common) frameSkip(skip int) int { panic("testing: zero callers found") } frames := runtime.CallersFrames(pc[:n]) - var frame runtime.Frame - more := true - for i := 0; more; i++ { + var firstFrame, prevFrame, frame runtime.Frame + for more := true; more; prevFrame = frame { frame, more = frames.Next() + if firstFrame.PC == 0 { + firstFrame = frame + } 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 this is a top-level test, only skip up to the test function itself. + // If we're in a subtest, continue searching in the parent test, + // starting from the point of the call to Run which created this subtest. + if c.level > 1 { + frames = runtime.CallersFrames(c.creator) + parent := c.parent + // We're no longer looking at the current c after this point, + // so we should unlock its mu, unless it's the original receiver, + // in which case our caller doesn't expect us to do that. + if shouldUnlock { + c.mu.Unlock() + } + c = parent + // Remember to unlock c.mu when we no longer need it, either + // because we went up another nesting level, or because we + // returned. + shouldUnlock = true + c.mu.Lock() + continue + } + return prevFrame } if _, ok := c.helpers[frame.Function]; !ok { // Found a frame that wasn't inside a helper function. - return skip + i + return frame } } - return skip + return firstFrame } // 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. +// and inserts the final newline if needed and indentation spaces for formatting. // 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 { + frame := c.frameSkip(3) // decorate + log + public function. + file := frame.File + line := frame.Line + if file != "" { // Truncate file name at last file name separator. if index := strings.LastIndex(file, "/"); index >= 0 { file = file[index+1:] @@ -374,11 +409,13 @@ func (c *common) decorate(s string) string { } } else { file = "???" + } + if line == 0 { line = 1 } - buf := new(bytes.Buffer) - // Every line is indented at least one tab. - buf.WriteByte('\t') + buf := new(strings.Builder) + // Every line is indented at least 4 spaces. + buf.WriteString(" ") fmt.Fprintf(buf, "%s:%d: ", file, line) lines := strings.Split(s, "\n") if l := len(lines); l > 1 && lines[l-1] == "" { @@ -386,8 +423,8 @@ func (c *common) decorate(s string) string { } for i, line := range lines { if i > 0 { - // Second and subsequent lines are indented an extra tab. - buf.WriteString("\n\t\t") + // Second and subsequent lines are indented an additional 4 spaces. + buf.WriteString("\n ") } buf.WriteString(line) } @@ -639,8 +676,6 @@ func (c *common) Skipped() bool { // 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() @@ -718,6 +753,8 @@ type InternalTest struct { F func(*T) } +var errNilPanicOrGoexit = errors.New("test executed panic(nil) or runtime.Goexit") + func tRunner(t *T, fn func(t *T)) { t.runner = callerName(0) @@ -726,6 +763,10 @@ func tRunner(t *T, fn func(t *T)) { // a call to runtime.Goexit, record the duration and send // a signal saying that the test is done. defer func() { + if t.Failed() { + atomic.AddUint32(&numFailed, 1) + } + if t.raceErrors+race.Errors() > 0 { t.Errorf("race detected during execution of test") } @@ -733,8 +774,17 @@ func tRunner(t *T, fn func(t *T)) { t.duration += time.Since(t.start) // If the test panicked, print any test output before dying. err := recover() + signal := true if !t.finished && err == nil { - err = fmt.Errorf("test executed panic(nil) or runtime.Goexit") + err = errNilPanicOrGoexit + for p := t.parent; p != nil; p = p.parent { + if p.finished { + t.Errorf("%v: subtest may have called FailNow on a parent test", err) + err = nil + signal = false + break + } + } } if err != nil { t.Fail() @@ -769,16 +819,14 @@ func tRunner(t *T, fn func(t *T)) { if t.parent != nil && atomic.LoadInt32(&t.hasSub) == 0 { t.setRan() } - t.signal <- true + t.signal <- signal }() t.start = time.Now() t.raceErrors = -race.Errors() fn(t) - if t.failed { - atomic.AddUint32(&numFailed, 1) - } + // code beyond here will not be executed when FailNow is invoked t.finished = true } @@ -794,6 +842,11 @@ func (t *T) Run(name string, f func(t *T)) bool { if !ok || shouldFailFast() { return true } + // Record the stack trace at the point of this call so that if the subtest + // function - which runs in a separate stack - is marked as a helper, we can + // continue walking the stack into the parent test. + var pc [maxStackLen]uintptr + n := runtime.Callers(2, pc[:]) t = &T{ common: common{ barrier: make(chan bool), @@ -801,6 +854,7 @@ func (t *T) Run(name string, f func(t *T)) bool { name: testName, parent: &t.common, level: t.level + 1, + creator: pc[:n], chatty: t.chatty, }, context: t.context, @@ -822,7 +876,11 @@ func (t *T) Run(name string, f func(t *T)) bool { // without being preempted, even when their parent is a parallel test. This // may especially reduce surprises if *parallel == 1. go tRunner(t, f) - <-t.signal + if !<-t.signal { + // At this point, it is likely that FailNow was called on one of the + // parent tests by one of the subtests. Continue aborting up the chain. + runtime.Goexit() + } return !t.failed } @@ -889,7 +947,6 @@ type matchStringOnly func(pat, str string) (bool, error) func (f matchStringOnly) MatchString(pat, str string) (bool, error) { return f(pat, str) } func (f matchStringOnly) StartCPUProfile(w io.Writer) error { return errMain } 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 "" } func (f matchStringOnly) StartTestLog(io.Writer) {} @@ -929,7 +986,6 @@ type testDeps interface { StopCPUProfile() StartTestLog(io.Writer) StopTestLog() error - WriteHeapProfile(io.Writer) error WriteProfileTo(string, io.Writer, int) error } @@ -1168,7 +1224,7 @@ func (m *M) writeProfiles() { os.Exit(2) } runtime.GC() // materialize all statistics - if err = m.deps.WriteHeapProfile(f); err != nil { + if err = m.deps.WriteProfileTo("allocs", f, 0); err != nil { fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *memProfile, err) os.Exit(2) } |