|
@@ -514,6 +514,7 @@ type suiteRunner struct {
|
|
|
suite interface{}
|
|
|
setUpSuite, tearDownSuite *methodType
|
|
|
setUpTest, tearDownTest *methodType
|
|
|
+ onTimeout *methodType
|
|
|
tests []*methodType
|
|
|
tracker *resultTracker
|
|
|
tempDir *tempDir
|
|
@@ -591,6 +592,8 @@ func newSuiteRunner(suite interface{}, runConf *RunConf) *suiteRunner {
|
|
|
runner.setUpTest = method
|
|
|
case "TearDownTest":
|
|
|
runner.tearDownTest = method
|
|
|
+ case "OnTimeout":
|
|
|
+ runner.onTimeout = method
|
|
|
default:
|
|
|
prefix := "Test"
|
|
|
if conf.Benchmark {
|
|
@@ -671,6 +674,23 @@ func (runner *suiteRunner) forkCall(method *methodType, kind funcKind, testName
|
|
|
return c
|
|
|
}
|
|
|
|
|
|
+type timeoutErr struct {
|
|
|
+ method *methodType
|
|
|
+ t time.Duration
|
|
|
+}
|
|
|
+
|
|
|
+func (e timeoutErr) Error() string {
|
|
|
+ return fmt.Sprintf("%s test timed out after %v", e.method.String(), e.t)
|
|
|
+}
|
|
|
+
|
|
|
+func isTimeout(e error) bool {
|
|
|
+ if e == nil {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ _, ok := e.(timeoutErr)
|
|
|
+ return ok
|
|
|
+}
|
|
|
+
|
|
|
// 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 {
|
|
|
var timeout <-chan time.Time
|
|
@@ -681,7 +701,19 @@ func (runner *suiteRunner) runFunc(method *methodType, kind funcKind, testName s
|
|
|
select {
|
|
|
case <-c.done:
|
|
|
case <-timeout:
|
|
|
- panic(fmt.Sprintf("%s test timed out after %v", method.String(), runner.checkTimeout))
|
|
|
+ if runner.onTimeout != nil {
|
|
|
+ // run the OnTimeout callback, allowing the suite to collect any sort of debug information it can
|
|
|
+ // `runFixture` is syncronous, so run this in a separate goroutine with a timeout
|
|
|
+ cChan := make(chan *C)
|
|
|
+ go func() {
|
|
|
+ cChan <- runner.runFixture(runner.onTimeout, c.testName, c.logb)
|
|
|
+ }()
|
|
|
+ select {
|
|
|
+ case <-cChan:
|
|
|
+ case <-time.After(runner.checkTimeout):
|
|
|
+ }
|
|
|
+ }
|
|
|
+ panic(timeoutErr{method, runner.checkTimeout})
|
|
|
}
|
|
|
return c
|
|
|
}
|
|
@@ -777,12 +809,14 @@ func (runner *suiteRunner) forkTest(method *methodType) *C {
|
|
|
c.logArgPanic(c.method, "*check.C")
|
|
|
return
|
|
|
}
|
|
|
+
|
|
|
if strings.HasPrefix(c.method.Info.Name, "Test") {
|
|
|
c.ResetTimer()
|
|
|
c.StartTimer()
|
|
|
c.method.Call([]reflect.Value{reflect.ValueOf(c)})
|
|
|
return
|
|
|
}
|
|
|
+
|
|
|
if !strings.HasPrefix(c.method.Info.Name, "Benchmark") {
|
|
|
panic("unexpected method prefix: " + c.method.Info.Name)
|
|
|
}
|
|
@@ -791,6 +825,7 @@ func (runner *suiteRunner) forkTest(method *methodType) *C {
|
|
|
c.N = benchN
|
|
|
c.ResetTimer()
|
|
|
c.StartTimer()
|
|
|
+
|
|
|
c.method.Call([]reflect.Value{reflect.ValueOf(c)})
|
|
|
c.StopTimer()
|
|
|
if c.status() != succeededSt || c.duration >= c.benchTime || benchN >= 1e9 {
|
|
@@ -825,7 +860,19 @@ func (runner *suiteRunner) runTest(method *methodType) *C {
|
|
|
select {
|
|
|
case <-c.done:
|
|
|
case <-timeout:
|
|
|
- panic(fmt.Sprintf("%s test timed out after %v", method.String(), runner.checkTimeout))
|
|
|
+ if runner.onTimeout != nil {
|
|
|
+ // run the OnTimeout callback, allowing the suite to collect any sort of debug information it can
|
|
|
+ // `runFixture` is syncronous, so run this in a separate goroutine with a timeout
|
|
|
+ cChan := make(chan *C)
|
|
|
+ go func() {
|
|
|
+ cChan <- runner.runFixture(runner.onTimeout, c.testName, c.logb)
|
|
|
+ }()
|
|
|
+ select {
|
|
|
+ case <-cChan:
|
|
|
+ case <-time.After(runner.checkTimeout):
|
|
|
+ }
|
|
|
+ }
|
|
|
+ panic(timeoutErr{method, runner.checkTimeout})
|
|
|
}
|
|
|
return c
|
|
|
}
|
|
@@ -846,7 +893,7 @@ func (runner *suiteRunner) skipTests(status funcStatus, methods []*methodType) {
|
|
|
func (runner *suiteRunner) checkFixtureArgs() bool {
|
|
|
succeeded := true
|
|
|
argType := reflect.TypeOf(&C{})
|
|
|
- for _, method := range []*methodType{runner.setUpSuite, runner.tearDownSuite, runner.setUpTest, runner.tearDownTest} {
|
|
|
+ for _, method := range []*methodType{runner.setUpSuite, runner.tearDownSuite, runner.setUpTest, runner.tearDownTest, runner.onTimeout} {
|
|
|
if method != nil {
|
|
|
mt := method.Type()
|
|
|
if mt.NumIn() != 1 || mt.In(0) != argType {
|