1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
|
package congestion
import (
"math"
"math/rand/v2"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestPacerPacing(t *testing.T) {
bandwidth := 50 * initialMaxDatagramSize // 50 full-size packets per second
p := newPacer(func() Bandwidth { return Bandwidth(bandwidth) * BytesPerSecond * 4 / 5 })
now := time.Now()
require.Zero(t, p.TimeUntilSend())
budget := p.Budget(now)
require.Equal(t, maxBurstSizePackets*initialMaxDatagramSize, budget)
// consume the initial budget by sending packets
for budget > 0 {
require.Zero(t, p.TimeUntilSend())
require.Equal(t, budget, p.Budget(now))
p.SentPacket(now, initialMaxDatagramSize)
budget -= initialMaxDatagramSize
}
// now packets are being paced
for range 5 {
require.Zero(t, p.Budget(now))
nextPacket := p.TimeUntilSend()
require.NotZero(t, nextPacket)
require.Equal(t, time.Second/50, nextPacket.Sub(now))
now = nextPacket
p.SentPacket(now, initialMaxDatagramSize)
}
nextPacket := p.TimeUntilSend()
require.Equal(t, time.Second/50, nextPacket.Sub(now))
// send this packet a bit later, simulating timer delay
p.SentPacket(nextPacket.Add(time.Millisecond), initialMaxDatagramSize)
// the next packet should be paced again, without a delay
require.Equal(t, time.Second/50, p.TimeUntilSend().Sub(nextPacket))
// now send a half-size packet
now = p.TimeUntilSend()
p.SentPacket(now, initialMaxDatagramSize/2)
require.Equal(t, initialMaxDatagramSize/2, p.Budget(now))
require.Equal(t, time.Second/100, p.TimeUntilSend().Sub(now))
p.SentPacket(p.TimeUntilSend(), initialMaxDatagramSize/2)
now = p.TimeUntilSend()
// budget accumulates if no packets are sent for a while
// we should have accumulated budget to send a burst now
require.Equal(t, 5*initialMaxDatagramSize, p.Budget(now.Add(4*time.Second/50)))
// but the budget is capped at the max burst size
require.Equal(t, maxBurstSizePackets*initialMaxDatagramSize, p.Budget(now.Add(time.Hour)))
p.SentPacket(now, initialMaxDatagramSize)
require.Zero(t, p.Budget(now))
// reduce the bandwidth
bandwidth = 10 * initialMaxDatagramSize // 10 full-size packets per second
require.Equal(t, time.Second/10, p.TimeUntilSend().Sub(now))
}
func TestPacerUpdatePacketSize(t *testing.T) {
const bandwidth = 50 * initialMaxDatagramSize // 50 full-size packets per second
p := newPacer(func() Bandwidth { return Bandwidth(bandwidth) * BytesPerSecond * 4 / 5 })
// consume the initial budget by sending packets
now := time.Now()
for p.Budget(now) > 0 {
p.SentPacket(now, initialMaxDatagramSize)
}
require.Equal(t, time.Second/50, p.TimeUntilSend().Sub(now))
// Double the packet size. We now need to wait twice as long to send the next packet.
const newDatagramSize = 2 * initialMaxDatagramSize
p.SetMaxDatagramSize(newDatagramSize)
require.Equal(t, 2*time.Second/50, p.TimeUntilSend().Sub(now))
// check that the maximum burst size is updated
require.Equal(t, maxBurstSizePackets*newDatagramSize, p.Budget(now.Add(time.Hour)))
}
func TestPacerFastPacing(t *testing.T) {
const bandwidth = 10000 * initialMaxDatagramSize // 10,000 full-size packets per second
p := newPacer(func() Bandwidth { return Bandwidth(bandwidth) * BytesPerSecond * 4 / 5 })
// consume the initial budget by sending packets
now := time.Now()
for p.Budget(now) > 0 {
p.SentPacket(now, initialMaxDatagramSize)
}
// If we were pacing by packet, we'd expect the next packet to send in 1/10ms.
// However, we don't want to arm the pacing timer for less than 1ms,
// so we wait for 1ms, and then send 10 packets in a burst.
require.Equal(t, time.Millisecond, p.TimeUntilSend().Sub(now))
require.Equal(t, 10*initialMaxDatagramSize, p.Budget(now.Add(time.Millisecond)))
now = now.Add(time.Millisecond)
for range 10 {
require.NotZero(t, p.Budget(now))
p.SentPacket(now, initialMaxDatagramSize)
}
require.Zero(t, p.Budget(now))
require.Equal(t, time.Millisecond, p.TimeUntilSend().Sub(now))
}
func TestPacerNoOverflows(t *testing.T) {
p := newPacer(func() Bandwidth { return infBandwidth })
now := time.Now()
p.SentPacket(now, initialMaxDatagramSize)
for range 100000 {
require.NotZero(t, p.Budget(now.Add(time.Duration(rand.Int64N(math.MaxInt64)))))
}
}
|