|
@@ -0,0 +1,96 @@
|
|
|
|
+// 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
|
|
|
|
+}
|