github.com/susy-go/susy-graviton@v0.0.0-20190614130430-36cddae42305/swarm/network/fetcher_test.go (about) 1 // Copyleft 2018 The susy-graviton Authors 2 // This file is part of the susy-graviton library. 3 // 4 // The susy-graviton library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The susy-graviton library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MSRCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the susy-graviton library. If not, see <http://www.gnu.org/licenses/>. 16 17 package network 18 19 import ( 20 "context" 21 "sync" 22 "testing" 23 "time" 24 25 "github.com/susy-go/susy-graviton/p2p/enode" 26 ) 27 28 var requestedPeerID = enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8") 29 var sourcePeerID = enode.HexID("99d8594b52298567d2ca3f4c441a5ba0140ee9245e26460d01102a52773c73b9") 30 31 // mockRequester pushes every request to the requestC channel when its doRequest function is called 32 type mockRequester struct { 33 // requests []Request 34 requestC chan *Request // when a request is coming it is pushed to requestC 35 waitTimes []time.Duration // with waitTimes[i] you can define how much to wait on the ith request (optional) 36 count int //counts the number of requests 37 quitC chan struct{} 38 } 39 40 func newMockRequester(waitTimes ...time.Duration) *mockRequester { 41 return &mockRequester{ 42 requestC: make(chan *Request), 43 waitTimes: waitTimes, 44 quitC: make(chan struct{}), 45 } 46 } 47 48 func (m *mockRequester) doRequest(ctx context.Context, request *Request) (*enode.ID, chan struct{}, error) { 49 waitTime := time.Duration(0) 50 if m.count < len(m.waitTimes) { 51 waitTime = m.waitTimes[m.count] 52 m.count++ 53 } 54 time.Sleep(waitTime) 55 m.requestC <- request 56 57 // if there is a Source in the request use that, if not use the global requestedPeerId 58 source := request.Source 59 if source == nil { 60 source = &requestedPeerID 61 } 62 return source, m.quitC, nil 63 } 64 65 // TestFetcherSingleRequest creates a Fetcher using mockRequester, and run it with a sample set of peers to skip. 66 // mockRequester pushes a Request on a channel every time the request function is called. Using 67 // this channel we test if calling Fetcher.Request calls the request function, and whether it uses 68 // the correct peers to skip which we provided for the fetcher.run function. 69 func TestFetcherSingleRequest(t *testing.T) { 70 requester := newMockRequester() 71 addr := make([]byte, 32) 72 73 ctx, cancel := context.WithCancel(context.Background()) 74 defer cancel() 75 76 fetcher := NewFetcher(ctx, addr, requester.doRequest, true) 77 78 peers := []string{"a", "b", "c", "d"} 79 peersToSkip := &sync.Map{} 80 for _, p := range peers { 81 peersToSkip.Store(p, time.Now()) 82 } 83 84 go fetcher.run(peersToSkip) 85 86 fetcher.Request(0) 87 88 select { 89 case request := <-requester.requestC: 90 // request should contain all peers from peersToSkip provided to the fetcher 91 for _, p := range peers { 92 if _, ok := request.peersToSkip.Load(p); !ok { 93 t.Fatalf("request.peersToSkip misses peer") 94 } 95 } 96 97 // source peer should be also added to peersToSkip eventually 98 time.Sleep(100 * time.Millisecond) 99 if _, ok := request.peersToSkip.Load(requestedPeerID.String()); !ok { 100 t.Fatalf("request.peersToSkip does not contain peer returned by the request function") 101 } 102 103 // hopCount in the forwarded request should be incremented 104 if request.HopCount != 1 { 105 t.Fatalf("Expected request.HopCount 1 got %v", request.HopCount) 106 } 107 108 // fetch should trigger a request, if it doesn't happen in time, test should fail 109 case <-time.After(200 * time.Millisecond): 110 t.Fatalf("fetch timeout") 111 } 112 } 113 114 // TestCancelStopsFetcher tests that a cancelled fetcher does not initiate further requests even if its fetch function is called 115 func TestFetcherCancelStopsFetcher(t *testing.T) { 116 requester := newMockRequester() 117 addr := make([]byte, 32) 118 119 ctx, cancel := context.WithCancel(context.Background()) 120 121 fetcher := NewFetcher(ctx, addr, requester.doRequest, true) 122 123 peersToSkip := &sync.Map{} 124 125 // we start the fetcher, and then we immediately cancel the context 126 go fetcher.run(peersToSkip) 127 cancel() 128 129 // we call Request with an active context 130 fetcher.Request(0) 131 132 // fetcher should not initiate request, we can only check by waiting a bit and making sure no request is happening 133 select { 134 case <-requester.requestC: 135 t.Fatalf("cancelled fetcher initiated request") 136 case <-time.After(200 * time.Millisecond): 137 } 138 } 139 140 // TestFetchCancelStopsRequest tests that calling a Request function with a cancelled context does not initiate a request 141 func TestFetcherCancelStopsRequest(t *testing.T) { 142 t.Skip("since context is now per fetcher, this test is likely redundant") 143 144 requester := newMockRequester(100 * time.Millisecond) 145 addr := make([]byte, 32) 146 147 ctx, cancel := context.WithCancel(context.Background()) 148 defer cancel() 149 150 fetcher := NewFetcher(ctx, addr, requester.doRequest, true) 151 152 peersToSkip := &sync.Map{} 153 154 // we start the fetcher with an active context 155 go fetcher.run(peersToSkip) 156 157 // we call Request with a cancelled context 158 fetcher.Request(0) 159 160 // fetcher should not initiate request, we can only check by waiting a bit and making sure no request is happening 161 select { 162 case <-requester.requestC: 163 t.Fatalf("cancelled fetch function initiated request") 164 case <-time.After(200 * time.Millisecond): 165 } 166 167 // if there is another Request with active context, there should be a request, because the fetcher itself is not cancelled 168 fetcher.Request(0) 169 170 select { 171 case <-requester.requestC: 172 case <-time.After(200 * time.Millisecond): 173 t.Fatalf("expected request") 174 } 175 } 176 177 // TestOfferUsesSource tests Fetcher Offer behavior. 178 // In this case there should be 1 (and only one) request initiated from the source peer, and the 179 // source nodeid should appear in the peersToSkip map. 180 func TestFetcherOfferUsesSource(t *testing.T) { 181 requester := newMockRequester(100 * time.Millisecond) 182 addr := make([]byte, 32) 183 184 ctx, cancel := context.WithCancel(context.Background()) 185 defer cancel() 186 187 fetcher := NewFetcher(ctx, addr, requester.doRequest, true) 188 189 peersToSkip := &sync.Map{} 190 191 // start the fetcher 192 go fetcher.run(peersToSkip) 193 194 // call the Offer function with the source peer 195 fetcher.Offer(&sourcePeerID) 196 197 // fetcher should not initiate request 198 select { 199 case <-requester.requestC: 200 t.Fatalf("fetcher initiated request") 201 case <-time.After(200 * time.Millisecond): 202 } 203 204 // call Request after the Offer 205 fetcher.Request(0) 206 207 // there should be exactly 1 request coming from fetcher 208 var request *Request 209 select { 210 case request = <-requester.requestC: 211 if *request.Source != sourcePeerID { 212 t.Fatalf("Expected source id %v got %v", sourcePeerID, request.Source) 213 } 214 case <-time.After(200 * time.Millisecond): 215 t.Fatalf("fetcher did not initiate request") 216 } 217 218 select { 219 case <-requester.requestC: 220 t.Fatalf("Fetcher number of requests expected 1 got 2") 221 case <-time.After(200 * time.Millisecond): 222 } 223 224 // source peer should be added to peersToSkip eventually 225 time.Sleep(100 * time.Millisecond) 226 if _, ok := request.peersToSkip.Load(sourcePeerID.String()); !ok { 227 t.Fatalf("SourcePeerId not added to peersToSkip") 228 } 229 } 230 231 func TestFetcherOfferAfterRequestUsesSourceFromContext(t *testing.T) { 232 requester := newMockRequester(100 * time.Millisecond) 233 addr := make([]byte, 32) 234 235 ctx, cancel := context.WithCancel(context.Background()) 236 defer cancel() 237 238 fetcher := NewFetcher(ctx, addr, requester.doRequest, true) 239 240 peersToSkip := &sync.Map{} 241 242 // start the fetcher 243 go fetcher.run(peersToSkip) 244 245 // call Request first 246 fetcher.Request(0) 247 248 // there should be a request coming from fetcher 249 var request *Request 250 select { 251 case request = <-requester.requestC: 252 if request.Source != nil { 253 t.Fatalf("Incorrect source peer id, expected nil got %v", request.Source) 254 } 255 case <-time.After(200 * time.Millisecond): 256 t.Fatalf("fetcher did not initiate request") 257 } 258 259 // after the Request call Offer 260 fetcher.Offer(&sourcePeerID) 261 262 // there should be a request coming from fetcher 263 select { 264 case request = <-requester.requestC: 265 if *request.Source != sourcePeerID { 266 t.Fatalf("Incorrect source peer id, expected %v got %v", sourcePeerID, request.Source) 267 } 268 case <-time.After(200 * time.Millisecond): 269 t.Fatalf("fetcher did not initiate request") 270 } 271 272 // source peer should be added to peersToSkip eventually 273 time.Sleep(100 * time.Millisecond) 274 if _, ok := request.peersToSkip.Load(sourcePeerID.String()); !ok { 275 t.Fatalf("SourcePeerId not added to peersToSkip") 276 } 277 } 278 279 // TestFetcherRetryOnTimeout tests that fetch retries after searchTimeOut has passed 280 func TestFetcherRetryOnTimeout(t *testing.T) { 281 requester := newMockRequester() 282 addr := make([]byte, 32) 283 284 ctx, cancel := context.WithCancel(context.Background()) 285 defer cancel() 286 287 fetcher := NewFetcher(ctx, addr, requester.doRequest, true) 288 // set searchTimeOut to low value so the test is quicker 289 fetcher.searchTimeout = 250 * time.Millisecond 290 291 peersToSkip := &sync.Map{} 292 293 // start the fetcher 294 go fetcher.run(peersToSkip) 295 296 // call the fetch function with an active context 297 fetcher.Request(0) 298 299 // after 100ms the first request should be initiated 300 time.Sleep(100 * time.Millisecond) 301 302 select { 303 case <-requester.requestC: 304 default: 305 t.Fatalf("fetch did not initiate request") 306 } 307 308 // after another 100ms no new request should be initiated, because search timeout is 250ms 309 time.Sleep(100 * time.Millisecond) 310 311 select { 312 case <-requester.requestC: 313 t.Fatalf("unexpected request from fetcher") 314 default: 315 } 316 317 // after another 300ms search timeout is over, there should be a new request 318 time.Sleep(300 * time.Millisecond) 319 320 select { 321 case <-requester.requestC: 322 default: 323 t.Fatalf("fetch did not retry request") 324 } 325 } 326 327 // TestFetcherFactory creates a FetcherFactory and checks if the factory really creates and starts 328 // a Fetcher when it return a fetch function. We test the fetching functionality just by checking if 329 // a request is initiated when the fetch function is called 330 func TestFetcherFactory(t *testing.T) { 331 requester := newMockRequester(100 * time.Millisecond) 332 addr := make([]byte, 32) 333 fetcherFactory := NewFetcherFactory(requester.doRequest, false) 334 335 peersToSkip := &sync.Map{} 336 337 fetcher := fetcherFactory.New(context.Background(), addr, peersToSkip) 338 339 fetcher.Request(0) 340 341 // check if the created fetchFunction really starts a fetcher and initiates a request 342 select { 343 case <-requester.requestC: 344 case <-time.After(200 * time.Millisecond): 345 t.Fatalf("fetch timeout") 346 } 347 348 } 349 350 func TestFetcherRequestQuitRetriesRequest(t *testing.T) { 351 requester := newMockRequester() 352 addr := make([]byte, 32) 353 354 ctx, cancel := context.WithCancel(context.Background()) 355 defer cancel() 356 357 fetcher := NewFetcher(ctx, addr, requester.doRequest, true) 358 359 // make sure the searchTimeout is long so it is sure the request is not 360 // retried because of timeout 361 fetcher.searchTimeout = 10 * time.Second 362 363 peersToSkip := &sync.Map{} 364 365 go fetcher.run(peersToSkip) 366 367 fetcher.Request(0) 368 369 select { 370 case <-requester.requestC: 371 case <-time.After(200 * time.Millisecond): 372 t.Fatalf("request is not initiated") 373 } 374 375 close(requester.quitC) 376 377 select { 378 case <-requester.requestC: 379 case <-time.After(200 * time.Millisecond): 380 t.Fatalf("request is not initiated after failed request") 381 } 382 } 383 384 // TestRequestSkipPeer checks if PeerSkip function will skip provided peer 385 // and not skip unknown one. 386 func TestRequestSkipPeer(t *testing.T) { 387 addr := make([]byte, 32) 388 peers := []enode.ID{ 389 enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8"), 390 enode.HexID("99d8594b52298567d2ca3f4c441a5ba0140ee9245e26460d01102a52773c73b9"), 391 } 392 393 peersToSkip := new(sync.Map) 394 peersToSkip.Store(peers[0].String(), time.Now()) 395 r := NewRequest(addr, false, peersToSkip) 396 397 if !r.SkipPeer(peers[0].String()) { 398 t.Errorf("peer not skipped") 399 } 400 401 if r.SkipPeer(peers[1].String()) { 402 t.Errorf("peer skipped") 403 } 404 } 405 406 // TestRequestSkipPeerExpired checks if a peer to skip is not skipped 407 // after RequestTimeout has passed. 408 func TestRequestSkipPeerExpired(t *testing.T) { 409 addr := make([]byte, 32) 410 peer := enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8") 411 412 // set RequestTimeout to a low value and reset it after the test 413 defer func(t time.Duration) { RequestTimeout = t }(RequestTimeout) 414 RequestTimeout = 250 * time.Millisecond 415 416 peersToSkip := new(sync.Map) 417 peersToSkip.Store(peer.String(), time.Now()) 418 r := NewRequest(addr, false, peersToSkip) 419 420 if !r.SkipPeer(peer.String()) { 421 t.Errorf("peer not skipped") 422 } 423 424 time.Sleep(500 * time.Millisecond) 425 426 if r.SkipPeer(peer.String()) { 427 t.Errorf("peer skipped") 428 } 429 } 430 431 // TestRequestSkipPeerPermanent checks if a peer to skip is not skipped 432 // after RequestTimeout is not skipped if it is set for a permanent skipping 433 // by value to peersToSkip map is not time.Duration. 434 func TestRequestSkipPeerPermanent(t *testing.T) { 435 addr := make([]byte, 32) 436 peer := enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8") 437 438 // set RequestTimeout to a low value and reset it after the test 439 defer func(t time.Duration) { RequestTimeout = t }(RequestTimeout) 440 RequestTimeout = 250 * time.Millisecond 441 442 peersToSkip := new(sync.Map) 443 peersToSkip.Store(peer.String(), true) 444 r := NewRequest(addr, false, peersToSkip) 445 446 if !r.SkipPeer(peer.String()) { 447 t.Errorf("peer not skipped") 448 } 449 450 time.Sleep(500 * time.Millisecond) 451 452 if !r.SkipPeer(peer.String()) { 453 t.Errorf("peer not skipped") 454 } 455 } 456 457 func TestFetcherMaxHopCount(t *testing.T) { 458 requester := newMockRequester() 459 addr := make([]byte, 32) 460 461 ctx, cancel := context.WithCancel(context.Background()) 462 defer cancel() 463 464 fetcher := NewFetcher(ctx, addr, requester.doRequest, true) 465 466 peersToSkip := &sync.Map{} 467 468 go fetcher.run(peersToSkip) 469 470 // if hopCount is already at max no request should be initiated 471 select { 472 case <-requester.requestC: 473 t.Fatalf("cancelled fetcher initiated request") 474 case <-time.After(200 * time.Millisecond): 475 } 476 }