github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/fstest/mockobject/mockobject.go (about)

     1  // Package mockobject provides a mock object which can be created from a string
     2  package mockobject
     3  
     4  import (
     5  	"bytes"
     6  	"context"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"time"
    11  
    12  	"github.com/ncw/rclone/fs"
    13  	"github.com/ncw/rclone/fs/hash"
    14  )
    15  
    16  var errNotImpl = errors.New("not implemented")
    17  
    18  // Object is a mock fs.Object useful for testing
    19  type Object string
    20  
    21  // New returns mock fs.Object useful for testing
    22  func New(name string) Object {
    23  	return Object(name)
    24  }
    25  
    26  // String returns a description of the Object
    27  func (o Object) String() string {
    28  	return string(o)
    29  }
    30  
    31  // Fs returns read only access to the Fs that this object is part of
    32  func (o Object) Fs() fs.Info {
    33  	return nil
    34  }
    35  
    36  // Remote returns the remote path
    37  func (o Object) Remote() string {
    38  	return string(o)
    39  }
    40  
    41  // Hash returns the selected checksum of the file
    42  // If no checksum is available it returns ""
    43  func (o Object) Hash(ctx context.Context, t hash.Type) (string, error) {
    44  	return "", errNotImpl
    45  }
    46  
    47  // ModTime returns the modification date of the file
    48  // It should return a best guess if one isn't available
    49  func (o Object) ModTime(ctx context.Context) (t time.Time) {
    50  	return t
    51  }
    52  
    53  // Size returns the size of the file
    54  func (o Object) Size() int64 { return 0 }
    55  
    56  // Storable says whether this object can be stored
    57  func (o Object) Storable() bool {
    58  	return true
    59  }
    60  
    61  // SetModTime sets the metadata on the object to set the modification date
    62  func (o Object) SetModTime(ctx context.Context, t time.Time) error {
    63  	return errNotImpl
    64  }
    65  
    66  // Open opens the file for read.  Call Close() on the returned io.ReadCloser
    67  func (o Object) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadCloser, error) {
    68  	return nil, errNotImpl
    69  }
    70  
    71  // Update in to the object with the modTime given of the given size
    72  func (o Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error {
    73  	return errNotImpl
    74  }
    75  
    76  // Remove this object
    77  func (o Object) Remove(ctx context.Context) error {
    78  	return errNotImpl
    79  }
    80  
    81  // SeekMode specifies the optional Seek interface for the ReadCloser returned by Open
    82  type SeekMode int
    83  
    84  const (
    85  	// SeekModeNone specifies no seek interface
    86  	SeekModeNone SeekMode = iota
    87  	// SeekModeRegular specifies the regular io.Seek interface
    88  	SeekModeRegular
    89  	// SeekModeRange specifies the fs.RangeSeek interface
    90  	SeekModeRange
    91  )
    92  
    93  // SeekModes contains all valid SeekMode's
    94  var SeekModes = []SeekMode{SeekModeNone, SeekModeRegular, SeekModeRange}
    95  
    96  type contentMockObject struct {
    97  	Object
    98  	content  []byte
    99  	seekMode SeekMode
   100  }
   101  
   102  // WithContent returns a fs.Object with the given content.
   103  func (o Object) WithContent(content []byte, mode SeekMode) fs.Object {
   104  	return &contentMockObject{
   105  		Object:   o,
   106  		content:  content,
   107  		seekMode: mode,
   108  	}
   109  }
   110  
   111  func (o *contentMockObject) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadCloser, error) {
   112  	var offset, limit int64 = 0, -1
   113  	for _, option := range options {
   114  		switch x := option.(type) {
   115  		case *fs.SeekOption:
   116  			offset = x.Offset
   117  		case *fs.RangeOption:
   118  			offset, limit = x.Decode(o.Size())
   119  		default:
   120  			if option.Mandatory() {
   121  				return nil, fmt.Errorf("Unsupported mandatory option: %v", option)
   122  			}
   123  		}
   124  	}
   125  	if limit == -1 || offset+limit > o.Size() {
   126  		limit = o.Size() - offset
   127  	}
   128  
   129  	var r *bytes.Reader
   130  	if o.seekMode == SeekModeNone {
   131  		r = bytes.NewReader(o.content[offset : offset+limit])
   132  	} else {
   133  		r = bytes.NewReader(o.content)
   134  		_, err := r.Seek(offset, io.SeekStart)
   135  		if err != nil {
   136  			return nil, err
   137  		}
   138  	}
   139  	switch o.seekMode {
   140  	case SeekModeNone:
   141  		return &readCloser{r}, nil
   142  	case SeekModeRegular:
   143  		return &readSeekCloser{r}, nil
   144  	case SeekModeRange:
   145  		return &readRangeSeekCloser{r}, nil
   146  	default:
   147  		return nil, errors.New(o.seekMode.String())
   148  	}
   149  }
   150  func (o *contentMockObject) Size() int64 {
   151  	return int64(len(o.content))
   152  }
   153  
   154  type readCloser struct{ io.Reader }
   155  
   156  func (r *readCloser) Close() error { return nil }
   157  
   158  type readSeekCloser struct{ io.ReadSeeker }
   159  
   160  func (r *readSeekCloser) Close() error { return nil }
   161  
   162  type readRangeSeekCloser struct{ io.ReadSeeker }
   163  
   164  func (r *readRangeSeekCloser) RangeSeek(offset int64, whence int, length int64) (int64, error) {
   165  	return r.ReadSeeker.Seek(offset, whence)
   166  }
   167  
   168  func (r *readRangeSeekCloser) Close() error { return nil }
   169  
   170  func (m SeekMode) String() string {
   171  	switch m {
   172  	case SeekModeNone:
   173  		return "SeekModeNone"
   174  	case SeekModeRegular:
   175  		return "SeekModeRegular"
   176  	case SeekModeRange:
   177  		return "SeekModeRange"
   178  	default:
   179  		return fmt.Sprintf("SeekModeInvalid(%d)", m)
   180  	}
   181  }