test_fs.go (3852B)
1 package lib9p 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/fs" 7 "path" 8 "strings" 9 "time" 10 ) 11 12 const sleepTime = 1 * time.Second 13 14 type TestFile struct { 15 Fsys *TestFS 16 Parent *TestFile 17 Children []*TestFile 18 Content []byte 19 Reader *bytes.Reader 20 21 St Stat 22 } 23 24 func (f *TestFile) Stat() (*FileInfo, error) { 25 return &FileInfo{Stat: f.St}, nil 26 } 27 func (f *TestFile) Close() error { 28 f.Reader = nil 29 return nil 30 } 31 32 func (f *TestFile) Read(b []byte) (int, error) { 33 if f.Fsys.Slow { 34 time.Sleep(sleepTime) 35 } 36 return f.Reader.Read(b) 37 } 38 39 func (f *TestFile) ReadAt(b []byte, off int64) (n int, err error) { 40 if f.Fsys.Slow { 41 time.Sleep(sleepTime) 42 } 43 return f.Reader.ReadAt(b, off) 44 } 45 46 func (f *TestFile) ReadDir(n int) ([]*DirEntry, error) { 47 de := make([]*DirEntry, len(f.Children)) 48 for i, c := range f.Children { 49 de[i], _ = c.Stat() 50 } 51 return de, nil 52 } 53 54 func (f *TestFile) WriteAt(p []byte, off int64) (int, error) { 55 if f.Fsys.Slow { 56 time.Sleep(sleepTime) 57 } 58 if f.Reader == nil { 59 return 0, fmt.Errorf("not open") 60 } 61 if off < 0 || off > int64(len(f.Content)) { 62 return 0, fmt.Errorf("bad offset") 63 } 64 65 if off+int64(len(p)) > int64(len(f.Content)) { 66 newcon := make([]byte, off+int64(len(p))) 67 copy(newcon, f.Content) 68 f.Content = newcon 69 } 70 copy(f.Content[off:], p) 71 f.Reader.Reset(f.Content) 72 return len(p), nil 73 } 74 75 type TestFS struct { 76 Root *TestFile 77 Slow bool 78 } 79 80 func (fs *TestFS) OpenFile(path string, omode OpenMode, perm fs.FileMode) (File, error) { 81 path = clean9path(path) 82 wnames := split9path(path) 83 f, err := fs.walk(wnames) 84 if err != nil { 85 return nil, fmt.Errorf("walk: %v", err) 86 } 87 f.Reader = bytes.NewReader(f.Content) 88 return f, nil 89 } 90 91 func (fs *TestFS) walk(wnames []string) (*TestFile, error) { 92 cwd := fs.Root 93 L: 94 for i, name := range wnames { 95 for _, child := range cwd.Children { 96 if child.St.Name == name { 97 cwd = child 98 continue L 99 } 100 } 101 return nil, fmt.Errorf("%s not found", strings.Join(wnames[:i+1], "/")) 102 } 103 return cwd, nil 104 } 105 106 func clean9path(name string) string { 107 // TODO: eliminate leading ".." 108 name = path.Clean(name) 109 return name 110 } 111 112 func split9path(path string) []string { 113 if path == "." || path == "/" { 114 return []string{} 115 } 116 return strings.Split(path, "/") 117 } 118 119 var fsys *TestFS 120 121 func init() { 122 fsys = &TestFS{ 123 Root: &TestFile{ 124 St: Stat{ 125 Qid: Qid{Path: 0, Type: QTDIR}, 126 Mode: FileMode(fs.ModeDir | 0755), 127 Name: "root", 128 Uid: "glenda", 129 Gid: "glenda", 130 Muid: "glenda", 131 }, 132 Children: []*TestFile{ 133 &TestFile{ 134 Content: []byte("a\n"), 135 St: Stat{ 136 Qid: Qid{Path: 1, Type: QTFILE}, 137 Mode: FileMode(0644), 138 Name: "a", 139 Uid: "glenda", 140 Gid: "glenda", 141 Muid: "glenda", 142 }, 143 }, 144 &TestFile{ 145 Content: []byte("b\n"), 146 St: Stat{ 147 Qid: Qid{Path: 2, Type: QTFILE}, 148 Mode: FileMode(0400), 149 Name: "b", 150 Uid: "ken", 151 Gid: "ken", 152 Muid: "ken", 153 }, 154 }, 155 &TestFile{ 156 St: Stat{ 157 Qid: Qid{Path: 3, Type: QTDIR}, 158 Mode: FileMode(fs.ModeDir | 0755), 159 Name: "dir", 160 Uid: "rob", 161 Gid: "rob", 162 Muid: "rob", 163 }, 164 Children: []*TestFile{ 165 &TestFile{ 166 Content: []byte("unko\n"), 167 St: Stat{ 168 Qid: Qid{Path: 4, Type: QTFILE}, 169 Mode: FileMode(0666), 170 Name: "file", 171 Uid: "brian", 172 Gid: "brian", 173 Muid: "dennis", 174 }, 175 }, 176 }, 177 }, 178 }, 179 }, 180 } 181 SetFsysAndParent(fsys, nil) 182 } 183 184 // SetFsysAndParent sets file.fsys and file.parent for every file in the fsys. 185 // Pass nil as file to setup entire file system. 186 func SetFsysAndParent(fsys *TestFS, file *TestFile) { 187 if file == nil { 188 file = fsys.Root 189 file.Parent = fsys.Root 190 file.Fsys = fsys 191 } 192 for _, child := range file.Children { 193 child.Parent = file 194 child.Fsys = fsys 195 SetFsysAndParent(fsys, child) 196 } 197 }