forked from quic-go/quic-go
* Implement simnet * simnet: remove separate license * simnet: remove go.mod, use standard require package * simnet: add README * simnet: use synctest wrapper in tests * simnet: minor code cleanup * simnet: expose Packet.Data * simnet: explose Simnet.Router * simnet: remove SimpleFirewallRouter * simnet: remove stray fmt.Println in tests * fix deadline check for write deadlines Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * simnet: fix SetReadDeadline logic --------- Co-authored-by: Marco Munizaga <git@marcopolo.io> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
146 lines
3.0 KiB
Go
146 lines
3.0 KiB
Go
package simnet
|
|
|
|
import (
|
|
"bytes"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestPacketQueue_Basic(t *testing.T) {
|
|
q := newPacketQ(1000)
|
|
|
|
// Test adding and removing single packet
|
|
testPacket := packetWithDeliveryTime{Packet: Packet{Data: []byte("test packet")}, DeliveryTime: time.Now()}
|
|
q.Push(testPacket)
|
|
|
|
got, ok := q.Pop()
|
|
if !ok {
|
|
t.Error("Expected successful Pop, got not ok")
|
|
}
|
|
if !bytes.Equal(got.Data, testPacket.Data) {
|
|
t.Errorf("Expected packet %v, got %v", testPacket, got)
|
|
}
|
|
}
|
|
|
|
func TestPacketQueue_Order(t *testing.T) {
|
|
q := newPacketQ(1000)
|
|
|
|
packets := []packetWithDeliveryTime{
|
|
{Packet: Packet{Data: []byte("first")}, DeliveryTime: time.Now()},
|
|
{Packet: Packet{Data: []byte("second")}, DeliveryTime: time.Now()},
|
|
{Packet: Packet{Data: []byte("third")}, DeliveryTime: time.Now()},
|
|
}
|
|
|
|
for _, p := range packets {
|
|
q.Push(p)
|
|
}
|
|
|
|
for i, want := range packets {
|
|
got, ok := q.Pop()
|
|
if !ok {
|
|
t.Errorf("Pop %d: expected success, got not ok", i)
|
|
continue
|
|
}
|
|
if !bytes.Equal(got.Data, want.Data) {
|
|
t.Errorf("Pop %d: expected %v, got %v", i, want, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPacketQueue_BlockedThenClose(t *testing.T) {
|
|
q := newPacketQ(1000)
|
|
|
|
go func() {
|
|
time.Sleep(10 * time.Millisecond)
|
|
q.Close()
|
|
}()
|
|
|
|
startTime := time.Now()
|
|
|
|
// Test Pop on empty queue
|
|
_, ok := q.Pop()
|
|
if ok {
|
|
t.Error("Expected closed queue")
|
|
}
|
|
|
|
dur := time.Since(startTime)
|
|
if dur < 10*time.Millisecond {
|
|
t.Errorf("Expected Pop to block for at least 10ms, got %v", dur)
|
|
}
|
|
}
|
|
|
|
func TestPacketQueue_Blocking(t *testing.T) {
|
|
q := newPacketQ(1000)
|
|
done := make(chan bool)
|
|
timeout := time.After(100 * time.Millisecond)
|
|
|
|
testPacket := Packet{Data: []byte("test packet")}
|
|
|
|
var readPacket atomic.Bool
|
|
// Start consumer before pushing any data
|
|
go func() {
|
|
packet, ok := q.Pop()
|
|
if !ok {
|
|
t.Error("Expected successful Pop, got not ok")
|
|
done <- true
|
|
return
|
|
}
|
|
readPacket.Store(true)
|
|
if !bytes.Equal(packet.Data, testPacket.Data) {
|
|
t.Errorf("Expected %v, got %v", testPacket, packet)
|
|
}
|
|
done <- true
|
|
}()
|
|
|
|
// Wait a bit to ensure consumer is blocked
|
|
time.Sleep(10 * time.Millisecond)
|
|
if readPacket.Load() {
|
|
t.Error("Consumer should not have read packet")
|
|
}
|
|
|
|
// Push data that should unblock consumer
|
|
q.Push(packetWithDeliveryTime{Packet: testPacket, DeliveryTime: time.Now()})
|
|
|
|
select {
|
|
case <-done:
|
|
// Success - consumer received the packet
|
|
case <-timeout:
|
|
t.Error("Test timed out - Pop did not unblock after Push")
|
|
}
|
|
if !readPacket.Load() {
|
|
t.Error("Consumer should have read packet")
|
|
}
|
|
}
|
|
|
|
func TestPacketQueue_Concurrent(t *testing.T) {
|
|
q := newPacketQ(1000)
|
|
done := make(chan bool)
|
|
|
|
// Start producer goroutine
|
|
go func() {
|
|
for i := 0; i < 100; i++ {
|
|
q.Push(packetWithDeliveryTime{Packet: Packet{Data: []byte{byte(i)}}, DeliveryTime: time.Now()})
|
|
time.Sleep(time.Millisecond)
|
|
}
|
|
done <- true
|
|
}()
|
|
|
|
// Start consumer goroutine
|
|
go func() {
|
|
count := 0
|
|
for count < 100 {
|
|
_, ok := q.Pop()
|
|
if ok {
|
|
count++
|
|
}
|
|
time.Sleep(time.Millisecond)
|
|
}
|
|
done <- true
|
|
}()
|
|
|
|
// Wait for both goroutines to finish
|
|
<-done
|
|
<-done
|
|
}
|