func (c *swimCluster) Addresses() (hostports []string) {
	for _, node := range c.nodes {
		hostports = append(hostports, node.Address())
	}
	return
}
Addresses returns a slice of addresses of all nodes in the cluster.
package swim
import (
	"fmt"
	"reflect"
	"sync"
	"testing"
	"time"
	"github.com/benbjohnson/clock"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/uber/ringpop-go/discovery/statichosts"
	"github.com/uber/ringpop-go/events"
	"github.com/uber/ringpop-go/shared"
	"github.com/uber/ringpop-go/util"
	"github.com/uber/tchannel-go"
)
var testNow = time.Now()
var testInc = util.TimeNowMS()
var testSuspect = Change{
	Address:           "127.0.0.1:3002",
	Incarnation:       testInc,
	Source:            "127.0.0.1:3001",
	SourceIncarnation: testInc,
	Status:            Suspect,
	Timestamp:         util.Timestamp(testNow),
}
type dummyIter struct{}
func (dummyIter) Next() (*Member, bool) {
	return &Member{
		Address:     "127.0.0.1:3010",
		Status:      Alive,
		Incarnation: testInc,
	}, true
}
type testNode struct {
	node    *Node
	channel *tchannel.Channel
	clock   *clock.Mock
}
func (n *testNode) Destroy() {
	n.node.Destroy()
	n.channel.Close()
}
func (n *testNode) closeAndWait(sender *tchannel.Channel) {
	n.channel.Close()
	for {
		ctx, cancel := shared.NewTChannelContext(time.Millisecond * 50)
		err := sender.Ping(ctx, n.node.Address())
		if err == nil {
			cancel()
			continue
		}
		_, ok := err.(tchannel.SystemError)
		if ok {
			cancel()
			continue
		}
		cancel()
		break
	}
}
func newChannelNode(t *testing.T) *testNode {
	ch, err := tchannel.NewChannel("test", nil)
	require.NoError(t, err, "channel must create successfully")
	err = ch.ListenAndServe("127.0.0.1:0")
	require.NoError(t, err, "channel must listen")
	hostport := ch.PeerInfo().HostPort
	c := clock.NewMock()
	node := NewNode("test", hostport, ch.GetSubChannel("test"), &Options{
		Clock: c,
	})
	return &testNode{node, ch, c}
}
func newChannelNodeWithHostPort(t *testing.T, hostport string) *testNode {
	ch, err := tchannel.NewChannel("test", nil)
	require.NoError(t, err, "channel must create successfully")
	c := clock.NewMock()
	node := NewNode("test", hostport, ch.GetSubChannel("test"), &Options{
		Clock: c,
	})
	return &testNode{node, ch, c}
}
func genChannelNodes(t *testing.T, n int) (nodes []*testNode) {
	for i := 0; i < n; i++ {
		node := newChannelNode(t)
		nodes = append(nodes, node)
	}
	return
}
func memberlistHasMembers(t *testing.T, m *memberlist, members []Member) {
	for _, expected := range members {
		member, ok := m.Member(expected.Address)
		assert.True(t, ok, "expected member to be in memberlist")
		require.NotNil(t, member, "member cannot be nil")
		assert.Equal(t, expected.Status, member.Status, "expected statuses to be the same")
	}
}
func bootstrapNodes(t *testing.T, testNodes ...*testNode) []string {
	var hostports []string
	for _, tn := range testNodes {
		hostports = append(hostports, tn.node.Address())
		_, err := tn.node.Bootstrap(&BootstrapOptions{
			DiscoverProvider: statichosts.New(hostports...),
			Stopped:          true,
		})
		require.NoError(t, err, "node must bootstrap successfully")
	}
	return hostports
}
func waitForConvergence(t *testing.T, maxIterations int, testNodes ...*testNode) {
	waitForConvergenceNodes(t, maxIterations, testNodesToNodes(testNodes)...)
}
func waitForConvergenceNodes(t *testing.T, maxIterations int, nodes ...*Node) {
	if len(nodes) == 1 {
		nodes[0].disseminator.ClearChanges()
		return
	}
	wasGossiping := make([]bool, len(nodes))
	for i, node := range nodes {
		wasGossiping[i] = !node.gossip.Stopped()
		if wasGossiping[i] {
			node.gossip.Stop()
		}
	}
	defer func() {
		for i, shouldStart := range wasGossiping {
			if shouldStart {
				nodes[i].gossip.Start()
			}
		}
	}()
Tick:
	maxIterations--
	if maxIterations < 0 {
		t.Errorf("exhausted the maximum number of iterations while waiting for convergence")
		return
	}
	for _, node := range nodes {
		node.gossip.ProtocolPeriod()
	}
	for _, node := range nodes {
		if node.HasChanges() {
			goto Tick
		}
	}
	if !nodesConverged(nodes) {
		t.Errorf("nodes did not converge to 1 checksum")
	}
}
func destroyNodes(tnodes ...*testNode) {
	for _, tnode := range tnodes {
		tnode.Destroy()
	}
}
func fakeHostPorts(fromHost, toHost, fromPort, toPort int) []string {
	var hostports []string
	for h := fromHost; h <= toHost; h++ {
		for p := fromPort; p <= toPort; p++ {
			hostports = append(hostports, fmt.Sprintf("192.0.2.%v:%v", h, p))
		}
	}
	return hostports
}
type swimCluster struct {
	nodes    []*Node
	channels []*tchannel.Channel
}
func newSwimCluster(size int) *swimCluster {
	var nodes []*Node
	var channels []*tchannel.Channel
	for i := 0; i < size; i++ {
		ch, err := tchannel.NewChannel("test", nil)
		if err != nil {
			panic(err)
		}
		if err := ch.ListenAndServe("127.0.0.1:0"); err != nil {
			panic(err)
		}
		channels = append(channels, ch)
		hostport := ch.PeerInfo().HostPort
		node := NewNode("test", hostport, ch.GetSubChannel("test"), nil)
		nodes = append(nodes, node)
	}
	return &swimCluster{nodes: nodes, channels: channels}
}
func (c *swimCluster) Add(n *Node) {
	n.Bootstrap(&BootstrapOptions{
		DiscoverProvider: statichosts.New(c.Addresses()...),
	})
	c.nodes = append(c.nodes, n)
}