Skip to content

Commit 844b517

Browse files
committed
[FAB-1218] - Add gossip communication mock
Added mock implementation of gossip communication component, to enable de-coupeling and increase testability of different gossip modules, such as for example: state transfer, leader election. Change-Id: Iefcdf8a7b2424880cedb8dc5bc7f8d45f3785a85 Signed-off-by: Artem Barger <[email protected]>
1 parent 6cd7be2 commit 844b517

File tree

2 files changed

+283
-0
lines changed

2 files changed

+283
-0
lines changed

gossip/comm/mock/mock_comm.go

+185
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package mock
18+
19+
import (
20+
"github.com/hyperledger/fabric/gossip/comm"
21+
"github.com/hyperledger/fabric/gossip/common"
22+
"github.com/hyperledger/fabric/gossip/proto"
23+
"github.com/op/go-logging"
24+
)
25+
26+
// Mock which aims to simulate socket
27+
type socketMock struct {
28+
// socket endpoint
29+
endpoint string
30+
31+
// To simulate simple tcp socket
32+
socket chan interface{}
33+
}
34+
35+
// Mock of primitive tcp packet structure
36+
type packetMock struct {
37+
// Sender channel message sent from
38+
src *socketMock
39+
40+
// Destination channel sent to
41+
dst *socketMock
42+
43+
msg interface{}
44+
}
45+
46+
type channelMock struct {
47+
accept common.MessageAcceptor
48+
49+
channel chan comm.ReceivedMessage
50+
}
51+
52+
type commMock struct {
53+
id string
54+
55+
members map[string]*socketMock
56+
57+
acceptors []*channelMock
58+
59+
deadChannel chan common.PKIidType
60+
61+
done chan struct{}
62+
}
63+
64+
var logger *logging.Logger // package-level logger
65+
66+
func init() {
67+
logger = logging.MustGetLogger("mockComm")
68+
logging.SetLevel(logging.DEBUG, logger.Module)
69+
}
70+
71+
// NewCommMock creates mocked communication object
72+
func NewCommMock(id string, members map[string]*socketMock) comm.Comm {
73+
res := &commMock{
74+
id: id,
75+
76+
members: members,
77+
78+
acceptors: make([]*channelMock, 0),
79+
80+
done: make(chan struct{}),
81+
82+
deadChannel: make(chan common.PKIidType),
83+
}
84+
// Start communication service
85+
go res.start()
86+
87+
return res
88+
}
89+
90+
// Respond sends a GossipMessage to the origin from which this ReceivedMessage was sent from
91+
func (packet *packetMock) Respond(msg *proto.GossipMessage) {
92+
packet.src.socket <- &packetMock{
93+
src: packet.dst,
94+
dst: packet.src,
95+
msg: msg,
96+
}
97+
}
98+
99+
// GetGossipMessage returns the underlying GossipMessage
100+
func (packet *packetMock) GetGossipMessage() *proto.GossipMessage {
101+
return packet.msg.(*proto.GossipMessage)
102+
}
103+
104+
func (mock *commMock) start() {
105+
logger.Debug("Starting communication mock module...")
106+
for {
107+
select {
108+
case <-mock.done:
109+
{
110+
// Got final signal, exiting...
111+
logger.Debug("Exiting...")
112+
return
113+
}
114+
case msg := <-mock.members[mock.id].socket:
115+
{
116+
logger.Debug("Got new message", msg)
117+
packet := msg.(*packetMock)
118+
for _, channel := range mock.acceptors {
119+
// if message acceptor agrees to get
120+
// new message forward it to the received
121+
// messages channel
122+
if channel.accept(packet) {
123+
channel.channel <- packet
124+
}
125+
}
126+
}
127+
}
128+
}
129+
}
130+
131+
// GetPKIid returns this instance's PKI id
132+
func (mock *commMock) GetPKIid() common.PKIidType {
133+
return common.PKIidType(mock.id)
134+
}
135+
136+
// Send sends a message to remote peers
137+
func (mock *commMock) Send(msg *proto.GossipMessage, peers ...*comm.RemotePeer) {
138+
for _, peer := range peers {
139+
logger.Debug("Sending message to peer ", peer.Endpoint, "from ", mock.id)
140+
mock.members[peer.Endpoint].socket <- &packetMock{
141+
src: mock.members[mock.id],
142+
dst: mock.members[peer.Endpoint],
143+
msg: msg,
144+
}
145+
}
146+
}
147+
148+
// Probe probes a remote node and returns nil if its responsive
149+
func (mock *commMock) Probe(peer *comm.RemotePeer) error {
150+
return nil
151+
}
152+
153+
// Accept returns a dedicated read-only channel for messages sent by other nodes that match a certain predicate.
154+
// Each message from the channel can be used to send a reply back to the sender
155+
func (mock *commMock) Accept(accept common.MessageAcceptor) <-chan comm.ReceivedMessage {
156+
ch := make(chan comm.ReceivedMessage)
157+
mock.acceptors = append(mock.acceptors, &channelMock{accept, ch})
158+
return ch
159+
}
160+
161+
// PresumedDead returns a read-only channel for node endpoints that are suspected to be offline
162+
func (mock *commMock) PresumedDead() <-chan common.PKIidType {
163+
return mock.deadChannel
164+
}
165+
166+
// CloseConn closes a connection to a certain endpoint
167+
func (mock *commMock) CloseConn(peer *comm.RemotePeer) {
168+
// NOOP
169+
}
170+
171+
// Stop stops the module
172+
func (mock *commMock) Stop() {
173+
logger.Debug("Stopping communication module, closing all accepting channels.")
174+
for _, accept := range mock.acceptors {
175+
close(accept.channel)
176+
}
177+
logger.Debug("[XXX]: Sending done signal to close the module.")
178+
mock.done <- struct{}{}
179+
}
180+
181+
// BlackListPKIid prohibits the module communicating with the given PKIid
182+
func (mock *commMock) BlackListPKIid(PKIid common.PKIidType) {
183+
// NOOP
184+
}
185+

