github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/network/p2p/peer_test.go (about) 1 package p2p 2 3 import ( 4 "errors" 5 "fmt" 6 "math/rand" 7 "net" 8 "reflect" 9 "testing" 10 "time" 11 ) 12 13 var discard = Protocol{ 14 Name: "discard", 15 Length: 1, 16 Run: func(p *Peer, rw MsgReadWriter) error { 17 for { 18 msg, err := rw.ReadMsg() 19 if err != nil { 20 return err 21 } 22 fmt.Printf("discarding %d\n", msg.Code) 23 if err = msg.Discard(); err != nil { 24 return err 25 } 26 } 27 }, 28 } 29 30 func testPeer(protos []Protocol) (func(), *conn, *Peer, <-chan error) { 31 fd1, fd2 := net.Pipe() 32 c1 := &conn{fd: fd1, transport: newTestTransport(randomID(), fd1)} 33 c2 := &conn{fd: fd2, transport: newTestTransport(randomID(), fd2)} 34 for _, p := range protos { 35 c1.caps = append(c1.caps, p.cap()) 36 c2.caps = append(c2.caps, p.cap()) 37 } 38 39 peer := newPeer(c1, protos) 40 errc := make(chan error, 1) 41 go func() { 42 _, err := peer.run() 43 errc <- err 44 }() 45 46 closer := func() { c2.close(errors.New("close func called")) } 47 return closer, c2, peer, errc 48 } 49 50 func TestPeerProtoReadMsg(t *testing.T) { 51 proto := Protocol{ 52 Name: "a", 53 Length: 5, 54 Run: func(peer *Peer, rw MsgReadWriter) error { 55 if err := ExpectMsg(rw, 2, []uint{1}); err != nil { 56 t.Error(err) 57 } 58 if err := ExpectMsg(rw, 3, []uint{2}); err != nil { 59 t.Error(err) 60 } 61 if err := ExpectMsg(rw, 4, []uint{3}); err != nil { 62 t.Error(err) 63 } 64 return nil 65 }, 66 } 67 68 closer, rw, _, errc := testPeer([]Protocol{proto}) 69 defer closer() 70 71 Send(rw, baseProtocolLength+2, []uint{1}) 72 Send(rw, baseProtocolLength+3, []uint{2}) 73 Send(rw, baseProtocolLength+4, []uint{3}) 74 75 select { 76 case err := <-errc: 77 if err != errProtocolReturned { 78 t.Errorf("peer returned error: %v", err) 79 } 80 case <-time.After(2 * time.Second): 81 t.Errorf("receive timeout") 82 } 83 } 84 85 func TestPeerProtoEncodeMsg(t *testing.T) { 86 proto := Protocol{ 87 Name: "a", 88 Length: 2, 89 Run: func(peer *Peer, rw MsgReadWriter) error { 90 if err := SendItems(rw, 2); err == nil { 91 t.Error("expected error for out-of-range msg code, got nil") 92 } 93 if err := SendItems(rw, 1, "foo", "bar"); err != nil { 94 t.Errorf("write error: %v", err) 95 } 96 return nil 97 }, 98 } 99 closer, rw, _, _ := testPeer([]Protocol{proto}) 100 defer closer() 101 102 if err := ExpectMsg(rw, 17, []string{"foo", "bar"}); err != nil { 103 t.Error(err) 104 } 105 } 106 107 func TestPeerPing(t *testing.T) { 108 closer, rw, _, _ := testPeer(nil) 109 defer closer() 110 if err := SendItems(rw, pingMsg); err != nil { 111 t.Fatal(err) 112 } 113 if err := ExpectMsg(rw, pongMsg, nil); err != nil { 114 t.Error(err) 115 } 116 } 117 118 func TestPeerDisconnect(t *testing.T) { 119 closer, rw, _, disc := testPeer(nil) 120 defer closer() 121 if err := SendItems(rw, discMsg, DiscQuitting); err != nil { 122 t.Fatal(err) 123 } 124 select { 125 case reason := <-disc: 126 if reason != DiscQuitting { 127 t.Errorf("run returned wrong reason: got %v, want %v", reason, DiscQuitting) 128 } 129 case <-time.After(500 * time.Millisecond): 130 t.Error("peer did not return") 131 } 132 } 133 134 func TestPeerDisconnectRace(t *testing.T) { 135 maybe := func() bool { return rand.Intn(1) == 1 } 136 137 for i := 0; i < 1000; i++ { 138 protoclose := make(chan error) 139 protodisc := make(chan DiscReason) 140 closer, rw, p, disc := testPeer([]Protocol{ 141 { 142 Name: "closereq", 143 Run: func(p *Peer, rw MsgReadWriter) error { return <-protoclose }, 144 Length: 1, 145 }, 146 { 147 Name: "disconnect", 148 Run: func(p *Peer, rw MsgReadWriter) error { p.Disconnect(<-protodisc); return nil }, 149 Length: 1, 150 }, 151 }) 152 153 go SendItems(rw, baseProtocolLength+1) 154 go SendItems(rw, baseProtocolLength+2) 155 156 go closer() 157 158 protoclose <- errors.New("protocol closed") 159 160 protodisc <- DiscAlreadyConnected 161 162 if maybe() { 163 go p.Disconnect(DiscInvalidIdentity) 164 } 165 166 if maybe() { 167 go SendItems(rw, discMsg, DiscQuitting) 168 } 169 170 select { 171 case <-disc: 172 case <-time.After(2 * time.Second): 173 174 panic("Peer.run took to long to return.") 175 } 176 } 177 } 178 179 func TestNewPeer(t *testing.T) { 180 name := "nodename" 181 caps := []Cap{{"foo", 2}, {"bar", 3}} 182 id := randomID() 183 p := NewPeer(id, name, caps) 184 if p.ID() != id { 185 t.Errorf("ID mismatch: got %v, expected %v", p.ID(), id) 186 } 187 if p.Name() != name { 188 t.Errorf("Name mismatch: got %v, expected %v", p.Name(), name) 189 } 190 if !reflect.DeepEqual(p.Caps(), caps) { 191 t.Errorf("Caps mismatch: got %v, expected %v", p.Caps(), caps) 192 } 193 194 p.Disconnect(DiscAlreadyConnected) 195 } 196 197 func TestMatchProtocols(t *testing.T) { 198 tests := []struct { 199 Remote []Cap 200 Local []Protocol 201 Match map[string]protoRW 202 }{ 203 { 204 205 Local: []Protocol{{Name: "a"}}, 206 }, 207 { 208 209 Remote: []Cap{{Name: "a"}}, 210 }, 211 { 212 213 Remote: []Cap{{Name: "a"}}, 214 Local: []Protocol{{Name: "b"}}, 215 }, 216 { 217 218 Remote: []Cap{{Name: "local"}, {Name: "match1"}, {Name: "match2"}}, 219 Local: []Protocol{{Name: "match1"}, {Name: "match2"}, {Name: "remote"}}, 220 Match: map[string]protoRW{"match1": {Protocol: Protocol{Name: "match1"}}, "match2": {Protocol: Protocol{Name: "match2"}}}, 221 }, 222 { 223 224 Remote: []Cap{{Name: "aa"}, {Name: "ab"}, {Name: "bb"}, {Name: "ba"}}, 225 Local: []Protocol{{Name: "ba"}, {Name: "bb"}, {Name: "ab"}, {Name: "aa"}}, 226 Match: map[string]protoRW{"aa": {Protocol: Protocol{Name: "aa"}}, "ab": {Protocol: Protocol{Name: "ab"}}, "ba": {Protocol: Protocol{Name: "ba"}}, "bb": {Protocol: Protocol{Name: "bb"}}}, 227 }, 228 { 229 230 Remote: []Cap{{Version: 1}}, 231 Local: []Protocol{{Version: 2}}, 232 }, 233 { 234 235 Remote: []Cap{{Version: 1}, {Version: 2}}, 236 Local: []Protocol{{Version: 2}, {Version: 3}}, 237 Match: map[string]protoRW{"": {Protocol: Protocol{Version: 2}}}, 238 }, 239 { 240 241 Remote: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Version: 4}}, 242 Local: []Protocol{{Version: 2}, {Version: 3}}, 243 Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}}, 244 }, 245 { 246 247 Remote: []Cap{{Version: 4}, {Version: 1}, {Version: 3}, {Version: 2}}, 248 Local: []Protocol{{Version: 2}, {Version: 3}, {Version: 1}}, 249 Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}}, 250 }, 251 { 252 253 Remote: []Cap{{Version: 1}, {Version: 2}, {Version: 3}, {Name: "a"}}, 254 Local: []Protocol{{Version: 1, Length: 1}, {Version: 2, Length: 2}, {Version: 3, Length: 3}, {Name: "a"}}, 255 Match: map[string]protoRW{"": {Protocol: Protocol{Version: 3}}, "a": {Protocol: Protocol{Name: "a"}, offset: 3}}, 256 }, 257 } 258 259 for i, tt := range tests { 260 result := matchProtocols(tt.Local, tt.Remote, nil) 261 if len(result) != len(tt.Match) { 262 t.Errorf("test %d: negotiation mismatch: have %v, want %v", i, len(result), len(tt.Match)) 263 continue 264 } 265 266 for name, proto := range result { 267 match, ok := tt.Match[name] 268 if !ok { 269 t.Errorf("test %d, proto '%s': negotiated but shouldn't have", i, name) 270 continue 271 } 272 if proto.Name != match.Name { 273 t.Errorf("test %d, proto '%s': name mismatch: have %v, want %v", i, name, proto.Name, match.Name) 274 } 275 if proto.Version != match.Version { 276 t.Errorf("test %d, proto '%s': version mismatch: have %v, want %v", i, name, proto.Version, match.Version) 277 } 278 if proto.offset-baseProtocolLength != match.offset { 279 t.Errorf("test %d, proto '%s': offset mismatch: have %v, want %v", i, name, proto.offset-baseProtocolLength, match.offset) 280 } 281 } 282 283 for name := range tt.Match { 284 if _, ok := result[name]; !ok { 285 t.Errorf("test %d, proto '%s': not negotiated, should have", i, name) 286 continue 287 } 288 } 289 } 290 }