whspbrd-final/sfudp/sfudp_test.go
2026-05-02 22:09:19 +02:00

265 lines
4.9 KiB
Go

package sfudp_test
// 100% of AI generated code
// not reviewed at all
import (
"WhspBrd/sfudp"
"bytes"
"encoding/binary"
"errors"
"net"
"sync"
"testing"
"time"
)
const testTimeout = 2 * time.Second
// --- helpers ---
func newPair(t *testing.T) (*sfudp.SFUDPConn, *sfudp.SFUDPConn) {
t.Helper()
s, err := sfudp.ListenSFUDP("udp", &net.UDPAddr{Port: 0})
if err != nil {
t.Fatal(err)
}
c, err := sfudp.DialSFUDP("udp", nil, s.GetUDP().LocalAddr().(*net.UDPAddr))
if err != nil {
s.Close()
t.Fatal(err)
}
_ = s.GetUDP().SetReadDeadline(time.Now().Add(testTimeout))
_ = c.GetUDP().SetReadDeadline(time.Now().Add(testTimeout))
return s, c
}
func mustWrite(t *testing.T, c *sfudp.SFUDPConn, b []byte) {
t.Helper()
n, err := c.Write(b)
if err != nil {
t.Fatal(err)
}
if n != len(b) {
t.Fatalf("short write: %d != %d", n, len(b))
}
}
func mustRead(t *testing.T, s *sfudp.SFUDPConn, expected []byte) {
t.Helper()
buf := make([]byte, len(expected))
n, err := s.Read(buf)
if err != nil {
t.Fatal(err)
}
if n != len(expected) {
t.Fatalf("size mismatch: %d != %d", n, len(expected))
}
if !bytes.Equal(buf, expected) {
t.Fatal("payload mismatch")
}
}
// --- tests ---
func TestMessageSizes(t *testing.T) {
s, c := newPair(t)
defer s.Close()
defer c.Close()
tests := []int{
1,
100,
sfudp.MaxPayload - 1,
sfudp.MaxPayload,
sfudp.MaxPayload + 1,
sfudp.MaxPayload*3 + 123,
}
for _, size := range tests {
t.Run("size_"+itoa(size), func(t *testing.T) {
msg := make([]byte, size)
for i := range msg {
msg[i] = byte(i)
}
mustWrite(t, c, msg)
mustRead(t, s, msg)
})
}
}
func TestTooLargeMessage(t *testing.T) {
s, c := newPair(t)
defer s.Close()
defer c.Close()
msg := make([]byte, sfudp.MaxPayload*sfudp.MaxFragments+1)
_, err := c.Write(msg)
if err == nil {
t.Fatal("expected error")
}
}
func TestBufferTooSmall(t *testing.T) {
s, c := newPair(t)
defer s.Close()
defer c.Close()
msg := make([]byte, sfudp.MaxPayload*2)
mustWrite(t, c, msg)
small := make([]byte, 10)
_, err := s.Read(small)
if err == nil {
t.Fatal("expected buffer too small error")
}
}
func TestConcurrentWrites(t *testing.T) {
s, c := newPair(t)
defer s.Close()
defer c.Close()
const count = 20
var wg sync.WaitGroup
for i := 0; i < count; i++ {
wg.Add(1)
go func(v int) {
defer wg.Done()
msg := bytes.Repeat([]byte{byte(v)}, sfudp.MaxPayload+50)
mustWrite(t, c, msg)
}(i)
}
go func() {
wg.Wait()
}()
for i := 0; i < count; i++ {
buf := make([]byte, sfudp.MaxPayload+50)
_, err := s.Read(buf)
if err != nil {
t.Fatal(err)
}
}
}
func TestFragmentTimeoutGC(t *testing.T) {
s, _ := newPair(t)
defer s.Close()
raw, err := net.DialUDP("udp", nil, s.GetUDP().LocalAddr().(*net.UDPAddr))
if err != nil {
t.Fatal(err)
}
defer raw.Close()
// send incomplete fragment set
pkt := make([]byte, sfudp.HeaderSize+10)
binary.LittleEndian.PutUint32(pkt[0:4], 999)
binary.LittleEndian.PutUint16(pkt[4:6], 0)
pkt[6] = 2
_, _ = raw.Write(pkt)
time.Sleep(sfudp.FragmentTTL + time.Second)
// IMPORTANT: reset deadline after long sleep
_ = s.GetUDP().SetReadDeadline(time.Now().Add(testTimeout))
// send valid single fragment
valid := []byte("ok")
pkt = make([]byte, sfudp.HeaderSize+len(valid))
pkt[6] = 1
copy(pkt[sfudp.HeaderSize:], valid)
_, _ = raw.Write(pkt)
buf := make([]byte, 10)
n, err := s.Read(buf)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(buf[:n], valid) {
t.Fatal("unexpected payload")
}
}
func TestInvalidFragmentIgnored(t *testing.T) {
s, _ := newPair(t)
defer s.Close()
raw, err := net.DialUDP("udp", nil, s.GetUDP().LocalAddr().(*net.UDPAddr))
if err != nil {
t.Fatal(err)
}
defer raw.Close()
// invalid index >= count
pkt := make([]byte, sfudp.HeaderSize+10)
binary.LittleEndian.PutUint32(pkt[0:4], 111)
binary.LittleEndian.PutUint16(pkt[4:6], 5)
pkt[6] = 2
_, _ = raw.Write(pkt)
// valid single packet
valid := []byte("good")
pkt = make([]byte, sfudp.HeaderSize+len(valid))
pkt[6] = 1
copy(pkt[sfudp.HeaderSize:], valid)
_, _ = raw.Write(pkt)
buf := make([]byte, 16)
n, err := s.Read(buf)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(buf[:n], valid) {
t.Fatal("invalid fragment was not ignored")
}
}
func TestEmptyWrite(t *testing.T) {
s, c := newPair(t)
defer s.Close()
defer c.Close()
n, err := c.Write(nil)
if err != nil || n != 0 {
t.Fatal("empty write failed")
}
buf := make([]byte, 16)
_, err = s.Read(buf)
if err == nil {
t.Fatal("expected timeout")
}
if !errors.Is(err, net.ErrClosed) && !isTimeout(err) {
t.Fatal("unexpected error:", err)
}
}
// small int to string helper without fmt
func itoa(v int) string {
if v == 0 {
return "0"
}
var b [20]byte
i := len(b)
for v > 0 {
i--
b[i] = byte('0' + v%10)
v /= 10
}
return string(b[i:])
}
func isTimeout(err error) bool {
nerr, ok := err.(net.Error)
return ok && nerr.Timeout()
}