Skip to content

Commit

Permalink
fix/mantaray-walker-4639 (#4640)
Browse files Browse the repository at this point in the history
Co-authored-by: Levente Kiss <[email protected]>
  • Loading branch information
LevilkTheReal and Levente Kiss authored May 6, 2024
1 parent 52be59c commit ac52e18
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 214 deletions.
81 changes: 14 additions & 67 deletions pkg/manifest/mantaray/walker.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@

package mantaray

import "context"
import (
"context"
"sort"
)

// WalkNodeFunc is the type of the function called for each node visited
// by WalkNode.
type WalkNodeFunc func(path []byte, node *Node, err error) error

func walkNodeFnCopyBytes(ctx context.Context, path []byte, node *Node, err error, walkFn WalkNodeFunc) error {
func walkNodeFnCopyBytes(path []byte, node *Node, walkFn WalkNodeFunc) error {
return walkFn(append(path[:0:0], path...), node, nil)
}

Expand All @@ -22,12 +25,19 @@ func walkNode(ctx context.Context, path []byte, l Loader, n *Node, walkFn WalkNo
}
}

err := walkNodeFnCopyBytes(ctx, path, n, nil, walkFn)
err := walkNodeFnCopyBytes(path, n, walkFn)
if err != nil {
return err
}

for _, v := range n.forks {
keys := make([]byte, 0, len(n.forks))
for k := range n.forks {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })

for _, k := range keys {
v := n.forks[k]
nextPath := append(path[:0:0], path...)
nextPath = append(nextPath, v.prefix...)

Expand All @@ -52,66 +62,3 @@ func (n *Node) WalkNode(ctx context.Context, root []byte, l Loader, walkFn WalkN
}
return err
}

// WalkFunc is the type of the function called for each file or directory
// visited by Walk.
type WalkFunc func(path []byte, isDir bool, err error) error

func walkFnCopyBytes(path []byte, isDir bool, err error, walkFn WalkFunc) error {
return walkFn(append(path[:0:0], path...), isDir, nil)
}

// walk recursively descends path, calling walkFn.
func walk(ctx context.Context, path, prefix []byte, l Loader, n *Node, walkFn WalkFunc) error {
if n.forks == nil {
if err := n.load(ctx, l); err != nil {
return err
}
}

nextPath := append(path[:0:0], path...)

for i := 0; i < len(prefix); i++ {
if prefix[i] == PathSeparator {
// path ends with separator
err := walkFnCopyBytes(nextPath, true, nil, walkFn)
if err != nil {
return err
}
}
nextPath = append(nextPath, prefix[i])
}

if n.IsValueType() {
if nextPath[len(nextPath)-1] == PathSeparator {
// path ends with separator; already reported
} else {
err := walkFnCopyBytes(nextPath, false, nil, walkFn)
if err != nil {
return err
}
}
}

if n.IsEdgeType() {
for _, v := range n.forks {
err := walk(ctx, nextPath, v.prefix, l, v.Node, walkFn)
if err != nil {
return err
}
}
}

return nil
}

// Walk walks the node tree structure rooted at root, calling walkFn for
// each file or directory in the tree, including root. All errors that arise
// visiting files and directories are filtered by walkFn.
func (n *Node) Walk(ctx context.Context, root []byte, l Loader, walkFn WalkFunc) error {
node, err := n.LookupNode(ctx, root, l)
if err != nil {
return walkFn(root, false, err)
}
return walk(ctx, root, []byte{}, l, node, walkFn)
}
172 changes: 25 additions & 147 deletions pkg/manifest/mantaray/walker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,27 @@ func TestWalkNode(t *testing.T) {
{
name: "simple",
toAdd: [][]byte{
[]byte("index.html.backup"),
[]byte("index.html"),
[]byte("img/1.png"),
[]byte("img/test/oho.png"),
[]byte("img/test/old/test.png.backup"),
[]byte("img/test/old/test.png"),
[]byte("img/2.png"),
[]byte("img/1.png"),
[]byte("robots.txt"),
},
expected: [][]byte{
[]byte(""),
[]byte("i"),
[]byte("index.html"),
[]byte("img/"),
[]byte("img/1.png"),
[]byte("img/2.png"),
[]byte("img/test/o"),
[]byte("img/test/oho.png"),
[]byte("img/test/old/test.png"),
[]byte("img/test/old/test.png.backup"),
[]byte("index.html"),
[]byte("index.html.backup"),
[]byte("robots.txt"),
},
},
Expand All @@ -59,17 +68,15 @@ func TestWalkNode(t *testing.T) {
return n
}

pathExists := func(found []byte, expected [][]byte) bool {
pathFound := false
pathExistsInRightSequence := func(found []byte, expected [][]byte, walkedCount int) bool {
rightPathInSequence := false

for i := 0; i < len(tc.expected); i++ {
c := tc.expected[i]
if bytes.Equal(found, c) {
pathFound = true
break
}
c := expected[walkedCount]
if bytes.Equal(found, c) {
rightPathInSequence = true
}
return pathFound

return rightPathInSequence
}

t.Run(tc.name, func(t *testing.T) {
Expand All @@ -80,13 +87,14 @@ func TestWalkNode(t *testing.T) {
walkedCount := 0

walker := func(path []byte, node *mantaray.Node, err error) error {
walkedCount++

if !pathExists(path, tc.expected) {
return fmt.Errorf("walkFn returned unknown path: %s", path)
if !pathExistsInRightSequence(path, tc.expected, walkedCount) {
return fmt.Errorf("walkFn returned unexpected path: %s", path)
}
walkedCount++
return nil
}

// Expect no errors.
err := n.WalkNode(ctx, []byte{}, nil, walker)
if err != nil {
Expand Down Expand Up @@ -115,153 +123,23 @@ func TestWalkNode(t *testing.T) {
walkedCount := 0

walker := func(path []byte, node *mantaray.Node, err error) error {
walkedCount++

if !pathExists(path, tc.expected) {
return fmt.Errorf("walkFn returned unknown path: %s", path)
if !pathExistsInRightSequence(path, tc.expected, walkedCount) {
return fmt.Errorf("walkFn returned unexpected path: %s", path)
}

return nil
}
// Expect no errors.
err = n2.WalkNode(ctx, []byte{}, ls, walker)
if err != nil {
t.Fatalf("no error expected, found: %s", err)
}

if len(tc.expected) != walkedCount {
t.Errorf("expected %d nodes, got %d", len(tc.expected), walkedCount)
}
})
}
}

func TestWalk(t *testing.T) {
t.Parallel()

for _, tc := range []struct {
name string
toAdd [][]byte
expected [][]byte
}{
{
name: "simple",
toAdd: [][]byte{
[]byte("index.html"),
[]byte("img/test/"),
[]byte("img/test/oho.png"),
[]byte("img/test/old/test.png"),
// file with same prefix but not a directory prefix
[]byte("img/test/old/test.png.backup"),
[]byte("robots.txt"),
},
expected: [][]byte{
[]byte("index.html"),
[]byte("img"),
[]byte("img/test"),
[]byte("img/test/oho.png"),
[]byte("img/test/old"),
[]byte("img/test/old/test.png"),
[]byte("img/test/old/test.png.backup"),
[]byte("robots.txt"),
},
},
} {
ctx := context.Background()

createTree := func(t *testing.T, toAdd [][]byte) *mantaray.Node {
t.Helper()

n := mantaray.New()

for i := 0; i < len(toAdd); i++ {
c := toAdd[i]
e := append(make([]byte, 32-len(c)), c...)
err := n.Add(ctx, c, e, nil, nil)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
}
return n
}

pathExists := func(found []byte, expected [][]byte) bool {
pathFound := false

for i := 0; i < len(tc.expected); i++ {
c := tc.expected[i]
if bytes.Equal(found, c) {
pathFound = true
break
}
}
return pathFound
}

tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

n := createTree(t, tc.toAdd)

walkedCount := 0

walker := func(path []byte, isDir bool, err error) error {
walkedCount++

if !pathExists(path, tc.expected) {
return fmt.Errorf("walkFn returned unknown path: %s", path)
}

return nil
}
// Expect no errors.
err := n.Walk(ctx, []byte{}, nil, walker)
if err != nil {
t.Fatalf("no error expected, found: %s", err)
}

if len(tc.expected) != walkedCount {
t.Errorf("expected %d nodes, got %d", len(tc.expected), walkedCount)
}

})

t.Run(tc.name+"/with load save", func(t *testing.T) {
t.Parallel()

n := createTree(t, tc.toAdd)

ls := newMockLoadSaver()

err := n.Save(ctx, ls)
if err != nil {
t.Fatal(err)
}

n2 := mantaray.NewNodeRef(n.Reference())

walkedCount := 0

walker := func(path []byte, isDir bool, err error) error {
walkedCount++

if !pathExists(path, tc.expected) {
return fmt.Errorf("walkFn returned unknown path: %s", path)
}

return nil
}
// Expect no errors.
err = n2.Walk(ctx, []byte{}, ls, walker)
err = n2.WalkNode(ctx, []byte{}, ls, walker)
if err != nil {
t.Fatalf("no error expected, found: %s", err)
}

if len(tc.expected) != walkedCount {
t.Errorf("expected %d nodes, got %d", len(tc.expected), walkedCount)
}

})
}
}

0 comments on commit ac52e18

Please sign in to comment.