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