whitebox_test.go raw

   1  package ffldb
   2  
   3  // This file is part of the ffldb package rather than the ffldb_test package as it provides whitebox testing.
   4  import (
   5  	"compress/bzip2"
   6  	"encoding/binary"
   7  	"fmt"
   8  	"github.com/p9c/p9/pkg/block"
   9  	"hash/crc32"
  10  	"io"
  11  	"os"
  12  	"path/filepath"
  13  	"testing"
  14  	
  15  	"github.com/btcsuite/goleveldb/leveldb"
  16  	ldberrors "github.com/btcsuite/goleveldb/leveldb/errors"
  17  	
  18  	"github.com/p9c/p9/pkg/chaincfg"
  19  	"github.com/p9c/p9/pkg/database"
  20  	"github.com/p9c/p9/pkg/wire"
  21  )
  22  
  23  var (
  24  	// blockDataNet is the expected network in the test block data.
  25  	blockDataNet = wire.MainNet
  26  	// blockDataFile is the path to a file containing the first 256 blocks of the block chain.
  27  	blockDataFile = filepath.Join("..", "tstdata", "blocks1-256.bz2")
  28  	// errSubTestFail is used to signal that a sub test returned false.
  29  	errSubTestFail = fmt.Errorf("sub test failure")
  30  )
  31  
  32  // loadBlocks loads the blocks contained in the tstdata directory and returns a slice of them.
  33  func loadBlocks(t *testing.T, dataFile string, network wire.BitcoinNet) ([]*block.Block, error) {
  34  	// Open the file that contains the blocks for reading.
  35  	fi, e := os.Open(dataFile)
  36  	if e != nil {
  37  		t.Errorf("failed to open file %v, err %v", dataFile, e)
  38  		return nil, e
  39  	}
  40  	defer func() {
  41  		if e := fi.Close(); E.Chk(e) {
  42  			t.Errorf(
  43  				"failed to close file %v %v", dataFile,
  44  				e,
  45  			)
  46  		}
  47  	}()
  48  	dr := bzip2.NewReader(fi)
  49  	// Set the first block as the genesis block.
  50  	blocks := make([]*block.Block, 0, 256)
  51  	genesis := block.NewBlock(chaincfg.MainNetParams.GenesisBlock)
  52  	blocks = append(blocks, genesis)
  53  	// Load the remaining blocks.
  54  	for height := 1; ; height++ {
  55  		var net uint32
  56  		e := binary.Read(dr, binary.LittleEndian, &net)
  57  		if e == io.EOF {
  58  			// Hit end of file at the expected offset.  No error.
  59  			break
  60  		}
  61  		if e != nil {
  62  			t.Errorf(
  63  				"Failed to load network type for block %d: %v",
  64  				height, e,
  65  			)
  66  			return nil, e
  67  		}
  68  		if net != uint32(network) {
  69  			t.Errorf(
  70  				"Block doesn't match network: %v expects %v",
  71  				net, network,
  72  			)
  73  			return nil, e
  74  		}
  75  		var blockLen uint32
  76  		e = binary.Read(dr, binary.LittleEndian, &blockLen)
  77  		if e != nil {
  78  			t.Errorf(
  79  				"Failed to load block size for block %d: %v",
  80  				height, e,
  81  			)
  82  			return nil, e
  83  		}
  84  		// Read the block.
  85  		blockBytes := make([]byte, blockLen)
  86  		_, e = io.ReadFull(dr, blockBytes)
  87  		if e != nil {
  88  			t.Errorf("Failed to load block %d: %v", height, e)
  89  			return nil, e
  90  		}
  91  		// Deserialize and store the block.
  92  		block, e := block.NewFromBytes(blockBytes)
  93  		if e != nil {
  94  			t.Errorf("Failed to parse block %v: %v", height, e)
  95  			return nil, e
  96  		}
  97  		blocks = append(blocks, block)
  98  	}
  99  	return blocks, nil
 100  }
 101  
 102  // checkDbError ensures the passed error is a database.DBError with an error code that matches the passed  error code.
 103  func checkDbError(t *testing.T, testName string, gotErr error, wantErrCode database.ErrorCode) bool {
 104  	dbErr, ok := gotErr.(database.DBError)
 105  	if !ok {
 106  		t.Errorf(
 107  			"%s: unexpected error type - got %T, want %T",
 108  			testName, gotErr, database.DBError{},
 109  		)
 110  		return false
 111  	}
 112  	if dbErr.ErrorCode != wantErrCode {
 113  		t.Errorf(
 114  			"%s: unexpected error code - got %s (%s), want %s",
 115  			testName, dbErr.ErrorCode, dbErr.Description,
 116  			wantErrCode,
 117  		)
 118  		return false
 119  	}
 120  	return true
 121  }
 122  
 123  // testContext is used to store context information about a running test which is passed into helper functions.
 124  type testContext struct {
 125  	t            *testing.T
 126  	db           database.DB
 127  	files        map[uint32]*lockableFile
 128  	maxFileSizes map[uint32]int64
 129  	blocks       []*block.Block
 130  }
 131  
 132  // TestConvertErr ensures the leveldb error to database error conversion works as expected.
 133  func TestConvertErr(t *testing.T) {
 134  	t.Parallel()
 135  	tests := []struct {
 136  		err         error
 137  		wantErrCode database.ErrorCode
 138  	}{
 139  		{&ldberrors.ErrCorrupted{}, database.ErrCorruption},
 140  		{leveldb.ErrClosed, database.ErrDbNotOpen},
 141  		{leveldb.ErrSnapshotReleased, database.ErrTxClosed},
 142  		{leveldb.ErrIterReleased, database.ErrTxClosed},
 143  	}
 144  	for i, test := range tests {
 145  		gotErr := convertErr("test", test.err)
 146  		if gotErr.ErrorCode != test.wantErrCode {
 147  			t.Errorf("convertErr #%d unexpected error - got %v, want %v", i, gotErr.ErrorCode, test.wantErrCode)
 148  			continue
 149  		}
 150  	}
 151  }
 152  
 153  // TestCornerCases ensures several corner cases which can happen when opening a database and/or block files work as expected.
 154  func TestCornerCases(t *testing.T) {
 155  	t.Parallel()
 156  	// Create a file at the datapase path to force the open below to fail.
 157  	dbPath := filepath.Join(os.TempDir(), "ffldb-errors")
 158  	_ = os.RemoveAll(dbPath)
 159  	fi, e := os.Create(dbPath)
 160  	if e != nil {
 161  		t.Errorf("os.Create: unexpected error: %v", e)
 162  		return
 163  	}
 164  	if e = fi.Close(); E.Chk(e) {
 165  	}
 166  	// Ensure creating a new database fails when a file exists where a directory is needed.
 167  	testName := "openDB: fail due to file at target location"
 168  	wantErrCode := database.ErrDriverSpecific
 169  	var idb database.DB
 170  	if idb, e = openDB(dbPath, blockDataNet, true); E.Chk(e) {
 171  	}
 172  	if !checkDbError(t, testName, e, wantErrCode) {
 173  		if e = idb.Close(); E.Chk(e) {
 174  		}
 175  		if e = os.RemoveAll(dbPath); E.Chk(e) {
 176  		}
 177  		return
 178  	}
 179  	// Remove the file and create the database to run tests against.  It should be successful this time.
 180  	_ = os.RemoveAll(dbPath)
 181  	idb, e = openDB(dbPath, blockDataNet, true)
 182  	if e != nil {
 183  		t.Errorf("openDB: unexpected error: %v", e)
 184  		return
 185  	}
 186  	defer func() {
 187  		if e = os.RemoveAll(dbPath); E.Chk(e) {
 188  		}
 189  		if e = idb.Close(); E.Chk(e) {
 190  		}
 191  	}()
 192  	// Ensure attempting to write to a file that can't be created returns the expected error.
 193  	testName = "writeBlock: open file failure"
 194  	filePath := blockFilePath(dbPath, 0)
 195  	if e = os.Mkdir(filePath, 0755); E.Chk(e) {
 196  		t.Errorf("os.Mkdir: unexpected error: %v", e)
 197  		return
 198  	}
 199  	store := idb.(*db).store
 200  	_, e = store.writeBlock([]byte{0x00})
 201  	if !checkDbError(t, testName, e, database.ErrDriverSpecific) {
 202  		return
 203  	}
 204  	_ = os.RemoveAll(filePath)
 205  	// Close the underlying leveldb database out from under the database.
 206  	ldb := idb.(*db).cache.ldb
 207  	if e = ldb.Close(); E.Chk(e) {
 208  	}
 209  	// Ensure initilization errors in the underlying database work as expected.
 210  	testName = "initDB: reinitialization"
 211  	wantErrCode = database.ErrDbNotOpen
 212  	e = initDB(ldb)
 213  	if !checkDbError(t, testName, e, wantErrCode) {
 214  		return
 215  	}
 216  	// Ensure the View handles errors in the underlying leveldb database properly.
 217  	testName = "View: underlying leveldb error"
 218  	wantErrCode = database.ErrDbNotOpen
 219  	e = idb.View(
 220  		func(tx database.Tx) (e error) {
 221  			return nil
 222  		},
 223  	)
 224  	if !checkDbError(t, testName, e, wantErrCode) {
 225  		return
 226  	}
 227  	// Ensure the Update handles errors in the underlying leveldb database properly.
 228  	testName = "Update: underlying leveldb error"
 229  	e = idb.Update(
 230  		func(tx database.Tx) (e error) {
 231  			return nil
 232  		},
 233  	)
 234  	if !checkDbError(t, testName, e, wantErrCode) {
 235  		return
 236  	}
 237  }
 238  
 239  // resetDatabase removes everything from the opened database associated with the test context including all metadata and the mock files.
 240  func resetDatabase(tc *testContext) bool {
 241  	// Reset the metadata.
 242  	e := tc.db.Update(
 243  		func(tx database.Tx) (e error) {
 244  			// Remove all the keys using a cursor while also generating a list of buckets.  It's not safe to remove keys during ForEach iteration nor is it safe to remove buckets during cursor iteration, so this dual approach is needed.
 245  			var bucketNames [][]byte
 246  			cursor := tx.Metadata().Cursor()
 247  			for ok := cursor.First(); ok; ok = cursor.Next() {
 248  				if cursor.Value() != nil {
 249  					if e = cursor.Delete(); E.Chk(e) {
 250  						return e
 251  					}
 252  				} else {
 253  					bucketNames = append(bucketNames, cursor.Key())
 254  				}
 255  			}
 256  			// Remove the buckets.
 257  			for _, k := range bucketNames {
 258  				if e = tx.Metadata().DeleteBucket(k); E.Chk(e) {
 259  					return e
 260  				}
 261  			}
 262  			_, e = tx.Metadata().CreateBucket(blockIdxBucketName)
 263  			return e
 264  		},
 265  	)
 266  	if e != nil {
 267  		tc.t.Errorf("Update: unexpected error: %v", e)
 268  		return false
 269  	}
 270  	// Reset the mock files.
 271  	store := tc.db.(*db).store
 272  	wc := store.writeCursor
 273  	wc.curFile.Lock()
 274  	if wc.curFile.file != nil {
 275  		if e := wc.curFile.file.Close(); E.Chk(e) {
 276  		}
 277  		wc.curFile.file = nil
 278  	}
 279  	wc.curFile.Unlock()
 280  	wc.Lock()
 281  	wc.curFileNum = 0
 282  	wc.curOffset = 0
 283  	wc.Unlock()
 284  	tc.files = make(map[uint32]*lockableFile)
 285  	tc.maxFileSizes = make(map[uint32]int64)
 286  	return true
 287  }
 288  
 289  // testWriteFailures tests various failures paths when writing to the block files.
 290  func testWriteFailures(tc *testContext) bool {
 291  	if !resetDatabase(tc) {
 292  		return false
 293  	}
 294  	// Ensure file sync errors during flush return the expected error.
 295  	store := tc.db.(*db).store
 296  	testName := "flush: file sync failure"
 297  	store.writeCursor.Lock()
 298  	oldFile := store.writeCursor.curFile
 299  	store.writeCursor.curFile = &lockableFile{
 300  		file: &mockFile{forceSyncErr: true, maxSize: -1},
 301  	}
 302  	store.writeCursor.Unlock()
 303  	var e error
 304  	e = tc.db.(*db).cache.flush()
 305  	if !checkDbError(tc.t, testName, e, database.ErrDriverSpecific) {
 306  		return false
 307  	}
 308  	store.writeCursor.Lock()
 309  	store.writeCursor.curFile = oldFile
 310  	store.writeCursor.Unlock()
 311  	// Force errors in the various error paths when writing data by using mock files with a limited max size.
 312  	block0Bytes, _ := tc.blocks[0].Bytes()
 313  	tests := []struct {
 314  		fileNum uint32
 315  		maxSize int64
 316  	}{
 317  		// Force an error when writing the network bytes.
 318  		{fileNum: 0, maxSize: 2},
 319  		// Force an error when writing the block size.
 320  		{fileNum: 0, maxSize: 6},
 321  		// Force an error when writing the block.
 322  		{fileNum: 0, maxSize: 17},
 323  		// Force an error when writing the checksum.
 324  		{fileNum: 0, maxSize: int64(len(block0Bytes)) + 10},
 325  		// Force an error after writing enough blocks for force multiple
 326  		// files.
 327  		{fileNum: 15, maxSize: 1},
 328  	}
 329  	for i, test := range tests {
 330  		if !resetDatabase(tc) {
 331  			return false
 332  		}
 333  		// Ensure storing the specified number of blocks using a mock file that fails the write fails when the transaction is committed, not when the block is stored.
 334  		tc.maxFileSizes = map[uint32]int64{test.fileNum: test.maxSize}
 335  		e = tc.db.Update(
 336  			func(tx database.Tx) (e error) {
 337  				for i, block := range tc.blocks {
 338  					e := tx.StoreBlock(block)
 339  					if e != nil {
 340  						tc.t.Errorf(
 341  							"StoreBlock (%d): unexpected "+
 342  								"error: %v", i, e,
 343  						)
 344  						return errSubTestFail
 345  					}
 346  				}
 347  				return nil
 348  			},
 349  		)
 350  		testName := fmt.Sprintf(
 351  			"Force update commit failure - test "+
 352  				"%d, fileNum %d, maxsize %d", i, test.fileNum,
 353  			test.maxSize,
 354  		)
 355  		if !checkDbError(tc.t, testName, e, database.ErrDriverSpecific) {
 356  			tc.t.Errorf("%v", e)
 357  			return false
 358  		}
 359  		// Ensure the commit rollback removed all extra files and data.
 360  		if len(tc.files) != 1 {
 361  			tc.t.Errorf(
 362  				"Update rollback: new not removed - want "+
 363  					"1 file, got %d", len(tc.files),
 364  			)
 365  			return false
 366  		}
 367  		if _, ok := tc.files[0]; !ok {
 368  			tc.t.Error("Update rollback: file 0 does not exist")
 369  			return false
 370  		}
 371  		file := tc.files[0].file.(*mockFile)
 372  		if len(file.data) != 0 {
 373  			tc.t.Errorf(
 374  				"Update rollback: file did not truncate - "+
 375  					"want len 0, got len %d", len(file.data),
 376  			)
 377  			return false
 378  		}
 379  	}
 380  	return true
 381  }
 382  
 383  // testBlockFileErrors ensures the database returns expected errors with various file-related issues such as closed and missing files.
 384  func testBlockFileErrors(tc *testContext) bool {
 385  	if !resetDatabase(tc) {
 386  		return false
 387  	}
 388  	// Ensure errors in blockFile and openFile when requesting invalid file numbers.
 389  	store := tc.db.(*db).store
 390  	testName := "blockFile invalid file open"
 391  	_, e := store.blockFile(^uint32(0))
 392  	if !checkDbError(tc.t, testName, e, database.ErrDriverSpecific) {
 393  		return false
 394  	}
 395  	testName = "openFile invalid file open"
 396  	_, e = store.openFile(^uint32(0))
 397  	if !checkDbError(tc.t, testName, e, database.ErrDriverSpecific) {
 398  		return false
 399  	}
 400  	// Insert the first block into the mock file.
 401  	e = tc.db.Update(
 402  		func(tx database.Tx) (e error) {
 403  			e = tx.StoreBlock(tc.blocks[0])
 404  			if e != nil {
 405  				tc.t.Errorf("StoreBlock: unexpected error: %v", e)
 406  				return errSubTestFail
 407  			}
 408  			return nil
 409  		},
 410  	)
 411  	if e != nil {
 412  		if e != errSubTestFail {
 413  			tc.t.Errorf("Update: unexpected error: %v", e)
 414  		}
 415  		return false
 416  	}
 417  	// Ensure errors in readBlock and readBlockRegion when requesting a file number that doesn't exist.
 418  	block0Hash := tc.blocks[0].Hash()
 419  	testName = "readBlock invalid file number"
 420  	invalidLoc := blockLocation{
 421  		blockFileNum: ^uint32(0),
 422  		blockLen:     80,
 423  	}
 424  	_, e = store.readBlock(block0Hash, invalidLoc)
 425  	if !checkDbError(tc.t, testName, e, database.ErrDriverSpecific) {
 426  		return false
 427  	}
 428  	testName = "readBlockRegion invalid file number"
 429  	_, e = store.readBlockRegion(invalidLoc, 0, 80)
 430  	if !checkDbError(tc.t, testName, e, database.ErrDriverSpecific) {
 431  		return false
 432  	}
 433  	// Close the block file out from under the database.
 434  	store.writeCursor.curFile.Lock()
 435  	if e = store.writeCursor.curFile.file.Close(); E.Chk(e) {
 436  	}
 437  	store.writeCursor.curFile.Unlock()
 438  	// Ensure failures in FetchBlock and FetchBlockRegion(s) since the underlying file they need to read from has been closed.
 439  	e = tc.db.View(
 440  		func(tx database.Tx) (e error) {
 441  			testName = "FetchBlock closed file"
 442  			wantErrCode := database.ErrDriverSpecific
 443  			_, e = tx.FetchBlock(block0Hash)
 444  			if !checkDbError(tc.t, testName, e, wantErrCode) {
 445  				return errSubTestFail
 446  			}
 447  			testName = "FetchBlockRegion closed file"
 448  			regions := []database.BlockRegion{
 449  				{
 450  					Hash:   block0Hash,
 451  					Len:    80,
 452  					Offset: 0,
 453  				},
 454  			}
 455  			_, e = tx.FetchBlockRegion(&regions[0])
 456  			if !checkDbError(tc.t, testName, e, wantErrCode) {
 457  				return errSubTestFail
 458  			}
 459  			testName = "FetchBlockRegions closed file"
 460  			_, e = tx.FetchBlockRegions(regions)
 461  			if !checkDbError(tc.t, testName, e, wantErrCode) {
 462  				return errSubTestFail
 463  			}
 464  			return nil
 465  		},
 466  	)
 467  	if e != nil {
 468  		if e != errSubTestFail {
 469  			tc.t.Errorf("View: unexpected error: %v", e)
 470  		}
 471  		return false
 472  	}
 473  	return true
 474  }
 475  
 476  // testCorruption ensures the database returns expected errors under various corruption scenarios.
 477  func testCorruption(tc *testContext) bool {
 478  	if !resetDatabase(tc) {
 479  		return false
 480  	}
 481  	// Insert the first block into the mock file.
 482  	e := tc.db.Update(
 483  		func(tx database.Tx) (e error) {
 484  			e = tx.StoreBlock(tc.blocks[0])
 485  			if e != nil {
 486  				tc.t.Errorf("StoreBlock: unexpected error: %v", e)
 487  				return errSubTestFail
 488  			}
 489  			return nil
 490  		},
 491  	)
 492  	if e != nil {
 493  		if e != errSubTestFail {
 494  			tc.t.Errorf("Update: unexpected error: %v", e)
 495  		}
 496  		return false
 497  	}
 498  	// Ensure corruption is detected by intentionally modifying the bytes stored to the mock file and reading the block.
 499  	block0Bytes, _ := tc.blocks[0].Bytes()
 500  	block0Hash := tc.blocks[0].Hash()
 501  	tests := []struct {
 502  		offset      uint32
 503  		fixChecksum bool
 504  		wantErrCode database.ErrorCode
 505  	}{
 506  		// One of the network bytes.  The checksum needs to be fixed so the invalid network is detected.
 507  		{2, true, database.ErrDriverSpecific},
 508  		// The same network byte, but this time don't fix the checksum to ensure the corruption is detected.
 509  		{2, false, database.ErrCorruption},
 510  		// One of the block length bytes.
 511  		{6, false, database.ErrCorruption},
 512  		// Random header byte.
 513  		{17, false, database.ErrCorruption},
 514  		// Random transaction byte.
 515  		{90, false, database.ErrCorruption},
 516  		// Random checksum byte.
 517  		{uint32(len(block0Bytes)) + 10, false, database.ErrCorruption},
 518  	}
 519  	e = tc.db.View(
 520  		func(tx database.Tx) (e error) {
 521  			data := tc.files[0].file.(*mockFile).data
 522  			for i, test := range tests {
 523  				// Corrupt the byte at the offset by a single bit.
 524  				data[test.offset] ^= 0x10
 525  				// Fix the checksum if requested to force other errors.
 526  				fileLen := len(data)
 527  				var oldChecksumBytes [4]byte
 528  				copy(oldChecksumBytes[:], data[fileLen-4:])
 529  				if test.fixChecksum {
 530  					toSum := data[:fileLen-4]
 531  					cksum := crc32.Checksum(toSum, castagnoli)
 532  					binary.BigEndian.PutUint32(data[fileLen-4:], cksum)
 533  				}
 534  				testName := fmt.Sprintf(
 535  					"FetchBlock (test #%d): "+
 536  						"corruption", i,
 537  				)
 538  				_, e = tx.FetchBlock(block0Hash)
 539  				if !checkDbError(tc.t, testName, e, test.wantErrCode) {
 540  					return errSubTestFail
 541  				}
 542  				// Reset the corrupted data back to the original.
 543  				data[test.offset] ^= 0x10
 544  				if test.fixChecksum {
 545  					copy(data[fileLen-4:], oldChecksumBytes[:])
 546  				}
 547  			}
 548  			return nil
 549  		},
 550  	)
 551  	if e != nil {
 552  		if e != errSubTestFail {
 553  			tc.t.Errorf("View: unexpected error: %v", e)
 554  		}
 555  		return false
 556  	}
 557  	return true
 558  }
 559  
 560  // // TestFailureScenarios ensures several failure scenarios such as database corruption, block file write failures, and rollback failures are handled correctly.
 561  // func TestFailureScenarios(// 	t *testing.T) {
 562  // 	// Create a new database to run tests against.
 563  // 	dbPath := filepath.Join(os.TempDir(), "ffldb-failurescenarios")
 564  // 	_ = os.RemoveAll(dbPath)
 565  // 	idb, e := database.Create(dbType, dbPath, blockDataNet)
 566  // 	if e != nil  {
 567  // 		t.Errorf("Failed to create test database (%s) %v", dbType, err)
 568  // 		return
 569  // 	}
 570  // 	defer os.RemoveAll(dbPath)
 571  // 	defer idb.Close()
 572  // 	// Create a test context to pass around.
 573  // 	tc := &testContext{
 574  // 		t:            t,
 575  // 		db:           idb,
 576  // 		files:        make(map[uint32]*lockableFile),
 577  // 		maxFileSizes: make(map[uint32]int64),
 578  // 	}
 579  // 	// Change the maximum file size to a small value to force multiple flat files with the test data set and replace the file-related functions to make use of mock files in memory.  This allows injection of various file-related errors.
 580  // 	store := idb.(*db).store
 581  // 	store.maxBlockFileSize = 1024 // 1KiB
 582  // 	store.openWriteFileFunc = func(fileNum uint32) (filer, error) {
 583  // 		if file, ok := tc.files[fileNum]; ok {
 584  // 			// "Reopen" the file.
 585  // 			file.Lock()
 586  // 			mock := file.file.(*mockFile)
 587  // 			mock.Lock()
 588  // 			mock.closed = false
 589  // 			mock.Unlock()
 590  // 			file.Unlock()
 591  // 			return mock, nil
 592  // 		}
 593  // 		// Limit the max size of the mock file as specified in the test context.
 594  // 		maxSize := int64(-1)
 595  // 		if maxFileSize, ok := tc.maxFileSizes[fileNum]; ok {
 596  // 			maxSize = int64(maxFileSize)
 597  // 		}
 598  // 		file := &mockFile{maxSize: int64(maxSize)}
 599  // 		tc.files[fileNum] = &lockableFile{file: file}
 600  // 		return file, nil
 601  // 	}
 602  // 	store.openFileFunc = func(fileNum uint32) (*lockableFile, error) {
 603  // 		// Force error when trying to open max file num.
 604  // 		if fileNum == ^uint32(0) {
 605  // 			return nil, makeDbErr(database.ErrDriverSpecific,
 606  // 				"test", nil)
 607  // 		}
 608  // 		if file, ok := tc.files[fileNum]; ok {
 609  // 			// "Reopen" the file.
 610  // 			file.Lock()
 611  // 			mock := file.file.(*mockFile)
 612  // 			mock.Lock()
 613  // 			mock.closed = false
 614  // 			mock.Unlock()
 615  // 			file.Unlock()
 616  // 			return file, nil
 617  // 		}
 618  // 		file := &lockableFile{file: &mockFile{}}
 619  // 		tc.files[fileNum] = file
 620  // 		return file, nil
 621  // 	}
 622  // 	store.deleteFileFunc = func(fileNum uint32) (e error) {
 623  // 		if file, ok := tc.files[fileNum]; ok {
 624  // 			file.Lock()
 625  // 			file.file.Close()
 626  // 			file.Unlock()
 627  // 			delete(tc.files, fileNum)
 628  // 			return nil
 629  // 		}
 630  // 		str := fmt.Sprintf("file %d does not exist", fileNum)
 631  // 		return makeDbErr(database.ErrDriverSpecific, str, nil)
 632  // 	}
 633  // 	// Load the test blocks and save in the test context for use throughout the tests.
 634  // 	blocks, e := loadBlocks(t, blockDataFile, blockDataNet)
 635  // 	if e != nil  {
 636  // 		t.Errorf("loadBlocks: Unexpected error: %v", err)
 637  // 		return
 638  // 	}
 639  // 	tc.blocks = blocks
 640  // 	// Test various failures paths when writing to the block files.
 641  // 	if !testWriteFailures(tc) {
 642  // 		return
 643  // 	}
 644  // 	// Test various file-related issues such as closed and missing files.
 645  // 	if !testBlockFileErrors(tc) {
 646  // 		return
 647  // 	}
 648  // 	// Test various corruption scenarios.
 649  // 	testCorruption(tc)
 650  // }
 651