Signed-off-by: Tibor Vass <tibor@docker.com>
(cherry picked from commit fd0ed80ff2f15370bf0606632e274d946394bb54)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
| 1 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,33 @@ |
| 0 |
+package suite |
|
| 1 |
+ |
|
| 2 |
+import "testing" |
|
| 3 |
+ |
|
| 4 |
+// SetupAllSuite has a SetupSuite method, which will run before the |
|
| 5 |
+// tests in the suite are run. |
|
| 6 |
+type SetupAllSuite interface {
|
|
| 7 |
+ SetUpSuite(t *testing.T) |
|
| 8 |
+} |
|
| 9 |
+ |
|
| 10 |
+// SetupTestSuite has a SetupTest method, which will run before each |
|
| 11 |
+// test in the suite. |
|
| 12 |
+type SetupTestSuite interface {
|
|
| 13 |
+ SetUpTest(t *testing.T) |
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+// TearDownAllSuite has a TearDownSuite method, which will run after |
|
| 17 |
+// all the tests in the suite have been run. |
|
| 18 |
+type TearDownAllSuite interface {
|
|
| 19 |
+ TearDownSuite(t *testing.T) |
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+// TearDownTestSuite has a TearDownTest method, which will run after |
|
| 23 |
+// each test in the suite. |
|
| 24 |
+type TearDownTestSuite interface {
|
|
| 25 |
+ TearDownTest(t *testing.T) |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+// TimeoutTestSuite has a OnTimeout method, which will run after |
|
| 29 |
+// a single test times out after a period specified by -timeout flag. |
|
| 30 |
+type TimeoutTestSuite interface {
|
|
| 31 |
+ OnTimeout() |
|
| 32 |
+} |
| 0 | 33 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,96 @@ |
| 0 |
+// Package suite is a simplified version of testify's suite package which has unnecessary dependencies. |
|
| 1 |
+// Please remove this package whenever possible. |
|
| 2 |
+package suite |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "flag" |
|
| 6 |
+ "fmt" |
|
| 7 |
+ "reflect" |
|
| 8 |
+ "runtime/debug" |
|
| 9 |
+ "strings" |
|
| 10 |
+ "testing" |
|
| 11 |
+ "time" |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+// TimeoutFlag is the flag to set a per-test timeout when running tests. Defaults to `-timeout`. |
|
| 15 |
+var TimeoutFlag = flag.Duration("timeout", 0, "per-test panic after duration `d` (default 0, timeout disabled)")
|
|
| 16 |
+ |
|
| 17 |
+var typTestingT = reflect.TypeOf(new(testing.T)) |
|
| 18 |
+ |
|
| 19 |
+// Run takes a testing suite and runs all of the tests attached to it. |
|
| 20 |
+func Run(t *testing.T, suite interface{}) {
|
|
| 21 |
+ defer failOnPanic(t) |
|
| 22 |
+ |
|
| 23 |
+ suiteSetupDone := false |
|
| 24 |
+ |
|
| 25 |
+ methodFinder := reflect.TypeOf(suite) |
|
| 26 |
+ suiteName := methodFinder.Elem().Name() |
|
| 27 |
+ for index := 0; index < methodFinder.NumMethod(); index++ {
|
|
| 28 |
+ method := methodFinder.Method(index) |
|
| 29 |
+ if !methodFilter(method.Name, method.Type) {
|
|
| 30 |
+ continue |
|
| 31 |
+ } |
|
| 32 |
+ if !suiteSetupDone {
|
|
| 33 |
+ if setupAllSuite, ok := suite.(SetupAllSuite); ok {
|
|
| 34 |
+ setupAllSuite.SetUpSuite(t) |
|
| 35 |
+ } |
|
| 36 |
+ defer func() {
|
|
| 37 |
+ if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok {
|
|
| 38 |
+ tearDownAllSuite.TearDownSuite(t) |
|
| 39 |
+ } |
|
| 40 |
+ }() |
|
| 41 |
+ suiteSetupDone = true |
|
| 42 |
+ } |
|
| 43 |
+ t.Run(suiteName+"/"+method.Name, func(t *testing.T) {
|
|
| 44 |
+ defer failOnPanic(t) |
|
| 45 |
+ |
|
| 46 |
+ if setupTestSuite, ok := suite.(SetupTestSuite); ok {
|
|
| 47 |
+ setupTestSuite.SetUpTest(t) |
|
| 48 |
+ } |
|
| 49 |
+ defer func() {
|
|
| 50 |
+ if tearDownTestSuite, ok := suite.(TearDownTestSuite); ok {
|
|
| 51 |
+ tearDownTestSuite.TearDownTest(t) |
|
| 52 |
+ } |
|
| 53 |
+ }() |
|
| 54 |
+ |
|
| 55 |
+ var timeout <-chan time.Time |
|
| 56 |
+ if *TimeoutFlag > 0 {
|
|
| 57 |
+ timeout = time.After(*TimeoutFlag) |
|
| 58 |
+ } |
|
| 59 |
+ panicCh := make(chan error) |
|
| 60 |
+ go func() {
|
|
| 61 |
+ defer func() {
|
|
| 62 |
+ if r := recover(); r != nil {
|
|
| 63 |
+ panicCh <- fmt.Errorf("test panicked: %v\n%s", r, debug.Stack())
|
|
| 64 |
+ } else {
|
|
| 65 |
+ close(panicCh) |
|
| 66 |
+ } |
|
| 67 |
+ }() |
|
| 68 |
+ method.Func.Call([]reflect.Value{reflect.ValueOf(suite), reflect.ValueOf(t)})
|
|
| 69 |
+ }() |
|
| 70 |
+ select {
|
|
| 71 |
+ case err := <-panicCh: |
|
| 72 |
+ if err != nil {
|
|
| 73 |
+ t.Fatal(err.Error()) |
|
| 74 |
+ } |
|
| 75 |
+ case <-timeout: |
|
| 76 |
+ if timeoutSuite, ok := suite.(TimeoutTestSuite); ok {
|
|
| 77 |
+ timeoutSuite.OnTimeout() |
|
| 78 |
+ } |
|
| 79 |
+ t.Fatalf("timeout: test timed out after %s since start of test", *TimeoutFlag)
|
|
| 80 |
+ } |
|
| 81 |
+ }) |
|
| 82 |
+ } |
|
| 83 |
+} |
|
| 84 |
+ |
|
| 85 |
+func failOnPanic(t *testing.T) {
|
|
| 86 |
+ r := recover() |
|
| 87 |
+ if r != nil {
|
|
| 88 |
+ t.Errorf("test suite panicked: %v\n%s", r, debug.Stack())
|
|
| 89 |
+ t.FailNow() |
|
| 90 |
+ } |
|
| 91 |
+} |
|
| 92 |
+ |
|
| 93 |
+func methodFilter(name string, typ reflect.Type) bool {
|
|
| 94 |
+ return strings.HasPrefix(name, "Test") && typ.NumIn() == 2 && typ.In(1) == typTestingT // 2 params: method receiver and *testing.T |
|
| 95 |
+} |
| 0 | 96 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,21 @@ |
| 0 |
+MIT License |
|
| 1 |
+ |
|
| 2 |
+Copyright (c) 2012-2018 Mat Ryer and Tyler Bunnell |
|
| 3 |
+ |
|
| 4 |
+Permission is hereby granted, free of charge, to any person obtaining a copy |
|
| 5 |
+of this software and associated documentation files (the "Software"), to deal |
|
| 6 |
+in the Software without restriction, including without limitation the rights |
|
| 7 |
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
| 8 |
+copies of the Software, and to permit persons to whom the Software is |
|
| 9 |
+furnished to do so, subject to the following conditions: |
|
| 10 |
+ |
|
| 11 |
+The above copyright notice and this permission notice shall be included in all |
|
| 12 |
+copies or substantial portions of the Software. |
|
| 13 |
+ |
|
| 14 |
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
| 15 |
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
| 16 |
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
| 17 |
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
| 18 |
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
| 19 |
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
| 20 |
+SOFTWARE. |