1
0
Fork 0

Adding upstream version 3.10.8.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-18 09:37:23 +02:00
parent 37e9b6d587
commit 03bfe4079e
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
356 changed files with 28857 additions and 0 deletions

View file

@ -0,0 +1,74 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT
package generic
import (
"context"
"testing"
"code.forgejo.org/f3/gof3/v3/id"
"code.forgejo.org/f3/gof3/v3/kind"
"code.forgejo.org/f3/gof3/v3/tree/generic"
"code.forgejo.org/f3/gof3/v3/tree/memory"
"github.com/stretchr/testify/assert"
)
func TestCompare(t *testing.T) {
ctx := context.Background()
aTree := NewMemoryTree(ctx, "O")
testTreeBuild(t, aTree, 2)
bTree := NewMemoryTree(ctx, "O")
testTreeBuild(t, bTree, 2)
assert.True(t, generic.TreeCompare(ctx, aTree, generic.NewPathFromString(""), bTree, generic.NewPathFromString("")))
{
toDelete := generic.NewPathFromString("/O-A/O-B")
aTree.Find(toDelete).Delete(ctx)
assert.False(t, generic.TreeCompare(ctx, aTree, generic.NewPathFromString(""), bTree, generic.NewPathFromString("")))
bTree.Find(toDelete).Delete(ctx)
assert.True(t, generic.TreeCompare(ctx, aTree, generic.NewPathFromString(""), bTree, generic.NewPathFromString("")))
}
{
content := "OTHER CONTENT"
toModify := generic.NewPathFromString("/O-A/O-F")
aNode := aTree.Find(toModify)
memory.SetContent(aNode, content)
aNode.Upsert(ctx)
assert.False(t, generic.TreeCompare(ctx, aTree, generic.NewPathFromString(""), bTree, generic.NewPathFromString("")))
bNode := bTree.Find(toModify)
memory.SetContent(bNode, content)
bNode.Upsert(ctx)
assert.True(t, generic.TreeCompare(ctx, aTree, generic.NewPathFromString(""), bTree, generic.NewPathFromString("")))
}
{
toModify := generic.NewPathFromString("/O-A/O-F")
aTree.Find(toModify).SetKind(kind.Kind("???"))
assert.False(t, generic.TreeCompare(ctx, aTree, generic.NewPathFromString(""), bTree, generic.NewPathFromString("")))
bTree.Find(toModify).SetKind(kind.Kind("???"))
assert.True(t, generic.TreeCompare(ctx, aTree, generic.NewPathFromString(""), bTree, generic.NewPathFromString("")))
}
{
pathToMap := generic.NewPathFromString("/O-A/O-J/O-M")
mappedID := id.NewNodeID("MAPPED")
aTree.Find(pathToMap).SetMappedID(mappedID)
assert.False(t, generic.TreeCompare(ctx, aTree, generic.NewPathFromString(""), bTree, generic.NewPathFromString("")))
bNode := bTree.Find(pathToMap).Delete(ctx)
bNode.SetID(mappedID)
parentPathToMap := generic.NewPathFromString("/O-A/O-J")
bTree.Find(parentPathToMap).SetChild(bNode)
assert.True(t, generic.TreeCompare(ctx, aTree, generic.NewPathFromString(""), bTree, generic.NewPathFromString("")))
}
}

View file

