moby--moby/internal/test/suite/suite.go

97 lines
2.5 KiB
Go
Raw Normal View History

// Package suite is a simplified version of testify's suite package which has unnecessary dependencies.
// Please remove this package whenever possible.
package suite
import (
"flag"
"fmt"
"reflect"
"runtime/debug"
"strings"
"testing"
"time"
)
// TimeoutFlag is the flag to set a per-test timeout when running tests. Defaults to `-timeout`.
var TimeoutFlag = flag.Duration("timeout", 0, "per-test panic after duration `d` (default 0, timeout disabled)")
var typTestingT = reflect.TypeOf(new(testing.T))
// Run takes a testing suite and runs all of the tests attached to it.
func Run(t *testing.T, suite interface{}) {
defer failOnPanic(t)
suiteSetupDone := false
methodFinder := reflect.TypeOf(suite)
suiteName := methodFinder.Elem().Name()
for index := 0; index < methodFinder.NumMethod(); index++ {
method := methodFinder.Method(index)
if !methodFilter(method.Name, method.Type) {
continue
}
if !suiteSetupDone {
if setupAllSuite, ok := suite.(SetupAllSuite); ok {
setupAllSuite.SetUpSuite(t)
}
defer func() {
if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok {
tearDownAllSuite.TearDownSuite(t)
}
}()
suiteSetupDone = true
}
t.Run(suiteName+"/"+method.Name, func(t *testing.T) {
defer failOnPanic(t)
if setupTestSuite, ok := suite.(SetupTestSuite); ok {
setupTestSuite.SetUpTest(t)
}
defer func() {
if tearDownTestSuite, ok := suite.(TearDownTestSuite); ok {
tearDownTestSuite.TearDownTest(t)
}
}()
var timeout <-chan time.Time
if *TimeoutFlag > 0 {
timeout = time.After(*TimeoutFlag)
}
panicCh := make(chan error)
go func() {
defer func() {
if r := recover(); r != nil {
panicCh <- fmt.Errorf("test panicked: %v\n%s", r, debug.Stack())
} else {
close(panicCh)
}
}()
method.Func.Call([]reflect.Value{reflect.ValueOf(suite), reflect.ValueOf(t)})
}()
select {
case err := <-panicCh:
if err != nil {
t.Fatal(err.Error())
}
case <-timeout:
if timeoutSuite, ok := suite.(TimeoutTestSuite); ok {
timeoutSuite.OnTimeout()
}
t.Fatalf("timeout: test timed out after %s since start of test", *TimeoutFlag)
}
})
}
}
func failOnPanic(t *testing.T) {
r := recover()
if r != nil {
t.Errorf("test suite panicked: %v\n%s", r, debug.Stack())
t.FailNow()
}
}
func methodFilter(name string, typ reflect.Type) bool {
return strings.HasPrefix(name, "Test") && typ.NumIn() == 2 && typ.In(1) == typTestingT // 2 params: method receiver and *testing.T
}