github.com/hedzr/evendeep@v0.4.8/diff/diff_test.go (about)

     1  package diff_test
     2  
     3  import (
     4  	"github.com/hedzr/evendeep/diff"
     5  	"github.com/hedzr/evendeep/diff/testdata" //nolint:typecheck
     6  	"github.com/hedzr/evendeep/internal/tool"
     7  	"github.com/hedzr/evendeep/typ"
     8  
     9  	"reflect"
    10  	"testing"
    11  	"time"
    12  )
    13  
    14  type testStruct struct {
    15  	A, b int
    16  	C    []int
    17  	D    [3]int
    18  }
    19  
    20  type RecursiveStruct struct {
    21  	Key   int
    22  	Child *RecursiveStruct
    23  }
    24  
    25  func newRecursiveStruct(key int) *RecursiveStruct {
    26  	a := &RecursiveStruct{
    27  		Key: key,
    28  	}
    29  	b := &RecursiveStruct{
    30  		Key:   key,
    31  		Child: a,
    32  	}
    33  	a.Child = b
    34  	return a
    35  }
    36  
    37  type testCase struct {
    38  	a, b  typ.Any
    39  	diff  string
    40  	equal bool
    41  	opt   diff.Opt
    42  }
    43  
    44  func checkTestCases(t *testing.T, testData []testCase) {
    45  	for i, td := range testData {
    46  		dif, equal := diff.New(td.a, td.b, td.opt)
    47  		if dif.PrettyPrint() != td.diff {
    48  			t.Errorf("%d. PrettyDiff(%#v, %#v) diff = %#v; not %#v", i, td.a, td.b, dif.String(), td.diff)
    49  			continue
    50  		}
    51  		if equal != td.equal {
    52  			t.Errorf("%d. PrettyDiff(%#v, %#v) equal = %#v; not %#v", i, td.a, td.b, equal, td.equal)
    53  			continue
    54  		}
    55  		t.Logf("%d passed. PrettyDiff(%#v, %#v)", i, td.a, td.b)
    56  	}
    57  }
    58  
    59  type timeComparer struct{}
    60  
    61  func (c *timeComparer) Match(typ reflect.Type) bool {
    62  	return typ.String() == "time.Time"
    63  }
    64  
    65  func (c *timeComparer) Equal(ctx diff.Context, lhs, rhs reflect.Value, path diff.Path) (equal bool) {
    66  	aTime := lhs.Interface().(time.Time) //nolint:errcheck //no need
    67  	bTime := rhs.Interface().(time.Time) //nolint:errcheck //no need
    68  	if equal = aTime.Equal(bTime); !equal {
    69  		ctx.PutModified(ctx.PutPath(path), diff.Update{Old: aTime.String(), New: bTime.String(), Typ: tool.Typfmtvlite(&lhs)})
    70  	}
    71  	return
    72  }
    73  
    74  func TestPrettyDiff(t *testing.T) {
    75  	testData := []testCase{
    76  		{
    77  			[]int{3, 0, 9},
    78  			[]int{9, 3, 0},
    79  			"",
    80  			true,
    81  			diff.WithSliceOrderedComparison(true),
    82  		},
    83  
    84  		{
    85  			[]typ.Any{3, 0, 9},
    86  			[]typ.Any{9, 3, 0},
    87  			"",
    88  			true,
    89  			diff.WithSliceOrderedComparison(true),
    90  		},
    91  
    92  		{
    93  			true,
    94  			false,
    95  			// "modified:  = false\n",
    96  			"modified:  = false (bool) (Old: true)\n",
    97  			false,
    98  			diff.WithComparer(&timeComparer{}),
    99  		},
   100  		{
   101  			true,
   102  			0,
   103  			"modified:  = <zero> (int) (Old: true)\n",
   104  			false,
   105  			diff.WithIgnoredFields(),
   106  		},
   107  		{
   108  			[]int{0, 1, 2},
   109  			[]int{0, 1, 2, 3},
   110  			"added: [3] = 3\n",
   111  			false,
   112  			nil,
   113  		},
   114  		{
   115  			[]int{0, 1, 2, 3},
   116  			[]int{0, 1, 2},
   117  			"removed: [3] = 3\n",
   118  			false,
   119  			nil,
   120  		},
   121  		{
   122  			[]int{0},
   123  			[]int{1},
   124  			// "added: [0] = 1\nremoved: [0] = <zero>\n",
   125  			"modified: [0] = 1 (int) (Old: <zero>)\n",
   126  			false,
   127  			nil,
   128  		},
   129  		{
   130  			&[]int{0},
   131  			&[]int{1},
   132  			// "added: [0] = 1\nremoved: [0] = <zero>\n",
   133  			"modified: [0] = 1 (int) (Old: <zero>)\n",
   134  			false,
   135  			nil,
   136  		},
   137  		{
   138  			map[string]int{"a": 1, "b": 2},
   139  			map[string]int{"b": 4, "c": 3},
   140  			"added: [\"c\"] = 3\nmodified: [\"b\"] = 4 (int) (Old: 2)\nremoved: [\"a\"] = 1\n",
   141  			// "added: [\"c\"] = 3\nmodified: [\"b\"] = 4\nremoved: [\"a\"] = 1\n",
   142  			false,
   143  			diff.WithSliceOrderedComparison(false),
   144  		},
   145  		{
   146  			// about two slices with different size
   147  
   148  			testStruct{1, 2, []int{1}, [3]int{4, 5, 6}},
   149  			testStruct{1, 3, []int{1, 2}, [3]int{4, 5, 6}},
   150  			// "modified: .b = 3 (int) (Old: 2)\n",
   151  			"added: .C.[1] = 2\nmodified: .b = 3 (int) (Old: 2)\n",
   152  			// "added: .C[1] = 2\nmodified: .b = 3\n",
   153  			false,
   154  			// diff.WithSliceOrderedComparison(false),
   155  			diff.WithCompareDifferentSizeArrays(false),
   156  		},
   157  		{
   158  			testStruct{1, 3, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, [3]int{4, 5, 6}},
   159  			testStruct{1, 3, []int{42, 43, 44, 3, 4, 5, 6, 7, 8, 9, 45, 46, 12}, [3]int{4, 5, 6}},
   160  			"modified: .C.[0] = 42 (int) (Old: <zero>)\nmodified: .C.[1] = 43 (int) (Old: 1)\nmodified: .C.[2] = 44 (int) (Old: 2)\nmodified: .C.[10] = 45 (int) (Old: 10)\nmodified: .C.[11] = 46 (int) (Old: 11)\n",
   161  			// "modified: .C[0] = 42\nmodified: .C[1] = 43\nmodified: .C[2] = 44\nmodified: .C[10] = 45\nmodified: .C[11] = 46\n",
   162  			false,
   163  			diff.WithSliceOrderedComparison(false),
   164  		},
   165  		{
   166  			nil,
   167  			nil,
   168  			"",
   169  			true,
   170  			nil,
   171  		},
   172  		{
   173  			&struct{}{},
   174  			nil,
   175  			"modified:  = nil (Old: &{} (*struct {}))\n",
   176  			// "modified:  = <nil>\n",
   177  			false,
   178  			nil,
   179  		},
   180  		{
   181  			nil,
   182  			&struct{}{},
   183  			"modified:  = &{} (*struct {}) (Old: nil)\n",
   184  			// "modified:  = &struct {}{}\n",
   185  			false,
   186  			nil,
   187  		},
   188  		{
   189  			time.Time{},
   190  			time.Time{},
   191  			"",
   192  			true,
   193  			nil,
   194  		},
   195  		{
   196  			testdata.MakeTest(10, "duck"),
   197  			testdata.MakeTest(20, "foo"),
   198  			"modified: .a = 20 (int) (Old: 10)\nmodified: .b = foo (string) (Old: duck)\n",
   199  			false,
   200  			nil,
   201  		},
   202  		{
   203  			time.Date(2018, 7, 24, 14, 6, 59, 0, &time.Location{}),
   204  			time.Date(2018, 7, 24, 14, 6, 59, 0, time.UTC),
   205  			"",
   206  			true,
   207  			nil,
   208  		},
   209  		{
   210  			time.Date(2017, 1, 1, 0, 0, 0, 0, &time.Location{}),
   211  			time.Date(2018, 7, 24, 14, 6, 59, 0, time.UTC),
   212  			"modified:  = 2018-07-24 14:06:59 +0000 UTC (time.Time) (Old: 2017-01-01 00:00:00 +0000 UTC)\n",
   213  			false,
   214  			nil,
   215  		},
   216  	}
   217  	checkTestCases(t, testData)
   218  }
   219  
   220  func TestPrettyDiffRecursive(t *testing.T) {
   221  	testData := []testCase{
   222  		{
   223  			newRecursiveStruct(1),
   224  			newRecursiveStruct(1),
   225  			"",
   226  			true,
   227  			nil,
   228  		},
   229  		{
   230  			newRecursiveStruct(1),
   231  			newRecursiveStruct(2),
   232  			"modified: .Child..Key = 2 (int) (Old: 1)\nmodified: .Key = 2 (int) (Old: 1)\n",
   233  			false,
   234  			nil,
   235  		},
   236  	}
   237  	checkTestCases(t, testData)
   238  }
   239  
   240  // func TestPathString(t *testing.T) {
   241  // 	testData := []struct {
   242  // 		in   Path
   243  // 		want string
   244  // 	}{{
   245  // 		Path{StructField("test"), SliceIndex(1), MapKey{"blue"}, MapKey{12.3}},
   246  // 		".test[1][\"blue\"][12.3]",
   247  // 	}}
   248  // 	for i, td := range testData {
   249  // 		if out := td.in.String(); out != td.want {
   250  // 			t.Errorf("%d. %#v.String() = %#v; not %#v", i, td.in, out, td.want)
   251  // 		}
   252  // 	}
   253  // }
   254  
   255  type ignoreStruct struct {
   256  	A int `diff:"ignore"`
   257  	a int
   258  	B [3]int `diff:"-"`
   259  	b [3]int
   260  }
   261  
   262  func TestIgnoreTag(t *testing.T) {
   263  	s1 := ignoreStruct{1, 1, [3]int{1, 2, 3}, [3]int{4, 5, 6}}
   264  	s2 := ignoreStruct{2, 1, [3]int{1, 8, 3}, [3]int{4, 5, 6}}
   265  
   266  	dif, equal := diff.New(s1, s2)
   267  	if !equal {
   268  		t.Errorf("Expected structs to be equal. Diff:\n%s", dif.PrettyPrint())
   269  	}
   270  
   271  	s2 = ignoreStruct{2, 2, [3]int{1, 8, 3}, [3]int{4, 9, 6}}
   272  	dif, equal = diff.New(s1, s2)
   273  	if equal {
   274  		t.Errorf("Expected structs NOT to be equal.")
   275  	}
   276  	expect := "modified: .a = 2 (int) (Old: 1)\nmodified: .b.[1] = 9 (int) (Old: 5)\n"
   277  	if dif.PrettyPrint() != expect {
   278  		t.Errorf("Expected diff to be:\n%v\nbut got:\n%v", expect, dif)
   279  	}
   280  }
   281  
   282  func TestIgnoreStructFieldOption(t *testing.T) {
   283  	a := struct {
   284  		X string
   285  		Y string
   286  	}{
   287  		X: "x",
   288  		Y: "y",
   289  	}
   290  	b := struct {
   291  		X string
   292  		Y string
   293  	}{
   294  		X: "xx",
   295  		Y: "y",
   296  	}
   297  
   298  	dif, equal := diff.New(a, b, diff.WithIgnoredFields("X"))
   299  	if !equal {
   300  		t.Errorf("Expected structs to be equal. Diff:\n%s", dif)
   301  	}
   302  
   303  	dif, equal = diff.New(a, b, diff.WithIgnoredFields("Y"))
   304  	if equal {
   305  		t.Errorf("Expected structs NOT to be equal.")
   306  	}
   307  	expect := "modified: .X = xx (string) (Old: x)\n"
   308  	if dif.PrettyPrint() != expect {
   309  		t.Errorf("Expected diff to be:\n%v\nbut got:\n%v", expect, dif)
   310  	}
   311  }