Browse code

internal/test/suite

Signed-off-by: Tibor Vass <tibor@docker.com>
(cherry picked from commit fd0ed80ff2f15370bf0606632e274d946394bb54)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Tibor Vass authored on 2019/08/09 08:59:56
Showing 3 changed files
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.