github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/fstest/mockfs/mockfs.go (about)

     1  // Package mockfs provides mock Fs for testing.
     2  package mockfs
     3  
     4  import (
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"path"
    10  	"time"
    11  
    12  	"github.com/rclone/rclone/fs"
    13  	"github.com/rclone/rclone/fs/config/configmap"
    14  	"github.com/rclone/rclone/fs/hash"
    15  )
    16  
    17  // Register with Fs
    18  func Register() {
    19  	fs.Register(&fs.RegInfo{
    20  		Name:        "mockfs",
    21  		Description: "Mock FS",
    22  		NewFs:       NewFs,
    23  		Options: []fs.Option{{
    24  			Name:     "potato",
    25  			Help:     "Does it have a potato?.",
    26  			Required: true,
    27  		}},
    28  	})
    29  }
    30  
    31  // Fs is a minimal mock Fs
    32  type Fs struct {
    33  	name     string        // the name of the remote
    34  	root     string        // The root directory (OS path)
    35  	features *fs.Features  // optional features
    36  	rootDir  fs.DirEntries // directory listing of root
    37  	hashes   hash.Set      // which hashes we support
    38  }
    39  
    40  // ErrNotImplemented is returned by unimplemented methods
    41  var ErrNotImplemented = errors.New("not implemented")
    42  
    43  // NewFs returns a new mock Fs
    44  func NewFs(ctx context.Context, name string, root string, config configmap.Mapper) (fs.Fs, error) {
    45  	f := &Fs{
    46  		name: name,
    47  		root: root,
    48  	}
    49  	f.features = (&fs.Features{}).Fill(ctx, f)
    50  	return f, nil
    51  }
    52  
    53  // AddObject adds an Object for List to return
    54  // Only works for the root for the moment
    55  func (f *Fs) AddObject(o fs.Object) {
    56  	f.rootDir = append(f.rootDir, o)
    57  	// Make this object part of mockfs if possible
    58  	do, ok := o.(interface{ SetFs(f fs.Fs) })
    59  	if ok {
    60  		do.SetFs(f)
    61  	}
    62  }
    63  
    64  // Name of the remote (as passed into NewFs)
    65  func (f *Fs) Name() string {
    66  	return f.name
    67  }
    68  
    69  // Root of the remote (as passed into NewFs)
    70  func (f *Fs) Root() string {
    71  	return f.root
    72  }
    73  
    74  // String returns a description of the FS
    75  func (f *Fs) String() string {
    76  	return fmt.Sprintf("Mock file system at %s", f.root)
    77  }
    78  
    79  // Precision of the ModTimes in this Fs
    80  func (f *Fs) Precision() time.Duration {
    81  	return time.Second
    82  }
    83  
    84  // Hashes returns the supported hash types of the filesystem
    85  func (f *Fs) Hashes() hash.Set {
    86  	return f.hashes
    87  }
    88  
    89  // SetHashes sets the hashes that this supports
    90  func (f *Fs) SetHashes(hashes hash.Set) {
    91  	f.hashes = hashes
    92  }
    93  
    94  // Features returns the optional features of this Fs
    95  func (f *Fs) Features() *fs.Features {
    96  	return f.features
    97  }
    98  
    99  // List the objects and directories in dir into entries.  The
   100  // entries can be returned in any order but should be for a
   101  // complete directory.
   102  //
   103  // dir should be "" to list the root, and should not have
   104  // trailing slashes.
   105  //
   106  // This should return ErrDirNotFound if the directory isn't
   107  // found.
   108  func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
   109  	if dir == "" {
   110  		return f.rootDir, nil
   111  	}
   112  	return entries, fs.ErrorDirNotFound
   113  }
   114  
   115  // NewObject finds the Object at remote.  If it can't be found
   116  // it returns the error ErrorObjectNotFound.
   117  func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
   118  	dirPath := path.Dir(remote)
   119  	if dirPath == "" || dirPath == "." {
   120  		for _, entry := range f.rootDir {
   121  			if entry.Remote() == remote {
   122  				return entry.(fs.Object), nil
   123  			}
   124  		}
   125  	}
   126  	return nil, fs.ErrorObjectNotFound
   127  }
   128  
   129  // Put in to the remote path with the modTime given of the given size
   130  //
   131  // May create the object even if it returns an error - if so
   132  // will return the object and the error, otherwise will return
   133  // nil and the error
   134  func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
   135  	return nil, ErrNotImplemented
   136  }
   137  
   138  // Mkdir makes the directory (container, bucket)
   139  //
   140  // Shouldn't return an error if it already exists
   141  func (f *Fs) Mkdir(ctx context.Context, dir string) error {
   142  	return ErrNotImplemented
   143  }
   144  
   145  // Rmdir removes the directory (container, bucket) if empty
   146  //
   147  // Return an error if it doesn't exist or isn't empty
   148  func (f *Fs) Rmdir(ctx context.Context, dir string) error {
   149  	return ErrNotImplemented
   150  }
   151  
   152  // Assert it is the correct type
   153  var _ fs.Fs = (*Fs)(nil)