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