package afero import ( "os" "path/filepath" "runtime" "testing" "time" ) func TestNormalizePath(t *testing.T) { type test struct { input string expected string } data := []test{ {".", FilePathSeparator}, {"./", FilePathSeparator}, {"..", FilePathSeparator}, {"../", FilePathSeparator}, {"./..", FilePathSeparator}, {"./../", FilePathSeparator}, } for i, d := range data { cpath := normalizePath(d.input) if d.expected != cpath { t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, cpath) } } } func TestPathErrors(t *testing.T) { path := filepath.Join(".", "some", "path") path2 := filepath.Join(".", "different", "path") fs := NewMemMapFs() perm := os.FileMode(0755) // relevant functions: // func (m *MemMapFs) Chmod(name string, mode os.FileMode) error // func (m *MemMapFs) Chtimes(name string, atime time.Time, mtime time.Time) error // func (m *MemMapFs) Create(name string) (File, error) // func (m *MemMapFs) Mkdir(name string, perm os.FileMode) error // func (m *MemMapFs) MkdirAll(path string, perm os.FileMode) error // func (m *MemMapFs) Open(name string) (File, error) // func (m *MemMapFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) // func (m *MemMapFs) Remove(name string) error // func (m *MemMapFs) Rename(oldname, newname string) error // func (m *MemMapFs) Stat(name string) (os.FileInfo, error) err := fs.Chmod(path, perm) checkPathError(t, err, "Chmod") err = fs.Chtimes(path, time.Now(), time.Now()) checkPathError(t, err, "Chtimes") // fs.Create doesn't return an error err = fs.Mkdir(path2, perm) if err != nil { t.Error(err) } err = fs.Mkdir(path2, perm) checkPathError(t, err, "Mkdir") err = fs.MkdirAll(path2, perm) if err != nil { t.Error("MkdirAll:", err) } _, err = fs.Open(path) checkPathError(t, err, "Open") _, err = fs.OpenFile(path, os.O_RDWR, perm) checkPathError(t, err, "OpenFile") err = fs.Remove(path) checkPathError(t, err, "Remove") err = fs.RemoveAll(path) if err != nil { t.Error("RemoveAll:", err) } err = fs.Rename(path, path2) checkPathError(t, err, "Rename") _, err = fs.Stat(path) checkPathError(t, err, "Stat") } func checkPathError(t *testing.T, err error, op string) { pathErr, ok := err.(*os.PathError) if !ok { t.Error(op+":", err, "is not a os.PathError") return } _, ok = pathErr.Err.(*os.PathError) if ok { t.Error(op+":", err, "contains another os.PathError") } } // Fails if multiple file objects use the same file.at counter in MemMapFs func TestMultipleOpenFiles(t *testing.T) { defer removeAllTestFiles(t) const fileName = "afero-demo2.txt" var data = make([][]byte, len(Fss)) for i, fs := range Fss { dir := testDir(fs) path := filepath.Join(dir, fileName) fh1, err := fs.Create(path) if err != nil { t.Error("fs.Create failed: " + err.Error()) } _, err = fh1.Write([]byte("test")) if err != nil { t.Error("fh.Write failed: " + err.Error()) } _, err = fh1.Seek(0, os.SEEK_SET) if err != nil { t.Error(err) } fh2, err := fs.OpenFile(path, os.O_RDWR, 0777) if err != nil { t.Error("fs.OpenFile failed: " + err.Error()) } _, err = fh2.Seek(0, os.SEEK_END) if err != nil { t.Error(err) } _, err = fh2.Write([]byte("data")) if err != nil { t.Error(err) } err = fh2.Close() if err != nil { t.Error(err) } _, err = fh1.Write([]byte("data")) if err != nil { t.Error(err) } err = fh1.Close() if err != nil { t.Error(err) } // the file now should contain "datadata" data[i], err = ReadFile(fs, path) if err != nil { t.Error(err) } } for i, fs := range Fss { if i == 0 { continue } if string(data[0]) != string(data[i]) { t.Errorf("%s and %s don't behave the same\n"+ "%s: \"%s\"\n%s: \"%s\"\n", Fss[0].Name(), fs.Name(), Fss[0].Name(), data[0], fs.Name(), data[i]) } } } // Test if file.Write() fails when opened as read only func TestReadOnly(t *testing.T) { defer removeAllTestFiles(t) const fileName = "afero-demo.txt" for _, fs := range Fss { dir := testDir(fs) path := filepath.Join(dir, fileName) f, err := fs.Create(path) if err != nil { t.Error(fs.Name()+":", "fs.Create failed: "+err.Error()) } _, err = f.Write([]byte("test")) if err != nil { t.Error(fs.Name()+":", "Write failed: "+err.Error()) } f.Close() f, err = fs.Open(path) if err != nil { t.Error("fs.Open failed: " + err.Error()) } _, err = f.Write([]byte("data")) if err == nil { t.Error(fs.Name()+":", "No write error") } f.Close() f, err = fs.OpenFile(path, os.O_RDONLY, 0644) if err != nil { t.Error("fs.Open failed: " + err.Error()) } _, err = f.Write([]byte("data")) if err == nil { t.Error(fs.Name()+":", "No write error") } f.Close() } } func TestWriteCloseTime(t *testing.T) { defer removeAllTestFiles(t) const fileName = "afero-demo.txt" for _, fs := range Fss { dir := testDir(fs) path := filepath.Join(dir, fileName) f, err := fs.Create(path) if err != nil { t.Error(fs.Name()+":", "fs.Create failed: "+err.Error()) } f.Close() f, err = fs.Create(path) if err != nil { t.Error(fs.Name()+":", "fs.Create failed: "+err.Error()) } fi, err := f.Stat() if err != nil { t.Error(fs.Name()+":", "Stat failed: "+err.Error()) } timeBefore := fi.ModTime() // sorry for the delay, but we have to make sure time advances, // also on non Un*x systems... switch runtime.GOOS { case "windows": time.Sleep(2 * time.Second) case "darwin": time.Sleep(1 * time.Second) default: // depending on the FS, this may work with < 1 second, on my old ext3 it does not time.Sleep(1 * time.Second) } _, err = f.Write([]byte("test")) if err != nil { t.Error(fs.Name()+":", "Write failed: "+err.Error()) } f.Close() fi, err = fs.Stat(path) if err != nil { t.Error(fs.Name()+":", "fs.Stat failed: "+err.Error()) } if fi.ModTime().Equal(timeBefore) { t.Error(fs.Name()+":", "ModTime was not set on Close()") } } } // This test should be run with the race detector on: // go test -race -v -timeout 10s -run TestRacingDeleteAndClose func TestRacingDeleteAndClose(t *testing.T) { fs := NewMemMapFs() pathname := "testfile" f, err := fs.Create(pathname) if err != nil { t.Fatal(err) } in := make(chan bool) go func() { <-in f.Close() }() go func() { <-in fs.Remove(pathname) }() close(in) }