diff --git a/src/tests/Dragon-ISO.Engine.Tests/Pipeline/ManagedNearestNeighborFrameScalerTests.cs b/src/tests/Dragon-ISO.Engine.Tests/Pipeline/ManagedNearestNeighborFrameScalerTests.cs index 589abd0..66e520a 100644 --- a/src/tests/Dragon-ISO.Engine.Tests/Pipeline/ManagedNearestNeighborFrameScalerTests.cs +++ b/src/tests/Dragon-ISO.Engine.Tests/Pipeline/ManagedNearestNeighborFrameScalerTests.cs @@ -69,4 +69,41 @@ public class ManagedNearestNeighborFrameScalerTests var centerOffset = (1080 / 2 * 1920 + 1920 / 2) * 4; dst.Pixels.Span[centerOffset].Should().Be(0x10); } + + // ============================================================ + // Zero-dimension source guard + // ============================================================ + // + // A malformed/glitched NDI frame can report a zero dimension. Pre-fix this + // divided by zero in ComputeFitRect (Pillarbox/Letterbox) or drove a + // degenerate copy loop (Stretch). The guard must absorb it as a black frame + // at the target size rather than throw into the pipeline supervisor. + + [Theory] + [InlineData(0, 100)] + [InlineData(100, 0)] + [InlineData(0, 0)] + public void ZeroDimensionSource_DoesNotThrow_AndYieldsBlackTargetSizedFrame(int srcW, int srcH) + { + var scaler = new ManagedNearestNeighborFrameScaler(); + var src = Solid(Math.Max(srcW, 1), Math.Max(srcH, 1), 0x10, 0x20, 0x30, 0xFF) with + { + Width = srcW, + Height = srcH, + }; + + ProcessedFrame dst = default!; + var act = () => dst = scaler.Scale(src, 1280, 720, AspectMode.Pillarbox, 77); + act.Should().NotThrow(); + + dst.Width.Should().Be(1280); + dst.Height.Should().Be(720); + dst.Pixels.Length.Should().Be(1280 * 720 * 4); + dst.TimestampTicks.Should().Be(77); + // Black, fully opaque: B=G=R=0, A=0xFF. + dst.Pixels.Span[0].Should().Be(0x00); + dst.Pixels.Span[1].Should().Be(0x00); + dst.Pixels.Span[2].Should().Be(0x00); + dst.Pixels.Span[3].Should().Be(0xFF); + } }