github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/les/distributor_test.go (about) 1 // Copyright 2017 The Spectrum Authors 2 // This file is part of the Spectrum library. 3 // 4 // The Spectrum 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 Spectrum 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 Spectrum 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 } else { 101 return time.Duration(sumCost - testDistBufLimit), 0 102 } 103 } 104 105 func (p *testDistPeer) canQueue() bool { 106 return true 107 } 108 109 func (p *testDistPeer) queueSend(f func()) { 110 f() 111 } 112 113 func TestRequestDistributor(t *testing.T) { 114 testRequestDistributor(t, false) 115 } 116 117 func TestRequestDistributorResend(t *testing.T) { 118 testRequestDistributor(t, true) 119 } 120 121 func testRequestDistributor(t *testing.T, resend bool) { 122 stop := make(chan struct{}) 123 defer close(stop) 124 125 dist := newRequestDistributor(nil, stop) 126 var peers [testDistPeerCount]*testDistPeer 127 for i := range peers { 128 peers[i] = &testDistPeer{} 129 go peers[i].worker(t, !resend, stop) 130 dist.registerTestPeer(peers[i]) 131 } 132 133 var wg sync.WaitGroup 134 135 for i := 1; i <= testDistReqCount; i++ { 136 cost := uint64(rand.Int63n(testDistMaxCost)) 137 procTime := uint64(rand.Int63n(int64(cost + 1))) 138 rq := &testDistReq{ 139 cost: cost, 140 procTime: procTime, 141 order: uint64(i), 142 canSendTo: make(map[*testDistPeer]struct{}), 143 } 144 for _, peer := range peers { 145 if rand.Intn(2) != 0 { 146 rq.canSendTo[peer] = struct{}{} 147 } 148 } 149 150 wg.Add(1) 151 req := &distReq{ 152 getCost: rq.getCost, 153 canSend: rq.canSend, 154 request: rq.request, 155 } 156 chn := dist.queue(req) 157 go func() { 158 cnt := 1 159 if resend && len(rq.canSendTo) != 0 { 160 cnt = rand.Intn(testDistMaxResendCount) + 1 161 } 162 for i := 0; i < cnt; i++ { 163 if i != 0 { 164 chn = dist.queue(req) 165 } 166 p := <-chn 167 if p == nil { 168 if len(rq.canSendTo) != 0 { 169 t.Errorf("Request that could have been sent was dropped") 170 } 171 } else { 172 peer := p.(*testDistPeer) 173 if _, ok := rq.canSendTo[peer]; !ok { 174 t.Errorf("Request sent to wrong peer") 175 } 176 } 177 } 178 wg.Done() 179 }() 180 if rand.Intn(1000) == 0 { 181 time.Sleep(time.Duration(rand.Intn(5000000))) 182 } 183 } 184 185 wg.Wait() 186 }