github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/store/diff/diff_test.go (about) 1 // Copyright 2019 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // This file incorporates work covered by the following copyright and 16 // permission notice: 17 // 18 // Copyright 2016 Attic Labs, Inc. All rights reserved. 19 // Licensed under the Apache License, version 2.0: 20 // http://www.apache.org/licenses/LICENSE-2.0 21 22 package diff 23 24 import ( 25 "bytes" 26 "context" 27 "fmt" 28 "strings" 29 "testing" 30 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/require" 33 34 "github.com/dolthub/dolt/go/store/d" 35 "github.com/dolthub/dolt/go/store/types" 36 "github.com/dolthub/dolt/go/store/util/test" 37 "github.com/dolthub/dolt/go/store/util/writers" 38 ) 39 40 var ( 41 aa1 types.Map 42 aa1x types.Map 43 44 mm1 types.Map 45 mm2 types.Map 46 mm3 types.Map 47 mm3x types.Map 48 mm4 types.Map 49 ) 50 51 func initmaps(vs types.ValueReadWriter) { 52 aa1 = createMap(vs, "a1", "a-one", "a2", "a-two", "a3", "a-three", "a4", "a-four") 53 aa1x = createMap(vs, "a1", "a-one-diff", "a2", "a-two", "a3", "a-three", "a4", "a-four") 54 55 mm1 = createMap(vs, "k1", "k-one", "k2", "k-two", "k3", "k-three", "k4", aa1) 56 mm2 = createMap(vs, "l1", "l-one", "l2", "l-two", "l3", "l-three", "l4", aa1) 57 mm3 = createMap(vs, "m1", "m-one", "v2", "m-two", "m3", "m-three", "m4", aa1) 58 mm3x = createMap(vs, "m1", "m-one", "v2", "m-two", "m3", "m-three-diff", "m4", aa1x) 59 mm4 = createMap(vs, "n1", "n-one", "n2", "n-two", "n3", "n-three", "n4", aa1) 60 } 61 62 func valToTypesValue(v interface{}) types.Value { 63 var v1 types.Value 64 switch t := v.(type) { 65 case string: 66 v1 = types.String(t) 67 case int: 68 v1 = types.Float(t) 69 case types.Value: 70 v1 = t 71 } 72 return v1 73 } 74 75 func valsToTypesValues(kv ...interface{}) []types.Value { 76 keyValues := []types.Value{} 77 for _, e := range kv { 78 v := valToTypesValue(e) 79 keyValues = append(keyValues, v) 80 } 81 return keyValues 82 } 83 84 func createMap(vs types.ValueReadWriter, kv ...interface{}) types.Map { 85 keyValues := valsToTypesValues(kv...) 86 m, err := types.NewMap(context.Background(), vs, keyValues...) 87 d.PanicIfError(err) 88 return m 89 } 90 91 func createSet(vs types.ValueReadWriter, kv ...interface{}) types.Set { 92 keyValues := valsToTypesValues(kv...) 93 s, err := types.NewSet(context.Background(), vs, keyValues...) 94 d.PanicIfError(err) 95 return s 96 } 97 98 func createList(vs types.ValueReadWriter, kv ...interface{}) types.List { 99 keyValues := valsToTypesValues(kv...) 100 l, err := types.NewList(context.Background(), vs, keyValues...) 101 d.PanicIfError(err) 102 return l 103 } 104 105 func createStruct(vs types.ValueReadWriter, name string, kv ...interface{}) types.Struct { 106 fields := types.StructData{} 107 for i := 0; i < len(kv); i += 2 { 108 fields[kv[i].(string)] = valToTypesValue(kv[i+1]) 109 } 110 st, err := types.NewStruct(vs.Format(), name, fields) 111 d.PanicIfError(err) 112 return st 113 } 114 115 func pathsFromDiff(v1, v2 types.Value, leftRight bool) ([]string, error) { 116 var derr error 117 dChan := make(chan Difference) 118 go func() { 119 defer close(dChan) 120 derr = Diff(context.Background(), v1, v2, dChan, leftRight, nil) 121 }() 122 123 var paths []string 124 for d := range dChan { 125 paths = append(paths, d.Path.String()) 126 } 127 128 return paths, derr 129 } 130 131 func mustParsePath(assert *assert.Assertions, s string) types.Path { 132 if s == "" { 133 return nil 134 } 135 p, err := types.ParsePath(s) 136 assert.NoError(err) 137 return p 138 } 139 140 func TestNomsDiffPrintMap(t *testing.T) { 141 assert := assert.New(t) 142 vs := newTestValueStore() 143 defer vs.Close() 144 initmaps(vs) 145 146 expected := `["map-3"] { 147 - "m3": "m-three" 148 + "m3": "m-three-diff" 149 } 150 ["map-3"]["m4"] { 151 - "a1": "a-one" 152 + "a1": "a-one-diff" 153 } 154 ` 155 expectedPaths := []string{ 156 `["map-3"]["m3"]`, 157 `["map-3"]["m4"]["a1"]`, 158 } 159 160 tf := func(leftRight bool) { 161 m1 := createMap(vs, "map-1", mm1, "map-2", mm2, "map-3", mm3, "map-4", mm4) 162 m2 := createMap(vs, "map-1", mm1, "map-2", mm2, "map-3", mm3x, "map-4", mm4) 163 buf := &bytes.Buffer{} 164 PrintDiff(context.Background(), buf, m1, m2, leftRight) 165 assert.Equal(expected, buf.String()) 166 167 paths, err := pathsFromDiff(m1, m2, leftRight) 168 require.NoError(t, err) 169 assert.Equal(expectedPaths, paths) 170 } 171 172 tf(true) 173 tf(false) 174 } 175 176 func TestNomsDiffPrintSet(t *testing.T) { 177 assert := assert.New(t) 178 vs := newTestValueStore() 179 defer vs.Close() 180 181 expected1 := `(root) { 182 - "five" 183 + "five-diff" 184 } 185 ` 186 expectedPaths1 := []string{ 187 `["five"]`, 188 `["five-diff"]`, 189 } 190 191 expected2 := `(root) { 192 - map { // 4 items 193 - "m1": "m-one", 194 - "m3": "m-three", 195 - "m4": map { // 4 items 196 - "a1": "a-one", 197 - "a2": "a-two", 198 - "a3": "a-three", 199 - "a4": "a-four", 200 - }, 201 - "v2": "m-two", 202 - } 203 + map { // 4 items 204 + "m1": "m-one", 205 + "m3": "m-three-diff", 206 + "m4": map { // 4 items 207 + "a1": "a-one-diff", 208 + "a2": "a-two", 209 + "a3": "a-three", 210 + "a4": "a-four", 211 + }, 212 + "v2": "m-two", 213 + } 214 } 215 ` 216 h3, err := mm3.Hash(vs.Format()) 217 require.NoError(t, err) 218 h3x, err := mm3x.Hash(vs.Format()) 219 require.NoError(t, err) 220 expectedPaths2 := []string{ 221 fmt.Sprintf("[#%s]", h3), 222 fmt.Sprintf("[#%s]", h3x), 223 } 224 225 s1 := createSet(vs, "one", "three", "five", "seven", "nine") 226 s2 := createSet(vs, "one", "three", "five-diff", "seven", "nine") 227 s3 := createSet(vs, mm1, mm2, mm3, mm4) 228 s4 := createSet(vs, mm1, mm2, mm3x, mm4) 229 230 tf := func(leftRight bool) { 231 buf := &bytes.Buffer{} 232 PrintDiff(context.Background(), buf, s1, s2, leftRight) 233 assert.Equal(expected1, buf.String()) 234 235 paths, err := pathsFromDiff(s1, s2, leftRight) 236 require.NoError(t, err) 237 assert.Equal(expectedPaths1, paths) 238 239 buf = &bytes.Buffer{} 240 err = PrintDiff(context.Background(), buf, s3, s4, leftRight) 241 require.NoError(t, err) 242 assert.Equal(expected2, buf.String()) 243 244 paths, err = pathsFromDiff(s3, s4, leftRight) 245 require.NoError(t, err) 246 assert.Equal(expectedPaths2, paths) 247 } 248 249 tf(true) 250 tf(false) 251 } 252 253 // This function tests stop functionality in PrintDiff and Diff. 254 func TestNomsDiffPrintStop(t *testing.T) { 255 assert := assert.New(t) 256 vs := newTestValueStore() 257 defer vs.Close() 258 initmaps(vs) 259 260 expected1 := `(root) { 261 - "five" 262 ` 263 264 expected2 := `(root) { 265 - map { // 4 items 266 ` 267 268 s1 := createSet(vs, "one", "three", "five", "seven", "nine") 269 s2 := createSet(vs, "one", "three", "five-diff", "seven", "nine") 270 s3 := createSet(vs, mm1, mm2, mm3, mm4) 271 s4 := createSet(vs, mm1, mm2, mm3x, mm4) 272 273 tf := func(leftRight bool) { 274 buf := &bytes.Buffer{} 275 mlw := &writers.MaxLineWriter{Dest: buf, MaxLines: 2} 276 PrintDiff(context.Background(), mlw, s1, s2, leftRight) 277 assert.Equal(expected1, buf.String()) 278 279 buf = &bytes.Buffer{} 280 mlw = &writers.MaxLineWriter{Dest: buf, MaxLines: 2} 281 PrintDiff(context.Background(), mlw, s3, s4, leftRight) 282 assert.Equal(expected2, buf.String()) 283 } 284 285 tf(true) 286 tf(false) 287 } 288 289 func TestNomsDiffPrintStruct(t *testing.T) { 290 assert := assert.New(t) 291 vs := newTestValueStore() 292 defer vs.Close() 293 initmaps(vs) 294 295 expected1 := `(root) { 296 - "four": "four" 297 + "four": "four-diff" 298 } 299 ["three"] { 300 - field1: "field1-data" 301 - field3: "field3-data" 302 + field3: "field3-data-diff" 303 + field4: "field4-data" 304 } 305 ` 306 expectedPaths1 := []string{ 307 `["four"]`, 308 `["three"].field1`, 309 `["three"].field3`, 310 `["three"].field4`, 311 } 312 313 expected2 := `(root) { 314 - four: "four" 315 + four: "four-diff" 316 } 317 .three { 318 - field1: "field1-data" 319 - field3: "field3-data" 320 + field3: "field3-data-diff" 321 + field4: "field4-data" 322 } 323 ` 324 expectedPaths2 := []string{ 325 `.four`, 326 `.three.field1`, 327 `.three.field3`, 328 `.three.field4`, 329 } 330 331 s1 := createStruct(vs, "TestData", 332 "field1", "field1-data", 333 "field2", "field2-data", 334 "field3", "field3-data", 335 ) 336 s2 := createStruct(vs, "TestData", 337 "field2", "field2-data", 338 "field3", "field3-data-diff", 339 "field4", "field4-data", 340 ) 341 342 m1 := createMap(vs, "one", 1, "two", 2, "three", s1, "four", "four") 343 m2 := createMap(vs, "one", 1, "two", 2, "three", s2, "four", "four-diff") 344 345 s3 := createStruct(vs, "", "one", 1, "two", 2, "three", s1, "four", "four") 346 s4 := createStruct(vs, "", "one", 1, "two", 2, "three", s2, "four", "four-diff") 347 348 tf := func(leftRight bool) { 349 buf := &bytes.Buffer{} 350 PrintDiff(context.Background(), buf, m1, m2, leftRight) 351 assert.Equal(expected1, buf.String()) 352 353 paths, err := pathsFromDiff(m1, m2, leftRight) 354 require.NoError(t, err) 355 assert.Equal(expectedPaths1, paths) 356 357 buf = &bytes.Buffer{} 358 err = PrintDiff(context.Background(), buf, s3, s4, leftRight) 359 require.NoError(t, err) 360 assert.Equal(expected2, buf.String()) 361 362 paths, err = pathsFromDiff(s3, s4, leftRight) 363 require.NoError(t, err) 364 assert.Equal(expectedPaths2, paths) 365 } 366 367 tf(true) 368 tf(false) 369 } 370 371 func TestNomsDiffPrintMapWithStructKeys(t *testing.T) { 372 a := assert.New(t) 373 vs := newTestValueStore() 374 defer vs.Close() 375 initmaps(vs) 376 377 k1 := createStruct(vs, "TestKey", "name", "n1", "label", "l1") 378 379 expected1 := `(root) { 380 - struct TestKey { 381 - label: "l1", 382 - name: "n1", 383 - }: true 384 + struct TestKey { 385 + label: "l1", 386 + name: "n1", 387 + }: false 388 } 389 ` 390 391 m1, err := types.NewMap(context.Background(), vs, k1, types.Bool(true)) 392 require.NoError(t, err) 393 m2, err := types.NewMap(context.Background(), vs, k1, types.Bool(false)) 394 require.NoError(t, err) 395 tf := func(leftRight bool) { 396 buf := &bytes.Buffer{} 397 PrintDiff(context.Background(), buf, m1, m2, leftRight) 398 a.Equal(expected1, buf.String()) 399 } 400 tf(true) 401 tf(false) 402 } 403 404 func TestNomsDiffPrintList(t *testing.T) { 405 assert := assert.New(t) 406 vs := newTestValueStore() 407 defer vs.Close() 408 initmaps(vs) 409 410 expected1 := `(root) { 411 - 2 412 + 22 413 - 44 414 } 415 ` 416 expectedPaths1 := []string{ 417 `[1]`, 418 `[4]`, 419 } 420 421 l1 := createList(vs, 1, 2, 3, 4, 44, 5, 6) 422 l2 := createList(vs, 1, 22, 3, 4, 5, 6) 423 424 expected2 := `(root) { 425 + "seven" 426 } 427 ` 428 expectedPaths2 := []string{ 429 `[6]`, 430 } 431 432 l3 := createList(vs, "one", "two", "three", "four", "five", "six") 433 l4 := createList(vs, "one", "two", "three", "four", "five", "six", "seven") 434 435 expected3 := `[2] { 436 - "m3": "m-three" 437 + "m3": "m-three-diff" 438 } 439 [2]["m4"] { 440 - "a1": "a-one" 441 + "a1": "a-one-diff" 442 } 443 ` 444 expectedPaths3 := []string{ 445 `[2]["m3"]`, 446 `[2]["m4"]["a1"]`, 447 } 448 449 l5 := createList(vs, mm1, mm2, mm3, mm4) 450 l6 := createList(vs, mm1, mm2, mm3x, mm4) 451 452 tf := func(leftRight bool) { 453 buf := &bytes.Buffer{} 454 err := PrintDiff(context.Background(), buf, l1, l2, leftRight) 455 require.NoError(t, err) 456 assert.Equal(expected1, buf.String()) 457 458 paths, err := pathsFromDiff(l1, l2, leftRight) 459 require.NoError(t, err) 460 assert.Equal(expectedPaths1, paths) 461 462 buf = &bytes.Buffer{} 463 err = PrintDiff(context.Background(), buf, l3, l4, leftRight) 464 require.NoError(t, err) 465 assert.Equal(expected2, buf.String()) 466 467 paths, err = pathsFromDiff(l3, l4, leftRight) 468 require.NoError(t, err) 469 assert.Equal(expectedPaths2, paths) 470 471 buf = &bytes.Buffer{} 472 err = PrintDiff(context.Background(), buf, l5, l6, leftRight) 473 require.NoError(t, err) 474 assert.Equal(expected3, buf.String()) 475 476 paths, err = pathsFromDiff(l5, l6, leftRight) 477 require.NoError(t, err) 478 assert.Equal(expectedPaths3, paths) 479 } 480 481 tf(true) 482 tf(false) 483 } 484 485 func TestNomsDiffPrintBlob(t *testing.T) { 486 assert := assert.New(t) 487 vs := newTestValueStore() 488 defer vs.Close() 489 initmaps(vs) 490 491 expected := "- Blob (2.0 kB)\n+ Blob (11 B)\n" 492 expectedPaths1 := []string{``} 493 b1, err := types.NewBlob(context.Background(), vs, strings.NewReader(strings.Repeat("x", 2*1024))) 494 require.NoError(t, err) 495 b2, err := types.NewBlob(context.Background(), vs, strings.NewReader("Hello World")) 496 require.NoError(t, err) 497 498 tf := func(leftRight bool) { 499 buf := &bytes.Buffer{} 500 err = PrintDiff(context.Background(), buf, b1, b2, leftRight) 501 require.NoError(t, err) 502 assert.Equal(expected, buf.String()) 503 504 paths, err := pathsFromDiff(b1, b2, leftRight) 505 require.NoError(t, err) 506 assert.Equal(expectedPaths1, paths) 507 } 508 509 tf(true) 510 tf(false) 511 } 512 513 func TestNomsDiffPrintType(t *testing.T) { 514 assert := assert.New(t) 515 vs := newTestValueStore() 516 defer vs.Close() 517 initmaps(vs) 518 519 expected1 := "- List<Float>\n+ List<String>\n" 520 expectedPaths1 := []string{""} 521 t1, err := types.MakeListType(types.PrimitiveTypeMap[types.FloatKind]) 522 require.NoError(t, err) 523 t2, err := types.MakeListType(types.PrimitiveTypeMap[types.StringKind]) 524 require.NoError(t, err) 525 526 expected2 := "- List<Float>\n+ Set<String>\n" 527 expectedPaths2 := []string{``} 528 t3, err := types.MakeListType(types.PrimitiveTypeMap[types.FloatKind]) 529 require.NoError(t, err) 530 t4, err := types.MakeSetType(types.PrimitiveTypeMap[types.StringKind]) 531 require.NoError(t, err) 532 533 tf := func(leftRight bool) { 534 buf := &bytes.Buffer{} 535 err = PrintDiff(context.Background(), buf, t1, t2, leftRight) 536 require.NoError(t, err) 537 assert.Equal(expected1, buf.String()) 538 539 paths, err := pathsFromDiff(t1, t2, leftRight) 540 require.NoError(t, err) 541 assert.Equal(expectedPaths1, paths) 542 543 buf = &bytes.Buffer{} 544 err = PrintDiff(context.Background(), buf, t3, t4, leftRight) 545 require.NoError(t, err) 546 assert.Equal(expected2, buf.String()) 547 548 paths, err = pathsFromDiff(t3, t4, leftRight) 549 require.NoError(t, err) 550 assert.Equal(expectedPaths2, paths) 551 } 552 553 tf(true) 554 tf(false) 555 } 556 557 func TestNomsDiffPrintRef(t *testing.T) { 558 assert := assert.New(t) 559 vs := newTestValueStore() 560 defer vs.Close() 561 initmaps(vs) 562 563 expected := "- #fckcbt7nk5jl4arco2dk7r9nj7abb6ci\n+ #i7d3u5gekm48ot419t2cot6cnl7ltcah\n" 564 expectedPaths1 := []string{``} 565 l1 := createList(vs, 1) 566 l2 := createList(vs, 2) 567 r1, err := types.NewRef(l1, vs.Format()) 568 require.NoError(t, err) 569 r2, err := types.NewRef(l2, vs.Format()) 570 require.NoError(t, err) 571 572 tf := func(leftRight bool) { 573 buf := &bytes.Buffer{} 574 err := PrintDiff(context.Background(), buf, r1, r2, leftRight) 575 require.NoError(t, err) 576 test.EqualsIgnoreHashes(t, expected, buf.String()) 577 578 paths, err := pathsFromDiff(r1, r2, leftRight) 579 require.NoError(t, err) 580 assert.Equal(expectedPaths1, paths) 581 } 582 583 tf(true) 584 tf(false) 585 }