@ -0,0 +1,193 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT
package generic
import (
"context"
"sort"
"testing"
"code.forgejo.org/f3/gof3/v3/id"
"code.forgejo.org/f3/gof3/v3/kind"
"code.forgejo.org/f3/gof3/v3/path"
"code.forgejo.org/f3/gof3/v3/tree/generic"
"code.forgejo.org/f3/gof3/v3/tree/memory"
"github.com/stretchr/testify/assert"
)
func NewMemoryTree(ctx context.Context, name string) generic.TreeInterface {
return generic.GetFactory("memory")(ctx, memory.NewOptions(memory.NewIDAllocatorGenerator(name)))
}
func TestMemoryTreeIDAllocator(t *testing.T) {
ctx := context.Background()
name := "T"
id := "THEID"
for _, testCase := range []struct {
idAllocator memory.IDAllocatorInterface
setID bool
expectedID string
}{
{
idAllocator: memory.NewIDAllocatorGenerator(name),
setID: false,
expectedID: name + "-A",
},
{
idAllocator: memory.NewIDAllocatorNull(),
setID: true,
expectedID: id,
},
} {
t.Run(testCase.expectedID, func(t *testing.T) {
tree := generic.GetFactory("memory")(ctx, memory.NewOptions(testCase.idAllocator))
node := tree.GetRoot().CreateChild(ctx)
f := memory.NewFormat("")
if testCase.setID {
f.SetID(id)
}
node.FromFormat(f)
node.Upsert(ctx)
assert.EqualValues(t, testCase.expectedID, node.GetID())
})
}
}
func testTreeBuild(t *testing.T, tree generic.TreeInterface, maxDepth int) {
ctx := context.Background()
insert := func(tree generic.TreeInterface, parent generic.NodeInterface) generic.NodeInterface {
node := parent.CreateChild(ctx)
node.Upsert(ctx)
memory.SetContent(node, "content "+node.GetID().String())
node.Upsert(ctx)
return node
}
var populate func(depth int, tree generic.TreeInterface, parent generic.NodeInterface)
populate = func(depth int, tree generic.TreeInterface, parent generic.NodeInterface) {
if depth >= maxDepth {
return
}
depth++
for i := 1; i <= 3; i++ {
node := insert(tree, parent)
populate(depth, tree, node)
}
}
populate(0, tree, insert(tree, tree.GetRoot()))
}
func TestMemoryTreeBuild(t *testing.T) {
ctx := context.Background()
verify := func(tree generic.TreeInterface, expected []string) {
collected := make([]string, 0, 10)
collect := func(ctx context.Context, p path.Path, node generic.NodeInterface) {
if node.GetKind() == kind.KindRoot {
return
}
p = p.Append(node)
collected = append(collected, p.String()+":"+memory.GetContent(node))
}
tree.WalkAndGet(ctx, generic.NewWalkOptions(collect))
sort.Strings(expected)
sort.Strings(collected)
assert.EqualValues(t, expected, collected)
}
for _, testCase := range []struct {
name string
build func(tree generic.TreeInterface)
operations func(tree generic.TreeInterface)
expected []string
}{
{
name: "full tree",
build: func(tree generic.TreeInterface) { testTreeBuild(t, tree, 2) },
operations: func(tree generic.TreeInterface) {},
expected: []string{"/T-A/T-B/T-C:content T-C", "/T-A/T-B/T-D:content T-D", "/T-A/T-B/T-E:content T-E", "/T-A/T-B:content T-B", "/T-A/T-F/T-G:content T-G", "/T-A/T-F/T-H:content T-H", "/T-A/T-F/T-I:content T-I", "/T-A/T-F:content T-F", "/T-A/T-J/T-K:content T-K", "/T-A/T-J/T-L:content T-L", "/T-A/T-J/T-M:content T-M", "/T-A/T-J:content T-J", "/T-A:content T-A"},
},
{
name: "scenario 1",
build: func(tree generic.TreeInterface) { testTreeBuild(t, tree, 2) },
operations: func(tree generic.TreeInterface) {
root := tree.GetRoot()
id0 := id.NewNodeID("T-A")
root.List(ctx)
zero := root.GetChild(id0)
assert.False(t, generic.NilNode == zero)
assert.True(t, zero == zero.Get(ctx))
id1 := id.NewNodeID("T-B")
zero.List(ctx)
one := zero.GetChild(id1)
assert.False(t, generic.NilNode == one)
one.Get(ctx)
memory.SetContent(one, "other one")
one.Upsert(ctx)
id2 := id.NewNodeID("T-F")
two := zero.GetChild(id2)
two.Delete(ctx)
two.Delete(ctx)
assert.True(t, generic.NilNode == zero.GetChild(id2))
},
expected: []string{"/T-A/T-B/T-C:content T-C", "/T-A/T-B/T-D:content T-D", "/T-A/T-B/T-E:content T-E", "/T-A/T-B:other one", "/T-A/T-J/T-K:content T-K", "/T-A/T-J/T-L:content T-L", "/T-A/T-J/T-M:content T-M", "/T-A/T-J:content T-J", "/T-A:content T-A"},
},
{
name: "scenario 2",
build: func(tree generic.TreeInterface) { testTreeBuild(t, tree, 0) },
operations: func(tree generic.TreeInterface) {
root := tree.GetRoot()
id0 := id.NewNodeID("T-A")
root.List(ctx)
zero := root.GetChild(id0)
assert.False(t, generic.NilNode == zero)
zero.Get(ctx)
one := zero.CreateChild(ctx)
one.Upsert(ctx)
memory.SetContent(one, "ONE")
one.Upsert(ctx)
two := one.CreateChild(ctx)
two.Upsert(ctx)
memory.SetContent(two, "SOMETHING")
two.Upsert(ctx)
memory.SetContent(two, "ONE/TWO")
two.Upsert(ctx)
one.DeleteChild(two.GetID())
two.Get(ctx)
three := two.CreateChild(ctx)
three.Upsert(ctx)
memory.SetContent(three, "ONE/THREE")
three.Upsert(ctx)
three.Delete(ctx)
},
expected: []string{"/T-A/T-B/T-C:ONE/TWO", "/T-A/T-B:ONE", "/T-A:content T-A"},
},
} {
t.Run(testCase.name, func(t *testing.T) {
tree := NewMemoryTree(ctx, "T")
tree.Trace("========== BUILD")
testCase.build(tree)
tree.Trace("========== OPERATIONS")
testCase.operations(tree)
verify(tree, testCase.expected)
tree.Trace("========== VERIFY RELOAD")
tree.Clear(ctx)
verify(tree, testCase.expected)
})
}
}

View file

@ -0,0 +1,98 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT
package generic
import (
"context"
"testing"
"code.forgejo.org/f3/gof3/v3/path"
"code.forgejo.org/f3/gof3/v3/tree/generic"
"code.forgejo.org/f3/gof3/v3/tree/memory"
"github.com/stretchr/testify/assert"
)
type testReference struct {
originPath string
originReference string
destinationPath string
destinationReference string
}
func TestMirror(t *testing.T) {
ctx := context.Background()
for _, testCase := range []struct {
start string
references []testReference
expected []string
}{
{
start: "/",
expected: []string{":ROOT:", "/D-A:content O-A:", "/D-A/D-B:content O-B:", "/D-A/D-B/D-C:content O-C:", "/D-A/D-B/D-D:content O-D:", "/D-A/D-B/D-E:content O-E:", "/D-A/D-F:content O-F:", "/D-A/D-F/D-G:content O-G:", "/D-A/D-F/D-H:content O-H:", "/D-A/D-F/D-I:content O-I:", "/D-A/D-J:content O-J:", "/D-A/D-J/D-K:content O-K:", "/D-A/D-J/D-L:content O-L:", "/D-A/D-J/D-M:content O-M:"},
},
{
start: "/O-A/O-B",
references: []testReference{
{
originPath: "/O-A/O-B/O-C",
originReference: "/O-A/O-F",
destinationPath: "/D-A/D-B/D-D",
destinationReference: "/D-A/D-C",
},
},
expected: []string{":ROOT:", "/D-A:content O-A:", "/D-A/D-B:content O-B:", "/D-A/D-B/D-D:content O-C:/D-A/D-C", "/D-A/D-B/D-E:content O-D:", "/D-A/D-B/D-F:content O-E:", "/D-A/D-C:content O-F:"},
},
{
start: "/O-A/O-F",
references: []testReference{
{
originPath: "/O-A/O-F/O-G",
originReference: "../../O-J",
destinationPath: "/D-A/D-B/D-D",
destinationReference: "../../D-C",
},
},
expected: []string{":ROOT:", "/D-A:content O-A:", "/D-A/D-B:content O-F:", "/D-A/D-B/D-D:content O-G:../../D-C", "/D-A/D-B/D-E:content O-H:", "/D-A/D-B/D-F:content O-I:", "/D-A/D-C:content O-J:"},
},
} {
t.Run(" "+testCase.start, func(t *testing.T) {
originTree := NewMemoryTree(ctx, "O")
log := originTree.GetLogger()
log.Trace("=========== build")
testTreeBuild(t, originTree, 2)
for _, c := range testCase.references {
log.Trace("=========== inject reference %s", c.originReference)
assert.True(t, originTree.Apply(ctx, generic.NewPathFromString(c.originPath), generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
memory.SetRef(node, c.originReference)
})))
}
log.Trace("=========== mirror")
destinationTree := NewMemoryTree(ctx, "D")
generic.TreeMirror(ctx, originTree, destinationTree, generic.NewPathFromString(testCase.start), generic.NewMirrorOptions())
log.Trace("=========== verify")
collected := make([]string, 0, 10)
collect := func(ctx context.Context, parent path.Path, node generic.NodeInterface) {
collected = append(collected, node.GetCurrentPath().String()+":"+memory.GetContent(node)+":"+memory.GetRef(node))
}
destinationTree.Walk(ctx, generic.NewWalkOptions(collect))
assert.EqualValues(t, testCase.expected, collected)
for _, c := range testCase.references {
log.Trace("=========== look for reference %s", c.destinationReference)
var called bool
assert.True(t, destinationTree.Apply(ctx, generic.NewPathFromString(c.destinationPath), generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
assert.EqualValues(t, c.destinationReference, memory.GetRef(node))
called = true
})))
assert.True(t, called)
}
})
}
}

