1// Copyright 2021 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 fstest 6 7import ( 8 "errors" 9 "internal/testenv" 10 "io/fs" 11 "os" 12 "path/filepath" 13 "slices" 14 "strings" 15 "testing" 16) 17 18func TestSymlink(t *testing.T) { 19 testenv.MustHaveSymlink(t) 20 21 tmp := t.TempDir() 22 tmpfs := os.DirFS(tmp) 23 24 if err := os.WriteFile(filepath.Join(tmp, "hello"), []byte("hello, world\n"), 0644); err != nil { 25 t.Fatal(err) 26 } 27 28 if err := os.Symlink(filepath.Join(tmp, "hello"), filepath.Join(tmp, "hello.link")); err != nil { 29 t.Fatal(err) 30 } 31 32 if err := TestFS(tmpfs, "hello", "hello.link"); err != nil { 33 t.Fatal(err) 34 } 35} 36 37func TestDash(t *testing.T) { 38 m := MapFS{ 39 "a-b/a": {Data: []byte("a-b/a")}, 40 } 41 if err := TestFS(m, "a-b/a"); err != nil { 42 t.Error(err) 43 } 44} 45 46type shuffledFS MapFS 47 48func (fsys shuffledFS) Open(name string) (fs.File, error) { 49 f, err := MapFS(fsys).Open(name) 50 if err != nil { 51 return nil, err 52 } 53 return &shuffledFile{File: f}, nil 54} 55 56type shuffledFile struct{ fs.File } 57 58func (f *shuffledFile) ReadDir(n int) ([]fs.DirEntry, error) { 59 dirents, err := f.File.(fs.ReadDirFile).ReadDir(n) 60 // Shuffle in a deterministic way, all we care about is making sure that the 61 // list of directory entries is not is the lexicographic order. 62 // 63 // We do this to make sure that the TestFS test suite is not affected by the 64 // order of directory entries. 65 slices.SortFunc(dirents, func(a, b fs.DirEntry) int { 66 return strings.Compare(b.Name(), a.Name()) 67 }) 68 return dirents, err 69} 70 71func TestShuffledFS(t *testing.T) { 72 fsys := shuffledFS{ 73 "tmp/one": {Data: []byte("1")}, 74 "tmp/two": {Data: []byte("2")}, 75 "tmp/three": {Data: []byte("3")}, 76 } 77 if err := TestFS(fsys, "tmp/one", "tmp/two", "tmp/three"); err != nil { 78 t.Error(err) 79 } 80} 81 82// failPermFS is a filesystem that always fails with fs.ErrPermission. 83type failPermFS struct{} 84 85func (f failPermFS) Open(name string) (fs.File, error) { 86 if !fs.ValidPath(name) { 87 return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrInvalid} 88 } 89 return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrPermission} 90} 91 92func TestTestFSWrappedErrors(t *testing.T) { 93 err := TestFS(failPermFS{}) 94 if err == nil { 95 t.Fatal("error expected") 96 } 97 t.Logf("Error (expecting wrapped fs.ErrPermission):\n%v", err) 98 99 if !errors.Is(err, fs.ErrPermission) { 100 t.Errorf("error should be a wrapped ErrPermission: %#v", err) 101 } 102 103 // TestFS is expected to return a list of errors. 104 // Enforce that the list can be extracted for browsing. 105 var errs interface{ Unwrap() []error } 106 if !errors.As(err, &errs) { 107 t.Errorf("caller should be able to extract the errors as a list: %#v", err) 108 } else { 109 for _, err := range errs.Unwrap() { 110 // ErrPermission is expected 111 // but any other error must be reported. 112 if !errors.Is(err, fs.ErrPermission) { 113 t.Errorf("unexpected error: %v", err) 114 } 115 } 116 } 117} 118