mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-02-08 05:27:04 +00:00
847 lines
21 KiB
C#
847 lines
21 KiB
C#
using System;
|
|
using System.Buffers;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Xunit;
|
|
|
|
namespace SharpCompress.Test;
|
|
|
|
public class UtilityTests
|
|
{
|
|
#region URShift Tests
|
|
|
|
[Fact]
|
|
public void URShift_Int_PositiveNumber_ShiftsCorrectly()
|
|
{
|
|
var result = Utility.URShift(16, 2);
|
|
Assert.Equal(4, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void URShift_Int_NegativeNumber_PerformsUnsignedShift()
|
|
{
|
|
// -1 in binary is all 1s (0xFFFFFFFF), shifted right by 1 should be 0x7FFFFFFF
|
|
var result = Utility.URShift(-1, 1);
|
|
Assert.Equal(int.MaxValue, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void URShift_Int_Zero_ReturnsZero()
|
|
{
|
|
var result = Utility.URShift(0, 5);
|
|
Assert.Equal(0, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void URShift_Long_PositiveNumber_ShiftsCorrectly()
|
|
{
|
|
var result = Utility.URShift(32L, 3);
|
|
Assert.Equal(4L, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void URShift_Long_NegativeNumber_PerformsUnsignedShift()
|
|
{
|
|
var result = Utility.URShift(-1L, 1);
|
|
Assert.Equal(long.MaxValue, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void URShift_Long_Zero_ReturnsZero()
|
|
{
|
|
var result = Utility.URShift(0L, 10);
|
|
Assert.Equal(0L, result);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ReadFully Tests
|
|
|
|
[Fact]
|
|
public void ReadFully_ByteArray_ReadsExactlyRequiredBytes()
|
|
{
|
|
var data = new byte[] { 1, 2, 3, 4, 5 };
|
|
using var stream = new MemoryStream(data);
|
|
var buffer = new byte[5];
|
|
|
|
var result = stream.ReadFully(buffer);
|
|
|
|
Assert.True(result);
|
|
Assert.Equal(data, buffer);
|
|
}
|
|
|
|
[Fact]
|
|
public void ReadFully_ByteArray_ReturnsFalseWhenNotEnoughData()
|
|
{
|
|
var data = new byte[] { 1, 2, 3 };
|
|
using var stream = new MemoryStream(data);
|
|
var buffer = new byte[5];
|
|
|
|
var result = stream.ReadFully(buffer);
|
|
|
|
Assert.False(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void ReadFully_ByteArray_EmptyStream_ReturnsFalse()
|
|
{
|
|
using var stream = new MemoryStream();
|
|
var buffer = new byte[5];
|
|
|
|
var result = stream.ReadFully(buffer);
|
|
|
|
Assert.False(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void ReadFully_ByteArray_EmptyBuffer_ReturnsTrue()
|
|
{
|
|
var data = new byte[] { 1, 2, 3 };
|
|
using var stream = new MemoryStream(data);
|
|
var buffer = new byte[0];
|
|
|
|
var result = stream.ReadFully(buffer);
|
|
|
|
Assert.True(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void ReadFully_Span_ReadsExactlyRequiredBytes()
|
|
{
|
|
var data = new byte[] { 1, 2, 3, 4, 5 };
|
|
using var stream = new MemoryStream(data);
|
|
Span<byte> buffer = new byte[5];
|
|
|
|
var result = stream.ReadFully(buffer);
|
|
|
|
Assert.True(result);
|
|
Assert.Equal(data, buffer.ToArray());
|
|
}
|
|
|
|
[Fact]
|
|
public void ReadFully_Span_ReturnsFalseWhenNotEnoughData()
|
|
{
|
|
var data = new byte[] { 1, 2, 3 };
|
|
using var stream = new MemoryStream(data);
|
|
Span<byte> buffer = new byte[5];
|
|
|
|
var result = stream.ReadFully(buffer);
|
|
|
|
Assert.False(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void ReadFully_Span_EmptyStream_ReturnsFalse()
|
|
{
|
|
using var stream = new MemoryStream();
|
|
Span<byte> buffer = new byte[5];
|
|
|
|
var result = stream.ReadFully(buffer);
|
|
|
|
Assert.False(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void ReadFully_Span_EmptyBuffer_ReturnsTrue()
|
|
{
|
|
var data = new byte[] { 1, 2, 3 };
|
|
using var stream = new MemoryStream(data);
|
|
Span<byte> buffer = new byte[0];
|
|
|
|
var result = stream.ReadFully(buffer);
|
|
|
|
Assert.True(result);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ReadByteAsync Tests
|
|
|
|
[Fact]
|
|
public async ValueTask ReadByteAsync_ReadsOneByte()
|
|
{
|
|
var data = new byte[] { 42, 1, 2, 3 };
|
|
using var stream = new MemoryStream(data);
|
|
using var reader = new BinaryReader(stream);
|
|
|
|
var result = await reader.ReadByteAsync();
|
|
|
|
Assert.Equal(42, result);
|
|
Assert.Equal(1, stream.Position);
|
|
}
|
|
|
|
[Fact]
|
|
public async ValueTask ReadByteAsync_EmptyStream_ThrowsEndOfStreamException()
|
|
{
|
|
using var stream = new MemoryStream();
|
|
using var reader = new BinaryReader(stream);
|
|
|
|
await Assert.ThrowsAsync<EndOfStreamException>(async () => await reader.ReadByteAsync());
|
|
}
|
|
|
|
[Fact]
|
|
public async ValueTask ReadByteAsync_MultipleReads_ReadsSequentially()
|
|
{
|
|
var data = new byte[] { 1, 2, 3 };
|
|
using var stream = new MemoryStream(data);
|
|
using var reader = new BinaryReader(stream);
|
|
|
|
var first = await reader.ReadByteAsync();
|
|
var second = await reader.ReadByteAsync();
|
|
var third = await reader.ReadByteAsync();
|
|
|
|
Assert.Equal(1, first);
|
|
Assert.Equal(2, second);
|
|
Assert.Equal(3, third);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ReadBytesAsync Tests
|
|
|
|
[Fact]
|
|
public async ValueTask ReadBytesAsync_ReadsExactlyRequiredBytes()
|
|
{
|
|
var data = new byte[] { 1, 2, 3, 4, 5 };
|
|
using var stream = new MemoryStream(data);
|
|
using var reader = new BinaryReader(stream);
|
|
|
|
var result = await reader.ReadBytesAsync(3);
|
|
|
|
Assert.Equal(new byte[] { 1, 2, 3 }, result);
|
|
Assert.Equal(3, stream.Position);
|
|
}
|
|
|
|
[Fact]
|
|
public async ValueTask ReadBytesAsync_NotEnoughData_ThrowsEndOfStreamException()
|
|
{
|
|
var data = new byte[] { 1, 2, 3 };
|
|
using var stream = new MemoryStream(data);
|
|
using var reader = new BinaryReader(stream);
|
|
|
|
await Assert.ThrowsAsync<EndOfStreamException>(async () => await reader.ReadBytesAsync(5));
|
|
}
|
|
|
|
[Fact]
|
|
public async ValueTask ReadBytesAsync_EmptyStream_ThrowsEndOfStreamException()
|
|
{
|
|
using var stream = new MemoryStream();
|
|
using var reader = new BinaryReader(stream);
|
|
|
|
await Assert.ThrowsAsync<EndOfStreamException>(async () => await reader.ReadBytesAsync(1));
|
|
}
|
|
|
|
[Fact]
|
|
public async ValueTask ReadBytesAsync_ZeroBytes_ReturnsEmptyArray()
|
|
{
|
|
var data = new byte[] { 1, 2, 3 };
|
|
using var stream = new MemoryStream(data);
|
|
using var reader = new BinaryReader(stream);
|
|
|
|
var result = await reader.ReadBytesAsync(0);
|
|
|
|
Assert.Empty(result);
|
|
Assert.Equal(0, stream.Position);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Skip Tests
|
|
|
|
[Fact]
|
|
public void Skip_SeekableStream_UsesSeek()
|
|
{
|
|
var data = new byte[] { 1, 2, 3, 4, 5 };
|
|
using var stream = new MemoryStream(data);
|
|
|
|
stream.Skip(3);
|
|
|
|
Assert.Equal(3, stream.Position);
|
|
}
|
|
|
|
[Fact]
|
|
public void Skip_SeekableStream_SkipsCorrectAmount()
|
|
{
|
|
var data = new byte[] { 1, 2, 3, 4, 5 };
|
|
using var stream = new MemoryStream(data);
|
|
|
|
stream.Skip(2);
|
|
var buffer = new byte[2];
|
|
stream.Read(buffer);
|
|
|
|
Assert.Equal(new byte[] { 3, 4 }, buffer);
|
|
}
|
|
|
|
[Fact]
|
|
public void Skip_NonSeekableStream_SkipsCorrectAmount()
|
|
{
|
|
var data = new byte[] { 1, 2, 3, 4, 5 };
|
|
using var seekableStream = new MemoryStream(data);
|
|
using var nonSeekableStream = new NonSeekableStream(seekableStream);
|
|
|
|
nonSeekableStream.Skip(2);
|
|
var buffer = new byte[2];
|
|
nonSeekableStream.Read(buffer);
|
|
|
|
Assert.Equal(new byte[] { 3, 4 }, buffer);
|
|
}
|
|
|
|
[Fact]
|
|
public void Skip_NonSeekableStream_SkipsBeyondStreamEnd()
|
|
{
|
|
var data = new byte[] { 1, 2, 3 };
|
|
using var seekableStream = new MemoryStream(data);
|
|
using var nonSeekableStream = new NonSeekableStream(seekableStream);
|
|
|
|
// Should not throw, just skip what's available
|
|
nonSeekableStream.Skip(10);
|
|
|
|
Assert.Equal(-1, nonSeekableStream.ReadByte());
|
|
}
|
|
|
|
[Fact]
|
|
public void Skip_Parameterless_SkipsEntireStream()
|
|
{
|
|
var data = new byte[] { 1, 2, 3, 4, 5 };
|
|
using var stream = new MemoryStream(data);
|
|
|
|
stream.Skip();
|
|
|
|
Assert.Equal(-1, stream.ReadByte());
|
|
}
|
|
|
|
[Fact]
|
|
public void Skip_Zero_DoesNotMove()
|
|
{
|
|
var data = new byte[] { 1, 2, 3, 4, 5 };
|
|
using var stream = new MemoryStream(data);
|
|
stream.Position = 2;
|
|
|
|
stream.Skip(0);
|
|
|
|
Assert.Equal(2, stream.Position);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region SetSize Tests
|
|
|
|
[Fact]
|
|
public void SetSize_GrowsList_AddsZeroBytes()
|
|
{
|
|
var list = new List<byte> { 1, 2, 3 };
|
|
|
|
Utility.SetSize(list, 5);
|
|
|
|
Assert.Equal(5, list.Count);
|
|
Assert.Equal(new byte[] { 1, 2, 3, 0, 0 }, list);
|
|
}
|
|
|
|
[Fact]
|
|
public void SetSize_ShrinksListByOne()
|
|
{
|
|
var list = new List<byte> { 1, 2, 3, 4, 5 };
|
|
|
|
Utility.SetSize(list, 3);
|
|
|
|
Assert.Equal(3, list.Count);
|
|
Assert.Equal(new byte[] { 1, 2, 3 }, list);
|
|
}
|
|
|
|
[Fact]
|
|
public void SetSize_ToZero_ClearsAllItems()
|
|
{
|
|
var list = new List<byte> { 1, 2, 3 };
|
|
|
|
Utility.SetSize(list, 0);
|
|
|
|
Assert.Empty(list);
|
|
}
|
|
|
|
[Fact]
|
|
public void SetSize_SameSize_NoChange()
|
|
{
|
|
var list = new List<byte> { 1, 2, 3 };
|
|
|
|
Utility.SetSize(list, 3);
|
|
|
|
Assert.Equal(3, list.Count);
|
|
Assert.Equal(new byte[] { 1, 2, 3 }, list);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ForEach Tests
|
|
|
|
[Fact]
|
|
public void ForEach_ExecutesActionForEachItem()
|
|
{
|
|
var items = new[] { 1, 2, 3, 4, 5 };
|
|
var results = new List<int>();
|
|
|
|
items.ForEach(x => results.Add(x));
|
|
|
|
Assert.Equal(items, results);
|
|
}
|
|
|
|
[Fact]
|
|
public void ForEach_EmptyCollection_NoExecutions()
|
|
{
|
|
var items = Array.Empty<int>();
|
|
var count = 0;
|
|
|
|
items.ForEach(x => count++);
|
|
|
|
Assert.Equal(0, count);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region AsEnumerable Tests
|
|
|
|
[Fact]
|
|
public void AsEnumerable_SingleItem_YieldsItem()
|
|
{
|
|
var item = 42;
|
|
|
|
var result = item.AsEnumerable().ToList();
|
|
|
|
Assert.Single(result);
|
|
Assert.Equal(42, result[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public void AsEnumerable_String_YieldsString()
|
|
{
|
|
var item = "test";
|
|
|
|
var result = item.AsEnumerable().ToList();
|
|
|
|
Assert.Single(result);
|
|
Assert.Equal("test", result[0]);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region DosDateToDateTime Tests
|
|
|
|
[Fact]
|
|
public void DosDateToDateTime_ValidDate_ConvertsCorrectly()
|
|
{
|
|
// DOS date format: year (7 bits) | month (4 bits) | day (5 bits)
|
|
// DOS time format: hour (5 bits) | minute (6 bits) | second (5 bits, in 2-second increments)
|
|
// This represents: 2020-01-15 10:30:20 (approximately)
|
|
ushort dosDate = (ushort)(((2020 - 1980) << 9) | (1 << 5) | 15); // 2020-01-15
|
|
ushort dosTime = (ushort)((10 << 11) | (30 << 5) | 10); // 10:30:20
|
|
|
|
var result = Utility.DosDateToDateTime(dosDate, dosTime);
|
|
|
|
Assert.Equal(2020, result.Year);
|
|
Assert.Equal(1, result.Month);
|
|
Assert.Equal(15, result.Day);
|
|
Assert.Equal(10, result.Hour);
|
|
Assert.Equal(30, result.Minute);
|
|
Assert.Equal(20, result.Second);
|
|
}
|
|
|
|
[Fact]
|
|
public void DosDateToDateTime_InvalidDate_DefaultsTo1980_01_01()
|
|
{
|
|
ushort dosDate = ushort.MaxValue;
|
|
ushort dosTime = (ushort)((10 << 11) | (30 << 5) | 10);
|
|
|
|
var result = Utility.DosDateToDateTime(dosDate, dosTime);
|
|
|
|
Assert.Equal(1980, result.Year);
|
|
Assert.Equal(1, result.Month);
|
|
Assert.Equal(1, result.Day);
|
|
}
|
|
|
|
[Fact]
|
|
public void DosDateToDateTime_InvalidTime_DefaultsToMidnight()
|
|
{
|
|
ushort dosDate = (ushort)(((2020 - 1980) << 9) | (1 << 5) | 15);
|
|
ushort dosTime = ushort.MaxValue;
|
|
|
|
var result = Utility.DosDateToDateTime(dosDate, dosTime);
|
|
|
|
Assert.Equal(0, result.Hour);
|
|
Assert.Equal(0, result.Minute);
|
|
Assert.Equal(0, result.Second);
|
|
}
|
|
|
|
[Fact]
|
|
public void DosDateToDateTime_FromUint_ConvertsCorrectly()
|
|
{
|
|
ushort dosDate = (ushort)(((2020 - 1980) << 9) | (6 << 5) | 20); // 2020-06-20
|
|
ushort dosTime = (ushort)((14 << 11) | (45 << 5) | 15); // 14:45:30
|
|
uint combined = (uint)(dosDate << 16) | dosTime;
|
|
|
|
var result = Utility.DosDateToDateTime(combined);
|
|
|
|
Assert.Equal(2020, result.Year);
|
|
Assert.Equal(6, result.Month);
|
|
Assert.Equal(20, result.Day);
|
|
Assert.Equal(14, result.Hour);
|
|
Assert.Equal(45, result.Minute);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region DateTimeToDosTime Tests
|
|
|
|
[Fact]
|
|
public void DateTimeToDosTime_ValidDateTime_ConvertsCorrectly()
|
|
{
|
|
//always do local time
|
|
var dt = new DateTime(2020, 6, 15, 14, 30, 20, DateTimeKind.Local);
|
|
|
|
var result = Utility.DateTimeToDosTime(dt);
|
|
|
|
// Verify we can convert back
|
|
var reversed = Utility.DosDateToDateTime(result);
|
|
Assert.Equal(2020, reversed.Year);
|
|
Assert.Equal(6, reversed.Month);
|
|
Assert.Equal(15, reversed.Day);
|
|
Assert.Equal(14, reversed.Hour);
|
|
Assert.Equal(30, reversed.Minute);
|
|
// Seconds are rounded down to nearest even number in DOS format
|
|
Assert.True(reversed.Second == 20 || reversed.Second == 18);
|
|
}
|
|
|
|
[Fact]
|
|
public void DateTimeToDosTime_NullDateTime_ReturnsZero()
|
|
{
|
|
DateTime? dt = null;
|
|
|
|
var result = Utility.DateTimeToDosTime(dt);
|
|
|
|
Assert.Equal(0u, result);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region UnixTimeToDateTime Tests
|
|
|
|
[Fact]
|
|
public void UnixTimeToDateTime_Zero_Returns1970_01_01()
|
|
{
|
|
var result = Utility.UnixTimeToDateTime(0);
|
|
|
|
Assert.Equal(1970, result.Year);
|
|
Assert.Equal(1, result.Month);
|
|
Assert.Equal(1, result.Day);
|
|
Assert.Equal(0, result.Hour);
|
|
Assert.Equal(0, result.Minute);
|
|
Assert.Equal(0, result.Second);
|
|
}
|
|
|
|
[Fact]
|
|
public void UnixTimeToDateTime_ValidTimestamp_ConvertsCorrectly()
|
|
{
|
|
// January 1, 2000 00:00:00 UTC is 946684800 seconds after epoch
|
|
var result = Utility.UnixTimeToDateTime(946684800);
|
|
|
|
Assert.Equal(2000, result.Year);
|
|
Assert.Equal(1, result.Month);
|
|
Assert.Equal(1, result.Day);
|
|
}
|
|
|
|
[Fact]
|
|
public void UnixTimeToDateTime_NegativeTimestamp_ReturnsBeforeEpoch()
|
|
{
|
|
// -86400 is one day before epoch
|
|
var result = Utility.UnixTimeToDateTime(-86400);
|
|
|
|
Assert.Equal(1969, result.Year);
|
|
Assert.Equal(12, result.Month);
|
|
Assert.Equal(31, result.Day);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region TransferTo Tests
|
|
|
|
[Fact]
|
|
public void TransferTo_WithMaxLength_TransfersCorrectAmount()
|
|
{
|
|
var sourceData = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
|
|
using var source = new MemoryStream(sourceData);
|
|
using var destination = new MemoryStream();
|
|
|
|
var transferred = source.TransferTo(destination, 5);
|
|
|
|
Assert.Equal(5, transferred);
|
|
Assert.Equal(new byte[] { 1, 2, 3, 4, 5 }, destination.ToArray());
|
|
}
|
|
|
|
[Fact]
|
|
public void TransferTo_SourceSmallerThanMax_TransfersAll()
|
|
{
|
|
var sourceData = new byte[] { 1, 2, 3 };
|
|
using var source = new MemoryStream(sourceData);
|
|
using var destination = new MemoryStream();
|
|
|
|
var transferred = source.TransferTo(destination, 100);
|
|
|
|
Assert.Equal(3, transferred);
|
|
Assert.Equal(sourceData, destination.ToArray());
|
|
}
|
|
|
|
[Fact]
|
|
public void TransferTo_EmptySource_TransfersNothing()
|
|
{
|
|
using var source = new MemoryStream();
|
|
using var destination = new MemoryStream();
|
|
|
|
var transferred = source.TransferTo(destination, 100);
|
|
|
|
Assert.Equal(0, transferred);
|
|
Assert.Empty(destination.ToArray());
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region SwapUINT32 Tests
|
|
|
|
[Fact]
|
|
public void SwapUINT32_SimpleValue_SwapsEndianness()
|
|
{
|
|
uint value = 0x12345678;
|
|
|
|
var result = Utility.SwapUINT32(value);
|
|
|
|
Assert.Equal(0x78563412u, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void SwapUINT32_Zero_ReturnsZero()
|
|
{
|
|
var result = Utility.SwapUINT32(0);
|
|
|
|
Assert.Equal(0u, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void SwapUINT32_MaxValue_SwapsCorrectly()
|
|
{
|
|
var result = Utility.SwapUINT32(uint.MaxValue);
|
|
|
|
Assert.Equal(uint.MaxValue, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void SwapUINT32_Involution_SwappingTwiceReturnsOriginal()
|
|
{
|
|
uint value = 0x12345678;
|
|
|
|
var result = Utility.SwapUINT32(Utility.SwapUINT32(value));
|
|
|
|
Assert.Equal(value, result);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region SetLittleUInt32 Tests
|
|
|
|
[Fact]
|
|
public void SetLittleUInt32_InsertsValueCorrectly()
|
|
{
|
|
byte[] buffer = new byte[10];
|
|
uint value = 0x12345678;
|
|
|
|
Utility.SetLittleUInt32(ref buffer, value, 2);
|
|
|
|
Assert.Equal(0x78, buffer[2]);
|
|
Assert.Equal(0x56, buffer[3]);
|
|
Assert.Equal(0x34, buffer[4]);
|
|
Assert.Equal(0x12, buffer[5]);
|
|
}
|
|
|
|
[Fact]
|
|
public void SetLittleUInt32_AtOffset_InsertsBehindOffset()
|
|
{
|
|
byte[] buffer = new byte[10];
|
|
uint value = 0xDEADBEEF;
|
|
|
|
Utility.SetLittleUInt32(ref buffer, value, 5);
|
|
|
|
Assert.Equal(0xEF, buffer[5]);
|
|
Assert.Equal(0xBE, buffer[6]);
|
|
Assert.Equal(0xAD, buffer[7]);
|
|
Assert.Equal(0xDE, buffer[8]);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region SetBigUInt32 Tests
|
|
|
|
[Fact]
|
|
public void SetBigUInt32_InsertsValueCorrectly()
|
|
{
|
|
byte[] buffer = new byte[10];
|
|
uint value = 0x12345678;
|
|
|
|
Utility.SetBigUInt32(ref buffer, value, 2);
|
|
|
|
Assert.Equal(0x12, buffer[2]);
|
|
Assert.Equal(0x34, buffer[3]);
|
|
Assert.Equal(0x56, buffer[4]);
|
|
Assert.Equal(0x78, buffer[5]);
|
|
}
|
|
|
|
[Fact]
|
|
public void SetBigUInt32_AtOffset_InsertsBehindOffset()
|
|
{
|
|
byte[] buffer = new byte[10];
|
|
uint value = 0xDEADBEEF;
|
|
|
|
Utility.SetBigUInt32(ref buffer, value, 5);
|
|
|
|
Assert.Equal(0xDE, buffer[5]);
|
|
Assert.Equal(0xAD, buffer[6]);
|
|
Assert.Equal(0xBE, buffer[7]);
|
|
Assert.Equal(0xEF, buffer[8]);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ReplaceInvalidFileNameChars Tests
|
|
|
|
#if WINDOWS
|
|
[Theory]
|
|
[InlineData("valid_filename.txt", "valid_filename.txt")]
|
|
[InlineData("file<name>test.txt", "file_name_test.txt")]
|
|
[InlineData("<>:\"|?*", "_______")]
|
|
public void ReplaceInvalidFileNameChars_Windows(string fileName, string expected)
|
|
{
|
|
var result = Utility.ReplaceInvalidFileNameChars(fileName);
|
|
|
|
Assert.Equal(expected, result);
|
|
}
|
|
|
|
#else
|
|
[Theory]
|
|
[InlineData("valid_filename.txt", "valid_filename.txt")]
|
|
[InlineData("file<name>test.txt", "file<name>test.txt")]
|
|
[InlineData("<>:\"|?*", "<>:\"|?*")]
|
|
public void ReplaceInvalidFileNameChars_NonWindows(string fileName, string expected)
|
|
{
|
|
var result = Utility.ReplaceInvalidFileNameChars(fileName);
|
|
|
|
Assert.Equal(expected, result);
|
|
}
|
|
#endif
|
|
|
|
#endregion
|
|
|
|
#region ToReadOnly Tests
|
|
|
|
[Fact]
|
|
public void ToReadOnly_IList_ReturnsReadOnlyCollection()
|
|
{
|
|
var list = new List<int> { 1, 2, 3, 4, 5 };
|
|
|
|
var result = list.ToReadOnly();
|
|
|
|
Assert.Equal(5, result.Count);
|
|
Assert.Equal(1, result[0]);
|
|
Assert.Equal(5, result[4]);
|
|
}
|
|
|
|
[Fact]
|
|
public void ToReadOnly_EmptyList_ReturnsEmptyReadOnlyCollection()
|
|
{
|
|
var list = new List<int>();
|
|
|
|
var result = list.ToReadOnly();
|
|
|
|
Assert.Empty(result);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region TrimNulls Tests
|
|
|
|
[Fact]
|
|
public void TrimNulls_StringWithNulls_ReplacesAndTrims()
|
|
{
|
|
var input = " hello\0world\0 ";
|
|
|
|
var result = Utility.TrimNulls(input);
|
|
|
|
Assert.Equal("hello world", result);
|
|
}
|
|
|
|
[Fact]
|
|
public void TrimNulls_StringWithoutNulls_TrimsWhitespace()
|
|
{
|
|
var input = " hello world ";
|
|
|
|
var result = Utility.TrimNulls(input);
|
|
|
|
Assert.Equal("hello world", result);
|
|
}
|
|
|
|
[Fact]
|
|
public void TrimNulls_OnlyNulls_ReturnsEmpty()
|
|
{
|
|
var input = "\0\0\0";
|
|
|
|
var result = Utility.TrimNulls(input);
|
|
|
|
Assert.Empty(result);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper class for testing non-seekable streams
|
|
/// </summary>
|
|
internal class NonSeekableStream : Stream
|
|
{
|
|
private readonly Stream _inner;
|
|
|
|
public NonSeekableStream(Stream inner)
|
|
{
|
|
_inner = inner;
|
|
}
|
|
|
|
public override bool CanRead => _inner.CanRead;
|
|
public override bool CanSeek => false; // Force non-seekable
|
|
public override bool CanWrite => _inner.CanWrite;
|
|
public override long Length => _inner.Length;
|
|
public override long Position
|
|
{
|
|
get => _inner.Position;
|
|
set => throw new NotSupportedException("Stream is not seekable");
|
|
}
|
|
|
|
public override void Flush() => _inner.Flush();
|
|
|
|
public override int Read(byte[] buffer, int offset, int count) =>
|
|
_inner.Read(buffer, offset, count);
|
|
|
|
public override long Seek(long offset, SeekOrigin origin) =>
|
|
throw new NotSupportedException("Stream is not seekable");
|
|
|
|
public override void SetLength(long value) =>
|
|
throw new NotSupportedException("Stream is not seekable");
|
|
|
|
public override void Write(byte[] buffer, int offset, int count) =>
|
|
_inner.Write(buffer, offset, count);
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
_inner.Dispose();
|
|
}
|
|
base.Dispose(disposing);
|
|
}
|
|
}
|