View file

@ -0,0 +1,341 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT
package generic
import (
"context"
"fmt"
"sort"
"testing"
"code.forgejo.org/f3/gof3/v3/path"
"code.forgejo.org/f3/gof3/v3/tree/generic"
"code.forgejo.org/f3/gof3/v3/tree/memory"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestBothApplyWalk(t *testing.T) {
ctx := context.Background()
tree := NewMemoryTree(ctx, "T")
for _, testCase := range []struct {
path string
expected []string
}{
{
path: "/T-A",
expected: []string{"/T-A", "/T-A/T-B", "/T-A/T-B/T-C", "/T-A/T-B/T-D", "/T-A/T-B/T-E", "/T-A/T-F", "/T-A/T-F/T-G", "/T-A/T-F/T-H", "/T-A/T-F/T-I", "/T-A/T-J", "/T-A/T-J/T-K", "/T-A/T-J/T-L", "/T-A/T-J/T-M"},
},
{
path: "/T-A/T-B",
expected: []string{"/T-A/T-B", "/T-A/T-B/T-C", "/T-A/T-B/T-D", "/T-A/T-B/T-E"},
},
{
path: "/T-A/T-B/T-C",
expected: []string{"/T-A/T-B/T-C"},
},
} {
testTreeBuild(t, tree, 2)
collected := make([]string, 0, 10)
p := generic.NewPathFromString(testCase.path)
walk := func(ctx context.Context, parent, p path.Path, node generic.NodeInterface) {
collect := func(ctx context.Context, p path.Path, node generic.NodeInterface) {
p = p.Append(node)
collected = append(collected, p.String())
}
node.Walk(ctx, parent, generic.NewWalkOptions(collect))
}
assert.True(t, tree.Apply(ctx, p, generic.NewApplyOptions(walk)))
sort.Strings(testCase.expected)
sort.Strings(collected)
assert.EqualValues(t, testCase.expected, collected)
}
}
func TestWalk(t *testing.T) {
ctx := context.Background()
tree := NewMemoryTree(ctx, "T")
expected := []string{"", "/T-A", "/T-A/T-B", "/T-A/T-B/T-C", "/T-A/T-B/T-D", "/T-A/T-B/T-E", "/T-A/T-F", "/T-A/T-F/T-G", "/T-A/T-F/T-H", "/T-A/T-F/T-I", "/T-A/T-J", "/T-A/T-J/T-K", "/T-A/T-J/T-L", "/T-A/T-J/T-M"}
testTreeBuild(t, tree, 2)
collected := make([]string, 0, 10)
collect := func(ctx context.Context, p path.Path, node generic.NodeInterface) {
p = p.Append(node)
collected = append(collected, p.String())
}
tree.Walk(ctx, generic.NewWalkOptions(collect))
sort.Strings(expected)
sort.Strings(collected)
assert.EqualValues(t, expected, collected)
}
func TestWalkAndGet(t *testing.T) {
ctx := context.Background()
tree := NewMemoryTree(ctx, "T")
testTreeBuild(t, tree, 1)
tree.Clear(ctx)
collected := make([]string, 0, 10)
collect := func(ctx context.Context, p path.Path, node generic.NodeInterface) {
p = p.Append(node)
collected = append(collected, p.String())
}
tree.Walk(ctx, generic.NewWalkOptions(collect))
assert.EqualValues(t, []string{""}, collected)
collected = make([]string, 0, 10)
tree.WalkAndGet(ctx, generic.NewWalkOptions(collect))
expected := []string{"", "/T-A", "/T-A/T-B", "/T-A/T-C", "/T-A/T-D"}
sort.Strings(expected)
sort.Strings(collected)
assert.EqualValues(t, expected, collected)
}
func TestApplyVisitID(t *testing.T) {
ctx := context.Background()
tree := NewMemoryTree(ctx, "T")
testTreeBuild(t, tree, 2)
for _, testPath := range []string{"/T-A", "/T-A/T-B", "/T-A/T-B/T-C"} {
collected := make([]string, 0, 10)
p := generic.NewPathFromString(testPath)
collect := func(ctx context.Context, parent, p path.Path, node generic.NodeInterface) {
parent = parent.Append(node)
assert.False(t, node.GetIsNil(), node.String())
assert.EqualValues(t, parent.Length(), node.GetCurrentPath().Length())
expected := parent.PathString().Join()
actual := node.GetCurrentPath().String()
assert.EqualValues(t, actual, expected)
collected = append(collected, parent.String())
}
assert.True(t, tree.Apply(ctx, p, generic.NewApplyOptions(collect)))
if assert.EqualValues(t, 1, len(collected)) {
assert.EqualValues(t, testPath, collected[0])
}
}
p := generic.NewPathFromString("/1/2/3/4")
called := false
assert.False(t, tree.Apply(ctx, p, generic.NewApplyOptions(func(context.Context, path.Path, path.Path, generic.NodeInterface) { called = true })))
assert.False(t, called)
}
func TestApplyVisitByName(t *testing.T) {
ctx := context.Background()
tree := NewMemoryTree(ctx, "T")
testTreeBuild(t, tree, 2)
for _, testCase := range []struct {
path string
expected string
}{
{
path: "/T-A/content T-B/T-C",
expected: "/T-A/T-B/T-C",
},
{
path: "/T-A/content T-B/content T-C",
expected: "/T-A/T-B/T-C",
},
{
path: "/content T-A/content T-B/content T-C",
expected: "/T-A/T-B/T-C",
},
} {
collected := make([]string, 0, 10)
p := generic.NewPathFromString(testCase.path)
collect := func(ctx context.Context, parent, p path.Path, node generic.NodeInterface) {
parent = parent.Append(node)
assert.False(t, node.GetIsNil(), node.String())
assert.EqualValues(t, parent.Length(), node.GetCurrentPath().Length())
expected := parent.PathString().Join()
actual := node.GetCurrentPath().String()
assert.EqualValues(t, actual, expected)
collected = append(collected, parent.String())
}
assert.True(t, tree.Apply(ctx, p, generic.NewApplyOptions(collect).SetSearch(generic.ApplySearchByName)))
if assert.EqualValues(t, 1, len(collected)) {
assert.EqualValues(t, testCase.expected, collected[0])
}
}
}
func TestApplyAndGet(t *testing.T) {
ctx := context.Background()
for _, testCase := range []struct {
path string
expected []string
}{
{
path: "/T-A",
expected: []string{"", "/T-A"},
},
{
path: "/T-A/T-B",
expected: []string{"", "/T-A", "/T-A/T-B"},
},
{
path: "/T-A/T-B/T-C",
expected: []string{"", "/T-A", "/T-A/T-B", "/T-A/T-B/T-C"},
},
} {
tree := NewMemoryTree(ctx, "T")
testTreeBuild(t, tree, 2)
tree.Clear(ctx)
p := generic.NewPathFromString(testCase.path)
var collected []string
collect := func(ctx context.Context, parent, p path.Path, node generic.NodeInterface) {
parent = parent.Append(node)
collected = append(collected, parent.String())
}
{
collected = make([]string, 0, 10)
require.False(t, tree.Apply(ctx, p, generic.NewApplyOptions(collect)))
}
{
collected = make([]string, 0, 10)
require.True(t, tree.ApplyAndGet(ctx, p, generic.NewApplyOptions(collect)))
require.EqualValues(t, 1, len(collected))
assert.EqualValues(t, testCase.path, collected[0])
}
{
collected = make([]string, 0, 10)
require.True(t, tree.ApplyAndGet(ctx, p, generic.NewApplyOptions(collect).SetWhere(generic.ApplyEachNode)))
sort.Strings(testCase.expected)
sort.Strings(collected)
assert.EqualValues(t, testCase.expected, collected)
}
}
}
func TestApplyVisitRelative(t *testing.T) {
ctx := context.Background()
tree := NewMemoryTree(ctx, "T")
// The "destination" is clean and there is no need to test a/../b which becomes
// b etc. Only when the first element of the path is either an id or ..
for _, testCase := range []struct {
start string
destination string
expected string
}{
{
start: "/T-A",
destination: "T-B",
expected: "/T-A/T-B",
},
{
start: "/T-A/T-B",
destination: ".",
expected: "/T-A/T-B",
},
{
start: "/T-A/T-B",
destination: "T-C",
expected: "/T-A/T-B/T-C",
},
{
start: "/T-A/T-B",
destination: "..",
expected: "/T-A",
},
{
start: "/T-A/T-B",
destination: "../T-F/T-G",
expected: "/T-A/T-F/T-G",
},
{
start: "/T-A/T-B/T-C",
destination: "../../T-F",
expected: "/T-A/T-F",
},
} {
t.Run(" "+testCase.start+" => "+testCase.destination, func(t *testing.T) {
testTreeBuild(t, tree, 2)
collected := make([]string, 0, 10)
start := generic.NewPathFromString(testCase.start)
collect := func(ctx context.Context, parent, p path.Path, node generic.NodeInterface) {
parent = parent.Append(node)
assert.False(t, node.GetIsNil(), node.String())
assert.EqualValues(t, parent.Length(), node.GetCurrentPath().Length())
expected := parent.PathString().Join()
actual := node.GetCurrentPath().String()
assert.EqualValues(t, actual, expected)
collected = append(collected, parent.String())
}
cd := func(ctx context.Context, parent, p path.Path, node generic.NodeInterface) {
destination := generic.NewPathFromString(testCase.destination)
fmt.Println("start ", node.GetCurrentPath().String())
assert.True(t, node.Apply(ctx, parent, destination, generic.NewApplyOptions(collect)))
}
assert.True(t, tree.Apply(ctx, start, generic.NewApplyOptions(cd)))
if assert.EqualValues(t, 1, len(collected)) {
assert.EqualValues(t, testCase.expected, collected[0])
}
})
p := generic.NewPathFromString("/1/2/3/4")
called := false
assert.False(t, tree.Apply(ctx, p, generic.NewApplyOptions(func(context.Context, path.Path, path.Path, generic.NodeInterface) { called = true })))
assert.False(t, called)
}
}
func TestApplyUpsert(t *testing.T) {
ctx := context.Background()
tree := NewMemoryTree(ctx, "T")
testTreeBuild(t, tree, 2)
expected := generic.NewPathFromString("/T-A/T-B/T-N")
assert.False(t, tree.Exists(ctx, expected))
assert.True(t, tree.Apply(ctx, generic.NewPathFromString("/T-A/T-B"), generic.NewApplyOptions(func(ctx context.Context, parent, p path.Path, node generic.NodeInterface) {
new := node.CreateChild(ctx)
new.Upsert(ctx)
memory.SetContent(new, "content "+new.GetID().String())
new.Upsert(ctx)
})))
assert.True(t, tree.Exists(ctx, expected))
}
func TestApplyDelete(t *testing.T) {
ctx := context.Background()
tree := NewMemoryTree(ctx, "T")
testTreeBuild(t, tree, 2)
toDelete := generic.NewPathFromString("/T-A/T-B")
assert.True(t, tree.Exists(ctx, toDelete))
assert.True(t, tree.Apply(ctx, toDelete, generic.NewApplyOptions(func(ctx context.Context, parent, p path.Path, node generic.NodeInterface) {
node.Delete(ctx)
})))
assert.False(t, tree.Exists(ctx, toDelete))
}

View file

@ -0,0 +1,74 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT
package generic
import (
"context"
"sort"
"testing"
"code.forgejo.org/f3/gof3/v3/path"
"code.forgejo.org/f3/gof3/v3/tree/generic"
"code.forgejo.org/f3/gof3/v3/tree/memory"
"github.com/stretchr/testify/assert"
)
func TestTreeCollectReferences(t *testing.T) {
ctx := context.Background()
for _, testCase := range []struct {
path string
expected []string
}{
{
path: "/O-A/O-B",
expected: []string{"/O-A/O-B/O-C"},
},
{
path: "/O-A/O-D",
expected: []string{},
},
{
path: "/O-A/O-J",
expected: []string{"/O-A/O-F", "/O-A/O-J/O-K"},
},
{
path: "/O-A/O-J/O-L",
expected: []string{"/O-A/O-J/O-K"},
},
{
path: "/O-A/O-J/O-M",
expected: []string{"/O-A/O-F"},
},
{
path: "/O-A",
expected: []string{"/O-A/O-B/O-C", "/O-A/O-F", "/O-A/O-F/O-H", "/O-A/O-J/O-K"},
},
} {
t.Run(testCase.path, func(t *testing.T) {
tree := NewMemoryTree(ctx, "O")
testTreeBuild(t, tree, 2)
setReference := func(p, reference string) {
assert.True(t, tree.Apply(ctx, generic.NewPathFromString(p), generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
memory.SetRef(node, reference)
})))
}
setReference("/O-A/O-B", "/O-A/O-B/O-C")
setReference("/O-A/O-F/O-G", "/O-A/O-F/O-H")
setReference("/O-A/O-J/O-M", "../../O-F")
setReference("/O-A/O-J/O-L", "../O-K")
actual := make([]string, 0, 10)
for _, reference := range generic.TreeCollectReferences(ctx, tree, generic.NewPathFromString(testCase.path)) {
actual = append(actual, reference.String())
}
sort.Strings(actual)
assert.EqualValues(t, testCase.expected, actual)
})
}
}

