// 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") }