github.com/luckypickle/go-ethereum-vet@v1.14.2/les/distributor_test.go (about)

     1  // Copyright 2017 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 light implements on-demand retrieval capable state and chain objects
    18  // for the Ethereum Light Client.
    19  package les
    20  
    21  import (
    22  	"math/rand"
    23  	"sync"
    24  	"testing"
    25  	"time"
    26  )
    27  
    28  type testDistReq struct {
    29  	cost, procTime, order uint64
    30  	canSendTo             map[*testDistPeer]struct{}
    31  }
    32  
    33  func (r *testDistReq) getCost(dp distPeer) uint64 {
    34  	return r.cost
    35  }
    36  
    37  func (r *testDistReq) canSend(dp distPeer) bool {
    38  	_, ok := r.canSendTo[dp.(*testDistPeer)]
    39  	return ok
    40  }
    41  
    42  func (r *testDistReq) request(dp distPeer) func() {
    43  	return func() { dp.(*testDistPeer).send(r) }
    44  }
    45  
    46  type testDistPeer struct {
    47  	sent    []*testDistReq
    48  	sumCost uint64
    49  	lock    sync.RWMutex
    50  }
    51  
    52  func (p *testDistPeer) send(r *testDistReq) {
    53  	p.lock.Lock()
    54  	defer p.lock.Unlock()
    55  
    56  	p.sent = append(p.sent, r)
    57  	p.sumCost += r.cost
    58  }
    59  
    60  func (p *testDistPeer) worker(t *testing.T, checkOrder bool, stop chan struct{}) {
    61  	var last uint64
    62  	for {
    63  		wait := time.Millisecond
    64  		p.lock.Lock()
    65  		if len(p.sent) > 0 {
    66  			rq := p.sent[0]
    67  			wait = time.Duration(rq.procTime)
    68  			p.sumCost -= rq.cost
    69  			if checkOrder {
    70  				if rq.order <= last {
    71  					t.Errorf("Requests processed in wrong order")
    72  				}
    73  				last = rq.order
    74  			}
    75  			p.sent = p.sent[1:]
    76  		}
    77  		p.lock.Unlock()
    78  		select {
    79  		case <-stop:
    80  			return
    81  		case <-time.After(wait):
    82  		}
    83  	}
    84  }
    85  
    86  const (
    87  	testDistBufLimit       = 10000000
    88  	testDistMaxCost        = 1000000
    89  	testDistPeerCount      = 5
    90  	testDistReqCount       = 50000
    91  	testDistMaxResendCount = 3
    92  )
    93  
    94  func (p *testDistPeer) waitBefore(cost uint64) (time.Duration, float64) {
    95  	p.lock.RLock()
    96  	sumCost := p.sumCost + cost
    97  	p.lock.RUnlock()
    98  	if sumCost < testDistBufLimit {
    99  		return 0, float64(testDistBufLimit-sumCost) / float64(testDistBufLimit)
   100  	}
   101  	return time.Duration(sumCost - testDistBufLimit), 0
   102  }
   103  
   104  func (p *testDistPeer) canQueue() bool {
   105  	return true
   106  }
   107  
   108  func (p *testDistPeer) queueSend(f func()) {
   109  	f()
   110  }
   111  
   112  func TestRequestDistributor(t *testing.T) {
   113  	testRequestDistributor(t, false)
   114  }
   115  
   116  func TestRequestDistributorResend(t *testing.T) {
   117  	testRequestDistributor(t, true)
   118  }
   119  
   120  func testRequestDistributor(t *testing.T, resend bool) {
   121  	stop := make(chan struct{})
   122  	defer close(stop)
   123  
   124  	dist := newRequestDistributor(nil, stop)
   125  	var peers [testDistPeerCount]*testDistPeer
   126  	for i := range peers {
   127  		peers[i] = &testDistPeer{}
   128  		go peers[i].worker(t, !resend, stop)
   129  		dist.registerTestPeer(peers[i])
   130  	}
   131  
   132  	var wg sync.WaitGroup
   133  
   134  	for i := 1; i <= testDistReqCount; i++ {
   135  		cost := uint64(rand.Int63n(testDistMaxCost))
   136  		procTime := uint64(rand.Int63n(int64(cost + 1)))
   137  		rq := &testDistReq{
   138  			cost:      cost,
   139  			procTime:  procTime,
   140  			order:     uint64(i),
   141  			canSendTo: make(map[*testDistPeer]struct{}),
   142  		}
   143  		for _, peer := range peers {
   144  			if rand.Intn(2) != 0 {
   145  				rq.canSendTo[peer] = struct{}{}
   146  			}
   147  		}
   148  
   149  		wg.Add(1)
   150  		req := &distReq{
   151  			getCost: rq.getCost,
   152  			canSend: rq.canSend,
   153  			request: rq.request,
   154  		}
   155  		chn := dist.queue(req)
   156  		go func() {
   157  			cnt := 1
   158  			if resend && len(rq.canSendTo) != 0 {
   159  				cnt = rand.Intn(testDistMaxResendCount) + 1
   160  			}
   161  			for i := 0; i < cnt; i++ {
   162  				if i != 0 {
   163  					chn = dist.queue(req)
   164  				}
   165  				p := <-chn
   166  				if p == nil {
   167  					if len(rq.canSendTo) != 0 {
   168  						t.Errorf("Request that could have been sent was dropped")
   169  					}
   170  				} else {
   171  					peer := p.(*testDistPeer)
   172  					if _, ok := rq.canSendTo[peer]; !ok {
   173  						t.Errorf("Request sent to wrong peer")
   174  					}
   175  				}
   176  			}
   177  			wg.Done()
   178  		}()
   179  		if rand.Intn(1000) == 0 {
   180  			time.Sleep(time.Duration(rand.Intn(5000000)))
   181  		}
   182  	}
   183  
   184  	wg.Wait()
   185  }