1// Copyright (c) 2016 The btcsuite developers
2// Use of this source code is governed by an ISC
3// license that can be found in the LICENSE file.
4
5package blockchain
6
7import (
8	"testing"
9
10	"github.com/btcsuite/btcd/chaincfg/chainhash"
11)
12
13// TestThresholdStateStringer tests the stringized output for the
14// ThresholdState type.
15func TestThresholdStateStringer(t *testing.T) {
16	t.Parallel()
17
18	tests := []struct {
19		in   ThresholdState
20		want string
21	}{
22		{ThresholdDefined, "ThresholdDefined"},
23		{ThresholdStarted, "ThresholdStarted"},
24		{ThresholdLockedIn, "ThresholdLockedIn"},
25		{ThresholdActive, "ThresholdActive"},
26		{ThresholdFailed, "ThresholdFailed"},
27		{0xff, "Unknown ThresholdState (255)"},
28	}
29
30	// Detect additional threshold states that don't have the stringer added.
31	if len(tests)-1 != int(numThresholdsStates) {
32		t.Errorf("It appears a threshold statewas added without " +
33			"adding an associated stringer test")
34	}
35
36	t.Logf("Running %d tests", len(tests))
37	for i, test := range tests {
38		result := test.in.String()
39		if result != test.want {
40			t.Errorf("String #%d\n got: %s want: %s", i, result,
41				test.want)
42			continue
43		}
44	}
45}
46
47// TestThresholdStateCache ensure the threshold state cache works as intended
48// including adding entries, updating existing entries, and flushing.
49func TestThresholdStateCache(t *testing.T) {
50	t.Parallel()
51
52	tests := []struct {
53		name       string
54		numEntries int
55		state      ThresholdState
56	}{
57		{name: "2 entries defined", numEntries: 2, state: ThresholdDefined},
58		{name: "7 entries started", numEntries: 7, state: ThresholdStarted},
59		{name: "10 entries active", numEntries: 10, state: ThresholdActive},
60		{name: "5 entries locked in", numEntries: 5, state: ThresholdLockedIn},
61		{name: "3 entries failed", numEntries: 3, state: ThresholdFailed},
62	}
63
64nextTest:
65	for _, test := range tests {
66		cache := &newThresholdCaches(1)[0]
67		for i := 0; i < test.numEntries; i++ {
68			var hash chainhash.Hash
69			hash[0] = uint8(i + 1)
70
71			// Ensure the hash isn't available in the cache already.
72			_, ok := cache.Lookup(&hash)
73			if ok {
74				t.Errorf("Lookup (%s): has entry for hash %v",
75					test.name, hash)
76				continue nextTest
77			}
78
79			// Ensure hash that was added to the cache reports it's
80			// available and the state is the expected value.
81			cache.Update(&hash, test.state)
82			state, ok := cache.Lookup(&hash)
83			if !ok {
84				t.Errorf("Lookup (%s): missing entry for hash "+
85					"%v", test.name, hash)
86				continue nextTest
87			}
88			if state != test.state {
89				t.Errorf("Lookup (%s): state mismatch - got "+
90					"%v, want %v", test.name, state,
91					test.state)
92				continue nextTest
93			}
94
95			// Ensure adding an existing hash with the same state
96			// doesn't break the existing entry.
97			cache.Update(&hash, test.state)
98			state, ok = cache.Lookup(&hash)
99			if !ok {
100				t.Errorf("Lookup (%s): missing entry after "+
101					"second add for hash %v", test.name,
102					hash)
103				continue nextTest
104			}
105			if state != test.state {
106				t.Errorf("Lookup (%s): state mismatch after "+
107					"second add - got %v, want %v",
108					test.name, state, test.state)
109				continue nextTest
110			}
111
112			// Ensure adding an existing hash with a different state
113			// updates the existing entry.
114			newState := ThresholdFailed
115			if newState == test.state {
116				newState = ThresholdStarted
117			}
118			cache.Update(&hash, newState)
119			state, ok = cache.Lookup(&hash)
120			if !ok {
121				t.Errorf("Lookup (%s): missing entry after "+
122					"state change for hash %v", test.name,
123					hash)
124				continue nextTest
125			}
126			if state != newState {
127				t.Errorf("Lookup (%s): state mismatch after "+
128					"state change - got %v, want %v",
129					test.name, state, newState)
130				continue nextTest
131			}
132		}
133	}
134}
135