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 }