github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/p2p/peer_test.go (about) 1 // This file is part of the go-sberex library. The go-sberex library is 2 // free software: you can redistribute it and/or modify it under the terms 3 // of the GNU Lesser General Public License as published by the Free 4 // Software Foundation, either version 3 of the License, or (at your option) 5 // any later version. 6 // 7 // The go-sberex library is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 // General Public License <http://www.gnu.org/licenses/> for more details. 11 12 package p2p 13 14 import ( 15 "errors" 16 "fmt" 17 "math/rand" 18 "net" 19 "reflect" 20 "testing" 21 "time" 22 ) 23 24 var discard = Protocol{ 25 Name: "discard", 26 Length: 1, 27 Run: func(p *Peer, rw MsgReadWriter) error { 28 for { 29 msg, err := rw.ReadMsg() 30 if err != nil { 31 return err 32 } 33 fmt.Printf("discarding %d\n", msg.Code) 34 if err = msg.Discard(); err != nil { 35 return err 36 } 37 } 38 }, 39 } 40 41 func testPeer(protos []Protocol) (func(), *conn, *Peer, <-chan error) { 42 fd1, fd2 := net.Pipe() 43 c1 := &conn{fd: fd1, transport: newTestTransport(randomID(), fd1)} 44 c2 := &conn{fd: fd2, transport: newTestTransport(randomID(), fd2)} 45 for _, p := range protos { 46 c1.caps = append(c1.caps, p.cap()) 47 c2.caps = append(c2.caps, p.cap()) 48 } 49 50 peer := newPeer(c1, protos) 51 errc := make(chan error, 1) 52 go func() { 53 _, err := peer.run() 54 errc <- err 55 }() 56 57 closer := func() { c2.close(errors.New("close func called")) } 58 return closer, c2, peer, errc 59 } 60 61 func TestPeerProtoReadMsg(t *testing.T) { 62 proto := Protocol{ 63 Name: "a", 64 Length: 5, 65 Run: func(peer *Peer, rw MsgReadWriter) error { 66 if err := ExpectMsg(rw, 2, []uint{1}); err != nil { 67 t.Error(err) 68 } 69 if err := ExpectMsg(rw, 3, []uint{2}); err != nil { 70 t.Error(err) 71 } 72 if err := ExpectMsg(rw, 4, []uint{3}); err != nil { 73 t.Error(err) 74 } 75 return nil 76 }, 77 } 78 79 closer, rw, _, errc := testPeer([]Protocol{proto}) 80 defer closer() 81 82 Send(rw, baseProtocolLength+2, []uint{1}) 83 Send(rw, baseProtocolLength+3, []uint{2}) 84 Send(rw, baseProtocolLength+4, []uint{3}) 85 86 select { 87 case err := <-errc: 88 if err != errProtocolReturned { 89 t.Errorf("peer returned error: %v", err) 90 } 91 case <-time.After(2 * time.Second): 92 t.Errorf("receive timeout") 93 } 94 } 95 96 func TestPeerProtoEncodeMsg(t *testing.T) { 97 proto := Protocol{ 98 Name: "a", 99 Length: 2, 100 Run: func(peer *Peer, rw MsgReadWriter) error { 101 if err := SendItems(rw, 2); err == nil { 102 t.Error("expected error for out-of-range msg code, got nil") 103 } 104 if err := SendItems(rw, 1, "foo", "bar"); err != nil { 105 t.Errorf("write error: %v", err) 106 } 107 return nil 108 }, 109 } 110 closer, rw, _, _ := testPeer([]Protocol{proto}) 111 defer closer() 112 113 if err := ExpectMsg(rw, 17, []string{"foo", "bar"}); err != nil { 114 t.Error(err) 115 } 116 } 117 118 func TestPeerPing(t *testing.T) { 119 closer, rw, _, _ := testPeer(nil) 120 defer closer() 121 if err := SendItems(rw, pingMsg); err != nil { 122 t.Fatal(err) 123 } 124 if err := ExpectMsg(rw, pongMsg, nil); err != nil { 125 t.Error(err) 126 } 127 } 128 129 func TestPeerDisconnect(t *testing.T) { 130 closer, rw, _, disc := testPeer(nil) 131 defer closer() 132 if err := SendItems(rw, discMsg, DiscQuitting); err != nil { 133 t.Fatal(err) 134 } 135 select { 136 case reason := <-disc: 137 if reason != DiscQuitting { 138 t.Errorf("run returned wrong reason: got %v, want %v", reason, DiscQuitting) 139 } 140 case <-time.After(500 * time.Millisecond): 141 t.Error("peer did not return") 142 } 143 } 144 145 // This test is supposed to verify that Peer can reliably handle 146 // multiple causes of disconnection occurring at the same time. 147 func TestPeerDisconnectRace(t *testing.T) { 148 maybe := func() bool { return rand.Intn(1) == 1 } 149 150 for i := 0; i < 1000; i++ { 151 protoclose := make(chan error) 152 protodisc := make(chan DiscReason) 153 closer, rw, p, disc := testPeer([]Protocol{ 154 { 155 Name: "closereq", 156 Run: func(p *Peer, rw MsgReadWriter) error { return <-protoclose }, 157 Length: 1, 158 }, 159 { 160 Name: "disconnect", 161 Run: func(p *Peer, rw MsgReadWriter) error { p.Disconnect(<-protodisc); return nil }, 162 Length: 1, 163 }, 164 }) 165 166 // Simulate incoming messages. 167 go SendItems(rw, baseProtocolLength+1) 168 go SendItems(rw, baseProtocolLength+2) 169 // Close the network connection. 170 go closer() 171 // Make protocol "closereq" return. 172 protoclose <- errors.New("protocol closed") 173 // Make protocol "disconnect" call peer.Disconnect 174 protodisc <- DiscAlreadyConnected 175 // In some cases, simulate something else calling peer.Disconnect. 176 if maybe() { 177 go p.Disconnect(DiscInvalidIdentity) 178 } 179 // In some cases, simulate remote requesting a disconnect. 180 if maybe() { 181 go SendItems(rw, discMsg, DiscQuitting) 182 } 183 184 select { 185 case <-disc: 186 case <-time.After(2 * time.Second): 187 // Peer.run should return quickly. If it doesn't the Peer 188 // goroutines are probably deadlocked. Call panic in order to 189 // show the stacks. 190 panic("Peer.run took to long to return.") 191 } 192 } 193 } 194 195 func TestNewPeer(t *testing.T) { 196 name := "nodename" 197 caps := []Cap{{"foo", 2}, {"bar", 3}} 198 id := randomID() 199 p := NewPeer(id, name, caps) 200 if p.ID() != id { 201 t.Errorf("ID mismatch: got %v, expected %v", p.ID(), id) 202 } 203 if p.Name() != name { 204 t.Errorf("Name mismatch: got %v, expected %v", p.Name(), name) 205 } 206 if !reflect.DeepEqual(p.Caps(), caps) { 207 t.Errorf("Caps mismatch: got %v, expected %v", p.Caps(), caps) 208 } 209 210 p.Disconnect(DiscAlreadyConnected) // Should not hang 211 } 212 213 func TestMatchProtocols(t *testing.T) { 214 tests := []struct { 215 Remote []Cap 216 Local []Protocol 217 Match map[string]protoRW 218 }{ 219 { 220 // No remote capabilities 221 Local: []Protocol{{Name: "a"}}, 222 }, 223 { 224 // No local protocols 225 Remote: []Cap{{Name: "a"}}, 226 }, 227 { 228 // No mutual protocols 229 Remote: []Cap{{Name: "a"}}, 230 Local: []Protocol{{Name: "b"}}, 231 }, 232 { 233 // Some matches, some differences 234 Remote: []Cap{{Name: "local"}, {Name: "match1"}, {Name: "match2"}}, 235 Local: []Protocol{{Name: "match1"}, {Name: "match2"}, {Name: "remote"}}, 236 Match: map[string]protoRW{"match1": {Protocol: Protocol{Name: "match1"}}, "match2": {Protocol: Protocol{Name: "match2"}}}, 237 }, 238 { 239 // Various alphabetical ordering 240 Remote: []Cap{{Name: "aa"}, {Name: "ab"}, {Name: "bb"}, {Name: "ba"}}, 241 Local: []Protocol{{Name: "ba"}, {Name: "bb"}, {Name: "ab"}, {Name: "aa"}}, 242 Match: map[string]protoRW{"aa": {Protocol: Protocol{Name: "aa"}}, "ab": {Protocol: Protocol{Name: "ab"}}, "ba": {Protocol: Protocol{Name: "ba"}}, "bb": {Protocol: Protocol{Name: "bb"}}}, 243 }, 244 { 245 // No mutual versions 246 Remote: []Cap{{Version: 1}}, 247 Local: []Protocol{{Version: 2}}, 248 }, 249 { 250 // Multiple versions, single common 251 Remote: []Cap{{Version: 1}, {Version: 2}}, 252 Local: []Protocol{{Version: 2}, {Version: 3}}, 253 Match: map[string]protoRW{"": {Protocol: Protocol{Version: 2}}}, 254 }, 255 { 256 // Multiple versions, multiple common 257 Remote: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Version: 4}}, 258 Local: []Protocol{{Version: 2}, {Version: 3}}, 259 Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}}, 260 }, 261 { 262 // Various version orderings 263 Remote: []Cap{{Version: 4}, {Version: 1}, {Version: 3}, {Version: 2}}, 264 Local: []Protocol{{Version: 2}, {Version: 3}, {Version: 1}}, 265 Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}}, 266 }, 267 { 268 // Versions overriding sub-protocol lengths 269 Remote: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Name: "a"}}, 270 Local: []Protocol{{Version: 1, Length: 1}, {Version: 2, Length: 2}, {Version: 3, Length: 3}, {Name: "a"}}, 271 Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}, "a": {Protocol: Protocol{Name: "a"}, offset: 3}}, 272 }, 273 } 274 275 for i, tt := range tests { 276 result := matchProtocols(tt.Local, tt.Remote, nil) 277 if len(result) != len(tt.Match) { 278 t.Errorf("test %d: negotiation mismatch: have %v, want %v", i, len(result), len(tt.Match)) 279 continue 280 } 281 // Make sure all negotiated protocols are needed and correct 282 for name, proto := range result { 283 match, ok := tt.Match[name] 284 if !ok { 285 t.Errorf("test %d, proto '%s': negotiated but shouldn't have", i, name) 286 continue 287 } 288 if proto.Name != match.Name { 289 t.Errorf("test %d, proto '%s': name mismatch: have %v, want %v", i, name, proto.Name, match.Name) 290 } 291 if proto.Version != match.Version { 292 t.Errorf("test %d, proto '%s': version mismatch: have %v, want %v", i, name, proto.Version, match.Version) 293 } 294 if proto.offset-baseProtocolLength != match.offset { 295 t.Errorf("test %d, proto '%s': offset mismatch: have %v, want %v", i, name, proto.offset-baseProtocolLength, match.offset) 296 } 297 } 298 // Make sure no protocols missed negotiation 299 for name := range tt.Match { 300 if _, ok := result[name]; !ok { 301 t.Errorf("test %d, proto '%s': not negotiated, should have", i, name) 302 continue 303 } 304 } 305 } 306 }