|
@@ -1,7 +1,11 @@
|
|
|
package bwtest
|
|
package bwtest
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
|
|
|
+ "bufio"
|
|
|
"context"
|
|
"context"
|
|
|
|
|
+ "encoding/json"
|
|
|
|
|
+ "net"
|
|
|
|
|
+ "strings"
|
|
|
"testing"
|
|
"testing"
|
|
|
"time"
|
|
"time"
|
|
|
)
|
|
)
|
|
@@ -10,21 +14,99 @@ func TestUDPPacerUsesDeliveryFeedback(t *testing.T) {
|
|
|
p := newUDPPacer(0)
|
|
p := newUDPPacer(0)
|
|
|
initial := p.rate
|
|
initial := p.rate
|
|
|
now := p.started.Add(2 * time.Second)
|
|
now := p.started.Add(2 * time.Second)
|
|
|
- p.observe(10000, now)
|
|
|
|
|
|
|
+ p.observe(10000, 10, 0, now)
|
|
|
if p.rate >= initial {
|
|
if p.rate >= initial {
|
|
|
t.Fatal("automatic rate did not fall after severe loss")
|
|
t.Fatal("automatic rate did not fall after severe loss")
|
|
|
}
|
|
}
|
|
|
lower := p.rate
|
|
lower := p.rate
|
|
|
- p.observe(10000+lower/8, now.Add(time.Second))
|
|
|
|
|
|
|
+ p.observe(10000+lower/8, 110, 0, now.Add(time.Second))
|
|
|
if p.rate <= lower {
|
|
if p.rate <= lower {
|
|
|
t.Fatal("automatic rate did not grow after successful delivery")
|
|
t.Fatal("automatic rate did not grow after successful delivery")
|
|
|
}
|
|
}
|
|
|
fixed := newUDPPacer(1234567)
|
|
fixed := newUDPPacer(1234567)
|
|
|
- fixed.observe(0, fixed.started.Add(2*time.Second))
|
|
|
|
|
|
|
+ fixed.observe(0, 0, 0, fixed.started.Add(2*time.Second))
|
|
|
if fixed.rate != 1234567 {
|
|
if fixed.rate != 1234567 {
|
|
|
t.Fatal("receiver feedback changed explicit rate limit")
|
|
t.Fatal("receiver feedback changed explicit rate limit")
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+func TestAutomaticUDPPacerBacksOffForPacketLoss(t *testing.T) {
|
|
|
|
|
+ p := newUDPPacer(0)
|
|
|
|
|
+ now := p.started.Add(2 * time.Second)
|
|
|
|
|
+ // Delivery remains high enough that byte-only feedback would increase rate.
|
|
|
|
|
+ p.observe(480000, 1000, 20, now)
|
|
|
|
|
+ if p.rate >= 2_000_000 {
|
|
|
|
|
+ t.Fatalf("rate did not fall on loss: %d", p.rate)
|
|
|
|
|
+ }
|
|
|
|
|
+ lower := p.rate
|
|
|
|
|
+ p.observe(480000+lower/8, 2000, 20, now.Add(time.Second))
|
|
|
|
|
+ if p.rate > lower {
|
|
|
|
|
+ t.Fatalf("rate rose during loss cooldown: %d", p.rate)
|
|
|
|
|
+ }
|
|
|
|
|
+ p.observe(480000+3*lower/8, 3000, 20, now.Add(3*time.Second))
|
|
|
|
|
+ if p.rate <= lower {
|
|
|
|
|
+ t.Fatalf("rate failed to recover after cooldown: %d", p.rate)
|
|
|
|
|
+ }
|
|
|
|
|
+ fixed := newUDPPacer(2_000_000)
|
|
|
|
|
+ fixed.observe(480000, 1000, 100, fixed.started.Add(2*time.Second))
|
|
|
|
|
+ if fixed.rate != 2_000_000 {
|
|
|
|
|
+ t.Fatal("fixed rate changed on loss")
|
|
|
|
|
+ }
|
|
|
|
|
+ legacy := newUDPPacer(0)
|
|
|
|
|
+ legacy.observe(500000, 0, 0, legacy.started.Add(2*time.Second))
|
|
|
|
|
+ if legacy.rate <= 2_000_000 {
|
|
|
|
|
+ t.Fatal("byte-only feedback from older peer no longer works")
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func TestUDPNoDeliveryReportsError(t *testing.T) {
|
|
|
|
|
+ ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatal(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ defer ln.Close()
|
|
|
|
|
+ u, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP("127.0.0.1")})
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatal(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ defer u.Close()
|
|
|
|
|
+ go func() {
|
|
|
|
|
+ for {
|
|
|
|
|
+ conn, e := ln.Accept()
|
|
|
|
|
+ if e != nil {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ go func() {
|
|
|
|
|
+ defer conn.Close()
|
|
|
|
|
+ rd := bufio.NewReader(conn)
|
|
|
|
|
+ if _, e := rd.ReadBytes('\n'); e != nil {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ json.NewEncoder(conn).Encode(SessionHandshake{Type: "accept", UDPFeedback: udpFeedbackVersion, UDPPort: u.LocalAddr().(*net.UDPAddr).Port, PacketSize: 1200})
|
|
|
|
|
+ for {
|
|
|
|
|
+ if _, e := rd.ReadBytes('\n'); e != nil {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }()
|
|
|
|
|
+ }
|
|
|
|
|
+ }()
|
|
|
|
|
+ for _, direction := range []string{"tx", "rx", "both"} {
|
|
|
|
|
+ c := NewClient()
|
|
|
|
|
+ if err := c.Run(context.Background(), SessionRequest{Target: "127.0.0.1", Port: ln.Addr().(*net.TCPAddr).Port, Protocol: "udp", Direction: direction, Duration: 2, PacketSize: 1200}); err != nil {
|
|
|
|
|
+ t.Fatal(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ deadline := time.Now().Add(4 * time.Second)
|
|
|
|
|
+ for c.GetStatus().Running && time.Now().Before(deadline) {
|
|
|
|
|
+ time.Sleep(10 * time.Millisecond)
|
|
|
|
|
+ }
|
|
|
|
|
+ st := c.GetStatus()
|
|
|
|
|
+ c.Stop()
|
|
|
|
|
+ if st.Running || !strings.Contains(st.Error, "No UDP") {
|
|
|
|
|
+ t.Fatalf("%s no-data not reported: %+v", direction, st)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
func TestUDPPacerCancellation(t *testing.T) {
|
|
func TestUDPPacerCancellation(t *testing.T) {
|
|
|
p := newUDPPacer(1)
|
|
p := newUDPPacer(1)
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Millisecond)
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Millisecond)
|