d06c0bb181
This modifies the blockNode and BestState structs in the blockchain package to store hashes directly instead of pointers to them and updates callers to deal with the API change in the exported BestState struct. In general, the preferred approach for hashes moving forward is to store hash values in complex data structures, particularly those that will be used for cache entries, and accept pointers to hashes in arguments to functions. Some of the reasoning behind making this change is: - It is generally preferred to avoid storing pointers to data in cache objects since doing so can easily lead to storing interior pointers into other structs that then can't be GC'd - Keeping the hash values directly in the block node provides better cache locality
195 lines
5.4 KiB
Go
195 lines
5.4 KiB
Go
// Copyright (c) 2016 The btcsuite developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package blockchain
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
)
|
|
|
|
// TestThresholdStateStringer tests the stringized output for the
|
|
// ThresholdState type.
|
|
func TestThresholdStateStringer(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
in ThresholdState
|
|
want string
|
|
}{
|
|
{ThresholdDefined, "ThresholdDefined"},
|
|
{ThresholdStarted, "ThresholdStarted"},
|
|
{ThresholdLockedIn, "ThresholdLockedIn"},
|
|
{ThresholdActive, "ThresholdActive"},
|
|
{ThresholdFailed, "ThresholdFailed"},
|
|
{0xff, "Unknown ThresholdState (255)"},
|
|
}
|
|
|
|
// Detect additional threshold states that don't have the stringer added.
|
|
if len(tests)-1 != int(numThresholdsStates) {
|
|
t.Errorf("It appears a threshold statewas added without " +
|
|
"adding an associated stringer test")
|
|
}
|
|
|
|
t.Logf("Running %d tests", len(tests))
|
|
for i, test := range tests {
|
|
result := test.in.String()
|
|
if result != test.want {
|
|
t.Errorf("String #%d\n got: %s want: %s", i, result,
|
|
test.want)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestThresholdStateCache ensure the threshold state cache works as intended
|
|
// including adding entries, updating existing entries, and flushing.
|
|
func TestThresholdStateCache(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
numEntries int
|
|
state ThresholdState
|
|
}{
|
|
{name: "2 entries defined", numEntries: 2, state: ThresholdDefined},
|
|
{name: "7 entries started", numEntries: 7, state: ThresholdStarted},
|
|
{name: "10 entries active", numEntries: 10, state: ThresholdActive},
|
|
{name: "5 entries locked in", numEntries: 5, state: ThresholdLockedIn},
|
|
{name: "3 entries failed", numEntries: 3, state: ThresholdFailed},
|
|
}
|
|
|
|
nextTest:
|
|
for _, test := range tests {
|
|
cache := &newThresholdCaches(1)[0]
|
|
for i := 0; i < test.numEntries; i++ {
|
|
var hash chainhash.Hash
|
|
hash[0] = uint8(i + 1)
|
|
|
|
// Ensure the hash isn't available in the cache already.
|
|
_, ok := cache.Lookup(&hash)
|
|
if ok {
|
|
t.Errorf("Lookup (%s): has entry for hash %v",
|
|
test.name, hash)
|
|
continue nextTest
|
|
}
|
|
|
|
// Ensure hash that was added to the cache reports it's
|
|
// available and the state is the expected value.
|
|
cache.Update(&hash, test.state)
|
|
state, ok := cache.Lookup(&hash)
|
|
if !ok {
|
|
t.Errorf("Lookup (%s): missing entry for hash "+
|
|
"%v", test.name, hash)
|
|
continue nextTest
|
|
}
|
|
if state != test.state {
|
|
t.Errorf("Lookup (%s): state mismatch - got "+
|
|
"%v, want %v", test.name, state,
|
|
test.state)
|
|
continue nextTest
|
|
}
|
|
|
|
// Ensure the update is also added to the internal
|
|
// database updates map and its state matches.
|
|
state, ok = cache.dbUpdates[hash]
|
|
if !ok {
|
|
t.Errorf("dbUpdates (%s): missing entry for "+
|
|
"hash %v", test.name, hash)
|
|
continue nextTest
|
|
}
|
|
if state != test.state {
|
|
t.Errorf("dbUpdates (%s): state mismatch - "+
|
|
"got %v, want %v", test.name, state,
|
|
test.state)
|
|
continue nextTest
|
|
}
|
|
|
|
// Ensure flushing the cache removes all entries from
|
|
// the internal database updates map.
|
|
cache.MarkFlushed()
|
|
if len(cache.dbUpdates) != 0 {
|
|
t.Errorf("dbUpdates (%s): unflushed entries",
|
|
test.name)
|
|
continue nextTest
|
|
}
|
|
|
|
// Ensure hash is still available in the cache and the
|
|
// state is the expected value.
|
|
state, ok = cache.Lookup(&hash)
|
|
if !ok {
|
|
t.Errorf("Lookup (%s): missing entry after "+
|
|
"flush for hash %v", test.name, hash)
|
|
continue nextTest
|
|
}
|
|
if state != test.state {
|
|
t.Errorf("Lookup (%s): state mismatch after "+
|
|
"flush - got %v, want %v", test.name,
|
|
state, test.state)
|
|
continue nextTest
|
|
}
|
|
|
|
// Ensure adding an existing hash with the same state
|
|
// doesn't break the existing entry and it is NOT added
|
|
// to the database updates map.
|
|
cache.Update(&hash, test.state)
|
|
state, ok = cache.Lookup(&hash)
|
|
if !ok {
|
|
t.Errorf("Lookup (%s): missing entry after "+
|
|
"second add for hash %v", test.name,
|
|
hash)
|
|
continue nextTest
|
|
}
|
|
if state != test.state {
|
|
t.Errorf("Lookup (%s): state mismatch after "+
|
|
"second add - got %v, want %v",
|
|
test.name, state, test.state)
|
|
continue nextTest
|
|
}
|
|
if len(cache.dbUpdates) != 0 {
|
|
t.Errorf("dbUpdates (%s): unflushed entries "+
|
|
"after duplicate add", test.name)
|
|
continue nextTest
|
|
}
|
|
|
|
// Ensure adding an existing hash with a different state
|
|
// updates the existing entry.
|
|
newState := ThresholdFailed
|
|
if newState == test.state {
|
|
newState = ThresholdStarted
|
|
}
|
|
cache.Update(&hash, newState)
|
|
state, ok = cache.Lookup(&hash)
|
|
if !ok {
|
|
t.Errorf("Lookup (%s): missing entry after "+
|
|
"state change for hash %v", test.name,
|
|
hash)
|
|
continue nextTest
|
|
}
|
|
if state != newState {
|
|
t.Errorf("Lookup (%s): state mismatch after "+
|
|
"state change - got %v, want %v",
|
|
test.name, state, newState)
|
|
continue nextTest
|
|
}
|
|
|
|
// Ensure the update is also added to the internal
|
|
// database updates map and its state matches.
|
|
state, ok = cache.dbUpdates[hash]
|
|
if !ok {
|
|
t.Errorf("dbUpdates (%s): missing entry after "+
|
|
"state change for hash %v", test.name,
|
|
hash)
|
|
continue nextTest
|
|
}
|
|
if state != newState {
|
|
t.Errorf("dbUpdates (%s): state mismatch "+
|
|
"after state change - got %v, want %v",
|
|
test.name, state, newState)
|
|
continue nextTest
|
|
}
|
|
}
|
|
}
|
|
}
|