Files
LlamaCasty/ytLive.Tests/ClipRingTests.cs
T
gramps b93744ec7f feat(65): the replay ring — encoded fragments in RAM, capped by bytes
Slice 1 of TASK 65, pure memory, no encoder change yet.

A 1080p BGRA frame is 8.3 MB, so fifteen seconds of raw composite is ~7.4 GB — the 15 MB figure in the
spec is the *compressed* stream. So the ring holds fragmented-MP4 bytes that ffmpeg will write to a
pipe, and a save is init-segment + surviving fragments in order.

Two rules the tests pin: the cap is by **bytes** (bitrate moves; seconds do not), and the newest
fragments always win, because a clip that ends at the keypress is the entire point. The init segment does
not rotate and cannot be reused across encoder starts, so `Clear` drops it too — an MP4 without its moov
is noise.

Next slice: ffmpeg writes the buffer output to stdout while `-stats` stays on stderr, and the pump feeds
this ring. Not done here on purpose — an encoder change is the riskiest edit in the app and deserves its
own commit and test.
2026-11-10 11:59:02 -08:00

69 lines
2.1 KiB
C#

using System;
using Xunit;
using ytLive.Services;
namespace ytLive.Tests;
/// <summary>
/// TASK 65 slice 1 — the replay ring as pure memory.
///
/// <para>The two rules that matter and are easy to break later: the cap is by <b>bytes</b> (bitrate moves,
/// seconds do not), and a snapshot is always init-segment-first, because an MP4 without its moov is not a
/// video, it is noise.</para>
/// </summary>
public sealed class ClipRingTests
{
[Fact]
public void Snapshot_IsInitThenFragmentsInOrder()
{
var ring = new ClipRing(1024);
ring.SetInit(new byte[] { 1, 2 });
ring.Append(new byte[] { 3 });
ring.Append(new byte[] { 4, 5 });
Assert.Equal(new byte[] { 1, 2, 3, 4, 5 }, ring.Snapshot());
Assert.Equal(5, ring.Bytes);
Assert.Equal(2, ring.FragmentCount);
}
[Fact]
public void TheCapDropsOldestFragments_SoTheClipEndsAtTheKeypress()
{
var ring = new ClipRing(capBytes: 10);
ring.SetInit(new byte[2]);
for (var i = 0; i < 5; i++) ring.Append(new byte[3]); // 2 + 15 = 17 → trims
Assert.True(ring.Bytes <= 10);
Assert.Equal(2, ring.FragmentCount); // newest two survive
var snapshot = ring.Snapshot();
Assert.Equal(2 + 3 + 3, snapshot.Length);
}
[Fact]
public void NothingBuffered_SnapshotsEmpty_SoTheCallerCannotWriteAZeroByteFile()
{
var ring = new ClipRing(1024);
Assert.True(ring.IsEmpty);
Assert.Empty(ring.Snapshot());
ring.SetInit(new byte[4]);
Assert.True(ring.IsEmpty); // init alone is not a clip
Assert.Empty(ring.Snapshot());
}
[Fact]
public void ClearForgetsTheInit_SoTheNextEncoderStartCannotReuseAStaleHeader()
{
var ring = new ClipRing(1024);
ring.SetInit(new byte[] { 9 });
ring.Append(new byte[] { 8 });
ring.Clear();
Assert.True(ring.IsEmpty);
Assert.Equal(0, ring.Bytes);
ring.Append(new byte[] { 7 });
Assert.Empty(ring.Snapshot()); // still no init → not playable
}
}