• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2020 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package fs_test
6
7import (
8	. "io/fs"
9	"os"
10	pathpkg "path"
11	"path/filepath"
12	"reflect"
13	"testing"
14	"testing/fstest"
15)
16
17type Node struct {
18	name    string
19	entries []*Node // nil if the entry is a file
20	mark    int
21}
22
23var tree = &Node{
24	"testdata",
25	[]*Node{
26		{"a", nil, 0},
27		{"b", []*Node{}, 0},
28		{"c", nil, 0},
29		{
30			"d",
31			[]*Node{
32				{"x", nil, 0},
33				{"y", []*Node{}, 0},
34				{
35					"z",
36					[]*Node{
37						{"u", nil, 0},
38						{"v", nil, 0},
39					},
40					0,
41				},
42			},
43			0,
44		},
45	},
46	0,
47}
48
49func walkTree(n *Node, path string, f func(path string, n *Node)) {
50	f(path, n)
51	for _, e := range n.entries {
52		walkTree(e, pathpkg.Join(path, e.name), f)
53	}
54}
55
56func makeTree() FS {
57	fsys := fstest.MapFS{}
58	walkTree(tree, tree.name, func(path string, n *Node) {
59		if n.entries == nil {
60			fsys[path] = &fstest.MapFile{}
61		} else {
62			fsys[path] = &fstest.MapFile{Mode: ModeDir}
63		}
64	})
65	return fsys
66}
67
68// Assumes that each node name is unique. Good enough for a test.
69// If clear is true, any incoming error is cleared before return. The errors
70// are always accumulated, though.
71func mark(entry DirEntry, err error, errors *[]error, clear bool) error {
72	name := entry.Name()
73	walkTree(tree, tree.name, func(path string, n *Node) {
74		if n.name == name {
75			n.mark++
76		}
77	})
78	if err != nil {
79		*errors = append(*errors, err)
80		if clear {
81			return nil
82		}
83		return err
84	}
85	return nil
86}
87
88func TestWalkDir(t *testing.T) {
89	tmpDir := t.TempDir()
90
91	origDir, err := os.Getwd()
92	if err != nil {
93		t.Fatal("finding working dir:", err)
94	}
95	if err = os.Chdir(tmpDir); err != nil {
96		t.Fatal("entering temp dir:", err)
97	}
98	defer os.Chdir(origDir)
99
100	fsys := makeTree()
101	errors := make([]error, 0, 10)
102	clear := true
103	markFn := func(path string, entry DirEntry, err error) error {
104		return mark(entry, err, &errors, clear)
105	}
106	// Expect no errors.
107	err = WalkDir(fsys, ".", markFn)
108	if err != nil {
109		t.Fatalf("no error expected, found: %s", err)
110	}
111	if len(errors) != 0 {
112		t.Fatalf("unexpected errors: %s", errors)
113	}
114	walkTree(tree, tree.name, func(path string, n *Node) {
115		if n.mark != 1 {
116			t.Errorf("node %s mark = %d; expected 1", path, n.mark)
117		}
118		n.mark = 0
119	})
120}
121
122func TestIssue51617(t *testing.T) {
123	dir := t.TempDir()
124	for _, sub := range []string{"a", filepath.Join("a", "bad"), filepath.Join("a", "next")} {
125		if err := os.Mkdir(filepath.Join(dir, sub), 0755); err != nil {
126			t.Fatal(err)
127		}
128	}
129	bad := filepath.Join(dir, "a", "bad")
130	if err := os.Chmod(bad, 0); err != nil {
131		t.Fatal(err)
132	}
133	defer os.Chmod(bad, 0700) // avoid errors on cleanup
134	var saw []string
135	err := WalkDir(os.DirFS(dir), ".", func(path string, d DirEntry, err error) error {
136		if err != nil {
137			return filepath.SkipDir
138		}
139		if d.IsDir() {
140			saw = append(saw, path)
141		}
142		return nil
143	})
144	if err != nil {
145		t.Fatal(err)
146	}
147	want := []string{".", "a", "a/bad", "a/next"}
148	if !reflect.DeepEqual(saw, want) {
149		t.Errorf("got directories %v, want %v", saw, want)
150	}
151}
152