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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
|
// Licensed under the MIT license, see LICENSE file for details.
package quicktest
import (
"fmt"
"reflect"
"strings"
"sync"
"testing"
)
// Check runs the given check using the provided t and continues execution in
// case of failure. For instance:
//
// qt.Check(t, answer, qt.Equals, 42)
// qt.Check(t, got, qt.IsNil, qt.Commentf("iteration %d", i))
//
// Additional args (not consumed by the checker), when provided, are included as
// comments in the failure output when the check fails.
func Check(t testing.TB, got interface{}, checker Checker, args ...interface{}) bool {
t.Helper()
return New(t).Check(got, checker, args...)
}
// Assert runs the given check using the provided t and stops execution in case
// of failure. For instance:
//
// qt.Assert(t, got, qt.DeepEquals, []int{42, 47})
// qt.Assert(t, got, qt.ErrorMatches, "bad wolf .*", qt.Commentf("a comment"))
//
// Additional args (not consumed by the checker), when provided, are included as
// comments in the failure output when the check fails.
func Assert(t testing.TB, got interface{}, checker Checker, args ...interface{}) bool {
t.Helper()
return New(t).Assert(got, checker, args...)
}
// New returns a new checker instance that uses t to fail the test when checks
// fail. It only ever calls the Fatal, Error and (when available) Run methods
// of t. For instance.
//
// func TestFoo(t *testing.T) {
// t.Run("A=42", func(t *testing.T) {
// c := qt.New(t)
// c.Assert(a, qt.Equals, 42)
// })
// }
//
// The library already provides some base checkers, and more can be added by
// implementing the Checker interface.
//
// If there is a likelihood that Defer will be called, then
// a call to Done should be deferred after calling New.
// For example:
//
// func TestFoo(t *testing.T) {
// c := qt.New(t)
// defer c.Done()
// c.Setenv("HOME", "/non-existent")
// c.Assert(os.Getenv("HOME"), qt.Equals, "/non-existent")
// })
//
// A value of C that's has a non-nil TB field but is otherwise zero is valid.
// So:
//
// c := &qt.C{TB: t}
//
// is valid a way to create a C value; it's exactly the same as:
//
// c := qt.New(t)
//
// Methods on C may be called concurrently, assuming the underlying
// `testing.TB` implementation also allows that.
func New(t testing.TB) *C {
return &C{
TB: t,
}
}
// C is a quicktest checker. It embeds a testing.TB value and provides
// additional checking functionality. If an Assert or Check operation fails, it
// uses the wrapped TB value to fail the test appropriately.
type C struct {
testing.TB
mu sync.Mutex
doneNeeded bool
deferred func()
format formatFunc
}
// cleaner is implemented by testing.TB on Go 1.14 and later.
type cleaner interface {
Cleanup(func())
}
// Defer registers a function to be called when c.Done is
// called. Deferred functions will be called in last added, first called
// order. If c.Done is not called by the end of the test, the test
// may panic. Note that if Cleanup is called, there is no
// need to call Done.
//
// Deprecated: in Go >= 1.14 use testing.TB.Cleanup instead.
func (c *C) Defer(f func()) {
c.mu.Lock()
defer c.mu.Unlock()
if cleaner, ok := c.TB.(cleaner); ok {
// Use TB.Cleanup when available, but add a check
// that Done has been called so that we don't run
// into unexpected Go version incompatibilities.
if c.doneNeeded {
// We've already installed the wrapper func that checks for Done
// so we can avoid doing it again.
cleaner.Cleanup(f)
return
}
c.doneNeeded = true
cleaner.Cleanup(func() {
c.mu.Lock()
doneNeeded := c.doneNeeded
c.mu.Unlock()
if doneNeeded {
panic("Done not called after Defer")
}
f()
})
return
}
oldDeferred := c.deferred
c.deferred = func() {
if oldDeferred != nil {
defer oldDeferred()
}
f()
}
}
// Done calls all the functions registered by Defer in reverse
// registration order. After it's called, the functions are
// unregistered, so calling Done twice will only call them once.
//
// When a test function is called by Run, Done will be called
// automatically on the C value passed into it.
//
// Deprecated: in Go >= 1.14 this is no longer needed if using
// testing.TB.Cleanup.
func (c *C) Done() {
c.mu.Lock()
deferred := c.deferred
c.deferred = nil
c.doneNeeded = false
c.mu.Unlock()
if deferred != nil {
deferred()
}
}
// SetFormat sets the function used to print values in test failures.
// By default Format is used.
// Any subsequent subtests invoked with c.Run will also use this function by
// default.
func (c *C) SetFormat(format func(interface{}) string) {
c.mu.Lock()
c.format = format
c.mu.Unlock()
}
// getFormat returns the format function
// safely acquired under lock.
func (c *C) getFormat() func(interface{}) string {
c.mu.Lock()
defer c.mu.Unlock()
return c.format
}
// Check runs the given check and continues execution in case of failure.
// For instance:
//
// c.Check(answer, qt.Equals, 42)
// c.Check(got, qt.IsNil, qt.Commentf("iteration %d", i))
//
// Additional args (not consumed by the checker), when provided, are included
// as comments in the failure output when the check fails.
func (c *C) Check(got interface{}, checker Checker, args ...interface{}) bool {
c.TB.Helper()
return check(c, checkParams{
fail: c.TB.Error,
checker: checker,
got: got,
args: args,
})
}
// Assert runs the given check and stops execution in case of failure.
// For instance:
//
// c.Assert(got, qt.DeepEquals, []int{42, 47})
// c.Assert(got, qt.ErrorMatches, "bad wolf .*", qt.Commentf("a comment"))
//
// Additional args (not consumed by the checker), when provided, are included
// as comments in the failure output when the check fails.
func (c *C) Assert(got interface{}, checker Checker, args ...interface{}) bool {
c.TB.Helper()
return check(c, checkParams{
fail: c.TB.Fatal,
checker: checker,
got: got,
args: args,
})
}
var (
stringType = reflect.TypeOf("")
boolType = reflect.TypeOf(true)
tbType = reflect.TypeOf(new(testing.TB)).Elem()
)
// Run runs f as a subtest of t called name. It's a wrapper around
// the Run method of c.TB that provides the quicktest checker to f. When
// the function completes, c.Done will be called to run any
// functions registered with c.Defer.
//
// c.TB must implement a Run method of the following form:
//
// Run(string, func(T)) bool
//
// where T is any type that is assignable to testing.TB.
// Implementations include *testing.T, *testing.B and *C itself.
//
// The TB field in the subtest will hold the value passed
// by Run to its argument function.
//
// func TestFoo(t *testing.T) {
// c := qt.New(t)
// c.Run("A=42", func(c *qt.C) {
// // This assertion only stops the current subtest.
// c.Assert(a, qt.Equals, 42)
// })
// }
//
// A panic is raised when Run is called and the embedded concrete type does not
// implement a Run method with a correct signature.
func (c *C) Run(name string, f func(c *C)) bool {
badType := func(m string) {
panic(fmt.Sprintf("cannot execute Run with underlying concrete type %T (%s)", c.TB, m))
}
m := reflect.ValueOf(c.TB).MethodByName("Run")
if !m.IsValid() {
// c.TB doesn't implement a Run method.
badType("no Run method")
}
mt := m.Type()
if mt.NumIn() != 2 ||
mt.In(0) != stringType ||
mt.NumOut() != 1 ||
mt.Out(0) != boolType {
// The Run method doesn't have the right argument counts and types.
badType("wrong argument count for Run method")
}
farg := mt.In(1)
if farg.Kind() != reflect.Func ||
farg.NumIn() != 1 ||
farg.NumOut() != 0 ||
!farg.In(0).AssignableTo(tbType) {
// The first argument to the Run function arg isn't right.
badType("bad first argument type for Run method")
}
fv := reflect.MakeFunc(farg, func(args []reflect.Value) []reflect.Value {
c2 := New(args[0].Interface().(testing.TB))
defer c2.Done()
c2.SetFormat(c.getFormat())
f(c2)
return nil
})
return m.Call([]reflect.Value{reflect.ValueOf(name), fv})[0].Interface().(bool)
}
// Parallel signals that this test is to be run in parallel with (and only with) other parallel tests.
// It's a wrapper around *testing.T.Parallel.
//
// A panic is raised when Parallel is called and the embedded concrete type does not
// implement Parallel, for instance if TB's concrete type is a benchmark.
func (c *C) Parallel() {
p, ok := c.TB.(interface {
Parallel()
})
if !ok {
panic(fmt.Sprintf("cannot execute Parallel with underlying concrete type %T", c.TB))
}
p.Parallel()
}
// check performs the actual check with the provided params.
// In case of failure p.fail is called. In the fail report values are formatted
// using p.format.
func check(c *C, p checkParams) bool {
c.TB.Helper()
rp := reportParams{
got: p.got,
args: p.args,
format: c.getFormat(),
}
if rp.format == nil {
// No format set; use the default: Format.
rp.format = Format
}
// Allow checkers to annotate messages.
note := func(key string, value interface{}) {
rp.notes = append(rp.notes, note{
key: key,
value: value,
})
}
// Ensure that we have a checker.
if p.checker == nil {
p.fail(report(BadCheckf("nil checker provided"), rp))
return false
}
// Extract comments if provided.
for len(p.args) > 0 {
comment, ok := p.args[len(p.args)-1].(Comment)
if !ok {
break
}
rp.comments = append([]Comment{comment}, rp.comments...)
p.args = p.args[:len(p.args)-1]
}
rp.args = p.args
// Validate that we have the correct number of arguments.
rp.argNames = p.checker.ArgNames()
wantNumArgs := len(rp.argNames) - 1
if gotNumArgs := len(rp.args); gotNumArgs != wantNumArgs {
if gotNumArgs > 0 {
note("got args", rp.args)
}
if wantNumArgs > 0 {
note("want args", Unquoted(strings.Join(rp.argNames[1:], ", ")))
}
var prefix string
if gotNumArgs > wantNumArgs {
prefix = "too many arguments provided to checker"
} else {
prefix = "not enough arguments provided to checker"
}
p.fail(report(BadCheckf("%s: got %d, want %d", prefix, gotNumArgs, wantNumArgs), rp))
return false
}
// Execute the check and report the failure if necessary.
if err := p.checker.Check(p.got, p.args, note); err != nil {
p.fail(report(err, rp))
return false
}
return true
}
// checkParams holds parameters for executing a check.
type checkParams struct {
fail func(...interface{})
checker Checker
got interface{}
args []interface{}
}
|