mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
testing: hoisted chunks of code to prepare for Run method
testing.go: - run method will evolve into the Run method. - added level field in common benchmark.go: - benchContext will be central to distinguish handling of benchmarks between normal Run methods and ones called from within Benchmark function. - expandCPU will evolve into the processing hook for Run methods called within normal processing. - runBench will evolve into the Run method. Change-Id: I1816f9985d5ba94deb0ad062302ea9aee0bb5338 Reviewed-on: https://go-review.googlesource.com/18894 Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
parent
5c83e651ad
commit
89cda2db00
2 changed files with 100 additions and 58 deletions
|
|
@ -46,6 +46,7 @@ type InternalBenchmark struct {
|
|||
// affecting benchmark results.
|
||||
type B struct {
|
||||
common
|
||||
context *benchContext
|
||||
N int
|
||||
previousN int // number of iterations in the previous run
|
||||
previousDuration time.Duration // total duration of the previous run
|
||||
|
|
@ -299,6 +300,10 @@ func benchmarkName(name string, n int) string {
|
|||
return name
|
||||
}
|
||||
|
||||
type benchContext struct {
|
||||
maxLen int // The largest recorded benchmark name.
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
|
@ -334,19 +339,26 @@ func runBenchmarksInternal(matchString func(pat, str string) (bool, error), benc
|
|||
}
|
||||
}
|
||||
ok := true
|
||||
main := &B{
|
||||
common: common{name: "Main"},
|
||||
context: &benchContext{
|
||||
maxLen: maxlen,
|
||||
},
|
||||
}
|
||||
for _, Benchmark := range bs {
|
||||
ok = ok && expandCPU(main, Benchmark)
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
func expandCPU(parent *B, Benchmark InternalBenchmark) bool {
|
||||
ok := true
|
||||
for _, procs := range cpuList {
|
||||
runtime.GOMAXPROCS(procs)
|
||||
b := &B{
|
||||
common: common{
|
||||
signal: make(chan bool),
|
||||
name: Benchmark.Name,
|
||||
},
|
||||
benchFunc: Benchmark.F,
|
||||
}
|
||||
benchName := benchmarkName(Benchmark.Name, procs)
|
||||
fmt.Printf("%-*s\t", maxlen, benchName)
|
||||
r := b.run()
|
||||
fmt.Printf("%-*s\t", parent.context.maxLen, benchName)
|
||||
b := parent.runBench(Benchmark.Name, Benchmark.F)
|
||||
r := b.result
|
||||
if b.failed {
|
||||
ok = false
|
||||
// The output could be very long here, but probably isn't.
|
||||
|
|
@ -370,10 +382,29 @@ func runBenchmarksInternal(matchString func(pat, str string) (bool, error), benc
|
|||
fmt.Fprintf(os.Stderr, "testing: %s left GOMAXPROCS set to %d\n", benchName, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// runBench benchmarks f as a subbenchmark with the given name. It reports
|
||||
// whether there were any failures.
|
||||
//
|
||||
// A subbenchmark is like any other benchmark. A benchmark that calls Run at
|
||||
// least once will not be measured itself and will only run for one iteration.
|
||||
func (b *B) runBench(name string, f func(b *B)) *B {
|
||||
sub := &B{
|
||||
common: common{
|
||||
signal: make(chan bool),
|
||||
name: name,
|
||||
parent: &b.common,
|
||||
level: b.level + 1,
|
||||
},
|
||||
benchFunc: f,
|
||||
context: b.context,
|
||||
}
|
||||
sub.run()
|
||||
return sub
|
||||
}
|
||||
|
||||
// trimOutput shortens the output from a benchmark, which can be very long.
|
||||
func (b *B) trimOutput() {
|
||||
// The output is likely to appear multiple times because the benchmark
|
||||
|
|
|
|||
|
|
@ -204,6 +204,7 @@ type common struct {
|
|||
finished bool
|
||||
|
||||
parent *common
|
||||
level int // Nesting depth of test or benchmark.
|
||||
name string // Name of test or benchmark.
|
||||
start time.Time // Time test or benchmark started
|
||||
duration time.Duration
|
||||
|
|
@ -524,6 +525,37 @@ 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.
|
||||
func (t *T) run(name string, f func(t *T)) bool {
|
||||
testName := name
|
||||
if t.level > 0 {
|
||||
testName = t.name + "/" + name
|
||||
}
|
||||
t = &T{
|
||||
common: common{
|
||||
barrier: make(chan bool),
|
||||
signal: make(chan bool),
|
||||
name: testName,
|
||||
parent: &t.common,
|
||||
level: t.level + 1,
|
||||
},
|
||||
context: t.context,
|
||||
}
|
||||
|
||||
if *chatty {
|
||||
fmt.Printf("=== RUN %s\n", t.name)
|
||||
}
|
||||
// Instead of reducing the running count of this test before calling the
|
||||
// tRunner and increasing it afterwards, we rely on tRunner keeping the
|
||||
// count correct. This ensures that a sequence of sequential tests runs
|
||||
// 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
|
||||
return !t.failed
|
||||
}
|
||||
|
||||
// testContext holds all fields that are common to all tests. This includes
|
||||
// synchronization primitives to run at most *parallel tests.
|
||||
type testContext struct {
|
||||
|
|
@ -660,11 +692,10 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT
|
|||
},
|
||||
context: ctx,
|
||||
}
|
||||
|
||||
tRunner(t, func(t *T) {
|
||||
for i := 0; i < len(tests); i++ {
|
||||
for _, test := range tests {
|
||||
// TODO: a version of this will be the Run method.
|
||||
matched, err := matchString(*match, tests[i].Name)
|
||||
matched, err := matchString(*match, test.Name)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.run: %s\n", err)
|
||||
os.Exit(1)
|
||||
|
|
@ -672,27 +703,7 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT
|
|||
if !matched {
|
||||
continue
|
||||
}
|
||||
testName := tests[i].Name
|
||||
t := &T{
|
||||
common: common{
|
||||
barrier: make(chan bool),
|
||||
signal: make(chan bool),
|
||||
name: testName,
|
||||
parent: &t.common,
|
||||
},
|
||||
context: t.context,
|
||||
}
|
||||
|
||||
if *chatty {
|
||||
fmt.Printf("=== RUN %s\n", t.name)
|
||||
}
|
||||
// Instead of reducing the running count of this test before calling the
|
||||
// tRunner and increasing it afterwards, we rely on tRunner keeping the
|
||||
// count correct. This ensures that a sequence of sequential tests runs
|
||||
// without being preempted, even when their parent is a parallel test. This
|
||||
// may especially reduce surprises if *parallel == 1.
|
||||
go tRunner(t, tests[i].F)
|
||||
<-t.signal
|
||||
t.run(test.Name, test.F)
|
||||
}
|
||||
// Run catching the signal rather than the tRunner as a separate
|
||||
// goroutine to avoid adding a goroutine during the sequential
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue