github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/store/merge/three_way_keyval_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 merge 23 24 import ( 25 "context" 26 "testing" 27 28 "github.com/stretchr/testify/suite" 29 30 "github.com/dolthub/dolt/go/store/types" 31 ) 32 33 func TestThreeWayMapMerge(t *testing.T) { 34 suite.Run(t, &ThreeWayMapMergeSuite{}) 35 } 36 37 func TestThreeWayStructMerge(t *testing.T) { 38 suite.Run(t, &ThreeWayStructMergeSuite{}) 39 } 40 41 type kvs []interface{} 42 43 func (kv kvs) items() []interface{} { 44 return kv 45 } 46 47 func (kv kvs) remove(k interface{}) kvs { 48 out := make(kvs, 0, len(kv)) 49 for i := 0; i < len(kv); i += 2 { 50 if kv[i] != k { 51 out = append(out, kv[i], kv[i+1]) 52 } 53 } 54 return out 55 } 56 57 func (kv kvs) set(k, v interface{}) kvs { 58 out := make(kvs, len(kv)) 59 for i := 0; i < len(kv); i += 2 { 60 out[i], out[i+1] = kv[i], kv[i+1] 61 if kv[i] == k { 62 out[i+1] = v 63 } 64 } 65 return out 66 } 67 68 var ( 69 aa1 = kvs{"a1", "a-one", "a2", "a-two", "a3", "a-three", "a4", "a-four"} 70 aa1a = kvs{"a1", "a-one", "a2", "a-two", "a3", "a-three-diff", "a4", "a-four", "a6", "a-six"} 71 aa1b = kvs{"a1", "a-one", "a3", "a-three-diff", "a4", "a-four", "a5", "a-five"} 72 aaMerged = kvs{"a1", "a-one", "a3", "a-three-diff", "a4", "a-four", "a5", "a-five", "a6", "a-six"} 73 74 mm1 = kvs{} 75 mm1a = kvs{"k1", kvs{"a", 0}} 76 mm1b = kvs{"k1", kvs{"b", 1}} 77 mm1Merged = kvs{"k1", kvs{"a", 0, "b", 1}} 78 79 mm2 = kvs{"k2", aa1, "k3", "k-three"} 80 mm2a = kvs{"k1", kvs{"a", 0}, "k2", aa1a, "k3", "k-three", "k4", "k-four"} 81 mm2b = kvs{"k1", kvs{"b", 1}, "k2", aa1b} 82 mm2Merged = kvs{"k1", kvs{"a", 0, "b", 1}, "k2", aaMerged, "k4", "k-four"} 83 ) 84 85 type ThreeWayKeyValMergeSuite struct { 86 ThreeWayMergeSuite 87 } 88 89 type ThreeWayMapMergeSuite struct { 90 ThreeWayKeyValMergeSuite 91 } 92 93 func (s *ThreeWayMapMergeSuite) SetupSuite() { 94 s.create = func(seq seq) (val types.Value, err error) { 95 if seq != nil { 96 keyValues, err := valsToTypesValues(s.create, seq.items()...) 97 s.NoError(err) 98 val, err = types.NewMap(context.Background(), s.vs, keyValues...) 99 s.NoError(err) 100 } 101 return 102 } 103 s.typeStr = "Map" 104 } 105 106 type ThreeWayStructMergeSuite struct { 107 ThreeWayKeyValMergeSuite 108 } 109 110 func (s *ThreeWayStructMergeSuite) SetupSuite() { 111 s.create = func(seq seq) (val types.Value, err error) { 112 if seq != nil { 113 kv := seq.items() 114 fields := types.StructData{} 115 for i := 0; i < len(kv); i += 2 { 116 var err error 117 fields[kv[i].(string)], err = valToTypesValue(s.create, kv[i+1]) 118 s.NoError(err) 119 } 120 val, err = types.NewStruct(types.Format_Default, "TestStruct", fields) 121 } 122 return val, err 123 } 124 s.typeStr = "Struct" 125 } 126 127 func (s *ThreeWayKeyValMergeSuite) TestThreeWayMerge_DoNothing() { 128 s.tryThreeWayMerge(nil, nil, aa1, aa1) 129 } 130 131 func (s *ThreeWayKeyValMergeSuite) TestThreeWayMerge_NoRecursion() { 132 s.tryThreeWayMerge(aa1a, aa1b, aa1, aaMerged) 133 s.tryThreeWayMerge(aa1b, aa1a, aa1, aaMerged) 134 } 135 136 func (s *ThreeWayKeyValMergeSuite) TestThreeWayMerge_RecursiveCreate() { 137 s.tryThreeWayMerge(mm1a, mm1b, mm1, mm1Merged) 138 s.tryThreeWayMerge(mm1b, mm1a, mm1, mm1Merged) 139 } 140 141 func (s *ThreeWayKeyValMergeSuite) TestThreeWayMerge_RecursiveCreateNil() { 142 s.tryThreeWayMerge(mm1a, mm1b, nil, mm1Merged) 143 s.tryThreeWayMerge(mm1b, mm1a, nil, mm1Merged) 144 } 145 146 func (s *ThreeWayKeyValMergeSuite) TestThreeWayMerge_RecursiveMerge() { 147 s.tryThreeWayMerge(mm2a, mm2b, mm2, mm2Merged) 148 s.tryThreeWayMerge(mm2b, mm2a, mm2, mm2Merged) 149 } 150 151 func (s *ThreeWayKeyValMergeSuite) TestThreeWayMerge_RefMerge() { 152 st, err := types.NewStruct(types.Format_Default, "Foo", types.StructData{"life": types.Float(42)}) 153 s.NoError(err) 154 strRef, err := s.vs.WriteValue(context.Background(), st) 155 s.NoError(err) 156 157 m := kvs{"r2", mustValue(s.vs.WriteValue(context.Background(), mustValue(s.create(aa1))))} 158 ma := kvs{"r1", strRef, "r2", mustValue(s.vs.WriteValue(context.Background(), mustValue(s.create(aa1a))))} 159 mb := kvs{"r1", strRef, "r2", mustValue(s.vs.WriteValue(context.Background(), mustValue(s.create(aa1b))))} 160 mMerged := kvs{"r1", strRef, "r2", mustValue(s.vs.WriteValue(context.Background(), mustValue(s.create(aaMerged))))} 161 162 s.tryThreeWayMerge(ma, mb, m, mMerged) 163 s.tryThreeWayMerge(mb, ma, m, mMerged) 164 } 165 166 func (s *ThreeWayKeyValMergeSuite) TestThreeWayMerge_RecursiveMultiLevelMerge() { 167 m := kvs{"mm1", mm1, "mm2", mustValue(s.vs.WriteValue(context.Background(), mustValue(s.create(mm2))))} 168 ma := kvs{"mm1", mm1a, "mm2", mustValue(s.vs.WriteValue(context.Background(), mustValue(s.create(mm2a))))} 169 mb := kvs{"mm1", mm1b, "mm2", mustValue(s.vs.WriteValue(context.Background(), mustValue(s.create(mm2b))))} 170 mMerged := kvs{"mm1", mm1Merged, "mm2", mustValue(s.vs.WriteValue(context.Background(), mustValue(s.create(mm2Merged))))} 171 172 s.tryThreeWayMerge(ma, mb, m, mMerged) 173 s.tryThreeWayMerge(mb, ma, m, mMerged) 174 } 175 176 func (s *ThreeWayKeyValMergeSuite) TestThreeWayMerge_CustomMerge() { 177 p := kvs{"k1", "k-one", "k2", "k-two", "mm1", mm1, "s1", "s-one"} 178 a := kvs{"k1", "k-won", "k2", "k-too", "mm1", mm1, "s1", "s-one", "n1", kvs{"a", "1"}} 179 b := kvs{"k2", "k-two", "mm1", "mm-one", "s1", "s-one", "n1", kvs{"a", "2"}} 180 exp := kvs{"k2", "k-too", "mm1", "mm-one", "s1", "s-one", "n1", kvs{"a", "1"}} 181 182 expectedConflictPaths := [][]string{{"k1"}, {"n1", "a"}} 183 conflictPaths := []types.Path{} 184 resolve := func(aChange, bChange types.DiffChangeType, aVal, bVal types.Value, p types.Path) (change types.DiffChangeType, merged types.Value, ok bool) { 185 conflictPaths = append(conflictPaths, p) 186 if _, ok := aVal.(types.Map); ok || bChange == types.DiffChangeRemoved { 187 return bChange, bVal, true 188 } 189 return aChange, aVal, true 190 } 191 192 merged, err := ThreeWay(context.Background(), mustValue(s.create(a)), mustValue(s.create(b)), mustValue(s.create(p)), s.vs, resolve, nil) 193 if s.NoError(err) { 194 expected, err := s.create(exp) 195 s.NoError(err) 196 s.True(expected.Equals(merged), "%s != %s", mustString(types.EncodedValue(context.Background(), expected)), mustString(types.EncodedValue(context.Background(), merged))) 197 } 198 if s.Len(conflictPaths, len(expectedConflictPaths), "Wrong number of conflicts!") { 199 for i := 0; i < len(conflictPaths); i++ { 200 for j, c := range conflictPaths[i] { 201 s.Contains(c.String(), expectedConflictPaths[i][j]) 202 } 203 } 204 } 205 } 206 207 func (s *ThreeWayKeyValMergeSuite) TestThreeWayMerge_MergeOurs() { 208 p := kvs{"k1", "k-one"} 209 a := kvs{"k1", "k-won"} 210 b := kvs{"k1", "k-too", "k2", "k-two"} 211 exp := kvs{"k1", "k-won", "k2", "k-two"} 212 213 merged, err := ThreeWay(context.Background(), mustValue(s.create(a)), mustValue(s.create(b)), mustValue(s.create(p)), s.vs, Ours, nil) 214 if s.NoError(err) { 215 expected, err := s.create(exp) 216 s.NoError(err) 217 s.True(expected.Equals(merged), "%s != %s", mustString(types.EncodedValue(context.Background(), expected)), mustString(types.EncodedValue(context.Background(), merged))) 218 } 219 } 220 221 func (s *ThreeWayKeyValMergeSuite) TestThreeWayMerge_MergeTheirs() { 222 p := kvs{"k1", "k-one"} 223 a := kvs{"k1", "k-won"} 224 b := kvs{"k1", "k-too", "k2", "k-two"} 225 exp := kvs{"k1", "k-too", "k2", "k-two"} 226 227 merged, err := ThreeWay(context.Background(), mustValue(s.create(a)), mustValue(s.create(b)), mustValue(s.create(p)), s.vs, Theirs, nil) 228 if s.NoError(err) { 229 expected, err := s.create(exp) 230 s.NoError(err) 231 s.True(expected.Equals(merged), "%s != %s", mustString(types.EncodedValue(context.Background(), expected)), mustString(types.EncodedValue(context.Background(), merged))) 232 } 233 } 234 235 func (s *ThreeWayKeyValMergeSuite) TestThreeWayMerge_NilConflict() { 236 s.tryThreeWayConflict(nil, mustValue(s.create(mm2b)), mustValue(s.create(mm2)), "Cannot merge nil Value with") 237 s.tryThreeWayConflict(mustValue(s.create(mm2a)), nil, mustValue(s.create(mm2)), "with nil Value.") 238 } 239 240 func (s *ThreeWayKeyValMergeSuite) TestThreeWayMerge_ImmediateConflict() { 241 s.tryThreeWayConflict(mustValue(types.NewSet(context.Background(), s.vs)), mustValue(s.create(mm2b)), mustValue(s.create(mm2)), "Cannot merge Set<Union<>> with "+s.typeStr) 242 s.tryThreeWayConflict(mustValue(s.create(mm2b)), mustValue(types.NewSet(context.Background(), s.vs)), mustValue(s.create(mm2)), "Cannot merge "+s.typeStr) 243 } 244 245 func (s *ThreeWayKeyValMergeSuite) TestThreeWayMerge_RefConflict() { 246 strRef, err := s.vs.WriteValue(context.Background(), mustValue(types.NewStruct(types.Format_Default, "Foo", types.StructData{"life": types.Float(42)}))) 247 s.NoError(err) 248 numRef, err := s.vs.WriteValue(context.Background(), types.Float(7)) 249 s.NoError(err) 250 251 m := kvs{"r2", strRef} 252 ma := kvs{"r1", strRef, "r2", strRef} 253 mb := kvs{"r1", numRef, "r2", strRef} 254 255 s.tryThreeWayConflict(mustValue(s.create(ma)), mustValue(s.create(mb)), mustValue(s.create(m)), "Cannot merge Struct Foo") 256 s.tryThreeWayConflict(mustValue(s.create(mb)), mustValue(s.create(ma)), mustValue(s.create(m)), "Cannot merge Float and Struct Foo") 257 } 258 259 func (s *ThreeWayKeyValMergeSuite) TestThreeWayMerge_NestedConflict() { 260 a := mm2a.set("k2", mustValue(types.NewSet(context.Background(), s.vs))) 261 s.tryThreeWayConflict(mustValue(s.create(a)), mustValue(s.create(mm2b)), mustValue(s.create(mm2)), mustString(types.EncodedValue(context.Background(), mustValue(types.NewSet(context.Background(), s.vs))))) 262 s.tryThreeWayConflict(mustValue(s.create(a)), mustValue(s.create(mm2b)), mustValue(s.create(mm2)), mustString(types.EncodedValue(context.Background(), mustValue(s.create(aa1b))))) 263 } 264 265 func (s *ThreeWayKeyValMergeSuite) TestThreeWayMerge_NestedConflictingOperation() { 266 a := mm2a.remove("k2") 267 s.tryThreeWayConflict(mustValue(s.create(a)), mustValue(s.create(mm2b)), mustValue(s.create(mm2)), `removed "k2"`) 268 s.tryThreeWayConflict(mustValue(s.create(a)), mustValue(s.create(mm2b)), mustValue(s.create(mm2)), `modded "k2"`) 269 }