github.com/xhghs/rclone@v1.51.1-0.20200430155106-e186a28cced8/fs/operations/reopen_test.go (about)

     1  package operations
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"io/ioutil"
     7  	"testing"
     8  
     9  	"github.com/pkg/errors"
    10  	"github.com/rclone/rclone/fs"
    11  	"github.com/rclone/rclone/fs/hash"
    12  	"github.com/rclone/rclone/fstest/mockobject"
    13  	"github.com/stretchr/testify/assert"
    14  )
    15  
    16  // check interface
    17  var _ io.ReadCloser = (*reOpen)(nil)
    18  
    19  var errorTestError = errors.New("test error")
    20  
    21  // this is a wrapper for an mockobject with a custom Open function
    22  //
    23  // breaks indicate the number of bytes to read before returning an
    24  // error
    25  type reOpenTestObject struct {
    26  	fs.Object
    27  	breaks []int64
    28  }
    29  
    30  // Open opens the file for read.  Call Close() on the returned io.ReadCloser
    31  //
    32  // This will break after reading the number of bytes in breaks
    33  func (o *reOpenTestObject) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadCloser, error) {
    34  	rc, err := o.Object.Open(ctx, options...)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	if len(o.breaks) > 0 {
    39  		// Pop a breakpoint off
    40  		N := o.breaks[0]
    41  		o.breaks = o.breaks[1:]
    42  		// If 0 then return an error immediately
    43  		if N == 0 {
    44  			return nil, errorTestError
    45  		}
    46  		// Read N bytes then an error
    47  		r := io.MultiReader(&io.LimitedReader{R: rc, N: N}, errorReader{errorTestError})
    48  		// Wrap with Close in a new readCloser
    49  		rc = readCloser{Reader: r, Closer: rc}
    50  	}
    51  	return rc, nil
    52  }
    53  
    54  // Return an error only
    55  type errorReader struct {
    56  	err error
    57  }
    58  
    59  // Read returning an error
    60  func (er errorReader) Read(p []byte) (n int, err error) {
    61  	return 0, er.err
    62  }
    63  
    64  func TestReOpen(t *testing.T) {
    65  	for testIndex, testName := range []string{"Seek", "Range"} {
    66  		t.Run(testName, func(t *testing.T) {
    67  			// Contents for the mock object
    68  			var (
    69  				reOpenTestcontents = []byte("0123456789")
    70  				expectedRead       = reOpenTestcontents
    71  				rangeOption        *fs.RangeOption
    72  			)
    73  			if testIndex > 0 {
    74  				rangeOption = &fs.RangeOption{Start: 1, End: 7}
    75  				expectedRead = reOpenTestcontents[1:8]
    76  			}
    77  
    78  			// Start the test with the given breaks
    79  			testReOpen := func(breaks []int64, maxRetries int) (io.ReadCloser, error) {
    80  				srcOrig := mockobject.New("potato").WithContent(reOpenTestcontents, mockobject.SeekModeNone)
    81  				src := &reOpenTestObject{
    82  					Object: srcOrig,
    83  					breaks: breaks,
    84  				}
    85  				hashOption := &fs.HashesOption{Hashes: hash.NewHashSet(hash.MD5)}
    86  				return newReOpen(context.Background(), src, hashOption, rangeOption, maxRetries)
    87  			}
    88  
    89  			t.Run("Basics", func(t *testing.T) {
    90  				// open
    91  				h, err := testReOpen(nil, 10)
    92  				assert.NoError(t, err)
    93  
    94  				// Check contents read correctly
    95  				got, err := ioutil.ReadAll(h)
    96  				assert.NoError(t, err)
    97  				assert.Equal(t, expectedRead, got)
    98  
    99  				// Check read after end
   100  				var buf = make([]byte, 1)
   101  				n, err := h.Read(buf)
   102  				assert.Equal(t, 0, n)
   103  				assert.Equal(t, io.EOF, err)
   104  
   105  				// Check close
   106  				assert.NoError(t, h.Close())
   107  
   108  				// Check double close
   109  				assert.Equal(t, errorFileClosed, h.Close())
   110  
   111  				// Check read after close
   112  				n, err = h.Read(buf)
   113  				assert.Equal(t, 0, n)
   114  				assert.Equal(t, errorFileClosed, err)
   115  			})
   116  
   117  			t.Run("ErrorAtStart", func(t *testing.T) {
   118  				// open with immediate breaking
   119  				h, err := testReOpen([]int64{0}, 10)
   120  				assert.Equal(t, errorTestError, err)
   121  				assert.Nil(t, h)
   122  			})
   123  
   124  			t.Run("WithErrors", func(t *testing.T) {
   125  				// open with a few break points but less than the max
   126  				h, err := testReOpen([]int64{2, 1, 3}, 10)
   127  				assert.NoError(t, err)
   128  
   129  				// check contents
   130  				got, err := ioutil.ReadAll(h)
   131  				assert.NoError(t, err)
   132  				assert.Equal(t, expectedRead, got)
   133  
   134  				// check close
   135  				assert.NoError(t, h.Close())
   136  			})
   137  
   138  			t.Run("TooManyErrors", func(t *testing.T) {
   139  				// open with a few break points but >= the max
   140  				h, err := testReOpen([]int64{2, 1, 3}, 3)
   141  				assert.NoError(t, err)
   142  
   143  				// check contents
   144  				got, err := ioutil.ReadAll(h)
   145  				assert.Equal(t, errorTestError, err)
   146  				assert.Equal(t, expectedRead[:6], got)
   147  
   148  				// check old error is returned
   149  				var buf = make([]byte, 1)
   150  				n, err := h.Read(buf)
   151  				assert.Equal(t, 0, n)
   152  				assert.Equal(t, errorTooManyTries, err)
   153  
   154  				// Check close
   155  				assert.Equal(t, errorFileClosed, h.Close())
   156  			})
   157  		})
   158  	}
   159  }