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 }