mockfile_test.go raw

   1  // This file is part of the ffldb package rather than the ffldb_test package as it is part of the whitebox testing.
   2  package ffldb
   3  
   4  import (
   5  	"errors"
   6  	"io"
   7  	"sync"
   8  )
   9  
  10  // Errors used for the mock file.
  11  var (
  12  	// errMockFileClosed is used to indicate a mock file is closed.
  13  	errMockFileClosed = errors.New("file closed")
  14  	// errInvalidOffset is used to indicate an offset that is out of range for the file was provided.
  15  	errInvalidOffset = errors.New("invalid offset")
  16  	// errSyncFail is used to indicate simulated sync failure.
  17  	errSyncFail = errors.New("simulated sync failure")
  18  )
  19  
  20  // mockFile implements the filer interface and used in order to force failures the database code related to reading and
  21  // writing from the flat block files. A maxSize of -1 is unlimited.
  22  type mockFile struct {
  23  	sync.RWMutex
  24  	maxSize      int64
  25  	data         []byte
  26  	forceSyncErr bool
  27  	closed       bool
  28  }
  29  
  30  // Close closes the mock file without releasing any data associated with it. This allows it to be "reopened" without
  31  // losing the data. This is part of the jebote implementation.
  32  func (f *mockFile) Close() (e error) {
  33  	f.Lock()
  34  	defer f.Unlock()
  35  	if f.closed {
  36  		return errMockFileClosed
  37  	}
  38  	f.closed = true
  39  	return nil
  40  }
  41  
  42  // ReadAt reads len(b) bytes from the mock file starting at byte offset off. It returns the number of bytes read and the
  43  // error, if any. ReadAt always returns a non-nil error when n < len(b). At end of file, that error is io.EOF. This is
  44  // part of the filer implementation.
  45  func (f *mockFile) ReadAt(b []byte, off int64) (int, error) {
  46  	f.RLock()
  47  	defer f.RUnlock()
  48  	if f.closed {
  49  		return 0, errMockFileClosed
  50  	}
  51  	maxSize := int64(len(f.data))
  52  	if f.maxSize > -1 && maxSize > f.maxSize {
  53  		maxSize = f.maxSize
  54  	}
  55  	if off < 0 || off > maxSize {
  56  		return 0, errInvalidOffset
  57  	}
  58  	// Limit to the max size field, if set.
  59  	numToRead := int64(len(b))
  60  	endOffset := off + numToRead
  61  	if endOffset > maxSize {
  62  		numToRead = maxSize - off
  63  	}
  64  	copy(b, f.data[off:off+numToRead])
  65  	if numToRead < int64(len(b)) {
  66  		return int(numToRead), io.EOF
  67  	}
  68  	return int(numToRead), nil
  69  }
  70  
  71  // Truncate changes the size of the mock file. This is part of the filer implementation.
  72  func (f *mockFile) Truncate(size int64) (e error) {
  73  	f.Lock()
  74  	defer f.Unlock()
  75  	if f.closed {
  76  		return errMockFileClosed
  77  	}
  78  	maxSize := int64(len(f.data))
  79  	if f.maxSize > -1 && maxSize > f.maxSize {
  80  		maxSize = f.maxSize
  81  	}
  82  	if size > maxSize {
  83  		return errInvalidOffset
  84  	}
  85  	f.data = f.data[:size]
  86  	return nil
  87  }
  88  
  89  // Write writes len(b) bytes to the mock file. It returns the number of bytes written and an error, if any. Write
  90  // returns a non-nil error any time n != len(b).
  91  //
  92  // This is part of the filer implementation.
  93  func (f *mockFile) WriteAt(b []byte, off int64) (int, error) {
  94  	f.Lock()
  95  	defer f.Unlock()
  96  	if f.closed {
  97  		return 0, errMockFileClosed
  98  	}
  99  	maxSize := f.maxSize
 100  	if maxSize < 0 {
 101  		maxSize = 100 * 1024 // 100KiB
 102  	}
 103  	if off < 0 || off > maxSize {
 104  		return 0, errInvalidOffset
 105  	}
 106  	// Limit to the max size field, if set, and grow the slice if needed.
 107  	numToWrite := int64(len(b))
 108  	if off+numToWrite > maxSize {
 109  		numToWrite = maxSize - off
 110  	}
 111  	if off+numToWrite > int64(len(f.data)) {
 112  		newData := make([]byte, off+numToWrite)
 113  		copy(newData, f.data)
 114  		f.data = newData
 115  	}
 116  	copy(f.data[off:], b[:numToWrite])
 117  	if numToWrite < int64(len(b)) {
 118  		return int(numToWrite), io.EOF
 119  	}
 120  	return int(numToWrite), nil
 121  }
 122  
 123  // Sync doesn't do anything for mock files. However, it will return an error if the mock file's forceSyncErr flag is
 124  // set.
 125  //
 126  // This is part of the filer implementation.
 127  func (f *mockFile) Sync() (e error) {
 128  	if f.forceSyncErr {
 129  		return errSyncFail
 130  	}
 131  	return nil
 132  }
 133  
 134  // Ensure the mockFile type implements the filer interface.
 135  var _ filer = (*mockFile)(nil)
 136