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