Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
kardolus
GitHub Repository: kardolus/chatgpt-cli
Path: blob/main/vendor/github.com/sclevine/spec/spec.go
2875 views
1
package spec
2
3
import (
4
"bytes"
5
"io"
6
"testing"
7
"time"
8
)
9
10
// G defines a group of specs.
11
// Unlike other testing libraries, it is re-evaluated for each subspec.
12
//
13
// Valid Options:
14
// Sequential, Random, Reverse, Parallel
15
// Local, Global, Flat, Nested
16
type G func(text string, f func(), opts ...Option)
17
18
// Pend skips all specs in the provided group.
19
//
20
// All Options are ignored.
21
func (g G) Pend(text string, f func(), _ ...Option) {
22
g(text, f, func(c *config) { c.pend = true })
23
}
24
25
// Focus focuses the provided group.
26
// This skips all specs in the suite except the group and other focused specs.
27
//
28
// Valid Options:
29
// Sequential, Random, Reverse, Parallel
30
// Local, Global, Flat, Nested
31
func (g G) Focus(text string, f func(), opts ...Option) {
32
g(text, f, append(opts, func(c *config) { c.focus = true })...)
33
}
34
35
// S defines a spec.
36
//
37
// Valid Options: Parallel
38
type S func(text string, f func(), opts ...Option)
39
40
// Before runs a function before each spec in the group.
41
func (s S) Before(f func()) {
42
s("", f, func(c *config) { c.before = true })
43
}
44
45
// After runs a function after each spec in the group.
46
func (s S) After(f func()) {
47
s("", f, func(c *config) { c.after = true })
48
}
49
50
// Pend skips the provided spec.
51
//
52
// All Options are ignored.
53
func (s S) Pend(text string, f func(), _ ...Option) {
54
s(text, f, func(c *config) { c.pend = true })
55
}
56
57
// Focus focuses the provided spec.
58
// This skips all specs in the suite except the spec and other focused specs.
59
//
60
// Valid Options: Parallel
61
func (s S) Focus(text string, f func(), opts ...Option) {
62
s(text, f, append(opts, func(c *config) { c.focus = true })...)
63
}
64
65
// Out provides an dedicated writer for the test to store output.
66
// Reporters usually display the contents on test failure.
67
//
68
// Valid context: inside S blocks only, nil elsewhere
69
func (s S) Out() io.Writer {
70
var out io.Writer
71
s("", nil, func(c *config) {
72
c.out = func(w io.Writer) {
73
out = w
74
}
75
})
76
return out
77
}
78
79
// Suite defines a top-level group of specs within a suite.
80
// Suite behaves like a top-level version of G.
81
// Unlike other testing libraries, it is re-evaluated for each subspec.
82
//
83
// Valid Options:
84
// Sequential, Random, Reverse, Parallel
85
// Local, Global, Flat, Nested
86
type Suite func(text string, f func(*testing.T, G, S), opts ...Option) bool
87
88
// Before runs a function before each spec in the suite.
89
func (s Suite) Before(f func(*testing.T)) bool {
90
return s("", func(t *testing.T, _ G, _ S) {
91
t.Helper()
92
f(t)
93
}, func(c *config) { c.before = true })
94
}
95
96
// After runs a function after each spec in the suite.
97
func (s Suite) After(f func(*testing.T)) bool {
98
return s("", func(t *testing.T, _ G, _ S) {
99
t.Helper()
100
f(t)
101
}, func(c *config) { c.after = true })
102
}
103
104
// Pend skips the provided top-level group of specs.
105
//
106
// All Options are ignored.
107
func (s Suite) Pend(text string, f func(*testing.T, G, S), _ ...Option) bool {
108
return s(text, f, func(c *config) { c.pend = true })
109
}
110
111
// Focus focuses the provided top-level group.
112
// This skips all specs in the suite except the group and other focused specs.
113
//
114
// Valid Options:
115
// Sequential, Random, Reverse, Parallel
116
// Local, Global, Flat, Nested
117
func (s Suite) Focus(text string, f func(*testing.T, G, S), opts ...Option) bool {
118
return s(text, f, append(opts, func(c *config) { c.focus = true })...)
119
}
120
121
// Run executes the specs defined in each top-level group of the suite.
122
func (s Suite) Run(t *testing.T) bool {
123
t.Helper()
124
return s("", nil, func(c *config) { c.t = t })
125
}
126
127
// New creates an empty suite and returns an Suite function.
128
// The Suite function may be called to add top-level groups to the suite.
129
// The suite may be executed with Suite.Run.
130
//
131
// Valid Options:
132
// Sequential, Random, Reverse, Parallel
133
// Local, Global, Flat, Nested
134
// Seed, Report
135
func New(text string, opts ...Option) Suite {
136
var fs []func(*testing.T, G, S)
137
return func(newText string, f func(*testing.T, G, S), newOpts ...Option) bool {
138
cfg := options(newOpts).apply()
139
if cfg.t == nil {
140
fs = append(fs, func(t *testing.T, g G, s S) {
141
var do func(string, func(), ...Option) = g
142
if cfg.before || cfg.after {
143
do = s
144
}
145
do(newText, func() { f(t, g, s) }, newOpts...)
146
})
147
return true
148
}
149
cfg.t.Helper()
150
return Run(cfg.t, text, func(t *testing.T, g G, s S) {
151
for _, f := range fs {
152
f(t, g, s)
153
}
154
}, opts...)
155
}
156
}
157
158
// Run immediately executes the provided specs as a suite.
159
// Unlike other testing libraries, it is re-evaluated for each spec.
160
//
161
// Valid Options:
162
// Sequential, Random, Reverse, Parallel
163
// Local, Global, Flat, Nested
164
// Seed, Report
165
func Run(t *testing.T, text string, f func(*testing.T, G, S), opts ...Option) bool {
166
t.Helper()
167
cfg := options(opts).apply()
168
n := &node{
169
text: []string{text},
170
seed: defaultZero64(cfg.seed, time.Now().Unix()),
171
order: cfg.order.or(orderSequential),
172
scope: cfg.scope.or(scopeLocal),
173
nest: cfg.nest.or(nestOff),
174
pend: cfg.pend,
175
focus: cfg.focus,
176
}
177
report := cfg.report
178
plan := n.parse(f)
179
180
var specs chan Spec
181
if report != nil {
182
report.Start(t, plan)
183
specs = make(chan Spec, plan.Total)
184
done := make(chan struct{})
185
defer func() {
186
close(specs)
187
<-done
188
}()
189
go func() {
190
report.Specs(t, specs)
191
close(done)
192
}()
193
}
194
195
return n.run(t, func(t *testing.T, n node) {
196
t.Helper()
197
buffer := &bytes.Buffer{}
198
defer func() {
199
if specs == nil {
200
return
201
}
202
specs <- Spec{
203
Text: n.text,
204
Failed: t.Failed(),
205
Skipped: t.Skipped(),
206
Focused: n.focus,
207
Parallel: n.order == orderParallel,
208
Out: buffer,
209
}
210
}()
211
switch {
212
case n.pend, plan.HasFocus && !n.focus:
213
t.SkipNow()
214
case n.order == orderParallel:
215
t.Parallel()
216
}
217
218
var spec, group func()
219
hooks := newHooks()
220
group = func() {}
221
222
f(t, func(_ string, f func(), _ ...Option) {
223
switch {
224
case len(n.loc) == 1, n.loc[0] > 0:
225
n.loc[0]--
226
case n.loc[0] == 0:
227
group = func() {
228
n.loc = n.loc[1:]
229
hooks.next()
230
group = func() {}
231
f()
232
group()
233
}
234
n.loc[0]--
235
}
236
}, func(_ string, f func(), opts ...Option) {
237
cfg := options(opts).apply()
238
switch {
239
case cfg.out != nil:
240
cfg.out(buffer)
241
case cfg.before:
242
hooks.before(f)
243
case cfg.after:
244
hooks.after(f)
245
case spec != nil:
246
case len(n.loc) > 1, n.loc[0] > 0:
247
n.loc[0]--
248
default:
249
spec = f
250
}
251
})
252
group()
253
254
if spec == nil {
255
t.Fatal("Failed to locate spec.")
256
}
257
hooks.run(t, spec)
258
})
259
}
260
261
type specHooks struct {
262
first, last *specHook
263
}
264
265
type specHook struct {
266
before, after []func()
267
next *specHook
268
}
269
270
func newHooks() specHooks {
271
h := &specHook{}
272
return specHooks{first: h, last: h}
273
}
274
275
func (s specHooks) run(t *testing.T, spec func()) {
276
t.Helper()
277
for h := s.first; h != nil; h = h.next {
278
defer run(t, h.after...)
279
run(t, h.before...)
280
}
281
run(t, spec)
282
}
283
284
func (s specHooks) before(f func()) {
285
s.last.before = append(s.last.before, f)
286
}
287
288
func (s specHooks) after(f func()) {
289
s.last.after = append(s.last.after, f)
290
}
291
292
func (s *specHooks) next() {
293
s.last.next = &specHook{}
294
s.last = s.last.next
295
}
296
297
func run(t *testing.T, fs ...func()) {
298
t.Helper()
299
for _, f := range fs {
300
f()
301
}
302
}
303
304
// Pend skips all specs in the top-level group.
305
//
306
// All Options are ignored.
307
func Pend(t *testing.T, text string, f func(*testing.T, G, S), _ ...Option) bool {
308
t.Helper()
309
return Run(t, text, f, func(c *config) { c.pend = true })
310
}
311
312
// Focus focuses every spec in the provided suite.
313
// This is useful as a shortcut for unfocusing all focused specs.
314
//
315
// Valid Options:
316
// Sequential, Random, Reverse, Parallel
317
// Local, Global, Flat, Nested
318
// Seed, Report
319
func Focus(t *testing.T, text string, f func(*testing.T, G, S), opts ...Option) bool {
320
t.Helper()
321
return Run(t, text, f, append(opts, func(c *config) { c.focus = true })...)
322
}
323
324
// A Plan provides a Reporter with information about a suite.
325
type Plan struct {
326
Text string
327
Total int
328
Pending int
329
Focused int
330
Seed int64
331
HasRandom bool
332
HasFocus bool
333
}
334
335
// A Spec provides a Reporter with information about a spec immediately after
336
// the spec completes.
337
type Spec struct {
338
Text []string
339
Failed bool
340
Skipped bool
341
Focused bool
342
Parallel bool
343
Out io.Reader
344
}
345
346
// A Reporter is provided with information about a suite as it runs.
347
type Reporter interface {
348
349
// Start provides the Reporter with a Plan that describes the suite.
350
// No specs will run until the Start method call finishes.
351
Start(*testing.T, Plan)
352
353
// Specs provides the Reporter with a channel of Specs.
354
// The specs will start running concurrently with the Specs method call.
355
// The Run method will not complete until the Specs method call completes.
356
Specs(*testing.T, <-chan Spec)
357
}
358
359