detached 5f61847

test(dgram): use state-bearing flood outputs

commit
5f6184712a112f99dd63a644e0695759c1ab6c58
author
brice · · 2026-07-10 05:56 UTC
parent
be2c9fb
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
1 file · +28 -4
M internal/adapters/dgram/network_sim_test.go +28 -4
@@ -428,7 +428,7 @@ func TestTransportFloodClassification(t *testing.T) {
428428 }
429429 a.mu.Unlock()
430430 go func() {
431- freshReturned <- a.Send(ports.Frame{Type: ports.MsgOutput, Payload: floodPayload(128)})
431+ freshReturned <- a.Send(ports.Frame{Type: ports.MsgOutput, Payload: floodOutputPayload(floodRecordCount, 128)})
432432 }()
433433 awaitSignal(t, freshAtPace, "fresh output pacing barrier")
434434 a.mu.Lock()
@@ -479,7 +479,31 @@ func floodState(sender, receiver *Transport, link *simulatedLink) floodTransport
479479 return floodTransportState{packetAge: packetAge, recordAge: recordAge, ackAge: ackAge, queuedBytes: queuedBytes, retransmits: retransmits}
480480 }
481481
482-func floodPayload(mtu int) []byte { return make([]byte, floodRecordMTUs*mtu) }
482+func floodOutputPayload(baseState, mtu int) []byte {
483+ return ports.MarshalOutput(ports.Output{
484+ BaseStateNum: uint64(baseState),
485+ NewStateNum: uint64(baseState + 1),
486+ Data: make([]byte, floodRecordMTUs*mtu),
487+ })
488+}
489+
490+func TestFloodOutputPayloadIsIncrementalStateBearingOutput(t *testing.T) {
491+ const mtu = 128
492+ for state := range floodRecordCount {
493+ payload := floodOutputPayload(state, mtu)
494+ output, err := ports.UnmarshalOutput(payload)
495+ if err != nil {
496+ t.Fatalf("state %d: %v", state, err)
497+ }
498+ if output.BaseStateNum != uint64(state) || output.NewStateNum != uint64(state+1) {
499+ t.Fatalf("output state = %d -> %d, want %d -> %d", output.BaseStateNum, output.NewStateNum, state, state+1)
500+ }
501+ minimumBytes := floodRecordMTUs * mtu
502+ if len(output.Data) < minimumBytes || len(payload) < minimumBytes {
503+ t.Fatalf("state %d: data bytes=%d encoded bytes=%d, want each at least %d", state, len(output.Data), len(payload), minimumBytes)
504+ }
505+ }
506+}
483507
484508 func sendFloodOutputs(t *testing.T, sender *Transport, count, mtu int) {
485509 t.Helper()
@@ -487,11 +511,11 @@ func sendFloodOutputs(t *testing.T, sender *Transport, count, mtu int) {
487511 sender.dataPaceRemaining = count * floodRecordMTUs * 4
488512 sender.dataPaceNext = sender.clock.Now().Add(time.Hour)
489513 sender.dataPaceMu.Unlock()
490- for range count {
514+ for state := range count {
491515 sender.mu.Lock()
492516 sender.outputNext = time.Time{}
493517 sender.mu.Unlock()
494- if err := sender.Send(ports.Frame{Type: ports.MsgOutput, Payload: floodPayload(mtu)}); err != nil {
518+ if err := sender.Send(ports.Frame{Type: ports.MsgOutput, Payload: floodOutputPayload(state, mtu)}); err != nil {
495519 t.Fatal(err)
496520 }
497521 }
@@ -428,7 +428,7 @@ func TestTransportFloodClassification(t *testing.T) {
428 }428 }
429 a.mu.Unlock()429 a.mu.Unlock()
430 go func() {430 go func() {
431- freshReturned <- a.Send(ports.Frame{Type: ports.MsgOutput, Payload: floodPayload(128)})431+ freshReturned <- a.Send(ports.Frame{Type: ports.MsgOutput, Payload: floodOutputPayload(floodRecordCount, 128)})
432 }()432 }()
433 awaitSignal(t, freshAtPace, "fresh output pacing barrier")433 awaitSignal(t, freshAtPace, "fresh output pacing barrier")
434 a.mu.Lock()434 a.mu.Lock()
@@ -479,7 +479,31 @@ func floodState(sender, receiver *Transport, link *simulatedLink) floodTransport
479 return floodTransportState{packetAge: packetAge, recordAge: recordAge, ackAge: ackAge, queuedBytes: queuedBytes, retransmits: retransmits}479 return floodTransportState{packetAge: packetAge, recordAge: recordAge, ackAge: ackAge, queuedBytes: queuedBytes, retransmits: retransmits}
480 }480 }
481 481
482-func floodPayload(mtu int) []byte { return make([]byte, floodRecordMTUs*mtu) }482+func floodOutputPayload(baseState, mtu int) []byte {
483+ return ports.MarshalOutput(ports.Output{
484+ BaseStateNum: uint64(baseState),
485+ NewStateNum: uint64(baseState + 1),
486+ Data: make([]byte, floodRecordMTUs*mtu),
487+ })
488+}
489+
490+func TestFloodOutputPayloadIsIncrementalStateBearingOutput(t *testing.T) {
491+ const mtu = 128
492+ for state := range floodRecordCount {
493+ payload := floodOutputPayload(state, mtu)
494+ output, err := ports.UnmarshalOutput(payload)
495+ if err != nil {
496+ t.Fatalf("state %d: %v", state, err)
497+ }
498+ if output.BaseStateNum != uint64(state) || output.NewStateNum != uint64(state+1) {
499+ t.Fatalf("output state = %d -> %d, want %d -> %d", output.BaseStateNum, output.NewStateNum, state, state+1)
500+ }
501+ minimumBytes := floodRecordMTUs * mtu
502+ if len(output.Data) < minimumBytes || len(payload) < minimumBytes {
503+ t.Fatalf("state %d: data bytes=%d encoded bytes=%d, want each at least %d", state, len(output.Data), len(payload), minimumBytes)
504+ }
505+ }
506+}
483 507
484 func sendFloodOutputs(t *testing.T, sender *Transport, count, mtu int) {508 func sendFloodOutputs(t *testing.T, sender *Transport, count, mtu int) {
485 t.Helper()509 t.Helper()
@@ -487,11 +511,11 @@ func sendFloodOutputs(t *testing.T, sender *Transport, count, mtu int) {
487 sender.dataPaceRemaining = count * floodRecordMTUs * 4511 sender.dataPaceRemaining = count * floodRecordMTUs * 4
488 sender.dataPaceNext = sender.clock.Now().Add(time.Hour)512 sender.dataPaceNext = sender.clock.Now().Add(time.Hour)
489 sender.dataPaceMu.Unlock()513 sender.dataPaceMu.Unlock()
490- for range count {514+ for state := range count {
491 sender.mu.Lock()515 sender.mu.Lock()
492 sender.outputNext = time.Time{}516 sender.outputNext = time.Time{}
493 sender.mu.Unlock()517 sender.mu.Unlock()
494- if err := sender.Send(ports.Frame{Type: ports.MsgOutput, Payload: floodPayload(mtu)}); err != nil {518+ if err := sender.Send(ports.Frame{Type: ports.MsgOutput, Payload: floodOutputPayload(state, mtu)}); err != nil {
495 t.Fatal(err)519 t.Fatal(err)
496 }520 }
497 }521 }

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close