diff options
author | Ian Lance Taylor <iant@golang.org> | 2020-01-02 15:05:27 -0800 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2020-01-21 23:53:22 -0800 |
commit | 5a8ea165926cb0737ab03bc48c18dc5198ab5305 (patch) | |
tree | 962dc3357c57f019f85658f99e2e753e30201c27 /libgo/go/testing | |
parent | 6ac6529e155c9baa0aaaed7aca06bd38ebda5b43 (diff) | |
download | gcc-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.go | 32 | ||||
-rw-r--r-- | libgo/go/testing/example.go | 4 | ||||
-rw-r--r-- | libgo/go/testing/iotest/logger_test.go | 160 | ||||
-rw-r--r-- | libgo/go/testing/iotest/reader_test.go | 226 | ||||
-rw-r--r-- | libgo/go/testing/iotest/writer_test.go | 39 | ||||
-rw-r--r-- | libgo/go/testing/panic_test.go | 83 | ||||
-rw-r--r-- | libgo/go/testing/sub_test.go | 97 | ||||
-rw-r--r-- | libgo/go/testing/testing.go | 105 |
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 { |