2
0
mirror of synced 2025-02-23 06:08:07 +00:00
torrent/rlreader_test.go
YenForYang a8db640c62
Drop bradfitz/iter dependency (#605)
* Drop bradfitz/iter dependency

`range iter.N` looks nice and doesn't allocate, but unfortunately using a `range` expression blocks a function from being inlined wherever it's used (for now). It's not that we need inlining in all cases, but I do think a C-style for loop looks just as nice and is probably clearer to the majority. There also aren't any clear disadvantages to changing (unless you just happen to dislike the look of C)

* Update misc_test.go

* Update rlreader_test.go

* Update torrent_test.go

* Update bench_test.go

* Update client_test.go

* Update iplist_test.go

* Update mse_test.go

* Update peerconn_test.go

* Update peerconn.go

* Update order_test.go

* Update decoder_test.go

* Update main.go

* Update bench-piece-mark-complete.go

* Update main.go

* Update torrent.go

* Update iplist_test.go

* Update main.go
2021-09-14 13:46:50 +10:00

129 lines
2.3 KiB
Go

package torrent
import (
"io"
"log"
"math/rand"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/time/rate"
)
func writeN(ws []io.Writer, n int) error {
b := make([]byte, n)
for _, w := range ws[1:] {
n1 := rand.Intn(n)
wn, err := w.Write(b[:n1])
if wn != n1 {
if err == nil {
panic(n1)
}
return err
}
n -= n1
}
wn, err := ws[0].Write(b[:n])
if wn != n {
if err == nil {
panic(n)
}
}
return err
}
func TestRateLimitReaders(t *testing.T) {
const (
numReaders = 2
bytesPerSecond = 100
burst = 5
readSize = 6
writeRounds = 10
bytesPerRound = 12
)
control := rate.NewLimiter(bytesPerSecond, burst)
shared := rate.NewLimiter(bytesPerSecond, burst)
var (
ws []io.Writer
cs []io.Closer
)
wg := sync.WaitGroup{}
type read struct {
N int
// When the read was allowed.
At time.Time
}
reads := make(chan read)
done := make(chan struct{})
for i := 0; i < numReaders; i += 1 {
r, w := io.Pipe()
ws = append(ws, w)
cs = append(cs, w)
wg.Add(1)
go func() {
defer wg.Done()
r := rateLimitedReader{
l: shared,
r: r,
}
b := make([]byte, readSize)
for {
n, err := r.Read(b)
select {
case reads <- read{n, r.lastRead}:
case <-done:
return
}
if err == io.EOF {
return
}
if err != nil {
panic(err)
}
}
}()
}
closeAll := func() {
for _, c := range cs {
c.Close()
}
}
defer func() {
close(done)
closeAll()
wg.Wait()
}()
written := 0
go func() {
for i := 0; i < writeRounds; i += 1 {
err := writeN(ws, bytesPerRound)
if err != nil {
log.Printf("error writing: %s", err)
break
}
written += bytesPerRound
}
closeAll()
wg.Wait()
close(reads)
}()
totalBytesRead := 0
started := time.Now()
for r := range reads {
totalBytesRead += r.N
require.False(t, r.At.IsZero())
// Copy what the reader should have done with its reservation.
res := control.ReserveN(r.At, r.N)
// If we don't have to wait with the control, the reader has gone too
// fast.
if res.Delay() > 0 {
log.Printf("%d bytes not allowed at %s", r.N, time.Since(started))
t.FailNow()
}
}
assert.EqualValues(t, writeRounds*bytesPerRound, totalBytesRead)
}