package memberlist import ( "fmt" "net" "strconv" "time" ) // MockNetwork is used as a factory that produces MockTransport instances which // are uniquely addressed and wired up to talk to each other. type MockNetwork struct { transports map[string]*MockTransport port int } // NewTransport returns a new MockTransport with a unique address, wired up to // talk to the other transports in the MockNetwork. func (n *MockNetwork) NewTransport() *MockTransport { n.port += 1 addr := fmt.Sprintf("127.0.0.1:%d", n.port) transport := &MockTransport{ net: n, addr: &MockAddress{addr}, packetCh: make(chan *Packet), streamCh: make(chan net.Conn), } if n.transports == nil { n.transports = make(map[string]*MockTransport) } n.transports[addr] = transport return transport } // MockAddress is a wrapper which adds the net.Addr interface to our mock // address scheme. type MockAddress struct { addr string } // See net.Addr. func (a *MockAddress) Network() string { return "mock" } // See net.Addr. func (a *MockAddress) String() string { return a.addr } // MockTransport directly plumbs messages to other transports its MockNetwork. type MockTransport struct { net *MockNetwork addr *MockAddress packetCh chan *Packet streamCh chan net.Conn } // See Transport. func (t *MockTransport) FinalAdvertiseAddr(string, int) (net.IP, int, error) { host, portStr, err := net.SplitHostPort(t.addr.String()) if err != nil { return nil, 0, err } ip := net.ParseIP(host) if ip == nil { return nil, 0, fmt.Errorf("Failed to parse IP %q", host) } port, err := strconv.ParseInt(portStr, 10, 16) if err != nil { return nil, 0, err } return ip, int(port), nil } // See Transport. func (t *MockTransport) WriteTo(b []byte, addr string) (time.Time, error) { dest, ok := t.net.transports[addr] if !ok { return time.Time{}, fmt.Errorf("No route to %q", addr) } now := time.Now() dest.packetCh <- &Packet{ Buf: b, From: t.addr, Timestamp: now, } return now, nil } // See Transport. func (t *MockTransport) PacketCh() <-chan *Packet { return t.packetCh } // See Transport. func (t *MockTransport) DialTimeout(addr string, timeout time.Duration) (net.Conn, error) { dest, ok := t.net.transports[addr] if !ok { return nil, fmt.Errorf("No route to %q", addr) } p1, p2 := net.Pipe() dest.streamCh <- p1 return p2, nil } // See Transport. func (t *MockTransport) StreamCh() <-chan net.Conn { return t.streamCh } // See Transport. func (t *MockTransport) Shutdown() error { return nil }