274 lines
6.6 KiB
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")
|
|
}
|
|
}
|