Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Capture test stats so they can be processed in the TearDownTest function #98

Open
wants to merge 3 commits into
base: v1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 52 additions & 3 deletions benchmark.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright (c) 2012 The Go Authors. All rights reserved.
//
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
Expand All @@ -13,7 +13,7 @@
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
Expand All @@ -36,6 +36,14 @@ import (

var memStats runtime.MemStats

type testStats struct {
duration time.Duration
N int
// The net total of this test after being run.
netAllocs uint64
netBytes uint64
}

// testingB is a type passed to Benchmark functions to manage benchmark
// timing and to specify the number of iterations to run.
type timer struct {
Expand All @@ -51,6 +59,7 @@ type timer struct {
// The net total of this test after being run.
netAllocs uint64
netBytes uint64
stats testStats
}

// StartTimer starts timing a test. This function is called automatically
Expand Down Expand Up @@ -94,6 +103,46 @@ func (c *C) ResetTimer() {
c.netBytes = 0
}

// GetDuration returns the test total duration.
func (c *C) GetDuration() time.Duration {
return c.duration
}

// GetNsPerOp returns the test duration per iteration.
func (c *C) GetNsPerOp() int64 {
if c.N <= 0 {
return 0
}
return c.duration.Nanoseconds() / int64(c.N)
}

// GetDuration returns the benchmark total duration.
// When GetTestDuration() is invoked within TearDownTest(), it returns the test stats.
func (c *C) GetTestDuration() time.Duration {
return c.stats.duration
}

// GetNsPerOp returns the benchmark duration per iteration.
// When GetTestNsPerOp() is invoked within TearDownTest(), it returns the test stats.
func (c *C) GetTestNsPerOp() int64 {
if c.stats.N <= 0 {
return 0
}
return c.stats.duration.Nanoseconds() / int64(c.stats.N)
}

// GetTestNetAllocs returns the net memory allocations.
// When GetTestNetAllocs() is invoked within TearDownTest(), it returns the test stats.
func (c *C) GetTestNetAllocs() uint64 {
return c.stats.netAllocs
}

// GetTestNetBytes returns the net memory allocations.
// When GetTestNetBytes() is invoked within TearDownTest(), it returns the test stats.
func (c *C) GetTestNetBytes() uint64 {
return c.stats.netBytes
}

// SetBytes informs the number of bytes that the benchmark processes
// on each iteration. If this is called in a benchmark it will also
// report MB/s.
Expand Down
45 changes: 29 additions & 16 deletions check.go
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,7 @@ func (runner *suiteRunner) run() *Result {
if runner.tracker.result.RunError == nil && len(runner.tests) > 0 {
runner.tracker.start()
if runner.checkFixtureArgs() {
c := runner.runFixture(runner.setUpSuite, "", nil)
c := runner.runFixture(runner.setUpSuite, "", nil, nil)
if c == nil || c.status() == succeededSt {
for i := 0; i != len(runner.tests); i++ {
c := runner.runTest(runner.tests[i])
Expand All @@ -626,7 +626,7 @@ func (runner *suiteRunner) run() *Result {
} else {
runner.skipTests(missedSt, runner.tests)
}
runner.runFixture(runner.tearDownSuite, "", nil)
runner.runFixture(runner.tearDownSuite, "", nil, nil)
} else {
runner.skipTests(missedSt, runner.tests)
}
Expand All @@ -642,7 +642,7 @@ func (runner *suiteRunner) run() *Result {

// Create a call object with the given suite method, and fork a
// goroutine with the provided dispatcher for running it.
func (runner *suiteRunner) forkCall(method *methodType, kind funcKind, testName string, logb *logger, dispatcher func(c *C)) *C {
func (runner *suiteRunner) forkCall(method *methodType, kind funcKind, testName string, logb *logger, stats *testStats, dispatcher func(c *C)) *C {
var logw io.Writer
if runner.output.Stream {
logw = runner.output
Expand All @@ -662,6 +662,12 @@ func (runner *suiteRunner) forkCall(method *methodType, kind funcKind, testName
startTime: time.Now(),
benchMem: runner.benchMem,
}
if stats != nil {
c.stats.duration = stats.duration
c.stats.N = stats.N
c.stats.netAllocs = stats.netAllocs
c.stats.netBytes = stats.netBytes
}
runner.tracker.expectCall(c)
go (func() {
runner.reportCallStarted(c)
Expand All @@ -672,8 +678,8 @@ func (runner *suiteRunner) forkCall(method *methodType, kind funcKind, testName
}

// Same as forkCall(), but wait for call to finish before returning.
func (runner *suiteRunner) runFunc(method *methodType, kind funcKind, testName string, logb *logger, dispatcher func(c *C)) *C {
c := runner.forkCall(method, kind, testName, logb, dispatcher)
func (runner *suiteRunner) runFunc(method *methodType, kind funcKind, testName string, logb *logger, stats *testStats, dispatcher func(c *C)) *C {
c := runner.forkCall(method, kind, testName, logb, stats, dispatcher)
<-c.done
return c
}
Expand Down Expand Up @@ -715,9 +721,9 @@ func (runner *suiteRunner) callDone(c *C) {
// goroutine like all suite methods, but this method will not return
// while the fixture goroutine is not done, because the fixture must be
// run in a desired order.
func (runner *suiteRunner) runFixture(method *methodType, testName string, logb *logger) *C {
func (runner *suiteRunner) runFixture(method *methodType, testName string, logb *logger, stats *testStats) *C {
if method != nil {
c := runner.runFunc(method, fixtureKd, testName, logb, func(c *C) {
c := runner.runFunc(method, fixtureKd, testName, logb, stats, func(c *C) {
c.ResetTimer()
c.StartTimer()
defer c.StopTimer()
Expand All @@ -731,11 +737,11 @@ func (runner *suiteRunner) runFixture(method *methodType, testName string, logb
// Run the fixture method with runFixture(), but panic with a fixturePanic{}
// in case the fixture method panics. This makes it easier to track the
// fixture panic together with other call panics within forkTest().
func (runner *suiteRunner) runFixtureWithPanic(method *methodType, testName string, logb *logger, skipped *bool) *C {
func (runner *suiteRunner) runFixtureWithPanic(method *methodType, testName string, logb *logger, skipped *bool, stats *testStats) *C {
if skipped != nil && *skipped {
return nil
}
c := runner.runFixture(method, testName, logb)
c := runner.runFixture(method, testName, logb, stats)
if c != nil && c.status() != succeededSt {
if skipped != nil {
*skipped = c.status() == skippedSt
Expand All @@ -754,13 +760,21 @@ type fixturePanic struct {
// asynchronously.
func (runner *suiteRunner) forkTest(method *methodType) *C {
testName := method.String()
return runner.forkCall(method, testKd, testName, nil, func(c *C) {
return runner.forkCall(method, testKd, testName, nil, nil, func(c *C) {
var skipped bool
defer runner.runFixtureWithPanic(runner.tearDownTest, testName, nil, &skipped)
defer func() {
st := testStats{
duration: c.duration,
N: c.N,
netBytes: c.netBytes,
netAllocs: c.netAllocs,
}
runner.runFixtureWithPanic(runner.tearDownTest, testName, nil, &skipped, &st)
}()
defer c.StopTimer()
benchN := 1
for {
runner.runFixtureWithPanic(runner.setUpTest, testName, c.logb, &skipped)
runner.runFixtureWithPanic(runner.setUpTest, testName, c.logb, &skipped, nil)
mt := c.method.Type()
if mt.NumIn() != 1 || mt.In(0) != reflect.TypeOf(c) {
// Rather than a plain panic, provide a more helpful message when
Expand Down Expand Up @@ -799,9 +813,8 @@ func (runner *suiteRunner) forkTest(method *methodType) *C {
// - Be sure to run at least one more than last time.
benchN = max(min(perOpN+perOpN/2, 100*benchN), benchN+1)
benchN = roundUp(benchN)

skipped = true // Don't run the deferred one if this panics.
runner.runFixtureWithPanic(runner.tearDownTest, testName, nil, nil)
runner.runFixtureWithPanic(runner.tearDownTest, testName, nil, nil, &c.stats)
skipped = false
}
})
Expand All @@ -819,7 +832,7 @@ func (runner *suiteRunner) runTest(method *methodType) *C {
// nice verbose output.
func (runner *suiteRunner) skipTests(status funcStatus, methods []*methodType) {
for _, method := range methods {
runner.runFunc(method, testKd, "", nil, func(c *C) {
runner.runFunc(method, testKd, "", nil, nil, func(c *C) {
c.setStatus(status)
})
}
Expand All @@ -835,7 +848,7 @@ func (runner *suiteRunner) checkFixtureArgs() bool {
mt := method.Type()
if mt.NumIn() != 1 || mt.In(0) != argType {
succeeded = false
runner.runFunc(method, fixtureKd, "", nil, func(c *C) {
runner.runFunc(method, fixtureKd, "", nil, nil, func(c *C) {
c.logArgPanic(method, "*check.C")
c.setStatus(panickedSt)
})
Expand Down