mockobject.go 5.9 KB

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