1package check
2
3import (
4	"fmt"
5	"reflect"
6	"regexp"
7	"strings"
8
9	"github.com/niemeyer/pretty"
10)
11
12// -----------------------------------------------------------------------
13// CommentInterface and Commentf helper, to attach extra information to checks.
14
15type comment struct {
16	format string
17	args   []interface{}
18}
19
20// Commentf returns an infomational value to use with Assert or Check calls.
21// If the checker test fails, the provided arguments will be passed to
22// fmt.Sprintf, and will be presented next to the logged failure.
23//
24// For example:
25//
26//     c.Assert(v, Equals, 42, Commentf("Iteration #%d failed.", i))
27//
28// Note that if the comment is constant, a better option is to
29// simply use a normal comment right above or next to the line, as
30// it will also get printed with any errors:
31//
32//     c.Assert(l, Equals, 8192) // Ensure buffer size is correct (bug #123)
33//
34func Commentf(format string, args ...interface{}) CommentInterface {
35	return &comment{format, args}
36}
37
38// CommentInterface must be implemented by types that attach extra
39// information to failed checks. See the Commentf function for details.
40type CommentInterface interface {
41	CheckCommentString() string
42}
43
44func (c *comment) CheckCommentString() string {
45	return fmt.Sprintf(c.format, c.args...)
46}
47
48// -----------------------------------------------------------------------
49// The Checker interface.
50
51// The Checker interface must be provided by checkers used with
52// the Assert and Check verification methods.
53type Checker interface {
54	Info() *CheckerInfo
55	Check(params []interface{}, names []string) (result bool, error string)
56}
57
58// See the Checker interface.
59type CheckerInfo struct {
60	Name   string
61	Params []string
62}
63
64func (info *CheckerInfo) Info() *CheckerInfo {
65	return info
66}
67
68// -----------------------------------------------------------------------
69// Not checker logic inverter.
70
71// The Not checker inverts the logic of the provided checker.  The
72// resulting checker will succeed where the original one failed, and
73// vice-versa.
74//
75// For example:
76//
77//     c.Assert(a, Not(Equals), b)
78//
79func Not(checker Checker) Checker {
80	return &notChecker{checker}
81}
82
83type notChecker struct {
84	sub Checker
85}
86
87func (checker *notChecker) Info() *CheckerInfo {
88	info := *checker.sub.Info()
89	info.Name = "Not(" + info.Name + ")"
90	return &info
91}
92
93func (checker *notChecker) Check(params []interface{}, names []string) (result bool, error string) {
94	result, error = checker.sub.Check(params, names)
95	result = !result
96	if result {
97		// clear error message if the new result is true
98		error = ""
99	}
100	return
101}
102
103// -----------------------------------------------------------------------
104// IsNil checker.
105
106type isNilChecker struct {
107	*CheckerInfo
108}
109
110// The IsNil checker tests whether the obtained value is nil.
111//
112// For example:
113//
114//    c.Assert(err, IsNil)
115//
116var IsNil Checker = &isNilChecker{
117	&CheckerInfo{Name: "IsNil", Params: []string{"value"}},
118}
119
120func (checker *isNilChecker) Check(params []interface{}, names []string) (result bool, error string) {
121	return isNil(params[0]), ""
122}
123
124func isNil(obtained interface{}) (result bool) {
125	if obtained == nil {
126		result = true
127	} else {
128		switch v := reflect.ValueOf(obtained); v.Kind() {
129		case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
130			return v.IsNil()
131		}
132	}
133	return
134}
135
136// -----------------------------------------------------------------------
137// NotNil checker. Alias for Not(IsNil), since it's so common.
138
139type notNilChecker struct {
140	*CheckerInfo
141}
142
143// The NotNil checker verifies that the obtained value is not nil.
144//
145// For example:
146//
147//     c.Assert(iface, NotNil)
148//
149// This is an alias for Not(IsNil), made available since it's a
150// fairly common check.
151//
152var NotNil Checker = &notNilChecker{
153	&CheckerInfo{Name: "NotNil", Params: []string{"value"}},
154}
155
156func (checker *notNilChecker) Check(params []interface{}, names []string) (result bool, error string) {
157	return !isNil(params[0]), ""
158}
159
160// -----------------------------------------------------------------------
161// Equals checker.
162
163func diffworthy(a interface{}) bool {
164	if a == nil {
165		return false
166	}
167
168	t := reflect.TypeOf(a)
169	switch t.Kind() {
170	case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct, reflect.String, reflect.Ptr:
171		return true
172	}
173	return false
174}
175
176// formatUnequal will dump the actual and expected values into a textual
177// representation and return an error message containing a diff.
178func formatUnequal(obtained interface{}, expected interface{}) string {
179	// We do not do diffs for basic types because go-check already
180	// shows them very cleanly.
181	if !diffworthy(obtained) || !diffworthy(expected) {
182		return ""
183	}
184
185	// Handle strings, short strings are ignored (go-check formats
186	// them very nicely already). We do multi-line strings by
187	// generating two string slices and using kr.Diff to compare
188	// those (kr.Diff does not do string diffs by itself).
189	aStr, aOK := obtained.(string)
190	bStr, bOK := expected.(string)
191	if aOK && bOK {
192		l1 := strings.Split(aStr, "\n")
193		l2 := strings.Split(bStr, "\n")
194		// the "2" here is a bit arbitrary
195		if len(l1) > 2 && len(l2) > 2 {
196			diff := pretty.Diff(l1, l2)
197			return fmt.Sprintf(`String difference:
198%s`, formatMultiLine(strings.Join(diff, "\n"), false))
199		}
200		// string too short
201		return ""
202	}
203
204	// generic diff
205	diff := pretty.Diff(obtained, expected)
206	if len(diff) == 0 {
207		// No diff, this happens when e.g. just struct
208		// pointers are different but the structs have
209		// identical values.
210		return ""
211	}
212
213	return fmt.Sprintf(`Difference:
214%s`, formatMultiLine(strings.Join(diff, "\n"), false))
215}
216
217type equalsChecker struct {
218	*CheckerInfo
219}
220
221// The Equals checker verifies that the obtained value is equal to
222// the expected value, according to usual Go semantics for ==.
223//
224// For example:
225//
226//     c.Assert(value, Equals, 42)
227//
228var Equals Checker = &equalsChecker{
229	&CheckerInfo{Name: "Equals", Params: []string{"obtained", "expected"}},
230}
231
232func (checker *equalsChecker) Check(params []interface{}, names []string) (result bool, error string) {
233	defer func() {
234		if v := recover(); v != nil {
235			result = false
236			error = fmt.Sprint(v)
237		}
238	}()
239
240	result = params[0] == params[1]
241	if !result {
242		error = formatUnequal(params[0], params[1])
243	}
244	return
245}
246
247// -----------------------------------------------------------------------
248// DeepEquals checker.
249
250type deepEqualsChecker struct {
251	*CheckerInfo
252}
253
254// The DeepEquals checker verifies that the obtained value is deep-equal to
255// the expected value.  The check will work correctly even when facing
256// slices, interfaces, and values of different types (which always fail
257// the test).
258//
259// For example:
260//
261//     c.Assert(value, DeepEquals, 42)
262//     c.Assert(array, DeepEquals, []string{"hi", "there"})
263//
264var DeepEquals Checker = &deepEqualsChecker{
265	&CheckerInfo{Name: "DeepEquals", Params: []string{"obtained", "expected"}},
266}
267
268func (checker *deepEqualsChecker) Check(params []interface{}, names []string) (result bool, error string) {
269	result = reflect.DeepEqual(params[0], params[1])
270	if !result {
271		error = formatUnequal(params[0], params[1])
272	}
273	return
274}
275
276// -----------------------------------------------------------------------
277// HasLen checker.
278
279type hasLenChecker struct {
280	*CheckerInfo
281}
282
283// The HasLen checker verifies that the obtained value has the
284// provided length. In many cases this is superior to using Equals
285// in conjunction with the len function because in case the check
286// fails the value itself will be printed, instead of its length,
287// providing more details for figuring the problem.
288//
289// For example:
290//
291//     c.Assert(list, HasLen, 5)
292//
293var HasLen Checker = &hasLenChecker{
294	&CheckerInfo{Name: "HasLen", Params: []string{"obtained", "n"}},
295}
296
297func (checker *hasLenChecker) Check(params []interface{}, names []string) (result bool, error string) {
298	n, ok := params[1].(int)
299	if !ok {
300		return false, "n must be an int"
301	}
302	value := reflect.ValueOf(params[0])
303	switch value.Kind() {
304	case reflect.Map, reflect.Array, reflect.Slice, reflect.Chan, reflect.String:
305	default:
306		return false, "obtained value type has no length"
307	}
308	return value.Len() == n, ""
309}
310
311// -----------------------------------------------------------------------
312// ErrorMatches checker.
313
314type errorMatchesChecker struct {
315	*CheckerInfo
316}
317
318// The ErrorMatches checker verifies that the error value
319// is non nil and matches the regular expression provided.
320//
321// For example:
322//
323//     c.Assert(err, ErrorMatches, "perm.*denied")
324//
325var ErrorMatches Checker = errorMatchesChecker{
326	&CheckerInfo{Name: "ErrorMatches", Params: []string{"value", "regex"}},
327}
328
329func (checker errorMatchesChecker) Check(params []interface{}, names []string) (result bool, errStr string) {
330	if params[0] == nil {
331		return false, "Error value is nil"
332	}
333	err, ok := params[0].(error)
334	if !ok {
335		return false, "Value is not an error"
336	}
337	params[0] = err.Error()
338	names[0] = "error"
339	return matches(params[0], params[1])
340}
341
342// -----------------------------------------------------------------------
343// Matches checker.
344
345type matchesChecker struct {
346	*CheckerInfo
347}
348
349// The Matches checker verifies that the string provided as the obtained
350// value (or the string resulting from obtained.String()) matches the
351// regular expression provided.
352//
353// For example:
354//
355//     c.Assert(err, Matches, "perm.*denied")
356//
357var Matches Checker = &matchesChecker{
358	&CheckerInfo{Name: "Matches", Params: []string{"value", "regex"}},
359}
360
361func (checker *matchesChecker) Check(params []interface{}, names []string) (result bool, error string) {
362	return matches(params[0], params[1])
363}
364
365func matches(value, regex interface{}) (result bool, error string) {
366	reStr, ok := regex.(string)
367	if !ok {
368		return false, "Regex must be a string"
369	}
370	valueStr, valueIsStr := value.(string)
371	if !valueIsStr {
372		if valueWithStr, valueHasStr := value.(fmt.Stringer); valueHasStr {
373			valueStr, valueIsStr = valueWithStr.String(), true
374		}
375	}
376	if valueIsStr {
377		matches, err := regexp.MatchString("^"+reStr+"$", valueStr)
378		if err != nil {
379			return false, "Can't compile regex: " + err.Error()
380		}
381		return matches, ""
382	}
383	return false, "Obtained value is not a string and has no .String()"
384}
385
386// -----------------------------------------------------------------------
387// Panics checker.
388
389type panicsChecker struct {
390	*CheckerInfo
391}
392
393// The Panics checker verifies that calling the provided zero-argument
394// function will cause a panic which is deep-equal to the provided value.
395//
396// For example:
397//
398//     c.Assert(func() { f(1, 2) }, Panics, &SomeErrorType{"BOOM"}).
399//
400//
401var Panics Checker = &panicsChecker{
402	&CheckerInfo{Name: "Panics", Params: []string{"function", "expected"}},
403}
404
405func (checker *panicsChecker) Check(params []interface{}, names []string) (result bool, error string) {
406	f := reflect.ValueOf(params[0])
407	if f.Kind() != reflect.Func || f.Type().NumIn() != 0 {
408		return false, "Function must take zero arguments"
409	}
410	defer func() {
411		// If the function has not panicked, then don't do the check.
412		if error != "" {
413			return
414		}
415		params[0] = recover()
416		names[0] = "panic"
417		result = reflect.DeepEqual(params[0], params[1])
418	}()
419	f.Call(nil)
420	return false, "Function has not panicked"
421}
422
423type panicMatchesChecker struct {
424	*CheckerInfo
425}
426
427// The PanicMatches checker verifies that calling the provided zero-argument
428// function will cause a panic with an error value matching
429// the regular expression provided.
430//
431// For example:
432//
433//     c.Assert(func() { f(1, 2) }, PanicMatches, `open.*: no such file or directory`).
434//
435//
436var PanicMatches Checker = &panicMatchesChecker{
437	&CheckerInfo{Name: "PanicMatches", Params: []string{"function", "expected"}},
438}
439
440func (checker *panicMatchesChecker) Check(params []interface{}, names []string) (result bool, errmsg string) {
441	f := reflect.ValueOf(params[0])
442	if f.Kind() != reflect.Func || f.Type().NumIn() != 0 {
443		return false, "Function must take zero arguments"
444	}
445	defer func() {
446		// If the function has not panicked, then don't do the check.
447		if errmsg != "" {
448			return
449		}
450		obtained := recover()
451		names[0] = "panic"
452		if e, ok := obtained.(error); ok {
453			params[0] = e.Error()
454		} else if _, ok := obtained.(string); ok {
455			params[0] = obtained
456		} else {
457			errmsg = "Panic value is not a string or an error"
458			return
459		}
460		result, errmsg = matches(params[0], params[1])
461	}()
462	f.Call(nil)
463	return false, "Function has not panicked"
464}
465
466// -----------------------------------------------------------------------
467// FitsTypeOf checker.
468
469type fitsTypeChecker struct {
470	*CheckerInfo
471}
472
473// The FitsTypeOf checker verifies that the obtained value is
474// assignable to a variable with the same type as the provided
475// sample value.
476//
477// For example:
478//
479//     c.Assert(value, FitsTypeOf, int64(0))
480//     c.Assert(value, FitsTypeOf, os.Error(nil))
481//
482var FitsTypeOf Checker = &fitsTypeChecker{
483	&CheckerInfo{Name: "FitsTypeOf", Params: []string{"obtained", "sample"}},
484}
485
486func (checker *fitsTypeChecker) Check(params []interface{}, names []string) (result bool, error string) {
487	obtained := reflect.ValueOf(params[0])
488	sample := reflect.ValueOf(params[1])
489	if !obtained.IsValid() {
490		return false, ""
491	}
492	if !sample.IsValid() {
493		return false, "Invalid sample value"
494	}
495	return obtained.Type().AssignableTo(sample.Type()), ""
496}
497
498// -----------------------------------------------------------------------
499// Implements checker.
500
501type implementsChecker struct {
502	*CheckerInfo
503}
504
505// The Implements checker verifies that the obtained value
506// implements the interface specified via a pointer to an interface
507// variable.
508//
509// For example:
510//
511//     var e os.Error
512//     c.Assert(err, Implements, &e)
513//
514var Implements Checker = &implementsChecker{
515	&CheckerInfo{Name: "Implements", Params: []string{"obtained", "ifaceptr"}},
516}
517
518func (checker *implementsChecker) Check(params []interface{}, names []string) (result bool, error string) {
519	obtained := reflect.ValueOf(params[0])
520	ifaceptr := reflect.ValueOf(params[1])
521	if !obtained.IsValid() {
522		return false, ""
523	}
524	if !ifaceptr.IsValid() || ifaceptr.Kind() != reflect.Ptr || ifaceptr.Elem().Kind() != reflect.Interface {
525		return false, "ifaceptr should be a pointer to an interface variable"
526	}
527	return obtained.Type().Implements(ifaceptr.Elem().Type()), ""
528}
529