github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/merge/merge_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  package merge
    16  
    17  import (
    18  	"context"
    19  	"strconv"
    20  	"testing"
    21  
    22  	"github.com/google/uuid"
    23  	"github.com/stretchr/testify/assert"
    24  	"github.com/stretchr/testify/require"
    25  
    26  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    27  	"github.com/dolthub/dolt/go/libraries/doltcore/ref"
    28  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    29  	"github.com/dolthub/dolt/go/libraries/doltcore/schema/encoding"
    30  	"github.com/dolthub/dolt/go/libraries/doltcore/table/editor"
    31  	"github.com/dolthub/dolt/go/store/types"
    32  )
    33  
    34  func mustTuple(tpl types.Tuple, err error) types.Tuple {
    35  	if err != nil {
    36  		panic(err)
    37  	}
    38  
    39  	return tpl
    40  }
    41  
    42  func mustString(str string, err error) string {
    43  	if err != nil {
    44  		panic(err)
    45  	}
    46  
    47  	return str
    48  }
    49  
    50  func mustGetValue(val types.Value, _ bool, err error) types.Value {
    51  	if err != nil {
    52  		panic(err)
    53  	}
    54  
    55  	return val
    56  }
    57  
    58  type RowMergeTest struct {
    59  	name                  string
    60  	row, mergeRow, ancRow types.Value
    61  	sch                   schema.Schema
    62  	expectedResult        types.Value
    63  	expectConflict        bool
    64  }
    65  
    66  func valsToTestTupleWithoutPks(vals []types.Value) types.Value {
    67  	return valsToTestTuple(vals, false)
    68  }
    69  
    70  func valsToTestTupleWithPks(vals []types.Value) types.Value {
    71  	return valsToTestTuple(vals, true)
    72  }
    73  
    74  func valsToTestTuple(vals []types.Value, includePrimaryKeys bool) types.Value {
    75  	if vals == nil {
    76  		return nil
    77  	}
    78  
    79  	tplVals := make([]types.Value, 0, 2*len(vals))
    80  	for i, val := range vals {
    81  		if !types.IsNull(val) {
    82  			tag := i
    83  			// Assume one primary key tag, add 1 to all other tags
    84  			if includePrimaryKeys {
    85  				tag++
    86  			}
    87  			tplVals = append(tplVals, types.Uint(tag))
    88  			tplVals = append(tplVals, val)
    89  		}
    90  	}
    91  
    92  	return mustTuple(types.NewTuple(types.Format_Default, tplVals...))
    93  }
    94  
    95  func createRowMergeStruct(name string, vals, mergeVals, ancVals, expected []types.Value, expectCnf bool) RowMergeTest {
    96  	longest := vals
    97  
    98  	if len(mergeVals) > len(longest) {
    99  		longest = mergeVals
   100  	}
   101  
   102  	if len(ancVals) > len(longest) {
   103  		longest = ancVals
   104  	}
   105  
   106  	cols := make([]schema.Column, len(longest)+1)
   107  	// Schema needs a primary key to be valid, but all the logic being tested works only on the non-key columns.
   108  	cols[0] = schema.NewColumn("primaryKey", 0, types.IntKind, true)
   109  	for i, val := range longest {
   110  		tag := i + 1
   111  		cols[tag] = schema.NewColumn(strconv.FormatInt(int64(tag), 10), uint64(tag), val.Kind(), false)
   112  	}
   113  
   114  	colColl := schema.NewColCollection(cols...)
   115  	sch := schema.MustSchemaFromCols(colColl)
   116  
   117  	tpl := valsToTestTupleWithPks(vals)
   118  	mergeTpl := valsToTestTupleWithPks(mergeVals)
   119  	ancTpl := valsToTestTupleWithPks(ancVals)
   120  	expectedTpl := valsToTestTupleWithPks(expected)
   121  	return RowMergeTest{name, tpl, mergeTpl, ancTpl, sch, expectedTpl, expectCnf}
   122  }
   123  
   124  func TestRowMerge(t *testing.T) {
   125  	tests := []RowMergeTest{
   126  		createRowMergeStruct(
   127  			"add same row",
   128  			[]types.Value{types.String("one"), types.Int(2)},
   129  			[]types.Value{types.String("one"), types.Int(2)},
   130  			nil,
   131  			[]types.Value{types.String("one"), types.Int(2)},
   132  			false,
   133  		),
   134  		createRowMergeStruct(
   135  			"add diff row",
   136  			[]types.Value{types.String("one"), types.String("two")},
   137  			[]types.Value{types.String("one"), types.String("three")},
   138  			nil,
   139  			nil,
   140  			true,
   141  		),
   142  		createRowMergeStruct(
   143  			"both delete row",
   144  			nil,
   145  			nil,
   146  			[]types.Value{types.String("one"), types.Uint(2)},
   147  			nil,
   148  			false,
   149  		),
   150  		createRowMergeStruct(
   151  			"one delete one modify",
   152  			nil,
   153  			[]types.Value{types.String("two"), types.Uint(2)},
   154  			[]types.Value{types.String("one"), types.Uint(2)},
   155  			nil,
   156  			true,
   157  		),
   158  		createRowMergeStruct(
   159  			"modify rows without overlap",
   160  			[]types.Value{types.String("two"), types.Uint(2)},
   161  			[]types.Value{types.String("one"), types.Uint(3)},
   162  			[]types.Value{types.String("one"), types.Uint(2)},
   163  			[]types.Value{types.String("two"), types.Uint(3)},
   164  			false,
   165  		),
   166  		createRowMergeStruct(
   167  			"modify rows with equal overlapping changes",
   168  			[]types.Value{types.String("two"), types.Uint(2), types.UUID(uuid.MustParse("ffffffff-ffff-ffff-ffff-ffffffffffff"))},
   169  			[]types.Value{types.String("one"), types.Uint(3), types.UUID(uuid.MustParse("ffffffff-ffff-ffff-ffff-ffffffffffff"))},
   170  			[]types.Value{types.String("one"), types.Uint(2), types.UUID(uuid.MustParse("00000000-0000-0000-0000-000000000000"))},
   171  			[]types.Value{types.String("two"), types.Uint(3), types.UUID(uuid.MustParse("ffffffff-ffff-ffff-ffff-ffffffffffff"))},
   172  			false,
   173  		),
   174  		createRowMergeStruct(
   175  			"modify rows with differing overlapping changes",
   176  			[]types.Value{types.String("two"), types.Uint(2), types.UUID(uuid.MustParse("99999999-9999-9999-9999-999999999999"))},
   177  			[]types.Value{types.String("one"), types.Uint(3), types.UUID(uuid.MustParse("ffffffff-ffff-ffff-ffff-ffffffffffff"))},
   178  			[]types.Value{types.String("one"), types.Uint(2), types.UUID(uuid.MustParse("00000000-0000-0000-0000-000000000000"))},
   179  			nil,
   180  			true,
   181  		),
   182  		createRowMergeStruct(
   183  			"modify rows where one adds a column",
   184  			[]types.Value{types.String("two"), types.Uint(2)},
   185  			[]types.Value{types.String("one"), types.Uint(3), types.UUID(uuid.MustParse("ffffffff-ffff-ffff-ffff-ffffffffffff"))},
   186  			[]types.Value{types.String("one"), types.Uint(2)},
   187  			[]types.Value{types.String("two"), types.Uint(3), types.UUID(uuid.MustParse("ffffffff-ffff-ffff-ffff-ffffffffffff"))},
   188  			false,
   189  		),
   190  		createRowMergeStruct(
   191  			"modify row where values added in different columns",
   192  			[]types.Value{types.String("one"), types.Uint(2), types.String(""), types.UUID(uuid.MustParse("ffffffff-ffff-ffff-ffff-ffffffffffff"))},
   193  			[]types.Value{types.String("one"), types.Uint(2), types.UUID(uuid.MustParse("ffffffff-ffff-ffff-ffff-ffffffffffff")), types.String("")},
   194  			[]types.Value{types.String("one"), types.Uint(2), types.NullValue, types.NullValue},
   195  			nil,
   196  			true,
   197  		),
   198  		createRowMergeStruct(
   199  			"modify row where initial value wasn't given",
   200  			[]types.Value{mustTuple(types.NewTuple(types.Format_Default, types.String("one"), types.Uint(2), types.String("a")))},
   201  			[]types.Value{mustTuple(types.NewTuple(types.Format_Default, types.String("one"), types.Uint(2), types.String("b")))},
   202  			[]types.Value{mustTuple(types.NewTuple(types.Format_Default, types.String("one"), types.Uint(2), types.NullValue))},
   203  			nil,
   204  			true,
   205  		),
   206  	}
   207  
   208  	for _, test := range tests {
   209  		t.Run(test.name, func(t *testing.T) {
   210  			actualResult, isConflict, err := pkRowMerge(context.Background(), types.Format_Default, test.sch, test.row, test.mergeRow, test.ancRow)
   211  			assert.NoError(t, err)
   212  			assert.Equal(t, test.expectedResult, actualResult, "expected "+mustString(types.EncodedValue(context.Background(), test.expectedResult))+"got "+mustString(types.EncodedValue(context.Background(), actualResult)))
   213  			assert.Equal(t, test.expectConflict, isConflict)
   214  		})
   215  	}
   216  }
   217  
   218  const (
   219  	tableName = "test-table"
   220  	name      = "billy bob"
   221  	email     = "bigbillieb@fake.horse"
   222  
   223  	idTag    = 100
   224  	nameTag  = 0
   225  	titleTag = 1
   226  )
   227  
   228  var colColl = schema.NewColCollection(
   229  	schema.NewColumn("id", idTag, types.UUIDKind, true, schema.NotNullConstraint{}),
   230  	schema.NewColumn("name", nameTag, types.StringKind, false, schema.NotNullConstraint{}),
   231  	schema.NewColumn("title", titleTag, types.StringKind, false),
   232  )
   233  var sch = schema.MustSchemaFromCols(colColl)
   234  
   235  var uuids = []types.UUID{
   236  	types.UUID(uuid.MustParse("00000000-0000-0000-0000-000000000000")),
   237  	types.UUID(uuid.MustParse("00000000-0000-0000-0000-000000000001")),
   238  	types.UUID(uuid.MustParse("00000000-0000-0000-0000-000000000002")),
   239  	types.UUID(uuid.MustParse("00000000-0000-0000-0000-000000000003")),
   240  	types.UUID(uuid.MustParse("00000000-0000-0000-0000-000000000004")),
   241  	types.UUID(uuid.MustParse("00000000-0000-0000-0000-000000000005")),
   242  	types.UUID(uuid.MustParse("00000000-0000-0000-0000-000000000006")),
   243  	types.UUID(uuid.MustParse("00000000-0000-0000-0000-000000000007")),
   244  	types.UUID(uuid.MustParse("00000000-0000-0000-0000-000000000008")),
   245  	types.UUID(uuid.MustParse("00000000-0000-0000-0000-000000000009")),
   246  	types.UUID(uuid.MustParse("00000000-0000-0000-0000-00000000000a")),
   247  	types.UUID(uuid.MustParse("00000000-0000-0000-0000-00000000000b")),
   248  	types.UUID(uuid.MustParse("00000000-0000-0000-0000-00000000000c")),
   249  }
   250  
   251  var keyTuples = make([]types.Tuple, len(uuids))
   252  
   253  var index schema.Index
   254  
   255  func init() {
   256  	keyTag := types.Uint(idTag)
   257  
   258  	for i, id := range uuids {
   259  		keyTuples[i] = mustTuple(types.NewTuple(types.Format_Default, keyTag, id))
   260  	}
   261  
   262  	index, _ = sch.Indexes().AddIndexByColTags("idx_name", []uint64{nameTag}, schema.IndexProperties{IsUnique: false, Comment: ""})
   263  }
   264  
   265  func setupMergeTest(t *testing.T) (types.ValueReadWriter, *doltdb.Commit, *doltdb.Commit, types.Map, types.Map) {
   266  	ddb, _ := doltdb.LoadDoltDB(context.Background(), types.Format_Default, doltdb.InMemDoltDB)
   267  	vrw := ddb.ValueReadWriter()
   268  
   269  	err := ddb.WriteEmptyRepo(context.Background(), name, email)
   270  	require.NoError(t, err)
   271  
   272  	masterHeadSpec, _ := doltdb.NewCommitSpec("master")
   273  	masterHead, err := ddb.Resolve(context.Background(), masterHeadSpec, nil)
   274  	require.NoError(t, err)
   275  
   276  	initialRows, err := types.NewMap(context.Background(), vrw,
   277  		keyTuples[0], valsToTestTupleWithoutPks([]types.Value{types.String("person 1"), types.String("dufus")}),
   278  		keyTuples[1], valsToTestTupleWithoutPks([]types.Value{types.String("person 2"), types.NullValue}),
   279  		keyTuples[2], valsToTestTupleWithoutPks([]types.Value{types.String("person 3"), types.NullValue}),
   280  		keyTuples[3], valsToTestTupleWithoutPks([]types.Value{types.String("person 4"), types.String("senior dufus")}),
   281  		keyTuples[4], valsToTestTupleWithoutPks([]types.Value{types.String("person 5"), types.NullValue}),
   282  		keyTuples[5], valsToTestTupleWithoutPks([]types.Value{types.String("person 6"), types.NullValue}),
   283  		keyTuples[6], valsToTestTupleWithoutPks([]types.Value{types.String("person 7"), types.String("madam")}),
   284  		keyTuples[7], valsToTestTupleWithoutPks([]types.Value{types.String("person 8"), types.String("miss")}),
   285  		keyTuples[8], valsToTestTupleWithoutPks([]types.Value{types.String("person 9"), types.NullValue}),
   286  	)
   287  	require.NoError(t, err)
   288  
   289  	updateRowEditor := initialRows.Edit()                                                                                          // leave 0 as is
   290  	updateRowEditor.Remove(keyTuples[1])                                                                                           // remove 1 from both
   291  	updateRowEditor.Remove(keyTuples[2])                                                                                           // remove 2 from update
   292  	updateRowEditor.Set(keyTuples[4], valsToTestTupleWithoutPks([]types.Value{types.String("person five"), types.NullValue}))      // modify 4 only in update
   293  	updateRowEditor.Set(keyTuples[6], valsToTestTupleWithoutPks([]types.Value{types.String("person 7"), types.String("dr")}))      // modify 6 in both without overlap
   294  	updateRowEditor.Set(keyTuples[7], valsToTestTupleWithoutPks([]types.Value{types.String("person eight"), types.NullValue}))     // modify 7 in both with equal overlap
   295  	updateRowEditor.Set(keyTuples[8], valsToTestTupleWithoutPks([]types.Value{types.String("person nine"), types.NullValue}))      // modify 8 in both with conflicting overlap
   296  	updateRowEditor.Set(keyTuples[9], valsToTestTupleWithoutPks([]types.Value{types.String("person ten"), types.NullValue}))       // add 9 in update
   297  	updateRowEditor.Set(keyTuples[11], valsToTestTupleWithoutPks([]types.Value{types.String("person twelve"), types.NullValue}))   // add 11 in both without difference
   298  	updateRowEditor.Set(keyTuples[12], valsToTestTupleWithoutPks([]types.Value{types.String("person thirteen"), types.NullValue})) // add 12 in both with differences
   299  
   300  	updatedRows, err := updateRowEditor.Map(context.Background())
   301  	require.NoError(t, err)
   302  
   303  	mergeRowEditor := initialRows.Edit()                                                                                                 // leave 0 as is
   304  	mergeRowEditor.Remove(keyTuples[1])                                                                                                  // remove 1 from both
   305  	mergeRowEditor.Remove(keyTuples[3])                                                                                                  // remove 3 from merge
   306  	mergeRowEditor.Set(keyTuples[5], valsToTestTupleWithoutPks([]types.Value{types.String("person six"), types.NullValue}))              // modify 5 only in merge
   307  	mergeRowEditor.Set(keyTuples[6], valsToTestTupleWithoutPks([]types.Value{types.String("person seven"), types.String("madam")}))      // modify 6 in both without overlap
   308  	mergeRowEditor.Set(keyTuples[7], valsToTestTupleWithoutPks([]types.Value{types.String("person eight"), types.NullValue}))            // modify 7 in both with equal overlap
   309  	mergeRowEditor.Set(keyTuples[8], valsToTestTupleWithoutPks([]types.Value{types.String("person number nine"), types.NullValue}))      // modify 8 in both with conflicting overlap
   310  	mergeRowEditor.Set(keyTuples[10], valsToTestTupleWithoutPks([]types.Value{types.String("person eleven"), types.NullValue}))          // add 10 in merge
   311  	mergeRowEditor.Set(keyTuples[11], valsToTestTupleWithoutPks([]types.Value{types.String("person twelve"), types.NullValue}))          // add 11 in both without difference
   312  	mergeRowEditor.Set(keyTuples[12], valsToTestTupleWithoutPks([]types.Value{types.String("person number thirteen"), types.NullValue})) // add 12 in both with differences
   313  
   314  	mergeRows, err := mergeRowEditor.Map(context.Background())
   315  	require.NoError(t, err)
   316  
   317  	expectedRows, err := types.NewMap(context.Background(), vrw,
   318  		keyTuples[0], mustGetValue(initialRows.MaybeGet(context.Background(), keyTuples[0])), // unaltered
   319  		keyTuples[4], mustGetValue(updatedRows.MaybeGet(context.Background(), keyTuples[4])), // modified in updated
   320  		keyTuples[5], mustGetValue(mergeRows.MaybeGet(context.Background(), keyTuples[5])), // modified in merged
   321  		keyTuples[6], valsToTestTupleWithoutPks([]types.Value{types.String("person seven"), types.String("dr")}), // modified in both with no overlap
   322  		keyTuples[7], mustGetValue(updatedRows.MaybeGet(context.Background(), keyTuples[7])), // modify both with the same value
   323  		keyTuples[8], mustGetValue(updatedRows.MaybeGet(context.Background(), keyTuples[8])), // conflict
   324  		keyTuples[9], mustGetValue(updatedRows.MaybeGet(context.Background(), keyTuples[9])), // added in update
   325  		keyTuples[10], mustGetValue(mergeRows.MaybeGet(context.Background(), keyTuples[10])), // added in merge
   326  		keyTuples[11], mustGetValue(updatedRows.MaybeGet(context.Background(), keyTuples[11])), // added same in both
   327  		keyTuples[12], mustGetValue(updatedRows.MaybeGet(context.Background(), keyTuples[12])), // conflict
   328  	)
   329  	require.NoError(t, err)
   330  
   331  	updateConflict := doltdb.NewConflict(
   332  		mustGetValue(initialRows.MaybeGet(context.Background(), keyTuples[8])),
   333  		mustGetValue(updatedRows.MaybeGet(context.Background(), keyTuples[8])),
   334  		mustGetValue(mergeRows.MaybeGet(context.Background(), keyTuples[8])))
   335  
   336  	addConflict := doltdb.NewConflict(
   337  		nil,
   338  		valsToTestTupleWithoutPks([]types.Value{types.String("person thirteen"), types.NullValue}),
   339  		valsToTestTupleWithoutPks([]types.Value{types.String("person number thirteen"), types.NullValue}),
   340  	)
   341  
   342  	expectedConflicts, err := types.NewMap(context.Background(), vrw,
   343  		keyTuples[8], mustTuple(updateConflict.ToNomsList(vrw)),
   344  		keyTuples[12], mustTuple(addConflict.ToNomsList(vrw)),
   345  	)
   346  	require.NoError(t, err)
   347  
   348  	schVal, err := encoding.MarshalSchemaAsNomsValue(context.Background(), vrw, sch)
   349  	require.NoError(t, err)
   350  
   351  	emptyMap, err := types.NewMap(context.Background(), vrw)
   352  	require.NoError(t, err)
   353  
   354  	tbl, err := doltdb.NewTable(context.Background(), vrw, schVal, initialRows, emptyMap, nil)
   355  	require.NoError(t, err)
   356  	tbl, err = editor.RebuildAllIndexes(context.Background(), tbl)
   357  	require.NoError(t, err)
   358  
   359  	updatedTbl, err := doltdb.NewTable(context.Background(), vrw, schVal, updatedRows, emptyMap, nil)
   360  	require.NoError(t, err)
   361  	updatedTbl, err = editor.RebuildAllIndexes(context.Background(), updatedTbl)
   362  	require.NoError(t, err)
   363  
   364  	mergeTbl, err := doltdb.NewTable(context.Background(), vrw, schVal, mergeRows, emptyMap, nil)
   365  	require.NoError(t, err)
   366  	mergeTbl, err = editor.RebuildAllIndexes(context.Background(), mergeTbl)
   367  	require.NoError(t, err)
   368  
   369  	mRoot, err := masterHead.GetRootValue()
   370  	require.NoError(t, err)
   371  
   372  	mRoot, err = mRoot.PutTable(context.Background(), tableName, tbl)
   373  	require.NoError(t, err)
   374  
   375  	updatedRoot, err := mRoot.PutTable(context.Background(), tableName, updatedTbl)
   376  	require.NoError(t, err)
   377  
   378  	mergeRoot, err := mRoot.PutTable(context.Background(), tableName, mergeTbl)
   379  	require.NoError(t, err)
   380  
   381  	masterHash, err := ddb.WriteRootValue(context.Background(), mRoot)
   382  	require.NoError(t, err)
   383  	hash, err := ddb.WriteRootValue(context.Background(), updatedRoot)
   384  	require.NoError(t, err)
   385  	mergeHash, err := ddb.WriteRootValue(context.Background(), mergeRoot)
   386  	require.NoError(t, err)
   387  
   388  	meta, err := doltdb.NewCommitMeta(name, email, "fake")
   389  	require.NoError(t, err)
   390  	initialCommit, err := ddb.Commit(context.Background(), masterHash, ref.NewBranchRef("master"), meta)
   391  	require.NoError(t, err)
   392  	commit, err := ddb.Commit(context.Background(), hash, ref.NewBranchRef("master"), meta)
   393  	require.NoError(t, err)
   394  
   395  	err = ddb.NewBranchAtCommit(context.Background(), ref.NewBranchRef("to-merge"), initialCommit)
   396  	require.NoError(t, err)
   397  	mergeCommit, err := ddb.Commit(context.Background(), mergeHash, ref.NewBranchRef("to-merge"), meta)
   398  	require.NoError(t, err)
   399  
   400  	return vrw, commit, mergeCommit, expectedRows, expectedConflicts
   401  }
   402  
   403  func TestMergeCommits(t *testing.T) {
   404  	vrw, commit, mergeCommit, expectedRows, expectedConflicts := setupMergeTest(t)
   405  
   406  	root, err := commit.GetRootValue()
   407  	require.NoError(t, err)
   408  
   409  	mergeRoot, err := mergeCommit.GetRootValue()
   410  	require.NoError(t, err)
   411  
   412  	ancCm, err := doltdb.GetCommitAncestor(context.Background(), commit, mergeCommit)
   413  	require.NoError(t, err)
   414  
   415  	ancRoot, err := ancCm.GetRootValue()
   416  	require.NoError(t, err)
   417  
   418  	ff, err := commit.CanFastForwardTo(context.Background(), mergeCommit)
   419  	require.NoError(t, err)
   420  	require.False(t, ff)
   421  
   422  	merger := NewMerger(context.Background(), root, mergeRoot, ancRoot, vrw)
   423  	tableEditSession := editor.CreateTableEditSession(root, editor.TableEditSessionProps{})
   424  	merged, stats, err := merger.MergeTable(context.Background(), tableName, tableEditSession)
   425  
   426  	if err != nil {
   427  		t.Fatal(err)
   428  	}
   429  
   430  	if stats.Adds != 2 || stats.Deletes != 2 || stats.Modifications != 3 || stats.Conflicts != 2 {
   431  		t.Error("Actual stats differ from expected")
   432  	}
   433  
   434  	tbl, _, err := root.GetTable(context.Background(), tableName)
   435  	assert.NoError(t, err)
   436  	schRef, err := tbl.GetSchemaRef()
   437  	assert.NoError(t, err)
   438  	targVal, err := schRef.TargetValue(context.Background(), vrw)
   439  	assert.NoError(t, err)
   440  	emptyMap, err := types.NewMap(context.Background(), vrw)
   441  	assert.NoError(t, err)
   442  	expected, err := doltdb.NewTable(context.Background(), vrw, targVal, expectedRows, emptyMap, nil)
   443  	assert.NoError(t, err)
   444  	expected, err = editor.RebuildAllIndexes(context.Background(), expected)
   445  	assert.NoError(t, err)
   446  	expected, err = expected.SetConflicts(context.Background(), doltdb.NewConflict(schRef, schRef, schRef), expectedConflicts)
   447  	assert.NoError(t, err)
   448  
   449  	h, err := merged.HashOf()
   450  	assert.NoError(t, err)
   451  	eh, err := expected.HashOf()
   452  	assert.NoError(t, err)
   453  	if h == eh {
   454  		mergedRows, err := merged.GetRowData(context.Background())
   455  		assert.NoError(t, err)
   456  		if !mergedRows.Equals(expectedRows) {
   457  			t.Error(mustString(types.EncodedValue(context.Background(), mergedRows)), "\n!=\n", mustString(types.EncodedValue(context.Background(), expectedRows)))
   458  		}
   459  		mergedIndexRows, err := merged.GetIndexRowData(context.Background(), index.Name())
   460  		assert.NoError(t, err)
   461  		expectedIndexRows, err := expected.GetIndexRowData(context.Background(), index.Name())
   462  		assert.NoError(t, err)
   463  		if expectedRows.Len() != mergedIndexRows.Len() || !mergedIndexRows.Equals(expectedIndexRows) {
   464  			t.Error("index contents are incorrect")
   465  		}
   466  	} else {
   467  		assert.Fail(t, "%v and %v do not equal", h, eh)
   468  	}
   469  }