1 // Copyright 2014 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.
22 "golang.org/x/net/context"
25 func TestSlashClean(t *testing.T) {
26 testCases := []string{
40 for _, tc := range testCases {
42 want := path.Clean("/" + tc)
44 t.Errorf("tc=%q: got %q, want %q", tc, got, want)
49 func TestDirResolve(t *testing.T) {
50 testCases := []struct {
51 dir, name, want string
63 {"/", "../bar/a", "/bar/a"},
64 {"/", "../baz/a", "/baz/a"},
66 {"/", ".../a", "/.../a"},
69 {"/", "a/./b", "/a/b"},
70 {"/", "a/../../b", "/b"},
71 {"/", "a/../b", "/b"},
73 {"/", "a/b/c/../../d", "/a/d"},
74 {"/", "a/b/c/../../../d", "/d"},
75 {"/", "a/b/c/../../../../d", "/d"},
76 {"/", "a/b/c/d", "/a/b/c/d"},
78 {"/foo/bar", "", "/foo/bar"},
79 {"/foo/bar", "/", "/foo/bar"},
80 {"/foo/bar", ".", "/foo/bar"},
81 {"/foo/bar", "./a", "/foo/bar/a"},
82 {"/foo/bar", "..", "/foo/bar"},
83 {"/foo/bar", "../", "/foo/bar"},
84 {"/foo/bar", "../.", "/foo/bar"},
85 {"/foo/bar", "../a", "/foo/bar/a"},
86 {"/foo/bar", "../..", "/foo/bar"},
87 {"/foo/bar", "../bar/a", "/foo/bar/bar/a"},
88 {"/foo/bar", "../baz/a", "/foo/bar/baz/a"},
89 {"/foo/bar", "...", "/foo/bar/..."},
90 {"/foo/bar", ".../a", "/foo/bar/.../a"},
91 {"/foo/bar", ".../..", "/foo/bar"},
92 {"/foo/bar", "a", "/foo/bar/a"},
93 {"/foo/bar", "a/./b", "/foo/bar/a/b"},
94 {"/foo/bar", "a/../../b", "/foo/bar/b"},
95 {"/foo/bar", "a/../b", "/foo/bar/b"},
96 {"/foo/bar", "a/b", "/foo/bar/a/b"},
97 {"/foo/bar", "a/b/c/../../d", "/foo/bar/a/d"},
98 {"/foo/bar", "a/b/c/../../../d", "/foo/bar/d"},
99 {"/foo/bar", "a/b/c/../../../../d", "/foo/bar/d"},
100 {"/foo/bar", "a/b/c/d", "/foo/bar/a/b/c/d"},
102 {"/foo/bar/", "", "/foo/bar"},
103 {"/foo/bar/", "/", "/foo/bar"},
104 {"/foo/bar/", ".", "/foo/bar"},
105 {"/foo/bar/", "./a", "/foo/bar/a"},
106 {"/foo/bar/", "..", "/foo/bar"},
108 {"/foo//bar///", "", "/foo/bar"},
109 {"/foo//bar///", "/", "/foo/bar"},
110 {"/foo//bar///", ".", "/foo/bar"},
111 {"/foo//bar///", "./a", "/foo/bar/a"},
112 {"/foo//bar///", "..", "/foo/bar"},
114 {"/x/y/z", "ab/c\x00d/ef", ""},
126 {".", "../bar/a", "bar/a"},
127 {".", "../baz/a", "baz/a"},
129 {".", ".../a", ".../a"},
130 {".", ".../..", "."},
132 {".", "a/./b", "a/b"},
133 {".", "a/../../b", "b"},
134 {".", "a/../b", "b"},
136 {".", "a/b/c/../../d", "a/d"},
137 {".", "a/b/c/../../../d", "d"},
138 {".", "a/b/c/../../../../d", "d"},
139 {".", "a/b/c/d", "a/b/c/d"},
148 for _, tc := range testCases {
149 d := Dir(filepath.FromSlash(tc.dir))
150 if got := filepath.ToSlash(d.resolve(tc.name)); got != tc.want {
151 t.Errorf("dir=%q, name=%q: got %q, want %q", tc.dir, tc.name, got, tc.want)
156 func TestWalk(t *testing.T) {
157 type walkStep struct {
162 testCases := []struct {
182 {"/a/b/", []walkStep{
186 {"/a/b/c", []walkStep{
191 // The following test case is the one mentioned explicitly
192 // in the method description.
193 {"/foo/bar/x", []walkStep{
195 {"foo", "bar", false},
200 ctx := context.Background()
202 for _, tc := range testCases {
203 fs := NewMemFS().(*memFS)
205 parts := strings.Split(tc.dir, "/")
206 for p := 2; p < len(parts); p++ {
207 d := strings.Join(parts[:p], "/")
208 if err := fs.Mkdir(ctx, d, 0666); err != nil {
209 t.Errorf("tc.dir=%q: mkdir: %q: %v", tc.dir, d, err)
214 err := fs.walk("test", tc.dir, func(dir *memFSNode, frag string, final bool) error {
223 return fmt.Errorf("got %+v, want %+v", got, want)
225 i, prevFrag = i+1, frag
229 t.Errorf("tc.dir=%q: %v", tc.dir, err)
234 // find appends to ss the names of the named file and its children. It is
235 // analogous to the Unix find command.
237 // The returned strings are not guaranteed to be in any particular order.
238 func find(ctx context.Context, ss []string, fs FileSystem, name string) ([]string, error) {
239 stat, err := fs.Stat(ctx, name)
243 ss = append(ss, name)
245 f, err := fs.OpenFile(ctx, name, os.O_RDONLY, 0)
250 children, err := f.Readdir(-1)
254 for _, c := range children {
255 ss, err = find(ctx, ss, fs, path.Join(name, c.Name()))
264 func testFS(t *testing.T, fs FileSystem) {
265 errStr := func(err error) string {
267 case os.IsExist(err):
269 case os.IsNotExist(err):
277 // The non-"find" non-"stat" test cases should change the file system state. The
278 // indentation of the "find"s and "stat"s helps distinguish such test cases.
279 testCases := []string{
281 " stat /a want errNotExist",
282 " stat /d want errNotExist",
283 " stat /d/e want errNotExist",
284 "create /a A want ok",
286 "create /d/e EEE want errNotExist",
287 "mk-dir /a want errExist",
288 "mk-dir /d/m want errNotExist",
291 "create /d/e EEE want ok",
293 " find / /a /d /d/e",
294 "create /d/f FFFF want ok",
295 "create /d/g GGGGGGG want ok",
296 "mk-dir /d/m want ok",
297 "mk-dir /d/m want errExist",
298 "create /d/m/p PPPPP want ok",
302 " stat /d/h want errNotExist",
303 " stat /d/m want dir",
304 " stat /d/m/p want 5",
305 " find / /a /d /d/e /d/f /d/g /d/m /d/m/p",
308 " stat /d want errNotExist",
309 " stat /d/e want errNotExist",
310 " stat /d/f want errNotExist",
311 " stat /d/g want errNotExist",
312 " stat /d/m want errNotExist",
313 " stat /d/m/p want errNotExist",
315 "mk-dir /d/m want errNotExist",
317 "create /d/f FFFF want ok",
318 "rm-all /d/f want ok",
319 "mk-dir /d/m want ok",
322 "create /b BB want ok",
326 " stat /c want errNotExist",
328 " stat /d/m want dir",
329 " find / /a /b /d /d/m",
330 "move__ o=F /b /c want ok",
331 " stat /b want errNotExist",
333 " stat /d/m want dir",
334 " stat /d/n want errNotExist",
335 " find / /a /c /d /d/m",
336 "move__ o=F /d/m /d/n want ok",
337 "create /d/n/q QQQQ want ok",
338 " stat /d/m want errNotExist",
339 " stat /d/n want dir",
340 " stat /d/n/q want 4",
341 "move__ o=F /d /d/n/z want err",
342 "move__ o=T /c /d/n/q want ok",
343 " stat /c want errNotExist",
344 " stat /d/n/q want 2",
345 " find / /a /d /d/n /d/n/q",
346 "create /d/n/r RRRRR want ok",
348 "mk-dir /u/v want ok",
349 "move__ o=F /d/n /u want errExist",
350 "create /t TTTTTT want ok",
351 "move__ o=F /d/n /t want errExist",
353 "move__ o=F /d/n /t want ok",
355 " stat /d/n want errNotExist",
356 " stat /d/n/r want errNotExist",
360 " find / /a /d /t /t/q /t/r /u /u/v",
361 "move__ o=F /t / want errExist",
362 "move__ o=T /t /u/v want ok",
363 " stat /u/v/r want 5",
364 "move__ o=F / /z want err",
365 " find / /a /d /u /u/v /u/v/q /u/v/r",
367 " stat /b want errNotExist",
368 " stat /c want errNotExist",
369 " stat /u/v/r want 5",
370 "copy__ o=F d=0 /a /b want ok",
371 "copy__ o=T d=0 /a /c want ok",
375 " stat /u/v/r want 5",
376 "copy__ o=F d=0 /u/v/r /b want errExist",
378 "copy__ o=T d=0 /u/v/r /b want ok",
381 " stat /u/v/r want 5",
384 "mk-dir /u/v/w want ok",
385 "create /u/v/w/s SSSSSSSS want ok",
387 " stat /d/x want errNotExist",
388 " stat /d/y want errNotExist",
389 " stat /u/v/r want 5",
390 " stat /u/v/w/s want 8",
391 " find / /c /d /u /u/v /u/v/q /u/v/r /u/v/w /u/v/w/s",
392 "copy__ o=T d=0 /u/v /d/x want ok",
393 "copy__ o=T d=∞ /u/v /d/y want ok",
395 " stat /d/x want dir",
396 " stat /d/x/q want errNotExist",
397 " stat /d/x/r want errNotExist",
398 " stat /d/x/w want errNotExist",
399 " stat /d/x/w/s want errNotExist",
400 " stat /d/y want dir",
401 " stat /d/y/q want 2",
402 " stat /d/y/r want 5",
403 " stat /d/y/w want dir",
404 " stat /d/y/w/s want 8",
405 " stat /u want errNotExist",
406 " find / /c /d /d/x /d/y /d/y/q /d/y/r /d/y/w /d/y/w/s",
407 "copy__ o=F d=∞ /d/y /d/x want errExist",
410 ctx := context.Background()
412 for i, tc := range testCases {
413 tc = strings.TrimSpace(tc)
414 j := strings.IndexByte(tc, ' ')
416 t.Fatalf("test case #%d %q: invalid command", i, tc)
418 op, arg := tc[:j], tc[j+1:]
422 t.Fatalf("test case #%d %q: invalid operation %q", i, tc, op)
425 parts := strings.Split(arg, " ")
426 if len(parts) != 4 || parts[2] != "want" {
427 t.Fatalf("test case #%d %q: invalid write", i, tc)
429 f, opErr := fs.OpenFile(ctx, parts[0], os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
430 if got := errStr(opErr); got != parts[3] {
431 t.Fatalf("test case #%d %q: OpenFile: got %q (%v), want %q", i, tc, got, opErr, parts[3])
434 if _, err := f.Write([]byte(parts[1])); err != nil {
435 t.Fatalf("test case #%d %q: Write: %v", i, tc, err)
437 if err := f.Close(); err != nil {
438 t.Fatalf("test case #%d %q: Close: %v", i, tc, err)
443 got, err := find(ctx, nil, fs, "/")
445 t.Fatalf("test case #%d %q: find: %v", i, tc, err)
448 want := strings.Split(arg, " ")
449 if !reflect.DeepEqual(got, want) {
450 t.Fatalf("test case #%d %q:\ngot %s\nwant %s", i, tc, got, want)
453 case "copy__", "mk-dir", "move__", "rm-all", "stat":
461 parts := strings.Split(arg, " ")
462 if len(parts) != nParts {
463 t.Fatalf("test case #%d %q: invalid %s", i, tc, op)
466 got, opErr := "", error(nil)
470 if parts[1] == "d=∞" {
471 depth = infiniteDepth
473 _, opErr = copyFiles(ctx, fs, parts[2], parts[3], parts[0] == "o=T", depth, 0)
475 opErr = fs.Mkdir(ctx, parts[0], 0777)
477 _, opErr = moveFiles(ctx, fs, parts[1], parts[2], parts[0] == "o=T")
479 opErr = fs.RemoveAll(ctx, parts[0])
483 if stat, opErr = fs.Stat(ctx, fileName); opErr == nil {
487 got = strconv.Itoa(int(stat.Size()))
491 // For a Dir FileSystem, the virtual file system root maps to a
492 // real file system name like "/tmp/webdav-test012345", which does
493 // not end with "/". We skip such cases.
494 } else if statName := stat.Name(); path.Base(fileName) != statName {
495 t.Fatalf("test case #%d %q: file name %q inconsistent with stat name %q",
496 i, tc, fileName, statName)
504 if parts[len(parts)-2] != "want" {
505 t.Fatalf("test case #%d %q: invalid %s", i, tc, op)
507 if want := parts[len(parts)-1]; got != want {
508 t.Fatalf("test case #%d %q: got %q (%v), want %q", i, tc, got, opErr, want)
514 func TestDir(t *testing.T) {
515 switch runtime.GOOS {
517 t.Skip("see golang.org/issue/12004")
519 t.Skip("see golang.org/issue/11453")
522 td, err := ioutil.TempDir("", "webdav-test")
526 defer os.RemoveAll(td)
530 func TestMemFS(t *testing.T) {
531 testFS(t, NewMemFS())
534 func TestMemFSRoot(t *testing.T) {
535 ctx := context.Background()
537 for i := 0; i < 5; i++ {
538 stat, err := fs.Stat(ctx, "/")
540 t.Fatalf("i=%d: Stat: %v", i, err)
543 t.Fatalf("i=%d: Stat.IsDir is false, want true", i)
546 f, err := fs.OpenFile(ctx, "/", os.O_RDONLY, 0)
548 t.Fatalf("i=%d: OpenFile: %v", i, err)
551 children, err := f.Readdir(-1)
553 t.Fatalf("i=%d: Readdir: %v", i, err)
555 if len(children) != i {
556 t.Fatalf("i=%d: got %d children, want %d", i, len(children), i)
559 if _, err := f.Write(make([]byte, 1)); err == nil {
560 t.Fatalf("i=%d: Write: got nil error, want non-nil", i)
563 if err := fs.Mkdir(ctx, fmt.Sprintf("/dir%d", i), 0777); err != nil {
564 t.Fatalf("i=%d: Mkdir: %v", i, err)
569 func TestMemFileReaddir(t *testing.T) {
570 ctx := context.Background()
572 if err := fs.Mkdir(ctx, "/foo", 0777); err != nil {
573 t.Fatalf("Mkdir: %v", err)
575 readdir := func(count int) ([]os.FileInfo, error) {
576 f, err := fs.OpenFile(ctx, "/foo", os.O_RDONLY, 0)
578 t.Fatalf("OpenFile: %v", err)
581 return f.Readdir(count)
583 if got, err := readdir(-1); len(got) != 0 || err != nil {
584 t.Fatalf("readdir(-1): got %d fileInfos with err=%v, want 0, <nil>", len(got), err)
586 if got, err := readdir(+1); len(got) != 0 || err != io.EOF {
587 t.Fatalf("readdir(+1): got %d fileInfos with err=%v, want 0, EOF", len(got), err)
591 func TestMemFile(t *testing.T) {
592 testCases := []string{
603 "wantData abcdexxxxxyyyyzzststst",
607 "wantData abcdEFGxxxyyyyzzststst",
613 "seek cur 2 want 10",
614 "seek cur -1 want 9",
616 "wantData abcdEFGxxJyyyyzzststst",
618 "seek cur -4 want 6",
620 "wantData abcdEFghijkyyyzzststst",
623 "seek cur 0 want 15",
625 "seek cur 0 want 15",
627 "seek cur 0 want 15",
628 "seek end -3 want 19",
630 "wantData abcdEFghijkyyyzzstsZZt",
633 "wantData abcdEFghijkyyyzzstsZZAAAA",
635 "seek end 0 want 25",
636 "seek end -5 want 20",
639 "wantData abcdEFghijkyyyzzstsZZAAAABBBBB",
641 "seek end 10 want 40",
643 "wantData abcdEFghijkyyyzzstsZZAAAABBBBB..........C",
646 "wantData abcdEFghijkyyyzzstsZZAAAABBBBB..........CD",
648 "seek set 43 want 43",
650 "wantData abcdEFghijkyyyzzstsZZAAAABBBBB..........CD.E",
653 "write 5*123456789_",
654 "wantData 123456789_123456789_123456789_123456789_123456789_",
656 "seek cur 0 want 50",
657 "seek cur -99 want err",
660 ctx := context.Background()
662 const filename = "/foo"
664 f, err := fs.OpenFile(ctx, filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
666 t.Fatalf("OpenFile: %v", err)
670 for i, tc := range testCases {
671 j := strings.IndexByte(tc, ' ')
673 t.Fatalf("test case #%d %q: invalid command", i, tc)
675 op, arg := tc[:j], tc[j+1:]
677 // Expand an arg like "3*a+2*b" to "aaabb".
678 parts := strings.Split(arg, "+")
679 for j, part := range parts {
680 if k := strings.IndexByte(part, '*'); k >= 0 {
681 repeatCount, repeatStr := part[:k], part[k+1:]
682 n, err := strconv.Atoi(repeatCount)
684 t.Fatalf("test case #%d %q: invalid repeat count %q", i, tc, repeatCount)
686 parts[j] = strings.Repeat(repeatStr, n)
689 arg = strings.Join(parts, "")
693 t.Fatalf("test case #%d %q: invalid operation %q", i, tc, op)
696 buf := make([]byte, len(arg))
697 if _, err := io.ReadFull(f, buf); err != nil {
698 t.Fatalf("test case #%d %q: ReadFull: %v", i, tc, err)
700 if got := string(buf); got != arg {
701 t.Fatalf("test case #%d %q:\ngot %q\nwant %q", i, tc, got, arg)
705 parts := strings.Split(arg, " ")
707 t.Fatalf("test case #%d %q: invalid seek", i, tc)
713 t.Fatalf("test case #%d %q: invalid seek whence", i, tc)
721 offset, err := strconv.Atoi(parts[1])
723 t.Fatalf("test case #%d %q: invalid offset %q", i, tc, parts[1])
726 if parts[2] != "want" {
727 t.Fatalf("test case #%d %q: invalid seek", i, tc)
729 if parts[3] == "err" {
730 _, err := f.Seek(int64(offset), whence)
732 t.Fatalf("test case #%d %q: Seek returned nil error, want non-nil", i, tc)
735 got, err := f.Seek(int64(offset), whence)
737 t.Fatalf("test case #%d %q: Seek: %v", i, tc, err)
739 want, err := strconv.Atoi(parts[3])
741 t.Fatalf("test case #%d %q: invalid want %q", i, tc, parts[3])
743 if got != int64(want) {
744 t.Fatalf("test case #%d %q: got %d, want %d", i, tc, got, want)
749 n, err := f.Write([]byte(arg))
751 t.Fatalf("test case #%d %q: write: %v", i, tc, err)
754 t.Fatalf("test case #%d %q: write returned %d bytes, want %d", i, tc, n, len(arg))
758 g, err := fs.OpenFile(ctx, filename, os.O_RDONLY, 0666)
760 t.Fatalf("test case #%d %q: OpenFile: %v", i, tc, err)
762 gotBytes, err := ioutil.ReadAll(g)
764 t.Fatalf("test case #%d %q: ReadAll: %v", i, tc, err)
766 for i, c := range gotBytes {
771 got := string(gotBytes)
773 t.Fatalf("test case #%d %q:\ngot %q\nwant %q", i, tc, got, arg)
775 if err := g.Close(); err != nil {
776 t.Fatalf("test case #%d %q: Close: %v", i, tc, err)
780 n, err := strconv.Atoi(arg)
782 t.Fatalf("test case #%d %q: invalid size %q", i, tc, arg)
784 fi, err := fs.Stat(ctx, filename)
786 t.Fatalf("test case #%d %q: Stat: %v", i, tc, err)
788 if got, want := fi.Size(), int64(n); got != want {
789 t.Fatalf("test case #%d %q: got %d, want %d", i, tc, got, want)
795 // TestMemFileWriteAllocs tests that writing N consecutive 1KiB chunks to a
796 // memFile doesn't allocate a new buffer for each of those N times. Otherwise,
797 // calling io.Copy(aMemFile, src) is likely to have quadratic complexity.
798 func TestMemFileWriteAllocs(t *testing.T) {
799 if runtime.Compiler == "gccgo" {
800 t.Skip("gccgo allocates here")
802 ctx := context.Background()
804 f, err := fs.OpenFile(ctx, "/xxx", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
806 t.Fatalf("OpenFile: %v", err)
810 xxx := make([]byte, 1024)
815 a := testing.AllocsPerRun(100, func() {
818 // AllocsPerRun returns an integral value, so we compare the rounded-down
821 t.Fatalf("%v allocs per run, want 0", a)
825 func BenchmarkMemFileWrite(b *testing.B) {
826 ctx := context.Background()
828 xxx := make([]byte, 1024)
834 for i := 0; i < b.N; i++ {
835 f, err := fs.OpenFile(ctx, "/xxx", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
837 b.Fatalf("OpenFile: %v", err)
839 for j := 0; j < 100; j++ {
842 if err := f.Close(); err != nil {
843 b.Fatalf("Close: %v", err)
845 if err := fs.RemoveAll(ctx, "/xxx"); err != nil {
846 b.Fatalf("RemoveAll: %v", err)
851 func TestCopyMoveProps(t *testing.T) {
852 ctx := context.Background()
854 create := func(name string) error {
855 f, err := fs.OpenFile(ctx, name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
859 _, wErr := f.Write([]byte("contents"))
866 patch := func(name string, patches ...Proppatch) error {
867 f, err := fs.OpenFile(ctx, name, os.O_RDWR, 0666)
871 _, pErr := f.(DeadPropsHolder).Patch(patches)
878 props := func(name string) (map[xml.Name]Property, error) {
879 f, err := fs.OpenFile(ctx, name, os.O_RDWR, 0666)
883 m, pErr := f.(DeadPropsHolder).DeadProps()
895 XMLName: xml.Name{Space: "x:", Local: "boat"},
896 InnerXML: []byte("pea-green"),
899 XMLName: xml.Name{Space: "x:", Local: "ring"},
900 InnerXML: []byte("1 shilling"),
903 XMLName: xml.Name{Space: "x:", Local: "spoon"},
904 InnerXML: []byte("runcible"),
907 XMLName: xml.Name{Space: "x:", Local: "moon"},
908 InnerXML: []byte("light"),
911 if err := create("/src"); err != nil {
912 t.Fatalf("create /src: %v", err)
914 if err := patch("/src", Proppatch{Props: []Property{p0, p1}}); err != nil {
915 t.Fatalf("patch /src +p0 +p1: %v", err)
917 if _, err := copyFiles(ctx, fs, "/src", "/tmp", true, infiniteDepth, 0); err != nil {
918 t.Fatalf("copyFiles /src /tmp: %v", err)
920 if _, err := moveFiles(ctx, fs, "/tmp", "/dst", true); err != nil {
921 t.Fatalf("moveFiles /tmp /dst: %v", err)
923 if err := patch("/src", Proppatch{Props: []Property{p0}, Remove: true}); err != nil {
924 t.Fatalf("patch /src -p0: %v", err)
926 if err := patch("/src", Proppatch{Props: []Property{p2}}); err != nil {
927 t.Fatalf("patch /src +p2: %v", err)
929 if err := patch("/dst", Proppatch{Props: []Property{p1}, Remove: true}); err != nil {
930 t.Fatalf("patch /dst -p1: %v", err)
932 if err := patch("/dst", Proppatch{Props: []Property{p3}}); err != nil {
933 t.Fatalf("patch /dst +p3: %v", err)
936 gotSrc, err := props("/src")
938 t.Fatalf("props /src: %v", err)
940 wantSrc := map[xml.Name]Property{
944 if !reflect.DeepEqual(gotSrc, wantSrc) {
945 t.Fatalf("props /src:\ngot %v\nwant %v", gotSrc, wantSrc)
948 gotDst, err := props("/dst")
950 t.Fatalf("props /dst: %v", err)
952 wantDst := map[xml.Name]Property{
956 if !reflect.DeepEqual(gotDst, wantDst) {
957 t.Fatalf("props /dst:\ngot %v\nwant %v", gotDst, wantDst)
961 func TestWalkFS(t *testing.T) {
962 testCases := []struct {
967 walkFn filepath.WalkFunc
979 "infinite walk from root",
1001 "infinite walk from subdir",
1020 "depth 1 walk from root",
1039 "depth 1 walk from subdir",
1058 "depth 0 walk from subdir",
1075 "infinite walk from file",
1088 "infinite walk with skipped subdir",
1101 func(path string, info os.FileInfo, err error) error {
1102 if path == "/a/b/g" {
1103 return filepath.SkipDir
1115 ctx := context.Background()
1116 for _, tc := range testCases {
1117 fs, err := buildTestFS(tc.buildfs)
1119 t.Fatalf("%s: cannot create test filesystem: %v", tc.desc, err)
1122 traceFn := func(path string, info os.FileInfo, err error) error {
1123 if tc.walkFn != nil {
1124 err = tc.walkFn(path, info, err)
1129 got = append(got, path)
1132 fi, err := fs.Stat(ctx, tc.startAt)
1134 t.Fatalf("%s: cannot stat: %v", tc.desc, err)
1136 err = walkFS(ctx, fs, tc.depth, tc.startAt, fi, traceFn)
1138 t.Errorf("%s:\ngot error %v, want nil", tc.desc, err)
1142 sort.Strings(tc.want)
1143 if !reflect.DeepEqual(got, tc.want) {
1144 t.Errorf("%s:\ngot %q\nwant %q", tc.desc, got, tc.want)
1150 func buildTestFS(buildfs []string) (FileSystem, error) {
1151 // TODO: Could this be merged with the build logic in TestFS?
1153 ctx := context.Background()
1155 for _, b := range buildfs {
1156 op := strings.Split(b, " ")
1159 err := fs.Mkdir(ctx, op[1], os.ModeDir|0777)
1164 f, err := fs.OpenFile(ctx, op[1], os.O_RDWR|os.O_CREATE, 0666)
1170 f, err := fs.OpenFile(ctx, op[1], os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
1174 _, err = f.Write([]byte(op[2]))
1180 return nil, fmt.Errorf("unknown file operation %q", op[0])