mirror of
https://github.com/dolthub/dolt.git
synced 2026-02-10 18:49:02 -06:00
ValueStore.Flush() now Puts all Chunks buffered in the ValueStore layer into the underlying ChunkStore. The Chunks are not persistent at this point, not until and unless the caller calls Commit() on the ChunkStore. This patch also removes ChunkStore.Flush(). The same effect can be achieved by calling ChunkStore.Commit() with the current Root for both last and current. NB: newTestValueStore is now private to the types package. The logic is that, now, outside the types package, callers need to hold onto the underlying ChunkStore if they want to persist Chunks. Toward #3404
354 lines
12 KiB
Go
354 lines
12 KiB
Go
// Copyright 2016 Attic Labs, Inc. All rights reserved.
|
|
// Licensed under the Apache License, version 2.0:
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
package diff
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/attic-labs/noms/go/chunks"
|
|
"github.com/attic-labs/noms/go/d"
|
|
"github.com/attic-labs/noms/go/marshal"
|
|
"github.com/attic-labs/noms/go/types"
|
|
"github.com/attic-labs/testify/assert"
|
|
)
|
|
|
|
func TestCommonPrefixCount(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
testCases := [][]interface{}{
|
|
{".value[#94a2oa20oka0jdv5lha03vuvvumul1vb].sizes[#316j9oc39b09fbc2qf3klenm6p1o1d7h]", 0},
|
|
{".value[#94a2oa20oka0jdv5lha03vuvvumul1vb].sizes[#77eavttned7llu1pkvhaei9a9qgcagir]", 3},
|
|
{".value[#94a2oa20oka0jdv5lha03vuvvumul1vb].sizes[#hboaq9581drq4g9jf62d3s06al3us49s]", 3},
|
|
{".value[#94a2oa20oka0jdv5lha03vuvvumul1vb].sizes[#l0hpa7sbr7qutrcfn5173kar4j2847m1]", 3},
|
|
{".value[#9vj5m3049mav94bttcujhgfdfqcavsbn].sizes[#33f6tb4h8agh57s2bqlmi9vbhlkbtmct]", 1},
|
|
{".value[#9vj5m3049mav94bttcujhgfdfqcavsbn].sizes[#a43ne9a8kotcqph4up5pqqdmr1e1qcsl]", 3},
|
|
{".value[#9vj5m3049mav94bttcujhgfdfqcavsbn].sizes[#ppqg6pem2sb64h2i2ptnh8ckj8gogj9h]", 3},
|
|
{".value[#9vj5m3049mav94bttcujhgfdfqcavsbn].sizes[#s7r2vpnqlk20sd72mg8ijerg9cmauaqo]", 3},
|
|
{".value[#bpspmmlc41pk0r144a7682oah0tmge1e].sizes[#9vuc1gg3c3eude5v3j5deqopjsobe3no]", 1},
|
|
{".value[#bpspmmlc41pk0r144a7682oah0tmge1e].sizes[#qo3gfdsf14v3dh0oer82vn1bg4o8nlsc]", 3},
|
|
{".value[#bpspmmlc41pk0r144a7682oah0tmge1e].sizes[#rlidki5ipbjdofsm2rq3a66v908m5fpl]", 3},
|
|
{".value[#bpspmmlc41pk0r144a7682oah0tmge1e].sizes[#st1n96rh89c2vgo090dt9lknd5ip4kck]", 3},
|
|
{".value[#hjh5hpn55591k0gjvgckc14erli968ao].sizes[#267889uv3mtih6fij3fhio2jiqtl6nho]", 1},
|
|
{".value[#hjh5hpn55591k0gjvgckc14erli968ao].sizes[#7ncb7guoip9e400bm2lcvr0dda29o9jn]", 3},
|
|
{".value[#hjh5hpn55591k0gjvgckc14erli968ao].sizes[#afscb0on7rt8bq6eutup8juusmid7i96]", 3},
|
|
{".value[#hjh5hpn55591k0gjvgckc14erli968ao].sizes[#drqe4lr0vdfdtmvejsjun1l3mfv6ums5]", 3},
|
|
}
|
|
|
|
var lastPath types.Path
|
|
|
|
for i, tc := range testCases {
|
|
path, expected := tc[0].(string), tc[1].(int)
|
|
p, err := types.ParsePath(path)
|
|
assert.NoError(err)
|
|
assert.Equal(expected, commonPrefixCount(lastPath, p), "failed for paths[%d]: %s", i, path)
|
|
lastPath = p
|
|
}
|
|
}
|
|
|
|
type testFunc func(parent types.Value) types.Value
|
|
type testKey struct {
|
|
X, Y int
|
|
}
|
|
|
|
var (
|
|
vm map[string]types.Value
|
|
)
|
|
|
|
func vfk(keys ...string) []types.Value {
|
|
var values []types.Value
|
|
for _, k := range keys {
|
|
values = append(values, vm[k])
|
|
}
|
|
return values
|
|
}
|
|
|
|
func testValues() map[string]types.Value {
|
|
if vm == nil {
|
|
vm = map[string]types.Value{
|
|
"k1": types.String("k1"),
|
|
"k2": types.String("k2"),
|
|
"k3": types.String("k3"),
|
|
"s1": types.String("string1"),
|
|
"s2": types.String("string2"),
|
|
"s3": types.String("string3"),
|
|
"s4": types.String("string4"),
|
|
"n1": types.Number(1),
|
|
"n2": types.Number(2),
|
|
"n3": types.Number(3.3),
|
|
"n4": types.Number(4.4),
|
|
"b1": mustMarshal(true),
|
|
"b2": mustMarshal(false),
|
|
"l1": mustMarshal([]string{}),
|
|
"l2": mustMarshal([]string{"one", "two", "three", "four"}),
|
|
"l3": mustMarshal([]string{"two", "three", "four", "five"}),
|
|
"l4": mustMarshal([]string{"two", "three", "four"}),
|
|
"l5": mustMarshal([]string{"one", "two", "three", "four", "five"}),
|
|
"l6": mustMarshal([]string{"one", "four"}),
|
|
"struct1": types.NewStruct("test1", types.StructData{"f1": types.Number(1), "f2": types.Number(2)}),
|
|
"struct2": types.NewStruct("test1", types.StructData{"f1": types.Number(11111), "f2": types.Number(2)}),
|
|
"struct3": types.NewStruct("test1", types.StructData{"f1": types.Number(1), "f2": types.Number(2), "f3": types.Number(3)}),
|
|
"struct4": types.NewStruct("test1", types.StructData{"f2": types.Number(2)}),
|
|
"m1": mustMarshal(map[string]int{}),
|
|
"m2": mustMarshal(map[string]int{"k1": 1, "k2": 2, "k3": 3}),
|
|
"m3": mustMarshal(map[string]int{"k2": 2, "k3": 3, "k4": 4}),
|
|
"m4": mustMarshal(map[string]int{"k1": 1, "k3": 3}),
|
|
"m5": mustMarshal(map[string]int{"k1": 1, "k2": 2222, "k3": 3}),
|
|
"ms1": mustMarshal(map[testKey]int{{1, 1}: 1, {2, 2}: 2, {3, 3}: 3}),
|
|
"ms2": mustMarshal(map[testKey]int{{1, 1}: 1, {4, 4}: 4, {5, 5}: 5}),
|
|
}
|
|
|
|
vm["mh1"] = types.NewMap(vfk("k1", "struct1", "k2", "l1")...)
|
|
vm["mh2"] = types.NewMap(vfk("k1", "n1", "k2", "l2", "k3", "l3")...)
|
|
vm["set1"] = types.NewSet()
|
|
vm["set2"] = types.NewSet(vfk("s1", "s2")...)
|
|
vm["set3"] = types.NewSet(vfk("s1", "s2", "s3")...)
|
|
vm["set1"] = types.NewSet(vfk("s2")...)
|
|
vm["seth1"] = types.NewSet(vfk("struct1", "struct2", "struct3")...)
|
|
vm["seth2"] = types.NewSet(vfk("struct2", "struct3")...)
|
|
vm["setj3"] = types.NewSet(vfk("struct1")...)
|
|
vm["mk1"] = types.NewMap(vfk("struct1", "s1", "struct2", "s2")...)
|
|
vm["mk2"] = types.NewMap(vfk("struct1", "s3", "struct4", "s4")...)
|
|
}
|
|
return vm
|
|
}
|
|
|
|
func getPatch(g1, g2 types.Value) Patch {
|
|
dChan := make(chan Difference)
|
|
sChan := make(chan struct{})
|
|
go func() {
|
|
Diff(g1, g2, dChan, sChan, true)
|
|
close(dChan)
|
|
}()
|
|
|
|
patch := Patch{}
|
|
for dif := range dChan {
|
|
patch = append(patch, dif)
|
|
}
|
|
return patch
|
|
}
|
|
|
|
func checkApplyPatch(assert *assert.Assertions, g1, expectedG2 types.Value, k1, k2 string) {
|
|
patch := getPatch(g1, expectedG2)
|
|
g2 := Apply(g1, patch)
|
|
assert.True(expectedG2.Equals(g2), "failed to apply diffs for k1: %s and k2: %s", k1, k2)
|
|
}
|
|
|
|
func TestPatches(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
cnt := 0
|
|
for k1, g1 := range testValues() {
|
|
for k2, expectedG2 := range testValues() {
|
|
if k1 != k2 {
|
|
cnt++
|
|
checkApplyPatch(assert, g1, expectedG2, k1, k2)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNestedLists(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
ol1 := mustMarshal([]string{"one", "two", "three", "four"})
|
|
nl1 := mustMarshal([]string{"two", "three"})
|
|
ol2 := mustMarshal([]int{2, 3})
|
|
nl2 := mustMarshal([]int{1, 2, 3, 4})
|
|
nl3 := mustMarshal([]bool{true, false, true})
|
|
g1 := types.NewList(ol1, ol2)
|
|
g2 := types.NewList(nl1, nl2, nl3)
|
|
checkApplyPatch(assert, g1, g2, "g1", "g2")
|
|
}
|
|
|
|
func TestUpdateNode(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
storage := &chunks.MemoryStorage{}
|
|
vs := types.NewValueStore(storage.NewView())
|
|
defer vs.Close()
|
|
|
|
doTest := func(pp types.PathPart, parent, ov, nv, exp types.Value, f testFunc) {
|
|
stack := &patchStack{}
|
|
se := &stackElem{path: []types.PathPart{pp}, pathPart: pp, changeType: types.DiffChangeModified, oldValue: ov, newValue: nv}
|
|
updated := stack.updateNode(se, parent)
|
|
testVal := f(updated)
|
|
assert.True(exp.Equals(testVal), "%s != %s", nv, testVal)
|
|
}
|
|
|
|
var pp types.PathPart
|
|
oldVal := types.String("Yo")
|
|
newVal := types.String("YooHoo")
|
|
|
|
s1 := types.NewStruct("TestStruct", types.StructData{"f1": types.Number(1), "f2": oldVal})
|
|
pp = types.FieldPath{Name: "f2"}
|
|
doTest(pp, s1, oldVal, newVal, newVal, func(parent types.Value) types.Value {
|
|
return parent.(types.Struct).Get("f2")
|
|
})
|
|
|
|
l1 := types.NewList(types.String("one"), oldVal, types.String("three"))
|
|
pp = types.IndexPath{Index: types.Number(1)}
|
|
doTest(pp, l1, oldVal, newVal, newVal, func(parent types.Value) types.Value {
|
|
return parent.(types.List).Get(1)
|
|
})
|
|
|
|
m1 := types.NewMap(types.String("k1"), types.Number(1), types.String("k2"), oldVal)
|
|
pp = types.IndexPath{Index: types.String("k2")}
|
|
doTest(pp, m1, oldVal, newVal, newVal, func(parent types.Value) types.Value {
|
|
return parent.(types.Map).Get(types.String("k2"))
|
|
})
|
|
|
|
k1 := types.NewStruct("Sizes", types.StructData{"height": types.Number(200), "width": types.Number(300)})
|
|
vs.WriteValue(k1)
|
|
m1 = types.NewMap(k1, oldVal)
|
|
pp = types.HashIndexPath{Hash: k1.Hash()}
|
|
doTest(pp, m1, oldVal, newVal, newVal, func(parent types.Value) types.Value {
|
|
return parent.(types.Map).Get(k1)
|
|
})
|
|
|
|
set1 := types.NewSet(oldVal, k1)
|
|
pp = types.IndexPath{Index: oldVal}
|
|
exp := types.NewSet(newVal, k1)
|
|
doTest(pp, set1, oldVal, newVal, exp, func(parent types.Value) types.Value {
|
|
return parent
|
|
})
|
|
|
|
k2 := types.NewStruct("Sizes", types.StructData{"height": types.Number(300), "width": types.Number(500)})
|
|
set1 = types.NewSet(oldVal, k1)
|
|
pp = types.HashIndexPath{Hash: k1.Hash()}
|
|
exp = types.NewSet(oldVal, k2)
|
|
doTest(pp, set1, k1, k2, exp, func(parent types.Value) types.Value {
|
|
return parent
|
|
})
|
|
}
|
|
|
|
func checkApplyDiffs(a *assert.Assertions, n1, n2 types.Value, leftRight bool) {
|
|
dChan := make(chan Difference)
|
|
sChan := make(chan struct{})
|
|
go func() {
|
|
Diff(n1, n2, dChan, sChan, leftRight)
|
|
close(dChan)
|
|
}()
|
|
|
|
difs := Patch{}
|
|
for dif := range dChan {
|
|
difs = append(difs, dif)
|
|
}
|
|
|
|
res := Apply(n1, difs)
|
|
a.True(n2.Equals(res))
|
|
}
|
|
|
|
func tryApplyDiff(a *assert.Assertions, a1, a2 interface{}) {
|
|
n1 := mustMarshal(a1)
|
|
n2 := mustMarshal(a2)
|
|
|
|
checkApplyDiffs(a, n1, n2, true)
|
|
checkApplyDiffs(a, n1, n2, false)
|
|
checkApplyDiffs(a, n2, n1, true)
|
|
checkApplyDiffs(a, n2, n1, false)
|
|
}
|
|
|
|
func TestUpdateList(t *testing.T) {
|
|
a := assert.New(t)
|
|
|
|
// insert at beginning
|
|
a1 := []interface{}{"five", "ten", "fifteen"}
|
|
a2 := []interface{}{"one", "two", "three", "five", "ten", "fifteen"}
|
|
tryApplyDiff(a, a1, a2)
|
|
|
|
// append at end
|
|
a1 = []interface{}{"five", "ten", "fifteen"}
|
|
a2 = []interface{}{"five", "ten", "fifteen", "twenty", "twenty-five"}
|
|
tryApplyDiff(a, a1, a2)
|
|
|
|
// insert interleaved
|
|
a1 = []interface{}{"one", "three", "five", "seven"}
|
|
a2 = []interface{}{"one", "two", "three", "four", "five", "six", "seven"}
|
|
tryApplyDiff(a, a1, a2)
|
|
|
|
// delete from beginning and append to end
|
|
a1 = []interface{}{"one", "two", "three", "four", "five"}
|
|
a2 = []interface{}{"four", "five", "six", "seven"}
|
|
tryApplyDiff(a, a1, a2)
|
|
|
|
// replace entries at beginning
|
|
a1 = []interface{}{"one", "two", "three", "four", "five"}
|
|
a2 = []interface{}{"3.5", "four", "five"}
|
|
tryApplyDiff(a, a1, a2)
|
|
|
|
// replace entries at end
|
|
a1 = []interface{}{"one", "two", "three"}
|
|
a2 = []interface{}{"one", "four"}
|
|
tryApplyDiff(a, a1, a2)
|
|
|
|
// insert at beginning, replace at end
|
|
a1 = []interface{}{"five", "ten", "fifteen"}
|
|
a2 = []interface{}{"one", "two", "five", "eight", "eleven", "sixteen", "twenty"}
|
|
tryApplyDiff(a, a1, a2)
|
|
|
|
// remove everything
|
|
a1 = []interface{}{"five", "ten", "fifteen"}
|
|
a2 = []interface{}{}
|
|
tryApplyDiff(a, a1, a2)
|
|
}
|
|
|
|
func TestUpdateMap(t *testing.T) {
|
|
a := assert.New(t)
|
|
|
|
// insertions, deletions, and replacements
|
|
a1 := map[string]int{"five": 5, "ten": 10, "fifteen": 15, "twenty": 20}
|
|
a2 := map[string]int{"one": 1, "two": 2, "three": 3, "five": 5, "ten": 10, "fifteen": 15, "twenty": 2020}
|
|
tryApplyDiff(a, a1, a2)
|
|
|
|
// delete everything
|
|
a1 = map[string]int{"five": 5, "ten": 10, "fifteen": 15, "twenty": 20}
|
|
a2 = map[string]int{}
|
|
tryApplyDiff(a, a1, a2)
|
|
}
|
|
|
|
func TestUpdateStruct(t *testing.T) {
|
|
a := assert.New(t)
|
|
|
|
a1 := types.NewStruct("tStruct", types.StructData{
|
|
"f1": types.Number(1),
|
|
"f2": types.String("two"),
|
|
"f3": mustMarshal([]string{"one", "two", "three"}),
|
|
})
|
|
a2 := types.NewStruct("tStruct", types.StructData{
|
|
"f1": types.Number(2),
|
|
"f2": types.String("twotwo"),
|
|
"f3": mustMarshal([]interface{}{0, "one", 1, "two", 2, "three", 3}),
|
|
})
|
|
checkApplyDiffs(a, a1, a2, true)
|
|
checkApplyDiffs(a, a1, a2, false)
|
|
|
|
a2 = types.NewStruct("tStruct", types.StructData{
|
|
"f1": types.Number(2),
|
|
"f2": types.String("two"),
|
|
"f3": mustMarshal([]interface{}{0, "one", 1, "two", 2, "three", 3}),
|
|
"f4": types.Bool(true),
|
|
})
|
|
checkApplyDiffs(a, a1, a2, true)
|
|
checkApplyDiffs(a, a1, a2, false)
|
|
}
|
|
|
|
func TestUpdateSet(t *testing.T) {
|
|
a := assert.New(t)
|
|
|
|
a1 := types.NewSet(types.Number(1), types.String("two"), mustMarshal([]string{"one", "two", "three"}))
|
|
a2 := types.NewSet(types.Number(3), types.String("three"), mustMarshal([]string{"one", "two", "three", "four"}))
|
|
|
|
checkApplyDiffs(a, a1, a2, true)
|
|
checkApplyDiffs(a, a1, a2, false)
|
|
checkApplyDiffs(a, a2, a1, true)
|
|
checkApplyDiffs(a, a2, a1, false)
|
|
}
|
|
|
|
func mustMarshal(v interface{}) types.Value {
|
|
v1, err := marshal.Marshal(v)
|
|
d.Chk.NoError(err)
|
|
return v1
|
|
}
|