ollie/virtfs/tree_test.go

274 lines
6.6 KiB
Go

package virtfs
import (
"context"
"os"
"path/filepath"
"testing"
)
func TestTreeMkdirAll(t *testing.T) {
tmpDir := t.TempDir()
tree := &Tree{Tree: []string{tmpDir}, perm: 0755}
err := tree.MkdirAll("a/b/c")
if err != nil {
t.Fatalf("MkdirAll: %v", err)
}
info, err := os.Stat(filepath.Join(tmpDir, "a", "b", "c"))
if err != nil {
t.Fatalf("Stat after MkdirAll: %v", err)
}
if !info.IsDir() {
t.Error("expected directory")
}
}
func TestTreeMkdirAllNoBackingDir(t *testing.T) {
tree := &Tree{perm: 0755}
err := tree.MkdirAll("somedir")
if err == nil {
t.Fatal("expected error for nil backing dir")
}
}
func TestTreeWithCustomFunctions(t *testing.T) {
listed := false
tree := &Tree{perm: 0755}
tree.listFn = func(_ []string) ([]os.DirEntry, error) {
listed = true
return []os.DirEntry{fileEntry("a", 0644)}, nil
}
entries, err := tree.List()
if err != nil {
t.Fatalf("List: %v", err)
}
if !listed {
t.Error("listFn not called")
}
if len(entries) != 1 || entries[0].Name() != "a" {
t.Errorf("entries = %v", entries)
}
}
func TestTreeOpen(t *testing.T) {
tree := &Tree{perm: 0644}
tree.openFn = func(_ []string, name string) (File, error) {
return &fileConfig{
StatFn: func() (os.FileInfo, error) { return &SyntheticFileInfo{Name_: name}, nil },
ReadFn: func() ([]byte, error) { return []byte("content"), nil },
WriteFn: func([]byte) error { return nil },
BlockingReadFn: func(context.Context, string) ([]byte, string, error) { return nil, "", nil },
}, nil
}
f, err := tree.Open("test")
if err != nil {
t.Fatalf("Open: %v", err)
}
data, err := f.Read()
if err != nil {
t.Fatalf("Read: %v", err)
}
if string(data) != "content" {
t.Errorf("Read = %q", data)
}
}
func TestTreeOpenNotFound(t *testing.T) {
tree := &Tree{perm: 0644}
_, err := tree.Open("nonexistent")
if err == nil {
t.Fatal("expected error")
}
}
func TestTreeStat(t *testing.T) {
tree := &Tree{perm: 0755}
tree.statFn = func(_ []string, name string) (os.FileInfo, error) {
return &SyntheticFileInfo{Name_: name, Mode_: 0644, Size_: 42}, nil
}
fi, err := tree.Stat("hello")
if err != nil {
t.Fatalf("Stat: %v", err)
}
if fi.Size() != 42 {
t.Errorf("Size = %d", fi.Size())
}
}
func TestTreeStatOpenerFallback(t *testing.T) {
tree := &Tree{perm: 0755}
closed := false
tree.openFn = func(_ []string, name string) (File, error) {
return &fileConfig{
StatFn: func() (os.FileInfo, error) {
return &SyntheticFileInfo{Name_: name, Size_: 99}, nil
},
CloseFn: func() error {
closed = true
return nil
},
ReadFn: func() ([]byte, error) { return nil, nil },
WriteFn: func([]byte) error { return nil },
BlockingReadFn: func(context.Context, string) ([]byte, string, error) { return nil, "", nil },
}, nil
}
fi, err := tree.Stat("x")
if err != nil {
t.Fatalf("Stat: %v", err)
}
if fi.Size() != 99 {
t.Errorf("Size = %d; want 99", fi.Size())
}
if !closed {
t.Error("Stat did not close fallback-opened file")
}
}
func TestTreeChildDelegation(t *testing.T) {
child := &Tree{perm: 0755}
child.openFn = func(_ []string, name string) (File, error) {
return &fileConfig{
StatFn: func() (os.FileInfo, error) { return &SyntheticFileInfo{Name_: name}, nil },
ReadFn: func() ([]byte, error) { return []byte("child:" + name), nil },
WriteFn: func([]byte) error { return nil },
BlockingReadFn: func(context.Context, string) ([]byte, string, error) { return nil, "", nil },
}, nil
}
child.listFn = func(_ []string) ([]os.DirEntry, error) {
return []os.DirEntry{fileEntry("inner", 0644)}, nil
}
parent := &Tree{perm: 0755, children: make(map[string]*Tree)}
parent.children["sub"] = child
// Open delegated to child
f, err := parent.Open("sub/inner")
if err != nil {
t.Fatalf("Open: %v", err)
}
data, _ := f.Read()
if string(data) != "child:inner" {
t.Errorf("Read = %q", data)
}
// List parent includes mounted child
entries, err := parent.List()
if err != nil {
t.Fatalf("List: %v", err)
}
found := false
for _, e := range entries {
if e.Name() == "sub" && e.IsDir() {
found = true
}
}
if !found {
t.Error("mounted child not in List")
}
// Stat child directory
fi, err := parent.Stat("sub")
if err != nil {
t.Fatalf("Stat child: %v", err)
}
if !fi.IsDir() {
t.Error("child should be dir")
}
}
func TestTreeChildMkdirAll(t *testing.T) {
tmpDir := t.TempDir()
child := &Tree{Tree: []string{tmpDir}, perm: 0755}
parent := &Tree{perm: 0755, children: make(map[string]*Tree)}
parent.children["sub"] = child
err := parent.MkdirAll("sub/new/deep/path")
if err != nil {
t.Fatalf("MkdirAll: %v", err)
}
_, err = os.Stat(filepath.Join(tmpDir, "new", "deep", "path"))
if err != nil {
t.Fatalf("directory not created: %v", err)
}
}
func TestTreeCreate(t *testing.T) {
created := ""
tree := &Tree{perm: 0644}
tree.createFn = func(_ []string, name string, _ os.FileMode) error {
created = name
return nil
}
if err := tree.Create("newfile"); err != nil {
t.Fatalf("Create: %v", err)
}
if created != "newfile" {
t.Errorf("created = %q", created)
}
}
func TestTreeDelete(t *testing.T) {
deleted := ""
tree := &Tree{perm: 0644}
tree.deleteFn = func(_ []string, name string) error {
deleted = name
return nil
}
if err := tree.Delete("old"); err != nil {
t.Fatalf("Delete: %v", err)
}
if deleted != "old" {
t.Errorf("deleted = %q", deleted)
}
}
func TestTreeRename(t *testing.T) {
var oldN, newN string
tree := &Tree{perm: 0644}
tree.renameFn = func(_ []string, old, new string) error {
oldN, newN = old, new
return nil
}
if err := tree.Rename("a", "b"); err != nil {
t.Fatalf("Rename: %v", err)
}
if oldN != "a" || newN != "b" {
t.Errorf("rename(%q, %q)", oldN, newN)
}
}
func TestTreeReaddir(t *testing.T) {
tree := &Tree{perm: 0755}
tree.readdirFn = func(_ []string, name string) ([]os.DirEntry, error) {
return []os.DirEntry{fileEntry("child", 0644)}, nil
}
entries, err := tree.Readdir("subdir")
if err != nil {
t.Fatalf("Readdir: %v", err)
}
if len(entries) != 1 || entries[0].Name() != "child" {
t.Errorf("entries = %v", entries)
}
}
func TestReaddirNotFound(t *testing.T) {
tree := &Tree{perm: 0755}
_, err := tree.Readdir("nowhere")
if err == nil {
t.Fatal("expected error")
}
}
func TestGenerateHelpModes(t *testing.T) {
spec := DirNode("/",
FileNode("ro", 0444, Doc("read only file")),
FileNode("rw", 0644, Doc("read write file"), Write(func(_ []byte) error { return nil })),
FileNode("ctl", 0200, Doc("control file"), Rdwr(func(_ context.Context, _ []byte) ([]byte, error) { return nil, nil })),
)
help := GenerateHelp(spec)
if help == "" {
t.Fatal("empty help")
}
}