aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/testing
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2018-09-24 21:46:21 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2018-09-24 21:46:21 +0000
commitdd931d9b48647e898dc80927c532ae93cc09e192 (patch)
tree71be2295cd79b8a182f6130611658db8628772d5 /libgo/go/testing
parent779d8a5ad09b01428726ea5a0e6c87bd9ac3c0e4 (diff)
downloadgcc-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.go15
-rw-r--r--libgo/go/testing/cover.go13
-rw-r--r--libgo/go/testing/example.go3
-rw-r--r--libgo/go/testing/helper_test.go8
-rw-r--r--libgo/go/testing/helperfuncs_test.go18
-rw-r--r--libgo/go/testing/internal/testdeps/deps.go4
-rw-r--r--libgo/go/testing/match.go2
-rw-r--r--libgo/go/testing/sub_test.go90
-rw-r--r--libgo/go/testing/testing.go132
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)
}