158 lines
3.3 KiB
Go
158 lines
3.3 KiB
Go
|
// This is a mock implementation of the "fs" module for use with tests. It does
|
||
|
// not actually read from the file system. Instead, it reads from a pre-specified
|
||
|
// map of file paths to files.
|
||
|
|
||
|
package fs
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"path"
|
||
|
"strings"
|
||
|
"syscall"
|
||
|
)
|
||
|
|
||
|
type mockFS struct {
|
||
|
dirs map[string]DirEntries
|
||
|
files map[string]string
|
||
|
}
|
||
|
|
||
|
func MockFS(input map[string]string) FS {
|
||
|
dirs := make(map[string]DirEntries)
|
||
|
files := make(map[string]string)
|
||
|
|
||
|
for k, v := range input {
|
||
|
files[k] = v
|
||
|
original := k
|
||
|
|
||
|
// Build the directory map
|
||
|
for {
|
||
|
kDir := path.Dir(k)
|
||
|
dir, ok := dirs[kDir]
|
||
|
if !ok {
|
||
|
dir = DirEntries{kDir, make(map[string]*Entry), nil}
|
||
|
dirs[kDir] = dir
|
||
|
}
|
||
|
if kDir == k {
|
||
|
break
|
||
|
}
|
||
|
base := path.Base(k)
|
||
|
if k == original {
|
||
|
dir.data[strings.ToLower(base)] = &Entry{kind: FileEntry, base: base}
|
||
|
} else {
|
||
|
dir.data[strings.ToLower(base)] = &Entry{kind: DirEntry, base: base}
|
||
|
}
|
||
|
k = kDir
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return &mockFS{dirs, files}
|
||
|
}
|
||
|
|
||
|
func (fs *mockFS) ReadDirectory(path string) (DirEntries, error, error) {
|
||
|
if dir, ok := fs.dirs[path]; ok {
|
||
|
return dir, nil, nil
|
||
|
}
|
||
|
return DirEntries{}, syscall.ENOENT, syscall.ENOENT
|
||
|
}
|
||
|
|
||
|
func (fs *mockFS) ReadFile(path string) (string, error, error) {
|
||
|
if contents, ok := fs.files[path]; ok {
|
||
|
return contents, nil, nil
|
||
|
}
|
||
|
return "", syscall.ENOENT, syscall.ENOENT
|
||
|
}
|
||
|
|
||
|
func (fs *mockFS) OpenFile(path string) (OpenedFile, error, error) {
|
||
|
if contents, ok := fs.files[path]; ok {
|
||
|
return &InMemoryOpenedFile{Contents: []byte(contents)}, nil, nil
|
||
|
}
|
||
|
return nil, syscall.ENOENT, syscall.ENOENT
|
||
|
}
|
||
|
|
||
|
func (fs *mockFS) ModKey(path string) (ModKey, error) {
|
||
|
return ModKey{}, errors.New("This is not available during tests")
|
||
|
}
|
||
|
|
||
|
func (*mockFS) IsAbs(p string) bool {
|
||
|
return path.IsAbs(p)
|
||
|
}
|
||
|
|
||
|
func (*mockFS) Abs(p string) (string, bool) {
|
||
|
return path.Clean(path.Join("/", p)), true
|
||
|
}
|
||
|
|
||
|
func (*mockFS) Dir(p string) string {
|
||
|
return path.Dir(p)
|
||
|
}
|
||
|
|
||
|
func (*mockFS) Base(p string) string {
|
||
|
return path.Base(p)
|
||
|
}
|
||
|
|
||
|
func (*mockFS) Ext(p string) string {
|
||
|
return path.Ext(p)
|
||
|
}
|
||
|
|
||
|
func (*mockFS) Join(parts ...string) string {
|
||
|
return path.Clean(path.Join(parts...))
|
||
|
}
|
||
|
|
||
|
func (*mockFS) Cwd() string {
|
||
|
return "/"
|
||
|
}
|
||
|
|
||
|
func splitOnSlash(path string) (string, string) {
|
||
|
if slash := strings.IndexByte(path, '/'); slash != -1 {
|
||
|
return path[:slash], path[slash+1:]
|
||
|
}
|
||
|
return path, ""
|
||
|
}
|
||
|
|
||
|
func (*mockFS) Rel(base string, target string) (string, bool) {
|
||
|
base = path.Clean(base)
|
||
|
target = path.Clean(target)
|
||
|
|
||
|
// Base cases
|
||
|
if base == "" || base == "." {
|
||
|
return target, true
|
||
|
}
|
||
|
if base == target {
|
||
|
return ".", true
|
||
|
}
|
||
|
|
||
|
// Find the common parent directory
|
||
|
for {
|
||
|
bHead, bTail := splitOnSlash(base)
|
||
|
tHead, tTail := splitOnSlash(target)
|
||
|
if bHead != tHead {
|
||
|
break
|
||
|
}
|
||
|
base = bTail
|
||
|
target = tTail
|
||
|
}
|
||
|
|
||
|
// Stop now if base is a subpath of target
|
||
|
if base == "" {
|
||
|
return target, true
|
||
|
}
|
||
|
|
||
|
// Traverse up to the common parent
|
||
|
commonParent := strings.Repeat("../", strings.Count(base, "/")+1)
|
||
|
|
||
|
// Stop now if target is a subpath of base
|
||
|
if target == "" {
|
||
|
return commonParent[:len(commonParent)-1], true
|
||
|
}
|
||
|
|
||
|
// Otherwise, down to the parent
|
||
|
return commonParent + target, true
|
||
|
}
|
||
|
|
||
|
func (fs *mockFS) kind(dir string, base string) (symlink string, kind EntryKind) {
|
||
|
panic("This should never be called")
|
||
|
}
|
||
|
|
||
|
func (fs *mockFS) WatchData() WatchData {
|
||
|
panic("This should never be called")
|
||
|
}
|