using System; using System.IO; using System.Threading; using System.Threading.Tasks; using Paca.Core; using Paca.Core.Utils; using Xunit; namespace Paca.Tests.Core; public class StreamingAndResourceGuardianExtremeRedTests { // ========================================== // 1. TranscodeProfileManager & ThermalGuard // ========================================== [Theory] [InlineData(null)] [InlineData("")] [InlineData(" ")] public void TranscodeProfileManager_GetFfmpegProfileArgs_NullOrWhitespace_ReturnsCopySafeFallback(string? badProfile) { var manager = new TranscodeProfileManager(); var args = manager.GetFfmpegProfileArgs(badProfile!); Assert.NotNull(args); Assert.Equal("-c copy", args); } [Theory] [InlineData(0)] [InlineData(-5)] public void ThermalThrottlingGuard_AdjustedSegments_ZeroOrNegativeRequested_ReturnsAtLeastOne(int badSegments) { var guard = new ThermalThrottlingGuard(); int adjusted = guard.GetAdjustedSegments(badSegments, 25.0); // Normal temp Assert.True(adjusted >= 1, "Segments must be at least 1 even if requested was <= 0"); } // ========================================== // 2. RemoteStreamProxyBuffer Bound & Safety // ========================================== [Fact] public void RemoteStreamProxyBuffer_NullData_DoesNotThrowNullReference() { var buffer = new RemoteStreamProxyBuffer(1024); var ex = Record.Exception(() => buffer.Write(null!)); Assert.Null(ex); Assert.Equal(0, buffer.BufferedBytes); } [Fact] public void RemoteStreamProxyBuffer_ExceedingCapacity_EvictsOldestBytesToRespectCapacity() { const int capacity = 100; var buffer = new RemoteStreamProxyBuffer(capacity); // Write 60 bytes of 0x01 byte[] chunk1 = new byte[60]; Array.Fill(chunk1, (byte)1); buffer.Write(chunk1); Assert.Equal(60, buffer.BufferedBytes); // Write 60 bytes of 0x02 (Total would be 120, exceeding capacity 100) byte[] chunk2 = new byte[60]; Array.Fill(chunk2, (byte)2); buffer.Write(chunk2); // Capacity must NOT be exceeded Assert.True(buffer.BufferedBytes <= capacity, $"Buffered bytes ({buffer.BufferedBytes}) must not exceed capacity ({capacity})"); // Reading remaining bytes should contain the newer bytes (0x02) byte[] readTarget = new byte[capacity]; int readCount = buffer.Read(readTarget, 0, capacity); Assert.Equal(buffer.CapacityBytes, readCount); Assert.Equal(2, readTarget[readCount - 1]); // Last byte must be from chunk2 } // ========================================== // 3. DiskSpaceGuardian OS HResult & Unnesting // ========================================== [Fact] public void DiskSpaceGuardian_IsDiskFullException_HResultCheck_ReturnsTrueEvenWithUnknownLanguageMessage() { // 0x80070070 = HRESULT for ERROR_DISK_FULL (112) var foreignEx = new IOException("Espace disque insuffisant sur le lecteur C:", unchecked((int)0x80070070)); bool isFull = DiskSpaceGuardian.IsDiskFullException(foreignEx); Assert.True(isFull, "Must detect disk full via HResult 0x80070070 regardless of OS language"); } [Fact] public void DiskSpaceGuardian_IsDiskFullException_WrappedInAggregateException_DetectsInnerDiskFull() { var inner = new IOException("There is not enough space on the disk."); var aggregate = new AggregateException("Task failed", inner); bool isFull = DiskSpaceGuardian.IsDiskFullException(aggregate); Assert.True(isFull, "Must unnest AggregateException to find inner disk full exception"); } [Theory] [InlineData(0)] [InlineData(-100)] public void DiskSpaceGuardian_HasSufficientSpace_ZeroOrNegativeBytes_ReturnsTrue(long bytes) { bool result = DiskSpaceGuardian.HasSufficientSpace("C:\\fake\\path.mp4", bytes); Assert.True(result); } [Theory] [InlineData(null)] [InlineData("")] [InlineData(" ")] public void DiskSpaceGuardian_HasSufficientSpace_NullOrWhitespacePath_ReturnsTrue(string? badPath) { bool result = DiskSpaceGuardian.HasSufficientSpace(badPath!, 1024); Assert.True(result); } // ========================================== // 4. ThrottledStream & BufferPool Safety // ========================================== [Fact] public void ThrottledStream_NullBaseStream_ThrowsArgumentNullException() { Assert.Throws(() => new ThrottledStream(null!, 1024)); } [Fact] public void ThrottledStream_ZeroOrNegativeMaxBytes_DoesNotThrowAndReadsNormally() { byte[] source = new byte[] { 1, 2, 3, 4, 5 }; using var ms = new MemoryStream(source); using var throttled = new ThrottledStream(ms, maxBytesPerSecond: 0); // Unthrottled byte[] dest = new byte[5]; int read = throttled.Read(dest, 0, 5); Assert.Equal(5, read); Assert.Equal(source, dest); } [Fact] public void BufferPoolManager_Return_NullArray_DoesNotThrow() { var ex = Record.Exception(() => BufferPoolManager.Return(null!)); Assert.Null(ex); } [Theory] [InlineData(0)] [InlineData(-100)] public void AdaptiveChunkSizingEngine_NegativeOrZeroSpeed_ReturnsMinimum512KB(int speed) { var engine = new AdaptiveChunkSizingEngine(); int size = engine.CalculateOptimalChunkSize(speed); Assert.Equal(512 * 1024, size); } }