Skip to content

Commit 5eabb89

Browse files
author
Datanoise
committed
test(relay): cover freshness gate, OggS version validation, BeginSession
Regression tests for the audio path fix: - idle-source Subscribe returns Head instead of bursting stale audio - fresh-source Subscribe still bursts as requested - Broadcast rejects 'OggS' followed by a non-zero version byte and records the real header that follows - BeginSession wipes OggHead / PageOffsets / LastPageOffset / PageIndex and snaps MinListenerOffset + flushGen forward
1 parent 86911d2 commit 5eabb89

1 file changed

Lines changed: 126 additions & 0 deletions

File tree

relay/stream_test.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package relay
33
import (
44
"sync"
55
"testing"
6+
"time"
67
)
78

89
func TestSubscribeOggEmptyPageOffsetsFallsBackToBurst(t *testing.T) {
@@ -81,3 +82,128 @@ func TestSubscribeAfterCloseRejects(t *testing.T) {
8182
t.Fatal("expected SubscribeSafe to return false after Close")
8283
}
8384
}
85+
86+
// TestSubscribeSkipsBurstWhenSourceIdle is the regression test for the
87+
// stale-burst replay bug: when the producer has been silent for >2s, the
88+
// bytes in the buffer are stale and a new subscriber must start at Head
89+
// rather than being bursted with that old audio.
90+
func TestSubscribeSkipsBurstWhenSourceIdle(t *testing.T) {
91+
r := NewRelay(false, nil)
92+
s := r.GetOrCreateStream("/idle")
93+
94+
s.Broadcast(make([]byte, 8192), r)
95+
// Backdate LastDataReceived past the freshness window.
96+
s.mu.Lock()
97+
s.LastDataReceived = time.Now().Add(-10 * time.Second)
98+
s.mu.Unlock()
99+
100+
head := s.Buffer.Head
101+
start, _ := s.Subscribe("listener-idle", 4096)
102+
if start != head {
103+
t.Fatalf("idle-source Subscribe should start at Head=%d, got %d (would replay %d stale bytes)",
104+
head, start, head-start)
105+
}
106+
}
107+
108+
// TestSubscribeBurstsWhenSourceFresh confirms the normal burst path still
109+
// works on an active stream — the freshness gate must not break instant
110+
// playback for the common case.
111+
func TestSubscribeBurstsWhenSourceFresh(t *testing.T) {
112+
r := NewRelay(false, nil)
113+
s := r.GetOrCreateStream("/fresh")
114+
115+
// Broadcast sets LastDataReceived to time.Now() itself.
116+
s.Broadcast(make([]byte, 8192), r)
117+
118+
head := s.Buffer.Head
119+
start, _ := s.Subscribe("listener-fresh", 4096)
120+
if start >= head {
121+
t.Fatalf("fresh-source Subscribe should burst, start=%d head=%d", start, head)
122+
}
123+
if head-start != 4096 {
124+
t.Fatalf("expected 4096-byte burst, got %d", head-start)
125+
}
126+
}
127+
128+
// TestBroadcastIgnoresFalseOggMagic is the regression test for the false-
129+
// positive Ogg page detection bug. A "OggS" byte sequence followed by a
130+
// non-zero byte (a real Opus payload pattern) must NOT be recorded into
131+
// PageOffsets; a real header (version byte 0) must.
132+
func TestBroadcastIgnoresFalseOggMagic(t *testing.T) {
133+
r := NewRelay(false, nil)
134+
s := r.GetOrCreateStream("/ogg-validation")
135+
s.ContentType = "audio/ogg"
136+
s.IsOggStream = true
137+
138+
// False positive: "OggS" + 0xFF (version != 0). Must not be tracked.
139+
payload := make([]byte, 64)
140+
payload[10] = 'O'
141+
payload[11] = 'g'
142+
payload[12] = 'g'
143+
payload[13] = 'S'
144+
payload[14] = 0xFF
145+
s.Broadcast(payload, r)
146+
147+
for i, po := range s.PageOffsets {
148+
if po != 0 {
149+
t.Fatalf("PageOffsets[%d]=%d — a false-positive OggS was tracked", i, po)
150+
}
151+
}
152+
153+
// Real header: "OggS" + 0x00. Must be tracked.
154+
real := make([]byte, 32)
155+
real[5] = 'O'
156+
real[6] = 'g'
157+
real[7] = 'g'
158+
real[8] = 'S'
159+
real[9] = 0x00
160+
expected := s.Buffer.Head + 5
161+
s.Broadcast(real, r)
162+
163+
if s.LastPageOffset != expected {
164+
t.Fatalf("LastPageOffset=%d, expected %d", s.LastPageOffset, expected)
165+
}
166+
}
167+
168+
// TestBeginSessionWipesOggState is the regression test for the transcoder-
169+
// restart bug: a new producer session must clear PageOffsets, LastPageOffset,
170+
// OggHead, OggHeaderOffset so new subscribers can't align to stale bytes
171+
// from a previous Ogg serial. MinListenerOffset must also snap to Head so
172+
// existing listeners jump forward on their next flushGen check.
173+
func TestBeginSessionWipesOggState(t *testing.T) {
174+
r := NewRelay(false, nil)
175+
s := r.GetOrCreateStream("/session")
176+
s.ContentType = "audio/ogg"
177+
s.IsOggStream = true
178+
179+
s.StoreOggHead([]byte{1, 2, 3, 4}, 100)
180+
s.mu.Lock()
181+
s.LastPageOffset = 999
182+
s.PageOffsets[0] = 999
183+
s.PageIndex = 7
184+
s.mu.Unlock()
185+
186+
beforeGen := s.FlushGen()
187+
s.BeginSession()
188+
189+
if got := s.GetOggHead(); got != nil {
190+
t.Fatalf("OggHead should be nil after BeginSession, got %v", got)
191+
}
192+
s.mu.RLock()
193+
defer s.mu.RUnlock()
194+
if s.LastPageOffset != 0 {
195+
t.Fatalf("LastPageOffset should be reset, got %d", s.LastPageOffset)
196+
}
197+
if s.PageOffsets[0] != 0 {
198+
t.Fatalf("PageOffsets[0] should be reset, got %d", s.PageOffsets[0])
199+
}
200+
if s.PageIndex != 0 {
201+
t.Fatalf("PageIndex should be reset, got %d", s.PageIndex)
202+
}
203+
if s.MinListenerOffset != s.Buffer.Head {
204+
t.Fatalf("MinListenerOffset=%d should equal Head=%d", s.MinListenerOffset, s.Buffer.Head)
205+
}
206+
if s.flushGen.Load() <= beforeGen {
207+
t.Fatalf("flushGen should have advanced (was %d, now %d)", beforeGen, s.flushGen.Load())
208+
}
209+
}

0 commit comments

Comments
 (0)