Files
smux/stream_internal_test.go

205 lines
4.7 KiB
Go

package smux
import (
"bytes"
"io"
"sync/atomic"
"testing"
"time"
)
func newUnitTestStream() *stream {
cfg := DefaultConfig()
sess := &Session{
config: cfg,
streams: make(map[uint32]*stream),
chSocketReadError: make(chan struct{}),
chSocketWriteError: make(chan struct{}),
chProtoError: make(chan struct{}),
bucketNotify: make(chan struct{}, 1),
}
st := newStream(1, cfg.MaxFrameSize, sess)
sess.streams[st.id] = st
return st
}
func TestStreamWaitReadTimeout(t *testing.T) {
s := newUnitTestStream()
s.readDeadline.Store(time.Now().Add(20 * time.Millisecond))
if err := s.waitRead(); err != ErrTimeout {
t.Fatalf("expected ErrTimeout, got %v", err)
}
}
func TestStreamWaitReadFinWithBufferedData(t *testing.T) {
s := newUnitTestStream()
buf := []byte("abc")
s.pushBytes(&buf)
s.fin()
if err := s.waitRead(); err != nil {
t.Fatalf("expected nil after fin with buffered data, got %v", err)
}
readBuf := make([]byte, 3)
n, err := s.tryReadV1(readBuf)
if err != nil {
t.Fatalf("read failed: %v", err)
}
if n != 3 {
t.Fatalf("expected 3 bytes, got %d", n)
}
if !bytes.Equal(readBuf, []byte("abc")) {
t.Fatalf("read mismatch: %q", readBuf)
}
}
func TestStreamRecycleTokens(t *testing.T) {
s := newUnitTestStream()
b1 := []byte("hello")
b2 := []byte("world!")
s.pushBytes(&b1)
s.pushBytes(&b2)
n := s.recycleTokens()
if n != len(b1)+len(b2) {
t.Fatalf("unexpected recycled bytes: %d", n)
}
if s.bufferRing.len() != 0 {
t.Fatalf("expected empty buffer ring, got %d", s.bufferRing.len())
}
}
func TestStreamUpdateNotifiesWriter(t *testing.T) {
s := newUnitTestStream()
s.update(7, 9)
if got := atomic.LoadUint32(&s.peerConsumed); got != 7 {
t.Fatalf("peerConsumed mismatch: %d", got)
}
if got := atomic.LoadUint32(&s.peerWindow); got != 9 {
t.Fatalf("peerWindow mismatch: %d", got)
}
select {
case <-s.chUpdate:
// ok
default:
t.Fatal("expected chUpdate notification")
}
}
func TestStreamSetDeadlineWakesUp(t *testing.T) {
s := newUnitTestStream()
if err := s.SetDeadline(time.Now().Add(time.Second)); err != nil {
t.Fatalf("SetDeadline failed: %v", err)
}
select {
case <-s.chReaderWakeup:
// ok
default:
t.Fatal("expected reader wakeup")
}
select {
case <-s.chWriterWakeup:
// ok
default:
t.Fatal("expected writer wakeup")
}
}
func TestStreamWaitReadClosed(t *testing.T) {
s := newUnitTestStream()
close(s.die)
if err := s.waitRead(); err != io.ErrClosedPipe {
t.Fatalf("expected io.ErrClosedPipe, got %v", err)
}
}
func TestSendWindowUpdateTimeout(t *testing.T) {
cfg := DefaultConfig()
sess := &Session{
config: cfg,
streams: make(map[uint32]*stream),
chSocketReadError: make(chan struct{}),
chSocketWriteError: make(chan struct{}),
chProtoError: make(chan struct{}),
bucketNotify: make(chan struct{}, 1),
shaper: nil,
die: make(chan struct{}),
}
st := newStream(1, cfg.MaxFrameSize, sess)
st.readDeadline.Store(time.Now().Add(-time.Second))
if err := st.sendWindowUpdate(1); err != ErrTimeout {
t.Fatalf("expected ErrTimeout, got %v", err)
}
}
func TestStopTimer(t *testing.T) {
stopTimer(nil)
timer := time.NewTimer(time.Nanosecond)
<-timer.C
stopTimer(timer)
active := time.NewTimer(time.Second)
stopTimer(active)
}
func TestNewBufferRingMinCapacity(t *testing.T) {
r := newBufferRing(0)
if len(r.bufs) != 1 {
t.Fatalf("expected capacity 1, got %d", len(r.bufs))
}
}
func newUnitTestStreamV2() *stream {
cfg := DefaultConfig()
cfg.Version = 2
sess := &Session{
config: cfg,
streams: make(map[uint32]*stream),
chSocketReadError: make(chan struct{}),
chSocketWriteError: make(chan struct{}),
chProtoError: make(chan struct{}),
bucketNotify: make(chan struct{}, 1),
die: make(chan struct{}),
}
st := newStream(1, cfg.MaxFrameSize, sess)
sess.streams[st.id] = st
return st
}
func TestWriteV2ClosedPipe(t *testing.T) {
s := newUnitTestStreamV2()
close(s.chWriteClosed)
if _, err := s.writeV2([]byte("x")); err != io.ErrClosedPipe {
t.Fatalf("expected io.ErrClosedPipe, got %v", err)
}
}
func TestWriteV2ConsumedError(t *testing.T) {
s := newUnitTestStreamV2()
atomic.StoreUint32(&s.peerConsumed, 10)
atomic.StoreUint32(&s.numWritten, 0)
atomic.StoreUint32(&s.peerWindow, 0)
if _, err := s.writeV2([]byte("x")); err != ErrConsumed {
t.Fatalf("expected ErrConsumed, got %v", err)
}
}
func TestWriteV2TimeoutWhenWindowZero(t *testing.T) {
s := newUnitTestStreamV2()
atomic.StoreUint32(&s.peerWindow, 0)
s.writeDeadline.Store(time.Now().Add(-time.Second))
if _, err := s.writeV2([]byte("data")); err != ErrTimeout {
t.Fatalf("expected ErrTimeout, got %v", err)
}
}