file_test.go (7346B)
1 package client 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io" 8 "io/fs" 9 "reflect" 10 "strings" 11 "testing" 12 13 "git.mtkn.jp/lib9p" 14 ) 15 16 func TestFileInterface(t *testing.T) { 17 var _ lib9p.File = &File{} 18 var _ lib9p.WriterFile = &File{} 19 var _ lib9p.WriterStatFile = &File{} 20 var _ lib9p.ReadDirFile = &File{} 21 } 22 23 // Mount runs a server with fs and mounts it as *FS. 24 func mount(ctx context.Context, fs lib9p.FS) (cfs *FS, err error) { 25 cr, sw := io.Pipe() 26 sr, cw := io.Pipe() 27 s := lib9p.NewServer(fs) 28 go s.Serve(ctx, sr, sw) 29 cfs, err = Mount(ctx, cr, cw, "glenda", "") 30 if err != nil { 31 return nil, fmt.Errorf("mount: %v", err) 32 } 33 return cfs, nil 34 } 35 36 // TestFileStat tests whether Stat returns the same lib9p.Stat as testfs defines. 37 func TestFileStat(t *testing.T) { 38 ctx, cancel := context.WithCancel(context.Background()) 39 cfs, err := mount(ctx, testfs) 40 if err != nil { 41 t.Fatal(err) 42 } 43 defer cancel() 44 var walk = func(path string, d fs.DirEntry, err error) error { 45 cfi, err := fs.Stat(lib9p.ExportFS{FS: cfs}, path) 46 if err != nil { 47 if strings.Contains(err.Error(), "permission") { 48 return nil 49 } 50 t.Error(err) 51 return nil 52 } 53 tfi, err := fs.Stat(lib9p.ExportFS{FS: testfs}, path) 54 if err != nil { 55 t.Error(err) 56 } 57 if !reflect.DeepEqual(cfi, tfi) { 58 t.Errorf("fi not match:\n\twant: %v\n\tgot: %v", tfi, cfi) 59 } 60 return nil 61 } 62 if err := fs.WalkDir(lib9p.ExportFS{FS: testfs}, ".", walk); err != nil { 63 t.Error(err) 64 } 65 } 66 67 // TODO: implement 68 func TestFileWStat(t *testing.T) { 69 ctx, cancel := context.WithCancel(context.Background()) 70 cfs, err := mount(ctx, testfs) 71 if err != nil { 72 t.Fatal(err) 73 } 74 defer cancel() 75 _, _ = ctx, cfs 76 } 77 78 // TestClose checks it *File.Close calls are transmitted to the server and 79 // *testFile.Close is called for each *File.Close. 80 func TestClose(t *testing.T) { 81 ctx, cancel := context.WithCancel(context.Background()) 82 cfs, err := mount(ctx, testfs) 83 if err != nil { 84 t.Fatal(err) 85 } 86 defer cancel() 87 var walk = func(path string, d fs.DirEntry, err error) error { 88 if err != nil { 89 if err == io.EOF { 90 return nil 91 } 92 return err 93 } 94 tf, err := testfs.walkPath(path) 95 if err != nil { 96 t.Error(err) 97 return nil 98 } 99 cf, err := cfs.OpenFile(path, lib9p.O_RDONLY) 100 if err != nil { 101 if strings.Contains(err.Error(), "permission") { 102 return nil 103 } 104 return err 105 } 106 cc := make(chan struct{}) 107 tf.closec = cc 108 cf.Close() 109 <-cc 110 return nil 111 } 112 if err := fs.WalkDir(lib9p.ExportFS{FS: testfs}, ".", walk); err != nil { 113 t.Error(err) 114 } 115 } 116 117 // TestFileRead checks *File.Read reads the same []byte as *testFile.content. 118 func TestFileRead(t *testing.T) { 119 ctx, cancel := context.WithCancel(context.Background()) 120 cfs, err := mount(ctx, testfs) 121 if err != nil { 122 t.Fatal(err) 123 } 124 defer cancel() 125 var walk = func(path string, d fs.DirEntry, err error) error { 126 if err != nil { 127 if err == io.EOF { 128 return nil 129 } 130 return err 131 } 132 if d.IsDir() { 133 return nil 134 } 135 tf, err := testfs.walkPath(path) 136 if err != nil { 137 t.Error(err) 138 return nil 139 } 140 cf, err := cfs.OpenFile(path, lib9p.O_RDONLY) 141 if err != nil { 142 if strings.Contains(err.Error(), "permission") { 143 return nil 144 } 145 return err 146 } 147 defer cf.Close() 148 cb := make([]byte, len(tf.content)*2) 149 n, err := cf.Read(cb) 150 if err != nil { 151 t.Error(err) 152 return nil 153 } 154 if n != len(tf.content) || !bytes.Equal(cb[:n], tf.content) { 155 t.Errorf("read wrong content: want: %v, got: %v", tf.content, cb[:n]) 156 return nil 157 } 158 return nil 159 } 160 if err := fs.WalkDir(lib9p.ExportFS{FS: testfs}, ".", walk); err != nil { 161 t.Error(err) 162 } 163 } 164 165 // TestFileWrite tests the Write method of the struct File. 166 // It writes some bytes to a testFile using File.Write and checks if 167 // the underlying content of testFile is changed correctly. 168 func TestFileWrite(t *testing.T) { 169 ctx, cancel := context.WithCancel(context.Background()) 170 cfs, err := mount(ctx, testfs) 171 if err != nil { 172 t.Fatal(err) 173 } 174 defer cancel() 175 tf, err := testfs.walkPath("dir/file") 176 if err != nil { 177 t.Fatal(err) 178 } 179 orig := bytes.Clone(tf.content) 180 defer func() { tf.content = bytes.Clone(orig) }() 181 // consecutive writes to the same file without closing it. 182 ctnt0 := []byte("hogehoge") 183 ctnt1 := []byte("fugafuga") 184 cf, err := cfs.OpenFile("dir/file", lib9p.O_RDWR) 185 if err != nil { 186 t.Fatal(err) 187 } 188 _, err = cf.(lib9p.WriterFile).Write(ctnt0) 189 if err != nil { 190 t.Fatal(err) 191 } 192 _, err = cf.(lib9p.WriterFile).Write(ctnt1) 193 if err != nil { 194 t.Fatal(err) 195 } 196 if !bytes.Equal(tf.content[:len(ctnt0)+len(ctnt1)], bytes.Join([][]byte{ctnt0, ctnt1}, nil)) { 197 t.Errorf("not written propperly: want: %v, got: %v", 198 string(bytes.Join([][]byte{ctnt0, ctnt1}, nil)), 199 string(tf.content[:len(ctnt0)+len(ctnt1)])) 200 } 201 } 202 203 func FuzzFileWrite(f *testing.F) { 204 for _, seed := range []string{"", "fuga", "fugafuga", "fugafugafuga"} { 205 f.Add([]byte(seed)) 206 } 207 f.Add(make([]byte, 9000)) 208 ctx, cancel := context.WithCancel(context.Background()) 209 cfs, err := mount(ctx, testfs) 210 if err != nil { 211 f.Fatal(err) 212 } 213 defer cancel() 214 tf, err := testfs.walkPath("dir/file") 215 if err != nil { 216 f.Fatal(err) 217 } 218 orig := bytes.Clone(tf.content) 219 f.Fuzz(func(t *testing.T, in []byte) { 220 defer func() { tf.content = bytes.Clone(orig) }() 221 cf, err := cfs.OpenFile("dir/file", lib9p.O_RDWR) 222 if err != nil { 223 t.Fatal(err) 224 } 225 _, err = cf.(lib9p.WriterFile).Write(in) 226 if err != nil { 227 t.Fatal(err) 228 } 229 if !bytes.Equal(tf.content[:len(in)], in) { 230 t.Errorf("not written propperly: want: %v, got: %v", 231 string(in), string(tf.content[:len(in)])) 232 } 233 cf.Close() 234 }) 235 } 236 237 // TestReadDir tests whether ReadDir returns the same dir entries as testfs 238 // by comparing their *lib9p.Stat. 239 func TestReadDir(t *testing.T) { 240 ctx, cancel := context.WithCancel(context.Background()) 241 cfs, err := mount(ctx, testfs) 242 if err != nil { 243 t.Fatal(err) 244 } 245 defer cancel() 246 var walk = func(path string, d fs.DirEntry, err error) error { 247 t.Log(path) 248 if err != nil { 249 return err 250 } 251 if !d.IsDir() { 252 return nil 253 } 254 cf, err := cfs.OpenFile(path, lib9p.O_RDONLY) 255 if err != nil { 256 if !strings.Contains(err.Error(), "permission") { 257 t.Error(err) 258 } 259 return nil 260 } 261 cde, err := cf.(lib9p.ReadDirFile).ReadDir(-1) 262 if err != nil { 263 if err != io.EOF { 264 t.Error(err) 265 return nil 266 } 267 } 268 tf, err := testfs.OpenFile(path, lib9p.O_RDONLY) 269 if err != nil { 270 if !strings.Contains(err.Error(), "permission") { 271 t.Error(err) 272 } 273 return nil 274 } 275 tde, err := tf.(lib9p.ReadDirFile).ReadDir(-1) 276 if err != nil { 277 if err != io.EOF { 278 t.Error(err) 279 return nil 280 } 281 } 282 if len(cde) != len(tde) { 283 t.Errorf("%s: length of direntry not match", path) 284 return nil 285 } 286 demap := make(map[string]*lib9p.Stat) 287 for _, cde0 := range cde { 288 info, err := cde0.Info() 289 if err != nil { 290 t.Error(err) 291 return nil 292 } 293 demap[cde0.Name()] = info.Sys().(*lib9p.Stat) 294 } 295 for _, tde0 := range tde { 296 info, err := tde0.Info() 297 if err != nil { 298 t.Error(err) 299 return nil 300 } 301 if !reflect.DeepEqual(demap[tde0.Name()], info.Sys().(*lib9p.Stat)) { 302 t.Errorf("%s: stat doesn't match:\n\twant: %v\n\tgot: %v", 303 path, info.Sys().(*lib9p.Stat), demap[tde0.Name()]) 304 return nil 305 } 306 } 307 return nil 308 } 309 if err := fs.WalkDir(lib9p.ExportFS{FS: testfs}, ".", walk); err != nil { 310 t.Error(err) 311 } 312 }