github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/p2p/testing/protocoltester.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 12:09:45</date>
    10  //</624342662048124928>
    11  
    12  
    13  /*
    14  p2p/测试包提供了一个单元测试方案来检查
    15  协议消息与一个透视节点和多个虚拟对等点交换
    16  透视测试节点运行一个节点。服务,虚拟对等运行一个模拟节点
    17  可用于发送和接收消息的
    18  **/
    19  
    20  
    21  package testing
    22  
    23  import (
    24  	"bytes"
    25  	"fmt"
    26  	"io"
    27  	"io/ioutil"
    28  	"strings"
    29  	"sync"
    30  	"testing"
    31  
    32  	"github.com/ethereum/go-ethereum/log"
    33  	"github.com/ethereum/go-ethereum/node"
    34  	"github.com/ethereum/go-ethereum/p2p"
    35  	"github.com/ethereum/go-ethereum/p2p/discover"
    36  	"github.com/ethereum/go-ethereum/p2p/simulations"
    37  	"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
    38  	"github.com/ethereum/go-ethereum/rlp"
    39  	"github.com/ethereum/go-ethereum/rpc"
    40  )
    41  
    42  //ProtocolTester是用于单元测试协议的测试环境
    43  //消息交换。它使用P2P/仿真框架
    44  type ProtocolTester struct {
    45  	*ProtocolSession
    46  	network *simulations.Network
    47  }
    48  
    49  //NewProtocolTester构造了一个新的ProtocolTester
    50  //它将透视节点ID、虚拟对等数和
    51  //P2P服务器在对等连接上调用的协议运行函数
    52  func NewProtocolTester(t *testing.T, id discover.NodeID, n int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester {
    53  	services := adapters.Services{
    54  		"test": func(ctx *adapters.ServiceContext) (node.Service, error) {
    55  			return &testNode{run}, nil
    56  		},
    57  		"mock": func(ctx *adapters.ServiceContext) (node.Service, error) {
    58  			return newMockNode(), nil
    59  		},
    60  	}
    61  	adapter := adapters.NewSimAdapter(services)
    62  	net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{})
    63  	if _, err := net.NewNodeWithConfig(&adapters.NodeConfig{
    64  		ID:              id,
    65  		EnableMsgEvents: true,
    66  		Services:        []string{"test"},
    67  	}); err != nil {
    68  		panic(err.Error())
    69  	}
    70  	if err := net.Start(id); err != nil {
    71  		panic(err.Error())
    72  	}
    73  
    74  	node := net.GetNode(id).Node.(*adapters.SimNode)
    75  	peers := make([]*adapters.NodeConfig, n)
    76  	peerIDs := make([]discover.NodeID, n)
    77  	for i := 0; i < n; i++ {
    78  		peers[i] = adapters.RandomNodeConfig()
    79  		peers[i].Services = []string{"mock"}
    80  		peerIDs[i] = peers[i].ID
    81  	}
    82  	events := make(chan *p2p.PeerEvent, 1000)
    83  	node.SubscribeEvents(events)
    84  	ps := &ProtocolSession{
    85  		Server:  node.Server(),
    86  		IDs:     peerIDs,
    87  		adapter: adapter,
    88  		events:  events,
    89  	}
    90  	self := &ProtocolTester{
    91  		ProtocolSession: ps,
    92  		network:         net,
    93  	}
    94  
    95  	self.Connect(id, peers...)
    96  
    97  	return self
    98  }
    99  
   100  //停止停止P2P服务器
   101  func (t *ProtocolTester) Stop() error {
   102  	t.Server.Stop()
   103  	return nil
   104  }
   105  
   106  //Connect打开远程对等节点并使用
   107  //P2P/模拟与内存网络适配器的网络连接
   108  func (t *ProtocolTester) Connect(selfID discover.NodeID, peers ...*adapters.NodeConfig) {
   109  	for _, peer := range peers {
   110  		log.Trace(fmt.Sprintf("start node %v", peer.ID))
   111  		if _, err := t.network.NewNodeWithConfig(peer); err != nil {
   112  			panic(fmt.Sprintf("error starting peer %v: %v", peer.ID, err))
   113  		}
   114  		if err := t.network.Start(peer.ID); err != nil {
   115  			panic(fmt.Sprintf("error starting peer %v: %v", peer.ID, err))
   116  		}
   117  		log.Trace(fmt.Sprintf("connect to %v", peer.ID))
   118  		if err := t.network.Connect(selfID, peer.ID); err != nil {
   119  			panic(fmt.Sprintf("error connecting to peer %v: %v", peer.ID, err))
   120  		}
   121  	}
   122  
   123  }
   124  
   125  //testnode包装协议运行函数并实现node.service
   126  //界面
   127  type testNode struct {
   128  	run func(*p2p.Peer, p2p.MsgReadWriter) error
   129  }
   130  
   131  func (t *testNode) Protocols() []p2p.Protocol {
   132  	return []p2p.Protocol{{
   133  		Length: 100,
   134  		Run:    t.run,
   135  	}}
   136  }
   137  
   138  func (t *testNode) APIs() []rpc.API {
   139  	return nil
   140  }
   141  
   142  func (t *testNode) Start(server *p2p.Server) error {
   143  	return nil
   144  }
   145  
   146  func (t *testNode) Stop() error {
   147  	return nil
   148  }
   149  
   150  //mocknode是一个没有实际运行协议的testnode
   151  //公开通道,以便测试可以手动触发并预期
   152  //信息
   153  type mockNode struct {
   154  	testNode
   155  
   156  	trigger  chan *Trigger
   157  	expect   chan []Expect
   158  	err      chan error
   159  	stop     chan struct{}
   160  	stopOnce sync.Once
   161  }
   162  
   163  func newMockNode() *mockNode {
   164  	mock := &mockNode{
   165  		trigger: make(chan *Trigger),
   166  		expect:  make(chan []Expect),
   167  		err:     make(chan error),
   168  		stop:    make(chan struct{}),
   169  	}
   170  	mock.testNode.run = mock.Run
   171  	return mock
   172  }
   173  
   174  //运行是一个协议运行函数,它只循环等待测试
   175  //指示它触发或期望来自对等端的消息
   176  func (m *mockNode) Run(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
   177  	for {
   178  		select {
   179  		case trig := <-m.trigger:
   180  			wmsg := Wrap(trig.Msg)
   181  			m.err <- p2p.Send(rw, trig.Code, wmsg)
   182  		case exps := <-m.expect:
   183  			m.err <- expectMsgs(rw, exps)
   184  		case <-m.stop:
   185  			return nil
   186  		}
   187  	}
   188  }
   189  
   190  func (m *mockNode) Trigger(trig *Trigger) error {
   191  	m.trigger <- trig
   192  	return <-m.err
   193  }
   194  
   195  func (m *mockNode) Expect(exp ...Expect) error {
   196  	m.expect <- exp
   197  	return <-m.err
   198  }
   199  
   200  func (m *mockNode) Stop() error {
   201  	m.stopOnce.Do(func() { close(m.stop) })
   202  	return nil
   203  }
   204  
   205  func expectMsgs(rw p2p.MsgReadWriter, exps []Expect) error {
   206  	matched := make([]bool, len(exps))
   207  	for {
   208  		msg, err := rw.ReadMsg()
   209  		if err != nil {
   210  			if err == io.EOF {
   211  				break
   212  			}
   213  			return err
   214  		}
   215  		actualContent, err := ioutil.ReadAll(msg.Payload)
   216  		if err != nil {
   217  			return err
   218  		}
   219  		var found bool
   220  		for i, exp := range exps {
   221  			if exp.Code == msg.Code && bytes.Equal(actualContent, mustEncodeMsg(Wrap(exp.Msg))) {
   222  				if matched[i] {
   223  					return fmt.Errorf("message #%d received two times", i)
   224  				}
   225  				matched[i] = true
   226  				found = true
   227  				break
   228  			}
   229  		}
   230  		if !found {
   231  			expected := make([]string, 0)
   232  			for i, exp := range exps {
   233  				if matched[i] {
   234  					continue
   235  				}
   236  				expected = append(expected, fmt.Sprintf("code %d payload %x", exp.Code, mustEncodeMsg(Wrap(exp.Msg))))
   237  			}
   238  			return fmt.Errorf("unexpected message code %d payload %x, expected %s", msg.Code, actualContent, strings.Join(expected, " or "))
   239  		}
   240  		done := true
   241  		for _, m := range matched {
   242  			if !m {
   243  				done = false
   244  				break
   245  			}
   246  		}
   247  		if done {
   248  			return nil
   249  		}
   250  	}
   251  	for i, m := range matched {
   252  		if !m {
   253  			return fmt.Errorf("expected message #%d not received", i)
   254  		}
   255  	}
   256  	return nil
   257  }
   258  
   259  //mustencodemsg使用rlp对消息进行编码。
   260  //一旦出错,它就会惊慌失措。
   261  func mustEncodeMsg(msg interface{}) []byte {
   262  	contentEnc, err := rlp.EncodeToBytes(msg)
   263  	if err != nil {
   264  		panic("content encode error: " + err.Error())
   265  	}
   266  	return contentEnc
   267  }
   268  
   269  type WrappedMsg struct {
   270  	Context []byte
   271  	Size    uint32
   272  	Payload []byte
   273  }
   274  
   275  func Wrap(msg interface{}) interface{} {
   276  	data, _ := rlp.EncodeToBytes(msg)
   277  	return &WrappedMsg{
   278  		Size:    uint32(len(data)),
   279  		Payload: data,
   280  	}
   281  }
   282