aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/testing
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2017-09-14 17:11:35 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2017-09-14 17:11:35 +0000
commitbc998d034f45d1828a8663b2eed928faf22a7d01 (patch)
tree8d262a22ca7318f4bcd64269fe8fe9e45bcf8d0f /libgo/go/testing
parenta41a6142df74219f596e612d3a7775f68ca6e96f (diff)
downloadgcc-bc998d034f45d1828a8663b2eed928faf22a7d01.zip
gcc-bc998d034f45d1828a8663b2eed928faf22a7d01.tar.gz
gcc-bc998d034f45d1828a8663b2eed928faf22a7d01.tar.bz2
libgo: update to go1.9
Reviewed-on: https://go-review.googlesource.com/63753 From-SVN: r252767
Diffstat (limited to 'libgo/go/testing')
-rw-r--r--libgo/go/testing/benchmark.go44
-rw-r--r--libgo/go/testing/helper_test.go70
-rw-r--r--libgo/go/testing/helperfuncs_test.go67
-rw-r--r--libgo/go/testing/internal/testdeps/deps.go7
-rw-r--r--libgo/go/testing/match.go9
-rw-r--r--libgo/go/testing/match_test.go67
-rw-r--r--libgo/go/testing/quick/quick.go30
-rw-r--r--libgo/go/testing/quick/quick_test.go18
-rw-r--r--libgo/go/testing/sub_test.go93
-rw-r--r--libgo/go/testing/testing.go188
10 files changed, 496 insertions, 97 deletions
diff --git a/libgo/go/testing/benchmark.go b/libgo/go/testing/benchmark.go
index bcebb41..84005aa3 100644
--- a/libgo/go/testing/benchmark.go
+++ b/libgo/go/testing/benchmark.go
@@ -47,6 +47,7 @@ type InternalBenchmark struct {
// affecting benchmark results.
type B struct {
common
+ importPath string // import path of the package containing the benchmark
context *benchContext
N int
previousN int // number of iterations in the previous run
@@ -233,9 +234,18 @@ func (b *B) run1() bool {
return true
}
+var labelsOnce sync.Once
+
// run executes the benchmark in a separate goroutine, including all of its
// subbenchmarks. b must not have subbenchmarks.
func (b *B) run() BenchmarkResult {
+ labelsOnce.Do(func() {
+ fmt.Fprintf(b.w, "goos: %s\n", runtime.GOOS)
+ fmt.Fprintf(b.w, "goarch: %s\n", runtime.GOARCH)
+ if b.importPath != "" {
+ fmt.Fprintf(b.w, "pkg: %s\n", b.importPath)
+ }
+ })
if b.context != nil {
// Running go test --test.bench
b.context.processBench(b) // Must call doBench.
@@ -306,6 +316,7 @@ func (r BenchmarkResult) mbPerSec() float64 {
return (float64(r.Bytes) * float64(r.N) / 1e6) / r.T.Seconds()
}
+// AllocsPerOp returns r.MemAllocs / r.N.
func (r BenchmarkResult) AllocsPerOp() int64 {
if r.N <= 0 {
return 0
@@ -313,6 +324,7 @@ func (r BenchmarkResult) AllocsPerOp() int64 {
return int64(r.MemAllocs) / int64(r.N)
}
+// AllocedBytesPerOp returns r.MemBytes / r.N.
func (r BenchmarkResult) AllocedBytesPerOp() int64 {
if r.N <= 0 {
return 0
@@ -340,6 +352,7 @@ func (r BenchmarkResult) String() string {
return fmt.Sprintf("%8d\t%s%s", r.N, ns, mb)
}
+// MemString returns r.AllocedBytesPerOp and r.AllocsPerOp in the same format as 'go test'.
func (r BenchmarkResult) MemString() string {
return fmt.Sprintf("%8d B/op\t%8d allocs/op",
r.AllocedBytesPerOp(), r.AllocsPerOp())
@@ -363,10 +376,10 @@ type benchContext struct {
// An internal function but exported because it is cross-package; part of the implementation
// of the "go test" command.
func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) {
- runBenchmarks(matchString, benchmarks)
+ runBenchmarks("", matchString, benchmarks)
}
-func runBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) bool {
+func runBenchmarks(importPath string, matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) bool {
// If no flag was specified, don't run benchmarks.
if len(*matchBenchmarks) == 0 {
return true
@@ -384,7 +397,7 @@ func runBenchmarks(matchString func(pat, str string) (bool, error), benchmarks [
}
var bs []InternalBenchmark
for _, Benchmark := range benchmarks {
- if _, matched := ctx.match.fullName(nil, Benchmark.Name); matched {
+ if _, matched, _ := ctx.match.fullName(nil, Benchmark.Name); matched {
bs = append(bs, Benchmark)
benchName := benchmarkName(Benchmark.Name, maxprocs)
if l := len(benchName) + ctx.extLen + 1; l > ctx.maxLen {
@@ -398,6 +411,7 @@ func runBenchmarks(matchString func(pat, str string) (bool, error), benchmarks [
w: os.Stdout,
chatty: *chatty,
},
+ importPath: importPath,
benchFunc: func(b *B) {
for _, Benchmark := range bs {
b.Run(Benchmark.Name, Benchmark.F)
@@ -462,7 +476,7 @@ func (ctx *benchContext) processBench(b *B) {
// least once will not be measured itself and will be called once with N=1.
//
// Run may be called simultaneously from multiple goroutines, but all such
-// calls must happen before the outer benchmark function for b returns.
+// calls must return before the outer benchmark function for b returns.
func (b *B) Run(name string, f func(b *B)) bool {
// Since b has subbenchmarks, we will no longer run it as a benchmark itself.
// Release the lock and acquire it on exit to ensure locks stay paired.
@@ -470,9 +484,9 @@ func (b *B) Run(name string, f func(b *B)) bool {
benchmarkLock.Unlock()
defer benchmarkLock.Lock()
- benchName, ok := b.name, true
+ benchName, ok, partial := b.name, true, false
if b.context != nil {
- benchName, ok = b.context.match.fullName(&b.common, name)
+ benchName, ok, partial = b.context.match.fullName(&b.common, name)
}
if !ok {
return true
@@ -486,9 +500,15 @@ func (b *B) Run(name string, f func(b *B)) bool {
w: b.w,
chatty: b.chatty,
},
- benchFunc: f,
- benchTime: b.benchTime,
- context: b.context,
+ importPath: b.importPath,
+ benchFunc: f,
+ benchTime: b.benchTime,
+ context: b.context,
+ }
+ if partial {
+ // Partial name match, like -bench=X/Y matching BenchmarkX.
+ // Only process sub-benchmarks, if any.
+ atomic.StoreInt32(&sub.hasSub, 1)
}
if sub.run1() {
sub.run()
@@ -634,10 +654,10 @@ func Benchmark(f func(b *B)) BenchmarkResult {
benchFunc: f,
benchTime: *benchTime,
}
- if !b.run1() {
- return BenchmarkResult{}
+ if b.run1() {
+ b.run()
}
- return b.run()
+ return b.result
}
type discard struct{}
diff --git a/libgo/go/testing/helper_test.go b/libgo/go/testing/helper_test.go
new file mode 100644
index 0000000..f5cb27c
--- /dev/null
+++ b/libgo/go/testing/helper_test.go
@@ -0,0 +1,70 @@
+// Copyright 2017 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
+
+import (
+ "bytes"
+ "regexp"
+ "strings"
+)
+
+func TestTBHelper(t *T) {
+ var buf bytes.Buffer
+ ctx := newTestContext(1, newMatcher(regexp.MatchString, "", ""))
+ t1 := &T{
+ common: common{
+ signal: make(chan bool),
+ w: &buf,
+ },
+ context: ctx,
+ }
+ t1.Run("Test", testHelper)
+
+ want := `--- FAIL: Test (?s)
+helperfuncs_test.go:12: 0
+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
+`
+ lines := strings.Split(buf.String(), "\n")
+ durationRE := regexp.MustCompile(`\(.*\)$`)
+ for i, line := range lines {
+ line = strings.TrimSpace(line)
+ line = durationRE.ReplaceAllString(line, "(?s)")
+ lines[i] = line
+ }
+ got := strings.Join(lines, "\n")
+ if got != want {
+ t.Errorf("got output:\n\n%s\nwant:\n\n%s", got, want)
+ }
+}
+
+func TestTBHelperParallel(t *T) {
+ var buf bytes.Buffer
+ ctx := newTestContext(1, newMatcher(regexp.MatchString, "", ""))
+ t1 := &T{
+ common: common{
+ signal: make(chan bool),
+ w: &buf,
+ },
+ context: ctx,
+ }
+ t1.Run("Test", parallelTestHelper)
+
+ lines := strings.Split(strings.TrimSpace(buf.String()), "\n")
+ if len(lines) != 6 {
+ t.Fatalf("parallelTestHelper gave %d lines of output; want 6", len(lines))
+ }
+ want := "helperfuncs_test.go:21: parallel"
+ if got := strings.TrimSpace(lines[1]); got != want {
+ t.Errorf("got output line %q; want %q", got, want)
+ }
+}
diff --git a/libgo/go/testing/helperfuncs_test.go b/libgo/go/testing/helperfuncs_test.go
new file mode 100644
index 0000000..7cb2e2c
--- /dev/null
+++ b/libgo/go/testing/helperfuncs_test.go
@@ -0,0 +1,67 @@
+// Copyright 2017 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
+
+import "sync"
+
+// The line numbering of this file is important for TestTBHelper.
+
+func notHelper(t *T, msg string) {
+ t.Error(msg)
+}
+
+func helper(t *T, msg string) {
+ t.Helper()
+ t.Error(msg)
+}
+
+func notHelperCallingHelper(t *T, msg string) {
+ helper(t, msg)
+}
+
+func helperCallingHelper(t *T, msg string) {
+ t.Helper()
+ helper(t, msg)
+}
+
+func testHelper(t *T) {
+ // Check combinations of directly and indirectly
+ // calling helper functions.
+ notHelper(t, "0")
+ helper(t, "1")
+ notHelperCallingHelper(t, "2")
+ helperCallingHelper(t, "3")
+
+ // Check a function literal closing over t that uses Helper.
+ fn := func(msg string) {
+ t.Helper()
+ t.Error(msg)
+ }
+ 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")
+ t.Helper()
+ t.Error("8")
+ })
+}
+
+func parallelTestHelper(t *T) {
+ var wg sync.WaitGroup
+ for i := 0; i < 5; i++ {
+ wg.Add(1)
+ go func() {
+ notHelperCallingHelper(t, "parallel")
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+}
diff --git a/libgo/go/testing/internal/testdeps/deps.go b/libgo/go/testing/internal/testdeps/deps.go
index b08300b..042f696 100644
--- a/libgo/go/testing/internal/testdeps/deps.go
+++ b/libgo/go/testing/internal/testdeps/deps.go
@@ -49,3 +49,10 @@ func (TestDeps) WriteHeapProfile(w io.Writer) error {
func (TestDeps) WriteProfileTo(name string, w io.Writer, debug int) error {
return pprof.Lookup(name).WriteTo(w, debug)
}
+
+// ImportPath is the import path of the testing binary, set by the generated main function.
+var ImportPath string
+
+func (TestDeps) ImportPath() string {
+ return ImportPath
+}
diff --git a/libgo/go/testing/match.go b/libgo/go/testing/match.go
index 7751035..89e30d0 100644
--- a/libgo/go/testing/match.go
+++ b/libgo/go/testing/match.go
@@ -47,7 +47,7 @@ func newMatcher(matchString func(pat, str string) (bool, error), patterns, name
}
}
-func (m *matcher) fullName(c *common, subname string) (name string, ok bool) {
+func (m *matcher) fullName(c *common, subname string) (name string, ok, partial bool) {
name = subname
m.mu.Lock()
@@ -62,15 +62,16 @@ func (m *matcher) fullName(c *common, subname string) (name string, ok bool) {
// We check the full array of paths each time to allow for the case that
// a pattern contains a '/'.
- for i, s := range strings.Split(name, "/") {
+ elem := strings.Split(name, "/")
+ for i, s := range elem {
if i >= len(m.filter) {
break
}
if ok, _ := m.matchFunc(m.filter[i], s); !ok {
- return name, false
+ return name, false, false
}
}
- return name, true
+ return name, true, len(elem) < len(m.filter)
}
func splitRegexp(s string) []string {
diff --git a/libgo/go/testing/match_test.go b/libgo/go/testing/match_test.go
index 8c1c5f4..8c09dc6 100644
--- a/libgo/go/testing/match_test.go
+++ b/libgo/go/testing/match_test.go
@@ -88,43 +88,44 @@ func TestMatcher(t *T) {
pattern string
parent, sub string
ok bool
+ partial bool
}{
// Behavior without subtests.
- {"", "", "TestFoo", true},
- {"TestFoo", "", "TestFoo", true},
- {"TestFoo/", "", "TestFoo", true},
- {"TestFoo/bar/baz", "", "TestFoo", true},
- {"TestFoo", "", "TestBar", false},
- {"TestFoo/", "", "TestBar", false},
- {"TestFoo/bar/baz", "", "TestBar/bar/baz", false},
+ {"", "", "TestFoo", true, false},
+ {"TestFoo", "", "TestFoo", true, false},
+ {"TestFoo/", "", "TestFoo", true, true},
+ {"TestFoo/bar/baz", "", "TestFoo", true, true},
+ {"TestFoo", "", "TestBar", false, false},
+ {"TestFoo/", "", "TestBar", false, false},
+ {"TestFoo/bar/baz", "", "TestBar/bar/baz", false, false},
// with subtests
- {"", "TestFoo", "x", true},
- {"TestFoo", "TestFoo", "x", true},
- {"TestFoo/", "TestFoo", "x", true},
- {"TestFoo/bar/baz", "TestFoo", "bar", true},
+ {"", "TestFoo", "x", true, false},
+ {"TestFoo", "TestFoo", "x", true, false},
+ {"TestFoo/", "TestFoo", "x", true, false},
+ {"TestFoo/bar/baz", "TestFoo", "bar", true, true},
// Subtest with a '/' in its name still allows for copy and pasted names
// to match.
- {"TestFoo/bar/baz", "TestFoo", "bar/baz", true},
- {"TestFoo/bar/baz", "TestFoo/bar", "baz", true},
- {"TestFoo/bar/baz", "TestFoo", "x", false},
- {"TestFoo", "TestBar", "x", false},
- {"TestFoo/", "TestBar", "x", false},
- {"TestFoo/bar/baz", "TestBar", "x/bar/baz", false},
+ {"TestFoo/bar/baz", "TestFoo", "bar/baz", true, false},
+ {"TestFoo/bar/baz", "TestFoo/bar", "baz", true, false},
+ {"TestFoo/bar/baz", "TestFoo", "x", false, false},
+ {"TestFoo", "TestBar", "x", false, false},
+ {"TestFoo/", "TestBar", "x", false, false},
+ {"TestFoo/bar/baz", "TestBar", "x/bar/baz", false, false},
// subtests only
- {"", "TestFoo", "x", true},
- {"/", "TestFoo", "x", true},
- {"./", "TestFoo", "x", true},
- {"./.", "TestFoo", "x", true},
- {"/bar/baz", "TestFoo", "bar", true},
- {"/bar/baz", "TestFoo", "bar/baz", true},
- {"//baz", "TestFoo", "bar/baz", true},
- {"//", "TestFoo", "bar/baz", true},
- {"/bar/baz", "TestFoo/bar", "baz", true},
- {"//foo", "TestFoo", "bar/baz", false},
- {"/bar/baz", "TestFoo", "x", false},
- {"/bar/baz", "TestBar", "x/bar/baz", false},
+ {"", "TestFoo", "x", true, false},
+ {"/", "TestFoo", "x", true, false},
+ {"./", "TestFoo", "x", true, false},
+ {"./.", "TestFoo", "x", true, false},
+ {"/bar/baz", "TestFoo", "bar", true, true},
+ {"/bar/baz", "TestFoo", "bar/baz", true, false},
+ {"//baz", "TestFoo", "bar/baz", true, false},
+ {"//", "TestFoo", "bar/baz", true, false},
+ {"/bar/baz", "TestFoo/bar", "baz", true, false},
+ {"//foo", "TestFoo", "bar/baz", false, false},
+ {"/bar/baz", "TestFoo", "x", false, false},
+ {"/bar/baz", "TestBar", "x/bar/baz", false, false},
}
for _, tc := range testCases {
@@ -134,9 +135,9 @@ func TestMatcher(t *T) {
if tc.parent != "" {
parent.level = 1
}
- if n, ok := m.fullName(parent, tc.sub); ok != tc.ok {
- t.Errorf("for pattern %q, fullName(parent=%q, sub=%q) = %q, ok %v; want ok %v",
- tc.pattern, tc.parent, tc.sub, n, ok, tc.ok)
+ if n, ok, partial := m.fullName(parent, tc.sub); ok != tc.ok || partial != tc.partial {
+ t.Errorf("for pattern %q, fullName(parent=%q, sub=%q) = %q, ok %v partial %v; want ok %v partial %v",
+ tc.pattern, tc.parent, tc.sub, n, ok, partial, tc.ok, tc.partial)
}
}
}
@@ -178,7 +179,7 @@ func TestNaming(t *T) {
}
for i, tc := range testCases {
- if got, _ := m.fullName(parent, tc.name); got != tc.want {
+ if got, _, _ := m.fullName(parent, tc.name); got != tc.want {
t.Errorf("%d:%s: got %q; want %q", i, tc.name, got, tc.want)
}
}
diff --git a/libgo/go/testing/quick/quick.go b/libgo/go/testing/quick/quick.go
index 95860fd..0457fc7 100644
--- a/libgo/go/testing/quick/quick.go
+++ b/libgo/go/testing/quick/quick.go
@@ -14,6 +14,7 @@ import (
"math/rand"
"reflect"
"strings"
+ "time"
)
var defaultMaxCount *int = flag.Int("quickchecks", 100, "The default number of iterations for each check")
@@ -43,8 +44,10 @@ func randFloat64(rand *rand.Rand) float64 {
return f
}
-// randInt64 returns a random integer taking half the range of an int64.
-func randInt64(rand *rand.Rand) int64 { return rand.Int63() - 1<<62 }
+// randInt64 returns a random int64.
+func randInt64(rand *rand.Rand) int64 {
+ return int64(rand.Uint64())
+}
// complexSize is the maximum length of arbitrary values that contain other
// values.
@@ -172,19 +175,20 @@ func sizedValue(t reflect.Type, rand *rand.Rand, size int) (value reflect.Value,
// A Config structure contains options for running a test.
type Config struct {
- // MaxCount sets the maximum number of iterations. If zero,
- // MaxCountScale is used.
+ // MaxCount sets the maximum number of iterations.
+ // If zero, MaxCountScale is used.
MaxCount int
- // MaxCountScale is a non-negative scale factor applied to the default
- // maximum. If zero, the default is unchanged.
+ // MaxCountScale is a non-negative scale factor applied to the
+ // default maximum.
+ // If zero, the default is unchanged.
MaxCountScale float64
- // If non-nil, rand is a source of random numbers. Otherwise a default
- // pseudo-random source will be used.
+ // Rand specifies a source of random numbers.
+ // If nil, a default pseudo-random source will be used.
Rand *rand.Rand
- // If non-nil, the Values function generates a slice of arbitrary
- // reflect.Values that are congruent with the arguments to the function
- // being tested. Otherwise, the top-level Value function is used
- // to generate them.
+ // Values specifies a function to generate a slice of
+ // arbitrary reflect.Values that are congruent with the
+ // arguments to the function being tested.
+ // If nil, the top-level Value function is used to generate them.
Values func([]reflect.Value, *rand.Rand)
}
@@ -193,7 +197,7 @@ var defaultConfig Config
// getRand returns the *rand.Rand to use for a given Config.
func (c *Config) getRand() *rand.Rand {
if c.Rand == nil {
- return rand.New(rand.NewSource(0))
+ return rand.New(rand.NewSource(time.Now().UnixNano()))
}
return c.Rand
}
diff --git a/libgo/go/testing/quick/quick_test.go b/libgo/go/testing/quick/quick_test.go
index fe44359..4246cd1 100644
--- a/libgo/go/testing/quick/quick_test.go
+++ b/libgo/go/testing/quick/quick_test.go
@@ -307,3 +307,21 @@ func TestNonZeroSliceAndMap(t *testing.T) {
t.Fatal(err)
}
}
+
+func TestInt64(t *testing.T) {
+ var lo, hi int64
+ f := func(x int64) bool {
+ if x < lo {
+ lo = x
+ }
+ if x > hi {
+ hi = x
+ }
+ return true
+ }
+ cfg := &Config{MaxCount: 100000}
+ Check(f, cfg)
+ if uint64(lo)>>62 == 0 || uint64(hi)>>62 == 0 {
+ t.Errorf("int64 returned range %#016x,%#016x; does not look like full range", lo, hi)
+ }
+}
diff --git a/libgo/go/testing/sub_test.go b/libgo/go/testing/sub_test.go
index bb7b3e0..acf5dea 100644
--- a/libgo/go/testing/sub_test.go
+++ b/libgo/go/testing/sub_test.go
@@ -8,11 +8,18 @@ import (
"bytes"
"fmt"
"regexp"
+ "runtime"
"strings"
+ "sync"
"sync/atomic"
"time"
)
+func init() {
+ // Make benchmark tests run 10* faster.
+ *benchTime = 100 * time.Millisecond
+}
+
func TestTestContext(t *T) {
const (
add1 = 0
@@ -455,8 +462,14 @@ func TestBRun(t *T) {
_ = append([]byte(nil), buf[:]...)
}
}
- b.Run("", func(b *B) { alloc(b) })
- b.Run("", func(b *B) { alloc(b) })
+ b.Run("", func(b *B) {
+ alloc(b)
+ b.ReportAllocs()
+ })
+ b.Run("", func(b *B) {
+ alloc(b)
+ b.ReportAllocs()
+ })
// runtime.MemStats sometimes reports more allocations than the
// benchmark is responsible for. Luckily the point of this test is
// to ensure that the results are not underreported, so we can
@@ -517,6 +530,26 @@ func TestBenchmarkOutput(t *T) {
Benchmark(func(b *B) {})
}
+func TestBenchmarkStartsFrom1(t *T) {
+ var first = true
+ Benchmark(func(b *B) {
+ if first && b.N != 1 {
+ panic(fmt.Sprintf("Benchmark() first N=%v; want 1", b.N))
+ }
+ first = false
+ })
+}
+
+func TestBenchmarkReadMemStatsBeforeFirstRun(t *T) {
+ var first = true
+ Benchmark(func(b *B) {
+ if first && (b.startAllocs == 0 || b.startBytes == 0) {
+ panic(fmt.Sprintf("ReadMemStats not called before first run"))
+ }
+ first = false
+ })
+}
+
func TestParallelSub(t *T) {
c := make(chan int)
block := make(chan int)
@@ -532,3 +565,59 @@ func TestParallelSub(t *T) {
<-c
}
}
+
+type funcWriter func([]byte) (int, error)
+
+func (fw funcWriter) Write(b []byte) (int, error) { return fw(b) }
+
+func TestRacyOutput(t *T) {
+ var runs int32 // The number of running Writes
+ var races int32 // Incremented for each race detected
+ raceDetector := func(b []byte) (int, error) {
+ // Check if some other goroutine is concurrently calling Write.
+ if atomic.LoadInt32(&runs) > 0 {
+ atomic.AddInt32(&races, 1) // Race detected!
+ }
+ atomic.AddInt32(&runs, 1)
+ defer atomic.AddInt32(&runs, -1)
+ runtime.Gosched() // Increase probability of a race
+ return len(b), nil
+ }
+
+ var wg sync.WaitGroup
+ root := &T{
+ common: common{w: funcWriter(raceDetector), chatty: true},
+ context: newTestContext(1, newMatcher(regexp.MatchString, "", "")),
+ }
+ root.Run("", func(t *T) {
+ for i := 0; i < 100; i++ {
+ wg.Add(1)
+ go func(i int) {
+ defer wg.Done()
+ t.Run(fmt.Sprint(i), func(t *T) {
+ t.Logf("testing run %d", i)
+ })
+ }(i)
+ }
+ })
+ wg.Wait()
+
+ if races > 0 {
+ t.Errorf("detected %d racy Writes", races)
+ }
+}
+
+func TestBenchmark(t *T) {
+ res := Benchmark(func(b *B) {
+ for i := 0; i < 5; i++ {
+ b.Run("", func(b *B) {
+ for i := 0; i < b.N; i++ {
+ time.Sleep(time.Millisecond)
+ }
+ })
+ }
+ })
+ if res.NsPerOp() < 4000000 {
+ t.Errorf("want >5ms; got %v", time.Duration(res.NsPerOp()))
+ }
+}
diff --git a/libgo/go/testing/testing.go b/libgo/go/testing/testing.go
index b002aa0..a629742 100644
--- a/libgo/go/testing/testing.go
+++ b/libgo/go/testing/testing.go
@@ -83,16 +83,30 @@
// ignores leading and trailing space.) These are examples of an example:
//
// func ExampleHello() {
-// fmt.Println("hello")
-// // Output: hello
+// fmt.Println("hello")
+// // Output: hello
// }
//
// func ExampleSalutations() {
-// fmt.Println("hello, and")
-// fmt.Println("goodbye")
-// // Output:
-// // hello, and
-// // goodbye
+// fmt.Println("hello, and")
+// fmt.Println("goodbye")
+// // Output:
+// // hello, and
+// // goodbye
+// }
+//
+// The comment prefix "Unordered output:" is like "Output:", but matches any
+// line order:
+//
+// func ExamplePerm() {
+// for _, value := range Perm(4) {
+// fmt.Println(value)
+// }
+// // Unordered output: 4
+// // 2
+// // 1
+// // 3
+// // 0
// }
//
// Example functions without output comments are compiled but not executed.
@@ -238,6 +252,7 @@ var (
chatty = flag.Bool("test.v", false, "verbose: print additional output")
count = flag.Uint("test.count", 1, "run tests and benchmarks `n` times")
coverProfile = flag.String("test.coverprofile", "", "write a coverage profile to `file`")
+ matchList = flag.String("test.list", "", "list tests, examples, and benchmarch maching `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)")
@@ -247,7 +262,7 @@ var (
mutexProfile = flag.String("test.mutexprofile", "", "write a mutex contention profile to the named file after execution")
mutexProfileFraction = flag.Int("test.mutexprofilefraction", 1, "if >= 0, calls runtime.SetMutexProfileFraction()")
traceFile = flag.String("test.trace", "", "write an execution trace to `file`")
- timeout = flag.Duration("test.timeout", 0, "fail test binary execution after duration `d` (0 means unlimited)")
+ timeout = flag.Duration("test.timeout", 0, "panic test binary after duration `d` (0 means unlimited)")
cpuListStr = flag.String("test.cpu", "", "comma-separated `list` of cpu counts to run each test with")
parallel = flag.Int("test.parallel", runtime.GOMAXPROCS(0), "run at most `n` tests in parallel")
@@ -259,17 +274,20 @@ var (
// common holds the elements common between T and B and
// captures common methods such as Errorf.
type common struct {
- mu sync.RWMutex // guards output, failed, and done.
- output []byte // Output generated by test or benchmark.
- w io.Writer // For flushToParent.
- chatty bool // A copy of the chatty flag.
- ran bool // Test or benchmark (or one of its subtests) was executed.
- failed bool // Test or benchmark has failed.
- skipped bool // Test of benchmark has been skipped.
- finished bool // Test function has completed.
- done bool // Test is finished and all subtests have completed.
- hasSub int32 // written atomically
- raceErrors int // number of races detected during test
+ mu sync.RWMutex // guards this group of fields
+ output []byte // Output generated by test or benchmark.
+ w io.Writer // For flushToParent.
+ ran bool // Test or benchmark (or one of its subtests) was executed.
+ failed bool // Test or benchmark has failed.
+ skipped bool // Test of benchmark has been skipped.
+ done bool // Test is finished and all subtests have completed.
+ helpers map[string]struct{} // functions to be skipped when writing file/line info
+
+ chatty bool // A copy of the chatty flag.
+ finished bool // Test function has completed.
+ hasSub int32 // written atomically
+ raceErrors int // number of races detected during test
+ runner string // function name of tRunner running the test
parent *common
level int // Nesting depth of test or benchmark.
@@ -298,10 +316,48 @@ func Verbose() bool {
return *chatty
}
+// 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.
+// 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
+ // Skip two extra frames to account for this function
+ // and runtime.Callers itself.
+ n := runtime.Callers(skip+2, pc[:])
+ if n == 0 {
+ panic("testing: zero callers found")
+ }
+ frames := runtime.CallersFrames(pc[:n])
+ var frame runtime.Frame
+ more := true
+ for i := 0; more; i++ {
+ frame, more = frames.Next()
+ 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 _, ok := c.helpers[frame.Function]; !ok {
+ // Found a frame that wasn't inside a helper function.
+ return skip + i
+ }
+ }
+ return skip
+}
+
// 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.
-func decorate(s string) string {
- _, file, line, ok := runtime.Caller(3) // decorate + log + public function.
+// 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 {
// Truncate file name at last file name separator.
if index := strings.LastIndex(file, "/"); index >= 0 {
@@ -391,6 +447,7 @@ 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
@@ -450,8 +507,9 @@ func (c *common) Fail() {
// Failed reports whether the function has failed.
func (c *common) Failed() bool {
c.mu.RLock()
- defer c.mu.RUnlock()
- return c.failed
+ failed := c.failed
+ c.mu.RUnlock()
+ return failed || c.raceErrors+race.Errors() > 0
}
// FailNow marks the function as having failed and stops its execution.
@@ -490,7 +548,7 @@ func (c *common) FailNow() {
func (c *common) log(s string) {
c.mu.Lock()
defer c.mu.Unlock()
- c.output = append(c.output, decorate(s)...)
+ c.output = append(c.output, c.decorate(s)...)
}
// Log formats its arguments using default formatting, analogous to Println,
@@ -568,8 +626,38 @@ func (c *common) Skipped() bool {
return c.skipped
}
+// 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()
+ if c.helpers == nil {
+ c.helpers = make(map[string]struct{})
+ }
+ c.helpers[callerName(1)] = struct{}{}
+}
+
+// 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 [2]uintptr
+ n := runtime.Callers(skip+2, pc[:]) // skip + runtime.Callers + callerName
+ if n == 0 {
+ panic("testing: zero callers found")
+ }
+ frames := runtime.CallersFrames(pc[:n])
+ frame, _ := frames.Next()
+ return frame.Function
+}
+
// Parallel signals that this test is to be run in parallel with (and only with)
-// other parallel tests.
+// other parallel tests. When a test is run multiple times due to use of
+// -test.count or -test.cpu, multiple instances of a single test never run in
+// parallel with each other.
func (t *T) Parallel() {
if t.isParallel {
panic("testing: t.Parallel called multiple times")
@@ -600,13 +688,14 @@ type InternalTest struct {
}
func tRunner(t *T, fn func(t *T)) {
+ t.runner = callerName(0)
+
// When this goroutine is done, either because fn(t)
// returned normally or because a test failure triggered
// a call to runtime.Goexit, record the duration and send
// a signal saying that the test is done.
defer func() {
- t.raceErrors += race.Errors()
- if t.raceErrors > 0 {
+ if t.raceErrors+race.Errors() > 0 {
t.Errorf("race detected during execution of test")
}
@@ -658,14 +747,15 @@ func tRunner(t *T, fn func(t *T)) {
t.finished = true
}
-// Run runs f as a subtest of t called name. It reports whether f succeeded.
-// Run will block until all its parallel subtests have completed.
+// Run runs f as a subtest of t called name. It reports whether f succeeded. Run
+// runs f in a separate goroutine and will block until all its parallel subtests
+// have completed.
//
-// Run may be called simultaneously from multiple goroutines, but all such
-// calls must happen before the outer test function for t returns.
+// Run may be called simultaneously from multiple goroutines, but all such calls
+// must return before the outer test function for t returns.
func (t *T) Run(name string, f func(t *T)) bool {
atomic.StoreInt32(&t.hasSub, 1)
- testName, ok := t.context.match.fullName(&t.common, name)
+ testName, ok, _ := t.context.match.fullName(&t.common, name)
if !ok {
return true
}
@@ -687,7 +777,9 @@ func (t *T) Run(name string, f func(t *T)) bool {
root := t.parent
for ; root.parent != nil; root = root.parent {
}
+ root.mu.Lock()
fmt.Fprintf(root.w, "=== RUN %s\n", t.name)
+ root.mu.Unlock()
}
// Instead of reducing the running count of this test before calling the
// tRunner and increasing it afterwards, we rely on tRunner keeping the
@@ -764,6 +856,7 @@ func (f matchStringOnly) StartCPUProfile(w io.Writer) error { return e
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 "" }
// Main is an internal function, part of the implementation of the "go test" command.
// It was exported because it is cross-package and predates "internal" packages.
@@ -793,6 +886,7 @@ type testDeps interface {
StopCPUProfile()
WriteHeapProfile(io.Writer) error
WriteProfileTo(string, io.Writer, int) error
+ ImportPath() string
}
// MainStart is meant for use by tests generated by 'go test'.
@@ -814,6 +908,11 @@ func (m *M) Run() int {
flag.Parse()
}
+ if len(*matchList) != 0 {
+ listTests(m.deps.MatchString, m.tests, m.benchmarks, m.examples)
+ return 0
+ }
+
parseCpuList()
m.before()
@@ -825,7 +924,7 @@ func (m *M) Run() int {
if !testRan && !exampleRan && *matchBenchmarks == "" {
fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
}
- if !testOk || !exampleOk || !runBenchmarks(m.deps.MatchString, m.benchmarks) || race.Errors() > 0 {
+ if !testOk || !exampleOk || !runBenchmarks(m.deps.ImportPath(), m.deps.MatchString, m.benchmarks) || race.Errors() > 0 {
fmt.Println("FAIL")
m.after()
return 1
@@ -853,6 +952,29 @@ func (t *T) report() {
}
}
+func listTests(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) {
+ if _, err := matchString(*matchList, "non-empty"); err != nil {
+ fmt.Fprintf(os.Stderr, "testing: invalid regexp in -test.list (%q): %s\n", *matchList, err)
+ os.Exit(1)
+ }
+
+ for _, test := range tests {
+ if ok, _ := matchString(*matchList, test.Name); ok {
+ fmt.Println(test.Name)
+ }
+ }
+ for _, bench := range benchmarks {
+ if ok, _ := matchString(*matchList, bench.Name); ok {
+ fmt.Println(bench.Name)
+ }
+ }
+ for _, example := range examples {
+ if ok, _ := matchString(*matchList, example.Name); ok {
+ fmt.Println(example.Name)
+ }
+ }
+}
+
// An internal function but exported because it is cross-package; part of the implementation
// of the "go test" command.
func RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool) {