forked from quic-go/quic-go
220 lines
6.2 KiB
Go
220 lines
6.2 KiB
Go
package simnet
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"net"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.geeks-team.ru/gr1ffon/quic-go/internal/synctest"
|
|
)
|
|
|
|
type testRouter struct {
|
|
onSend func(p Packet)
|
|
onRecv func(p Packet)
|
|
}
|
|
|
|
func (r *testRouter) SendPacket(p Packet) error {
|
|
r.onSend(p)
|
|
return nil
|
|
}
|
|
|
|
func (r *testRouter) RecvPacket(p Packet) {
|
|
r.onRecv(p)
|
|
}
|
|
|
|
func (r *testRouter) AddNode(addr net.Addr, receiver PacketReceiver) {
|
|
r.onRecv = receiver.RecvPacket
|
|
}
|
|
|
|
const Mibps = 1_000_000
|
|
|
|
func TestBandwidthLimiterAndLatency(t *testing.T) {
|
|
for _, testUpload := range []bool{true, false} {
|
|
t.Run(fmt.Sprintf("testing upload=%t", testUpload), func(t *testing.T) {
|
|
synctest.Test(t, func(t *testing.T) {
|
|
const expectedSpeed = 10 * Mibps
|
|
const expectedLatency = 10 * time.Millisecond
|
|
const MTU = 1400
|
|
linkSettings := LinkSettings{
|
|
BitsPerSecond: expectedSpeed,
|
|
MTU: MTU,
|
|
Latency: expectedLatency,
|
|
}
|
|
|
|
recvStartTimeChan := make(chan time.Time, 1)
|
|
recvStarted := false
|
|
bytesRead := 0
|
|
packetHandler := func(p Packet) {
|
|
if !recvStarted {
|
|
recvStarted = true
|
|
recvStartTimeChan <- time.Now()
|
|
}
|
|
bytesRead += len(p.Data)
|
|
}
|
|
|
|
router := &testRouter{}
|
|
if testUpload {
|
|
router.onSend = packetHandler
|
|
} else {
|
|
router.onRecv = packetHandler
|
|
}
|
|
link := SimulatedLink{
|
|
UplinkSettings: linkSettings,
|
|
DownlinkSettings: linkSettings,
|
|
UploadPacket: router,
|
|
downloadPacket: router,
|
|
}
|
|
|
|
link.Start()
|
|
|
|
// Send 10MiB of data
|
|
chunk := make([]byte, MTU)
|
|
bytesSent := 0
|
|
|
|
sendStartTime := time.Now()
|
|
{
|
|
totalBytes := 10 << 20
|
|
// Blast a bunch of packets
|
|
for bytesSent < totalBytes {
|
|
// This sleep shouldn't limit the speed. 1400 Bytes/100us = 14KB/ms = 14MB/s = 14*8 Mbps
|
|
// but it acts as a simple pacer to avoid just dropping the packets when the link is saturated.
|
|
time.Sleep(100 * time.Microsecond)
|
|
if testUpload {
|
|
_ = link.SendPacket(Packet{Data: chunk})
|
|
} else {
|
|
link.RecvPacket(Packet{Data: chunk})
|
|
}
|
|
bytesSent += len(chunk)
|
|
}
|
|
}
|
|
|
|
// Wait for delayed packets to be sent
|
|
time.Sleep(40 * time.Millisecond)
|
|
fmt.Printf("sent: %d\n", bytesSent)
|
|
|
|
link.Close()
|
|
fmt.Printf("bytesRead: %d\n", bytesRead)
|
|
recvStartTime := <-recvStartTimeChan
|
|
duration := time.Since(recvStartTime)
|
|
|
|
observedLatency := recvStartTime.Sub(sendStartTime)
|
|
percentErrorLatency := math.Abs(observedLatency.Seconds()-expectedLatency.Seconds()) / expectedLatency.Seconds()
|
|
t.Logf("observed latency: %s, expected latency: %s, percent error: %f\n", observedLatency, expectedLatency, percentErrorLatency)
|
|
if percentErrorLatency > 0.20 {
|
|
t.Fatalf("observed latency %s is wrong", observedLatency)
|
|
}
|
|
|
|
observedSpeed := 8 * float64(bytesRead) / duration.Seconds()
|
|
t.Logf("observed speed: %f Mbps over %s\n", observedSpeed/Mibps, duration)
|
|
percentErrorSpeed := math.Abs(observedSpeed-float64(expectedSpeed)) / float64(expectedSpeed)
|
|
t.Logf("observed speed: %f Mbps, expected speed: %d Mbps, percent error: %f\n", observedSpeed/Mibps, expectedSpeed/Mibps, percentErrorSpeed)
|
|
if percentErrorSpeed > 0.20 {
|
|
t.Fatalf("observed speed %f Mbps is too far from expected speed %d Mbps. Percent error: %f", observedSpeed/Mibps, expectedSpeed/Mibps, percentErrorSpeed)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
type linkAdapter struct {
|
|
link PacketReceiver
|
|
}
|
|
|
|
var _ Router = &linkAdapter{}
|
|
|
|
// AddNode implements Router.
|
|
func (c *linkAdapter) AddNode(addr net.Addr, receiver PacketReceiver) {
|
|
c.link = receiver
|
|
}
|
|
|
|
// SendPacket implements Router.
|
|
func (c *linkAdapter) SendPacket(p Packet) error {
|
|
c.link.RecvPacket(p)
|
|
return nil
|
|
}
|
|
|
|
func TestBandwidthLimiterAndLatencyConnectedLinks(t *testing.T) {
|
|
synctest.Test(t, func(t *testing.T) {
|
|
const expectedSpeed = 100 * Mibps
|
|
const latencyOfOneLink = 10 * time.Millisecond
|
|
const expectedLatency = 2 * latencyOfOneLink
|
|
const MTU = 1400
|
|
linkSettings := LinkSettings{
|
|
BitsPerSecond: expectedSpeed,
|
|
MTU: MTU,
|
|
Latency: latencyOfOneLink,
|
|
}
|
|
|
|
recvStartTimeChan := make(chan time.Time, 1)
|
|
recvStarted := false
|
|
bytesRead := 0
|
|
packetHandler := func(p Packet) {
|
|
if !recvStarted {
|
|
recvStarted = true
|
|
recvStartTimeChan <- time.Now()
|
|
}
|
|
bytesRead += len(p.Data)
|
|
}
|
|
r := &testRouter{
|
|
onRecv: packetHandler,
|
|
}
|
|
|
|
link2 := SimulatedLink{
|
|
UplinkSettings: linkSettings,
|
|
DownlinkSettings: linkSettings,
|
|
downloadPacket: r,
|
|
}
|
|
link1 := SimulatedLink{
|
|
UplinkSettings: linkSettings,
|
|
DownlinkSettings: linkSettings,
|
|
UploadPacket: &linkAdapter{link: &link2},
|
|
downloadPacket: &testRouter{},
|
|
}
|
|
|
|
link1.Start()
|
|
link2.Start()
|
|
|
|
// Send 10MiB of data
|
|
chunk := make([]byte, MTU)
|
|
bytesSent := 0
|
|
|
|
sendStartTime := time.Now()
|
|
{
|
|
totalBytes := 10 << 20
|
|
// Blast a bunch of packets
|
|
for bytesSent < totalBytes {
|
|
time.Sleep(100 * time.Microsecond)
|
|
_ = link1.SendPacket(Packet{Data: chunk})
|
|
bytesSent += len(chunk)
|
|
}
|
|
}
|
|
|
|
// Wait for delayed packets to be sent
|
|
time.Sleep(40 * time.Millisecond)
|
|
t.Logf("sent: %d", bytesSent)
|
|
|
|
link1.Close()
|
|
link2.Close()
|
|
t.Logf("bytesRead: %d", bytesRead)
|
|
recvStartTime := <-recvStartTimeChan
|
|
duration := time.Since(recvStartTime)
|
|
|
|
observedLatency := recvStartTime.Sub(sendStartTime)
|
|
percentErrorLatency := math.Abs(observedLatency.Seconds()-expectedLatency.Seconds()) / expectedLatency.Seconds()
|
|
t.Logf("observed latency: %s, expected latency: %s, percent error: %f\n", observedLatency, expectedLatency, percentErrorLatency)
|
|
if percentErrorLatency > 0.20 {
|
|
t.Fatalf("observed latency %s is wrong", observedLatency)
|
|
}
|
|
|
|
observedSpeed := 8 * float64(bytesRead) / duration.Seconds()
|
|
t.Logf("observed speed: %f Mbps over %s\n", observedSpeed/Mibps, duration)
|
|
percentErrorSpeed := math.Abs(observedSpeed-float64(expectedSpeed)) / float64(expectedSpeed)
|
|
t.Logf("observed speed: %f Mbps, expected speed: %d Mbps, percent error: %f\n", observedSpeed/Mibps, expectedSpeed/Mibps, percentErrorSpeed)
|
|
if percentErrorSpeed > 0.20 {
|
|
t.Fatalf("observed speed %f Mbps is too far from expected speed %d Mbps. Percent error: %f", observedSpeed/Mibps, expectedSpeed/Mibps, percentErrorSpeed)
|
|
}
|
|
})
|
|
}
|