Commit 6b742b2f authored by Quentin Smith's avatar Quentin Smith

testing: print extra labels on benchmarks

When running benchmarks, print "goos", "goarch", and "pkg"
labels. This makes it easier to refer to benchmark logs and understand
how they were generated. "pkg" is printed only for benchmarks located
in GOPATH.

Change-Id: I397cbdd57b9fe8cbabbb354ec7bfba59f5625c42
Reviewed-on: https://go-review.googlesource.com/36356
Run-TryBot: Quentin Smith <quentin@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent c0bd4f33
...@@ -3690,6 +3690,28 @@ func TestMatchesOnlyBenchmarkIsOK(t *testing.T) { ...@@ -3690,6 +3690,28 @@ func TestMatchesOnlyBenchmarkIsOK(t *testing.T) {
tg.grepBoth(okPattern, "go test did not say ok") tg.grepBoth(okPattern, "go test did not say ok")
} }
func TestBenchmarkLabels(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
// TODO: tg.parallel()
tg.setenv("GOPATH", filepath.Join(tg.pwd(), "testdata"))
tg.run("test", "-run", "^$", "-bench", ".", "bench")
tg.grepStdout(`(?m)^goos: `+runtime.GOOS, "go test did not print goos")
tg.grepStdout(`(?m)^goarch: `+runtime.GOARCH, "go test did not print goarch")
tg.grepStdout(`(?m)^pkg: bench`, "go test did not say pkg: bench")
tg.grepBothNot(`(?s)pkg:.*pkg:`, "go test said pkg multiple times")
}
func TestBenchmarkLabelsOutsideGOPATH(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
// TODO: tg.parallel()
tg.run("test", "-run", "^$", "-bench", ".", "testdata/standalone_benchmark_test.go")
tg.grepStdout(`(?m)^goos: `+runtime.GOOS, "go test did not print goos")
tg.grepStdout(`(?m)^goarch: `+runtime.GOARCH, "go test did not print goarch")
tg.grepBothNot(`(?m)^pkg:`, "go test did say pkg:")
}
func TestMatchesOnlyTestIsOK(t *testing.T) { func TestMatchesOnlyTestIsOK(t *testing.T) {
tg := testgo(t) tg := testgo(t)
defer tg.cleanup() defer tg.cleanup()
......
...@@ -1373,6 +1373,19 @@ func (t *testFuncs) CoverEnabled() bool { ...@@ -1373,6 +1373,19 @@ func (t *testFuncs) CoverEnabled() bool {
return testCover return testCover
} }
// ImportPath returns the import path of the package being tested, if it is within GOPATH.
// This is printed by the testing package when running benchmarks.
func (t *testFuncs) ImportPath() string {
pkg := t.Package.ImportPath
if strings.HasPrefix(pkg, "_/") {
return ""
}
if pkg == "command-line-arguments" {
return ""
}
return pkg
}
// Covered returns a string describing which packages are being tested for coverage. // Covered returns a string describing which packages are being tested for coverage.
// If the covered package is the same as the tested package, it returns the empty string. // If the covered package is the same as the tested package, it returns the empty string.
// Otherwise it is a comma-separated human-readable list of packages beginning with // Otherwise it is a comma-separated human-readable list of packages beginning with
...@@ -1503,6 +1516,10 @@ var examples = []testing.InternalExample{ ...@@ -1503,6 +1516,10 @@ var examples = []testing.InternalExample{
{{end}} {{end}}
} }
func init() {
testdeps.ImportPath = {{.ImportPath | printf "%q"}}
}
{{if .CoverEnabled}} {{if .CoverEnabled}}
// Only updated by init functions, so no need for atomicity. // Only updated by init functions, so no need for atomicity.
......
package bench
import "testing"
func Benchmark(b *testing.B) {
}
...@@ -47,6 +47,7 @@ type InternalBenchmark struct { ...@@ -47,6 +47,7 @@ type InternalBenchmark struct {
// affecting benchmark results. // affecting benchmark results.
type B struct { type B struct {
common common
importPath string // import path of the package containing the benchmark
context *benchContext context *benchContext
N int N int
previousN int // number of iterations in the previous run previousN int // number of iterations in the previous run
...@@ -233,9 +234,18 @@ func (b *B) run1() bool { ...@@ -233,9 +234,18 @@ func (b *B) run1() bool {
return true return true
} }
var labelsOnce sync.Once
// run executes the benchmark in a separate goroutine, including all of its // run executes the benchmark in a separate goroutine, including all of its
// subbenchmarks. b must not have subbenchmarks. // subbenchmarks. b must not have subbenchmarks.
func (b *B) run() BenchmarkResult { 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 { if b.context != nil {
// Running go test --test.bench // Running go test --test.bench
b.context.processBench(b) // Must call doBench. b.context.processBench(b) // Must call doBench.
...@@ -363,10 +373,10 @@ type benchContext struct { ...@@ -363,10 +373,10 @@ type benchContext struct {
// An internal function but exported because it is cross-package; part of the implementation // An internal function but exported because it is cross-package; part of the implementation
// of the "go test" command. // of the "go test" command.
func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) { 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 no flag was specified, don't run benchmarks.
if len(*matchBenchmarks) == 0 { if len(*matchBenchmarks) == 0 {
return true return true
...@@ -398,6 +408,7 @@ func runBenchmarks(matchString func(pat, str string) (bool, error), benchmarks [ ...@@ -398,6 +408,7 @@ func runBenchmarks(matchString func(pat, str string) (bool, error), benchmarks [
w: os.Stdout, w: os.Stdout,
chatty: *chatty, chatty: *chatty,
}, },
importPath: importPath,
benchFunc: func(b *B) { benchFunc: func(b *B) {
for _, Benchmark := range bs { for _, Benchmark := range bs {
b.Run(Benchmark.Name, Benchmark.F) b.Run(Benchmark.Name, Benchmark.F)
...@@ -486,6 +497,7 @@ func (b *B) Run(name string, f func(b *B)) bool { ...@@ -486,6 +497,7 @@ func (b *B) Run(name string, f func(b *B)) bool {
w: b.w, w: b.w,
chatty: b.chatty, chatty: b.chatty,
}, },
importPath: b.importPath,
benchFunc: f, benchFunc: f,
benchTime: b.benchTime, benchTime: b.benchTime,
context: b.context, context: b.context,
......
...@@ -49,3 +49,10 @@ func (TestDeps) WriteHeapProfile(w io.Writer) error { ...@@ -49,3 +49,10 @@ func (TestDeps) WriteHeapProfile(w io.Writer) error {
func (TestDeps) WriteProfileTo(name string, w io.Writer, debug int) error { func (TestDeps) WriteProfileTo(name string, w io.Writer, debug int) error {
return pprof.Lookup(name).WriteTo(w, debug) 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
}
...@@ -766,6 +766,7 @@ func (f matchStringOnly) StartCPUProfile(w io.Writer) error { return e ...@@ -766,6 +766,7 @@ func (f matchStringOnly) StartCPUProfile(w io.Writer) error { return e
func (f matchStringOnly) StopCPUProfile() {} func (f matchStringOnly) StopCPUProfile() {}
func (f matchStringOnly) WriteHeapProfile(w io.Writer) error { return errMain } func (f matchStringOnly) WriteHeapProfile(w io.Writer) error { return errMain }
func (f matchStringOnly) WriteProfileTo(string, io.Writer, int) 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. // 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. // It was exported because it is cross-package and predates "internal" packages.
...@@ -795,6 +796,7 @@ type testDeps interface { ...@@ -795,6 +796,7 @@ type testDeps interface {
StopCPUProfile() StopCPUProfile()
WriteHeapProfile(io.Writer) error WriteHeapProfile(io.Writer) error
WriteProfileTo(string, io.Writer, int) error WriteProfileTo(string, io.Writer, int) error
ImportPath() string
} }
// MainStart is meant for use by tests generated by 'go test'. // MainStart is meant for use by tests generated by 'go test'.
...@@ -827,7 +829,7 @@ func (m *M) Run() int { ...@@ -827,7 +829,7 @@ func (m *M) Run() int {
if !testRan && !exampleRan && *matchBenchmarks == "" { if !testRan && !exampleRan && *matchBenchmarks == "" {
fmt.Fprintln(os.Stderr, "testing: warning: no tests to run") 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") fmt.Println("FAIL")
m.after() m.after()
return 1 return 1
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment