1 package cmp
2
3 import (
4 "errors"
5 "fmt"
6 "go/ast"
7 "io"
8 "os"
9 "reflect"
10 "regexp"
11 "strings"
12 "testing"
13
14 "github.com/google/go-cmp/cmp"
15 )
16
17 func TestDeepEqual(t *testing.T) {
18 t.Run("failure", func(t *testing.T) {
19 result := DeepEqual([]string{"a", "b"}, []string{"b", "a"})()
20 if result.Success() {
21 t.Errorf("expected failure")
22 }
23
24 args := []ast.Expr{&ast.Ident{Name: "result"}, &ast.Ident{Name: "exp"}}
25 message := result.(templatedResult).FailureMessage(args)
26 expected := "\n--- result\n+++ exp\n"
27 if !strings.HasPrefix(message, expected) {
28 t.Errorf("expected prefix \n%q\ngot\n%q\n", expected, message)
29 }
30 })
31
32 t.Run("success", func(t *testing.T) {
33 actual := DeepEqual([]string{"a"}, []string{"a"})()
34 assertSuccess(t, actual)
35 })
36 }
37
38 type Stub struct {
39 unx int
40 }
41
42 func TestDeepEqualWithUnexported(t *testing.T) {
43 result := DeepEqual(Stub{}, Stub{unx: 1})()
44 assertFailureHasPrefix(t, result, `cannot handle unexported field at {cmp.Stub}.unx:`)
45 }
46
47 func TestRegexp(t *testing.T) {
48 var testcases = []struct {
49 name string
50 regex interface{}
51 value string
52 match bool
53 expErr string
54 }{
55 {
56 name: "pattern string match",
57 regex: "^[0-9]+$",
58 value: "12123423456",
59 match: true,
60 },
61 {
62 name: "simple pattern string no match",
63 regex: "bob",
64 value: "Probably",
65 expErr: `value "Probably" does not match regexp "bob"`,
66 },
67 {
68 name: "pattern string no match",
69 regex: "^1",
70 value: "2123423456",
71 expErr: `value "2123423456" does not match regexp "^1"`,
72 },
73 {
74 name: "regexp match",
75 regex: regexp.MustCompile("^d[0-9a-f]{8}$"),
76 value: "d1632beef",
77 match: true,
78 },
79 {
80 name: "invalid regexp",
81 regex: "^1(",
82 value: "2",
83 expErr: "error parsing regexp: missing closing ): `^1(`",
84 },
85 {
86 name: "invalid type",
87 regex: struct{}{},
88 value: "some string",
89 expErr: "invalid type struct {} for regex pattern",
90 },
91 }
92
93 for _, tc := range testcases {
94 t.Run(tc.name, func(t *testing.T) {
95 res := Regexp(tc.regex, tc.value)()
96 if tc.match {
97 assertSuccess(t, res)
98 } else {
99 assertFailure(t, res, tc.expErr)
100 }
101 })
102 }
103 }
104
105 func TestLen(t *testing.T) {
106 var testcases = []struct {
107 seq interface{}
108 length int
109 expectedSuccess bool
110 expectedMessage string
111 }{
112 {
113 seq: []string{"A", "b", "c"},
114 length: 3,
115 expectedSuccess: true,
116 },
117 {
118 seq: []string{"A", "b", "c"},
119 length: 2,
120 expectedMessage: "expected [A b c] (length 3) to have length 2",
121 },
122 {
123 seq: map[string]int{"a": 1, "b": 2},
124 length: 2,
125 expectedSuccess: true,
126 },
127 {
128 seq: [3]string{"a", "b", "c"},
129 length: 3,
130 expectedSuccess: true,
131 },
132 {
133 seq: "abcd",
134 length: 4,
135 expectedSuccess: true,
136 },
137 {
138 seq: "abcd",
139 length: 3,
140 expectedMessage: "expected abcd (length 4) to have length 3",
141 },
142 }
143
144 for _, testcase := range testcases {
145 t.Run(fmt.Sprintf("%v len=%d", testcase.seq, testcase.length), func(t *testing.T) {
146 result := Len(testcase.seq, testcase.length)()
147 if testcase.expectedSuccess {
148 assertSuccess(t, result)
149 } else {
150 assertFailure(t, result, testcase.expectedMessage)
151 }
152 })
153 }
154 }
155
156 func TestPanics(t *testing.T) {
157 panicker := func() {
158 panic("AHHHHHHHHHHH")
159 }
160
161 result := Panics(panicker)()
162 assertSuccess(t, result)
163
164 result = Panics(func() {})()
165 assertFailure(t, result, "did not panic")
166 }
167
168 type innerstub struct {
169 num int
170 }
171
172 type stub struct {
173 stub innerstub
174 num int
175 }
176
177 func TestDeepEqualEquivalenceToReflectDeepEqual(t *testing.T) {
178 var testcases = []struct {
179 left interface{}
180 right interface{}
181 }{
182 {nil, nil},
183 {7, 7},
184 {false, false},
185 {stub{innerstub{1}, 2}, stub{innerstub{1}, 2}},
186 {[]int{1, 2, 3}, []int{1, 2, 3}},
187 {[]byte(nil), []byte(nil)},
188 {nil, []byte(nil)},
189 {1, uint64(1)},
190 {7, "7"},
191 }
192 for _, testcase := range testcases {
193 expected := reflect.DeepEqual(testcase.left, testcase.right)
194 res := DeepEqual(testcase.left, testcase.right, cmpStub)()
195 if res.Success() != expected {
196 msg := res.(StringResult).FailureMessage()
197 t.Errorf("deepEqual(%v, %v) did not return %v (message %s)",
198 testcase.left, testcase.right, expected, msg)
199 }
200 }
201 }
202
203 var cmpStub = cmp.AllowUnexported(stub{}, innerstub{})
204
205 func TestContains(t *testing.T) {
206 var testcases = []struct {
207 seq interface{}
208 item interface{}
209 expected bool
210 expectedMsg string
211 }{
212 {
213 seq: error(nil),
214 item: 0,
215 expectedMsg: "nil does not contain items",
216 },
217 {
218 seq: "abcdef",
219 item: "cde",
220 expected: true,
221 },
222 {
223 seq: "abcdef",
224 item: "foo",
225 expectedMsg: `string "abcdef" does not contain "foo"`,
226 },
227 {
228 seq: "abcdef",
229 item: 3,
230 expectedMsg: `string may only contain strings`,
231 },
232 {
233 seq: map[rune]int{'a': 1, 'b': 2},
234 item: 'b',
235 expected: true,
236 },
237 {
238 seq: map[rune]int{'a': 1},
239 item: 'c',
240 expectedMsg: "map[97:1] does not contain 99",
241 },
242 {
243 seq: map[int]int{'a': 1, 'b': 2},
244 item: 'b',
245 expectedMsg: "map[int]int can not contain a int32 key",
246 },
247 {
248 seq: []interface{}{"a", 1, 'a', 1.0, true},
249 item: 'a',
250 expected: true,
251 },
252 {
253 seq: []interface{}{"a", 1, 'a', 1.0, true},
254 item: 3,
255 expectedMsg: "[a 1 97 1 true] does not contain 3",
256 },
257 {
258 seq: [3]byte{99, 10, 100},
259 item: byte(99),
260 expected: true,
261 },
262 {
263 seq: [3]byte{99, 10, 100},
264 item: byte(98),
265 expectedMsg: "[99 10 100] does not contain 98",
266 },
267 }
268 for _, testcase := range testcases {
269 name := fmt.Sprintf("%v in %v", testcase.item, testcase.seq)
270 t.Run(name, func(t *testing.T) {
271 result := Contains(testcase.seq, testcase.item)()
272 if testcase.expected {
273 assertSuccess(t, result)
274 } else {
275 assertFailure(t, result, testcase.expectedMsg)
276 }
277 })
278 }
279 }
280
281 func TestEqualMultiLine(t *testing.T) {
282 result := `abcd
283 1234
284 aaaa
285 bbbb`
286
287 exp := `abcd
288 1111
289 aaaa
290 bbbb`
291
292 expected := `
293 --- result
294 +++ exp
295 @@ -1,4 +1,4 @@
296 abcd
297 -1234
298 +1111
299 aaaa
300 bbbb
301 `
302
303 args := []ast.Expr{&ast.Ident{Name: "result"}, &ast.Ident{Name: "exp"}}
304 res := Equal(result, exp)()
305 assertFailureTemplate(t, res, args, expected)
306 }
307
308 func TestEqual_PointersNotEqual(t *testing.T) {
309 x := 123
310 y := 123
311
312 res := Equal(&x, &y)()
313 args := []ast.Expr{&ast.Ident{Name: "x"}, &ast.Ident{Name: "y"}}
314 expected := fmt.Sprintf("%p (x *int) != %p (y *int)", &x, &y)
315 assertFailureTemplate(t, res, args, expected)
316 }
317
318
319 type errorWithCause struct {
320 msg string
321 cause error
322 }
323
324 func (e errorWithCause) Error() string {
325 return fmt.Sprintf("%v with cause: %v", e.msg, e.cause)
326 }
327
328 func (e errorWithCause) Cause() error {
329 return e.cause
330 }
331
332 func (e errorWithCause) Format(s fmt.State, verb rune) {
333 switch verb {
334 case 'v':
335 if s.Flag('+') {
336 fmt.Fprintf(s, "%+v", e.Cause())
337 fmt.Fprint(s, "\nstack trace")
338 return
339 }
340 fallthrough
341 case 's':
342 io.WriteString(s, e.Error())
343 case 'q':
344 fmt.Fprintf(s, "%q", e.Error())
345 }
346 }
347
348 func TestError(t *testing.T) {
349 result := Error(nil, "the error message")()
350 assertFailure(t, result, "expected an error, got nil")
351
352
353 result = Error(errorWithCause{cause: errors.New("other"), msg: "wrapped"}, "the error message")()
354 assertFailureHasPrefix(t, result,
355 `expected error "the error message", got "wrapped with cause: other"
356 other
357 stack trace`)
358
359 msg := "the message"
360 result = Error(errors.New(msg), msg)()
361 assertSuccess(t, result)
362 }
363
364 func TestErrorContains(t *testing.T) {
365 result := ErrorContains(nil, "the error message")()
366 assertFailure(t, result, "expected an error, got nil")
367
368 result = ErrorContains(errors.New("other"), "the error")()
369 assertFailureHasPrefix(t, result,
370 `expected error to contain "the error", got "other"`)
371
372 msg := "the full message"
373 result = ErrorContains(errors.New(msg), "full")()
374 assertSuccess(t, result)
375 }
376
377 func TestNil(t *testing.T) {
378 result := Nil(nil)()
379 assertSuccess(t, result)
380
381 var s *string
382 result = Nil(s)()
383 assertSuccess(t, result)
384
385 var closer io.Closer
386 result = Nil(closer)()
387 assertSuccess(t, result)
388
389 result = Nil("wrong")()
390 assertFailure(t, result, "wrong (type string) can not be nil")
391
392 notnil := "notnil"
393 result = Nil(¬nil)()
394 assertFailure(t, result, "notnil (type *string) is not nil")
395
396 result = Nil([]string{"a"})()
397 assertFailure(t, result, "[a] (type []string) is not nil")
398 }
399
400 type testingT interface {
401 Errorf(msg string, args ...interface{})
402 }
403
404 type helperT interface {
405 Helper()
406 }
407
408 func assertSuccess(t testingT, res Result) {
409 if ht, ok := t.(helperT); ok {
410 ht.Helper()
411 }
412 if !res.Success() {
413 msg := res.(StringResult).FailureMessage()
414 t.Errorf("expected success, but got failure with message %q", msg)
415 }
416 }
417
418 func assertFailure(t testingT, res Result, expected string) {
419 if ht, ok := t.(helperT); ok {
420 ht.Helper()
421 }
422 if res.Success() {
423 t.Errorf("expected failure")
424 }
425 message := res.(StringResult).FailureMessage()
426 if message != expected {
427 t.Errorf("expected \n%q\ngot\n%q\n", expected, message)
428 }
429 }
430
431 func assertFailureHasPrefix(t testingT, res Result, prefix string) {
432 if ht, ok := t.(helperT); ok {
433 ht.Helper()
434 }
435 if res.Success() {
436 t.Errorf("expected failure")
437 }
438 message := res.(StringResult).FailureMessage()
439 if !strings.HasPrefix(message, prefix) {
440 t.Errorf("expected \n%v\nto start with\n%v\n", message, prefix)
441 }
442 }
443
444 func assertFailureTemplate(t testingT, res Result, args []ast.Expr, expected string) {
445 if ht, ok := t.(helperT); ok {
446 ht.Helper()
447 }
448 if res.Success() {
449 t.Errorf("expected failure")
450 }
451 message := res.(templatedResult).FailureMessage(args)
452 if message != expected {
453 t.Errorf("expected \n%q\ngot\n%q\n", expected, message)
454 }
455 }
456
457 type stubError struct{}
458
459 func (s stubError) Error() string {
460 return "stub error"
461 }
462
463 func isErrorOfTypeStub(err error) bool {
464 return reflect.TypeOf(err) == reflect.TypeOf(stubError{})
465 }
466
467 type notStubError struct{}
468
469 func (s notStubError) Error() string {
470 return "not stub error"
471 }
472
473 func isErrorOfTypeNotStub(err error) bool {
474 return reflect.TypeOf(err) == reflect.TypeOf(notStubError{})
475 }
476
477 type specialStubIface interface {
478 Special()
479 }
480
481 type stubPtrError struct{}
482
483 func (s *stubPtrError) Error() string {
484 return "stub ptr error"
485 }
486
487 func TestErrorTypeWithNil(t *testing.T) {
488 var testcases = []struct {
489 name string
490 expType interface{}
491 expected string
492 }{
493 {
494 name: "with struct",
495 expType: stubError{},
496 expected: "error is nil, not cmp.stubError",
497 },
498 {
499 name: "with pointer to struct",
500 expType: &stubPtrError{},
501 expected: "error is nil, not *cmp.stubPtrError",
502 },
503 {
504 name: "with interface",
505 expType: (*specialStubIface)(nil),
506 expected: "error is nil, not cmp.specialStubIface",
507 },
508 {
509 name: "with reflect.Type",
510 expType: reflect.TypeOf(stubError{}),
511 expected: "error is nil, not cmp.stubError",
512 },
513 }
514 for _, testcase := range testcases {
515 t.Run(testcase.name, func(t *testing.T) {
516 result := ErrorType(nil, testcase.expType)()
517 assertFailure(t, result, testcase.expected)
518 })
519 }
520 }
521
522 func TestErrorTypeSuccess(t *testing.T) {
523 var testcases = []struct {
524 name string
525 expType interface{}
526 err error
527 }{
528 {
529 name: "with function",
530 expType: isErrorOfTypeStub,
531 err: stubError{},
532 },
533 {
534 name: "with struct",
535 expType: stubError{},
536 err: stubError{},
537 },
538 {
539 name: "with pointer to struct",
540 expType: &stubPtrError{},
541 err: &stubPtrError{},
542 },
543 {
544 name: "with interface",
545 expType: (*error)(nil),
546 err: stubError{},
547 },
548 {
549 name: "with reflect.Type struct",
550 expType: reflect.TypeOf(stubError{}),
551 err: stubError{},
552 },
553 {
554 name: "with reflect.Type interface",
555 expType: reflect.TypeOf((*error)(nil)).Elem(),
556 err: stubError{},
557 },
558 }
559 for _, testcase := range testcases {
560 t.Run(testcase.name, func(t *testing.T) {
561 result := ErrorType(testcase.err, testcase.expType)()
562 assertSuccess(t, result)
563 })
564 }
565 }
566
567 func TestErrorTypeFailure(t *testing.T) {
568 var testcases = []struct {
569 name string
570 expType interface{}
571 expected string
572 }{
573 {
574 name: "with struct",
575 expType: notStubError{},
576 expected: "error is stub error (cmp.stubError), not cmp.notStubError",
577 },
578 {
579 name: "with pointer to struct",
580 expType: &stubPtrError{},
581 expected: "error is stub error (cmp.stubError), not *cmp.stubPtrError",
582 },
583 {
584 name: "with interface",
585 expType: (*specialStubIface)(nil),
586 expected: "error is stub error (cmp.stubError), not cmp.specialStubIface",
587 },
588 {
589 name: "with reflect.Type struct",
590 expType: reflect.TypeOf(notStubError{}),
591 expected: "error is stub error (cmp.stubError), not cmp.notStubError",
592 },
593 {
594 name: "with reflect.Type interface",
595 expType: reflect.TypeOf((*specialStubIface)(nil)).Elem(),
596 expected: "error is stub error (cmp.stubError), not cmp.specialStubIface",
597 },
598 }
599 for _, testcase := range testcases {
600 t.Run(testcase.name, func(t *testing.T) {
601 result := ErrorType(stubError{}, testcase.expType)()
602 assertFailure(t, result, testcase.expected)
603 })
604 }
605 }
606
607 func TestErrorTypeInvalid(t *testing.T) {
608 result := ErrorType(stubError{}, nil)()
609 assertFailure(t, result, "invalid type for expected: nil")
610
611 result = ErrorType(stubError{}, "my type!")()
612 assertFailure(t, result, "invalid type for expected: string")
613 }
614
615 func TestErrorTypeWithFunc(t *testing.T) {
616 result := ErrorType(nil, isErrorOfTypeStub)()
617 assertFailureTemplate(t, result,
618 []ast.Expr{nil, &ast.Ident{Name: "isErrorOfTypeStub"}},
619 "error is nil, not isErrorOfTypeStub")
620
621 result = ErrorType(stubError{}, isErrorOfTypeNotStub)()
622 assertFailureTemplate(t, result,
623 []ast.Expr{nil, &ast.Ident{Name: "isErrorOfTypeNotStub"}},
624 "error is stub error (cmp.stubError), not isErrorOfTypeNotStub")
625 }
626
627 func TestErrorIs(t *testing.T) {
628 t.Run("equal", func(t *testing.T) {
629 result := ErrorIs(stubError{}, stubError{})()
630 assertSuccess(t, result)
631 })
632 t.Run("actual is nil, not stdlib error", func(t *testing.T) {
633 result := ErrorIs(nil, stubError{})()
634 args := []ast.Expr{
635 &ast.Ident{Name: "err"},
636 &ast.SelectorExpr{
637 X: &ast.Ident{Name: "mypkg"},
638 Sel: &ast.Ident{Name: "StubError"},
639 },
640 }
641 expected := `error is nil, not "stub error" (mypkg.StubError cmp.stubError)`
642 assertFailureTemplate(t, result, args, expected)
643 })
644 t.Run("not equal, not stdlib error", func(t *testing.T) {
645 result := ErrorIs(notStubError{}, stubError{})()
646 args := []ast.Expr{
647 &ast.Ident{Name: "err"},
648 &ast.SelectorExpr{
649 X: &ast.Ident{Name: "mypkg"},
650 Sel: &ast.Ident{Name: "StubError"},
651 },
652 }
653 expected := `error is "not stub error" (cmp.notStubError), not "stub error" (mypkg.StubError cmp.stubError)`
654 assertFailureTemplate(t, result, args, expected)
655 })
656 t.Run("actual is nil, stdlib error", func(t *testing.T) {
657 result := ErrorIs(nil, os.ErrClosed)()
658 args := []ast.Expr{
659 &ast.Ident{Name: "err"},
660 &ast.SelectorExpr{
661 X: &ast.Ident{Name: "os"},
662 Sel: &ast.Ident{Name: "ErrClosed"},
663 },
664 }
665 expected := `error is nil, not "file already closed" (os.ErrClosed)`
666 assertFailureTemplate(t, result, args, expected)
667 })
668 t.Run("not equal, stdlib error", func(t *testing.T) {
669 result := ErrorIs(fmt.Errorf("foo"), os.ErrClosed)()
670 args := []ast.Expr{
671 &ast.Ident{Name: "err"},
672 &ast.SelectorExpr{
673 X: &ast.Ident{Name: "os"},
674 Sel: &ast.Ident{Name: "ErrClosed"},
675 },
676 }
677 expected := `error is "foo", not "file already closed" (os.ErrClosed)`
678 assertFailureTemplate(t, result, args, expected)
679 })
680 }
681
View as plain text