]> Cypherpunks repositories - gostls13.git/commitdiff
testing: hoisted chunks of code to prepare for Run method
authorMarcel van Lohuizen <mpvl@golang.org>
Fri, 29 Jan 2016 15:55:35 +0000 (16:55 +0100)
committerMarcel van Lohuizen <mpvl@golang.org>
Fri, 18 Mar 2016 11:35:16 +0000 (11:35 +0000)
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>
src/testing/benchmark.go
src/testing/testing.go

index b092a9d9e2f3d1b0224ae6d1c4e1f710a17f5433..4dac1e6d63a74f0f520591e8cd96c685d2528e63 100644 (file)
@@ -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,46 +339,72 @@ 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 {
-               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()
-                       if b.failed {
-                               ok = false
-                               // The output could be very long here, but probably isn't.
-                               // We print it all, regardless, because we don't want to trim the reason
-                               // the benchmark failed.
-                               fmt.Printf("--- FAIL: %s\n%s", benchName, b.output)
-                               continue
-                       }
-                       results := r.String()
-                       if *benchmarkMemory || b.showAllocResult {
-                               results += "\t" + r.MemString()
-                       }
-                       fmt.Println(results)
-                       // Unlike with tests, we ignore the -chatty flag and always print output for
-                       // benchmarks since the output generation time will skew the results.
-                       if len(b.output) > 0 {
-                               b.trimOutput()
-                               fmt.Printf("--- BENCH: %s\n%s", benchName, b.output)
-                       }
-                       if p := runtime.GOMAXPROCS(-1); p != procs {
-                               fmt.Fprintf(os.Stderr, "testing: %s left GOMAXPROCS set to %d\n", benchName, p)
-                       }
+               ok = ok && expandCPU(main, Benchmark)
+       }
+       return ok
+}
+
+func expandCPU(parent *B, Benchmark InternalBenchmark) bool {
+       ok := true
+       for _, procs := range cpuList {
+               runtime.GOMAXPROCS(procs)
+               benchName := benchmarkName(Benchmark.Name, procs)
+               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.
+                       // We print it all, regardless, because we don't want to trim the reason
+                       // the benchmark failed.
+                       fmt.Printf("--- FAIL: %s\n%s", benchName, b.output)
+                       continue
+               }
+               results := r.String()
+               if *benchmarkMemory || b.showAllocResult {
+                       results += "\t" + r.MemString()
+               }
+               fmt.Println(results)
+               // Unlike with tests, we ignore the -chatty flag and always print output for
+               // benchmarks since the output generation time will skew the results.
+               if len(b.output) > 0 {
+                       b.trimOutput()
+                       fmt.Printf("--- BENCH: %s\n%s", benchName, b.output)
+               }
+               if p := runtime.GOMAXPROCS(-1); p != procs {
+                       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
index edd6c4fe749c859d9188de37608d6ba60fd567f5..13739ccd9d2331b6e782ec423cbd9972fc8507c4 100644 (file)
@@ -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