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