gossip/comm/mock/mock_comm_test.go

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package mock
18+
19+
import (
20+
"testing"
21+
22+
"github.com/hyperledger/fabric/gossip/comm"
23+
"github.com/hyperledger/fabric/gossip/common"
24+
"github.com/hyperledger/fabric/gossip/proto"
25+
"github.com/stretchr/testify/assert"
26+
)
27+
28+
func TestMockComm(t *testing.T) {
29+
first := &socketMock{"first", make(chan interface{})}
30+
second := &socketMock{"second", make(chan interface{})}
31+
members := make(map[string]*socketMock)
32+
33+
members[first.endpoint] = first
34+
members[second.endpoint] = second
35+
36+
comm1 := NewCommMock(first.endpoint, members)
37+
defer comm1.Stop()
38+
39+
msgCh := comm1.Accept(func(message interface{}) bool {
40+
return message.(comm.ReceivedMessage).GetGossipMessage().GetStateRequest() != nil ||
41+
message.(comm.ReceivedMessage).GetGossipMessage().GetStateResponse() != nil
42+
})
43+
44+
comm2 := NewCommMock(second.endpoint, members)
45+
defer comm2.Stop()
46+
47+
comm2.Send(&proto.GossipMessage{
48+
Content: &proto.GossipMessage_StateRequest{&proto.RemoteStateRequest{
49+
SeqNums: []uint64{1, 2, 3},
50+
}},
51+
}, &comm.RemotePeer{"first", common.PKIidType("first")})
52+
53+
msg := <-msgCh
54+
55+
assert.NotNil(t, msg.GetGossipMessage().GetStateRequest())
56+
}
57+
58+
func TestMockComm_PingPong(t *testing.T) {
59+
members := make(map[string]*socketMock)
60+
61+
members["peerA"] = &socketMock{"peerA", make(chan interface{})}
62+
members["peerB"] = &socketMock{"peerB", make(chan interface{})}
63+
64+
peerA := NewCommMock("peerA", members)
65+
peerB := NewCommMock("peerB", members)
66+
67+
all := func(interface{}) bool {
68+
return true
69+
}
70+
71+
rcvChA := peerA.Accept(all)
72+
rcvChB := peerB.Accept(all)
73+
74+
peerA.Send(&proto.GossipMessage{
75+
Content: &proto.GossipMessage_DataMsg{
76+
&proto.DataMessage{
77+
&proto.Payload{1, "", []byte("Ping")},
78+
}},
79+
}, &comm.RemotePeer{"peerB", common.PKIidType("peerB")})
80+
81+
msg := <-rcvChB
82+
dataMsg := msg.GetGossipMessage().GetDataMsg()
83+
data := string(dataMsg.Payload.Data)
84+
assert.Equal(t, "Ping", data)
85+
86+
msg.Respond(&proto.GossipMessage{
87+
Content: &proto.GossipMessage_DataMsg{
88+
&proto.DataMessage{
89+
&proto.Payload{1, "", []byte("Pong")},
90+
}},
91+
})
92+
93+
msg = <-rcvChA
94+
dataMsg = msg.GetGossipMessage().GetDataMsg()
95+
data = string(dataMsg.Payload.Data)
96+
assert.Equal(t, "Pong", data)
97+
98+
}

0 commit comments

Comments
 (0)