mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 15:16:43 +00:00
p2p/testing, p2p/protocols: refactored test session setup and driver
This commit is contained in:
parent
11aeaec420
commit
d4a08fdf7e
4 changed files with 125 additions and 174 deletions
|
|
@ -126,8 +126,8 @@ func newProtocol(pp *p2ptest.TestPeerPool, wg *sync.WaitGroup) func(adapters.Nod
|
|||
}
|
||||
}
|
||||
|
||||
func protocolTester(t *testing.T, pp *p2ptest.TestPeerPool, wg *sync.WaitGroup) *p2ptest.ExchangeSession {
|
||||
id := p2ptest.RandomNodeId()
|
||||
func protocolTester(t *testing.T, pp *p2ptest.TestPeerPool, wg *sync.WaitGroup) *p2ptest.ProtocolTester {
|
||||
id := adapters.RandomNodeId()
|
||||
return p2ptest.NewProtocolTester(t, id, 2, newProtocol(pp, wg))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,31 +2,17 @@ package testing
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/adapters"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
)
|
||||
|
||||
// ExchangeTestSession assumes a network with a protocol running on multiple peer connection
|
||||
// and is used to test scanarios of message exchange among a select array of nodes
|
||||
// the scenarios are sets of exchanges, each with a trigger and an expectation
|
||||
// This rigid regime is suitable for
|
||||
// * unit testing protocol message exchanges (nodes are peers of a local node)
|
||||
// * testing routed messaging between remote non-connected nodes within a group
|
||||
type ExchangeTestSession struct {
|
||||
lock sync.Mutex
|
||||
type ProtocolSession struct {
|
||||
TestNodeAdapter
|
||||
Ids []*adapters.NodeId
|
||||
TestNetAdapter
|
||||
TestMessenger
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
// implemented by simulations/
|
||||
type TestNetAdapter interface {
|
||||
GetPeer(id *adapters.NodeId) *adapters.Peer
|
||||
}
|
||||
|
||||
type TestMessenger interface {
|
||||
|
|
@ -34,7 +20,16 @@ type TestMessenger interface {
|
|||
TriggerMsg(uint64, interface{}) error
|
||||
}
|
||||
|
||||
type TestNodeAdapter interface {
|
||||
GetPeer(id *adapters.NodeId) *adapters.Peer
|
||||
Connect([]byte) error
|
||||
}
|
||||
|
||||
// exchanges are the basic units of protocol tests
|
||||
// the triggers and expects in the arrays are run immediately and asynchronously
|
||||
// thus one cannot have multiple expects for the SAME peer with the DIFFERENT messagetypes
|
||||
// because it's unpredictable which expect will receive which message
|
||||
// (with expect #1 and #2, messages might be sent #2 and #1, and both expects will complain about wrong message code)
|
||||
// an exchange is defined on a session
|
||||
type Exchange struct {
|
||||
Label string
|
||||
|
|
@ -62,23 +57,17 @@ type Disconnect struct {
|
|||
Error error // disconnect reason
|
||||
}
|
||||
|
||||
// NewExchangeTestSession takes a network session and Messenger
|
||||
// and returns an exchange session test driver that can
|
||||
// be used to unit test protocol communications
|
||||
// it allows for resource-driven scenario testing
|
||||
// disconnect reason errors are written in session.Errs
|
||||
// (correcponding to session.Peers)
|
||||
//func NewExchangeTestSession(t *testing.T, n TestNetAdapter, m TestMessenger, ids []*adapters.NodeId) *ExchangeTestSession {
|
||||
func NewExchangeTestSession(t *testing.T, n TestNetAdapter, ids []*adapters.NodeId) *ExchangeTestSession {
|
||||
return &ExchangeTestSession{
|
||||
Ids: ids,
|
||||
TestNetAdapter: n,
|
||||
t: t,
|
||||
|
||||
func NewProtocolSession(na adapters.NodeAdapter, ids []*adapters.NodeId) *ProtocolSession {
|
||||
ps := &ProtocolSession {
|
||||
TestNodeAdapter: na.(TestNodeAdapter),
|
||||
Ids: ids,
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
||||
// trigger sends messages from peers
|
||||
func (self *ExchangeTestSession) trigger(trig Trigger) error {
|
||||
func (self *ProtocolSession) trigger(trig Trigger) error {
|
||||
peer := self.GetPeer(trig.Peer)
|
||||
if peer == nil {
|
||||
panic(fmt.Sprintf("trigger: peer %v does not exist (1- %v)", trig.Peer, len(self.Ids)))
|
||||
|
|
@ -90,9 +79,9 @@ func (self *ExchangeTestSession) trigger(trig Trigger) error {
|
|||
errc := make(chan error)
|
||||
|
||||
go func() {
|
||||
glog.V(6).Infof("trigger %v (%v)....", trig.Msg, trig.Code)
|
||||
glog.V(logger.Detail).Infof("trigger %v (%v)....", trig.Msg, trig.Code)
|
||||
errc <- m.(TestMessenger).TriggerMsg(trig.Code, trig.Msg)
|
||||
glog.V(6).Infof("triggered %v (%v)", trig.Msg, trig.Code)
|
||||
glog.V(logger.Detail).Infof("triggered %v (%v)", trig.Msg, trig.Code)
|
||||
}()
|
||||
|
||||
t := trig.Timeout
|
||||
|
|
@ -108,12 +97,8 @@ func (self *ExchangeTestSession) trigger(trig Trigger) error {
|
|||
}
|
||||
}
|
||||
|
||||
func Key(id []byte) string {
|
||||
return string(id)
|
||||
}
|
||||
|
||||
// expect checks an expectation
|
||||
func (self *ExchangeTestSession) expect(exp Expect) error {
|
||||
func (self *ProtocolSession) expect(exp Expect) error {
|
||||
if exp.Msg == nil {
|
||||
panic("no message to expect")
|
||||
}
|
||||
|
|
@ -128,7 +113,7 @@ func (self *ExchangeTestSession) expect(exp Expect) error {
|
|||
|
||||
errc := make(chan error)
|
||||
go func() {
|
||||
glog.V(6).Infof("waiting for msg, %v", exp.Msg)
|
||||
glog.V(logger.Detail).Infof("waiting for msg, %v", exp.Msg)
|
||||
errc <- m.(TestMessenger).ExpectMsg(exp.Code, exp.Msg)
|
||||
}()
|
||||
|
||||
|
|
@ -139,17 +124,18 @@ func (self *ExchangeTestSession) expect(exp Expect) error {
|
|||
alarm := time.NewTimer(t)
|
||||
select {
|
||||
case err := <-errc:
|
||||
glog.V(6).Infof("expected msg arrives with error %v", err)
|
||||
glog.V(logger.Detail).Infof("expected msg arrives with error %v", err)
|
||||
return err
|
||||
case <-alarm.C:
|
||||
glog.V(6).Infof("caught timeout")
|
||||
glog.V(logger.Detail).Infof("caught timeout")
|
||||
return fmt.Errorf("timout expecting %v sent to peer %v", exp.Msg, exp.Peer)
|
||||
}
|
||||
// fatal upon encountering first exchange error
|
||||
}
|
||||
|
||||
|
||||
// TestExchange tests a series of exchanges againsts the session
|
||||
func (self *ExchangeTestSession) TestExchanges(exchanges ...Exchange) {
|
||||
func (self *ProtocolSession) TestExchanges(exchanges ...Exchange) error {
|
||||
// launch all triggers of this exchanges
|
||||
|
||||
for i, e := range exchanges {
|
||||
|
|
@ -177,7 +163,7 @@ func (self *ExchangeTestSession) TestExchanges(exchanges ...Exchange) {
|
|||
defer wg.Done()
|
||||
err := self.expect(exp)
|
||||
if err != nil {
|
||||
glog.V(6).Infof("expect msg fails %v", err)
|
||||
glog.V(logger.Detail).Infof("expect msg fails %v", err)
|
||||
errc <- err
|
||||
}
|
||||
}(ex)
|
||||
|
|
@ -195,17 +181,18 @@ func (self *ExchangeTestSession) TestExchanges(exchanges ...Exchange) {
|
|||
|
||||
case err := <-errc:
|
||||
if err != nil {
|
||||
self.t.Fatalf("exchange failed with: %v", err)
|
||||
return fmt.Errorf("exchange failed with: %v", err)
|
||||
} else {
|
||||
glog.V(6).Infof("exchange %v: '%v' run successfully", i, e.Label)
|
||||
glog.V(logger.Detail).Infof("exchange %v: '%v' run successfully", i, e.Label)
|
||||
}
|
||||
case <-alarm.C:
|
||||
self.t.Fatalf("exchange %v: '%v' timed out", i, e.Label)
|
||||
return fmt.Errorf("exchange %v: '%v' timed out", i, e.Label)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ExchangeTestSession) TestDisconnected(disconnects ...*Disconnect) {
|
||||
func (self *ProtocolSession) TestDisconnected(disconnects ...*Disconnect) error {
|
||||
for _, disconnect := range disconnects {
|
||||
id := disconnect.Peer
|
||||
err := disconnect.Error
|
||||
|
|
@ -215,10 +202,11 @@ func (self *ExchangeTestSession) TestDisconnected(disconnects ...*Disconnect) {
|
|||
select {
|
||||
case derr := <-peer.Errc:
|
||||
if !((err == nil && derr == nil) || err != nil && derr != nil && err.Error() == derr.Error()) {
|
||||
self.t.Fatalf("unexpected error on peer %v. expected '%v', got '%v'", id, err, derr)
|
||||
return fmt.Errorf("unexpected error on peer %v. expected '%v', got '%v'", id, err, derr)
|
||||
}
|
||||
case <-alarm.C:
|
||||
self.t.Fatalf("timed out waiting for peer %v to disconnect", id)
|
||||
return fmt.Errorf("timed out waiting for peer %v to disconnect", id)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
84
p2p/testing/protocoltester.go
Normal file
84
p2p/testing/protocoltester.go
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
package testing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/adapters"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
)
|
||||
|
||||
type ProtocolTester struct {
|
||||
*ProtocolSession
|
||||
network *simulations.Network
|
||||
na adapters.NodeAdapter
|
||||
}
|
||||
|
||||
func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(id adapters.NodeAdapter) adapters.ProtoCall) *ProtocolTester {
|
||||
|
||||
simPipe := adapters.NewSimPipe
|
||||
net := simulations.NewNetwork(&simulations.NetworkConfig{})
|
||||
naf := func(conf *simulations.NodeConfig) adapters.NodeAdapter {
|
||||
na := adapters.NewSimNode(conf.Id, net, simPipe)
|
||||
if conf.Id.NodeID == id.NodeID {
|
||||
glog.V(logger.Detail).Infof("adapter run function set to protocol for node %v (=%v)", conf.Id, id)
|
||||
na.Run = run(na)
|
||||
}
|
||||
return na
|
||||
}
|
||||
net.SetNaf(naf)
|
||||
err := net.NewNode(&simulations.NodeConfig{Id: id})
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
//na := net.GetNode(id).Adapter()
|
||||
na := net.GetNodeAdapter(id)
|
||||
|
||||
ids := adapters.RandomNodeIds(n)
|
||||
|
||||
ps := NewProtocolSession(na, ids)
|
||||
self := &ProtocolTester{
|
||||
ProtocolSession: ps,
|
||||
network: net,
|
||||
na: na,
|
||||
}
|
||||
|
||||
self.Connect(ids...)
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *ProtocolTester) Start(id *adapters.NodeId) error {
|
||||
err := self.network.NewNode(&simulations.NodeConfig{Id: id})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
node := self.network.GetNode(id)
|
||||
if node == nil {
|
||||
glog.V(logger.Detail).Infof("node for peer %v not found", id)
|
||||
return nil
|
||||
}
|
||||
if node.Adapter() == nil {
|
||||
glog.V(logger.Detail).Infof("node adapter for peer %v not found", id)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ProtocolTester) Connect(ids ...*adapters.NodeId) {
|
||||
for _, id := range ids {
|
||||
glog.V(logger.Detail).Infof("start node %v", id)
|
||||
err := self.Start(id)
|
||||
if err != nil {
|
||||
glog.V(logger.Detail).Infof("error starting peer %v: %v", id, err)
|
||||
}
|
||||
glog.V(logger.Detail).Infof("connect to %v", id)
|
||||
err = self.na.Connect(id.Bytes())
|
||||
if err != nil {
|
||||
glog.V(logger.Detail).Infof("error connecting to peer %v: %v", id, err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
package testing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/p2p/adapters"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations"
|
||||
)
|
||||
|
||||
type PeerAdapter interface {
|
||||
adapters.NodeAdapter
|
||||
TestMessenger
|
||||
TestNetAdapter
|
||||
}
|
||||
|
||||
type ExchangeSession struct {
|
||||
network *simulations.Network
|
||||
na adapters.NodeAdapter
|
||||
*ExchangeTestSession
|
||||
}
|
||||
|
||||
// NewProtocolTester returns an exchange test session
|
||||
// this is a resource driver for protocol message exchange
|
||||
// scenarios expressed as expects and triggers
|
||||
// see p2p/protocols/exhange_test.go for an example
|
||||
// this is used primarily to unit test protocols or protocol modules
|
||||
// correct message exchange, forwarding, and broadcast
|
||||
// higher level or network behaviour should be tested with network simulators
|
||||
func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(id adapters.NodeAdapter) adapters.ProtoCall) *ExchangeSession {
|
||||
simPipe := adapters.NewSimPipe
|
||||
network := simulations.NewNetwork(&simulations.NetworkConfig{})
|
||||
naf := func(conf *simulations.NodeConfig) adapters.NodeAdapter {
|
||||
na := adapters.NewSimNode(conf.Id, network, simPipe)
|
||||
if conf.Id.NodeID == id.NodeID {
|
||||
glog.V(6).Infof("adapter run function set to protocol for node %v (=%v)", conf.Id, id)
|
||||
na.Run = run(na)
|
||||
}
|
||||
return na
|
||||
}
|
||||
network.SetNaf(naf)
|
||||
// setup a simulated network of n nodes
|
||||
// Startup pivot node
|
||||
err := network.NewNode(&simulations.NodeConfig{Id: id})
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
glog.V(6).Infof("network created")
|
||||
na := network.GetNode(id).Adapter()
|
||||
//s := NewExchangeTestSession(t, na.(TestNetAdapter), na.Messenger().(TestMessenger), nil)
|
||||
s := NewExchangeTestSession(t, na.(TestNetAdapter), nil)
|
||||
self := &ExchangeSession{
|
||||
network: network,
|
||||
na: na,
|
||||
ExchangeTestSession: s,
|
||||
}
|
||||
ids := RandomNodeIds(n)
|
||||
self.Connect(ids...)
|
||||
// Start up connections to virual nodes serving as endpoints for sending/receiving messages for peers
|
||||
return self
|
||||
}
|
||||
|
||||
//
|
||||
func (self *ExchangeSession) Start(id *adapters.NodeId) error {
|
||||
err := self.network.NewNode(&simulations.NodeConfig{Id: id})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
node := self.network.GetNode(id)
|
||||
if node == nil {
|
||||
glog.V(6).Infof("node for peer %v not found", id)
|
||||
return nil
|
||||
}
|
||||
if node.Adapter() == nil {
|
||||
glog.V(6).Infof("node adapter for peer %v not found", id)
|
||||
return nil
|
||||
}
|
||||
self.Ids = append(self.Ids, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ExchangeSession) Connect(ids ...*adapters.NodeId) {
|
||||
for _, id := range ids {
|
||||
glog.V(6).Infof("start node %v", id)
|
||||
err := self.Start(id)
|
||||
if err != nil {
|
||||
glog.V(6).Infof("error starting peer %v: %v", id, err)
|
||||
}
|
||||
glog.V(6).Infof("connect to %v", id)
|
||||
err = self.na.Connect(id.Bytes())
|
||||
if err != nil {
|
||||
glog.V(6).Infof("error connecting to peer %v: %v", id, err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// func (self *ExchangeSession) Id(i int) *adapters.NodeId {
|
||||
// return self.network.Nodes[i].Id
|
||||
// }
|
||||
|
||||
func RandomNodeId() *adapters.NodeId {
|
||||
key, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
panic("unable to generate key")
|
||||
}
|
||||
var id discover.NodeID
|
||||
pubkey := crypto.FromECDSAPub(&key.PublicKey)
|
||||
copy(id[:], pubkey[1:])
|
||||
return &adapters.NodeId{id}
|
||||
}
|
||||
|
||||
func RandomNodeIds(n int) []*adapters.NodeId {
|
||||
var ids []*adapters.NodeId
|
||||
for i := 0; i < n; i++ {
|
||||
ids = append(ids, RandomNodeId())
|
||||
}
|
||||
return ids
|
||||
}
|
||||
Loading…
Reference in a new issue