github.com/rohankumardubey/proxyfs@v0.0.0-20210108201508-653efa9ab00e/inode/file_extent_test.go (about)

     1  package inode
     2  
     3  import (
     4  	"bytes"
     5  	cryptoRand "crypto/rand"
     6  	"fmt"
     7  	"math/big"
     8  	mathRand "math/rand"
     9  	"testing"
    10  
    11  	"github.com/swiftstack/sortedmap"
    12  )
    13  
    14  const (
    15  	sizeOfTestFile      uint64 = 0x01000000 // 16 MiB
    16  	maxExtentLength     uint64 = 0x00001000 //  4 KiB
    17  	numExtentOverwrites uint64 = 0x00001000 //  4 Ki
    18  	numZeroLengthReads  uint64 = 0x00001000 //  4 Ki
    19  
    20  	pseudoRandom     = false
    21  	pseudoRandomSeed = int64(0)
    22  )
    23  
    24  var (
    25  	randSource                *mathRand.Rand // A source for pseudo-random numbers (if selected)
    26  	inMemoryFileInodeContents []byte         // A value of 0x00 means the byte has never been written
    27  	byteToWrite               = byte(0x01)   // Incremented for each write, wrapping from 0xFF to 0x01 per above
    28  	curExtentOverwrites       = uint64(0)    // During write phase, loop until == numExtentOverwrites
    29  	curFileSize               = uint64(0)    // Tracks the highest offset actually written
    30  )
    31  
    32  func testChooseUint64(t *testing.T, mustBeLessThan uint64) (u64 uint64) {
    33  	if pseudoRandom {
    34  		if nil == randSource {
    35  			randSource = mathRand.New(mathRand.NewSource(pseudoRandomSeed))
    36  		}
    37  
    38  		u64 = uint64(randSource.Int63n(int64(mustBeLessThan)))
    39  	} else {
    40  		mustBeLessThanBigIntPtr := big.NewInt(int64(mustBeLessThan))
    41  		u64BigIntPtr, err := cryptoRand.Int(cryptoRand.Reader, mustBeLessThanBigIntPtr)
    42  		if nil != err {
    43  			t.Fatalf("rand.Int(rand.Reader, mustBeLessThanBigIntPtr) returned error == \"%v\"", err)
    44  		}
    45  
    46  		u64 = u64BigIntPtr.Uint64()
    47  	}
    48  
    49  	return
    50  }
    51  
    52  func testChooseExtentStart(t *testing.T) (offset uint64) {
    53  	offset = testChooseUint64(t, sizeOfTestFile)
    54  
    55  	return
    56  }
    57  
    58  func testChooseExtentSize(t *testing.T) (length uint64) {
    59  	length = testChooseUint64(t, maxExtentLength) + 1
    60  
    61  	return
    62  }
    63  
    64  func testChooseExtent(t *testing.T) (offset uint64, length uint64) {
    65  	offset = testChooseExtentStart(t)
    66  	length = testChooseExtentSize(t)
    67  
    68  	if (offset + length) > sizeOfTestFile {
    69  		length = sizeOfTestFile - offset
    70  	}
    71  
    72  	return
    73  }
    74  
    75  func testPopulateFileInode(t *testing.T, testVolumeHandle VolumeHandle, fileInodeNumber InodeNumber) {
    76  	for curExtentOverwrites < numExtentOverwrites {
    77  		offset, length := testChooseExtent(t)
    78  
    79  		overwriteOccurred := false
    80  
    81  		for i := offset; i < (offset + length); i++ {
    82  			if byte(0x00) != inMemoryFileInodeContents[i] {
    83  				overwriteOccurred = true
    84  			}
    85  
    86  			inMemoryFileInodeContents[i] = byteToWrite
    87  		}
    88  		if overwriteOccurred {
    89  			curExtentOverwrites++
    90  		}
    91  
    92  		err := testVolumeHandle.Write(fileInodeNumber, offset, inMemoryFileInodeContents[offset:(offset+length)], nil)
    93  		if nil != err {
    94  			t.Fatalf("Write(fileInodeNumber, offset, inMemoryFileInodeContents[offset:(offset+length)]) failed: %v", err)
    95  		}
    96  
    97  		if (offset + length) > curFileSize {
    98  			curFileSize = offset + length
    99  		}
   100  
   101  		if byte(0xFF) == byteToWrite {
   102  			byteToWrite = byte(0x01)
   103  		} else {
   104  			byteToWrite++
   105  		}
   106  	}
   107  }
   108  
   109  func testValidateFileInodeBPlusTree(t *testing.T, testVolumeHandle VolumeHandle, fileInodeNumber InodeNumber) {
   110  	// Fetch actual fileInode
   111  
   112  	fileInode, err := (testVolumeHandle.(*volumeStruct)).fetchInodeType(fileInodeNumber, FileType)
   113  	if nil != err {
   114  		t.Fatalf("testVolumeHandle.fetchInodeType(fileInodeNumber, FileType) failed: %v", err)
   115  	}
   116  
   117  	// Fetch extents B+Tree
   118  
   119  	extents := fileInode.payload.(sortedmap.BPlusTree)
   120  	extentsLen, err := extents.Len()
   121  	if nil != err {
   122  		t.Fatalf("extents.Len() failed: %v", err)
   123  	}
   124  
   125  	// Walk extents B+Tree (by index) ensuring coherence with:
   126  	//   1 - All keys (fileOffset) are monotonically increasing
   127  	//   2 - All keys agree with the fileOffset field of their values
   128  	//   3 - All values (extents) don't overlap
   129  	//   4 - GetByKey(fileOffset) returns the same extent
   130  
   131  	if 0 == extentsLen {
   132  		return
   133  	}
   134  
   135  	var lastExtentTerminationFileOffset uint64
   136  
   137  	for extentIndex := 0; extentIndex < extentsLen; extentIndex++ {
   138  		// Fetch current extent
   139  		keyByIndex, valueByIndex, ok, err := extents.GetByIndex(extentIndex)
   140  		if nil != err {
   141  			t.Fatalf("extents.GetByIndex(extentIndex) failed: %v", err)
   142  		}
   143  		if !ok {
   144  			err = fmt.Errorf("extents.GetByIndex(extentIndex) unexpectedly returned !ok")
   145  			t.Fatalf(err.Error())
   146  		}
   147  		fileOffsetByIndex, ok := keyByIndex.(uint64)
   148  		if !ok {
   149  			err = fmt.Errorf("keyByIndex.(uint64) unexpectedly returned !ok")
   150  			t.Fatalf(err.Error())
   151  		}
   152  		extentByIndex, ok := valueByIndex.(*fileExtentStruct)
   153  		if !ok {
   154  			err = fmt.Errorf("valueByIndex.(*fileExtent) unexpectedly returned !ok")
   155  			t.Fatalf(err.Error())
   156  		}
   157  
   158  		// Validate extent
   159  		if fileOffsetByIndex != extentByIndex.FileOffset {
   160  			err = fmt.Errorf("fileOffsetByIndex != extentByIndex.fileOffset")
   161  			t.Fatalf(err.Error())
   162  		}
   163  		if 0 == extentByIndex.Length {
   164  			err = fmt.Errorf("0 == extentByIndex.length")
   165  			t.Fatalf(err.Error())
   166  		}
   167  
   168  		valueByKey, ok, err := extents.GetByKey(fileOffsetByIndex)
   169  		if nil != err {
   170  			t.Fatalf("extents.GetByKey(fileOffsetByIndex) failed: %v", err)
   171  		}
   172  		if !ok {
   173  			err = fmt.Errorf("extents.GetByKey(fileOffsetByIndex) unexpectedly returned !ok")
   174  			t.Fatalf(err.Error())
   175  		}
   176  		extentByKey, ok := valueByKey.(*fileExtentStruct)
   177  		if !ok {
   178  			err = fmt.Errorf("valueByKey.(*fileExtent) unexpectedly returned !ok")
   179  			t.Fatalf(err.Error())
   180  		}
   181  		if fileOffsetByIndex != extentByKey.FileOffset {
   182  			err = fmt.Errorf("fileOffsetByIndex != extentByKey.fileOffset")
   183  			t.Fatalf(err.Error())
   184  		}
   185  
   186  		if 0 < extentIndex {
   187  			// Ensure this extent strictly follows prior extent
   188  			if lastExtentTerminationFileOffset > fileOffsetByIndex {
   189  				err = fmt.Errorf("(lastExtentFileOffset + lastExtentLength) > fileOffsetByIndex")
   190  				t.Fatalf(err.Error())
   191  			}
   192  		}
   193  
   194  		// Save this extent's lastExtentTerminationFileOffset for next iteration
   195  		lastExtentTerminationFileOffset = fileOffsetByIndex + extentByIndex.Length
   196  	}
   197  
   198  	// If we reach here, extents is valid
   199  }
   200  
   201  func testVerifyFileInodeBPlusTree(t *testing.T, testVolumeHandle VolumeHandle, fileInodeNumber InodeNumber) {
   202  	// Fetch actual fileInode
   203  
   204  	fileInode, err := (testVolumeHandle.(*volumeStruct)).fetchInodeType(fileInodeNumber, FileType)
   205  	if nil != err {
   206  		t.Fatalf("testVolumeHandle.fetchInodeType(fileInodeNumber, FileType) failed: %v", err)
   207  	}
   208  
   209  	// Fetch extents B+Tree
   210  
   211  	extents := fileInode.payload.(sortedmap.BPlusTree)
   212  	extentsLen, err := extents.Len()
   213  	if nil != err {
   214  		t.Fatalf("extents.Len() failed: %v", err)
   215  	}
   216  
   217  	if 0 == extentsLen {
   218  		// Verify entire inMemoryFileInodeContents is all zeroes
   219  		for _, byteValue := range inMemoryFileInodeContents {
   220  			if 0 != byteValue {
   221  				err = fmt.Errorf("0 != byteValue [case 1]")
   222  				t.Fatalf(err.Error())
   223  			}
   224  		}
   225  
   226  		// If we reach here, inMemoryFileInodeContents was all zeroes and (this trivial) extents is verified
   227  		return
   228  	}
   229  
   230  	var lastExtentTerminationFileOffset uint64
   231  
   232  	for extentIndex := 0; extentIndex < extentsLen; extentIndex++ {
   233  		// Fetch current extent
   234  		key, value, ok, err := extents.GetByIndex(extentIndex)
   235  		if nil != err {
   236  			t.Fatalf("extents.GetByIndex(extentIndex) failed: %v", err)
   237  		}
   238  		if !ok {
   239  			err = fmt.Errorf("extents.GetByIndex(extentIndex) unexpectedly returned !ok")
   240  			t.Fatalf(err.Error())
   241  		}
   242  		fileOffset, ok := key.(uint64)
   243  		if !ok {
   244  			err = fmt.Errorf("key.(uint64) unexpectedly returned !ok")
   245  			t.Fatalf(err.Error())
   246  		}
   247  		extent, ok := value.(*fileExtentStruct)
   248  		if !ok {
   249  			err = fmt.Errorf("value.(*fileExtent) unexpectedly returned !ok")
   250  			t.Fatalf(err.Error())
   251  		}
   252  
   253  		// Verify preceeding hole (if any) in extents matches all zeroes in inMemoryFileInodeContents
   254  		for _, byteValue := range inMemoryFileInodeContents[lastExtentTerminationFileOffset:fileOffset] {
   255  			if 0 != byteValue {
   256  				err = fmt.Errorf("0 != byteValue [case 2]")
   257  				t.Fatalf(err.Error())
   258  			}
   259  		}
   260  
   261  		// Update lastExtentTerminationFileOffset for next iteration but used for non-zero check below as well
   262  		lastExtentTerminationFileOffset = fileOffset + extent.Length
   263  
   264  		// Verify extent matches non-zeroes in inMemoryFileInodeContents
   265  		for _, byteValue := range inMemoryFileInodeContents[fileOffset:lastExtentTerminationFileOffset] {
   266  			if 0 == byteValue {
   267  				err = fmt.Errorf("0 == byteValue")
   268  				t.Fatalf(err.Error())
   269  			}
   270  		}
   271  	}
   272  
   273  	// Verify inMemoryFileInodeContents agrees that lastExtentTerminationFileOffset is EOF
   274  	for _, byteValue := range inMemoryFileInodeContents[lastExtentTerminationFileOffset:] {
   275  		if 0 != byteValue {
   276  			err = fmt.Errorf("0 != byteValue [case 3]")
   277  			t.Fatalf(err.Error())
   278  		}
   279  	}
   280  
   281  	// If we reach here, extents is verified
   282  }
   283  
   284  func testCondenseByteSlice(buf []byte) (condensedString string) {
   285  	type countValueTupleStruct struct {
   286  		count uint64
   287  		value byte
   288  	}
   289  
   290  	var countValueTupleSlice []*countValueTupleStruct
   291  
   292  	for _, value := range buf {
   293  		countValueTupleSliceLen := len(countValueTupleSlice)
   294  		if (0 == countValueTupleSliceLen) || (value != countValueTupleSlice[countValueTupleSliceLen-1].value) {
   295  			countValueTupleSlice = append(countValueTupleSlice, &countValueTupleStruct{count: 1, value: value})
   296  		} else {
   297  			countValueTupleSlice[countValueTupleSliceLen-1].count++
   298  		}
   299  	}
   300  
   301  	var condensedByteSlice []byte
   302  
   303  	condensedByteSlice = append(condensedByteSlice, '[')
   304  
   305  	for countValueTupleIndex, countValueTuple := range countValueTupleSlice {
   306  		if 0 < countValueTupleIndex {
   307  			condensedByteSlice = append(condensedByteSlice, ',', ' ')
   308  		}
   309  
   310  		valueAsString := fmt.Sprintf("0x%02x", countValueTuple.value)
   311  
   312  		if 1 == countValueTuple.count {
   313  			condensedByteSlice = append(condensedByteSlice, []byte(valueAsString)...)
   314  		} else {
   315  			countAsString := fmt.Sprintf("0x%x", countValueTuple.count)
   316  			condensedByteSlice = append(condensedByteSlice, []byte(countAsString+"("+valueAsString+")")...)
   317  		}
   318  	}
   319  
   320  	condensedByteSlice = append(condensedByteSlice, ']')
   321  
   322  	condensedString = string(condensedByteSlice[:])
   323  
   324  	return
   325  }
   326  
   327  func testVerifyFileInodeContents(t *testing.T, testVolumeHandle VolumeHandle, fileInodeNumber InodeNumber) {
   328  	// Verify written sections of fileInode match inMemoryFileInodeContents (i.e. non-0x00 bytes all match)
   329  
   330  	offset := uint64(0)
   331  	length := uint64(0)
   332  	currentlyScanningWrittenBytes := false
   333  
   334  	for (offset + length) < curFileSize {
   335  		if currentlyScanningWrittenBytes {
   336  			if byte(0x00) == inMemoryFileInodeContents[offset+length] {
   337  				readBuf, err := testVolumeHandle.Read(fileInodeNumber, offset, length, nil)
   338  				if nil != err {
   339  					t.Fatalf("Read(fileInodeNumber, offset, length) [case 1] failed: %v", err)
   340  				}
   341  
   342  				if bytes.Compare(readBuf, inMemoryFileInodeContents[offset:(offset+length)]) != 0 {
   343  					t.Fatalf("Read(fileInodeNumber, offset, length) [case 1] returned unexpected []byte:\n  expected %v\n       got %v", testCondenseByteSlice(inMemoryFileInodeContents[offset:(offset+length)]), testCondenseByteSlice(readBuf))
   344  				}
   345  
   346  				offset += length
   347  				length = uint64(0)
   348  				currentlyScanningWrittenBytes = false
   349  			} else {
   350  				length++
   351  			}
   352  		} else {
   353  			if byte(0x00) == inMemoryFileInodeContents[offset+length] {
   354  				offset++
   355  			} else {
   356  				length = uint64(1)
   357  				currentlyScanningWrittenBytes = true
   358  			}
   359  		}
   360  	}
   361  
   362  	if currentlyScanningWrittenBytes {
   363  		readBuf, err := testVolumeHandle.Read(fileInodeNumber, offset, length, nil)
   364  		if nil != err {
   365  			t.Fatalf("Read(fileInodeNumber, offset, length) [case 2] failed: %v", err)
   366  		}
   367  
   368  		if 0 != bytes.Compare(readBuf, inMemoryFileInodeContents[offset:(offset+length)]) {
   369  			t.Fatalf("Read(fileInodeNumber, offset, length) [case 2] returned unexpected []byte:\n  expected %v\n       got %v", testCondenseByteSlice(inMemoryFileInodeContents[offset:(offset+length)]), testCondenseByteSlice(readBuf))
   370  		}
   371  	}
   372  
   373  	// Walk through entire fileInode verifying entire inMemoryFileInodeContents
   374  
   375  	offset = uint64(0)
   376  
   377  	for offset < curFileSize {
   378  		length = testChooseExtentSize(t)
   379  
   380  		if offset+length > curFileSize {
   381  			length = curFileSize - offset
   382  		}
   383  
   384  		readBuf, err := testVolumeHandle.Read(fileInodeNumber, offset, length, nil)
   385  		if nil != err {
   386  			t.Fatalf("Read(fileInodeNumber, offset, length) [case 3] failed: %v", err)
   387  		}
   388  
   389  		if 0 != bytes.Compare(readBuf, inMemoryFileInodeContents[offset:(offset+length)]) {
   390  			t.Fatalf("Read(fileInodeNumber, offset, length) [case 3] returned unexpected []byte:\n  expected %v\n       got %v", testCondenseByteSlice(inMemoryFileInodeContents[offset:(offset+length)]), testCondenseByteSlice(readBuf))
   391  		}
   392  
   393  		offset += length
   394  	}
   395  
   396  	// Issue numZeroLengthReads zero-length reads using both Read() & GetReadPlan() for likely valid offsets
   397  
   398  	length = uint64(0)
   399  
   400  	for i := uint64(0); i < numZeroLengthReads; i++ {
   401  		offset = testChooseExtentStart(t)
   402  
   403  		readBuf, err := testVolumeHandle.Read(fileInodeNumber, offset, length, nil)
   404  		if nil != err {
   405  			t.Fatalf("Read(fileInodeNumber, offset, length) [case 4] failed: %v", err)
   406  		}
   407  
   408  		if 0 != len(readBuf) {
   409  			t.Fatalf("Read(fileInodeNumber, offset, length) [case 4] returned unexpected []byte:\n  expected %v\n       got %v", testCondenseByteSlice(inMemoryFileInodeContents[offset:(offset+length)]), testCondenseByteSlice(readBuf))
   410  		}
   411  
   412  		readPlan, err := testVolumeHandle.GetReadPlan(fileInodeNumber, &offset, &length)
   413  		if nil != err {
   414  			t.Fatalf("GetReadPlan(fileInodeNumber, offset, length) [case 4] failed: %v", err)
   415  		}
   416  
   417  		if 0 != len(readPlan) {
   418  			t.Fatalf("Read(fileInodeNumber, offset, length) [case 4] returned unexpected readPlan of length %v", len(readPlan))
   419  		}
   420  	}
   421  
   422  	// Issue numZeroLengthReads zero-length reads using both Read() & GetReadPlan() for definitely invalid offsets
   423  
   424  	length = uint64(0)
   425  
   426  	for i := uint64(0); i < numZeroLengthReads; i++ {
   427  		offset = testChooseExtentStart(t) + sizeOfTestFile
   428  
   429  		readBuf, err := testVolumeHandle.Read(fileInodeNumber, offset, length, nil)
   430  		if nil != err {
   431  			t.Fatalf("Read(fileInodeNumber, offset, length) [case 5] failed: %v", err)
   432  		}
   433  
   434  		if 0 != len(readBuf) {
   435  			t.Fatalf("Read(fileInodeNumber, offset, length) [case 5] returned unexpected []byte:\n  expected %v\n       got %v", testCondenseByteSlice(inMemoryFileInodeContents[offset:(offset+length)]), testCondenseByteSlice(readBuf))
   436  		}
   437  
   438  		readPlan, err := testVolumeHandle.GetReadPlan(fileInodeNumber, &offset, &length)
   439  		if nil != err {
   440  			t.Fatalf("GetReadPlan(fileInodeNumber, offset, length) [case 5] failed: %v", err)
   441  		}
   442  
   443  		if 0 != len(readPlan) {
   444  			t.Fatalf("Read(fileInodeNumber, offset, length) [case 5] returned unexpected readPlan of length %v", len(readPlan))
   445  		}
   446  	}
   447  }
   448  
   449  func TestFileExtents(t *testing.T) {
   450  	testSetup(t, false)
   451  
   452  	testVolumeHandle, err := FetchVolumeHandle("TestVolume")
   453  	if nil != err {
   454  		t.Fatalf("FetchVolumeHandle(\"TestVolume\") should have worked - got error: %v", err)
   455  	}
   456  
   457  	fileInodeNumber, err := testVolumeHandle.CreateFile(PosixModePerm, 0, 0)
   458  	if nil != err {
   459  		t.Fatalf("CreateFile() failed: %v", err)
   460  	}
   461  
   462  	inMemoryFileInodeContents = make([]byte, sizeOfTestFile) // Initially all 0x00... meaning no bytes written
   463  
   464  	testPopulateFileInode(t, testVolumeHandle, fileInodeNumber)
   465  
   466  	testValidateFileInodeBPlusTree(t, testVolumeHandle, fileInodeNumber)
   467  
   468  	testVerifyFileInodeBPlusTree(t, testVolumeHandle, fileInodeNumber)
   469  
   470  	// One might expect to be able to call `testVerifyFileInodeContents` here,
   471  	// but we haven't flushed yet.
   472  
   473  	err = testVolumeHandle.Flush(fileInodeNumber, false)
   474  	if nil != err {
   475  		t.Fatalf("Flush(fileInodeNumber, false) failed: %v", err)
   476  	}
   477  
   478  	testVerifyFileInodeContents(t, testVolumeHandle, fileInodeNumber)
   479  
   480  	err = testVolumeHandle.Purge(fileInodeNumber)
   481  	if nil != err {
   482  		t.Fatalf("Purge(fileInodeNumber) [case one] failed: %v", err)
   483  	}
   484  
   485  	testVerifyFileInodeContents(t, testVolumeHandle, fileInodeNumber)
   486  
   487  	err = testVolumeHandle.Purge(fileInodeNumber)
   488  	if nil != err {
   489  		t.Fatalf("Purge(fileInodeNumber) [case two] failed: %v", err)
   490  	}
   491  
   492  	err = testVolumeHandle.Destroy(fileInodeNumber)
   493  	if nil != err {
   494  		t.Fatalf("Destroy(fileInodeNumber) failed: %v", err)
   495  	}
   496  
   497  	testTeardown(t)
   498  }
   499  
   500  func TestWriteFileExtentAtExtantOffset(t *testing.T) {
   501  	testSetup(t, false)
   502  
   503  	testVolumeHandle, err := FetchVolumeHandle("TestVolume")
   504  	if nil != err {
   505  		t.Fatalf("FetchVolumeHandle(\"TestVolume\") should have worked - got error: %v", err)
   506  	}
   507  
   508  	fileInodeNumber, err := testVolumeHandle.CreateFile(PosixModePerm, 0, 0)
   509  	if nil != err {
   510  		t.Fatalf("CreateFile() failed: %v", err)
   511  	}
   512  
   513  	fileInode, ok, err := (testVolumeHandle.(*volumeStruct)).fetchInode(fileInodeNumber)
   514  	if err != nil {
   515  		t.Fatalf("testVolumeHandle.fetchInode() failed: %v", err)
   516  	}
   517  	if !ok {
   518  		t.Fatalf("testVolumeHandle.fetchInode() returned a free inode")
   519  	}
   520  
   521  	extents := fileInode.payload.(sortedmap.BPlusTree)
   522  
   523  	err = testVolumeHandle.Write(fileInodeNumber, 0, make([]byte, 20), nil)
   524  	if nil != err {
   525  		t.Fatalf("Write(fileInodeNumber, 0, make([]byte, 20)) failed: %v", err)
   526  	}
   527  
   528  	err = testVolumeHandle.Write(fileInodeNumber, 5, []byte("aaaa"), nil) // 4 bytes
   529  	if nil != err {
   530  		t.Fatalf("Write failed: %v", err)
   531  	}
   532  
   533  	// At this point, our file B+-tree should have three extents starting at
   534  	// file offsets 0, 5, and 5+4=9.
   535  
   536  	expectedOffsets := []uint64{0, 5, 9}
   537  	expectedLengths := []uint64{5, 4, 11}
   538  	for i := 0; i < 3; i++ {
   539  		_, value, ok, err := extents.GetByIndex(i)
   540  		if nil != err {
   541  			t.Fatal(err)
   542  		}
   543  		extantExtent := value.(*fileExtentStruct)
   544  		if !ok {
   545  			t.Fatalf("expected to be able to get extent")
   546  		}
   547  		if expectedOffsets[i] != extantExtent.FileOffset {
   548  			t.Fatalf("expected extent to be at offset %v, got %v", expectedOffsets[i], extantExtent.FileOffset)
   549  		}
   550  		if expectedLengths[i] != extantExtent.Length {
   551  			t.Fatalf("expected extent length %v, got %v", expectedLengths[i], extantExtent.Length)
   552  		}
   553  	}
   554  
   555  	err = testVolumeHandle.Write(fileInodeNumber, 9, []byte("bbb"), nil)
   556  	if nil != err {
   557  		t.Fatalf("Overwrite failed: %v", err)
   558  	}
   559  
   560  	err = testVolumeHandle.Flush(fileInodeNumber, false)
   561  	if nil != err {
   562  		t.Fatalf("Flush failed: %v", err)
   563  	}
   564  
   565  	testTeardown(t)
   566  }
   567  
   568  func TestOverwriteIncludesBeginningOfLastExtent(t *testing.T) {
   569  	testSetup(t, false)
   570  
   571  	testVolumeHandle, err := FetchVolumeHandle("TestVolume")
   572  	if nil != err {
   573  		t.Fatalf("FetchVolumeHandle(\"TestVolume\") should have worked - got error: %v", err)
   574  	}
   575  
   576  	fileInodeNumber, err := testVolumeHandle.CreateFile(PosixModePerm, 0, 0)
   577  	if nil != err {
   578  		t.Fatalf("CreateFile() failed: %v", err)
   579  	}
   580  
   581  	err = testVolumeHandle.Write(fileInodeNumber, 0, make([]byte, 20), nil)
   582  	if nil != err {
   583  		t.Fatalf("Write(fileInodeNumber, 0, make([]byte, 20)) failed: %v", err)
   584  	}
   585  
   586  	err = testVolumeHandle.Write(fileInodeNumber, 5, []byte("aaaa"), nil) // 4 bytes
   587  	if nil != err {
   588  		t.Fatalf("Write failed: %v", err)
   589  	}
   590  
   591  	err = testVolumeHandle.Write(fileInodeNumber, 3, []byte("bbbbbbbbbb"), nil)
   592  	if nil != err {
   593  		t.Fatalf("Write failed: %v", err)
   594  	}
   595  
   596  	err = testVolumeHandle.Flush(fileInodeNumber, false)
   597  	if nil != err {
   598  		t.Fatalf("Flush failed: %v", err)
   599  	}
   600  
   601  	testTeardown(t)
   602  }
   603  
   604  func TestReadYourWrite(t *testing.T) {
   605  	testSetup(t, false)
   606  
   607  	testVolumeHandle, err := FetchVolumeHandle("TestVolume")
   608  	if nil != err {
   609  		t.Fatalf("FetchVolumeHandle(\"TestVolume\") should have worked - got error: %v", err)
   610  	}
   611  
   612  	fileInodeNumber, err := testVolumeHandle.CreateFile(PosixModePerm, 0, 0)
   613  	if nil != err {
   614  		t.Fatalf("CreateFile() failed: %v", err)
   615  	}
   616  
   617  	ourBytes := []byte{1, 2, 3, 4, 5, 6, 7, 8}
   618  	err = testVolumeHandle.Write(fileInodeNumber, 0, ourBytes, nil)
   619  	if nil != err {
   620  		t.Fatalf("Write(fileInodeNumber, 0, []byte{1, 2, 3, 4, 5, 6, 7, 8}) failed: %v", err)
   621  	}
   622  	readBuf, err := testVolumeHandle.Read(fileInodeNumber, 0, 8, nil)
   623  	if err != nil {
   624  		t.Fatalf("Read(fileInodeNumber, 0, 8) failed: %v", err)
   625  	}
   626  
   627  	if bytes.Compare(ourBytes, readBuf) != 0 {
   628  		t.Fatalf("read after write didn't work: expected %v, got %v", ourBytes, readBuf)
   629  	}
   630  
   631  	err = testVolumeHandle.Flush(fileInodeNumber, false)
   632  	if nil != err {
   633  		t.Fatalf("Flush failed: %v", err)
   634  	}
   635  
   636  	testTeardown(t)
   637  }