265 lines
4.9 KiB
Go
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()
|
|
}
|