Files
quic-go/testutils/simnet/simlink_test.go
2025-11-14 04:04:40 +03:00

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)
}
})
}