github.com/sdboyer/gps@v0.16.3/filesystem_test.go (about) 1 package gps 2 3 import ( 4 "os" 5 "path/filepath" 6 "testing" 7 ) 8 9 // This file contains utilities for running tests around file system state. 10 11 // fspath represents a file system path in an OS-agnostic way. 12 type fsPath []string 13 14 func (f fsPath) String() string { return filepath.Join(f...) } 15 16 func (f fsPath) prepend(prefix string) fsPath { 17 p := fsPath{filepath.FromSlash(prefix)} 18 return append(p, f...) 19 } 20 21 type fsTestCase struct { 22 before, after filesystemState 23 } 24 25 // filesystemState represents the state of a file system. It has a setup method 26 // which inflates its state to the actual host file system, and an assert 27 // method which checks that the actual file system matches the described state. 28 type filesystemState struct { 29 root string 30 dirs []fsPath 31 files []fsPath 32 links []fsLink 33 } 34 35 // assert makes sure that the fs state matches the state of the actual host 36 // file system 37 func (fs filesystemState) assert(t *testing.T) { 38 dirMap := make(map[string]bool) 39 fileMap := make(map[string]bool) 40 linkMap := make(map[string]bool) 41 42 for _, d := range fs.dirs { 43 dirMap[d.prepend(fs.root).String()] = true 44 } 45 for _, f := range fs.files { 46 fileMap[f.prepend(fs.root).String()] = true 47 } 48 for _, l := range fs.links { 49 linkMap[l.path.prepend(fs.root).String()] = true 50 } 51 52 err := filepath.Walk(fs.root, func(path string, info os.FileInfo, err error) error { 53 if err != nil { 54 t.Errorf("filepath.Walk path=%q err=%q", path, err) 55 return err 56 } 57 58 if path == fs.root { 59 return nil 60 } 61 62 // Careful! Have to check whether the path is a symlink first because, on 63 // windows, a symlink to a directory will return 'true' for info.IsDir(). 64 if (info.Mode() & os.ModeSymlink) != 0 { 65 if linkMap[path] { 66 delete(linkMap, path) 67 } else { 68 t.Errorf("unexpected symlink exists %q", path) 69 } 70 return nil 71 } 72 73 if info.IsDir() { 74 if dirMap[path] { 75 delete(dirMap, path) 76 } else { 77 t.Errorf("unexpected directory exists %q", path) 78 } 79 return nil 80 } 81 82 if fileMap[path] { 83 delete(fileMap, path) 84 } else { 85 t.Errorf("unexpected file exists %q", path) 86 } 87 return nil 88 }) 89 90 if err != nil { 91 t.Errorf("filesystem.Walk err=%q", err) 92 } 93 94 for d := range dirMap { 95 t.Errorf("could not find expected directory %q", d) 96 } 97 for f := range fileMap { 98 t.Errorf("could not find expected file %q", f) 99 } 100 for l := range linkMap { 101 t.Errorf("could not find expected symlink %q", l) 102 } 103 } 104 105 // fsLink represents a symbolic link. 106 type fsLink struct { 107 path fsPath 108 to string 109 } 110 111 // setup inflates fs onto the actual host file system 112 func (fs filesystemState) setup(t *testing.T) { 113 fs.setupDirs(t) 114 fs.setupFiles(t) 115 fs.setupLinks(t) 116 } 117 118 func (fs filesystemState) setupDirs(t *testing.T) { 119 for _, dir := range fs.dirs { 120 p := dir.prepend(fs.root) 121 if err := os.MkdirAll(p.String(), 0777); err != nil { 122 t.Fatalf("os.MkdirAll(%q, 0777) err=%q", p, err) 123 } 124 } 125 } 126 127 func (fs filesystemState) setupFiles(t *testing.T) { 128 for _, file := range fs.files { 129 p := file.prepend(fs.root) 130 f, err := os.Create(p.String()) 131 if err != nil { 132 t.Fatalf("os.Create(%q) err=%q", p, err) 133 } 134 if err := f.Close(); err != nil { 135 t.Fatalf("file %q Close() err=%q", p, err) 136 } 137 } 138 } 139 140 func (fs filesystemState) setupLinks(t *testing.T) { 141 for _, link := range fs.links { 142 p := link.path.prepend(fs.root) 143 144 // On Windows, relative symlinks confuse filepath.Walk. This is golang/go 145 // issue 17540. So, we'll just sigh and do absolute links, assuming they are 146 // relative to the directory of link.path. 147 dir := filepath.Dir(p.String()) 148 to := filepath.Join(dir, link.to) 149 150 if err := os.Symlink(to, p.String()); err != nil { 151 t.Fatalf("os.Symlink(%q, %q) err=%q", to, p, err) 152 } 153 } 154 }