func waitForConvergence(t *testing.T, maxIterations int, testNodes ...*testNode) {
	waitForConvergenceNodes(t, maxIterations, testNodesToNodes(testNodes)...)
}
waitForConvergence lets the nodes gossip and returns when the nodes are converged.
After the cluster finished gossiping we double check that all nodes have the
same checksum for the memberlist, this means that the cluster is converged.
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
}