-
Notifications
You must be signed in to change notification settings - Fork 1
/
runner.go
240 lines (198 loc) · 6.97 KB
/
runner.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
package mt
import (
"testing"
"time"
)
const (
// ExecuteTestsFirst causes the test runner to execute tests before subgroups.
ExecuteTestsFirst = iota
// ExecuteSubgroupsFirst causes the test runner to execute subgroups before tests.
ExecuteSubgroupsFirst
)
// A TestRunner runs a set of tests.
type TestRunner struct {
// ContinueOnFailure indicates whether the test runner should continue
// executing further tests after a test encounters a failure.
//
// Default is false.
ContinueOnFailure bool
// GroupExecutionPriority indicates whether the test runner should execute
// tests before or after subgroups.
GroupExecutionPriority int
// TestTimeout the the amount of time to wait for any single test to complete.
//
// Default is 10 seconds.
TestTimeout time.Duration
}
// A TestRunResult contains information about a completed test case run.
type TestRunResult struct {
TestCase TestCase `json:"test"`
TestResult TestResult `json:"result"`
StartedAt time.Time `json:"started_at"`
EndedAt time.Time `json:"finished_at"`
Duration time.Duration `json:"duration"`
}
// A GroupRunResult contains information about a completed set of test cases run by a test runner.
type GroupRunResult struct {
// Group is a reference to the test group that was run.
Group *TestGroup `json:"-"`
// Results is a list of test run results for each test in the group that was run.
TestResults []TestRunResult `json:"test_results,omitempty"`
// GroupRunResults is a list of group run results for each subgroup that was run.
SubgroupResults []*GroupRunResult `json:"group_results,omitempty"`
// Passed is the number of tests that passed.
Passed int `json:"passed"`
// Failed is the number of tests that failed.
Failed int `json:"failed"`
// Skipped is the number of tests that were skipped.
Skipped int `json:"skipped"`
// Total is the total number of tests in the test group.
Total int `json:"total"`
// Duration is the total duration of all tests in the test group.
Duration time.Duration `json:"duration"`
}
// NewTestRunner creates a new TestRunner with default configuration.
func NewTestRunner() *TestRunner {
return &TestRunner{
ContinueOnFailure: cfg.ContinueOnFailure,
GroupExecutionPriority: ExecuteTestsFirst,
TestTimeout: 10 * time.Second,
}
}
// WithContinueOnFailure sets the ContinueOnFailure field of the TestRunner and
// returns the TestRunner.
func (r *TestRunner) WithContinueOnFailure(continueOnFailure bool) *TestRunner {
r.ContinueOnFailure = continueOnFailure
return r
}
// WithRequestTimeout sets the RequestTimeout field of the TestRunner and returns
// the TestRunner.
func (r *TestRunner) WithRequestTimeout(timeout time.Duration) *TestRunner {
r.TestTimeout = timeout
return r
}
// RunTests runs a set of tests.
//
// To run tests within a Go test context, use RunTestsT().
func (r *TestRunner) RunTests(tests ...TestCase) *GroupRunResult {
return r.RunTestsT(nil, tests...)
}
// RunTestsT runs a set of tests within a Go test context.
//
// To run tests standalone to print or examine results, use RunTests().
func (r *TestRunner) RunTestsT(t *testing.T, tests ...TestCase) *GroupRunResult {
group := NewTestGroup("").AddTests(tests...)
return r.RunTestGroupT(t, group)
}
// RunTestGroup runs a test group.
//
// To run a test group within a Go test context, use RunTestGroupT().
func (r *TestRunner) RunTestGroup(group *TestGroup) *GroupRunResult {
return r.RunTestGroupT(nil, group)
}
// RunTestGroupT runs a test group within the context of a Go test.
//
// To run tests as a standalone binary without a testing context, use RunTests().
func (r *TestRunner) RunTestGroupT(t *testing.T, group *TestGroup) *GroupRunResult {
groupResult := &GroupRunResult{
Group: group,
}
if group.BeforeFunc != nil {
group.BeforeFunc()
}
if r.GroupExecutionPriority == ExecuteSubgroupsFirst {
r.runSubgroups(t, groupResult)
}
for _, test := range group.Tests {
start := time.Now()
testResult := test.Execute()
end := time.Now()
runResult := TestRunResult{
TestCase: test,
TestResult: testResult,
StartedAt: start,
EndedAt: end,
Duration: end.Sub(start),
}
groupResult.TestResults = append(groupResult.TestResults, runResult)
groupResult.Total++
groupResult.Duration += runResult.Duration
if len(testResult.Failures()) > 0 {
groupResult.Failed++
if t != nil {
t.Run(test.Description(), func(t *testing.T) {
for _, err := range testResult.Failures() {
t.Log(err)
}
t.FailNow()
})
}
if !r.ContinueOnFailure {
groupResult.Skipped = len(group.Tests) - groupResult.Total
break
}
} else {
groupResult.Passed++
if t != nil {
t.Run(test.Description(), func(t *testing.T) {
t.Log(testResult.TestCase().Description())
})
}
}
}
if r.GroupExecutionPriority == ExecuteTestsFirst {
r.runSubgroups(t, groupResult)
}
if group.AfterFunc != nil {
group.AfterFunc()
}
return groupResult
}
func (r *TestRunner) runSubgroups(t *testing.T, groupResult *GroupRunResult) {
for _, subgroup := range groupResult.Group.Subgroups {
result := r.RunTestGroupT(t, subgroup)
groupResult.SubgroupResults = append(groupResult.SubgroupResults, result)
groupResult.Passed += result.Passed
groupResult.Failed += result.Failed
groupResult.Total += result.Total
groupResult.Duration += result.Duration
}
}
// RunTestGroups runs a set of test groups using the default test runner.
func (r *TestRunner) RunTestGroups(groups ...*TestGroup) *GroupRunResult {
group := NewTestGroup("").AddGroups(groups...)
return r.RunTestGroup(group)
}
// RunTestGroupsT runs a set of test groups within the context of a Go test
// using the default test runner.
func (r *TestRunner) RunTestGroupsT(t *testing.T, groups ...*TestGroup) *GroupRunResult {
group := NewTestGroup("").AddGroups(groups...)
return r.RunTestGroupT(t, group)
}
// RunTests runs a set of tests using the default test runner.
func RunTests(tests ...TestCase) *GroupRunResult {
return NewTestRunner().RunTests(tests...)
}
// RunTestsT runs a set of tests within a Go test context
// using the default test runner.
func RunTestsT(t *testing.T, tests ...TestCase) *GroupRunResult {
return NewTestRunner().RunTestsT(t, tests...)
}
// RunTestGroup runs a test group using the default test runner.
func RunTestGroup(group *TestGroup) *GroupRunResult {
return NewTestRunner().RunTestGroup(group)
}
// RunTestGroupT runs a test group within the context of a Go test
// using the default test runner.
func RunTestGroupT(t *testing.T, group *TestGroup) *GroupRunResult {
return NewTestRunner().RunTestGroupT(t, group)
}
// RunTestGroups runs a set of test groups using the default test runner.
func RunTestGroups(groups ...*TestGroup) *GroupRunResult {
return NewTestRunner().RunTestGroups(groups...)
}
// RunTestGroupsT runs a set of test groups within the context of a Go test
// using the default test runner.
func RunTestGroupsT(t *testing.T, groups ...*TestGroup) *GroupRunResult {
return NewTestRunner().RunTestGroupsT(t, groups...)
}