aboutsummaryrefslogtreecommitdiff
path: root/fstest/mockobject/mockobject.go
diff options
context:
space:
mode:
Diffstat (limited to 'fstest/mockobject/mockobject.go')
-rw-r--r--fstest/mockobject/mockobject.go235
1 files changed, 235 insertions, 0 deletions
diff --git a/fstest/mockobject/mockobject.go b/fstest/mockobject/mockobject.go
new file mode 100644
index 0000000..89f0fab
--- /dev/null
+++ b/fstest/mockobject/mockobject.go
@@ -0,0 +1,235 @@
+// Package mockobject provides a mock object which can be created from a string
+package mockobject
+
+import (
+ "bytes"
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "time"
+
+ "github.com/rclone/rclone/fs"
+ "github.com/rclone/rclone/fs/hash"
+)
+
+var errNotImpl = errors.New("not implemented")
+
+// Object is a mock fs.Object useful for testing
+type Object string
+
+// New returns mock fs.Object useful for testing
+func New(name string) Object {
+ return Object(name)
+}
+
+// String returns a description of the Object
+func (o Object) String() string {
+ return string(o)
+}
+
+// Fs returns read only access to the Fs that this object is part of
+func (o Object) Fs() fs.Info {
+ return nil
+}
+
+// Remote returns the remote path
+func (o Object) Remote() string {
+ return string(o)
+}
+
+// Hash returns the selected checksum of the file
+// If no checksum is available it returns ""
+func (o Object) Hash(ctx context.Context, t hash.Type) (string, error) {
+ return "", errNotImpl
+}
+
+// ModTime returns the modification date of the file
+// It should return a best guess if one isn't available
+func (o Object) ModTime(ctx context.Context) (t time.Time) {
+ return t
+}
+
+// Size returns the size of the file
+func (o Object) Size() int64 { return 0 }
+
+// Storable says whether this object can be stored
+func (o Object) Storable() bool {
+ return true
+}
+
+// SetModTime sets the metadata on the object to set the modification date
+func (o Object) SetModTime(ctx context.Context, t time.Time) error {
+ return errNotImpl
+}
+
+// Open opens the file for read. Call Close() on the returned io.ReadCloser
+func (o Object) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadCloser, error) {
+ return nil, errNotImpl
+}
+
+// Update in to the object with the modTime given of the given size
+func (o Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error {
+ return errNotImpl
+}
+
+// Remove this object
+func (o Object) Remove(ctx context.Context) error {
+ return errNotImpl
+}
+
+// SeekMode specifies the optional Seek interface for the ReadCloser returned by Open
+type SeekMode int
+
+const (
+ // SeekModeNone specifies no seek interface
+ SeekModeNone SeekMode = iota
+ // SeekModeRegular specifies the regular io.Seek interface
+ SeekModeRegular
+ // SeekModeRange specifies the fs.RangeSeek interface
+ SeekModeRange
+)
+
+// SeekModes contains all valid SeekMode's
+var SeekModes = []SeekMode{SeekModeNone, SeekModeRegular, SeekModeRange}
+
+// ContentMockObject mocks an fs.Object and has content, mod time
+type ContentMockObject struct {
+ Object
+ content []byte
+ seekMode SeekMode
+ f fs.Fs
+ unknownSize bool
+ modTime time.Time
+}
+
+// WithContent returns an fs.Object with the given content.
+func (o Object) WithContent(content []byte, mode SeekMode) *ContentMockObject {
+ return &ContentMockObject{
+ Object: o,
+ content: content,
+ seekMode: mode,
+ }
+}
+
+// SetFs sets the return value of the Fs() call
+func (o *ContentMockObject) SetFs(f fs.Fs) {
+ o.f = f
+}
+
+// SetUnknownSize makes the mock object return -1 for size if true
+func (o *ContentMockObject) SetUnknownSize(unknownSize bool) {
+ o.unknownSize = unknownSize
+}
+
+// Fs returns read only access to the Fs that this object is part of
+//
+// This is nil unless SetFs has been called
+func (o *ContentMockObject) Fs() fs.Info {
+ return o.f
+}
+
+// Open opens the file for read. Call Close() on the returned io.ReadCloser
+func (o *ContentMockObject) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadCloser, error) {
+ size := int64(len(o.content))
+ var offset, limit int64 = 0, -1
+ for _, option := range options {
+ switch x := option.(type) {
+ case *fs.SeekOption:
+ offset = x.Offset
+ case *fs.RangeOption:
+ offset, limit = x.Decode(size)
+ default:
+ if option.Mandatory() {
+ return nil, fmt.Errorf("unsupported mandatory option: %v", option)
+ }
+ }
+ }
+ if limit == -1 || offset+limit > size {
+ limit = size - offset
+ }
+
+ var r *bytes.Reader
+ if o.seekMode == SeekModeNone {
+ r = bytes.NewReader(o.content[offset : offset+limit])
+ } else {
+ r = bytes.NewReader(o.content)
+ _, err := r.Seek(offset, io.SeekStart)
+ if err != nil {
+ return nil, err
+ }
+ }
+ switch o.seekMode {
+ case SeekModeNone:
+ return &readCloser{r}, nil
+ case SeekModeRegular:
+ return &readSeekCloser{r}, nil
+ case SeekModeRange:
+ return &readRangeSeekCloser{r}, nil
+ default:
+ return nil, errors.New(o.seekMode.String())
+ }
+}
+
+// Size returns the size of the file
+func (o *ContentMockObject) Size() int64 {
+ if o.unknownSize {
+ return -1
+ }
+ return int64(len(o.content))
+}
+
+// Hash returns the selected checksum of the file
+// If no checksum is available it returns ""
+func (o *ContentMockObject) Hash(ctx context.Context, t hash.Type) (string, error) {
+ hasher, err := hash.NewMultiHasherTypes(hash.NewHashSet(t))
+ if err != nil {
+ return "", err
+ }
+ _, err = hasher.Write(o.content)
+ if err != nil {
+ return "", err
+ }
+ return hasher.Sums()[t], nil
+}
+
+// ModTime returns the modification date of the file
+// It should return a best guess if one isn't available
+func (o *ContentMockObject) ModTime(ctx context.Context) time.Time {
+ return o.modTime
+}
+
+// SetModTime sets the metadata on the object to set the modification date
+func (o *ContentMockObject) SetModTime(ctx context.Context, t time.Time) error {
+ o.modTime = t
+ return nil
+}
+
+type readCloser struct{ io.Reader }
+
+func (r *readCloser) Close() error { return nil }
+
+type readSeekCloser struct{ io.ReadSeeker }
+
+func (r *readSeekCloser) Close() error { return nil }
+
+type readRangeSeekCloser struct{ io.ReadSeeker }
+
+func (r *readRangeSeekCloser) RangeSeek(offset int64, whence int, length int64) (int64, error) {
+ return r.ReadSeeker.Seek(offset, whence)
+}
+
+func (r *readRangeSeekCloser) Close() error { return nil }
+
+func (m SeekMode) String() string {
+ switch m {
+ case SeekModeNone:
+ return "SeekModeNone"
+ case SeekModeRegular:
+ return "SeekModeRegular"
+ case SeekModeRange:
+ return "SeekModeRange"
+ default:
+ return fmt.Sprintf("SeekModeInvalid(%d)", m)
+ }
+}