Skip to content

Commit

Permalink
blockchain: Consolidate tests into the main package.
Browse files Browse the repository at this point in the history
Putting the test code in the same package makes it easier for forks
since they don't have to change the import paths as much and it also
gets rid of the need for internal_test.go to bridge.
  • Loading branch information
davecgh committed Feb 26, 2018
1 parent 1f66f20 commit 8c69c29
Show file tree
Hide file tree
Showing 14 changed files with 388 additions and 458 deletions.
2 changes: 1 addition & 1 deletion blockchain/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package blockchain_test
package blockchain

// TODO Make benchmarking tests for various functions, such as sidechain
// evaluation.
25 changes: 4 additions & 21 deletions blockchain/chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package blockchain_test
package blockchain

import (
"bytes"
Expand All @@ -14,10 +14,8 @@ import (
"path/filepath"
"testing"

"github.com/decred/dcrd/blockchain"
"github.com/decred/dcrd/blockchain/chaingen"
"github.com/decred/dcrd/chaincfg"
"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/dcrutil"
)

Expand All @@ -36,19 +34,6 @@ func cloneParams(params *chaincfg.Params) *chaincfg.Params {
return &paramsCopy
}

// mustParseHash converts the passed big-endian hex string into a
// chainhash.Hash and will panic if there is an error. It only differs from the
// one available in chainhash in that it will panic so errors in the source code
// be detected. It will only (and must only) be called with hard-coded, and
// therefore known good, hashes.
func mustParseHash(s string) *chainhash.Hash {
hash, err := chainhash.NewHashFromStr(s)
if err != nil {
panic("invalid hash in source file: " + s)
}
return hash
}

// TestBlockchainFunction tests the various blockchain API to ensure proper
// functionality.
func TestBlockchainFunctions(t *testing.T) {
Expand Down Expand Up @@ -96,7 +81,7 @@ func TestBlockchainFunctions(t *testing.T) {
t.Errorf("NewBlockFromBytes error: %v", err.Error())
}

_, _, err = chain.ProcessBlock(bl, blockchain.BFNone)
_, _, err = chain.ProcessBlock(bl, BFNone)
if err != nil {
t.Fatalf("ProcessBlock error at height %v: %v", i, err.Error())
}
Expand Down Expand Up @@ -173,8 +158,7 @@ func TestForceHeadReorg(t *testing.T) {
t.Logf("Testing block %s (hash %s, height %d)",
g.TipName(), block.Hash(), blockHeight)

isMainChain, isOrphan, err := chain.ProcessBlock(block,
blockchain.BFNone)
isMainChain, isOrphan, err := chain.ProcessBlock(block, BFNone)
if err != nil {
t.Fatalf("block %q (hash %s, height %d) should "+
"have been accepted: %v", g.TipName(),
Expand Down Expand Up @@ -213,8 +197,7 @@ func TestForceHeadReorg(t *testing.T) {
t.Logf("Testing block %s (hash %s, height %d)",
g.TipName(), block.Hash(), blockHeight)

isMainChain, isOrphan, err := chain.ProcessBlock(block,
blockchain.BFNone)
isMainChain, isOrphan, err := chain.ProcessBlock(block, BFNone)
if err != nil {
t.Fatalf("block %q (hash %s, height %d) should "+
"have been accepted: %v", g.TipName(),
Expand Down
64 changes: 59 additions & 5 deletions blockchain/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package blockchain_test
package blockchain

import (
"fmt"
"os"
"path/filepath"
"time"

"github.com/decred/dcrd/blockchain"
"github.com/decred/dcrd/blockchain/stake"
"github.com/decred/dcrd/chaincfg"
"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/database"
_ "github.com/decred/dcrd/database/ffldb"
"github.com/decred/dcrd/txscript"
Expand Down Expand Up @@ -55,7 +57,7 @@ func isSupportedDbType(dbType string) bool {
// chainSetup is used to create a new db and chain instance with the genesis
// block already inserted. In addition to the new chain instance, it returns
// a teardown function the caller should invoke when done testing to clean up.
func chainSetup(dbName string, params *chaincfg.Params) (*blockchain.BlockChain, func(), error) {
func chainSetup(dbName string, params *chaincfg.Params) (*BlockChain, func(), error) {
if !isSupportedDbType(testDbType) {
return nil, nil, fmt.Errorf("unsupported db type %v", testDbType)
}
Expand Down Expand Up @@ -109,10 +111,10 @@ func chainSetup(dbName string, params *chaincfg.Params) (*blockchain.BlockChain,
paramsCopy := *params

// Create the main chain instance.
chain, err := blockchain.New(&blockchain.Config{
chain, err := New(&Config{
DB: db,
ChainParams: &paramsCopy,
TimeSource: blockchain.NewMedianTime(),
TimeSource: NewMedianTime(),
SigCache: txscript.NewSigCache(1000),
})

Expand All @@ -124,3 +126,55 @@ func chainSetup(dbName string, params *chaincfg.Params) (*blockchain.BlockChain,

return chain, teardown, nil
}

// newFakeChain returns a chain that is usable for syntetic tests. It is
// important to note that this chain has no database associated with it, so
// it is not usable with all functions and the tests must take care when making
// use of it.
func newFakeChain(params *chaincfg.Params) *BlockChain {
// Create a genesis block node and block index populated with it for use
// when creating the fake chain below.
node := newBlockNode(&params.GenesisBlock.Header, nil)
node.inMainChain = true
index := newBlockIndex(nil, params)
index.AddNode(node)

return &BlockChain{
chainParams: params,
deploymentCaches: newThresholdCaches(params),
bestNode: node,
index: index,
isVoterMajorityVersionCache: make(map[[stakeMajorityCacheKeySize]byte]bool),
isStakeMajorityVersionCache: make(map[[stakeMajorityCacheKeySize]byte]bool),
calcPriorStakeVersionCache: make(map[[chainhash.HashSize]byte]uint32),
calcVoterVersionIntervalCache: make(map[[chainhash.HashSize]byte]uint32),
calcStakeVersionCache: make(map[[chainhash.HashSize]byte]uint32),
}
}

// newFakeNode creates a block node connected to the passed parent with the
// provided fields populated and fake values for the other fields.
func newFakeNode(parent *blockNode, blockVersion int32, stakeVersion uint32, bits uint32, timestamp time.Time) *blockNode {
// Make up a header and create a block node from it.
header := &wire.BlockHeader{
Version: blockVersion,
PrevBlock: parent.hash,
VoteBits: 0x01,
Bits: bits,
Height: uint32(parent.height) + 1,
Timestamp: timestamp,
StakeVersion: stakeVersion,
}
return newBlockNode(header, parent)
}

// appendFakeVotes appends the passed number of votes to the node with the
// provided version and vote bits.
func appendFakeVotes(node *blockNode, numVotes uint16, voteVersion uint32, voteBits uint16) {
for i := uint16(0); i < numVotes; i++ {
node.votes = append(node.votes, stake.VoteVersionTuple{
Version: voteVersion,
Bits: voteBits,
})
}
}
114 changes: 113 additions & 1 deletion blockchain/fullblocks_test.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,134 @@
// Copyright (c) 2016 The btcsuite developers
// Copyright (c) 2016-2017 The Decred developers
// Copyright (c) 2016-2018 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package blockchain_test

import (
"bytes"
"fmt"
"os"
"path/filepath"
"testing"

"github.com/decred/dcrd/blockchain"
"github.com/decred/dcrd/blockchain/fullblocktests"
"github.com/decred/dcrd/chaincfg"
"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/database"
"github.com/decred/dcrd/dcrutil"
"github.com/decred/dcrd/txscript"
"github.com/decred/dcrd/wire"
)

const (
// testDbType is the database backend type to use for the tests.
testDbType = "ffldb"

// testDbRoot is the root directory used to create all test databases.
testDbRoot = "testdbs"

// blockDataNet is the expected network in the test block data.
blockDataNet = wire.MainNet
)

// filesExists returns whether or not the named file or directory exists.
func fileExists(name string) bool {
if _, err := os.Stat(name); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}

// isSupportedDbType returns whether or not the passed database type is
// currently supported.
func isSupportedDbType(dbType string) bool {
supportedDrivers := database.SupportedDrivers()
for _, driver := range supportedDrivers {
if dbType == driver {
return true
}
}

return false
}

// chainSetup is used to create a new db and chain instance with the genesis
// block already inserted. In addition to the new chain instance, it returns
// a teardown function the caller should invoke when done testing to clean up.
func chainSetup(dbName string, params *chaincfg.Params) (*blockchain.BlockChain, func(), error) {
if !isSupportedDbType(testDbType) {
return nil, nil, fmt.Errorf("unsupported db type %v", testDbType)
}

// Handle memory database specially since it doesn't need the disk
// specific handling.
var db database.DB
var teardown func()
if testDbType == "memdb" {
ndb, err := database.Create(testDbType)
if err != nil {
return nil, nil, fmt.Errorf("error creating db: %v", err)
}
db = ndb

// Setup a teardown function for cleaning up. This function is
// returned to the caller to be invoked when it is done testing.
teardown = func() {
db.Close()
}
} else {
// Create the root directory for test databases.
if !fileExists(testDbRoot) {
if err := os.MkdirAll(testDbRoot, 0700); err != nil {
err := fmt.Errorf("unable to create test db "+
"root: %v", err)
return nil, nil, err
}
}

// Create a new database to store the accepted blocks into.
dbPath := filepath.Join(testDbRoot, dbName)
_ = os.RemoveAll(dbPath)
ndb, err := database.Create(testDbType, dbPath, blockDataNet)
if err != nil {
return nil, nil, fmt.Errorf("error creating db: %v", err)
}
db = ndb

// Setup a teardown function for cleaning up. This function is
// returned to the caller to be invoked when it is done testing.
teardown = func() {
db.Close()
os.RemoveAll(dbPath)
os.RemoveAll(testDbRoot)
}
}

// Copy the chain params to ensure any modifications the tests do to
// the chain parameters do not affect the global instance.
paramsCopy := *params

// Create the main chain instance.
chain, err := blockchain.New(&blockchain.Config{
DB: db,
ChainParams: &paramsCopy,
TimeSource: blockchain.NewMedianTime(),
SigCache: txscript.NewSigCache(1000),
})

if err != nil {
teardown()
err := fmt.Errorf("failed to create chain instance: %v", err)
return nil, nil, err
}

return chain, teardown, nil
}

// TestFullBlocks ensures all tests generated by the fullblocktests package
// have the expected result when processed via ProcessBlock.
func TestFullBlocks(t *testing.T) {
Expand Down
Loading

0 comments on commit 8c69c29

Please sign in to comment.