test(gateway): observe qualification batch pacing
Verify Data Plane / gateway (push) Failing after 1m31s

This commit is contained in:
sechmachine
2026-08-09 16:16:04 +07:00
parent aa4f948fbc
commit a491c4f733
2 changed files with 44 additions and 18 deletions
+23 -1
View File
@@ -100,6 +100,15 @@ func TestQualificationApolloFixturePacesSourceShapedVideo(t *testing.T) {
fixture := &qualificationApolloFixture{video: sender, failures: make(chan error, 1)} fixture := &qualificationApolloFixture{video: sender, failures: make(chan error, 1)}
remote := *receiver.LocalAddr().(*net.UDPAddr) remote := *receiver.LocalAddr().(*net.UDPAddr)
fixture.videoRemote.Store(&remote) fixture.videoRemote.Store(&remote)
var batchIndices []int
var batchStarts []time.Duration
var started time.Time
fixture.observeVideoBatch = func(index int, at time.Time) {
if len(batchIndices) < 3 {
batchIndices = append(batchIndices, index)
batchStarts = append(batchStarts, at.Sub(started))
}
}
drained := make(chan struct{}) drained := make(chan struct{})
go func() { go func() {
defer close(drained) defer close(drained)
@@ -111,7 +120,7 @@ func TestQualificationApolloFixturePacesSourceShapedVideo(t *testing.T) {
} }
}() }()
started := time.Now() started = time.Now()
if err := fixture.sendVideo(context.Background(), packets); err != nil { if err := fixture.sendVideo(context.Background(), packets); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -120,9 +129,22 @@ func TestQualificationApolloFixturePacesSourceShapedVideo(t *testing.T) {
} }
elapsed := time.Since(started) elapsed := time.Since(started)
wantCarry := qualificationApolloVideoOffset(len(packets), packetsPerMillisecond) wantCarry := qualificationApolloVideoOffset(len(packets), packetsPerMillisecond)
if wantCarry != 10416666*time.Nanosecond {
t.Fatalf("1000-packet carry = %s, want 10.416666 ms", wantCarry)
}
if elapsed < wantCarry { if elapsed < wantCarry {
t.Fatalf("source fixture sent the next frame after %s, before Apollo pacing carry %s", elapsed, wantCarry) t.Fatalf("source fixture sent the next frame after %s, before Apollo pacing carry %s", elapsed, wantCarry)
} }
wantIndices := []int{0, 63, 126}
wantStarts := []time.Duration{0, 656250 * time.Nanosecond, 1312500 * time.Nanosecond}
if len(batchIndices) != len(wantIndices) {
t.Fatalf("observed %d batch starts, want %d", len(batchIndices), len(wantIndices))
}
for index := range wantIndices {
if batchIndices[index] != wantIndices[index] || batchStarts[index] < wantStarts[index] {
t.Fatalf("batch starts = indices %v at %v; want indices %v no earlier than %v", batchIndices, batchStarts, wantIndices, wantStarts)
}
}
select { select {
case <-drained: case <-drained:
case <-time.After(time.Second): case <-time.After(time.Second):
+21 -17
View File
@@ -414,22 +414,23 @@ func (b *qualificationTracingBackend) session(sessionID string) *nativeApolloSes
} }
type qualificationApolloFixture struct { type qualificationApolloFixture struct {
sessionID string sessionID string
management *httptest.Server management *httptest.Server
stream net.Listener stream net.Listener
control *net.UDPConn control *net.UDPConn
audio *net.UDPConn audio *net.UDPConn
video *net.UDPConn video *net.UDPConn
videoRemote atomic.Pointer[net.UDPAddr] videoRemote atomic.Pointer[net.UDPAddr]
key atomic.Pointer[[]byte] key atomic.Pointer[[]byte]
keyReady chan []byte keyReady chan []byte
failures chan error failures chan error
closed atomic.Bool closed atomic.Bool
closeOnce sync.Once closeOnce sync.Once
sentPackets atomic.Uint64 sentPackets atomic.Uint64
work protocol.ProviderSessionWork work protocol.ProviderSessionWork
videoPaceMu sync.Mutex videoPaceMu sync.Mutex
videoNext time.Time videoNext time.Time
observeVideoBatch func(int, time.Time)
controlImpairmentMu sync.Mutex controlImpairmentMu sync.Mutex
controlRTT time.Duration controlRTT time.Duration
@@ -775,11 +776,14 @@ func (f *qualificationApolloFixture) sendVideo(ctx context.Context, packets [][]
} }
framePackets := 0 framePackets := 0
for batchStart := 0; batchStart < len(packets); batchStart += batchSize { for batchStart := 0; batchStart < len(packets); batchStart += batchSize {
batchEnd := min(batchStart+batchSize, len(packets))
due := frameStart.Add(qualificationApolloVideoOffset(framePackets, packetsPerMillisecond)) due := frameStart.Add(qualificationApolloVideoOffset(framePackets, packetsPerMillisecond))
if err := qualificationWaitContext(ctx, due); err != nil { if err := qualificationWaitContext(ctx, due); err != nil {
return err return err
} }
batchEnd := min(batchStart+batchSize, len(packets)) if f.observeVideoBatch != nil {
f.observeVideoBatch(framePackets, time.Now())
}
for _, packet := range packets[batchStart:batchEnd] { for _, packet := range packets[batchStart:batchEnd] {
if len(packet) != len(packets[0]) { if len(packet) != len(packets[0]) {
return ErrProviderMalformed return ErrProviderMalformed