table_test.go 5.75 KB
Newer Older
1
package kbucket
2 3 4 5

import (
	"math/rand"
	"testing"
6
	"time"
7

Jeromy's avatar
Jeromy committed
8 9
	peer "github.com/libp2p/go-libp2p-peer"
	pstore "github.com/libp2p/go-libp2p-peerstore"
George Antoniadis's avatar
George Antoniadis committed
10
	tu "github.com/libp2p/go-testutil"
11 12 13 14
)

// Test basic features of the bucket struct
func TestBucket(t *testing.T) {
15
	b := newBucket()
16

17
	peers := make([]peer.ID, 100)
18
	for i := 0; i < 100; i++ {
19
		peers[i] = tu.RandPeerIDFatal(t)
20
		b.PushFront(peers[i])
21 22
	}

23 24
	local := tu.RandPeerIDFatal(t)
	localID := ConvertPeerID(local)
25 26

	i := rand.Intn(len(peers))
27
	if !b.Has(peers[i]) {
28 29 30
		t.Errorf("Failed to find peer: %v", peers[i])
	}

31
	spl := b.Split(0, ConvertPeerID(local))
32
	llist := b.list
33
	for e := llist.Front(); e != nil; e = e.Next() {
34
		p := ConvertPeerID(e.Value.(peer.ID))
Matt Joiner's avatar
Matt Joiner committed
35
		cpl := CommonPrefixLen(p, localID)
36 37 38 39 40
		if cpl > 0 {
			t.Fatalf("Split failed. found id with cpl > 0 in 0 bucket")
		}
	}

41
	rlist := spl.list
42
	for e := rlist.Front(); e != nil; e = e.Next() {
43
		p := ConvertPeerID(e.Value.(peer.ID))
Matt Joiner's avatar
Matt Joiner committed
44
		cpl := CommonPrefixLen(p, localID)
45 46 47 48 49 50
		if cpl == 0 {
			t.Fatalf("Split failed. found id with cpl == 0 in non 0 bucket")
		}
	}
}

Jeromy's avatar
Jeromy committed
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
func TestTableCallbacks(t *testing.T) {
	local := tu.RandPeerIDFatal(t)
	m := pstore.NewMetrics()
	rt := NewRoutingTable(10, ConvertPeerID(local), time.Hour, m)

	peers := make([]peer.ID, 100)
	for i := 0; i < 100; i++ {
		peers[i] = tu.RandPeerIDFatal(t)
	}

	pset := make(map[peer.ID]struct{})
	rt.PeerAdded = func(p peer.ID) {
		pset[p] = struct{}{}
	}
	rt.PeerRemoved = func(p peer.ID) {
		delete(pset, p)
	}

	rt.Update(peers[0])
	if _, ok := pset[peers[0]]; !ok {
		t.Fatal("should have this peer")
	}

	rt.Remove(peers[0])
	if _, ok := pset[peers[0]]; ok {
		t.Fatal("should not have this peer")
	}

	for _, p := range peers {
		rt.Update(p)
	}

	out := rt.ListPeers()
	for _, outp := range out {
		if _, ok := pset[outp]; !ok {
			t.Fatal("should have peer in the peerset")
		}
		delete(pset, outp)
	}

	if len(pset) > 0 {
		t.Fatal("have peers in peerset that were not in the table", len(pset))
	}
}

96 97
// Right now, this just makes sure that it doesnt hang or crash
func TestTableUpdate(t *testing.T) {
98
	local := tu.RandPeerIDFatal(t)
Jeromy's avatar
Jeromy committed
99
	m := pstore.NewMetrics()
100
	rt := NewRoutingTable(10, ConvertPeerID(local), time.Hour, m)
101

102
	peers := make([]peer.ID, 100)
103
	for i := 0; i < 100; i++ {
104
		peers[i] = tu.RandPeerIDFatal(t)
105 106 107 108
	}

	// Testing Update
	for i := 0; i < 10000; i++ {
109
		rt.Update(peers[rand.Intn(len(peers))])
110 111 112
	}

	for i := 0; i < 100; i++ {
113
		id := ConvertPeerID(tu.RandPeerIDFatal(t))
114 115 116 117 118 119 120 121
		ret := rt.NearestPeers(id, 5)
		if len(ret) == 0 {
			t.Fatal("Failed to find node near ID.")
		}
	}
}

func TestTableFind(t *testing.T) {
122
	local := tu.RandPeerIDFatal(t)
Jeromy's avatar
Jeromy committed
123
	m := pstore.NewMetrics()
124
	rt := NewRoutingTable(10, ConvertPeerID(local), time.Hour, m)
125

126
	peers := make([]peer.ID, 100)
127
	for i := 0; i < 5; i++ {
128
		peers[i] = tu.RandPeerIDFatal(t)
129 130 131
		rt.Update(peers[i])
	}

132
	t.Logf("Searching for peer: '%s'", peers[2])
133 134
	found := rt.NearestPeer(ConvertPeerID(peers[2]))
	if !(found == peers[2]) {
135
		t.Fatalf("Failed to lookup known node...")
136 137 138 139 140 141 142 143 144 145 146
	}
}

