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