github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/store/diff/apply_patch_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 "context" 26 "testing" 27 28 "github.com/stretchr/testify/assert" 29 "github.com/stretchr/testify/require" 30 31 "github.com/dolthub/dolt/go/store/chunks" 32 "github.com/dolthub/dolt/go/store/d" 33 "github.com/dolthub/dolt/go/store/marshal" 34 "github.com/dolthub/dolt/go/store/types" 35 ) 36 37 func mustValue(v types.Value, err error) types.Value { 38 d.PanicIfError(err) 39 return v 40 } 41 42 func mustGetValue(v types.Value, found bool, err error) types.Value { 43 d.PanicIfError(err) 44 d.PanicIfFalse(found) 45 return v 46 } 47 48 func TestCommonPrefixCount(t *testing.T) { 49 assert := assert.New(t) 50 51 testCases := [][]interface{}{ 52 {".value[#94a2oa20oka0jdv5lha03vuvvumul1vb].sizes[#316j9oc39b09fbc2qf3klenm6p1o1d7h]", 0}, 53 {".value[#94a2oa20oka0jdv5lha03vuvvumul1vb].sizes[#77eavttned7llu1pkvhaei9a9qgcagir]", 3}, 54 {".value[#94a2oa20oka0jdv5lha03vuvvumul1vb].sizes[#hboaq9581drq4g9jf62d3s06al3us49s]", 3}, 55 {".value[#94a2oa20oka0jdv5lha03vuvvumul1vb].sizes[#l0hpa7sbr7qutrcfn5173kar4j2847m1]", 3}, 56 {".value[#9vj5m3049mav94bttcujhgfdfqcavsbn].sizes[#33f6tb4h8agh57s2bqlmi9vbhlkbtmct]", 1}, 57 {".value[#9vj5m3049mav94bttcujhgfdfqcavsbn].sizes[#a43ne9a8kotcqph4up5pqqdmr1e1qcsl]", 3}, 58 {".value[#9vj5m3049mav94bttcujhgfdfqcavsbn].sizes[#ppqg6pem2sb64h2i2ptnh8ckj8gogj9h]", 3}, 59 {".value[#9vj5m3049mav94bttcujhgfdfqcavsbn].sizes[#s7r2vpnqlk20sd72mg8ijerg9cmauaqo]", 3}, 60 {".value[#bpspmmlc41pk0r144a7682oah0tmge1e].sizes[#9vuc1gg3c3eude5v3j5deqopjsobe3no]", 1}, 61 {".value[#bpspmmlc41pk0r144a7682oah0tmge1e].sizes[#qo3gfdsf14v3dh0oer82vn1bg4o8nlsc]", 3}, 62 {".value[#bpspmmlc41pk0r144a7682oah0tmge1e].sizes[#rlidki5ipbjdofsm2rq3a66v908m5fpl]", 3}, 63 {".value[#bpspmmlc41pk0r144a7682oah0tmge1e].sizes[#st1n96rh89c2vgo090dt9lknd5ip4kck]", 3}, 64 {".value[#hjh5hpn55591k0gjvgckc14erli968ao].sizes[#267889uv3mtih6fij3fhio2jiqtl6nho]", 1}, 65 {".value[#hjh5hpn55591k0gjvgckc14erli968ao].sizes[#7ncb7guoip9e400bm2lcvr0dda29o9jn]", 3}, 66 {".value[#hjh5hpn55591k0gjvgckc14erli968ao].sizes[#afscb0on7rt8bq6eutup8juusmid7i96]", 3}, 67 {".value[#hjh5hpn55591k0gjvgckc14erli968ao].sizes[#drqe4lr0vdfdtmvejsjun1l3mfv6ums5]", 3}, 68 } 69 70 var lastPath types.Path 71 72 for i, tc := range testCases { 73 path, expected := tc[0].(string), tc[1].(int) 74 p, err := types.ParsePath(path) 75 require.NoError(t, err) 76 assert.Equal(expected, commonPrefixCount(lastPath, p), "failed for paths[%d]: %s", i, path) 77 lastPath = p 78 } 79 } 80 81 type testFunc func(parent types.Value) types.Value 82 type testKey struct { 83 X, Y int 84 } 85 86 var ( 87 vm map[string]types.Value 88 ) 89 90 func vfk(keys ...string) []types.Value { 91 var values []types.Value 92 for _, k := range keys { 93 values = append(values, vm[k]) 94 } 95 return values 96 } 97 98 func testValues(vrw types.ValueReadWriter) map[string]types.Value { 99 if vm == nil { 100 vm = map[string]types.Value{ 101 "k1": types.String("k1"), 102 "k2": types.String("k2"), 103 "k3": types.String("k3"), 104 "s1": types.String("string1"), 105 "s2": types.String("string2"), 106 "s3": types.String("string3"), 107 "s4": types.String("string4"), 108 "n1": types.Float(1), 109 "n2": types.Float(2), 110 "n3": types.Float(3.3), 111 "n4": types.Float(4.4), 112 "b1": mustMarshal(true), 113 "b2": mustMarshal(false), 114 "l1": mustMarshal([]string{}), 115 "l2": mustMarshal([]string{"one", "two", "three", "four"}), 116 "l3": mustMarshal([]string{"two", "three", "four", "five"}), 117 "l4": mustMarshal([]string{"two", "three", "four"}), 118 "l5": mustMarshal([]string{"one", "two", "three", "four", "five"}), 119 "l6": mustMarshal([]string{"one", "four"}), 120 "struct1": mustValue(types.NewStruct(vrw.Format(), "test1", types.StructData{"f1": types.Float(1), "f2": types.Float(2)})), 121 "struct2": mustValue(types.NewStruct(vrw.Format(), "test1", types.StructData{"f1": types.Float(11111), "f2": types.Float(2)})), 122 "struct3": mustValue(types.NewStruct(vrw.Format(), "test1", types.StructData{"f1": types.Float(1), "f2": types.Float(2), "f3": types.Float(3)})), 123 "struct4": mustValue(types.NewStruct(vrw.Format(), "test1", types.StructData{"f2": types.Float(2)})), 124 "m1": mustMarshal(map[string]int{}), 125 "m2": mustMarshal(map[string]int{"k1": 1, "k2": 2, "k3": 3}), 126 "m3": mustMarshal(map[string]int{"k2": 2, "k3": 3, "k4": 4}), 127 "m4": mustMarshal(map[string]int{"k1": 1, "k3": 3}), 128 "m5": mustMarshal(map[string]int{"k1": 1, "k2": 2222, "k3": 3}), 129 "ms1": mustMarshal(map[testKey]int{{1, 1}: 1, {2, 2}: 2, {3, 3}: 3}), 130 "ms2": mustMarshal(map[testKey]int{{1, 1}: 1, {4, 4}: 4, {5, 5}: 5}), 131 } 132 133 vm["mh1"] = mustValue(types.NewMap(context.Background(), vrw, vfk("k1", "struct1", "k2", "l1")...)) 134 vm["mh2"] = mustValue(types.NewMap(context.Background(), vrw, vfk("k1", "n1", "k2", "l2", "k3", "l3")...)) 135 vm["set1"] = mustValue(types.NewSet(context.Background(), vrw)) 136 vm["set2"] = mustValue(types.NewSet(context.Background(), vrw, vfk("s1", "s2")...)) 137 vm["set3"] = mustValue(types.NewSet(context.Background(), vrw, vfk("s1", "s2", "s3")...)) 138 vm["set1"] = mustValue(types.NewSet(context.Background(), vrw, vfk("s2")...)) 139 vm["seth1"] = mustValue(types.NewSet(context.Background(), vrw, vfk("struct1", "struct2", "struct3")...)) 140 vm["seth2"] = mustValue(types.NewSet(context.Background(), vrw, vfk("struct2", "struct3")...)) 141 vm["setj3"] = mustValue(types.NewSet(context.Background(), vrw, vfk("struct1")...)) 142 vm["mk1"] = mustValue(types.NewMap(context.Background(), vrw, vfk("struct1", "s1", "struct2", "s2")...)) 143 vm["mk2"] = mustValue(types.NewMap(context.Background(), vrw, vfk("struct1", "s3", "struct4", "s4")...)) 144 } 145 return vm 146 } 147 148 func newTestValueStore() *types.ValueStore { 149 st := &chunks.TestStorage{} 150 return types.NewValueStore(st.NewViewWithDefaultFormat()) 151 } 152 153 func getPatch(g1, g2 types.Value) (Patch, error) { 154 var derr error 155 dChan := make(chan Difference) 156 go func() { 157 defer close(dChan) 158 derr = Diff(context.Background(), g1, g2, dChan, true, nil) 159 }() 160 161 patch := Patch{} 162 for dif := range dChan { 163 patch = append(patch, dif) 164 } 165 166 return patch, derr 167 } 168 169 func checkApplyPatch(assert *assert.Assertions, vrw types.ValueReadWriter, g1, expectedG2 types.Value, k1, k2 string) { 170 patch, err := getPatch(g1, expectedG2) 171 assert.NoError(err) 172 g2, err := Apply(context.Background(), vrw, g1, patch) 173 assert.NoError(err) 174 assert.True(expectedG2.Equals(g2), "failed to apply diffs for k1: %s and k2: %s", k1, k2) 175 } 176 177 func TestPatches(t *testing.T) { 178 assert := assert.New(t) 179 180 vs := newTestValueStore() 181 defer vs.Close() 182 183 cnt := 0 184 for k1, g1 := range testValues(vs) { 185 for k2, expectedG2 := range testValues(vs) { 186 if k1 != k2 { 187 cnt++ 188 checkApplyPatch(assert, vs, g1, expectedG2, k1, k2) 189 } 190 } 191 } 192 } 193 194 func TestNestedLists(t *testing.T) { 195 assert := assert.New(t) 196 197 vs := newTestValueStore() 198 defer vs.Close() 199 200 ol1 := mustMarshal([]string{"one", "two", "three", "four"}) 201 nl1 := mustMarshal([]string{"two", "three"}) 202 ol2 := mustMarshal([]int{2, 3}) 203 nl2 := mustMarshal([]int{1, 2, 3, 4}) 204 nl3 := mustMarshal([]bool{true, false, true}) 205 g1 := mustValue(types.NewList(context.Background(), vs, ol1, ol2)) 206 g2 := mustValue(types.NewList(context.Background(), vs, nl1, nl2, nl3)) 207 checkApplyPatch(assert, vs, g1, g2, "g1", "g2") 208 } 209 210 func TestUpdateNode(t *testing.T) { 211 assert := assert.New(t) 212 213 vs := newTestValueStore() 214 defer vs.Close() 215 216 doTest := func(pp types.PathPart, parent, ov, nv, exp types.Value, f testFunc) { 217 stack := &patchStack{} 218 se := &stackElem{path: []types.PathPart{pp}, pathPart: pp, changeType: types.DiffChangeModified, oldValue: ov, newValue: nv} 219 updated, err := stack.updateNode(context.Background(), se, parent) 220 require.NoError(t, err) 221 testVal := f(updated) 222 assert.True(exp.Equals(testVal), "%s != %s", nv, testVal) 223 } 224 225 var pp types.PathPart 226 oldVal := types.String("Yo") 227 newVal := types.String("YooHoo") 228 229 s1, err := types.NewStruct(vs.Format(), "TestStruct", types.StructData{"f1": types.Float(1), "f2": oldVal}) 230 require.NoError(t, err) 231 pp = types.FieldPath{Name: "f2"} 232 doTest(pp, s1, oldVal, newVal, newVal, func(parent types.Value) types.Value { 233 return mustGetValue(parent.(types.Struct).MaybeGet("f2")) 234 }) 235 236 l1, err := types.NewList(context.Background(), vs, types.String("one"), oldVal, types.String("three")) 237 require.NoError(t, err) 238 pp = types.IndexPath{Index: types.Float(1)} 239 doTest(pp, l1, oldVal, newVal, newVal, func(parent types.Value) types.Value { 240 return mustValue(parent.(types.List).Get(context.Background(), 1)) 241 }) 242 243 m1, err := types.NewMap(context.Background(), vs, types.String("k1"), types.Float(1), types.String("k2"), oldVal) 244 require.NoError(t, err) 245 pp = types.IndexPath{Index: types.String("k2")} 246 doTest(pp, m1, oldVal, newVal, newVal, func(parent types.Value) types.Value { 247 return mustGetValue(parent.(types.Map).MaybeGet(context.Background(), types.String("k2"))) 248 }) 249 250 k1, err := types.NewStruct(vs.Format(), "Sizes", types.StructData{"height": types.Float(200), "width": types.Float(300)}) 251 require.NoError(t, err) 252 _, err = vs.WriteValue(context.Background(), k1) 253 require.NoError(t, err) 254 m1, err = types.NewMap(context.Background(), vs, k1, oldVal) 255 require.NoError(t, err) 256 h, err := k1.Hash(vs.Format()) 257 require.NoError(t, err) 258 pp = types.HashIndexPath{Hash: h} 259 doTest(pp, m1, oldVal, newVal, newVal, func(parent types.Value) types.Value { 260 return mustGetValue(parent.(types.Map).MaybeGet(context.Background(), k1)) 261 }) 262 263 set1, err := types.NewSet(context.Background(), vs, oldVal, k1) 264 require.NoError(t, err) 265 pp = types.IndexPath{Index: oldVal} 266 exp, err := types.NewSet(context.Background(), vs, newVal, k1) 267 require.NoError(t, err) 268 doTest(pp, set1, oldVal, newVal, exp, func(parent types.Value) types.Value { 269 return parent 270 }) 271 272 k2, err := types.NewStruct(vs.Format(), "Sizes", types.StructData{"height": types.Float(300), "width": types.Float(500)}) 273 require.NoError(t, err) 274 set1, err = types.NewSet(context.Background(), vs, oldVal, k1) 275 require.NoError(t, err) 276 h, err = k1.Hash(vs.Format()) 277 require.NoError(t, err) 278 pp = types.HashIndexPath{Hash: h} 279 exp, err = types.NewSet(context.Background(), vs, oldVal, k2) 280 require.NoError(t, err) 281 doTest(pp, set1, k1, k2, exp, func(parent types.Value) types.Value { 282 return parent 283 }) 284 } 285 286 func checkApplyDiffs(a *assert.Assertions, vrw types.ValueReadWriter, n1, n2 types.Value, leftRight bool) { 287 var derr error 288 dChan := make(chan Difference) 289 go func() { 290 defer close(dChan) 291 derr = Diff(context.Background(), n1, n2, dChan, leftRight, nil) 292 }() 293 294 difs := Patch{} 295 for dif := range dChan { 296 difs = append(difs, dif) 297 } 298 299 a.NoError(derr) 300 301 res, err := Apply(context.Background(), vrw, n1, difs) 302 a.NoError(err) 303 a.True(n2.Equals(res)) 304 } 305 306 func tryApplyDiff(a *assert.Assertions, vrw types.ValueReadWriter, a1, a2 interface{}) { 307 n1 := mustMarshal(a1) 308 n2 := mustMarshal(a2) 309 310 checkApplyDiffs(a, vrw, n1, n2, true) 311 checkApplyDiffs(a, vrw, n1, n2, false) 312 checkApplyDiffs(a, vrw, n2, n1, true) 313 checkApplyDiffs(a, vrw, n2, n1, false) 314 } 315 316 func TestUpdateList(t *testing.T) { 317 a := assert.New(t) 318 vs := newTestValueStore() 319 defer vs.Close() 320 321 // insert at beginning 322 a1 := []interface{}{"five", "ten", "fifteen"} 323 a2 := []interface{}{"one", "two", "three", "five", "ten", "fifteen"} 324 tryApplyDiff(a, vs, a1, a2) 325 326 // append at end 327 a1 = []interface{}{"five", "ten", "fifteen"} 328 a2 = []interface{}{"five", "ten", "fifteen", "twenty", "twenty-five"} 329 tryApplyDiff(a, vs, a1, a2) 330 331 // insert interleaved 332 a1 = []interface{}{"one", "three", "five", "seven"} 333 a2 = []interface{}{"one", "two", "three", "four", "five", "six", "seven"} 334 tryApplyDiff(a, vs, a1, a2) 335 336 // delete from beginning and append to end 337 a1 = []interface{}{"one", "two", "three", "four", "five"} 338 a2 = []interface{}{"four", "five", "six", "seven"} 339 tryApplyDiff(a, vs, a1, a2) 340 341 // replace entries at beginning 342 a1 = []interface{}{"one", "two", "three", "four", "five"} 343 a2 = []interface{}{"3.5", "four", "five"} 344 tryApplyDiff(a, vs, a1, a2) 345 346 // replace entries at end 347 a1 = []interface{}{"one", "two", "three"} 348 a2 = []interface{}{"one", "four"} 349 tryApplyDiff(a, vs, a1, a2) 350 351 // insert at beginning, replace at end 352 a1 = []interface{}{"five", "ten", "fifteen"} 353 a2 = []interface{}{"one", "two", "five", "eight", "eleven", "sixteen", "twenty"} 354 tryApplyDiff(a, vs, a1, a2) 355 356 // remove everything 357 a1 = []interface{}{"five", "ten", "fifteen"} 358 a2 = []interface{}{} 359 tryApplyDiff(a, vs, a1, a2) 360 } 361 362 func TestUpdateMap(t *testing.T) { 363 a := assert.New(t) 364 vs := newTestValueStore() 365 defer vs.Close() 366 367 // insertions, deletions, and replacements 368 a1 := map[string]int{"five": 5, "ten": 10, "fifteen": 15, "twenty": 20} 369 a2 := map[string]int{"one": 1, "two": 2, "three": 3, "five": 5, "ten": 10, "fifteen": 15, "twenty": 2020} 370 tryApplyDiff(a, vs, a1, a2) 371 372 // delete everything 373 a1 = map[string]int{"five": 5, "ten": 10, "fifteen": 15, "twenty": 20} 374 a2 = map[string]int{} 375 tryApplyDiff(a, vs, a1, a2) 376 } 377 378 func TestUpdateStruct(t *testing.T) { 379 a := assert.New(t) 380 vs := newTestValueStore() 381 defer vs.Close() 382 383 a1 := mustValue(types.NewStruct(vs.Format(), "tStruct", types.StructData{ 384 "f1": types.Float(1), 385 "f2": types.String("two"), 386 "f3": mustMarshal([]string{"one", "two", "three"}), 387 })) 388 a2 := mustValue(types.NewStruct(vs.Format(), "tStruct", types.StructData{ 389 "f1": types.Float(2), 390 "f2": types.String("twotwo"), 391 "f3": mustMarshal([]interface{}{0, "one", 1, "two", 2, "three", 3}), 392 })) 393 checkApplyDiffs(a, vs, a1, a2, true) 394 checkApplyDiffs(a, vs, a1, a2, false) 395 396 a2 = mustValue(types.NewStruct(vs.Format(), "tStruct", types.StructData{ 397 "f1": types.Float(2), 398 "f2": types.String("two"), 399 "f3": mustMarshal([]interface{}{0, "one", 1, "two", 2, "three", 3}), 400 "f4": types.Bool(true), 401 })) 402 checkApplyDiffs(a, vs, a1, a2, true) 403 checkApplyDiffs(a, vs, a1, a2, false) 404 } 405 406 func TestUpdateSet(t *testing.T) { 407 a := assert.New(t) 408 409 vs := newTestValueStore() 410 defer vs.Close() 411 412 a1 := mustValue(types.NewSet(context.Background(), vs, types.Float(1), types.String("two"), mustMarshal([]string{"one", "two", "three"}))) 413 a2 := mustValue(types.NewSet(context.Background(), vs, types.Float(3), types.String("three"), mustMarshal([]string{"one", "two", "three", "four"}))) 414 415 checkApplyDiffs(a, vs, a1, a2, true) 416 checkApplyDiffs(a, vs, a1, a2, false) 417 checkApplyDiffs(a, vs, a2, a1, true) 418 checkApplyDiffs(a, vs, a2, a1, false) 419 } 420 421 func mustMarshal(v interface{}) types.Value { 422 vs := newTestValueStore() 423 defer vs.Close() 424 425 v1, err := marshal.Marshal(context.Background(), vs, v) 426 d.Chk.NoError(err) 427 return v1 428 }