1
0
Fork 0
golang-forgejo-f3-gof3/tree/tests/generic/unify_test.go

627 lines
24 KiB
Go
Raw Permalink Normal View History

// 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)
})
}
}