github.com/Schaudge/grailbase@v0.0.0-20240223061707-44c758a471c0/cmd/grail-fuse/gfs/gfs_test.go (about) 1 //+build !unit 2 3 package gfs_test 4 5 import ( 6 "context" 7 "fmt" 8 "io" 9 "io/ioutil" 10 golog "log" 11 "os" 12 "os/exec" 13 "sort" 14 "syscall" 15 "testing" 16 17 "github.com/Schaudge/grailbase/cmd/grail-fuse/gfs" 18 "github.com/Schaudge/grailbase/log" 19 "github.com/grailbio/testutil/assert" 20 "github.com/grailbio/testutil/expect" 21 "github.com/grailbio/testutil/h" 22 "github.com/hanwen/go-fuse/v2/fs" 23 "github.com/hanwen/go-fuse/v2/fuse" 24 "github.com/hanwen/go-fuse/v2/posixtest" 25 ) 26 27 type tester struct { 28 t *testing.T 29 remoteDir string 30 mountDir string 31 tempDir string 32 server *fuse.Server 33 } 34 35 type logOutputter struct{} 36 37 func (logOutputter) Level() log.Level { return log.Debug } 38 39 func (logOutputter) Output(calldepth int, level log.Level, s string) error { 40 return golog.Output(calldepth+1, s) 41 } 42 43 func newTester(t *testing.T, remoteDir string) *tester { 44 log.SetFlags(log.Lmicroseconds | log.Lshortfile) 45 log.SetOutputter(logOutputter{}) 46 if remoteDir == "" { 47 var err error 48 remoteDir, err = ioutil.TempDir("", "remote") 49 assert.NoError(t, err) 50 } 51 tempDir, err := ioutil.TempDir("", "temp") 52 assert.NoError(t, err) 53 mountDir, err := ioutil.TempDir("", "mount") 54 assert.NoError(t, err) 55 root := gfs.NewRoot(context.Background(), remoteDir, tempDir) 56 server, err := fs.Mount(mountDir, root, &fs.Options{ 57 MountOptions: fuse.MountOptions{ 58 FsName: "test", 59 DisableXAttrs: true, 60 Debug: true}}) 61 assert.NoError(t, err) 62 log.Printf("mount remote dir %s on %s, tmp %s", remoteDir, mountDir, tempDir) 63 return &tester{ 64 t: t, 65 remoteDir: remoteDir, 66 mountDir: mountDir, 67 tempDir: tempDir, 68 server: server, 69 } 70 } 71 72 func (t *tester) MountDir() string { return t.mountDir } 73 func (t *tester) RemoteDir() string { return t.remoteDir } 74 75 func (t *tester) Cleanup() { 76 log.Printf("unmount %s", t.mountDir) 77 assert.NoError(t.t, t.server.Unmount()) 78 assert.NoError(t.t, os.RemoveAll(t.mountDir)) 79 log.Printf("unmount %s done", t.mountDir) 80 } 81 82 func writeFile(t *testing.T, path string, data string) { 83 assert.NoError(t, ioutil.WriteFile(path, []byte(data), 0600)) 84 } 85 86 func readFile(path string) string { 87 data, err := ioutil.ReadFile(path) 88 if err != nil { 89 return fmt.Sprintf("read %s: error %v", path, err) 90 } 91 return string(data) 92 } 93 94 func readdir(t *testing.T, dir string) []string { 95 fp, err := os.Open(dir) 96 assert.NoError(t, err) 97 names, err := fp.Readdirnames(0) 98 assert.NoError(t, err) 99 sort.Strings(names) 100 assert.NoError(t, fp.Close()) 101 return names 102 } 103 104 func TestSimple(t *testing.T) { 105 var ( 106 err error 107 remoteDir string 108 ) 109 if remoteDir, err = ioutil.TempDir("", "remote"); err != nil { 110 log.Panic(err) 111 } 112 defer func() { _ = os.RemoveAll(remoteDir) }() 113 114 writeFile(t, remoteDir+"/fox.txt", "pink fox") 115 tc := newTester(t, remoteDir) 116 defer tc.Cleanup() 117 118 expect.EQ(t, readFile(tc.MountDir()+"/fox.txt"), "pink fox") 119 expect.That(t, readdir(t, tc.MountDir()), h.ElementsAre("fox.txt")) 120 assert.NoError(t, os.Remove(tc.MountDir()+"/fox.txt")) 121 expect.HasSubstr(t, readFile(tc.MountDir()+"/fox.txt"), "no such file") 122 expect.HasSubstr(t, readFile(remoteDir+"/fox.txt"), "no such file") 123 expect.That(t, readdir(t, tc.MountDir()), h.ElementsAre()) 124 } 125 126 func TestOverwrite(t *testing.T) { 127 tc := newTester(t, "") 128 defer tc.Cleanup() 129 130 path := tc.MountDir() + "/bar.txt" 131 fp, err := os.Create(path) 132 assert.NoError(t, err) 133 _, err = fp.Write([]byte("purple dog")) 134 assert.NoError(t, err) 135 assert.NoError(t, fp.Close()) 136 expect.EQ(t, readFile(tc.RemoteDir()+"/bar.txt"), "purple dog") 137 expect.EQ(t, readFile(path), "purple dog") 138 139 fp, err = os.Create(path) 140 assert.NoError(t, err) 141 _, err = fp.Write([]byte("white giraffe")) 142 assert.NoError(t, err) 143 assert.NoError(t, fp.Close()) 144 expect.EQ(t, readFile(tc.RemoteDir()+"/bar.txt"), "white giraffe") 145 expect.EQ(t, readFile(path), "white giraffe") 146 } 147 148 func TestReadWrite(t *testing.T) { 149 tc := newTester(t, "") 150 defer tc.Cleanup() 151 152 path := tc.MountDir() + "/baz.txt" 153 writeFile(t, path, "purple cat") 154 fp, err := os.OpenFile(path, os.O_RDWR, 0600) 155 assert.NoError(t, err) 156 _, err = fp.Write([]byte("yellow")) 157 assert.NoError(t, err) 158 assert.NoError(t, fp.Close()) 159 expect.EQ(t, readFile(path), "yellow cat") 160 161 fp, err = os.OpenFile(path, os.O_RDWR, 0600) 162 assert.NoError(t, err) 163 _, err = fp.Seek(7, io.SeekStart) 164 assert.NoError(t, err) 165 _, err = fp.Write([]byte("bat")) 166 assert.NoError(t, fp.Close()) 167 expect.EQ(t, readFile(path), "yellow bat") 168 } 169 170 func TestAppend(t *testing.T) { 171 tc := newTester(t, "") 172 defer tc.Cleanup() 173 174 path := tc.MountDir() + "/append.txt" 175 writeFile(t, path, "orange ape") 176 log.Printf("reopening %s with O_APPEND", path) 177 fp, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND, 0600) 178 assert.NoError(t, err) 179 log.Printf("writing to %s", path) 180 _, err = fp.Write([]byte("red donkey")) 181 assert.NoError(t, err) 182 assert.NoError(t, fp.Close()) 183 expect.EQ(t, readFile(path), "orange apered donkey") 184 } 185 186 func TestMkdir(t *testing.T) { 187 tc := newTester(t, "") 188 defer tc.Cleanup() 189 190 path := tc.MountDir() + "/dir0" 191 assert.NoError(t, os.Mkdir(path, 0755)) 192 assert.EQ(t, readdir(t, path), []string{}) 193 } 194 195 func TestDup(t *testing.T) { 196 tc := newTester(t, "") 197 defer tc.Cleanup() 198 199 path := tc.MountDir() + "/f0.txt" 200 fp0, err := os.Create(path) 201 assert.NoError(t, err) 202 203 fd1, err := syscall.Dup(int(fp0.Fd())) 204 assert.NoError(t, err) 205 fp1 := os.NewFile(uintptr(fd1), path) 206 207 _, err = fp0.Write([]byte("yellow bug")) 208 assert.NoError(t, err) 209 assert.NoError(t, fp0.Close()) 210 _, err = fp1.Write([]byte("yellow hug")) 211 assert.HasSubstr(t, err, "bad file descriptor") 212 assert.NoError(t, fp1.Close()) 213 expect.EQ(t, readFile(path), "yellow bug") 214 } 215 216 func TestShell(t *testing.T) { 217 tc := newTester(t, "") 218 defer tc.Cleanup() 219 220 path := tc.MountDir() + "/cat.txt" 221 cmd := exec.Command("sh", "-c", fmt.Sprintf("echo foo >%s", path)) 222 assert.NoError(t, cmd.Run()) 223 expect.EQ(t, readFile(path), "foo\n") 224 225 cmd = exec.Command("sh", "-c", fmt.Sprintf("echo bar >>%s", path)) 226 assert.NoError(t, cmd.Run()) 227 expect.EQ(t, readFile(path), "foo\nbar\n") 228 229 path2 := tc.MountDir() + "/cat2.txt" 230 log.Printf("Start cat") 231 cmd = exec.Command("sh", "-c", fmt.Sprintf("cat <%s >%s", path, path2)) 232 assert.NoError(t, cmd.Run()) 233 expect.EQ(t, readFile(path2), "foo\nbar\n") 234 } 235 236 func TestPosix(t *testing.T) { 237 tc := newTester(t, "") 238 defer tc.Cleanup() 239 240 // Regression test for a directory listing bug (erroneously skipping some entries). 241 t.Run("ReadDir", func(t *testing.T) { 242 posixtest.ReadDir(t, tc.MountDir()) 243 }) 244 245 // TODO(josh): Consider running more tests from posixtest. This may require new features. 246 }