View file

@ -0,0 +1,626 @@
// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT
package generic
import (
"context"
"fmt"
"path/filepath"
"sort"
"testing"
"code.forgejo.org/f3/gof3/v3/kind"
"code.forgejo.org/f3/gof3/v3/path"
"code.forgejo.org/f3/gof3/v3/tree/generic"
"code.forgejo.org/f3/gof3/v3/tree/memory"
"github.com/stretchr/testify/assert"
)
func TestUnifyPathSimpleRemap(t *testing.T) {
ctx := context.Background()
for _, testCase := range []struct {
path string
expected []string
}{
{
path: "",
expected: []string{},
},
{
path: "/O-A",
expected: []string{"/O-A:O-A=content O-A => /D-A:D-A=content O-A"},
},
{
path: "/O-A/O-B",
expected: []string{"/O-A:O-A=content O-A => /D-A:D-A=content O-A", "/O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B"},
},
{
path: "/O-A/O-B/O-C",
expected: []string{"/O-A:O-A=content O-A => /D-A:D-A=content O-A", "/O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B", "/O-A/O-B/O-C:O-C=content O-C => /D-A/D-B/D-C:D-C=content O-C"},
},
} {
t.Run(" "+testCase.path, func(t *testing.T) {
originTree := NewMemoryTree(ctx, "O")
testTreeBuild(t, originTree, 2)
destinationTree := NewMemoryTree(ctx, "D")
collected := make([]string, 0, 10)
p := generic.NewPathFromString(testCase.path)
upsert := func(ctx context.Context, origin generic.NodeInterface, originPath path.Path, destination generic.NodeInterface, destinationPath path.Path) {
fmt.Printf("origin %v destination %v\n", origin, destination)
originPath = originPath.Append(origin)
destinationPath = destinationPath.Append(destination)
collected = append(collected, originPath.String()+":"+origin.String()+" => "+destinationPath.String()+":"+destination.String())
}
generic.TreeUnifyPath(ctx, originTree, p, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert))
assert.EqualValues(t, testCase.expected, collected)
collected = make([]string, 0, 10)
generic.TreeUnifyPath(ctx, originTree, p, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert))
assert.EqualValues(t, testCase.expected, collected)
})
}
}
func TestUnifyPathSimpleNoRemap(t *testing.T) {
ctx := context.Background()
for _, testCase := range []struct {
noremap bool
path string
expected []string
}{
{
noremap: false,
path: "/O-A/O-B",
expected: []string{"/O-A:O-A=content O-A => /D-A:D-A=content O-A", "/O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B"},
},
{
noremap: true,
path: "/O-A/O-B",
expected: []string{"/O-A:O-A=content O-A => /O-A:O-A=content O-A", "/O-A/O-B:O-B=content O-B => /O-A/O-B:O-B=content O-B"},
},
} {
t.Run(fmt.Sprintf("noremap=%v,path=%s", testCase.noremap, testCase.path), func(t *testing.T) {
originName := "O"
originTree := NewMemoryTree(ctx, originName)
testTreeBuild(t, originTree, 2)
var destinationName string
if testCase.noremap {
destinationName = originName
} else {
destinationName = "D"
}
destinationTree := NewMemoryTree(ctx, destinationName)
collected := make([]string, 0, 10)
p := generic.NewPathFromString(testCase.path)
upsert := func(ctx context.Context, origin generic.NodeInterface, originPath path.Path, destination generic.NodeInterface, destinationPath path.Path) {
fmt.Printf("origin %v destination %v\n", origin, destination)
originPath = originPath.Append(origin)
destinationPath = destinationPath.Append(destination)
collected = append(collected, originPath.String()+":"+origin.String()+" => "+destinationPath.String()+":"+destination.String())
}
generic.TreeUnifyPath(ctx, originTree, p, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert).SetNoRemap(testCase.noremap))
assert.EqualValues(t, testCase.expected, collected)
collected = make([]string, 0, 10)
generic.TreeUnifyPath(ctx, originTree, p, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert).SetNoRemap(testCase.noremap))
assert.EqualValues(t, testCase.expected, collected)
})
}
}
func TestUnifyPathRelative(t *testing.T) {
ctx := context.Background()
for _, testCase := range []struct {
start string
destination string
expected []string
}{
{
start: "/O-A/O-B",
destination: "O-C",
expected: []string{"cd: /O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B", "unify: /O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B", "unify: /O-A/O-B/O-C:O-C=content O-C => /D-A/D-B/D-C:D-C=content O-C"},
},
{
start: "/O-A/O-B",
destination: ".",
expected: []string{"cd: /O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B", "unify: /O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B"},
},
{
start: "/O-A/O-B",
destination: "../O-F/O-G",
expected: []string{"cd: /O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B", "unify: /O-A:O-A=content O-A => /D-A:D-A=content O-A", "unify: /O-A/O-F:O-F=content O-F => /D-A/D-C:D-C=content O-F", "unify: /O-A/O-F/O-G:O-G=content O-G => /D-A/D-C/D-D:D-D=content O-G"},
},
{
start: "/O-A/O-B/O-C",
destination: "../O-E",
expected: []string{"cd: /O-A/O-B/O-C:O-C=content O-C => /D-A/D-B/D-C:D-C=content O-C", "unify: /O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B", "unify: /O-A/O-B/O-E:O-E=content O-E => /D-A/D-B/D-D:D-D=content O-E"},
},
} {
t.Run(" "+testCase.start+" => "+testCase.destination, func(t *testing.T) {
originTree := NewMemoryTree(ctx, "O")
testTreeBuild(t, originTree, 2)
destinationTree := NewMemoryTree(ctx, "D")
var collected []string
start := generic.NewPathFromString(testCase.start)
collect := func(prefix string, origin, destination generic.NodeInterface) {
originPath := origin.GetCurrentPath().String()
destinationPath := destination.GetCurrentPath().String()
collected = append(collected, prefix+originPath+":"+origin.GetSelf().String()+" => "+destinationPath+":"+destination.GetSelf().String())
}
//
// Unify testCase.start
//
upsert := func(ctx context.Context, origin generic.NodeInterface, originParent path.Path, destination generic.NodeInterface, destinationParent path.Path) {
collect("unify: ", origin, destination)
}
generic.TreeUnifyPath(ctx, originTree, start, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert))
//
// Beginning from testCase.start, unify testCase.destination
//
cd := func(ctx context.Context, origin, destination generic.NodeInterface) {
collect("cd: ", origin, destination)
path := generic.NewPathFromString(testCase.destination)
originParent := origin.GetParent().GetCurrentPath()
destinationParent := destination.GetParent().GetCurrentPath()
generic.NodeUnifyPath(ctx, origin.GetSelf(), originParent, path, destinationParent, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert))
}
//
//
//
collected = make([]string, 0, 10)
generic.TreeParallelApply(ctx, originTree, start, destinationTree, generic.NewParallelApplyOptions(cd))
assert.EqualValues(t, testCase.expected, collected)
//
// Do it twice to verify it is idempotent
//
collected = make([]string, 0, 10)
generic.TreeParallelApply(ctx, originTree, start, destinationTree, generic.NewParallelApplyOptions(cd))
assert.EqualValues(t, testCase.expected, collected)
})
}
}
func TestUnifyPathScenario(t *testing.T) {
ctx := context.Background()
//
// build and populate a tree
//
originTree := NewMemoryTree(ctx, "O")
testTreeBuild(t, originTree, 2)
//
// build an empty tree
//
destinationTree := NewMemoryTree(ctx, "D")
//
// accumulate the call results for verification
//
var collected []string
upsert := func(ctx context.Context, origin generic.NodeInterface, originPath path.Path, destination generic.NodeInterface, destinationPath path.Path) {
originPath = originPath.Append(origin)
destinationPath = destinationPath.Append(destination)
what := originPath.String() + ":" + origin.String() + " => " + destinationPath.String() + ":" + destination.String()
fmt.Printf("unify: %T => %T | %s\n", origin, destination, what)
collected = append(collected, what)
}
assertTree := func(tree generic.TreeInterface, expected []string) {
collected := make([]string, 0, 10)
tree.Walk(ctx, generic.NewWalkOptions(func(ctx context.Context, path path.Path, node generic.NodeInterface) {
if node.GetKind() == kind.KindRoot {
return
}
path = path.Append(node)
collected = append(collected, path.String()+":"+node.String())
}))
sort.Strings(collected)
assert.EqualValues(t, expected, collected)
}
//
// unify the originTree with the destinationTree on the specified path
//
fullPath := generic.NewPathFromString("/O-A/O-B/O-C")
collected = make([]string, 0, 10)
generic.TreeUnifyPath(ctx, originTree, fullPath, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert))
sort.Strings(collected)
assert.EqualValues(t, []string{"/O-A/O-B/O-C:O-C=content O-C => /D-A/D-B/D-C:D-C=content O-C", "/O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B", "/O-A:O-A=content O-A => /D-A:D-A=content O-A"}, collected)
assertTree(destinationTree, []string{"/D-A/D-B/D-C:D-C=content O-C", "/D-A/D-B:D-B=content O-B", "/D-A:D-A=content O-A"})
//
// Add a node unrelated to the unification path
//
var unrelatedOriginPath path.Path
{
originTree.Apply(ctx, generic.NewPathFromString("/O-A/O-B"), generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
assert.EqualValues(t, parent.Length()+1, node.GetCurrentPath().Length())
unrelated := node.CreateChild(ctx)
unrelated.Upsert(ctx)
memory.SetContent(unrelated, "content "+unrelated.GetID().String())
unrelated.Upsert(ctx)
unrelatedOriginPath = unrelated.GetCurrentPath()
assert.EqualValues(t, "/O-A/O-B/O-N", (unrelatedOriginPath.PathString().Join()))
}))
}
//
// Replace the content of the last node
//
lastContent := "LAST"
{
lastPath := generic.NewPathFromString("/O-A/O-B/O-C")
originTree.Apply(ctx, lastPath, generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
memory.SetContent(node, lastContent)
}))
collected = make([]string, 0, 10)
generic.TreeUnifyPath(ctx, originTree, lastPath, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert))
sort.Strings(collected)
assert.EqualValues(t, []string{"/O-A/O-B/O-C:O-C=LAST => /D-A/D-B/D-C:D-C=LAST", "/O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B", "/O-A:O-A=content O-A => /D-A:D-A=content O-A"}, collected)
assertTree(destinationTree, []string{"/D-A/D-B/D-C:D-C=LAST", "/D-A/D-B:D-B=content O-B", "/D-A:D-A=content O-A"})
}
//
// Replace the content of the first node
//
firstContent := "FIRST"
{
firstPath := generic.NewPathFromString("/O-A")
originTree.Apply(ctx, firstPath, generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
memory.SetContent(node, firstContent)
}))
collected = make([]string, 0, 10)
generic.TreeUnifyPath(ctx, originTree, firstPath, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert))
sort.Strings(collected)
assert.EqualValues(t, []string{"/O-A:O-A=" + firstContent + " => /D-A:D-A=" + firstContent}, collected)
assertTree(destinationTree, []string{"/D-A/D-B/D-C:D-C=LAST", "/D-A/D-B:D-B=content O-B", "/D-A:D-A=FIRST"})
}
//
// Replace the content of the second node
//
secondContent := "SECOND"
{
secondPath := generic.NewPathFromString("/O-A/O-B")
originTree.Apply(ctx, secondPath, generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
memory.SetContent(node, secondContent)
}))
collected = make([]string, 0, 10)
generic.TreeUnifyPath(ctx, originTree, secondPath, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert))
assert.EqualValues(t, []string{"/O-A:O-A=" + firstContent + " => /D-A:D-A=" + firstContent, "/O-A/O-B:O-B=" + secondContent + " => /D-A/D-B:D-B=" + secondContent}, collected)
sort.Strings(collected)
assertTree(destinationTree, []string{"/D-A/D-B/D-C:D-C=LAST", "/D-A/D-B:D-B=SECOND", "/D-A:D-A=FIRST"})
}
//
// verify the node unrelated to the unification is still there
//
{
var found bool
originTree.Apply(ctx, unrelatedOriginPath, generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
found = true
}))
assert.True(t, found)
}
}
func TestUnifyMirror(t *testing.T) {
ctx := context.Background()
//
// build and populate a tree
//
originTree := NewMemoryTree(ctx, "O")
log := originTree.GetLogger()
testTreeBuild(t, originTree, 2)
//
// build an empty tree
//
destinationTree := NewMemoryTree(ctx, "D")
upsert := func(ctx context.Context, origin generic.NodeInterface, originPath path.Path, destination generic.NodeInterface, destinationPath path.Path) {
assert.NotNil(t, origin.GetDriver().(*memory.Driver))
assert.NotNil(t, destination.GetDriver().(*memory.Driver))
originPath = originPath.Append(origin)
destinationPath = destinationPath.Append(destination)
what := fmt.Sprintf("%s:%s => %s:%s", originPath, origin.GetSelf(), destinationPath, destination.GetSelf())
log.Trace("mirror upsert: %T => %T | %s", origin, destination, what)
}
delete := func(ctx context.Context, destination generic.NodeInterface, destinationPath path.Path) {
assert.NotNil(t, destination.GetDriver().(*memory.Driver))
destinationPath = destinationPath.Append(destination)
log.Trace("mirror delete: %T | %s:%s", destination, destinationPath, destination)
}
var sameTree func(origin, destination generic.NodeInterface) bool
sameTree = func(origin, destination generic.NodeInterface) bool {
what := origin.GetCurrentPath().String() + ":" + origin.GetSelf().String() + " => " + destination.GetCurrentPath().String() + ":" + destination.GetSelf().String()
log.Trace("sameTree: %T => %T | %s", origin.GetSelf(), destination.GetSelf(), what)
if origin.GetMappedID() != destination.GetID() {
log.Trace("sameTree: different: %s != %s", origin.GetMappedID(), destination.GetID())
return false
}
originChildren := origin.GetChildren()
destinationChildren := destination.GetChildren()
if len(originChildren) != len(destinationChildren) {
log.Trace("sameTree: different: length %v != %v", len(originChildren), len(destinationChildren))
return false
}
for _, originChild := range originChildren {
destinationChild := destination.GetChild(originChild.GetMappedID())
if destinationChild == generic.NilNode {
log.Trace("sameTree: different: %s not found", originChild.GetMappedID())
return false
}
if !sameTree(originChild, destinationChild) {
return false
}
}
return true
}
//
// unify the originTree with the destinationTree
//
assert.False(t, sameTree(originTree.GetRoot(), destinationTree.GetRoot()))
generic.TreeUnify(ctx, originTree, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert).SetDelete(delete))
assert.True(t, sameTree(originTree.GetRoot(), destinationTree.GetRoot()))
generic.TreeUnify(ctx, originTree, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert).SetDelete(delete))
assert.True(t, sameTree(originTree.GetRoot(), destinationTree.GetRoot()))
{
addNode := func(tree generic.TreeInterface, pathString string) {
tree.Apply(ctx, generic.NewPathFromString(pathString), generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
new := node.CreateChild(ctx)
new.Upsert(ctx)
memory.SetContent(new, "content "+new.GetID().String())
new.Upsert(ctx)
log.Trace("add: %s", parent.ReadableString())
log.Trace("add: %s", node.GetCurrentPath().ReadableString())
log.Trace("add: %s", new.GetCurrentPath().ReadableString())
}))
}
for _, testCase := range []struct {
existingPath string
newPath string
}{
{
existingPath: "/O-A/O-B",
newPath: "/D-A/D-B/D-N",
},
{
existingPath: "/O-A",
newPath: "/D-A/D-O",
},
{
existingPath: "/O-A/O-J/O-K",
newPath: "/D-A/D-J/D-K/D-P",
},
} {
t.Run("add"+testCase.newPath, func(t *testing.T) {
destinationPath := generic.NewPathFromString(testCase.newPath)
addNode(originTree, testCase.existingPath)
assert.False(t, sameTree(originTree.GetRoot(), destinationTree.GetRoot()))
assert.False(t, destinationTree.Exists(ctx, destinationPath), destinationPath.String())
generic.TreeUnify(ctx, originTree, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert).SetDelete(delete))
assert.True(t, destinationTree.Exists(ctx, destinationPath), destinationPath.String())
assert.True(t, sameTree(originTree.GetRoot(), destinationTree.GetRoot()))
})
}
}
{
deleteNode := func(tree generic.TreeInterface, toDelete path.Path) {
tree.Apply(ctx, toDelete, generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
node.Delete(ctx)
}))
}
for _, testCase := range []struct {
originPath string
destinationPath string
}{
{
originPath: "/O-A/O-F",
destinationPath: "/D-A/D-F",
},
} {
t.Run("delete"+testCase.originPath, func(t *testing.T) {
originPath := generic.NewPathFromString(testCase.originPath)
destinationPath := generic.NewPathFromString(testCase.destinationPath)
assert.True(t, originTree.Exists(ctx, originPath))
assert.True(t, destinationTree.Exists(ctx, destinationPath), destinationPath.String())
deleteNode(originTree, originPath)
assert.False(t, originTree.Exists(ctx, originPath))
generic.TreeUnify(ctx, originTree, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert).SetDelete(delete))
assert.False(t, destinationTree.Exists(ctx, destinationPath), destinationPath.String())
})
}
}
}
func TestNodeParallelApplyFound(t *testing.T) {
ctx := context.Background()
for _, testCase := range []struct {
start string
destination string
expected []string
expectedRemapped string
}{
{
start: "/O-A/O-B",
destination: "O-C",
expected: []string{"/O-A/O-B => /D-A/D-B", "/O-A/O-B/O-C => /D-A/D-B/D-C"},
expectedRemapped: "/D-A/D-B/D-C",
},
{
start: "/O-A/O-B/O-D",
destination: ".",
expected: []string{"/O-A/O-B/O-D => /D-A/D-B/D-D"},
expectedRemapped: "/D-A/D-B/D-D",
},
{
start: ".",
destination: ".",
expected: []string{" => "},
expectedRemapped: "",
},
{
start: "/O-A/O-B/O-C",
destination: "../O-D",
expected: []string{"/O-A/O-B => /D-A/D-B", "/O-A/O-B/O-D => /D-A/D-B/D-D"},
expectedRemapped: "/D-A/D-B/D-D",
},
{
start: "/",
destination: ".",
expected: []string{" => "},
expectedRemapped: "",
},
} {
t.Run(" "+testCase.start+" => "+testCase.destination, func(t *testing.T) {
originTree := NewMemoryTree(ctx, "O")
testTreeBuild(t, originTree, 2)
destinationTree := NewMemoryTree(ctx, "D")
//
// Mirror two trees
//
generic.TreeMirror(ctx, originTree, destinationTree, generic.NewPathFromString("/"), generic.NewMirrorOptions())
//
// collect all nodes traversed by the apply function along testCase.destination
//
collected := make([]string, 0, 10)
collect := func(ctx context.Context, origin, destination generic.NodeInterface) {
collected = append(collected, fmt.Sprintf("%s => %s", origin.GetCurrentPath().String(), destination.GetCurrentPath().String()))
}
//
// get to testCase.start and from there run the apply function to reach testCase.destination
//
nodeApply := func(ctx context.Context, origin, destination generic.NodeInterface) {
assert.True(t, generic.NodeParallelApply(ctx, origin, generic.NewPathFromString(testCase.destination), destination, generic.NewParallelApplyOptions(collect).SetWhere(generic.ApplyEachNode)))
}
assert.True(t, generic.TreeParallelApply(ctx, originTree, generic.NewPathFromString(testCase.start), destinationTree, generic.NewParallelApplyOptions(nodeApply)))
assert.EqualValues(t, testCase.expected, collected)
//
// test TreePathRemap
//
remappedPath := generic.TreePathRemap(ctx, originTree, generic.NewPathFromString(filepath.Join(testCase.start, testCase.destination)), destinationTree)
assert.EqualValues(t, testCase.expectedRemapped, remappedPath.String())
})
}
}
func TestNodeParallelApplyNoRemap(t *testing.T) {
ctx := context.Background()
for _, testCase := range []struct {
noremap bool
start string
expected []string
expectedRemapped string
}{
{
noremap: false,
start: "/O-A/O-B",
expected: []string{" => ", "/O-A => /D-A", "/O-A/O-B => /D-A/D-B"},
expectedRemapped: "/D-A/D-B",
},
{
noremap: true,
start: "/O-A/O-B",
expected: []string{" => ", "/O-A => /O-A", "/O-A/O-B => /O-A/O-B"},
expectedRemapped: "/O-A/O-B",
},
} {
t.Run(fmt.Sprintf("noremap=%v,start=%s", testCase.noremap, testCase.start), func(t *testing.T) {
originName := "O"
originTree := NewMemoryTree(ctx, originName)
testTreeBuild(t, originTree, 2)
var destinationName string
if testCase.noremap {
destinationName = originName
} else {
destinationName = "D"
}
destinationTree := NewMemoryTree(ctx, destinationName)
//
// Mirror two trees
//
generic.TreeMirror(ctx, originTree, destinationTree, generic.NewPathFromString("/"), generic.NewMirrorOptions())
//
// collect all nodes traversed by the apply function along testCase.destination
//
collected := make([]string, 0, 10)
collect := func(ctx context.Context, origin, destination generic.NodeInterface) {
collected = append(collected, fmt.Sprintf("%s => %s", origin.GetCurrentPath(), destination.GetCurrentPath()))
}
//
// get to testCase.start and from there run the apply function to reach testCase.destination
//
assert.True(t, generic.TreeParallelApply(ctx, originTree, generic.NewPathFromString(testCase.start), destinationTree, generic.NewParallelApplyOptions(collect).SetWhere(generic.ApplyEachNode).SetNoRemap(testCase.noremap)))
assert.EqualValues(t, testCase.expected, collected)
//
// test TreePathRemap
//
remappedPath := generic.TreePathRemap(ctx, originTree, generic.NewPathFromString(testCase.start), destinationTree)
assert.EqualValues(t, testCase.expectedRemapped, remappedPath.String())
})
}
}
func TestNodeParallelApplyNotFound(t *testing.T) {
ctx := context.Background()
for _, testCase := range []struct {
start string
destination string
}{
{
start: "/O-A",
destination: "O-B/???",
},
{
start: "/O-A/O-B",
destination: "???",
},
{
start: "/O-A/O-B",
destination: "../???",
},
} {
t.Run(" "+testCase.start+" => "+testCase.destination, func(t *testing.T) {
originTree := NewMemoryTree(ctx, "O")
testTreeBuild(t, originTree, 2)
destinationTree := NewMemoryTree(ctx, "D")
//
// Mirror two trees
//
generic.TreeMirror(ctx, originTree, destinationTree, generic.NewPathFromString("/"), generic.NewMirrorOptions())
//
// get to testCase.start and from there run the apply function to reach testCase.destination
//
var called bool
nodeApply := func(ctx context.Context, origin, destination generic.NodeInterface) {
called = true
found := generic.NodeParallelApply(ctx, origin, generic.NewPathFromString(testCase.destination), destination, generic.NewParallelApplyOptions(nil))
assert.False(t, found)
}
assert.True(t, generic.TreeParallelApply(ctx, originTree, generic.NewPathFromString(testCase.start), destinationTree, generic.NewParallelApplyOptions(nodeApply)))
assert.True(t, called)
})
}
}