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  }