github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/swarm/network/fetcher_test.go (about)

     1  // Copyright 2018 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum 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 go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY 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 go-ethereum 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/FusionFoundation/efsn/p2p/discover"
    26  )
    27  
    28  var requestedPeerID = discover.MustHexID("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439")
    29  var sourcePeerID = discover.MustHexID("2dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439")
    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  	ctr       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) (*discover.NodeID, chan struct{}, error) {
    49  	waitTime := time.Duration(0)
    50  	if m.ctr < len(m.waitTimes) {
    51  		waitTime = m.waitTimes[m.ctr]
    52  		m.ctr++
    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  	fetcher := NewFetcher(addr, requester.doRequest, true)
    73  
    74  	peers := []string{"a", "b", "c", "d"}
    75  	peersToSkip := &sync.Map{}
    76  	for _, p := range peers {
    77  		peersToSkip.Store(p, time.Now())
    78  	}
    79  
    80  	ctx, cancel := context.WithCancel(context.Background())
    81  	defer cancel()
    82  
    83  	go fetcher.run(ctx, peersToSkip)
    84  
    85  	rctx := context.Background()
    86  	fetcher.Request(rctx)
    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  		// fetch should trigger a request, if it doesn't happen in time, test should fail
   104  	case <-time.After(200 * time.Millisecond):
   105  		t.Fatalf("fetch timeout")
   106  	}
   107  }
   108  
   109  // TestCancelStopsFetcher tests that a cancelled fetcher does not initiate further requests even if its fetch function is called
   110  func TestFetcherCancelStopsFetcher(t *testing.T) {
   111  	requester := newMockRequester()
   112  	addr := make([]byte, 32)
   113  	fetcher := NewFetcher(addr, requester.doRequest, true)
   114  
   115  	peersToSkip := &sync.Map{}
   116  
   117  	ctx, cancel := context.WithCancel(context.Background())
   118  
   119  	// we start the fetcher, and then we immediately cancel the context
   120  	go fetcher.run(ctx, peersToSkip)
   121  	cancel()
   122  
   123  	rctx, rcancel := context.WithTimeout(ctx, 100*time.Millisecond)
   124  	defer rcancel()
   125  	// we call Request with an active context
   126  	fetcher.Request(rctx)
   127  
   128  	// fetcher should not initiate request, we can only check by waiting a bit and making sure no request is happening
   129  	select {
   130  	case <-requester.requestC:
   131  		t.Fatalf("cancelled fetcher initiated request")
   132  	case <-time.After(200 * time.Millisecond):
   133  	}
   134  }
   135  
   136  // TestFetchCancelStopsRequest tests that calling a Request function with a cancelled context does not initiate a request
   137  func TestFetcherCancelStopsRequest(t *testing.T) {
   138  	requester := newMockRequester(100 * time.Millisecond)
   139  	addr := make([]byte, 32)
   140  	fetcher := NewFetcher(addr, requester.doRequest, true)
   141  
   142  	peersToSkip := &sync.Map{}
   143  
   144  	ctx, cancel := context.WithCancel(context.Background())
   145  	defer cancel()
   146  
   147  	// we start the fetcher with an active context
   148  	go fetcher.run(ctx, peersToSkip)
   149  
   150  	rctx, rcancel := context.WithCancel(context.Background())
   151  	rcancel()
   152  
   153  	// we call Request with a cancelled context
   154  	fetcher.Request(rctx)
   155  
   156  	// fetcher should not initiate request, we can only check by waiting a bit and making sure no request is happening
   157  	select {
   158  	case <-requester.requestC:
   159  		t.Fatalf("cancelled fetch function initiated request")
   160  	case <-time.After(200 * time.Millisecond):
   161  	}
   162  
   163  	// if there is another Request with active context, there should be a request, because the fetcher itself is not cancelled
   164  	rctx = context.Background()
   165  	fetcher.Request(rctx)
   166  
   167  	select {
   168  	case <-requester.requestC:
   169  	case <-time.After(200 * time.Millisecond):
   170  		t.Fatalf("expected request")
   171  	}
   172  }
   173  
   174  // TestOfferUsesSource tests Fetcher Offer behavior.
   175  // In this case there should be 1 (and only one) request initiated from the source peer, and the
   176  // source nodeid should appear in the peersToSkip map.
   177  func TestFetcherOfferUsesSource(t *testing.T) {
   178  	requester := newMockRequester(100 * time.Millisecond)
   179  	addr := make([]byte, 32)
   180  	fetcher := NewFetcher(addr, requester.doRequest, true)
   181  
   182  	peersToSkip := &sync.Map{}
   183  
   184  	ctx, cancel := context.WithCancel(context.Background())
   185  	defer cancel()
   186  
   187  	// start the fetcher
   188  	go fetcher.run(ctx, peersToSkip)
   189  
   190  	rctx := context.Background()
   191  	// call the Offer function with the source peer
   192  	fetcher.Offer(rctx, &sourcePeerID)
   193  
   194  	// fetcher should not initiate request
   195  	select {
   196  	case <-requester.requestC:
   197  		t.Fatalf("fetcher initiated request")
   198  	case <-time.After(200 * time.Millisecond):
   199  	}
   200  
   201  	// call Request after the Offer
   202  	rctx = context.Background()
   203  	fetcher.Request(rctx)
   204  
   205  	// there should be exactly 1 request coming from fetcher
   206  	var request *Request
   207  	select {
   208  	case request = <-requester.requestC:
   209  		if *request.Source != sourcePeerID {
   210  			t.Fatalf("Expected source id %v got %v", sourcePeerID, request.Source)
   211  		}
   212  	case <-time.After(200 * time.Millisecond):
   213  		t.Fatalf("fetcher did not initiate request")
   214  	}
   215  
   216  	select {
   217  	case <-requester.requestC:
   218  		t.Fatalf("Fetcher number of requests expected 1 got 2")
   219  	case <-time.After(200 * time.Millisecond):
   220  	}
   221  
   222  	// source peer should be added to peersToSkip eventually
   223  	time.Sleep(100 * time.Millisecond)
   224  	if _, ok := request.peersToSkip.Load(sourcePeerID.String()); !ok {
   225  		t.Fatalf("SourcePeerId not added to peersToSkip")
   226  	}
   227  }
   228  
   229  func TestFetcherOfferAfterRequestUsesSourceFromContext(t *testing.T) {
   230  	requester := newMockRequester(100 * time.Millisecond)
   231  	addr := make([]byte, 32)
   232  	fetcher := NewFetcher(addr, requester.doRequest, true)
   233  
   234  	peersToSkip := &sync.Map{}
   235  
   236  	ctx, cancel := context.WithCancel(context.Background())
   237  	defer cancel()
   238  
   239  	// start the fetcher
   240  	go fetcher.run(ctx, peersToSkip)
   241  
   242  	// call Request first
   243  	rctx := context.Background()
   244  	fetcher.Request(rctx)
   245  
   246  	// there should be a request coming from fetcher
   247  	var request *Request
   248  	select {
   249  	case request = <-requester.requestC:
   250  		if request.Source != nil {
   251  			t.Fatalf("Incorrect source peer id, expected nil got %v", request.Source)
   252  		}
   253  	case <-time.After(200 * time.Millisecond):
   254  		t.Fatalf("fetcher did not initiate request")
   255  	}
   256  
   257  	// after the Request call Offer
   258  	fetcher.Offer(context.Background(), &sourcePeerID)
   259  
   260  	// there should be a request coming from fetcher
   261  	select {
   262  	case request = <-requester.requestC:
   263  		if *request.Source != sourcePeerID {
   264  			t.Fatalf("Incorrect source peer id, expected %v got %v", sourcePeerID, request.Source)
   265  		}
   266  	case <-time.After(200 * time.Millisecond):
   267  		t.Fatalf("fetcher did not initiate request")
   268  	}
   269  
   270  	// source peer should be added to peersToSkip eventually
   271  	time.Sleep(100 * time.Millisecond)
   272  	if _, ok := request.peersToSkip.Load(sourcePeerID.String()); !ok {
   273  		t.Fatalf("SourcePeerId not added to peersToSkip")
   274  	}
   275  }
   276  
   277  // TestFetcherRetryOnTimeout tests that fetch retries after searchTimeOut has passed
   278  func TestFetcherRetryOnTimeout(t *testing.T) {
   279  	requester := newMockRequester()
   280  	addr := make([]byte, 32)
   281  	fetcher := NewFetcher(addr, requester.doRequest, true)
   282  
   283  	peersToSkip := &sync.Map{}
   284  
   285  	// set searchTimeOut to low value so the test is quicker
   286  	defer func(t time.Duration) {
   287  		searchTimeout = t
   288  	}(searchTimeout)
   289  	searchTimeout = 250 * time.Millisecond
   290  
   291  	ctx, cancel := context.WithCancel(context.Background())
   292  	defer cancel()
   293  
   294  	// start the fetcher
   295  	go fetcher.run(ctx, peersToSkip)
   296  
   297  	// call the fetch function with an active context
   298  	rctx := context.Background()
   299  	fetcher.Request(rctx)
   300  
   301  	// after 100ms the first request should be initiated
   302  	time.Sleep(100 * time.Millisecond)
   303  
   304  	select {
   305  	case <-requester.requestC:
   306  	default:
   307  		t.Fatalf("fetch did not initiate request")
   308  	}
   309  
   310  	// after another 100ms no new request should be initiated, because search timeout is 250ms
   311  	time.Sleep(100 * time.Millisecond)
   312  
   313  	select {
   314  	case <-requester.requestC:
   315  		t.Fatalf("unexpected request from fetcher")
   316  	default:
   317  	}
   318  
   319  	// after another 300ms search timeout is over, there should be a new request
   320  	time.Sleep(300 * time.Millisecond)
   321  
   322  	select {
   323  	case <-requester.requestC:
   324  	default:
   325  		t.Fatalf("fetch did not retry request")
   326  	}
   327  }
   328  
   329  // TestFetcherFactory creates a FetcherFactory and checks if the factory really creates and starts
   330  // a Fetcher when it return a fetch function. We test the fetching functionality just by checking if
   331  // a request is initiated when the fetch function is called
   332  func TestFetcherFactory(t *testing.T) {
   333  	requester := newMockRequester(100 * time.Millisecond)
   334  	addr := make([]byte, 32)
   335  	fetcherFactory := NewFetcherFactory(requester.doRequest, false)
   336  
   337  	peersToSkip := &sync.Map{}
   338  
   339  	fetcher := fetcherFactory.New(context.Background(), addr, peersToSkip)
   340  
   341  	fetcher.Request(context.Background())
   342  
   343  	// check if the created fetchFunction really starts a fetcher and initiates a request
   344  	select {
   345  	case <-requester.requestC:
   346  	case <-time.After(200 * time.Millisecond):
   347  		t.Fatalf("fetch timeout")
   348  	}
   349  
   350  }
   351  
   352  func TestFetcherRequestQuitRetriesRequest(t *testing.T) {
   353  	requester := newMockRequester()
   354  	addr := make([]byte, 32)
   355  	fetcher := NewFetcher(addr, requester.doRequest, true)
   356  
   357  	// make sure searchTimeout is long so it is sure the request is not retried because of timeout
   358  	defer func(t time.Duration) {
   359  		searchTimeout = t
   360  	}(searchTimeout)
   361  	searchTimeout = 10 * time.Second
   362  
   363  	peersToSkip := &sync.Map{}
   364  
   365  	ctx, cancel := context.WithCancel(context.Background())
   366  	defer cancel()
   367  
   368  	go fetcher.run(ctx, peersToSkip)
   369  
   370  	rctx := context.Background()
   371  	fetcher.Request(rctx)
   372  
   373  	select {
   374  	case <-requester.requestC:
   375  	case <-time.After(200 * time.Millisecond):
   376  		t.Fatalf("request is not initiated")
   377  	}
   378  
   379  	close(requester.quitC)
   380  
   381  	select {
   382  	case <-requester.requestC:
   383  	case <-time.After(200 * time.Millisecond):
   384  		t.Fatalf("request is not initiated after failed request")
   385  	}
   386  }
   387  
   388  // TestRequestSkipPeer checks if PeerSkip function will skip provided peer
   389  // and not skip unknown one.
   390  func TestRequestSkipPeer(t *testing.T) {
   391  	addr := make([]byte, 32)
   392  	peers := []discover.NodeID{
   393  		discover.MustHexID("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
   394  		discover.MustHexID("2dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
   395  	}
   396  
   397  	peersToSkip := new(sync.Map)
   398  	peersToSkip.Store(peers[0].String(), time.Now())
   399  	r := NewRequest(addr, false, peersToSkip)
   400  
   401  	if !r.SkipPeer(peers[0].String()) {
   402  		t.Errorf("peer not skipped")
   403  	}
   404  
   405  	if r.SkipPeer(peers[1].String()) {
   406  		t.Errorf("peer skipped")
   407  	}
   408  }
   409  
   410  // TestRequestSkipPeerExpired checks if a peer to skip is not skipped
   411  // after RequestTimeout has passed.
   412  func TestRequestSkipPeerExpired(t *testing.T) {
   413  	addr := make([]byte, 32)
   414  	peer := discover.MustHexID("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439")
   415  
   416  	// set RequestTimeout to a low value and reset it after the test
   417  	defer func(t time.Duration) { RequestTimeout = t }(RequestTimeout)
   418  	RequestTimeout = 250 * time.Millisecond
   419  
   420  	peersToSkip := new(sync.Map)
   421  	peersToSkip.Store(peer.String(), time.Now())
   422  	r := NewRequest(addr, false, peersToSkip)
   423  
   424  	if !r.SkipPeer(peer.String()) {
   425  		t.Errorf("peer not skipped")
   426  	}
   427  
   428  	time.Sleep(500 * time.Millisecond)
   429  
   430  	if r.SkipPeer(peer.String()) {
   431  		t.Errorf("peer skipped")
   432  	}
   433  }
   434  
   435  // TestRequestSkipPeerPermanent checks if a peer to skip is not skipped
   436  // after RequestTimeout is not skipped if it is set for a permanent skipping
   437  // by value to peersToSkip map is not time.Duration.
   438  func TestRequestSkipPeerPermanent(t *testing.T) {
   439  	addr := make([]byte, 32)
   440  	peer := discover.MustHexID("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439")
   441  
   442  	// set RequestTimeout to a low value and reset it after the test
   443  	defer func(t time.Duration) { RequestTimeout = t }(RequestTimeout)
   444  	RequestTimeout = 250 * time.Millisecond
   445  
   446  	peersToSkip := new(sync.Map)
   447  	peersToSkip.Store(peer.String(), true)
   448  	r := NewRequest(addr, false, peersToSkip)
   449  
   450  	if !r.SkipPeer(peer.String()) {
   451  		t.Errorf("peer not skipped")
   452  	}
   453  
   454  	time.Sleep(500 * time.Millisecond)
   455  
   456  	if !r.SkipPeer(peer.String()) {
   457  		t.Errorf("peer not skipped")
   458  	}
   459  }