Skip to content

Commit 12b30df

Browse files
committed
test(wire): add WriteLoop batch splitting test
Signed-off-by: Johan Lindh <johan@linkdata.se>
1 parent f14f7e5 commit 12b30df

1 file changed

Lines changed: 66 additions & 0 deletions

File tree

‎lib/wire/wsio_test.go‎

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net"
88
"net/http"
99
"net/http/httptest"
10+
"strings"
1011
"testing"
1112
"time"
1213

@@ -194,6 +195,71 @@ func TestWriteLoop_ConcatenatesMessagesClosedChannel(t *testing.T) {
194195
waitDone(t, writeDoneCh, "WriteLoop after closed outbound")
195196
}
196197

198+
func TestWriteLoop_SplitsAtBatchLimit(t *testing.T) {
199+
// Drive the outbound backlog well past writeBatchLimit so writeData must split
200+
// it across more than one frame. The channel is buffered, pre-filled and closed,
201+
// so the coalescing loop is deterministic: it stops only on reaching the batch
202+
// limit or draining the (closed) channel, never on a transient empty read.
203+
msg := WsMsg{Jid: jid.Jid(1234), What: what.Inner, Data: strings.Repeat("x", 4096)}
204+
frame := msg.Format()
205+
count := (2*writeBatchLimit)/len(frame) + 2
206+
207+
outCh := make(chan WsMsg, count)
208+
for i := 0; i < count; i++ {
209+
outCh <- msg
210+
}
211+
close(outCh)
212+
213+
jawsDoneCh := make(chan struct{})
214+
client, server := pipe(t)
215+
defer func() { _ = client.CloseNow() }()
216+
defer func() { _ = server.CloseNow() }()
217+
// Frames at the batch limit exceed coder/websocket's default 32 KB per-message
218+
// read limit; in production the browser (not a Go reader) consumes them, so
219+
// lift the limit here to read the server's large outbound frames.
220+
client.SetReadLimit(-1)
221+
222+
ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
223+
defer cancel()
224+
225+
writeDoneCh := make(chan struct{})
226+
go func() {
227+
defer close(writeDoneCh)
228+
WriteLoop(ctx, nil, jawsDoneCh, outCh, server)
229+
}()
230+
231+
var frames [][]byte
232+
var total []byte
233+
for {
234+
mt, b, err := client.Read(ctx)
235+
if err != nil {
236+
break
237+
}
238+
if mt != websocket.MessageText {
239+
t.Fatalf("frame type = %v, want MessageText", mt)
240+
}
241+
frames = append(frames, append([]byte(nil), b...))
242+
total = append(total, b...)
243+
}
244+
245+
waitDone(t, writeDoneCh, "WriteLoop after backlog drained")
246+
247+
if len(frames) < 2 {
248+
t.Fatalf("got %d frame(s), want the backlog split across more than one", len(frames))
249+
}
250+
// Nothing is dropped, duplicated or reordered: the concatenation of every frame
251+
// equals the concatenation of every queued message.
252+
if want := strings.Repeat(frame, count); string(total) != want {
253+
t.Fatalf("reassembled %d bytes across %d frames, want %d bytes", len(total), len(frames), len(want))
254+
}
255+
// Every frame except the last coalesces up to the batch limit before flushing.
256+
for i, f := range frames[:len(frames)-1] {
257+
if len(f) < writeBatchLimit {
258+
t.Fatalf("frame %d is %d bytes, want >= writeBatchLimit (%d)", i, len(f), writeBatchLimit)
259+
}
260+
}
261+
}
262+
197263
func TestWriteLoop_RespectsContext(t *testing.T) {
198264
outCh := make(chan WsMsg)
199265
jawsDoneCh := make(chan struct{})

0 commit comments

Comments
 (0)