using System; using Xunit; using ytLive.Helpers; namespace ytLive.Tests; /// /// 2026-11-05: a 1080p video layer played at 20.9 fps on a 30 fps clip because pacing slept a full /// frame interval ON TOP of the frame's own work. These pin deadline pacing: sleep the remainder of /// the interval, resync when work overruns, never burst to catch up. /// public class FramePacerTests { private static readonly TimeSpan Frame = TimeSpan.FromMilliseconds(33); [Fact] public void FirstFrame_WaitsAFullInterval() => Assert.Equal(Frame, new FramePacer().DelayFor(Frame, TimeSpan.Zero)); [Fact] public void WorkInsideTheInterval_IsSubtractedFromTheWait() { var pacer = new FramePacer(); pacer.DelayFor(Frame, TimeSpan.Zero); // schedules the next frame at 33ms // The next frame is emitted 20ms later: only 13ms of waiting is left, not another 33ms. var wait = pacer.DelayFor(Frame, TimeSpan.FromMilliseconds(20)); Assert.Equal(TimeSpan.FromMilliseconds(13), wait); } [Fact] public void WorkOnSchedule_DoesNotAccumulateDrift() { var pacer = new FramePacer(); Assert.Equal(Frame, pacer.DelayFor(Frame, TimeSpan.Zero)); // A frame arriving exactly on its deadline waits not at all… Assert.Equal(TimeSpan.Zero, pacer.DelayFor(Frame, TimeSpan.FromMilliseconds(33))); // …and a frame arriving 3ms early waits exactly the 3ms that is left, every time: the // deadline advances one interval per frame, so no drift accumulates. Assert.Equal(TimeSpan.FromMilliseconds(3), pacer.DelayFor(Frame, TimeSpan.FromMilliseconds(63))); Assert.Equal(TimeSpan.FromMilliseconds(3), pacer.DelayFor(Frame, TimeSpan.FromMilliseconds(96))); } [Fact] public void FallingBehind_ReturnsZeroAndResyncs() { var pacer = new FramePacer(); pacer.DelayFor(Frame, TimeSpan.Zero); // The frame took 200ms: it is late, so no sleep — and the schedule restarts from now. var late = pacer.DelayFor(Frame, TimeSpan.FromMilliseconds(200)); Assert.Equal(TimeSpan.Zero, late); var next = pacer.DelayFor(Frame, TimeSpan.FromMilliseconds(200)); Assert.Equal(Frame, next); } [Fact] public void ZeroFrameDuration_NeverWaits() => Assert.Equal(TimeSpan.Zero, new FramePacer().DelayFor(TimeSpan.Zero, TimeSpan.Zero)); [Fact] public void Reset_RestartsTheSchedule() { var pacer = new FramePacer(); pacer.DelayFor(Frame, TimeSpan.Zero); pacer.Reset(); Assert.Equal(Frame, pacer.DelayFor(Frame, TimeSpan.FromSeconds(10))); } }