1
0
Fork 0
golang-forgejo-f3-gof3/tree/tests/generic/node_walk_test.go
Daniel Baumann 03bfe4079e
Adding upstream version 3.10.8.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-05-18 09:37:23 +02:00

341 lines
9.7 KiB
Go

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