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 }