Harden movie playback lifecycle and diagnostics
This commit is contained in:
102
engine/Age.Engine.Tests/MovieCorpusGateTests.cs
Normal file
102
engine/Age.Engine.Tests/MovieCorpusGateTests.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using Age.Engine.Sys4;
|
||||
|
||||
public class MovieCorpusGateTests
|
||||
{
|
||||
private sealed class FakeFrameSource(FfmpegMovieInfo info,
|
||||
params FfmpegVideoFrame[] frames) : IFfmpegFrameSource
|
||||
{
|
||||
private readonly Queue<FfmpegVideoFrame> _frames = new(frames);
|
||||
public FfmpegMovieInfo Info { get; } = info;
|
||||
public bool Disposed { get; private set; }
|
||||
public bool TryDecodeNextVideoFrame(out FfmpegVideoFrame frame)
|
||||
=> _frames.TryDequeue(out frame!);
|
||||
public void Dispose() => Disposed = true;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SequenceHeaderProbeReadsIndependentDimensions()
|
||||
{
|
||||
byte[] payload = SyntheticPayload(320, 240);
|
||||
|
||||
Assert.True(MovieCorpusDiscovery.TryReadSequenceDimensions(payload, out int width, out int height));
|
||||
Assert.Equal(320, width);
|
||||
Assert.Equal(240, height);
|
||||
Assert.False(MovieCorpusDiscovery.TryReadSequenceDimensions(new byte[] { 0, 0, 1, 0xba }, out _, out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GateReportsFramesMetadataTimingAndTeardown()
|
||||
{
|
||||
var source = new FakeFrameSource(new FfmpegMovieInfo(16, 16, 100, 20, 1, false),
|
||||
Frame(16, 16, 1, 0), Frame(16, 16, 2, 50));
|
||||
var input = new MovieCorpusInput(0x2bc2, "SYNTH.AGF", () =>
|
||||
new MoviePayload("SYNTH.AGF", SyntheticPayload(16, 16)));
|
||||
|
||||
MovieCorpusReport report = new MovieCorpusGate(_ => source).Run([input], 1, 1000);
|
||||
|
||||
Assert.True(report.Passed);
|
||||
var item = Assert.Single(report.Movies);
|
||||
Assert.True(item.Passed);
|
||||
Assert.Equal("0x2bc2", item.PackedIdHex);
|
||||
Assert.Equal(16, item.ExpectedWidth);
|
||||
Assert.Equal(2, item.FrameCount);
|
||||
Assert.Equal(0, item.FirstPresentationTimeMs);
|
||||
Assert.Equal(50, item.LastPresentationTimeMs);
|
||||
Assert.True(item.FramesChanged);
|
||||
Assert.True(source.Disposed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GateContinuesAfterValidationFailureAndReportsCountMismatch()
|
||||
{
|
||||
var badDimensions = new FakeFrameSource(new FfmpegMovieInfo(8, 16, 100, 20, 1, false),
|
||||
Frame(8, 16, 1, 0));
|
||||
var decreasingTimestamps = new FakeFrameSource(new FfmpegMovieInfo(16, 16, 100, 20, 1, false),
|
||||
Frame(16, 16, 1, 50), Frame(16, 16, 2, 40));
|
||||
var sources = new Queue<IFfmpegFrameSource>([badDimensions, decreasingTimestamps]);
|
||||
MovieCorpusInput[] inputs =
|
||||
[
|
||||
new(1, "BAD-DIMS.AGF", () => new MoviePayload("BAD-DIMS.AGF", SyntheticPayload(16, 16))),
|
||||
new(2, "BAD-PTS.AGF", () => new MoviePayload("BAD-PTS.AGF", SyntheticPayload(16, 16))),
|
||||
];
|
||||
|
||||
MovieCorpusReport report = new MovieCorpusGate(_ => sources.Dequeue()).Run(inputs, 3, 1000);
|
||||
|
||||
Assert.False(report.Passed);
|
||||
Assert.Equal(2, report.FailedCount);
|
||||
Assert.Contains("expected 3", Assert.Single(report.SelectionErrors));
|
||||
Assert.Contains("differ", report.Movies[0].Error);
|
||||
Assert.Contains("timestamp", report.Movies[1].Error);
|
||||
Assert.True(badDimensions.Disposed);
|
||||
Assert.True(decreasingTimestamps.Disposed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GateRejectsInvalidFrameRateMetadata()
|
||||
{
|
||||
var source = new FakeFrameSource(new FfmpegMovieInfo(16, 16, 100, 0, 1, false),
|
||||
Frame(16, 16, 1, 0));
|
||||
var input = new MovieCorpusInput(1, "BAD-RATE.AGF", () =>
|
||||
new MoviePayload("BAD-RATE.AGF", SyntheticPayload(16, 16)));
|
||||
|
||||
MovieCorpusReport report = new MovieCorpusGate(_ => source).Run([input], 1, 1000);
|
||||
|
||||
Assert.False(report.Passed);
|
||||
Assert.Contains("rate 0/1", Assert.Single(report.Movies).Error);
|
||||
Assert.True(source.Disposed);
|
||||
}
|
||||
|
||||
private static byte[] SyntheticPayload(int width, int height) =>
|
||||
[
|
||||
0, 0, 1, 0xba,
|
||||
0, 0, 1, 0xb3,
|
||||
(byte)(width >> 4),
|
||||
(byte)(((width & 0x0f) << 4) | (height >> 8)),
|
||||
(byte)height,
|
||||
0,
|
||||
];
|
||||
|
||||
private static FfmpegVideoFrame Frame(int width, int height, byte value, long timestamp)
|
||||
=> new(new RgbaImage(width, height,
|
||||
Enumerable.Repeat(value, checked(width * height * 4)).ToArray()), timestamp);
|
||||
}
|
||||
Reference in New Issue
Block a user