1package memberlist
2
3import (
4	"fmt"
5	"net"
6	"strconv"
7	"time"
8)
9
10// MockNetwork is used as a factory that produces MockTransport instances which
11// are uniquely addressed and wired up to talk to each other.
12type MockNetwork struct {
13	transports map[string]*MockTransport
14	port       int
15}
16
17// NewTransport returns a new MockTransport with a unique address, wired up to
18// talk to the other transports in the MockNetwork.
19func (n *MockNetwork) NewTransport() *MockTransport {
20	n.port += 1
21	addr := fmt.Sprintf("127.0.0.1:%d", n.port)
22	transport := &MockTransport{
23		net:      n,
24		addr:     &MockAddress{addr},
25		packetCh: make(chan *Packet),
26		streamCh: make(chan net.Conn),
27	}
28
29	if n.transports == nil {
30		n.transports = make(map[string]*MockTransport)
31	}
32	n.transports[addr] = transport
33	return transport
34}
35
36// MockAddress is a wrapper which adds the net.Addr interface to our mock
37// address scheme.
38type MockAddress struct {
39	addr string
40}
41
42// See net.Addr.
43func (a *MockAddress) Network() string {
44	return "mock"
45}
46
47// See net.Addr.
48func (a *MockAddress) String() string {
49	return a.addr
50}
51
52// MockTransport directly plumbs messages to other transports its MockNetwork.
53type MockTransport struct {
54	net      *MockNetwork
55	addr     *MockAddress
56	packetCh chan *Packet
57	streamCh chan net.Conn
58}
59
60// See Transport.
61func (t *MockTransport) FinalAdvertiseAddr(string, int) (net.IP, int, error) {
62	host, portStr, err := net.SplitHostPort(t.addr.String())
63	if err != nil {
64		return nil, 0, err
65	}
66
67	ip := net.ParseIP(host)
68	if ip == nil {
69		return nil, 0, fmt.Errorf("Failed to parse IP %q", host)
70	}
71
72	port, err := strconv.ParseInt(portStr, 10, 16)
73	if err != nil {
74		return nil, 0, err
75	}
76
77	return ip, int(port), nil
78}
79
80// See Transport.
81func (t *MockTransport) WriteTo(b []byte, addr string) (time.Time, error) {
82	dest, ok := t.net.transports[addr]
83	if !ok {
84		return time.Time{}, fmt.Errorf("No route to %q", addr)
85	}
86
87	now := time.Now()
88	dest.packetCh <- &Packet{
89		Buf:       b,
90		From:      t.addr,
91		Timestamp: now,
92	}
93	return now, nil
94}
95
96// See Transport.
97func (t *MockTransport) PacketCh() <-chan *Packet {
98	return t.packetCh
99}
100
101// See Transport.
102func (t *MockTransport) DialTimeout(addr string, timeout time.Duration) (net.Conn, error) {
103	dest, ok := t.net.transports[addr]
104	if !ok {
105		return nil, fmt.Errorf("No route to %q", addr)
106	}
107
108	p1, p2 := net.Pipe()
109	dest.streamCh <- p1
110	return p2, nil
111}
112
113// See Transport.
114func (t *MockTransport) StreamCh() <-chan net.Conn {
115	return t.streamCh
116}
117
118// See Transport.
119func (t *MockTransport) Shutdown() error {
120	return nil
121}
122