From a491c4f7337e492663b11a89ff15028855981240 Mon Sep 17 00:00:00 2001 From: sechmachine <97589681+sechmachine727@users.noreply.github.com> Date: Sun, 9 Aug 2026 16:16:04 +0700 Subject: [PATCH] test(gateway): observe qualification batch pacing --- gateway/qualification_contract_test.go | 24 +++++++++++++++- gateway/qualification_harness_test.go | 38 ++++++++++++++------------ 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/gateway/qualification_contract_test.go b/gateway/qualification_contract_test.go index b97efab..26495c0 100644 --- a/gateway/qualification_contract_test.go +++ b/gateway/qualification_contract_test.go @@ -100,6 +100,15 @@ func TestQualificationApolloFixturePacesSourceShapedVideo(t *testing.T) { fixture := &qualificationApolloFixture{video: sender, failures: make(chan error, 1)} remote := *receiver.LocalAddr().(*net.UDPAddr) 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{}) go func() { 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 { t.Fatal(err) } @@ -120,9 +129,22 @@ func TestQualificationApolloFixturePacesSourceShapedVideo(t *testing.T) { } elapsed := time.Since(started) wantCarry := qualificationApolloVideoOffset(len(packets), packetsPerMillisecond) + if wantCarry != 10416666*time.Nanosecond { + t.Fatalf("1000-packet carry = %s, want 10.416666 ms", wantCarry) + } if 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 { case <-drained: case <-time.After(time.Second): diff --git a/gateway/qualification_harness_test.go b/gateway/qualification_harness_test.go index 6f630ef..98611ac 100644 --- a/gateway/qualification_harness_test.go +++ b/gateway/qualification_harness_test.go @@ -414,22 +414,23 @@ func (b *qualificationTracingBackend) session(sessionID string) *nativeApolloSes } type qualificationApolloFixture struct { - sessionID string - management *httptest.Server - stream net.Listener - control *net.UDPConn - audio *net.UDPConn - video *net.UDPConn - videoRemote atomic.Pointer[net.UDPAddr] - key atomic.Pointer[[]byte] - keyReady chan []byte - failures chan error - closed atomic.Bool - closeOnce sync.Once - sentPackets atomic.Uint64 - work protocol.ProviderSessionWork - videoPaceMu sync.Mutex - videoNext time.Time + sessionID string + management *httptest.Server + stream net.Listener + control *net.UDPConn + audio *net.UDPConn + video *net.UDPConn + videoRemote atomic.Pointer[net.UDPAddr] + key atomic.Pointer[[]byte] + keyReady chan []byte + failures chan error + closed atomic.Bool + closeOnce sync.Once + sentPackets atomic.Uint64 + work protocol.ProviderSessionWork + videoPaceMu sync.Mutex + videoNext time.Time + observeVideoBatch func(int, time.Time) controlImpairmentMu sync.Mutex controlRTT time.Duration @@ -775,11 +776,14 @@ func (f *qualificationApolloFixture) sendVideo(ctx context.Context, packets [][] } framePackets := 0 for batchStart := 0; batchStart < len(packets); batchStart += batchSize { + batchEnd := min(batchStart+batchSize, len(packets)) due := frameStart.Add(qualificationApolloVideoOffset(framePackets, packetsPerMillisecond)) if err := qualificationWaitContext(ctx, due); err != nil { return err } - batchEnd := min(batchStart+batchSize, len(packets)) + if f.observeVideoBatch != nil { + f.observeVideoBatch(framePackets, time.Now()) + } for _, packet := range packets[batchStart:batchEnd] { if len(packet) != len(packets[0]) { return ErrProviderMalformed