mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-07-31 14:24:25 +03:00
Write unit tests with the help of afero
Afero is a package that lets you mock out a filesystem with an in-memory filesystem. It allows us to easily create the files required for a given test without worrying about a cleanup step or different tests tripping on eachother when run in parallel. Later on I'll standardise on using afero over the vanilla os package
This commit is contained in:
298
vendor/github.com/spf13/afero/iofs.go
generated
vendored
Normal file
298
vendor/github.com/spf13/afero/iofs.go
generated
vendored
Normal file
@ -0,0 +1,298 @@
|
||||
//go:build go1.16
|
||||
// +build go1.16
|
||||
|
||||
package afero
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/afero/internal/common"
|
||||
)
|
||||
|
||||
// IOFS adopts afero.Fs to stdlib io/fs.FS
|
||||
type IOFS struct {
|
||||
Fs
|
||||
}
|
||||
|
||||
func NewIOFS(fs Fs) IOFS {
|
||||
return IOFS{Fs: fs}
|
||||
}
|
||||
|
||||
var (
|
||||
_ fs.FS = IOFS{}
|
||||
_ fs.GlobFS = IOFS{}
|
||||
_ fs.ReadDirFS = IOFS{}
|
||||
_ fs.ReadFileFS = IOFS{}
|
||||
_ fs.StatFS = IOFS{}
|
||||
_ fs.SubFS = IOFS{}
|
||||
)
|
||||
|
||||
func (iofs IOFS) Open(name string) (fs.File, error) {
|
||||
const op = "open"
|
||||
|
||||
// by convention for fs.FS implementations we should perform this check
|
||||
if !fs.ValidPath(name) {
|
||||
return nil, iofs.wrapError(op, name, fs.ErrInvalid)
|
||||
}
|
||||
|
||||
file, err := iofs.Fs.Open(name)
|
||||
if err != nil {
|
||||
return nil, iofs.wrapError(op, name, err)
|
||||
}
|
||||
|
||||
// file should implement fs.ReadDirFile
|
||||
if _, ok := file.(fs.ReadDirFile); !ok {
|
||||
file = readDirFile{file}
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func (iofs IOFS) Glob(pattern string) ([]string, error) {
|
||||
const op = "glob"
|
||||
|
||||
// afero.Glob does not perform this check but it's required for implementations
|
||||
if _, err := path.Match(pattern, ""); err != nil {
|
||||
return nil, iofs.wrapError(op, pattern, err)
|
||||
}
|
||||
|
||||
items, err := Glob(iofs.Fs, pattern)
|
||||
if err != nil {
|
||||
return nil, iofs.wrapError(op, pattern, err)
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (iofs IOFS) ReadDir(name string) ([]fs.DirEntry, error) {
|
||||
f, err := iofs.Fs.Open(name)
|
||||
if err != nil {
|
||||
return nil, iofs.wrapError("readdir", name, err)
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
if rdf, ok := f.(fs.ReadDirFile); ok {
|
||||
items, err := rdf.ReadDir(-1)
|
||||
if err != nil {
|
||||
return nil, iofs.wrapError("readdir", name, err)
|
||||
}
|
||||
sort.Slice(items, func(i, j int) bool { return items[i].Name() < items[j].Name() })
|
||||
return items, nil
|
||||
}
|
||||
|
||||
items, err := f.Readdir(-1)
|
||||
if err != nil {
|
||||
return nil, iofs.wrapError("readdir", name, err)
|
||||
}
|
||||
sort.Sort(byName(items))
|
||||
|
||||
ret := make([]fs.DirEntry, len(items))
|
||||
for i := range items {
|
||||
ret[i] = common.FileInfoDirEntry{FileInfo: items[i]}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (iofs IOFS) ReadFile(name string) ([]byte, error) {
|
||||
const op = "readfile"
|
||||
|
||||
if !fs.ValidPath(name) {
|
||||
return nil, iofs.wrapError(op, name, fs.ErrInvalid)
|
||||
}
|
||||
|
||||
bytes, err := ReadFile(iofs.Fs, name)
|
||||
if err != nil {
|
||||
return nil, iofs.wrapError(op, name, err)
|
||||
}
|
||||
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
func (iofs IOFS) Sub(dir string) (fs.FS, error) { return IOFS{NewBasePathFs(iofs.Fs, dir)}, nil }
|
||||
|
||||
func (IOFS) wrapError(op, path string, err error) error {
|
||||
if _, ok := err.(*fs.PathError); ok {
|
||||
return err // don't need to wrap again
|
||||
}
|
||||
|
||||
return &fs.PathError{
|
||||
Op: op,
|
||||
Path: path,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// readDirFile provides adapter from afero.File to fs.ReadDirFile needed for correct Open
|
||||
type readDirFile struct {
|
||||
File
|
||||
}
|
||||
|
||||
var _ fs.ReadDirFile = readDirFile{}
|
||||
|
||||
func (r readDirFile) ReadDir(n int) ([]fs.DirEntry, error) {
|
||||
items, err := r.File.Readdir(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]fs.DirEntry, len(items))
|
||||
for i := range items {
|
||||
ret[i] = common.FileInfoDirEntry{FileInfo: items[i]}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// FromIOFS adopts io/fs.FS to use it as afero.Fs
|
||||
// Note that io/fs.FS is read-only so all mutating methods will return fs.PathError with fs.ErrPermission
|
||||
// To store modifications you may use afero.CopyOnWriteFs
|
||||
type FromIOFS struct {
|
||||
fs.FS
|
||||
}
|
||||
|
||||
var _ Fs = FromIOFS{}
|
||||
|
||||
func (f FromIOFS) Create(name string) (File, error) { return nil, notImplemented("create", name) }
|
||||
|
||||
func (f FromIOFS) Mkdir(name string, perm os.FileMode) error { return notImplemented("mkdir", name) }
|
||||
|
||||
func (f FromIOFS) MkdirAll(path string, perm os.FileMode) error {
|
||||
return notImplemented("mkdirall", path)
|
||||
}
|
||||
|
||||
func (f FromIOFS) Open(name string) (File, error) {
|
||||
file, err := f.FS.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return fromIOFSFile{File: file, name: name}, nil
|
||||
}
|
||||
|
||||
func (f FromIOFS) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
|
||||
return f.Open(name)
|
||||
}
|
||||
|
||||
func (f FromIOFS) Remove(name string) error {
|
||||
return notImplemented("remove", name)
|
||||
}
|
||||
|
||||
func (f FromIOFS) RemoveAll(path string) error {
|
||||
return notImplemented("removeall", path)
|
||||
}
|
||||
|
||||
func (f FromIOFS) Rename(oldname, newname string) error {
|
||||
return notImplemented("rename", oldname)
|
||||
}
|
||||
|
||||
func (f FromIOFS) Stat(name string) (os.FileInfo, error) { return fs.Stat(f.FS, name) }
|
||||
|
||||
func (f FromIOFS) Name() string { return "fromiofs" }
|
||||
|
||||
func (f FromIOFS) Chmod(name string, mode os.FileMode) error {
|
||||
return notImplemented("chmod", name)
|
||||
}
|
||||
|
||||
func (f FromIOFS) Chown(name string, uid, gid int) error {
|
||||
return notImplemented("chown", name)
|
||||
}
|
||||
|
||||
func (f FromIOFS) Chtimes(name string, atime time.Time, mtime time.Time) error {
|
||||
return notImplemented("chtimes", name)
|
||||
}
|
||||
|
||||
type fromIOFSFile struct {
|
||||
fs.File
|
||||
name string
|
||||
}
|
||||
|
||||
func (f fromIOFSFile) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
readerAt, ok := f.File.(io.ReaderAt)
|
||||
if !ok {
|
||||
return -1, notImplemented("readat", f.name)
|
||||
}
|
||||
|
||||
return readerAt.ReadAt(p, off)
|
||||
}
|
||||
|
||||
func (f fromIOFSFile) Seek(offset int64, whence int) (int64, error) {
|
||||
seeker, ok := f.File.(io.Seeker)
|
||||
if !ok {
|
||||
return -1, notImplemented("seek", f.name)
|
||||
}
|
||||
|
||||
return seeker.Seek(offset, whence)
|
||||
}
|
||||
|
||||
func (f fromIOFSFile) Write(p []byte) (n int, err error) {
|
||||
return -1, notImplemented("write", f.name)
|
||||
}
|
||||
|
||||
func (f fromIOFSFile) WriteAt(p []byte, off int64) (n int, err error) {
|
||||
return -1, notImplemented("writeat", f.name)
|
||||
}
|
||||
|
||||
func (f fromIOFSFile) Name() string { return f.name }
|
||||
|
||||
func (f fromIOFSFile) Readdir(count int) ([]os.FileInfo, error) {
|
||||
rdfile, ok := f.File.(fs.ReadDirFile)
|
||||
if !ok {
|
||||
return nil, notImplemented("readdir", f.name)
|
||||
}
|
||||
|
||||
entries, err := rdfile.ReadDir(count)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]os.FileInfo, len(entries))
|
||||
for i := range entries {
|
||||
ret[i], err = entries[i].Info()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (f fromIOFSFile) Readdirnames(n int) ([]string, error) {
|
||||
rdfile, ok := f.File.(fs.ReadDirFile)
|
||||
if !ok {
|
||||
return nil, notImplemented("readdir", f.name)
|
||||
}
|
||||
|
||||
entries, err := rdfile.ReadDir(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]string, len(entries))
|
||||
for i := range entries {
|
||||
ret[i] = entries[i].Name()
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (f fromIOFSFile) Sync() error { return nil }
|
||||
|
||||
func (f fromIOFSFile) Truncate(size int64) error {
|
||||
return notImplemented("truncate", f.name)
|
||||
}
|
||||
|
||||
func (f fromIOFSFile) WriteString(s string) (ret int, err error) {
|
||||
return -1, notImplemented("writestring", f.name)
|
||||
}
|
||||
|
||||
func notImplemented(op, path string) error {
|
||||
return &fs.PathError{Op: op, Path: path, Err: fs.ErrPermission}
|
||||
}
|
Reference in New Issue
Block a user