github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/store/datas/commit_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 datas
    23  
    24  import (
    25  	"context"
    26  	"testing"
    27  
    28  	"github.com/stretchr/testify/assert"
    29  
    30  	"github.com/dolthub/dolt/go/store/chunks"
    31  	"github.com/dolthub/dolt/go/store/d"
    32  	"github.com/dolthub/dolt/go/store/nomdl"
    33  	"github.com/dolthub/dolt/go/store/types"
    34  )
    35  
    36  func mustHead(ds Dataset) types.Struct {
    37  	s, ok := ds.MaybeHead()
    38  	if !ok {
    39  		panic("no head")
    40  	}
    41  
    42  	return s
    43  }
    44  
    45  func mustHeadRef(ds Dataset) types.Ref {
    46  	hr, ok, err := ds.MaybeHeadRef()
    47  
    48  	if err != nil {
    49  		panic("error getting head")
    50  	}
    51  
    52  	if !ok {
    53  		panic("no head")
    54  	}
    55  
    56  	return hr
    57  }
    58  
    59  func mustHeadValue(ds Dataset) types.Value {
    60  	val, ok, err := ds.MaybeHeadValue()
    61  
    62  	if err != nil {
    63  		panic("error getting head")
    64  	}
    65  
    66  	if !ok {
    67  		panic("no head")
    68  	}
    69  
    70  	return val
    71  }
    72  
    73  func mustString(str string, err error) string {
    74  	d.PanicIfError(err)
    75  	return str
    76  }
    77  
    78  func mustStruct(st types.Struct, err error) types.Struct {
    79  	d.PanicIfError(err)
    80  	return st
    81  }
    82  
    83  func mustSet(s types.Set, err error) types.Set {
    84  	d.PanicIfError(err)
    85  	return s
    86  }
    87  
    88  func mustList(l types.List, err error) types.List {
    89  	d.PanicIfError(err)
    90  	return l
    91  }
    92  
    93  func mustType(t *types.Type, err error) *types.Type {
    94  	d.PanicIfError(err)
    95  	return t
    96  }
    97  
    98  func mustRef(ref types.Ref, err error) types.Ref {
    99  	d.PanicIfError(err)
   100  	return ref
   101  }
   102  
   103  func mustValue(val types.Value, err error) types.Value {
   104  	d.PanicIfError(err)
   105  	return val
   106  }
   107  
   108  func TestNewCommit(t *testing.T) {
   109  	assert := assert.New(t)
   110  
   111  	assertTypeEquals := func(e, a *types.Type) {
   112  		t.Helper()
   113  		assert.True(a.Equals(e), "Actual: %s\nExpected %s", mustString(a.Describe(context.Background())), mustString(e.Describe(context.Background())))
   114  	}
   115  
   116  	storage := &chunks.TestStorage{}
   117  	db := NewDatabase(storage.NewView())
   118  	defer db.Close()
   119  
   120  	parents := mustList(types.NewList(context.Background(), db))
   121  	commit, err := NewCommit(context.Background(), types.Float(1), parents, types.EmptyStruct(types.Format_7_18))
   122  	assert.NoError(err)
   123  	at, err := types.TypeOf(commit)
   124  	assert.NoError(err)
   125  	et, err := makeCommitStructType(
   126  		types.EmptyStructType,
   127  		mustType(types.MakeSetType(mustType(types.MakeUnionType()))),
   128  		mustType(types.MakeListType(mustType(types.MakeUnionType()))),
   129  		types.PrimitiveTypeMap[types.FloatKind],
   130  	)
   131  	assert.NoError(err)
   132  	assertTypeEquals(et, at)
   133  
   134  	// Committing another Float
   135  	parents = mustList(types.NewList(context.Background(), db, mustRef(types.NewRef(commit, types.Format_7_18))))
   136  	commit2, err := NewCommit(context.Background(), types.Float(2), parents, types.EmptyStruct(types.Format_7_18))
   137  	assert.NoError(err)
   138  	at2, err := types.TypeOf(commit2)
   139  	assert.NoError(err)
   140  	et2 := nomdl.MustParseType(`Struct Commit {
   141                  meta: Struct {},
   142                  parents: Set<Ref<Cycle<Commit>>>,
   143                  parents_list: List<Ref<Cycle<Commit>>>,
   144                  value: Float,
   145          }`)
   146  	assertTypeEquals(et2, at2)
   147  
   148  	// Now commit a String
   149  	parents = mustList(types.NewList(context.Background(), db, mustRef(types.NewRef(commit2, types.Format_7_18))))
   150  	commit3, err := NewCommit(context.Background(), types.String("Hi"), parents, types.EmptyStruct(types.Format_7_18))
   151  	assert.NoError(err)
   152  	at3, err := types.TypeOf(commit3)
   153  	assert.NoError(err)
   154  	et3 := nomdl.MustParseType(`Struct Commit {
   155                  meta: Struct {},
   156                  parents: Set<Ref<Cycle<Commit>>>,
   157                  parents_list: List<Ref<Cycle<Commit>>>,
   158                  value: Float | String,
   159          }`)
   160  	assertTypeEquals(et3, at3)
   161  
   162  	// Now commit a String with MetaInfo
   163  	meta, err := types.NewStruct(types.Format_7_18, "Meta", types.StructData{"date": types.String("some date"), "number": types.Float(9)})
   164  	assert.NoError(err)
   165  	metaType := nomdl.MustParseType(`Struct Meta {
   166                  date: String,
   167                  number: Float,
   168  	}`)
   169  	assertTypeEquals(metaType, mustType(types.TypeOf(meta)))
   170  	parents = mustList(types.NewList(context.Background(), db, mustRef(types.NewRef(commit2, types.Format_7_18))))
   171  	commit4, err := NewCommit(context.Background(), types.String("Hi"), parents, meta)
   172  	assert.NoError(err)
   173  	at4, err := types.TypeOf(commit4)
   174  	assert.NoError(err)
   175  	et4 := nomdl.MustParseType(`Struct Commit {
   176                  meta: Struct {} | Struct Meta {
   177                          date: String,
   178                          number: Float,
   179          	},
   180                  parents: Set<Ref<Cycle<Commit>>>,
   181                  parents_list: List<Ref<Cycle<Commit>>>,
   182                  value: Float | String,
   183          }`)
   184  	assertTypeEquals(et4, at4)
   185  
   186  	// Merge-commit with different parent types
   187  	parents = mustList(types.NewList(context.Background(), db,
   188  		mustRef(types.NewRef(commit2, types.Format_7_18)),
   189  		mustRef(types.NewRef(commit3, types.Format_7_18))))
   190  	commit5, err := NewCommit(
   191  		context.Background(),
   192  		types.String("Hi"),
   193  		parents,
   194  		types.EmptyStruct(types.Format_7_18))
   195  	assert.NoError(err)
   196  	at5, err := types.TypeOf(commit5)
   197  	assert.NoError(err)
   198  	et5 := nomdl.MustParseType(`Struct Commit {
   199                  meta: Struct {},
   200                  parents: Set<Ref<Cycle<Commit>>>,
   201                  parents_list: List<Ref<Cycle<Commit>>>,
   202                  value: Float | String,
   203          }`)
   204  	assertTypeEquals(et5, at5)
   205  }
   206  
   207  func TestCommitWithoutMetaField(t *testing.T) {
   208  	assert := assert.New(t)
   209  
   210  	storage := &chunks.TestStorage{}
   211  	db := NewDatabase(storage.NewView())
   212  	defer db.Close()
   213  
   214  	metaCommit, err := types.NewStruct(types.Format_7_18, "Commit", types.StructData{
   215  		"value":   types.Float(9),
   216  		"parents": mustSet(types.NewSet(context.Background(), db)),
   217  		"meta":    types.EmptyStruct(types.Format_7_18),
   218  	})
   219  	assert.NoError(err)
   220  	assert.True(IsCommit(metaCommit))
   221  
   222  	noMetaCommit, err := types.NewStruct(types.Format_7_18, "Commit", types.StructData{
   223  		"value":   types.Float(9),
   224  		"parents": mustSet(types.NewSet(context.Background(), db)),
   225  	})
   226  	assert.NoError(err)
   227  	assert.False(IsCommit(noMetaCommit))
   228  }
   229  
   230  // Convert list of Struct's to List<Ref>
   231  func toRefList(vrw types.ValueReadWriter, commits ...types.Struct) (types.List, error) {
   232  	l, err := types.NewList(context.Background(), vrw)
   233  	if err != nil {
   234  		return types.EmptyList, err
   235  	}
   236  
   237  	le := l.Edit()
   238  	for _, p := range commits {
   239  		le = le.Append(mustRef(types.NewRef(p, types.Format_7_18)))
   240  	}
   241  	return le.List(context.Background())
   242  }
   243  
   244  func TestFindCommonAncestor(t *testing.T) {
   245  	assert := assert.New(t)
   246  
   247  	// Add a commit and return it
   248  	addCommit := func(db Database, datasetID string, val string, parents ...types.Struct) types.Struct {
   249  		ds, err := db.GetDataset(context.Background(), datasetID)
   250  		assert.NoError(err)
   251  		ds, err = db.Commit(context.Background(), ds, types.String(val), CommitOptions{ParentsList: mustList(toRefList(db, parents...))})
   252  		assert.NoError(err)
   253  		return mustHead(ds)
   254  	}
   255  
   256  	// Assert that c is the common ancestor of a and b
   257  	assertCommonAncestor := func(expected, a, b types.Struct, ldb, rdb Database) {
   258  		found, ok, err := FindCommonAncestor(context.Background(), mustRef(types.NewRef(a, types.Format_7_18)), mustRef(types.NewRef(b, types.Format_7_18)), ldb, rdb)
   259  		assert.NoError(err)
   260  
   261  		if assert.True(ok) {
   262  			tv, err := found.TargetValue(context.Background(), ldb)
   263  			assert.NoError(err)
   264  			ancestor := tv.(types.Struct)
   265  			expV, _, _ := expected.MaybeGet(ValueField)
   266  			aV, _, _ := a.MaybeGet(ValueField)
   267  			bV, _, _ := b.MaybeGet(ValueField)
   268  			ancV, _, _ := ancestor.MaybeGet(ValueField)
   269  			assert.True(
   270  				expected.Equals(ancestor),
   271  				"%s should be common ancestor of %s, %s. Got %s",
   272  				expV,
   273  				aV,
   274  				bV,
   275  				ancV,
   276  			)
   277  		}
   278  	}
   279  
   280  	storage := &chunks.TestStorage{}
   281  	db := NewDatabase(storage.NewView())
   282  
   283  	// Build commit DAG
   284  	//
   285  	// ds-a: a1<-a2<-a3<-a4<-a5<-a6
   286  	//       ^    ^   ^          |
   287  	//       |     \   \----\  /-/
   288  	//       |      \        \V
   289  	// ds-b:  \      b3<-b4<-b5
   290  	//         \
   291  	//          \
   292  	// ds-c:     c2<-c3
   293  	//              /
   294  	//             /
   295  	//            V
   296  	// ds-d: d1<-d2
   297  	//
   298  	a, b, c, d := "ds-a", "ds-b", "ds-c", "ds-d"
   299  	a1 := addCommit(db, a, "a1")
   300  	d1 := addCommit(db, d, "d1")
   301  	a2 := addCommit(db, a, "a2", a1)
   302  	c2 := addCommit(db, c, "c2", a1)
   303  	d2 := addCommit(db, d, "d2", d1)
   304  	a3 := addCommit(db, a, "a3", a2)
   305  	b3 := addCommit(db, b, "b3", a2)
   306  	c3 := addCommit(db, c, "c3", c2, d2)
   307  	a4 := addCommit(db, a, "a4", a3)
   308  	b4 := addCommit(db, b, "b4", b3)
   309  	a5 := addCommit(db, a, "a5", a4)
   310  	b5 := addCommit(db, b, "b5", b4, a3)
   311  	a6 := addCommit(db, a, "a6", a5, b5)
   312  
   313  	assertCommonAncestor(a1, a1, a1, db, db) // All self
   314  	assertCommonAncestor(a1, a1, a2, db, db) // One side self
   315  	assertCommonAncestor(a2, a3, b3, db, db) // Common parent
   316  	assertCommonAncestor(a2, a4, b4, db, db) // Common grandparent
   317  	assertCommonAncestor(a1, a6, c3, db, db) // Traversing multiple parents on both sides
   318  
   319  	// No common ancestor
   320  	found, ok, err := FindCommonAncestor(context.Background(), mustRef(types.NewRef(d2, types.Format_7_18)), mustRef(types.NewRef(a6, types.Format_7_18)), db, db)
   321  	assert.NoError(err)
   322  
   323  	if !assert.False(ok) {
   324  		d2V, _, _ := d2.MaybeGet(ValueField)
   325  		a6V, _, _ := a6.MaybeGet(ValueField)
   326  		fTV, _ := found.TargetValue(context.Background(), db)
   327  		fV, _, _ := fTV.(types.Struct).MaybeGet(ValueField)
   328  
   329  		assert.Fail(
   330  			"Unexpected common ancestor!",
   331  			"Should be no common ancestor of %s, %s. Got %s",
   332  			d2V,
   333  			a6V,
   334  			fV,
   335  		)
   336  	}
   337  
   338  	assert.NoError(db.Close())
   339  
   340  	storage = &chunks.TestStorage{}
   341  	db = NewDatabase(storage.NewView())
   342  	defer db.Close()
   343  
   344  	rstorage := &chunks.TestStorage{}
   345  	rdb := NewDatabase(rstorage.NewView())
   346  	defer rdb.Close()
   347  
   348  	// Rerun the tests when using two difference Databases for left and
   349  	// right commits. Both databases have all the previous commits.
   350  	a, b, c, d = "ds-a", "ds-b", "ds-c", "ds-d"
   351  	a1 = addCommit(db, a, "a1")
   352  	d1 = addCommit(db, d, "d1")
   353  	a2 = addCommit(db, a, "a2", a1)
   354  	c2 = addCommit(db, c, "c2", a1)
   355  	d2 = addCommit(db, d, "d2", d1)
   356  	a3 = addCommit(db, a, "a3", a2)
   357  	b3 = addCommit(db, b, "b3", a2)
   358  	c3 = addCommit(db, c, "c3", c2, d2)
   359  	a4 = addCommit(db, a, "a4", a3)
   360  	b4 = addCommit(db, b, "b4", b3)
   361  	a5 = addCommit(db, a, "a5", a4)
   362  	b5 = addCommit(db, b, "b5", b4, a3)
   363  	a6 = addCommit(db, a, "a6", a5, b5)
   364  
   365  	addCommit(rdb, a, "a1")
   366  	addCommit(rdb, d, "d1")
   367  	addCommit(rdb, a, "a2", a1)
   368  	addCommit(rdb, c, "c2", a1)
   369  	addCommit(rdb, d, "d2", d1)
   370  	addCommit(rdb, a, "a3", a2)
   371  	addCommit(rdb, b, "b3", a2)
   372  	addCommit(rdb, c, "c3", c2, d2)
   373  	addCommit(rdb, a, "a4", a3)
   374  	addCommit(rdb, b, "b4", b3)
   375  	addCommit(rdb, a, "a5", a4)
   376  	addCommit(rdb, b, "b5", b4, a3)
   377  	addCommit(rdb, a, "a6", a5, b5)
   378  
   379  	// Additionally, |db| has a6<-a7<-a8<-a9.
   380  	// |rdb| has a6<-ra7<-ra8<-ra9.
   381  	a7 := addCommit(db, a, "a7", a6)
   382  	a8 := addCommit(db, a, "a8", a7)
   383  	a9 := addCommit(db, a, "a9", a8)
   384  
   385  	ra7 := addCommit(rdb, a, "ra7", a6)
   386  	ra8 := addCommit(rdb, a, "ra8", ra7)
   387  	ra9 := addCommit(rdb, a, "ra9", ra8)
   388  
   389  	assertCommonAncestor(a1, a1, a1, db, rdb) // All self
   390  	assertCommonAncestor(a1, a1, a2, db, rdb) // One side self
   391  	assertCommonAncestor(a2, a3, b3, db, rdb) // Common parent
   392  	assertCommonAncestor(a2, a4, b4, db, rdb) // Common grandparent
   393  	assertCommonAncestor(a1, a6, c3, db, rdb) // Traversing multiple parents on both sides
   394  
   395  	assertCommonAncestor(a6, a9, ra9, db, rdb) // Common third parent
   396  
   397  	_, _, err = FindCommonAncestor(context.Background(), mustRef(types.NewRef(a9, types.Format_7_18)), mustRef(types.NewRef(ra9, types.Format_7_18)), rdb, db)
   398  	assert.Error(err)
   399  }
   400  
   401  func TestNewCommitRegressionTest(t *testing.T) {
   402  	storage := &chunks.TestStorage{}
   403  	db := NewDatabase(storage.NewView())
   404  	defer db.Close()
   405  
   406  	parents := mustList(types.NewList(context.Background(), db))
   407  	c1, err := NewCommit(context.Background(), types.String("one"), parents, types.EmptyStruct(types.Format_7_18))
   408  	assert.NoError(t, err)
   409  	cx, err := NewCommit(context.Background(), types.Bool(true), parents, types.EmptyStruct(types.Format_7_18))
   410  	assert.NoError(t, err)
   411  	value := types.String("two")
   412  	parents, err = types.NewList(context.Background(), db, mustRef(types.NewRef(c1, types.Format_7_18)))
   413  	assert.NoError(t, err)
   414  	meta, err := types.NewStruct(types.Format_7_18, "", types.StructData{
   415  		"basis": cx,
   416  	})
   417  	assert.NoError(t, err)
   418  
   419  	// Used to fail
   420  	_, err = NewCommit(context.Background(), value, parents, meta)
   421  	assert.NoError(t, err)
   422  }
   423  
   424  func TestPersistedCommitConsts(t *testing.T) {
   425  	// changing constants that are persisted requires a migration strategy
   426  	assert.Equal(t, "parents", ParentsField)
   427  	assert.Equal(t, "parents_list", ParentsListField)
   428  	assert.Equal(t, "value", ValueField)
   429  	assert.Equal(t, "meta", CommitMetaField)
   430  	assert.Equal(t, "Commit", CommitName)
   431  }