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  }