...

Source file src/gotest.tools/v3/assert/cmp/compare_test.go

Documentation: gotest.tools/v3/assert/cmp

     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  // errorWithCause mimics the error formatting of github.com/pkg/errors
   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  	// A Wrapped error also includes the stack
   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(&notnil)()
   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