github.com/oskarth/go-ethereum@v1.6.8-0.20191013093314-dac24a9d3494/swarm/storage/netstore_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 storage
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"crypto/rand"
    23  	"io/ioutil"
    24  	"sync"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/ethereum/go-ethereum/common"
    29  	"github.com/ethereum/go-ethereum/p2p/enode"
    30  	ch "github.com/ethereum/go-ethereum/swarm/chunk"
    31  )
    32  
    33  var sourcePeerID = enode.HexID("99d8594b52298567d2ca3f4c441a5ba0140ee9245e26460d01102a52773c73b9")
    34  
    35  type mockNetFetcher struct {
    36  	peers           *sync.Map
    37  	sources         []*enode.ID
    38  	peersPerRequest [][]Address
    39  	requestCalled   bool
    40  	offerCalled     bool
    41  	quit            <-chan struct{}
    42  	ctx             context.Context
    43  	hopCounts       []uint8
    44  }
    45  
    46  func (m *mockNetFetcher) Offer(ctx context.Context, source *enode.ID) {
    47  	m.offerCalled = true
    48  	m.sources = append(m.sources, source)
    49  }
    50  
    51  func (m *mockNetFetcher) Request(ctx context.Context, hopCount uint8) {
    52  	m.requestCalled = true
    53  	var peers []Address
    54  	m.peers.Range(func(key interface{}, _ interface{}) bool {
    55  		peers = append(peers, common.FromHex(key.(string)))
    56  		return true
    57  	})
    58  	m.peersPerRequest = append(m.peersPerRequest, peers)
    59  	m.hopCounts = append(m.hopCounts, hopCount)
    60  }
    61  
    62  type mockNetFetchFuncFactory struct {
    63  	fetcher *mockNetFetcher
    64  }
    65  
    66  func (m *mockNetFetchFuncFactory) newMockNetFetcher(ctx context.Context, _ Address, peers *sync.Map) NetFetcher {
    67  	m.fetcher.peers = peers
    68  	m.fetcher.quit = ctx.Done()
    69  	m.fetcher.ctx = ctx
    70  	return m.fetcher
    71  }
    72  
    73  func mustNewNetStore(t *testing.T) *NetStore {
    74  	netStore, _ := mustNewNetStoreWithFetcher(t)
    75  	return netStore
    76  }
    77  
    78  func mustNewNetStoreWithFetcher(t *testing.T) (*NetStore, *mockNetFetcher) {
    79  	t.Helper()
    80  
    81  	datadir, err := ioutil.TempDir("", "netstore")
    82  	if err != nil {
    83  		t.Fatal(err)
    84  	}
    85  	naddr := make([]byte, 32)
    86  	params := NewDefaultLocalStoreParams()
    87  	params.Init(datadir)
    88  	params.BaseKey = naddr
    89  	localStore, err := NewTestLocalStoreForAddr(params)
    90  	if err != nil {
    91  		t.Fatal(err)
    92  	}
    93  
    94  	fetcher := &mockNetFetcher{}
    95  	mockNetFetchFuncFactory := &mockNetFetchFuncFactory{
    96  		fetcher: fetcher,
    97  	}
    98  	netStore, err := NewNetStore(localStore, mockNetFetchFuncFactory.newMockNetFetcher)
    99  	if err != nil {
   100  		t.Fatal(err)
   101  	}
   102  	return netStore, fetcher
   103  }
   104  
   105  // TestNetStoreGetAndPut tests calling NetStore.Get which is blocked until the same chunk is Put.
   106  // After the Put there should no active fetchers, and the context created for the fetcher should
   107  // be cancelled.
   108  func TestNetStoreGetAndPut(t *testing.T) {
   109  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   110  
   111  	chunk := GenerateRandomChunk(ch.DefaultSize)
   112  
   113  	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
   114  	defer cancel()
   115  
   116  	c := make(chan struct{}) // this channel ensures that the gouroutine with the Put does not run earlier than the Get
   117  	go func() {
   118  		<-c                                // wait for the Get to be called
   119  		time.Sleep(200 * time.Millisecond) // and a little more so it is surely called
   120  
   121  		// check if netStore created a fetcher in the Get call for the unavailable chunk
   122  		if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil {
   123  			t.Fatal("Expected netStore to use a fetcher for the Get call")
   124  		}
   125  
   126  		err := netStore.Put(ctx, chunk)
   127  		if err != nil {
   128  			t.Fatalf("Expected no err got %v", err)
   129  		}
   130  	}()
   131  
   132  	close(c)
   133  	recChunk, err := netStore.Get(ctx, chunk.Address()) // this is blocked until the Put above is done
   134  	if err != nil {
   135  		t.Fatalf("Expected no err got %v", err)
   136  	}
   137  	// the retrieved chunk should be the same as what we Put
   138  	if !bytes.Equal(recChunk.Address(), chunk.Address()) || !bytes.Equal(recChunk.Data(), chunk.Data()) {
   139  		t.Fatalf("Different chunk received than what was put")
   140  	}
   141  	// the chunk is already available locally, so there should be no active fetchers waiting for it
   142  	if netStore.fetchers.Len() != 0 {
   143  		t.Fatal("Expected netStore to remove the fetcher after delivery")
   144  	}
   145  
   146  	// A fetcher was created when the Get was called (and the chunk was not available). The chunk
   147  	// was delivered with the Put call, so the fetcher should be cancelled now.
   148  	select {
   149  	case <-fetcher.ctx.Done():
   150  	default:
   151  		t.Fatal("Expected fetcher context to be cancelled")
   152  	}
   153  
   154  }
   155  
   156  // TestNetStoreGetAndPut tests calling NetStore.Put and then NetStore.Get.
   157  // After the Put the chunk is available locally, so the Get can just retrieve it from LocalStore,
   158  // there is no need to create fetchers.
   159  func TestNetStoreGetAfterPut(t *testing.T) {
   160  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   161  
   162  	chunk := GenerateRandomChunk(ch.DefaultSize)
   163  
   164  	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
   165  	defer cancel()
   166  
   167  	// First we Put the chunk, so the chunk will be available locally
   168  	err := netStore.Put(ctx, chunk)
   169  	if err != nil {
   170  		t.Fatalf("Expected no err got %v", err)
   171  	}
   172  
   173  	// Get should retrieve the chunk from LocalStore, without creating fetcher
   174  	recChunk, err := netStore.Get(ctx, chunk.Address())
   175  	if err != nil {
   176  		t.Fatalf("Expected no err got %v", err)
   177  	}
   178  	// the retrieved chunk should be the same as what we Put
   179  	if !bytes.Equal(recChunk.Address(), chunk.Address()) || !bytes.Equal(recChunk.Data(), chunk.Data()) {
   180  		t.Fatalf("Different chunk received than what was put")
   181  	}
   182  	// no fetcher offer or request should be created for a locally available chunk
   183  	if fetcher.offerCalled || fetcher.requestCalled {
   184  		t.Fatal("NetFetcher.offerCalled or requestCalled not expected to be called")
   185  	}
   186  	// no fetchers should be created for a locally available chunk
   187  	if netStore.fetchers.Len() != 0 {
   188  		t.Fatal("Expected netStore to not have fetcher")
   189  	}
   190  
   191  }
   192  
   193  // TestNetStoreGetTimeout tests a Get call for an unavailable chunk and waits for timeout
   194  func TestNetStoreGetTimeout(t *testing.T) {
   195  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   196  
   197  	chunk := GenerateRandomChunk(ch.DefaultSize)
   198  
   199  	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
   200  	defer cancel()
   201  
   202  	c := make(chan struct{}) // this channel ensures that the gouroutine does not run earlier than the Get
   203  	go func() {
   204  		<-c                                // wait for the Get to be called
   205  		time.Sleep(200 * time.Millisecond) // and a little more so it is surely called
   206  
   207  		// check if netStore created a fetcher in the Get call for the unavailable chunk
   208  		if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil {
   209  			t.Fatal("Expected netStore to use a fetcher for the Get call")
   210  		}
   211  	}()
   212  
   213  	close(c)
   214  	// We call Get on this chunk, which is not in LocalStore. We don't Put it at all, so there will
   215  	// be a timeout
   216  	_, err := netStore.Get(ctx, chunk.Address())
   217  
   218  	// Check if the timeout happened
   219  	if err != context.DeadlineExceeded {
   220  		t.Fatalf("Expected context.DeadLineExceeded err got %v", err)
   221  	}
   222  
   223  	// A fetcher was created, check if it has been removed after timeout
   224  	if netStore.fetchers.Len() != 0 {
   225  		t.Fatal("Expected netStore to remove the fetcher after timeout")
   226  	}
   227  
   228  	// Check if the fetcher context has been cancelled after the timeout
   229  	select {
   230  	case <-fetcher.ctx.Done():
   231  	default:
   232  		t.Fatal("Expected fetcher context to be cancelled")
   233  	}
   234  }
   235  
   236  // TestNetStoreGetCancel tests a Get call for an unavailable chunk, then cancels the context and checks
   237  // the errors
   238  func TestNetStoreGetCancel(t *testing.T) {
   239  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   240  
   241  	chunk := GenerateRandomChunk(ch.DefaultSize)
   242  
   243  	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
   244  
   245  	c := make(chan struct{}) // this channel ensures that the gouroutine with the cancel does not run earlier than the Get
   246  	go func() {
   247  		<-c                                // wait for the Get to be called
   248  		time.Sleep(200 * time.Millisecond) // and a little more so it is surely called
   249  		// check if netStore created a fetcher in the Get call for the unavailable chunk
   250  		if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil {
   251  			t.Fatal("Expected netStore to use a fetcher for the Get call")
   252  		}
   253  		cancel()
   254  	}()
   255  
   256  	close(c)
   257  	// We call Get with an unavailable chunk, so it will create a fetcher and wait for delivery
   258  	_, err := netStore.Get(ctx, chunk.Address())
   259  
   260  	// After the context is cancelled above Get should return with an error
   261  	if err != context.Canceled {
   262  		t.Fatalf("Expected context.Canceled err got %v", err)
   263  	}
   264  
   265  	// A fetcher was created, check if it has been removed after cancel
   266  	if netStore.fetchers.Len() != 0 {
   267  		t.Fatal("Expected netStore to remove the fetcher after cancel")
   268  	}
   269  
   270  	// Check if the fetcher context has been cancelled after the request context cancel
   271  	select {
   272  	case <-fetcher.ctx.Done():
   273  	default:
   274  		t.Fatal("Expected fetcher context to be cancelled")
   275  	}
   276  }
   277  
   278  // TestNetStoreMultipleGetAndPut tests four Get calls for the same unavailable chunk. The chunk is
   279  // delivered with a Put, we have to make sure all Get calls return, and they use a single fetcher
   280  // for the chunk retrieval
   281  func TestNetStoreMultipleGetAndPut(t *testing.T) {
   282  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   283  
   284  	chunk := GenerateRandomChunk(ch.DefaultSize)
   285  
   286  	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
   287  	defer cancel()
   288  
   289  	go func() {
   290  		// sleep to make sure Put is called after all the Get
   291  		time.Sleep(500 * time.Millisecond)
   292  		// check if netStore created exactly one fetcher for all Get calls
   293  		if netStore.fetchers.Len() != 1 {
   294  			t.Fatal("Expected netStore to use one fetcher for all Get calls")
   295  		}
   296  		err := netStore.Put(ctx, chunk)
   297  		if err != nil {
   298  			t.Fatalf("Expected no err got %v", err)
   299  		}
   300  	}()
   301  
   302  	// call Get 4 times for the same unavailable chunk. The calls will be blocked until the Put above.
   303  	getWG := sync.WaitGroup{}
   304  	for i := 0; i < 4; i++ {
   305  		getWG.Add(1)
   306  		go func() {
   307  			defer getWG.Done()
   308  			recChunk, err := netStore.Get(ctx, chunk.Address())
   309  			if err != nil {
   310  				t.Fatalf("Expected no err got %v", err)
   311  			}
   312  			if !bytes.Equal(recChunk.Address(), chunk.Address()) || !bytes.Equal(recChunk.Data(), chunk.Data()) {
   313  				t.Fatalf("Different chunk received than what was put")
   314  			}
   315  		}()
   316  	}
   317  
   318  	finishedC := make(chan struct{})
   319  	go func() {
   320  		getWG.Wait()
   321  		close(finishedC)
   322  	}()
   323  
   324  	// The Get calls should return after Put, so no timeout expected
   325  	select {
   326  	case <-finishedC:
   327  	case <-time.After(1 * time.Second):
   328  		t.Fatalf("Timeout waiting for Get calls to return")
   329  	}
   330  
   331  	// A fetcher was created, check if it has been removed after cancel
   332  	if netStore.fetchers.Len() != 0 {
   333  		t.Fatal("Expected netStore to remove the fetcher after delivery")
   334  	}
   335  
   336  	// A fetcher was created, check if it has been removed after delivery
   337  	select {
   338  	case <-fetcher.ctx.Done():
   339  	default:
   340  		t.Fatal("Expected fetcher context to be cancelled")
   341  	}
   342  
   343  }
   344  
   345  // TestNetStoreFetchFuncTimeout tests a FetchFunc call for an unavailable chunk and waits for timeout
   346  func TestNetStoreFetchFuncTimeout(t *testing.T) {
   347  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   348  
   349  	chunk := GenerateRandomChunk(ch.DefaultSize)
   350  
   351  	ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
   352  	defer cancel()
   353  
   354  	// FetchFunc is called for an unavaible chunk, so the returned wait function should not be nil
   355  	wait := netStore.FetchFunc(ctx, chunk.Address())
   356  	if wait == nil {
   357  		t.Fatal("Expected wait function to be not nil")
   358  	}
   359  
   360  	// There should an active fetcher for the chunk after the FetchFunc call
   361  	if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil {
   362  		t.Fatalf("Expected netStore to have one fetcher for the requested chunk")
   363  	}
   364  
   365  	// wait function should timeout because we don't deliver the chunk with a Put
   366  	err := wait(ctx)
   367  	if err != context.DeadlineExceeded {
   368  		t.Fatalf("Expected context.DeadLineExceeded err got %v", err)
   369  	}
   370  
   371  	// the fetcher should be removed after timeout
   372  	if netStore.fetchers.Len() != 0 {
   373  		t.Fatal("Expected netStore to remove the fetcher after timeout")
   374  	}
   375  
   376  	// the fetcher context should be cancelled after timeout
   377  	select {
   378  	case <-fetcher.ctx.Done():
   379  	default:
   380  		t.Fatal("Expected fetcher context to be cancelled")
   381  	}
   382  }
   383  
   384  // TestNetStoreFetchFuncAfterPut tests that the FetchFunc should return nil for a locally available chunk
   385  func TestNetStoreFetchFuncAfterPut(t *testing.T) {
   386  	netStore := mustNewNetStore(t)
   387  
   388  	chunk := GenerateRandomChunk(ch.DefaultSize)
   389  
   390  	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
   391  	defer cancel()
   392  
   393  	// We deliver the created the chunk with a Put
   394  	err := netStore.Put(ctx, chunk)
   395  	if err != nil {
   396  		t.Fatalf("Expected no err got %v", err)
   397  	}
   398  
   399  	// FetchFunc should return nil, because the chunk is available locally, no need to fetch it
   400  	wait := netStore.FetchFunc(ctx, chunk.Address())
   401  	if wait != nil {
   402  		t.Fatal("Expected wait to be nil")
   403  	}
   404  
   405  	// No fetchers should be created at all
   406  	if netStore.fetchers.Len() != 0 {
   407  		t.Fatal("Expected netStore to not have fetcher")
   408  	}
   409  }
   410  
   411  // TestNetStoreGetCallsRequest tests if Get created a request on the NetFetcher for an unavailable chunk
   412  func TestNetStoreGetCallsRequest(t *testing.T) {
   413  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   414  
   415  	chunk := GenerateRandomChunk(ch.DefaultSize)
   416  
   417  	ctx := context.WithValue(context.Background(), "hopcount", uint8(5))
   418  	ctx, cancel := context.WithTimeout(ctx, 200*time.Millisecond)
   419  	defer cancel()
   420  
   421  	// We call get for a not available chunk, it will timeout because the chunk is not delivered
   422  	_, err := netStore.Get(ctx, chunk.Address())
   423  
   424  	if err != context.DeadlineExceeded {
   425  		t.Fatalf("Expected context.DeadlineExceeded err got %v", err)
   426  	}
   427  
   428  	// NetStore should call NetFetcher.Request and wait for the chunk
   429  	if !fetcher.requestCalled {
   430  		t.Fatal("Expected NetFetcher.Request to be called")
   431  	}
   432  
   433  	if fetcher.hopCounts[0] != 5 {
   434  		t.Fatalf("Expected NetFetcher.Request be called with hopCount 5, got %v", fetcher.hopCounts[0])
   435  	}
   436  }
   437  
   438  // TestNetStoreGetCallsOffer tests if Get created a request on the NetFetcher for an unavailable chunk
   439  // in case of a source peer provided in the context.
   440  func TestNetStoreGetCallsOffer(t *testing.T) {
   441  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   442  
   443  	chunk := GenerateRandomChunk(ch.DefaultSize)
   444  
   445  	//  If a source peer is added to the context, NetStore will handle it as an offer
   446  	ctx := context.WithValue(context.Background(), "source", sourcePeerID.String())
   447  	ctx, cancel := context.WithTimeout(ctx, 200*time.Millisecond)
   448  	defer cancel()
   449  
   450  	// We call get for a not available chunk, it will timeout because the chunk is not delivered
   451  	chunk, err := netStore.Get(ctx, chunk.Address())
   452  
   453  	if err != context.DeadlineExceeded {
   454  		t.Fatalf("Expect error %v got %v", context.DeadlineExceeded, err)
   455  	}
   456  
   457  	// NetStore should call NetFetcher.Offer with the source peer
   458  	if !fetcher.offerCalled {
   459  		t.Fatal("Expected NetFetcher.Request to be called")
   460  	}
   461  
   462  	if len(fetcher.sources) != 1 {
   463  		t.Fatalf("Expected fetcher sources length 1 got %v", len(fetcher.sources))
   464  	}
   465  
   466  	if fetcher.sources[0].String() != sourcePeerID.String() {
   467  		t.Fatalf("Expected fetcher source %v got %v", sourcePeerID, fetcher.sources[0])
   468  	}
   469  
   470  }
   471  
   472  // TestNetStoreFetcherCountPeers tests multiple NetStore.Get calls with peer in the context.
   473  // There is no Put call, so the Get calls timeout
   474  func TestNetStoreFetcherCountPeers(t *testing.T) {
   475  
   476  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   477  
   478  	addr := randomAddr()
   479  	peers := []string{randomAddr().Hex(), randomAddr().Hex(), randomAddr().Hex()}
   480  
   481  	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
   482  	defer cancel()
   483  	errC := make(chan error)
   484  	nrGets := 3
   485  
   486  	// Call Get 3 times with a peer in context
   487  	for i := 0; i < nrGets; i++ {
   488  		peer := peers[i]
   489  		go func() {
   490  			ctx := context.WithValue(ctx, "peer", peer)
   491  			_, err := netStore.Get(ctx, addr)
   492  			errC <- err
   493  		}()
   494  	}
   495  
   496  	// All 3 Get calls should timeout
   497  	for i := 0; i < nrGets; i++ {
   498  		err := <-errC
   499  		if err != context.DeadlineExceeded {
   500  			t.Fatalf("Expected \"%v\" error got \"%v\"", context.DeadlineExceeded, err)
   501  		}
   502  	}
   503  
   504  	// fetcher should be closed after timeout
   505  	select {
   506  	case <-fetcher.quit:
   507  	case <-time.After(3 * time.Second):
   508  		t.Fatalf("mockNetFetcher not closed after timeout")
   509  	}
   510  
   511  	// All 3 peers should be given to NetFetcher after the 3 Get calls
   512  	if len(fetcher.peersPerRequest) != nrGets {
   513  		t.Fatalf("Expected 3 got %v", len(fetcher.peersPerRequest))
   514  	}
   515  
   516  	for i, peers := range fetcher.peersPerRequest {
   517  		if len(peers) < i+1 {
   518  			t.Fatalf("Expected at least %v got %v", i+1, len(peers))
   519  		}
   520  	}
   521  }
   522  
   523  // TestNetStoreFetchFuncCalledMultipleTimes calls the wait function given by FetchFunc three times,
   524  // and checks there is still exactly one fetcher for one chunk. Afthe chunk is delivered, it checks
   525  // if the fetcher is closed.
   526  func TestNetStoreFetchFuncCalledMultipleTimes(t *testing.T) {
   527  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   528  
   529  	chunk := GenerateRandomChunk(ch.DefaultSize)
   530  
   531  	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
   532  	defer cancel()
   533  
   534  	// FetchFunc should return a non-nil wait function, because the chunk is not available
   535  	wait := netStore.FetchFunc(ctx, chunk.Address())
   536  	if wait == nil {
   537  		t.Fatal("Expected wait function to be not nil")
   538  	}
   539  
   540  	// There should be exactly one fetcher for the chunk
   541  	if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil {
   542  		t.Fatalf("Expected netStore to have one fetcher for the requested chunk")
   543  	}
   544  
   545  	// Call wait three times parallelly
   546  	wg := sync.WaitGroup{}
   547  	for i := 0; i < 3; i++ {
   548  		wg.Add(1)
   549  		go func() {
   550  			err := wait(ctx)
   551  			if err != nil {
   552  				t.Fatalf("Expected no err got %v", err)
   553  			}
   554  			wg.Done()
   555  		}()
   556  	}
   557  
   558  	// sleep a little so the wait functions are called above
   559  	time.Sleep(100 * time.Millisecond)
   560  
   561  	// there should be still only one fetcher, because all wait calls are for the same chunk
   562  	if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil {
   563  		t.Fatal("Expected netStore to have one fetcher for the requested chunk")
   564  	}
   565  
   566  	// Deliver the chunk with a Put
   567  	err := netStore.Put(ctx, chunk)
   568  	if err != nil {
   569  		t.Fatalf("Expected no err got %v", err)
   570  	}
   571  
   572  	// wait until all wait calls return (because the chunk is delivered)
   573  	wg.Wait()
   574  
   575  	// There should be no more fetchers for the delivered chunk
   576  	if netStore.fetchers.Len() != 0 {
   577  		t.Fatal("Expected netStore to remove the fetcher after delivery")
   578  	}
   579  
   580  	// The context for the fetcher should be cancelled after delivery
   581  	select {
   582  	case <-fetcher.ctx.Done():
   583  	default:
   584  		t.Fatal("Expected fetcher context to be cancelled")
   585  	}
   586  }
   587  
   588  // TestNetStoreFetcherLifeCycleWithTimeout is similar to TestNetStoreFetchFuncCalledMultipleTimes,
   589  // the only difference is that we don't deilver the chunk, just wait for timeout
   590  func TestNetStoreFetcherLifeCycleWithTimeout(t *testing.T) {
   591  	netStore, fetcher := mustNewNetStoreWithFetcher(t)
   592  
   593  	chunk := GenerateRandomChunk(ch.DefaultSize)
   594  
   595  	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
   596  	defer cancel()
   597  
   598  	// FetchFunc should return a non-nil wait function, because the chunk is not available
   599  	wait := netStore.FetchFunc(ctx, chunk.Address())
   600  	if wait == nil {
   601  		t.Fatal("Expected wait function to be not nil")
   602  	}
   603  
   604  	// There should be exactly one fetcher for the chunk
   605  	if netStore.fetchers.Len() != 1 || netStore.getFetcher(chunk.Address()) == nil {
   606  		t.Fatalf("Expected netStore to have one fetcher for the requested chunk")
   607  	}
   608  
   609  	// Call wait three times parallelly
   610  	wg := sync.WaitGroup{}
   611  	for i := 0; i < 3; i++ {
   612  		wg.Add(1)
   613  		go func() {
   614  			defer wg.Done()
   615  			rctx, rcancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
   616  			defer rcancel()
   617  			err := wait(rctx)
   618  			if err != context.DeadlineExceeded {
   619  				t.Fatalf("Expected err %v got %v", context.DeadlineExceeded, err)
   620  			}
   621  		}()
   622  	}
   623  
   624  	// wait until all wait calls timeout
   625  	wg.Wait()
   626  
   627  	// There should be no more fetchers after timeout
   628  	if netStore.fetchers.Len() != 0 {
   629  		t.Fatal("Expected netStore to remove the fetcher after delivery")
   630  	}
   631  
   632  	// The context for the fetcher should be cancelled after timeout
   633  	select {
   634  	case <-fetcher.ctx.Done():
   635  	default:
   636  		t.Fatal("Expected fetcher context to be cancelled")
   637  	}
   638  }
   639  
   640  func randomAddr() Address {
   641  	addr := make([]byte, 32)
   642  	rand.Read(addr)
   643  	return Address(addr)
   644  }