File size: 4,248 Bytes
6380833 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | package treex
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// wrapper type that owns a value and has a Node pointing to itself
// This mirrors patterns like FieldDescriptor which maintain a Node[*T]
// to participate in a larger tree.
type wrapper struct {
name string
node *Node[*wrapper]
}
func newWrapper(name string) *wrapper {
w := &wrapper{name: name}
w.node = NewNode(w)
return w
}
func TestWrapper_EmbeddingAndHierarchy(t *testing.T) {
// Graph:
// root
// βββ a
// β βββ a1
// βββ b
root := newWrapper("root")
a := newWrapper("a")
a1 := newWrapper("a1")
b := newWrapper("b")
root.node.AddChild(a.node)
a.node.AddChild(a1.node)
root.node.AddChild(b.node)
tr, err := NewTree(root.node)
require.NoError(t, err)
require.NotNil(t, tr)
var order []string
require.NoError(t, tr.DFS(func(n *Node[*wrapper]) (bool, error) {
order = append(order, n.Value().name)
return false, nil
}))
assert.Equal(t, []string{"root", "a", "a1", "b"}, order)
// NewTree shallow-clones root and reattaches first-level children to the clone
assert.Equal(t, tr.Root(), a.node.Parent())
assert.True(t, a1.node.Parent() == a.node)
}
func TestWrapper_ImmutableUpdate_UsingShallowClone(t *testing.T) {
// We want to "rename" the wrapper at the root without mutating the original
root := newWrapper("root")
child := newWrapper("child")
root.node.AddChild(child.node)
// Build an initial tree
tr, err := NewTree(root.node)
require.NoError(t, err)
// To perform an immutable-style update on the root wrapper object,
// we create a new wrapper copy, and we shallow-clone the node to keep the
// existing relationships (children) but swap to the new value.
updated := &wrapper{name: "root-renamed"}
updated.node = root.node.ShallowClone()
updated.node.SetValue(updated)
// ensure node identity differs from the original root.node (clone)
assert.NotSame(t, root.node, updated.node)
// children and parent links preserved by the shallow clone
require.Len(t, updated.node.Children(), 1)
assert.Equal(t, child.node, updated.node.Children()[0])
assert.Nil(t, updated.node.Parent())
// Build a tree from the updated node; original tree remains intact
tr2, err := NewTree(updated.node)
require.NoError(t, err)
var order1 []string
require.NoError(t, tr.DFS(func(n *Node[*wrapper]) (bool, error) {
order1 = append(order1, n.Value().name)
return false, nil
}))
assert.Equal(t, []string{"root", "child"}, order1)
var order2 []string
require.NoError(t, tr2.DFS(func(n *Node[*wrapper]) (bool, error) {
order2 = append(order2, n.Value().name)
return false, nil
}))
assert.Equal(t, []string{"root-renamed", "child"}, order2)
}
func TestWrapper_PartialSubtreeUpdate_WithShallowCloneAtIntermediate(t *testing.T) {
// Graph initial:
// root
// βββ left
// β βββ ll
// βββ right
root := newWrapper("root")
left := newWrapper("left")
right := newWrapper("right")
ll := newWrapper("ll")
root.node.AddChild(left.node)
root.node.AddChild(right.node)
left.node.AddChild(ll.node)
// We want to update only the 'left' wrapper (e.g., rename or add metadata)
// without touching other branches. Clone the left node shallowly and swap it.
leftUpdated := &wrapper{name: "left*"}
leftUpdated.node = left.node.ShallowClone()
leftUpdated.node.SetValue(leftUpdated)
// ShallowClone reattaches first-level children, so no manual reattachment is needed.
// Swap the child on root from left.node to leftUpdated.node
require.NoError(t, root.node.SwapChild(left.node, leftUpdated.node))
assert.Equal(t, root.node, leftUpdated.node.Parent())
// original left node is now detached
assert.Nil(t, left.node.Parent())
// leftUpdated still has original child's identity (ll)
require.Len(t, leftUpdated.node.Children(), 1)
assert.Equal(t, ll.node, leftUpdated.node.Children()[0])
assert.Equal(t, leftUpdated.node, ll.node.Parent())
tr, err := NewTree(root.node)
require.NoError(t, err)
var order []string
require.NoError(t, tr.DFS(func(n *Node[*wrapper]) (bool, error) {
order = append(order, n.Value().name)
return false, nil
}))
assert.Equal(t, []string{"root", "left*", "ll", "right"}, order)
}
|