github.com/Schaudge/grailbase@v0.0.0-20240223061707-44c758a471c0/file/fsnode/fsnodetesting/walk.go (about) 1 package fsnodetesting 2 3 import ( 4 "context" 5 "io/ioutil" 6 "testing" 7 8 "github.com/Schaudge/grailbase/file/fsnode" 9 "github.com/Schaudge/grailbase/ioctx" 10 "github.com/grailbio/testutil/assert" 11 "github.com/stretchr/testify/require" 12 ) 13 14 // Walker is a collection of settings. 15 // TODO: Add (Walker).Walk* variant that inspects FileInfo, too, not just content. 16 type Walker struct { 17 IgnoredNames map[string]struct{} 18 // Info makes WalkContents return InfoT recursively. See that function. 19 Info bool 20 } 21 22 // T, Parent, and Leaf are aliases to improve readability of fixture definitions. 23 // InfoT augments T with its FileInfo for tests that want to check mode, size, etc. 24 type ( 25 T = interface{} 26 Parent = map[string]T 27 Leaf = []byte 28 29 InfoT = struct { 30 fsnode.FileInfo 31 T 32 } 33 ) 34 35 // WalkContents traverses all of node and returns map and []byte objects representing 36 // parents/directories and leaves/files, respectively. 37 // 38 // For example, if node is a Parent with children named a and b that are regular files, and an empty 39 // subdirectory subdir, returns: 40 // Parent{ 41 // "a": Leaf("a's content"), 42 // "b": Leaf("b's content"), 43 // "subdir": Parent{}, 44 // } 45 // 46 // If w.Info, the returned contents will include fsnode.FileInfo, for example: 47 // InfoT{ 48 // fsnode.NewDirInfo("parent"), 49 // Parent{ 50 // "a": InfoT{ 51 // fsnode.NewRegInfo("a").WithSize(11), 52 // Leaf("a's content"), 53 // }, 54 // "b": InfoT{ 55 // fsnode.NewRegInfo("b").WithModePerm(0755), 56 // Leaf("b's content"), 57 // }, 58 // "subdir": InfoT{ 59 // fsnode.NewDirInfo("subdir") 60 // Parent{}, 61 // }, 62 // }, 63 // } 64 func (w Walker) WalkContents(ctx context.Context, t testing.TB, node fsnode.T) T { 65 switch n := node.(type) { 66 case fsnode.Parent: 67 dir := make(Parent) 68 children, err := fsnode.IterateAll(ctx, n.Children()) 69 require.NoError(t, err) 70 for _, child := range children { 71 name := child.Info().Name() 72 if _, ok := w.IgnoredNames[name]; ok { 73 continue 74 } 75 _, collision := dir[name] 76 require.Falsef(t, collision, "name %q is repeated", name) 77 dir[name] = w.WalkContents(ctx, t, child) 78 } 79 if w.Info { 80 return InfoT{fsnode.CopyFileInfo(n.Info()), dir} 81 } 82 return dir 83 case fsnode.Leaf: 84 leaf := LeafReadAll(ctx, t, n) 85 if w.Info { 86 return InfoT{fsnode.CopyFileInfo(n.Info()), leaf} 87 } 88 return leaf 89 } 90 require.Failf(t, "invalid node type", "node: %T", node) 91 panic("unreachable") 92 } 93 94 func LeafReadAll(ctx context.Context, t testing.TB, n fsnode.Leaf) []byte { 95 file, err := fsnode.Open(ctx, n) 96 require.NoError(t, err) 97 defer func() { assert.NoError(t, file.Close(ctx)) }() 98 content, err := ioutil.ReadAll(ioctx.ToStdReader(ctx, file)) 99 require.NoError(t, err) 100 return content 101 }