1 // Copyright (c) 2016 The btcsuite developers
2 // Use of this source code is governed by an ISC
3 // license that can be found in the LICENSE file.
17 // Override the max retry duration when running tests.
18 maxRetryDuration = 2 * time.Millisecond
21 // mockAddr mocks a network address
22 type mockAddr struct {
26 func (m mockAddr) Network() string { return m.net }
27 func (m mockAddr) String() string { return m.address }
29 // mockConn mocks a network connection by implementing the net.Conn interface.
30 type mockConn struct {
35 // local network, address for the connection.
38 // remote network, address for the connection.
42 // LocalAddr returns the local address for the connection.
43 func (c mockConn) LocalAddr() net.Addr {
44 return &mockAddr{c.lnet, c.laddr}
47 // RemoteAddr returns the remote address for the connection.
48 func (c mockConn) RemoteAddr() net.Addr {
49 return &mockAddr{c.rAddr.Network(), c.rAddr.String()}
52 // Close handles closing the connection.
53 func (c mockConn) Close() error {
57 func (c mockConn) SetDeadline(t time.Time) error { return nil }
58 func (c mockConn) SetReadDeadline(t time.Time) error { return nil }
59 func (c mockConn) SetWriteDeadline(t time.Time) error { return nil }
61 // mockDialer mocks the net.Dial interface by returning a mock connection to
63 func mockDialer(addr net.Addr) (net.Conn, error) {
65 c := &mockConn{rAddr: addr}
71 // TestNewConfig tests that new ConnManager config is validated as expected.
72 func TestNewConfig(t *testing.T) {
73 _, err := New(&Config{})
75 t.Fatalf("New expected error: 'Dial can't be nil', got nil")
81 t.Fatalf("New unexpected error: %v", err)
85 // TestStartStop tests that the connection manager starts and stops as
87 func TestStartStop(t *testing.T) {
88 connected := make(chan *ConnReq)
89 disconnected := make(chan *ConnReq)
90 cmgr, err := New(&Config{
92 GetNewAddress: func() (net.Addr, error) {
94 IP: net.ParseIP("127.0.0.1"),
99 OnConnection: func(c *ConnReq, conn net.Conn) {
102 OnDisconnection: func(c *ConnReq) {
107 t.Fatalf("New error: %v", err)
110 gotConnReq := <-connected
117 IP: net.ParseIP("127.0.0.1"),
124 t.Fatalf("start/stop: got id: %v, want: 0", cr.ID())
126 cmgr.Disconnect(gotConnReq.ID())
127 cmgr.Remove(gotConnReq.ID())
130 t.Fatalf("start/stop: unexpected disconnection")
131 case <-time.Tick(10 * time.Millisecond):
136 // TestConnectMode tests that the connection manager works in the connect mode.
138 // In connect mode, automatic connections are disabled, so we test that
139 // requests using Connect are handled and that no other connections are made.
140 func TestConnectMode(t *testing.T) {
141 connected := make(chan *ConnReq)
142 cmgr, err := New(&Config{
145 OnConnection: func(c *ConnReq, conn net.Conn) {
150 t.Fatalf("New error: %v", err)
154 IP: net.ParseIP("127.0.0.1"),
161 gotConnReq := <-connected
163 gotID := gotConnReq.ID()
165 t.Fatalf("connect mode: %v - want ID %v, got ID %v", cr.Addr, wantID, gotID)
167 gotState := cr.State()
168 wantState := ConnEstablished
169 if gotState != wantState {
170 t.Fatalf("connect mode: %v - want state %v, got state %v", cr.Addr, wantState, gotState)
173 case c := <-connected:
174 t.Fatalf("connect mode: got unexpected connection - %v", c.Addr)
175 case <-time.After(time.Millisecond):
181 // TestTargetOutbound tests the target number of outbound connections.
183 // We wait until all connections are established, then test they there are the
184 // only connections made.
185 func TestTargetOutbound(t *testing.T) {
186 targetOutbound := uint32(10)
187 connected := make(chan *ConnReq)
188 cmgr, err := New(&Config{
189 TargetOutbound: targetOutbound,
191 GetNewAddress: func() (net.Addr, error) {
193 IP: net.ParseIP("127.0.0.1"),
197 OnConnection: func(c *ConnReq, conn net.Conn) {
202 t.Fatalf("New error: %v", err)
205 for i := uint32(0); i < targetOutbound; i++ {
210 case c := <-connected:
211 t.Fatalf("target outbound: got unexpected connection - %v", c.Addr)
212 case <-time.After(time.Millisecond):
218 // TestRetryPermanent tests that permanent connection requests are retried.
220 // We make a permanent connection request using Connect, disconnect it using
221 // Disconnect and we wait for it to be connected back.
222 func TestRetryPermanent(t *testing.T) {
223 connected := make(chan *ConnReq)
224 disconnected := make(chan *ConnReq)
225 cmgr, err := New(&Config{
226 RetryDuration: time.Millisecond,
229 OnConnection: func(c *ConnReq, conn net.Conn) {
232 OnDisconnection: func(c *ConnReq) {
237 t.Fatalf("New error: %v", err)
242 IP: net.ParseIP("127.0.0.1"),
249 gotConnReq := <-connected
251 gotID := gotConnReq.ID()
253 t.Fatalf("retry: %v - want ID %v, got ID %v", cr.Addr, wantID, gotID)
255 gotState := cr.State()
256 wantState := ConnEstablished
257 if gotState != wantState {
258 t.Fatalf("retry: %v - want state %v, got state %v", cr.Addr, wantState, gotState)
261 cmgr.Disconnect(cr.ID())
262 gotConnReq = <-disconnected
264 gotID = gotConnReq.ID()
266 t.Fatalf("retry: %v - want ID %v, got ID %v", cr.Addr, wantID, gotID)
268 gotState = cr.State()
269 wantState = ConnDisconnected
270 if gotState != wantState {
271 t.Fatalf("retry: %v - want state %v, got state %v", cr.Addr, wantState, gotState)
274 gotConnReq = <-connected
276 gotID = gotConnReq.ID()
278 t.Fatalf("retry: %v - want ID %v, got ID %v", cr.Addr, wantID, gotID)
280 gotState = cr.State()
281 wantState = ConnEstablished
282 if gotState != wantState {
283 t.Fatalf("retry: %v - want state %v, got state %v", cr.Addr, wantState, gotState)
287 gotConnReq = <-disconnected
289 gotID = gotConnReq.ID()
291 t.Fatalf("retry: %v - want ID %v, got ID %v", cr.Addr, wantID, gotID)
293 gotState = cr.State()
294 wantState = ConnDisconnected
295 if gotState != wantState {
296 t.Fatalf("retry: %v - want state %v, got state %v", cr.Addr, wantState, gotState)
301 // TestMaxRetryDuration tests the maximum retry duration.
303 // We have a timed dialer which initially returns err but after RetryDuration
304 // hits maxRetryDuration returns a mock conn.
305 func TestMaxRetryDuration(t *testing.T) {
306 networkUp := make(chan struct{})
307 time.AfterFunc(5*time.Millisecond, func() {
310 timedDialer := func(addr net.Addr) (net.Conn, error) {
313 return mockDialer(addr)
315 return nil, errors.New("network down")
319 connected := make(chan *ConnReq)
320 cmgr, err := New(&Config{
321 RetryDuration: time.Millisecond,
324 OnConnection: func(c *ConnReq, conn net.Conn) {
329 t.Fatalf("New error: %v", err)
334 IP: net.ParseIP("127.0.0.1"),
342 // retry in 2ms - max retry duration reached
343 // retry in 2ms - timedDialer returns mockDial
346 case <-time.Tick(100 * time.Millisecond):
347 t.Fatalf("max retry duration: connection timeout")
351 // TestNetworkFailure tests that the connection manager handles a network
352 // failure gracefully.
353 func TestNetworkFailure(t *testing.T) {
355 errDialer := func(net net.Addr) (net.Conn, error) {
356 atomic.AddUint32(&dials, 1)
357 return nil, errors.New("network down")
359 cmgr, err := New(&Config{
361 RetryDuration: 5 * time.Millisecond,
363 GetNewAddress: func() (net.Addr, error) {
365 IP: net.ParseIP("127.0.0.1"),
369 OnConnection: func(c *ConnReq, conn net.Conn) {
370 t.Fatalf("network failure: got unexpected connection - %v", c.Addr)
374 t.Fatalf("New error: %v", err)
377 time.AfterFunc(10*time.Millisecond, cmgr.Stop)
379 wantMaxDials := uint32(75)
380 if atomic.LoadUint32(&dials) > wantMaxDials {
381 t.Fatalf("network failure: unexpected number of dials - got %v, want < %v",
382 atomic.LoadUint32(&dials), wantMaxDials)
386 // TestStopFailed tests that failed connections are ignored after connmgr is
389 // We have a dailer which sets the stop flag on the conn manager and returns an
390 // err so that the handler assumes that the conn manager is stopped and ignores
392 func TestStopFailed(t *testing.T) {
393 done := make(chan struct{}, 1)
394 waitDialer := func(addr net.Addr) (net.Conn, error) {
396 time.Sleep(time.Millisecond)
397 return nil, errors.New("network down")
399 cmgr, err := New(&Config{
403 t.Fatalf("New error: %v", err)
408 atomic.StoreInt32(&cmgr.stop, 1)
409 time.Sleep(2 * time.Millisecond)
410 atomic.StoreInt32(&cmgr.stop, 0)
415 IP: net.ParseIP("127.0.0.1"),
424 // mockListener implements the net.Listener interface and is used to test
425 // code that deals with net.Listeners without having to actually make any real
427 type mockListener struct {
429 provideConn chan net.Conn
432 // Accept returns a mock connection when it receives a signal via the Connect
435 // This is part of the net.Listener interface.
436 func (m *mockListener) Accept() (net.Conn, error) {
437 for conn := range m.provideConn {
440 return nil, errors.New("network connection closed")
443 // Close closes the mock listener which will cause any blocked Accept
444 // operations to be unblocked and return errors.
446 // This is part of the net.Listener interface.
447 func (m *mockListener) Close() error {
452 // Addr returns the address the mock listener was configured with.
454 // This is part of the net.Listener interface.
455 func (m *mockListener) Addr() net.Addr {
456 return &mockAddr{"tcp", m.localAddr}
459 // Connect fakes a connection to the mock listener from the provided remote
460 // address. It will cause the Accept function to return a mock connection
461 // configured with the provided remote address and the local address for the
463 func (m *mockListener) Connect(ip string, port int) {
464 m.provideConn <- &mockConn{
474 // newMockListener returns a new mock listener for the provided local address
475 // and port. No ports are actually opened.
476 func newMockListener(localAddr string) *mockListener {
477 return &mockListener{
478 localAddr: localAddr,
479 provideConn: make(chan net.Conn),
483 // TestListeners ensures providing listeners to the connection manager along
484 // with an accept callback works properly.
485 func TestListeners(t *testing.T) {
486 // Setup a connection manager with a couple of mock listeners that
487 // notify a channel when they receive mock connections.
488 receivedConns := make(chan net.Conn)
489 listener1 := newMockListener("127.0.0.1:8333")
490 listener2 := newMockListener("127.0.0.1:9333")
491 listeners := []net.Listener{listener1, listener2}
492 cmgr, err := New(&Config{
493 Listeners: listeners,
494 OnAccept: func(conn net.Conn) {
495 receivedConns <- conn
500 t.Fatalf("New error: %v", err)
504 // Fake a couple of mock connections to each of the listeners.
506 for i, listener := range listeners {
507 l := listener.(*mockListener)
508 l.Connect("127.0.0.1", 10000+i*2)
509 l.Connect("127.0.0.1", 10000+i*2+1)
513 // Tally the receive connections to ensure the expected number are
514 // received. Also, fail the test after a timeout so it will not hang
515 // forever should the test not work.
516 expectedNumConns := len(listeners) * 2
521 case <-receivedConns:
523 if numConns == expectedNumConns {
527 case <-time.After(time.Millisecond * 50):
528 t.Fatalf("Timeout waiting for %d expected connections",