forked from onflow/flow-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
engine_test.go
131 lines (113 loc) · 4.05 KB
/
engine_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package compliance
import (
"context"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/onflow/flow-go/consensus/hotstuff/model"
"github.com/onflow/flow-go/model/flow"
"github.com/onflow/flow-go/model/messages"
"github.com/onflow/flow-go/module/irrecoverable"
modulemock "github.com/onflow/flow-go/module/mock"
"github.com/onflow/flow-go/utils/unittest"
)
func TestComplianceEngine(t *testing.T) {
suite.Run(t, new(EngineSuite))
}
// EngineSuite tests the compliance engine.
type EngineSuite struct {
CommonSuite
ctx irrecoverable.SignalerContext
cancel context.CancelFunc
errs <-chan error
engine *Engine
}
func (cs *EngineSuite) SetupTest() {
cs.CommonSuite.SetupTest()
e, err := NewEngine(unittest.Logger(), cs.me, cs.core)
require.NoError(cs.T(), err)
cs.engine = e
cs.ctx, cs.cancel, cs.errs = irrecoverable.WithSignallerAndCancel(context.Background())
cs.engine.Start(cs.ctx)
go unittest.FailOnIrrecoverableError(cs.T(), cs.ctx.Done(), cs.errs)
unittest.AssertClosesBefore(cs.T(), cs.engine.Ready(), time.Second)
}
// TearDownTest stops the engine and checks there are no errors thrown to the SignallerContext.
func (cs *EngineSuite) TearDownTest() {
cs.cancel()
unittest.RequireCloseBefore(cs.T(), cs.engine.Done(), time.Second, "engine failed to stop")
select {
case err := <-cs.errs:
assert.NoError(cs.T(), err)
default:
}
}
// TestSubmittingMultipleVotes tests that we can send multiple blocks, and they
// are queued and processed in expected way
func (cs *EngineSuite) TestSubmittingMultipleEntries() {
// create a vote
blockCount := 15
var wg sync.WaitGroup
wg.Add(1)
go func() {
for i := 0; i < blockCount; i++ {
block := unittest.BlockWithParentFixture(cs.head)
proposal := messages.NewBlockProposal(block)
hotstuffProposal := model.ProposalFromFlow(block.Header)
cs.hotstuff.On("SubmitProposal", hotstuffProposal).Return().Once()
cs.voteAggregator.On("AddBlock", hotstuffProposal).Once()
cs.validator.On("ValidateProposal", hotstuffProposal).Return(nil).Once()
// execute the block submission
cs.engine.OnBlockProposal(flow.Slashable[*messages.BlockProposal]{
OriginID: unittest.IdentifierFixture(),
Message: proposal,
})
}
wg.Done()
}()
wg.Add(1)
go func() {
// create a proposal that directly descends from the latest finalized header
block := unittest.BlockWithParentFixture(cs.head)
proposal := unittest.ProposalFromBlock(block)
hotstuffProposal := model.ProposalFromFlow(block.Header)
cs.hotstuff.On("SubmitProposal", hotstuffProposal).Return().Once()
cs.voteAggregator.On("AddBlock", hotstuffProposal).Once()
cs.validator.On("ValidateProposal", hotstuffProposal).Return(nil).Once()
cs.engine.OnBlockProposal(flow.Slashable[*messages.BlockProposal]{
OriginID: unittest.IdentifierFixture(),
Message: proposal,
})
wg.Done()
}()
// wait for all messages to be delivered to the engine message queue
wg.Wait()
// wait for the votes queue to drain
assert.Eventually(cs.T(), func() bool {
return cs.engine.pendingBlocks.Len() == 0
}, time.Second, time.Millisecond*10)
}
// TestOnFinalizedBlock tests if finalized block gets processed when send through `Engine`.
// Tests the whole processing pipeline.
func (cs *EngineSuite) TestOnFinalizedBlock() {
finalizedBlock := unittest.BlockHeaderFixture()
cs.head = finalizedBlock
cs.headerDB[finalizedBlock.ID()] = finalizedBlock
*cs.pending = *modulemock.NewPendingBlockBuffer(cs.T())
// wait for both expected calls before ending the test
wg := new(sync.WaitGroup)
wg.Add(2)
cs.pending.On("PruneByView", finalizedBlock.View).
Run(func(_ mock.Arguments) { wg.Done() }).
Return(nil).Once()
cs.pending.On("Size").
Run(func(_ mock.Arguments) { wg.Done() }).
Return(uint(0)).Once()
err := cs.engine.processOnFinalizedBlock(model.BlockFromFlow(finalizedBlock))
require.NoError(cs.T(), err)
unittest.AssertReturnsBefore(cs.T(), wg.Wait, time.Second, "an expected call to block buffer wasn't made")
}