package checker import ( "context" "encoding/binary" "net" "sync/atomic" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // fakeUDPRelay listens on a UDP socket and echoes SOCKS5-wrapped STUN // binding requests as a synthetic Binding Success Response, just like // fakeProxy.runRelay in checker_test.go but standalone (no SOCKS5 TCP // control channel needed). dropEveryN > 0 drops every Nth packet. type fakeUDPRelay struct { conn *net.UDPConn addr *net.UDPAddr dropEveryN atomic.Int32 count atomic.Int32 } func newFakeUDPRelay(t *testing.T) *fakeUDPRelay { t.Helper() pc, err := net.ListenPacket("udp", "127.0.0.1:0") require.NoError(t, err) uconn := pc.(*net.UDPConn) r := &fakeUDPRelay{ conn: uconn, addr: uconn.LocalAddr().(*net.UDPAddr), } t.Cleanup(func() { _ = uconn.Close() }) go r.serve() return r } func (r *fakeUDPRelay) serve() { buf := make([]byte, 2048) for { n, src, err := r.conn.ReadFromUDP(buf) if err != nil { return } if dropN := r.dropEveryN.Load(); dropN > 0 { c := r.count.Add(1) if c%dropN == 0 { continue } } else { r.count.Add(1) } if n < 10 { continue } if buf[0] != 0x00 || buf[1] != 0x00 || buf[2] != 0x00 { continue } var hdrLen int switch buf[3] { case 0x01: hdrLen = 10 case 0x04: hdrLen = 22 case 0x03: if n < 5 { continue } hdrLen = 4 + 1 + int(buf[4]) + 2 default: continue } if n < hdrLen+20 { continue } stunReq := buf[hdrLen:n] var txID [12]byte copy(txID[:], stunReq[8:20]) ip4 := src.IP.To4() if ip4 == nil { continue } xport := uint16(src.Port) ^ uint16(stunMagicCookie>>16) xaddr := binary.BigEndian.Uint32(ip4) ^ stunMagicCookie stunResp := make([]byte, 20+12) binary.BigEndian.PutUint16(stunResp[0:2], stunBindingSuccessResponse) binary.BigEndian.PutUint16(stunResp[2:4], 12) binary.BigEndian.PutUint32(stunResp[4:8], stunMagicCookie) copy(stunResp[8:20], txID[:]) binary.BigEndian.PutUint16(stunResp[20:22], stunAttrXORMappedAddress) binary.BigEndian.PutUint16(stunResp[22:24], 8) stunResp[24] = 0 stunResp[25] = 0x01 binary.BigEndian.PutUint16(stunResp[26:28], xport) binary.BigEndian.PutUint32(stunResp[28:32], xaddr) out := make([]byte, 0, 10+len(stunResp)) out = append(out, 0x00, 0x00, 0x00, 0x01) out = append(out, ip4...) var portBuf [2]byte binary.BigEndian.PutUint16(portBuf[:], uint16(src.Port)) out = append(out, portBuf[:]...) out = append(out, stunResp...) _, _ = r.conn.WriteToUDP(out, src) } } // TestVoiceQualityBurst_Math: full 30-of-30 reception on localhost, all // RTTs in single-digit milliseconds. func TestVoiceQualityBurst_Math(t *testing.T) { relay := newFakeUDPRelay(t) clientPC, err := net.ListenPacket("udp", "127.0.0.1:0") require.NoError(t, err) defer clientPC.Close() ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() res, err := runVoiceQualityBurst(ctx, clientPC, relay.addr, "localhost", 19302, 30, 5*time.Millisecond) require.NoError(t, err) assert.Equal(t, 30, res.Sent) assert.Equal(t, 30, res.Received) assert.InDelta(t, 0.0, res.LossPct, 0.001) assert.Less(t, res.P50RTTMS, 50.0, "loopback p50 should be tiny") } // TestVoiceQualityBurst_HalfLoss verifies the loss-percentage math when // the relay drops half the packets. func TestVoiceQualityBurst_HalfLoss(t *testing.T) { relay := newFakeUDPRelay(t) relay.dropEveryN.Store(2) // every other packet → 50% loss clientPC, err := net.ListenPacket("udp", "127.0.0.1:0") require.NoError(t, err) defer clientPC.Close() ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() res, err := runVoiceQualityBurst(ctx, clientPC, relay.addr, "localhost", 19302, 20, 3*time.Millisecond) require.NoError(t, err) assert.Equal(t, 20, res.Sent) assert.InDelta(t, 50.0, res.LossPct, 5.0, "expected ~50%% loss got %+v", res) } // TestVoiceQualityBurst_AllDropped: dropEveryN=1 → 100% loss. Should NOT // return an error; should report Sent=N, Received=0. func TestVoiceQualityBurst_AllDropped(t *testing.T) { relay := newFakeUDPRelay(t) relay.dropEveryN.Store(1) clientPC, err := net.ListenPacket("udp", "127.0.0.1:0") require.NoError(t, err) defer clientPC.Close() ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() res, err := runVoiceQualityBurst(ctx, clientPC, relay.addr, "localhost", 19302, 10, 3*time.Millisecond) require.NoError(t, err) assert.Equal(t, 10, res.Sent) assert.Equal(t, 0, res.Received) assert.InDelta(t, 100.0, res.LossPct, 0.001) assert.Equal(t, 0.0, res.P50RTTMS) assert.Equal(t, 0.0, res.JitterMS) } // TestVoiceQualityBurst_ZeroCount: count=0 → error (defensive). func TestVoiceQualityBurst_ZeroCount(t *testing.T) { relay := newFakeUDPRelay(t) clientPC, err := net.ListenPacket("udp", "127.0.0.1:0") require.NoError(t, err) defer clientPC.Close() ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() _, err = runVoiceQualityBurst(ctx, clientPC, relay.addr, "localhost", 19302, 0, 5*time.Millisecond) assert.Error(t, err) }