func TestTableEldestPreferred(t *testing.T) {
	local := tu.RandPeerIDFatal(t)
	m := pstore.NewMetrics()
	rt := NewRoutingTable(10, ConvertPeerID(local), time.Hour, m)

	// generate size + 1 peers to saturate a bucket
	peers := make([]peer.ID, 15)
	for i := 0; i < 15; {
Matt Joiner's avatar
Matt Joiner committed
147
		if p := tu.RandPeerIDFatal(t); CommonPrefixLen(ConvertPeerID(local), ConvertPeerID(p)) == 0 {
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
			peers[i] = p
			i++
		}
	}

	// test 10 first peers are accepted.
	for _, p := range peers[:10] {
		if _, err := rt.Update(p); err != nil {
			t.Errorf("expected all 10 peers to be accepted; instead got: %v", err)
		}
	}

	// test next 5 peers are rejected.
	for _, p := range peers[10:] {
		if _, err := rt.Update(p); err != ErrPeerRejectedNoCapacity {
			t.Errorf("expected extra 5 peers to be rejected; instead got: %v", err)
		}
165 166 167 168
	}
}

func TestTableFindMultiple(t *testing.T) {
169
	local := tu.RandPeerIDFatal(t)
Jeromy's avatar
Jeromy committed
170
	m := pstore.NewMetrics()
171
	rt := NewRoutingTable(20, ConvertPeerID(local), time.Hour, m)
172

173
	peers := make([]peer.ID, 100)
174
	for i := 0; i < 18; i++ {
175
		peers[i] = tu.RandPeerIDFatal(t)
176 177 178
		rt.Update(peers[i])
	}

179
	t.Logf("Searching for peer: '%s'", peers[2])
180
	found := rt.NearestPeers(ConvertPeerID(peers[2]), 15)
181 182 183 184
	if len(found) != 15 {
		t.Fatalf("Got back different number of peers than we expected.")
	}
}
185 186 187 188 189 190

// Looks for race conditions in table operations. For a more 'certain'
// test, increase the loop counter from 1000 to a much higher number
// and set GOMAXPROCS above 1
func TestTableMultithreaded(t *testing.T) {
	local := peer.ID("localPeer")
Jeromy's avatar
Jeromy committed
191
	m := pstore.NewMetrics()
192 193
	tab := NewRoutingTable(20, ConvertPeerID(local), time.Hour, m)
	var peers []peer.ID
194
	for i := 0; i < 500; i++ {
195
		peers = append(peers, tu.RandPeerIDFatal(t))
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
	}

	done := make(chan struct{})
	go func() {
		for i := 0; i < 1000; i++ {
			n := rand.Intn(len(peers))
			tab.Update(peers[n])
		}
		done <- struct{}{}
	}()

	go func() {
		for i := 0; i < 1000; i++ {
			n := rand.Intn(len(peers))
			tab.Update(peers[n])
		}
		done <- struct{}{}
	}()

	go func() {
		for i := 0; i < 1000; i++ {
			n := rand.Intn(len(peers))
218
			tab.Find(peers[n])
219 220 221 222 223 224 225 226 227 228
		}
		done <- struct{}{}
	}()
	<-done
	<-done
	<-done
}

func BenchmarkUpdates(b *testing.B) {
	b.StopTimer()
Chas Leichner's avatar
Chas Leichner committed
229
	local := ConvertKey("localKey")
Jeromy's avatar
Jeromy committed
230
	m := pstore.NewMetrics()
231
	tab := NewRoutingTable(20, local, time.Hour, m)
232

233
	var peers []peer.ID
234
	for i := 0; i < b.N; i++ {
235
		peers = append(peers, tu.RandPeerIDFatal(b))
236 237 238 239 240 241 242 243 244 245
	}

	b.StartTimer()
	for i := 0; i < b.N; i++ {
		tab.Update(peers[i])
	}
}

func BenchmarkFinds(b *testing.B) {
	b.StopTimer()
Chas Leichner's avatar
Chas Leichner committed
246
	local := ConvertKey("localKey")
Jeromy's avatar
Jeromy committed
247
	m := pstore.NewMetrics()
248
	tab := NewRoutingTable(20, local, time.Hour, m)
249

250
	var peers []peer.ID
251
	for i := 0; i < b.N; i++ {
252
		peers = append(peers, tu.RandPeerIDFatal(b))
253 254 255 256 257
		tab.Update(peers[i])
	}

	b.StartTimer()
	for i := 0; i < b.N; i++ {
258
		tab.Find(peers[i])
259 260
	}
}