mirror of
https://github.com/soypat/lneto.git
synced 2026-07-26 10:38:47 +00:00
claude: add tests for tcp and ntp to consolidate functionality
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
package ntp
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestClient_FullExchange(t *testing.T) {
|
||||
// Simulate a NTP client-server exchange without network.
|
||||
baseTime := BaseTime()
|
||||
clientStart := baseTime.Add(10 * time.Second)
|
||||
serverOffset := 500 * time.Millisecond // server is 500ms ahead
|
||||
|
||||
clockTime := clientStart
|
||||
client := Client{}
|
||||
client.Reset(-18, func() time.Time { return clockTime })
|
||||
|
||||
if client.IsDone() {
|
||||
t.Fatal("client should not be done before exchange")
|
||||
}
|
||||
|
||||
// Step 1: Client encapsulates request.
|
||||
reqBuf := make([]byte, SizeHeader)
|
||||
n, err := client.Encapsulate(reqBuf, 0, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n != SizeHeader {
|
||||
t.Fatalf("expected %d bytes, got %d", SizeHeader, n)
|
||||
}
|
||||
|
||||
// Verify request frame fields.
|
||||
reqFrm, err := NewFrame(reqBuf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mode, version, _ := reqFrm.Flags()
|
||||
if mode != ModeClient {
|
||||
t.Errorf("request mode = %d; want ModeClient (%d)", mode, ModeClient)
|
||||
}
|
||||
if version != Version4 {
|
||||
t.Errorf("request version = %d; want %d", version, Version4)
|
||||
}
|
||||
if reqFrm.Stratum() != StratumUnsync {
|
||||
t.Errorf("request stratum = %d; want StratumUnsync", reqFrm.Stratum())
|
||||
}
|
||||
|
||||
// Step 2: Simulate server response.
|
||||
// Server receives at clientStart + serverOffset, sends response at clientStart + serverOffset + 10ms processing.
|
||||
serverRecvTime := clientStart.Add(serverOffset)
|
||||
serverXmtTime := serverRecvTime.Add(10 * time.Millisecond)
|
||||
|
||||
respBuf := make([]byte, SizeHeader)
|
||||
respFrm, _ := NewFrame(respBuf)
|
||||
respFrm.SetFlags(ModeServer, Version4, LeapNoWarning)
|
||||
respFrm.SetStratum(StratumPrimary)
|
||||
respFrm.SetPrecision(-20)
|
||||
|
||||
// Echo client's origin time.
|
||||
respFrm.SetOriginTime(reqFrm.OriginTime())
|
||||
|
||||
// Set server timestamps.
|
||||
recvTS, err := TimestampFromTime(serverRecvTime)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
xmtTS, err := TimestampFromTime(serverXmtTime)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
respFrm.SetReceiveTime(recvTS)
|
||||
respFrm.SetTransmitTime(xmtTS)
|
||||
|
||||
// Advance client clock to simulate network delay.
|
||||
clockTime = clientStart.Add(100 * time.Millisecond)
|
||||
|
||||
// Step 3: Client demuxes response.
|
||||
err = client.Demux(respBuf, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !client.IsDone() {
|
||||
t.Fatal("client should be done after exchange")
|
||||
}
|
||||
|
||||
// Step 4: Verify results.
|
||||
if client.ServerStratum() != StratumPrimary {
|
||||
t.Errorf("server stratum = %s; want primary", client.ServerStratum())
|
||||
}
|
||||
|
||||
offset := client.Offset()
|
||||
if offset == 0 {
|
||||
t.Fatal("offset should be non-zero")
|
||||
}
|
||||
|
||||
rtd := client.RoundTripDelay()
|
||||
if rtd < 0 {
|
||||
t.Fatalf("round trip delay should be non-negative, got %s", rtd)
|
||||
}
|
||||
|
||||
// Verify Now() returns a reasonable time.
|
||||
ntpNow := client.Now()
|
||||
if ntpNow.Before(baseTime) {
|
||||
t.Errorf("NTP-corrected time %v is before base time %v", ntpNow, baseTime)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_Reset(t *testing.T) {
|
||||
var c Client
|
||||
c.Reset(-18, time.Now)
|
||||
if c.IsDone() {
|
||||
t.Fatal("should not be done after Reset")
|
||||
}
|
||||
if c.LocalPort() != ClientPort {
|
||||
t.Fatalf("expected port %d, got %d", ClientPort, c.LocalPort())
|
||||
}
|
||||
if c.Protocol() != 0 {
|
||||
t.Fatalf("expected protocol 0, got %d", c.Protocol())
|
||||
}
|
||||
id1 := *c.ConnectionID()
|
||||
|
||||
c.Reset(-18, time.Now)
|
||||
id2 := *c.ConnectionID()
|
||||
if id2 <= id1 {
|
||||
t.Fatal("ConnectionID should increment on Reset")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_Encapsulate_WhenDone(t *testing.T) {
|
||||
var c Client
|
||||
// Not reset, state is closed/done.
|
||||
buf := make([]byte, SizeHeader)
|
||||
n, err := c.Encapsulate(buf, 0, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n != 0 {
|
||||
t.Fatalf("expected 0 bytes when done, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_Demux_WhenDone(t *testing.T) {
|
||||
var c Client
|
||||
buf := make([]byte, SizeHeader)
|
||||
err := c.Demux(buf, 0)
|
||||
if err != nil {
|
||||
t.Fatal("Demux when done should be no-op")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_Demux_ShortBuffer(t *testing.T) {
|
||||
var c Client
|
||||
c.Reset(-18, time.Now)
|
||||
// Trigger encapsulate first to move to stateAwait1.
|
||||
buf := make([]byte, SizeHeader)
|
||||
c.Encapsulate(buf, 0, 0)
|
||||
|
||||
// Short buffer should error.
|
||||
err := c.Demux(make([]byte, 10), 0)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for short buffer")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_OffsetBeforeDone(t *testing.T) {
|
||||
var c Client
|
||||
c.Reset(-18, time.Now)
|
||||
if c.Offset() != 0 {
|
||||
t.Fatal("Offset should be 0 before exchange completes")
|
||||
}
|
||||
if c.RoundTripDelay() != -1 {
|
||||
t.Fatal("RoundTripDelay should be -1 before done")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_DemuxRejectsBogusResponse(t *testing.T) {
|
||||
var c Client
|
||||
clockTime := BaseTime().Add(time.Second)
|
||||
c.Reset(-18, func() time.Time { return clockTime })
|
||||
|
||||
// Encapsulate to move to stateAwait1.
|
||||
buf := make([]byte, SizeHeader)
|
||||
c.Encapsulate(buf, 0, 0)
|
||||
|
||||
// Build response with wrong origin time (not echoed correctly).
|
||||
resp := make([]byte, SizeHeader)
|
||||
frm, _ := NewFrame(resp)
|
||||
frm.SetFlags(ModeServer, Version4, LeapNoWarning)
|
||||
frm.SetOriginTime(TimestampFromUint64(99999)) // wrong origin
|
||||
xmt, _ := TimestampFromTime(clockTime.Add(time.Second))
|
||||
frm.SetTransmitTime(xmt)
|
||||
frm.SetReceiveTime(xmt)
|
||||
|
||||
err := c.Demux(resp, 0)
|
||||
if err == nil {
|
||||
t.Fatal("should reject response with mismatched origin time")
|
||||
}
|
||||
if c.IsDone() {
|
||||
t.Fatal("should not be done after rejected response")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func newConfiguredConn(t *testing.T) *Conn {
|
||||
t.Helper()
|
||||
var conn Conn
|
||||
err := conn.Configure(ConnConfig{
|
||||
RxBuf: make([]byte, 512),
|
||||
TxBuf: make([]byte, 512),
|
||||
TxPacketQueueSize: 4,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return &conn
|
||||
}
|
||||
|
||||
func TestConn_SetDeadline_Closed(t *testing.T) {
|
||||
conn := newConfiguredConn(t)
|
||||
err := conn.SetDeadline(time.Now().Add(time.Second))
|
||||
if err == nil {
|
||||
t.Fatal("SetDeadline on closed conn should fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConn_SetReadDeadline_Closed(t *testing.T) {
|
||||
conn := newConfiguredConn(t)
|
||||
err := conn.SetReadDeadline(time.Now().Add(time.Second))
|
||||
if err == nil {
|
||||
t.Fatal("SetReadDeadline on closed conn should fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConn_SetWriteDeadline_Closed(t *testing.T) {
|
||||
conn := newConfiguredConn(t)
|
||||
err := conn.SetWriteDeadline(time.Now().Add(time.Second))
|
||||
if err == nil {
|
||||
t.Fatal("SetWriteDeadline on closed conn should fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConn_OpenActive_InvalidAddr(t *testing.T) {
|
||||
conn := newConfiguredConn(t)
|
||||
err := conn.OpenActive(1234, netip.AddrPort{}, 100)
|
||||
if err == nil {
|
||||
t.Fatal("OpenActive with invalid addr should fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConn_OpenListen(t *testing.T) {
|
||||
conn := newConfiguredConn(t)
|
||||
err := conn.OpenListen(8080, 100)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if conn.State() != StateListen {
|
||||
t.Fatalf("expected StateListen, got %s", conn.State())
|
||||
}
|
||||
if conn.LocalPort() != 8080 {
|
||||
t.Fatalf("expected port 8080, got %d", conn.LocalPort())
|
||||
}
|
||||
}
|
||||
|
||||
func TestConn_Close_Abort(t *testing.T) {
|
||||
conn := newConfiguredConn(t)
|
||||
err := conn.OpenListen(8080, 100)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
conn.Abort()
|
||||
if conn.State() != StateClosed {
|
||||
t.Fatalf("expected StateClosed after Abort, got %s", conn.State())
|
||||
}
|
||||
}
|
||||
|
||||
func TestConn_ReadWrite_Closed(t *testing.T) {
|
||||
conn := newConfiguredConn(t)
|
||||
_, err := conn.Write([]byte("hello"))
|
||||
if err == nil {
|
||||
t.Fatal("Write on closed conn should fail")
|
||||
}
|
||||
buf := make([]byte, 64)
|
||||
_, err = conn.Read(buf)
|
||||
if err == nil {
|
||||
t.Fatal("Read on closed conn should fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConn_Flush_Closed(t *testing.T) {
|
||||
conn := newConfiguredConn(t)
|
||||
err := conn.Flush()
|
||||
if err == nil {
|
||||
t.Fatal("Flush on closed conn should fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConn_BufferedUnsent(t *testing.T) {
|
||||
conn := newConfiguredConn(t)
|
||||
if conn.BufferedUnsent() != 0 {
|
||||
t.Fatalf("expected 0, got %d", conn.BufferedUnsent())
|
||||
}
|
||||
}
|
||||
|
||||
func TestConn_InternalHandler(t *testing.T) {
|
||||
conn := newConfiguredConn(t)
|
||||
h := conn.InternalHandler()
|
||||
if h == nil {
|
||||
t.Fatal("InternalHandler returned nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConn_Configure_Twice(t *testing.T) {
|
||||
conn := newConfiguredConn(t)
|
||||
err := conn.Configure(ConnConfig{
|
||||
RxBuf: make([]byte, 1024),
|
||||
TxBuf: make([]byte, 1024),
|
||||
TxPacketQueueSize: 8,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("reconfigure should succeed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConn_OpenActive_IPv6(t *testing.T) {
|
||||
conn := newConfiguredConn(t)
|
||||
addr6 := netip.AddrFrom16([16]byte{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1})
|
||||
remote := netip.AddrPortFrom(addr6, 443)
|
||||
err := conn.OpenActive(1234, remote, 200)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
raddr := conn.RemoteAddr()
|
||||
if len(raddr) != 16 {
|
||||
t.Fatalf("expected 16-byte remote addr, got %d", len(raddr))
|
||||
}
|
||||
}
|
||||
|
||||
func TestConn_Protocol(t *testing.T) {
|
||||
var conn Conn
|
||||
if conn.Protocol() != 6 { // TCP protocol number
|
||||
t.Fatalf("expected protocol 6, got %d", conn.Protocol())
|
||||
}
|
||||
}
|
||||
|
||||
func TestConn_ImplementsNetConn(t *testing.T) {
|
||||
conn := newConfiguredConn(t)
|
||||
var _ interface {
|
||||
SetDeadline(time.Time) error
|
||||
SetReadDeadline(time.Time) error
|
||||
SetWriteDeadline(time.Time) error
|
||||
Read([]byte) (int, error)
|
||||
Write([]byte) (int, error)
|
||||
Close() error
|
||||
} = conn
|
||||
_ = net.ErrClosed
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
package tcp
|
||||
|
||||
import "testing"
|
||||
|
||||
// setIPv4Version sets the IP version nibble to 4 at the given offset,
|
||||
// as required by Drain's call to internal.SetIPAddrs.
|
||||
func setIPv4Version(carrier []byte, offsetToIP int) {
|
||||
carrier[offsetToIP] = 0x45 // version=4, IHL=5 (20 bytes)
|
||||
}
|
||||
|
||||
func TestRSTQueue_QueueAndDrain(t *testing.T) {
|
||||
var q RSTQueue
|
||||
if q.Pending() != 0 {
|
||||
t.Fatal("new queue should be empty")
|
||||
}
|
||||
|
||||
srcAddr := [4]byte{10, 0, 0, 1}
|
||||
q.Queue(srcAddr[:], 8080, 1234, 100, 200, FlagRST|FlagACK)
|
||||
if q.Pending() != 1 {
|
||||
t.Fatalf("expected 1 pending, got %d", q.Pending())
|
||||
}
|
||||
|
||||
carrier := make([]byte, 256)
|
||||
const offsetToIP = 14
|
||||
const offsetToTCP = 34
|
||||
setIPv4Version(carrier, offsetToIP)
|
||||
|
||||
n, err := q.Drain(carrier, offsetToIP, offsetToTCP)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n != sizeHeaderTCP {
|
||||
t.Fatalf("expected %d bytes written, got %d", sizeHeaderTCP, n)
|
||||
}
|
||||
if q.Pending() != 0 {
|
||||
t.Fatal("queue should be empty after drain")
|
||||
}
|
||||
|
||||
tfrm, err := NewFrame(carrier[offsetToTCP:])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if tfrm.SourcePort() != 1234 {
|
||||
t.Errorf("source port = %d; want 1234", tfrm.SourcePort())
|
||||
}
|
||||
if tfrm.DestinationPort() != 8080 {
|
||||
t.Errorf("dest port = %d; want 8080", tfrm.DestinationPort())
|
||||
}
|
||||
seg := tfrm.Segment(0)
|
||||
if seg.SEQ != 100 {
|
||||
t.Errorf("SEQ = %d; want 100", seg.SEQ)
|
||||
}
|
||||
if seg.ACK != 200 {
|
||||
t.Errorf("ACK = %d; want 200", seg.ACK)
|
||||
}
|
||||
if !seg.Flags.HasAll(FlagRST | FlagACK) {
|
||||
t.Errorf("flags = %s; want RST|ACK", seg.Flags)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSTQueue_DrainEmpty(t *testing.T) {
|
||||
var q RSTQueue
|
||||
carrier := make([]byte, 256)
|
||||
n, err := q.Drain(carrier, 14, 34)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n != 0 {
|
||||
t.Fatalf("drain of empty queue should return 0, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSTQueue_DrainNegativeOffset(t *testing.T) {
|
||||
var q RSTQueue
|
||||
q.Queue([]byte{10, 0, 0, 1}, 80, 1234, 0, 0, FlagRST)
|
||||
carrier := make([]byte, 256)
|
||||
n, err := q.Drain(carrier, -1, 34)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n != 0 {
|
||||
t.Fatalf("drain with negative offsetToIP should return 0, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSTQueue_Full(t *testing.T) {
|
||||
var q RSTQueue
|
||||
addr := []byte{10, 0, 0, 1}
|
||||
for i := 0; i < 4; i++ {
|
||||
q.Queue(addr, uint16(i), 1234, Value(i), 0, FlagRST)
|
||||
}
|
||||
if q.Pending() != 4 {
|
||||
t.Fatalf("expected 4 pending, got %d", q.Pending())
|
||||
}
|
||||
|
||||
// Overflow should be silently dropped.
|
||||
q.Queue(addr, 9999, 1234, 0, 0, FlagRST)
|
||||
if q.Pending() != 4 {
|
||||
t.Fatalf("expected 4 pending after overflow, got %d", q.Pending())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSTQueue_NonIPv4Dropped(t *testing.T) {
|
||||
var q RSTQueue
|
||||
addr6 := make([]byte, 16)
|
||||
q.Queue(addr6, 80, 1234, 0, 0, FlagRST)
|
||||
if q.Pending() != 0 {
|
||||
t.Fatalf("non-IPv4 should be dropped, got %d pending", q.Pending())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSTQueue_LIFO(t *testing.T) {
|
||||
var q RSTQueue
|
||||
addr := []byte{10, 0, 0, 1}
|
||||
q.Queue(addr, 1000, 1234, 0, 0, FlagRST)
|
||||
q.Queue(addr, 2000, 1234, 0, 0, FlagRST)
|
||||
|
||||
carrier := make([]byte, 256)
|
||||
const offsetToIP = 14
|
||||
const offsetToTCP = 34
|
||||
setIPv4Version(carrier, offsetToIP)
|
||||
|
||||
// Drain returns last-in first (LIFO).
|
||||
n, err := q.Drain(carrier, offsetToIP, offsetToTCP)
|
||||
if err != nil || n == 0 {
|
||||
t.Fatal("drain failed")
|
||||
}
|
||||
tfrm, _ := NewFrame(carrier[offsetToTCP:])
|
||||
if tfrm.DestinationPort() != 2000 {
|
||||
t.Errorf("expected LIFO order: first drain dest port = %d; want 2000", tfrm.DestinationPort())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSTQueue_MultipleDrains(t *testing.T) {
|
||||
var q RSTQueue
|
||||
addr := []byte{10, 0, 0, 1}
|
||||
q.Queue(addr, 1000, 100, 0, 0, FlagRST)
|
||||
q.Queue(addr, 2000, 200, 0, 0, FlagRST)
|
||||
q.Queue(addr, 3000, 300, 0, 0, FlagRST)
|
||||
|
||||
carrier := make([]byte, 256)
|
||||
const offsetToIP = 14
|
||||
const offsetToTCP = 34
|
||||
|
||||
// Drain all 3 entries.
|
||||
for i := 0; i < 3; i++ {
|
||||
setIPv4Version(carrier, offsetToIP)
|
||||
n, err := q.Drain(carrier, offsetToIP, offsetToTCP)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n != sizeHeaderTCP {
|
||||
t.Fatalf("drain %d: expected %d bytes, got %d", i, sizeHeaderTCP, n)
|
||||
}
|
||||
}
|
||||
if q.Pending() != 0 {
|
||||
t.Fatal("queue should be empty")
|
||||
}
|
||||
|
||||
// Fourth drain should return 0.
|
||||
n, _ := q.Drain(carrier, offsetToIP, offsetToTCP)
|
||||
if n != 0 {
|
||||
t.Fatal("expected 0 from empty queue")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package xnet
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/soypat/lneto/tcp"
|
||||
)
|
||||
|
||||
func TestTCPConn_SetDeadline_Established(t *testing.T) {
|
||||
const seed = 9999
|
||||
const MTU = 1500
|
||||
const svPort = 8080
|
||||
client, sv, clconn, svconn := newTCPStacks(t, seed, MTU)
|
||||
tst := testerFrom(t, MTU)
|
||||
|
||||
tst.TestTCPSetupAndEstablish(sv, client, svconn, clconn, svPort, 1337)
|
||||
|
||||
if clconn.State() != tcp.StateEstablished {
|
||||
t.Fatalf("expected StateEstablished, got %s", clconn.State())
|
||||
}
|
||||
|
||||
// SetDeadline should succeed on established connection.
|
||||
err := clconn.SetDeadline(time.Now().Add(time.Hour))
|
||||
if err != nil {
|
||||
t.Fatalf("SetDeadline on established conn failed: %v", err)
|
||||
}
|
||||
|
||||
// Clear deadlines.
|
||||
err = clconn.SetDeadline(time.Time{})
|
||||
if err != nil {
|
||||
t.Fatalf("SetDeadline(zero) failed: %v", err)
|
||||
}
|
||||
_ = sv
|
||||
}
|
||||
|
||||
func TestTCPConn_ReadDeadlineExceeded(t *testing.T) {
|
||||
const seed = 10001
|
||||
const MTU = 1500
|
||||
const svPort = 8080
|
||||
client, sv, clconn, svconn := newTCPStacks(t, seed, MTU)
|
||||
tst := testerFrom(t, MTU)
|
||||
|
||||
tst.TestTCPSetupAndEstablish(sv, client, svconn, clconn, svPort, 1337)
|
||||
|
||||
// Set read deadline in the past.
|
||||
err := svconn.SetReadDeadline(time.Now().Add(-time.Second))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Read should fail immediately with deadline exceeded.
|
||||
buf := make([]byte, 64)
|
||||
_, rerr := svconn.Read(buf)
|
||||
if rerr == nil {
|
||||
t.Fatal("Read with past deadline should fail")
|
||||
}
|
||||
if rerr != os.ErrDeadlineExceeded {
|
||||
t.Fatalf("expected os.ErrDeadlineExceeded, got %v", rerr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTCPConn_WriteDeadlineExceeded(t *testing.T) {
|
||||
const seed = 10002
|
||||
const MTU = 1500
|
||||
const svPort = 8080
|
||||
client, sv, clconn, svconn := newTCPStacks(t, seed, MTU)
|
||||
tst := testerFrom(t, MTU)
|
||||
|
||||
tst.TestTCPSetupAndEstablish(sv, client, svconn, clconn, svPort, 1337)
|
||||
|
||||
// Set write deadline in the past.
|
||||
err := clconn.SetWriteDeadline(time.Now().Add(-time.Second))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Write should fail immediately with deadline exceeded.
|
||||
_, werr := clconn.Write([]byte("hello"))
|
||||
if werr == nil {
|
||||
t.Fatal("Write with past deadline should fail")
|
||||
}
|
||||
if werr != os.ErrDeadlineExceeded {
|
||||
t.Fatalf("expected os.ErrDeadlineExceeded, got %v", werr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTCPConn_FlushEmptyNoop(t *testing.T) {
|
||||
const seed = 10003
|
||||
const MTU = 1500
|
||||
const svPort = 8080
|
||||
client, sv, clconn, svconn := newTCPStacks(t, seed, MTU)
|
||||
tst := testerFrom(t, MTU)
|
||||
|
||||
tst.TestTCPSetupAndEstablish(sv, client, svconn, clconn, svPort, 1337)
|
||||
|
||||
// Flush with no unsent data should return nil immediately.
|
||||
err := clconn.Flush()
|
||||
if err != nil {
|
||||
t.Fatalf("Flush with no unsent data should succeed: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -228,3 +228,78 @@ func TestStackAsyncListener_MultiSequentialConn(t *testing.T) {
|
||||
doRequest(netip.AddrPortFrom(caddr, uint16(sv.Prand32())), 0, []byte("HTTP 1.0\r\n"))
|
||||
}
|
||||
}
|
||||
|
||||
func TestListener_Close(t *testing.T) {
|
||||
const svPort uint16 = 80
|
||||
|
||||
pool, err := NewTCPPool(TCPPoolConfig{
|
||||
PoolSize: 1,
|
||||
QueueSize: 4,
|
||||
TxBufSize: 512,
|
||||
RxBufSize: 512,
|
||||
EstablishedTimeout: 10e9,
|
||||
ClosingTimeout: 10e9,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var listener tcp.Listener
|
||||
err = listener.Reset(svPort, pool)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if listener.LocalPort() != svPort {
|
||||
t.Fatalf("expected port %d, got %d", svPort, listener.LocalPort())
|
||||
}
|
||||
|
||||
err = listener.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("Close failed: %v", err)
|
||||
}
|
||||
if listener.LocalPort() != 0 {
|
||||
t.Fatalf("port should be 0 after Close, got %d", listener.LocalPort())
|
||||
}
|
||||
|
||||
// Double close should return net.ErrClosed.
|
||||
err = listener.Close()
|
||||
if err == nil {
|
||||
t.Fatal("double Close should return error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListener_ResetAfterClose(t *testing.T) {
|
||||
const svPort uint16 = 80
|
||||
|
||||
pool, err := NewTCPPool(TCPPoolConfig{
|
||||
PoolSize: 1,
|
||||
QueueSize: 4,
|
||||
TxBufSize: 512,
|
||||
RxBufSize: 512,
|
||||
EstablishedTimeout: 10e9,
|
||||
ClosingTimeout: 10e9,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var listener tcp.Listener
|
||||
err = listener.Reset(svPort, pool)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = listener.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Should be able to Reset after Close.
|
||||
err = listener.Reset(svPort, pool)
|
||||
if err != nil {
|
||||
t.Fatalf("Reset after Close failed: %v", err)
|
||||
}
|
||||
if listener.LocalPort() != svPort {
|
||||
t.Fatalf("expected port %d after re-Reset, got %d", svPort, listener.LocalPort())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user