aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/testing
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2020-01-02 15:05:27 -0800
committerIan Lance Taylor <iant@golang.org>2020-01-21 23:53:22 -0800
commit5a8ea165926cb0737ab03bc48c18dc5198ab5305 (patch)
tree962dc3357c57f019f85658f99e2e753e30201c27 /libgo/go/testing
parent6ac6529e155c9baa0aaaed7aca06bd38ebda5b43 (diff)
downloadgcc-5a8ea165926cb0737ab03bc48c18dc5198ab5305.zip
gcc-5a8ea165926cb0737ab03bc48c18dc5198ab5305.tar.gz
gcc-5a8ea165926cb0737ab03bc48c18dc5198ab5305.tar.bz2
libgo: update to Go1.14beta1
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/214297
Diffstat (limited to 'libgo/go/testing')
-rw-r--r--libgo/go/testing/benchmark.go32
-rw-r--r--libgo/go/testing/example.go4
-rw-r--r--libgo/go/testing/iotest/logger_test.go160
-rw-r--r--libgo/go/testing/iotest/reader_test.go226
-rw-r--r--libgo/go/testing/iotest/writer_test.go39
-rw-r--r--libgo/go/testing/panic_test.go83
-rw-r--r--libgo/go/testing/sub_test.go97
-rw-r--r--libgo/go/testing/testing.go105
8 files changed, 719 insertions, 27 deletions
diff --git a/libgo/go/testing/benchmark.go b/libgo/go/testing/benchmark.go
index 0e348be..0412772 100644
--- a/libgo/go/testing/benchmark.go
+++ b/libgo/go/testing/benchmark.go
@@ -69,8 +69,8 @@ var benchmarkLock sync.Mutex
// Used for every benchmark for measuring memory.
var memStats runtime.MemStats
-// An internal type but exported because it is cross-package; part of the implementation
-// of the "go test" command.
+// InternalBenchmark is an internal type but exported because it is cross-package;
+// it is part of the implementation of the "go test" command.
type InternalBenchmark struct {
Name string
F func(b *B)
@@ -342,7 +342,7 @@ func (b *B) ReportMetric(n float64, unit string) {
b.extra[unit] = n
}
-// The results of a benchmark run.
+// BenchmarkResult contains the results of a benchmark run.
type BenchmarkResult struct {
N int // The number of iterations.
T time.Duration // The total time taken.
@@ -488,8 +488,8 @@ type benchContext struct {
extLen int // Maximum extension length.
}
-// An internal function but exported because it is cross-package; part of the implementation
-// of the "go test" command.
+// RunBenchmarks is an internal function but exported because it is cross-package;
+// it is part of the implementation of the "go test" command.
func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) {
runBenchmarks("", matchString, benchmarks)
}
@@ -545,7 +545,11 @@ func (ctx *benchContext) processBench(b *B) {
for j := uint(0); j < *count; j++ {
runtime.GOMAXPROCS(procs)
benchName := benchmarkName(b.name, procs)
- fmt.Fprintf(b.w, "%-*s\t", ctx.maxLen, benchName)
+
+ // If it's chatty, we've already printed this information.
+ if !b.chatty {
+ fmt.Fprintf(b.w, "%-*s\t", ctx.maxLen, benchName)
+ }
// Recompute the running time for all but the first iteration.
if i > 0 || j > 0 {
b = &B{
@@ -569,6 +573,9 @@ func (ctx *benchContext) processBench(b *B) {
continue
}
results := r.String()
+ if b.chatty {
+ fmt.Fprintf(b.w, "%-*s\t", ctx.maxLen, benchName)
+ }
if *benchmarkMemory || b.showAllocResult {
results += "\t" + r.MemString()
}
@@ -627,6 +634,19 @@ func (b *B) Run(name string, f func(b *B)) bool {
// Only process sub-benchmarks, if any.
atomic.StoreInt32(&sub.hasSub, 1)
}
+
+ if b.chatty {
+ labelsOnce.Do(func() {
+ fmt.Printf("goos: %s\n", runtime.GOOS)
+ fmt.Printf("goarch: %s\n", runtime.GOARCH)
+ if b.importPath != "" {
+ fmt.Printf("pkg: %s\n", b.importPath)
+ }
+ })
+
+ fmt.Println(benchName)
+ }
+
if sub.run1() {
sub.run()
}
diff --git a/libgo/go/testing/example.go b/libgo/go/testing/example.go
index c122121..adc91d5 100644
--- a/libgo/go/testing/example.go
+++ b/libgo/go/testing/example.go
@@ -19,8 +19,8 @@ type InternalExample struct {
Unordered bool
}
-// An internal function but exported because it is cross-package; part of the implementation
-// of the "go test" command.
+// RunExamples is an internal function but exported because it is cross-package;
+// it is part of the implementation of the "go test" command.
func RunExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ok bool) {
_, ok = runExamples(matchString, examples)
return ok
diff --git a/libgo/go/testing/iotest/logger_test.go b/libgo/go/testing/iotest/logger_test.go
new file mode 100644
index 0000000..c121bf4
--- /dev/null
+++ b/libgo/go/testing/iotest/logger_test.go
@@ -0,0 +1,160 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package iotest
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "log"
+ "testing"
+)
+
+type errWriter struct {
+ err error
+}
+
+func (w errWriter) Write([]byte) (int, error) {
+ return 0, w.err
+}
+
+func TestWriteLogger(t *testing.T) {
+ olw := log.Writer()
+ olf := log.Flags()
+ olp := log.Prefix()
+
+ // Revert the original log settings before we exit.
+ defer func() {
+ log.SetFlags(olf)
+ log.SetPrefix(olp)
+ log.SetOutput(olw)
+ }()
+
+ lOut := new(bytes.Buffer)
+ log.SetPrefix("lw: ")
+ log.SetOutput(lOut)
+ log.SetFlags(0)
+
+ lw := new(bytes.Buffer)
+ wl := NewWriteLogger("write:", lw)
+ if _, err := wl.Write([]byte("Hello, World!")); err != nil {
+ t.Fatalf("Unexpectedly failed to write: %v", err)
+ }
+
+ if g, w := lw.String(), "Hello, World!"; g != w {
+ t.Errorf("WriteLogger mismatch\n\tgot: %q\n\twant: %q", g, w)
+ }
+ wantLogWithHex := fmt.Sprintf("lw: write: %x\n", "Hello, World!")
+ if g, w := lOut.String(), wantLogWithHex; g != w {
+ t.Errorf("WriteLogger mismatch\n\tgot: %q\n\twant: %q", g, w)
+ }
+}
+
+func TestWriteLogger_errorOnWrite(t *testing.T) {
+ olw := log.Writer()
+ olf := log.Flags()
+ olp := log.Prefix()
+
+ // Revert the original log settings before we exit.
+ defer func() {
+ log.SetFlags(olf)
+ log.SetPrefix(olp)
+ log.SetOutput(olw)
+ }()
+
+ lOut := new(bytes.Buffer)
+ log.SetPrefix("lw: ")
+ log.SetOutput(lOut)
+ log.SetFlags(0)
+
+ lw := errWriter{err: errors.New("Write Error!")}
+ wl := NewWriteLogger("write:", lw)
+ if _, err := wl.Write([]byte("Hello, World!")); err == nil {
+ t.Fatalf("Unexpectedly succeeded to write: %v", err)
+ }
+
+ wantLogWithHex := fmt.Sprintf("lw: write: %x: %v\n", "", "Write Error!")
+ if g, w := lOut.String(), wantLogWithHex; g != w {
+ t.Errorf("WriteLogger mismatch\n\tgot: %q\n\twant: %q", g, w)
+ }
+}
+
+type errReader struct {
+ err error
+}
+
+func (r errReader) Read([]byte) (int, error) {
+ return 0, r.err
+}
+
+func TestReadLogger(t *testing.T) {
+ olw := log.Writer()
+ olf := log.Flags()
+ olp := log.Prefix()
+
+ // Revert the original log settings before we exit.
+ defer func() {
+ log.SetFlags(olf)
+ log.SetPrefix(olp)
+ log.SetOutput(olw)
+ }()
+
+ lOut := new(bytes.Buffer)
+ log.SetPrefix("lr: ")
+ log.SetOutput(lOut)
+ log.SetFlags(0)
+
+ data := []byte("Hello, World!")
+ p := make([]byte, len(data))
+ lr := bytes.NewReader(data)
+ rl := NewReadLogger("read:", lr)
+
+ n, err := rl.Read(p)
+ if err != nil {
+ t.Fatalf("Unexpectedly failed to read: %v", err)
+ }
+
+ if g, w := p[:n], data; !bytes.Equal(g, w) {
+ t.Errorf("ReadLogger mismatch\n\tgot: %q\n\twant: %q", g, w)
+ }
+
+ wantLogWithHex := fmt.Sprintf("lr: read: %x\n", "Hello, World!")
+ if g, w := lOut.String(), wantLogWithHex; g != w {
+ t.Errorf("ReadLogger mismatch\n\tgot: %q\n\twant: %q", g, w)
+ }
+}
+
+func TestReadLogger_errorOnRead(t *testing.T) {
+ olw := log.Writer()
+ olf := log.Flags()
+ olp := log.Prefix()
+
+ // Revert the original log settings before we exit.
+ defer func() {
+ log.SetFlags(olf)
+ log.SetPrefix(olp)
+ log.SetOutput(olw)
+ }()
+
+ lOut := new(bytes.Buffer)
+ log.SetPrefix("lr: ")
+ log.SetOutput(lOut)
+ log.SetFlags(0)
+
+ data := []byte("Hello, World!")
+ p := make([]byte, len(data))
+
+ lr := errReader{err: errors.New("Read Error!")}
+ rl := NewReadLogger("read", lr)
+ n, err := rl.Read(p)
+ if err == nil {
+ t.Fatalf("Unexpectedly succeeded to read: %v", err)
+ }
+
+ wantLogWithHex := fmt.Sprintf("lr: read %x: %v\n", p[:n], "Read Error!")
+ if g, w := lOut.String(), wantLogWithHex; g != w {
+ t.Errorf("ReadLogger mismatch\n\tgot: %q\n\twant: %q", g, w)
+ }
+}
diff --git a/libgo/go/testing/iotest/reader_test.go b/libgo/go/testing/iotest/reader_test.go
new file mode 100644
index 0000000..9397837
--- /dev/null
+++ b/libgo/go/testing/iotest/reader_test.go
@@ -0,0 +1,226 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package iotest
+
+import (
+ "bytes"
+ "io"
+ "testing"
+)
+
+func TestOneByteReader_nonEmptyReader(t *testing.T) {
+ msg := "Hello, World!"
+ buf := new(bytes.Buffer)
+ buf.WriteString(msg)
+
+ obr := OneByteReader(buf)
+ var b []byte
+ n, err := obr.Read(b)
+ if err != nil || n != 0 {
+ t.Errorf("Empty buffer read returned n=%d err=%v", n, err)
+ }
+
+ b = make([]byte, 3)
+ // Read from obr until EOF.
+ got := new(bytes.Buffer)
+ for i := 0; ; i++ {
+ n, err = obr.Read(b)
+ if err != nil {
+ break
+ }
+ if g, w := n, 1; g != w {
+ t.Errorf("Iteration #%d read %d bytes, want %d", i, g, w)
+ }
+ got.Write(b[:n])
+ }
+ if g, w := err, io.EOF; g != w {
+ t.Errorf("Unexpected error after reading all bytes\n\tGot: %v\n\tWant: %v", g, w)
+ }
+ if g, w := got.String(), "Hello, World!"; g != w {
+ t.Errorf("Read mismatch\n\tGot: %q\n\tWant: %q", g, w)
+ }
+}
+
+func TestOneByteReader_emptyReader(t *testing.T) {
+ r := new(bytes.Buffer)
+
+ obr := OneByteReader(r)
+ var b []byte
+ if n, err := obr.Read(b); err != nil || n != 0 {
+ t.Errorf("Empty buffer read returned n=%d err=%v", n, err)
+ }
+
+ b = make([]byte, 5)
+ n, err := obr.Read(b)
+ if g, w := err, io.EOF; g != w {
+ t.Errorf("Error mismatch\n\tGot: %v\n\tWant: %v", g, w)
+ }
+ if g, w := n, 0; g != w {
+ t.Errorf("Unexpectedly read %d bytes, wanted %d", g, w)
+ }
+}
+
+func TestHalfReader_nonEmptyReader(t *testing.T) {
+ msg := "Hello, World!"
+ buf := new(bytes.Buffer)
+ buf.WriteString(msg)
+ // empty read buffer
+ hr := HalfReader(buf)
+ var b []byte
+ n, err := hr.Read(b)
+ if err != nil || n != 0 {
+ t.Errorf("Empty buffer read returned n=%d err=%v", n, err)
+ }
+ // non empty read buffer
+ b = make([]byte, 2)
+ got := new(bytes.Buffer)
+ for i := 0; ; i++ {
+ n, err = hr.Read(b)
+ if err != nil {
+ break
+ }
+ if g, w := n, 1; g != w {
+ t.Errorf("Iteration #%d read %d bytes, want %d", i, g, w)
+ }
+ got.Write(b[:n])
+ }
+ if g, w := err, io.EOF; g != w {
+ t.Errorf("Unexpected error after reading all bytes\n\tGot: %v\n\tWant: %v", g, w)
+ }
+ if g, w := got.String(), "Hello, World!"; g != w {
+ t.Errorf("Read mismatch\n\tGot: %q\n\tWant: %q", g, w)
+ }
+}
+
+func TestHalfReader_emptyReader(t *testing.T) {
+ r := new(bytes.Buffer)
+
+ hr := HalfReader(r)
+ var b []byte
+ if n, err := hr.Read(b); err != nil || n != 0 {
+ t.Errorf("Empty buffer read returned n=%d err=%v", n, err)
+ }
+
+ b = make([]byte, 5)
+ n, err := hr.Read(b)
+ if g, w := err, io.EOF; g != w {
+ t.Errorf("Error mismatch\n\tGot: %v\n\tWant: %v", g, w)
+ }
+ if g, w := n, 0; g != w {
+ t.Errorf("Unexpectedly read %d bytes, wanted %d", g, w)
+ }
+}
+
+func TestTimeOutReader_nonEmptyReader(t *testing.T) {
+ msg := "Hello, World!"
+ buf := new(bytes.Buffer)
+ buf.WriteString(msg)
+ // empty read buffer
+ tor := TimeoutReader(buf)
+ var b []byte
+ n, err := tor.Read(b)
+ if err != nil || n != 0 {
+ t.Errorf("Empty buffer read returned n=%d err=%v", n, err)
+ }
+ // Second call should timeout
+ n, err = tor.Read(b)
+ if g, w := err, ErrTimeout; g != w {
+ t.Errorf("Error mismatch\n\tGot: %v\n\tWant: %v", g, w)
+ }
+ if g, w := n, 0; g != w {
+ t.Errorf("Unexpectedly read %d bytes, wanted %d", g, w)
+ }
+ // non empty read buffer
+ tor2 := TimeoutReader(buf)
+ b = make([]byte, 3)
+ if n, err := tor2.Read(b); err != nil || n == 0 {
+ t.Errorf("Empty buffer read returned n=%d err=%v", n, err)
+ }
+ // Second call should timeout
+ n, err = tor2.Read(b)
+ if g, w := err, ErrTimeout; g != w {
+ t.Errorf("Error mismatch\n\tGot: %v\n\tWant: %v", g, w)
+ }
+ if g, w := n, 0; g != w {
+ t.Errorf("Unexpectedly read %d bytes, wanted %d", g, w)
+ }
+}
+
+func TestTimeOutReader_emptyReader(t *testing.T) {
+ r := new(bytes.Buffer)
+ // empty read buffer
+ tor := TimeoutReader(r)
+ var b []byte
+ if n, err := tor.Read(b); err != nil || n != 0 {
+ t.Errorf("Empty buffer read returned n=%d err=%v", n, err)
+ }
+ // Second call should timeout
+ n, err := tor.Read(b)
+ if g, w := err, ErrTimeout; g != w {
+ t.Errorf("Error mismatch\n\tGot: %v\n\tWant: %v", g, w)
+ }
+ if g, w := n, 0; g != w {
+ t.Errorf("Unexpectedly read %d bytes, wanted %d", g, w)
+ }
+ // non empty read buffer
+ tor2 := TimeoutReader(r)
+ b = make([]byte, 5)
+ if n, err := tor2.Read(b); err != io.EOF || n != 0 {
+ t.Errorf("Empty buffer read returned n=%d err=%v", n, err)
+ }
+ // Second call should timeout
+ n, err = tor2.Read(b)
+ if g, w := err, ErrTimeout; g != w {
+ t.Errorf("Error mismatch\n\tGot: %v\n\tWant: %v", g, w)
+ }
+ if g, w := n, 0; g != w {
+ t.Errorf("Unexpectedly read %d bytes, wanted %d", g, w)
+ }
+}
+
+func TestDataErrReader_nonEmptyReader(t *testing.T) {
+ msg := "Hello, World!"
+ buf := new(bytes.Buffer)
+ buf.WriteString(msg)
+
+ der := DataErrReader(buf)
+
+ b := make([]byte, 3)
+ got := new(bytes.Buffer)
+ var n int
+ var err error
+ for {
+ n, err = der.Read(b)
+ got.Write(b[:n])
+ if err != nil {
+ break
+ }
+ }
+ if err != io.EOF || n == 0 {
+ t.Errorf("Last Read returned n=%d err=%v", n, err)
+ }
+ if g, w := got.String(), "Hello, World!"; g != w {
+ t.Errorf("Read mismatch\n\tGot: %q\n\tWant: %q", g, w)
+ }
+}
+
+func TestDataErrReader_emptyReader(t *testing.T) {
+ r := new(bytes.Buffer)
+
+ der := DataErrReader(r)
+ var b []byte
+ if n, err := der.Read(b); err != io.EOF || n != 0 {
+ t.Errorf("Empty buffer read returned n=%d err=%v", n, err)
+ }
+
+ b = make([]byte, 5)
+ n, err := der.Read(b)
+ if g, w := err, io.EOF; g != w {
+ t.Errorf("Error mismatch\n\tGot: %v\n\tWant: %v", g, w)
+ }
+ if g, w := n, 0; g != w {
+ t.Errorf("Unexpectedly read %d bytes, wanted %d", g, w)
+ }
+}
diff --git a/libgo/go/testing/iotest/writer_test.go b/libgo/go/testing/iotest/writer_test.go
new file mode 100644
index 0000000..5aaa77c
--- /dev/null
+++ b/libgo/go/testing/iotest/writer_test.go
@@ -0,0 +1,39 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package iotest
+
+import (
+ "bytes"
+ "testing"
+)
+
+var truncateWriterTests = []struct {
+ in string
+ want string
+ trunc int64
+ n int
+}{
+ {"hello", "", -1, 5},
+ {"world", "", 0, 5},
+ {"abcde", "abc", 3, 5},
+ {"edcba", "edcba", 7, 5},
+}
+
+func TestTruncateWriter(t *testing.T) {
+ for _, tt := range truncateWriterTests {
+ buf := new(bytes.Buffer)
+ tw := TruncateWriter(buf, tt.trunc)
+ n, err := tw.Write([]byte(tt.in))
+ if err != nil {
+ t.Errorf("Unexpected error %v for\n\t%+v", err, tt)
+ }
+ if g, w := buf.String(), tt.want; g != w {
+ t.Errorf("got %q, expected %q", g, w)
+ }
+ if g, w := n, tt.n; g != w {
+ t.Errorf("read %d bytes, but expected to have read %d bytes for\n\t%+v", g, w, tt)
+ }
+ }
+}
diff --git a/libgo/go/testing/panic_test.go b/libgo/go/testing/panic_test.go
new file mode 100644
index 0000000..3491510
--- /dev/null
+++ b/libgo/go/testing/panic_test.go
@@ -0,0 +1,83 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing_test
+
+import (
+ "flag"
+ "fmt"
+ "internal/testenv"
+ "os"
+ "os/exec"
+ "regexp"
+ "strings"
+ "testing"
+)
+
+var testPanicTest = flag.String("test_panic_test", "", "TestPanic: indicates which test should panic")
+
+func TestPanic(t *testing.T) {
+ testenv.MustHaveExec(t)
+
+ testCases := []struct {
+ desc string
+ flags []string
+ want string
+ }{{
+ desc: "root test panics",
+ flags: []string{"-test_panic_test=TestPanicHelper"},
+ want: `
+--- FAIL: TestPanicHelper (N.NNs)
+ panic_test.go:NNN: TestPanicHelper
+`,
+ }, {
+ desc: "subtest panics",
+ flags: []string{"-test_panic_test=TestPanicHelper/1"},
+ want: `
+--- FAIL: TestPanicHelper (N.NNs)
+ panic_test.go:NNN: TestPanicHelper
+ --- FAIL: TestPanicHelper/1 (N.NNs)
+ panic_test.go:NNN: TestPanicHelper/1
+`,
+ }}
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ cmd := exec.Command(os.Args[0], "-test.run=TestPanicHelper")
+ cmd.Args = append(cmd.Args, tc.flags...)
+ cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
+ b, _ := cmd.CombinedOutput()
+ got := string(b)
+ want := strings.TrimSpace(tc.want)
+ re := makeRegexp(want)
+ if ok, err := regexp.MatchString(re, got); !ok || err != nil {
+ t.Errorf("output:\ngot:\n%s\nwant:\n%s", got, want)
+ }
+ })
+ }
+}
+
+func makeRegexp(s string) string {
+ s = regexp.QuoteMeta(s)
+ s = strings.ReplaceAll(s, ":NNN:", `:\d+:`)
+ s = strings.ReplaceAll(s, "N\\.NNs", `\d*\.\d*s`)
+ return s
+}
+
+func TestPanicHelper(t *testing.T) {
+ if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
+ return
+ }
+ t.Log(t.Name())
+ if t.Name() == *testPanicTest {
+ panic("panic")
+ }
+ for i := 0; i < 3; i++ {
+ t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
+ t.Log(t.Name())
+ if t.Name() == *testPanicTest {
+ panic("panic")
+ }
+ })
+ }
+}
diff --git a/libgo/go/testing/sub_test.go b/libgo/go/testing/sub_test.go
index cc5dd2f..3f0f71f 100644
--- a/libgo/go/testing/sub_test.go
+++ b/libgo/go/testing/sub_test.go
@@ -7,6 +7,7 @@ package testing
import (
"bytes"
"fmt"
+ "reflect"
"regexp"
"runtime"
"strings"
@@ -434,6 +435,31 @@ func TestTRun(t *T) {
<-ch
t.Errorf("error")
},
+ }, {
+ // A chatty test should always log with fmt.Print, even if the
+ // parent test has completed.
+ // TODO(deklerk) Capture the log of fmt.Print and assert that the
+ // subtest message is not lost.
+ desc: "log in finished sub test with chatty",
+ ok: false,
+ chatty: true,
+ output: `
+ --- FAIL: log in finished sub test with chatty (N.NNs)`,
+ maxPar: 1,
+ f: func(t *T) {
+ ch := make(chan bool)
+ t.Run("sub", func(t2 *T) {
+ go func() {
+ <-ch
+ t2.Log("message1")
+ ch <- true
+ }()
+ })
+ t.Log("message2")
+ ch <- true
+ <-ch
+ t.Errorf("error")
+ },
}}
for _, tc := range testCases {
ctx := newTestContext(tc.maxPar, newMatcher(regexp.MatchString, "", ""))
@@ -522,13 +548,6 @@ func TestBRun(t *T) {
output: "--- SKIP: root",
f: func(b *B) { b.SkipNow() },
}, {
- desc: "skipping with message, chatty",
- chatty: true,
- output: `
---- SKIP: root
- sub_test.go:NNN: skipping`,
- f: func(b *B) { b.Skip("skipping") },
- }, {
desc: "chatty with recursion",
chatty: true,
f: func(b *B) {
@@ -772,3 +791,67 @@ func TestBenchmark(t *T) {
t.Errorf("want >5ms; got %v", time.Duration(res.NsPerOp()))
}
}
+
+func TestCleanup(t *T) {
+ var cleanups []int
+ t.Run("test", func(t *T) {
+ t.Cleanup(func() { cleanups = append(cleanups, 1) })
+ t.Cleanup(func() { cleanups = append(cleanups, 2) })
+ })
+ if got, want := cleanups, []int{2, 1}; !reflect.DeepEqual(got, want) {
+ t.Errorf("unexpected cleanup record; got %v want %v", got, want)
+ }
+}
+
+func TestConcurrentCleanup(t *T) {
+ cleanups := 0
+ t.Run("test", func(t *T) {
+ done := make(chan struct{})
+ for i := 0; i < 2; i++ {
+ i := i
+ go func() {
+ t.Cleanup(func() {
+ cleanups |= 1 << i
+ })
+ done <- struct{}{}
+ }()
+ }
+ <-done
+ <-done
+ })
+ if cleanups != 1|2 {
+ t.Errorf("unexpected cleanup; got %d want 3", cleanups)
+ }
+}
+
+func TestCleanupCalledEvenAfterGoexit(t *T) {
+ cleanups := 0
+ t.Run("test", func(t *T) {
+ t.Cleanup(func() {
+ cleanups++
+ })
+ t.Cleanup(func() {
+ runtime.Goexit()
+ })
+ })
+ if cleanups != 1 {
+ t.Errorf("unexpected cleanup count; got %d want 1", cleanups)
+ }
+}
+
+func TestRunCleanup(t *T) {
+ outerCleanup := 0
+ innerCleanup := 0
+ t.Run("test", func(t *T) {
+ t.Cleanup(func() { outerCleanup++ })
+ t.Run("x", func(t *T) {
+ t.Cleanup(func() { innerCleanup++ })
+ })
+ })
+ if innerCleanup != 1 {
+ t.Errorf("unexpected inner cleanup count; got %d want 1", innerCleanup)
+ }
+ if outerCleanup != 1 {
+ t.Errorf("unexpected outer cleanup count; got %d want 0", outerCleanup)
+ }
+}
diff --git a/libgo/go/testing/testing.go b/libgo/go/testing/testing.go
index 6ab9b79..e05314e 100644
--- a/libgo/go/testing/testing.go
+++ b/libgo/go/testing/testing.go
@@ -99,7 +99,7 @@
// line order:
//
// func ExamplePerm() {
-// for _, value := range Perm(4) {
+// for _, value := range Perm(5) {
// fmt.Println(value)
// }
// // Unordered output: 4
@@ -344,6 +344,7 @@ type common struct {
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
chatty bool // A copy of the chatty flag.
finished bool // Test function has completed.
@@ -479,6 +480,9 @@ 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] == "" {
@@ -540,6 +544,7 @@ func fmtDuration(d time.Duration) string {
// TB is the interface common to T and B.
type TB interface {
+ Cleanup(func())
Error(args ...interface{})
Errorf(format string, args ...interface{})
Fail()
@@ -547,6 +552,7 @@ type TB interface {
Failed() bool
Fatal(args ...interface{})
Fatalf(format string, args ...interface{})
+ Helper()
Log(args ...interface{})
Logf(format string, args ...interface{})
Name() string
@@ -554,7 +560,6 @@ type TB interface {
SkipNow()
Skipf(format string, args ...interface{})
Skipped() bool
- Helper()
// A private method to prevent users implementing the
// interface and so future additions to it will not
@@ -662,9 +667,7 @@ func (c *common) log(s string) {
func (c *common) logDepth(s string, depth int) {
c.mu.Lock()
defer c.mu.Unlock()
- if !c.done {
- c.output = append(c.output, c.decorate(s, depth+1)...)
- } else {
+ if c.done {
// This test has already finished. Try and log this message
// with our parent. If we don't have a parent, panic.
for parent := c.parent; parent != nil; parent = parent.parent {
@@ -676,9 +679,21 @@ 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))
+ return
+ }
+ c.output = append(c.output, c.decorate(s, depth+1)...)
}
}
+// This is needed for gccgo to get the tests to pass, because
+// runtime.Callers doesn't correctly handle skips that land in the
+// middle of a sequence of inlined functions.
+// This shouldn't make any difference for normal use.
+//go:noinline
+
// Log formats its arguments using default formatting, analogous to Println,
// and records the text in the error log. For tests, the text will be printed only if
// the test fails or the -test.v flag is set. For benchmarks, the text is always
@@ -692,30 +707,60 @@ func (c *common) Log(args ...interface{}) { c.log(fmt.Sprintln(args...)) }
// depend on the value of the -test.v flag.
func (c *common) Logf(format string, args ...interface{}) { c.log(fmt.Sprintf(format, args...)) }
+// This is needed for gccgo to get the tests to pass, because
+// runtime.Callers doesn't correctly handle skips that land in the
+// middle of a sequence of inlined functions.
+// This shouldn't make any difference for normal use.
+//go:noinline
+
// Error is equivalent to Log followed by Fail.
func (c *common) Error(args ...interface{}) {
c.log(fmt.Sprintln(args...))
c.Fail()
}
+// This is needed for gccgo to get the tests to pass, because
+// runtime.Callers doesn't correctly handle skips that land in the
+// middle of a sequence of inlined functions.
+// This shouldn't make any difference for normal use.
+//go:noinline
+
// Errorf is equivalent to Logf followed by Fail.
func (c *common) Errorf(format string, args ...interface{}) {
c.log(fmt.Sprintf(format, args...))
c.Fail()
}
+// This is needed for gccgo to get the tests to pass, because
+// runtime.Callers doesn't correctly handle skips that land in the
+// middle of a sequence of inlined functions.
+// This shouldn't make any difference for normal use.
+//go:noinline
+
// Fatal is equivalent to Log followed by FailNow.
func (c *common) Fatal(args ...interface{}) {
c.log(fmt.Sprintln(args...))
c.FailNow()
}
+// This is needed for gccgo to get the tests to pass, because
+// runtime.Callers doesn't correctly handle skips that land in the
+// middle of a sequence of inlined functions.
+// This shouldn't make any difference for normal use.
+//go:noinline
+
// Fatalf is equivalent to Logf followed by FailNow.
func (c *common) Fatalf(format string, args ...interface{}) {
c.log(fmt.Sprintf(format, args...))
c.FailNow()
}
+// This is needed for gccgo to get the tests to pass, because
+// runtime.Callers doesn't correctly handle skips that land in the
+// middle of a sequence of inlined functions.
+// This shouldn't make any difference for normal use.
+//go:noinline
+
// Skip is equivalent to Log followed by SkipNow.
func (c *common) Skip(args ...interface{}) {
c.log(fmt.Sprintln(args...))
@@ -767,11 +812,37 @@ func (c *common) Helper() {
c.helpers[callerName(1)] = struct{}{}
}
+// Cleanup registers a function to be called when the test finishes.
+// Cleanup functions will be called in last added, first called
+// order.
+func (c *common) Cleanup(f func()) {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ oldCleanup := c.cleanup
+ c.cleanup = func() {
+ if oldCleanup != nil {
+ defer oldCleanup()
+ }
+ f()
+ }
+}
+
+// runCleanup is called at the end of the test.
+func (c *common) runCleanup() {
+ c.mu.Lock()
+ cleanup := c.cleanup
+ c.cleanup = nil
+ c.mu.Unlock()
+ if cleanup != nil {
+ cleanup()
+ }
+}
+
// callerName gives the function name (qualified with a package path)
// for the caller after skip frames (where 0 means the current function).
func callerName(skip int) string {
// Make room for the skip PC.
- var pc [1]uintptr
+ var pc [2]uintptr
n := runtime.Callers(skip+2, pc[:]) // skip + runtime.Callers + callerName
if n == 0 {
panic("testing: zero callers found")
@@ -828,8 +899,8 @@ func (t *T) Parallel() {
t.raceErrors += -race.Errors()
}
-// An internal type but exported because it is cross-package; part of the implementation
-// of the "go test" command.
+// InternalTest is an internal type but exported because it is cross-package;
+// it is part of the implementation of the "go test" command.
type InternalTest struct {
Name string
F func(*T)
@@ -853,7 +924,6 @@ func tRunner(t *T, fn func(t *T)) {
t.Errorf("race detected during execution of test")
}
- t.duration += time.Since(t.start)
// If the test panicked, print any test output before dying.
err := recover()
signal := true
@@ -870,10 +940,20 @@ func tRunner(t *T, fn func(t *T)) {
}
if err != nil {
t.Fail()
- t.report()
+ // Flush the output log up to the root before dying.
+ t.mu.Lock()
+ root := &t.common
+ for ; root.parent != nil; root = root.parent {
+ root.duration += time.Since(root.start)
+ fmt.Fprintf(root.parent.w, "--- FAIL: %s (%s)\n", root.name, fmtDuration(root.duration))
+ root.parent.mu.Lock()
+ io.Copy(root.parent.w, bytes.NewReader(root.output))
+ }
panic(err)
}
+ t.duration += time.Since(t.start)
+
if len(t.sub) > 0 {
// Run parallel subtests.
// Decrease the running count for this test.
@@ -903,6 +983,7 @@ func tRunner(t *T, fn func(t *T)) {
}
t.signal <- signal
}()
+ defer t.runCleanup()
t.start = time.Now()
t.raceErrors = -race.Errors()
@@ -1169,8 +1250,8 @@ func listTests(matchString func(pat, str string) (bool, error), tests []Internal
}
}
-// An internal function but exported because it is cross-package; part of the implementation
-// of the "go test" command.
+// 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)
if !ran && !haveExamples {