Add tree tests and benchmarks
This commit is contained in:
parent
0d6bbd9258
commit
fb64d4d840
@ -6,14 +6,19 @@ import (
|
||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/account"
|
||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb"
|
||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/storage"
|
||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/tree"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type IterFunc = func(record *Record) (IsContinue bool)
|
||||
|
||||
type RWLocker interface {
|
||||
sync.Locker
|
||||
RLock()
|
||||
RUnlock()
|
||||
}
|
||||
|
||||
type ACLList interface {
|
||||
tree.RWLocker
|
||||
RWLocker
|
||||
ID() string
|
||||
Header() *aclpb.Header
|
||||
ACLState() *ACLState
|
||||
|
||||
@ -15,7 +15,6 @@ const (
|
||||
Nothing
|
||||
)
|
||||
|
||||
// TODO: consider abstracting into separate package with iterator, remove
|
||||
type Tree struct {
|
||||
root *Change
|
||||
headIds []string
|
||||
@ -155,7 +154,6 @@ func (t *Tree) add(c *Change) (attached bool) {
|
||||
}
|
||||
// attaching only if all prev ids are attached
|
||||
attached = true
|
||||
// the logic below is the following
|
||||
for _, pid := range c.PreviousIds {
|
||||
if _, ok := t.attached[pid]; ok {
|
||||
continue
|
||||
@ -210,7 +208,7 @@ func (t *Tree) attach(c *Change, newEl bool) {
|
||||
break
|
||||
}
|
||||
}
|
||||
prev.Next = append(prev.Next[:insertIdx+1], prev.Next[:insertIdx]...)
|
||||
prev.Next = append(prev.Next[:insertIdx+1], prev.Next[insertIdx:]...)
|
||||
prev.Next[insertIdx] = c
|
||||
}
|
||||
}
|
||||
|
||||
214
pkg/acl/tree/tree_test.go
Normal file
214
pkg/acl/tree/tree_test.go
Normal file
@ -0,0 +1,214 @@
|
||||
package tree
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func newChange(id string, snapshotId string, prevIds ...string) *Change {
|
||||
return &Change{
|
||||
PreviousIds: prevIds,
|
||||
Id: id,
|
||||
SnapshotId: snapshotId,
|
||||
IsSnapshot: false,
|
||||
}
|
||||
}
|
||||
|
||||
func newSnapshot(id string, snapshotId string, prevIds ...string) *Change {
|
||||
return &Change{
|
||||
PreviousIds: prevIds,
|
||||
Id: id,
|
||||
SnapshotId: snapshotId,
|
||||
IsSnapshot: true,
|
||||
}
|
||||
}
|
||||
|
||||
func TestTree_Add(t *testing.T) {
|
||||
t.Run("add first el", func(t *testing.T) {
|
||||
tr := new(Tree)
|
||||
assert.Equal(t, Rebuild, tr.Add(newSnapshot("root", "")))
|
||||
assert.Equal(t, tr.root.Id, "root")
|
||||
assert.Equal(t, []string{"root"}, tr.Heads())
|
||||
})
|
||||
t.Run("linear add", func(t *testing.T) {
|
||||
tr := new(Tree)
|
||||
assert.Equal(t, Rebuild, tr.Add(
|
||||
newSnapshot("root", ""),
|
||||
newChange("one", "root", "root"),
|
||||
newChange("two", "root", "one"),
|
||||
))
|
||||
assert.Equal(t, []string{"two"}, tr.Heads())
|
||||
assert.Equal(t, Append, tr.Add(newChange("three", "root", "two")))
|
||||
el := tr.root
|
||||
var ids []string
|
||||
for el != nil {
|
||||
ids = append(ids, el.Id)
|
||||
if len(el.Next) > 0 {
|
||||
el = el.Next[0]
|
||||
} else {
|
||||
el = nil
|
||||
}
|
||||
}
|
||||
assert.Equal(t, []string{"root", "one", "two", "three"}, ids)
|
||||
assert.Equal(t, []string{"three"}, tr.Heads())
|
||||
})
|
||||
t.Run("branch", func(t *testing.T) {
|
||||
tr := new(Tree)
|
||||
assert.Equal(t, Rebuild, tr.Add(
|
||||
newSnapshot("root", ""),
|
||||
newChange("1", "root", "root"),
|
||||
newChange("2", "root", "1"),
|
||||
))
|
||||
assert.Equal(t, []string{"2"}, tr.Heads())
|
||||
assert.Equal(t, Rebuild, tr.Add(
|
||||
newChange("1.2", "root", "1.1"),
|
||||
newChange("1.3", "root", "1.2"),
|
||||
newChange("1.1", "root", "1"),
|
||||
))
|
||||
assert.Len(t, tr.attached["1"].Next, 2)
|
||||
assert.Len(t, tr.unAttached, 0)
|
||||
assert.Len(t, tr.attached, 6)
|
||||
assert.Equal(t, []string{"1.3", "2"}, tr.Heads())
|
||||
})
|
||||
t.Run("branch union", func(t *testing.T) {
|
||||
tr := new(Tree)
|
||||
assert.Equal(t, Rebuild, tr.Add(
|
||||
newSnapshot("root", ""),
|
||||
newChange("1", "root", "root"),
|
||||
newChange("2", "root", "1"),
|
||||
newChange("1.2", "root", "1.1"),
|
||||
newChange("1.3", "root", "1.2"),
|
||||
newChange("1.1", "root", "1"),
|
||||
newChange("3", "root", "2", "1.3"),
|
||||
newChange("4", "root", "3"),
|
||||
))
|
||||
assert.Len(t, tr.unAttached, 0)
|
||||
assert.Len(t, tr.attached, 8)
|
||||
assert.Equal(t, []string{"4"}, tr.Heads())
|
||||
})
|
||||
t.Run("big set", func(t *testing.T) {
|
||||
tr := new(Tree)
|
||||
tr.Add(newSnapshot("root", ""))
|
||||
var changes []*Change
|
||||
for i := 0; i < 10000; i++ {
|
||||
if i == 0 {
|
||||
changes = append(changes, newChange(fmt.Sprint(i), "root", "root"))
|
||||
} else {
|
||||
changes = append(changes, newChange(fmt.Sprint(i), "root", fmt.Sprint(i-1)))
|
||||
}
|
||||
}
|
||||
st := time.Now()
|
||||
tr.AddFast(changes...)
|
||||
t.Log(time.Since(st))
|
||||
assert.Equal(t, []string{"9999"}, tr.Heads())
|
||||
})
|
||||
}
|
||||
|
||||
func TestTree_Hash(t *testing.T) {
|
||||
tr := new(Tree)
|
||||
tr.Add(newSnapshot("root", ""))
|
||||
hash1 := tr.Hash()
|
||||
assert.Equal(t, tr.Hash(), hash1)
|
||||
tr.Add(newChange("1", "root", "root"))
|
||||
assert.NotEqual(t, tr.Hash(), hash1)
|
||||
assert.Equal(t, tr.Hash(), tr.Hash())
|
||||
}
|
||||
|
||||
func TestTree_AddFuzzy(t *testing.T) {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
getChanges := func() []*Change {
|
||||
changes := []*Change{
|
||||
newChange("1", "root", "root"),
|
||||
newChange("2", "root", "1"),
|
||||
newChange("1.2", "root", "1.1"),
|
||||
newChange("1.3", "root", "1.2"),
|
||||
newChange("1.1", "root", "1"),
|
||||
newChange("3", "root", "2", "1.3"),
|
||||
}
|
||||
rand.Shuffle(len(changes), func(i, j int) {
|
||||
changes[i], changes[j] = changes[j], changes[i]
|
||||
})
|
||||
return changes
|
||||
}
|
||||
var phash string
|
||||
for i := 0; i < 100; i++ {
|
||||
tr := new(Tree)
|
||||
tr.Add(newSnapshot("root", ""))
|
||||
tr.Add(getChanges()...)
|
||||
assert.Len(t, tr.unAttached, 0)
|
||||
assert.Len(t, tr.attached, 7)
|
||||
hash := tr.Hash()
|
||||
if phash != "" {
|
||||
assert.Equal(t, phash, hash)
|
||||
}
|
||||
phash = hash
|
||||
assert.Equal(t, []string{"3"}, tr.Heads())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTree_Iterate(t *testing.T) {
|
||||
t.Run("complex tree", func(t *testing.T) {
|
||||
tr := new(Tree)
|
||||
tr.Add(
|
||||
newSnapshot("0", ""),
|
||||
newChange("1", "0", "0"),
|
||||
newChange("1.1", "0", "1"),
|
||||
newChange("1.2", "0", "1"),
|
||||
newChange("1.3", "0", "1"),
|
||||
newChange("1.3.1", "0", "1.3"),
|
||||
newChange("1.2+3", "0", "1.2", "1.3.1"),
|
||||
newChange("1.2+3.1", "0", "1.2+3"),
|
||||
newChange("10", "0", "1.2+3.1", "1.1"),
|
||||
newChange("last", "0", "10"),
|
||||
)
|
||||
var res []string
|
||||
tr.Iterate("0", func(c *Change) (isContinue bool) {
|
||||
res = append(res, c.Id)
|
||||
return true
|
||||
})
|
||||
res = res[:0]
|
||||
tr.Iterate("0", func(c *Change) (isContinue bool) {
|
||||
res = append(res, c.Id)
|
||||
return true
|
||||
})
|
||||
assert.Equal(t, []string{"0", "1", "1.1", "1.2", "1.3", "1.3.1", "1.2+3", "1.2+3.1", "10", "last"}, res)
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkTree_Add(b *testing.B) {
|
||||
getChanges := func() []*Change {
|
||||
return []*Change{
|
||||
newChange("1", "root", "root"),
|
||||
newChange("2", "root", "1"),
|
||||
newChange("1.2", "root", "1.1"),
|
||||
newChange("1.3", "root", "1.2"),
|
||||
newChange("1.1", "root", "1"),
|
||||
newChange("3", "root", "2", "1.3"),
|
||||
}
|
||||
}
|
||||
b.Run("by one", func(b *testing.B) {
|
||||
tr := new(Tree)
|
||||
tr.Add(newSnapshot("root", ""))
|
||||
tr.Add(getChanges()...)
|
||||
for i := 0; i < b.N; i++ {
|
||||
tr.Add(newChange(fmt.Sprint(i+4), "root", fmt.Sprint(i+3)))
|
||||
}
|
||||
})
|
||||
b.Run("add", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
tr := new(Tree)
|
||||
tr.Add(newSnapshot("root", ""))
|
||||
tr.Add(getChanges()...)
|
||||
}
|
||||
})
|
||||
b.Run("add fast", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
tr := new(Tree)
|
||||
tr.AddFast(newSnapshot("root", ""))
|
||||
tr.AddFast(getChanges()...)
|
||||
}
|
||||
})
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user