github.com/grailbio/base@v0.0.11/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/grailbio/base/cmd/grail-fuse/gfs"
    18  	"github.com/grailbio/base/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  }