using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; using AMWD.Common.Packing.Ar; namespace AMWD.Common.Tests.Packing.Ar { [TestClass] public class ArWriterTest { private readonly DateTime _fixedDateTime = new(2023, 03, 01, 10, 20, 30, 0, DateTimeKind.Utc); private readonly Dictionary _files = []; private MemoryStream _outStream; [TestInitialize] public void Initialize() { _files.Clear(); for (int i = 0; i < 3; i++) { var (filePath, content) = GenerateTestFile(); _files.Add(filePath, content); } _outStream = new MemoryStream(); } [TestCleanup] public void Cleanup() { foreach (var kvp in _files) File.Delete(kvp.Key); _files.Clear(); _outStream.Dispose(); _outStream = null; } [TestMethod] public void ShouldInitializeArchive() { // Arrange byte[] initBytes = new byte[8]; // Act _ = new ArWriter(_outStream); _outStream.Seek(0, SeekOrigin.Begin); // Assert Assert.AreEqual(initBytes.Length, _outStream.Length); _outStream.Read(initBytes, 0, initBytes.Length); CollectionAssert.AreEqual(Encoding.ASCII.GetBytes("!\n"), initBytes); } [TestMethod] public void ShouldWriteOneFile() { // Arrange var firstFileKvp = _files.First(); byte[] headerBytes = new byte[60]; byte[] contentBytes = new byte[14]; var writer = new ArWriter(_outStream); // Act writer.WriteFile(firstFileKvp.Key, 123, 456); _outStream.Seek(8, SeekOrigin.Begin); // set behind init bytes // Assert Assert.AreEqual(8 + headerBytes.Length + contentBytes.Length, _outStream.Length); _outStream.Read(headerBytes, 0, headerBytes.Length); _outStream.Read(contentBytes, 0, contentBytes.Length); string header = Encoding.ASCII.GetString(headerBytes); string content = Encoding.UTF8.GetString(contentBytes); Assert.AreEqual($"{Path.GetFileName(firstFileKvp.Key),-16}1677666030 123 456 100644 14 `\n", header); Assert.AreEqual(firstFileKvp.Value, content); } [TestMethod] public void ShouldWriteMultipleFiles() { // Arrange var writer = new ArWriter(_outStream); // Act foreach (var kvp in _files) writer.WriteFile(kvp.Key, 123, 456); _outStream.Seek(8, SeekOrigin.Begin); // Assert Assert.AreEqual(8 + 3 * 60 + 3 * 14, _outStream.Length); foreach (var kvp in _files) { byte[] headerBytes = new byte[60]; byte[] contentBytes = new byte[14]; _outStream.Read(headerBytes, 0, headerBytes.Length); _outStream.Read(contentBytes, 0, contentBytes.Length); string header = Encoding.ASCII.GetString(headerBytes); string content = Encoding.UTF8.GetString(contentBytes); Assert.AreEqual($"{Path.GetFileName(kvp.Key),-16}1677666030 123 456 100644 14 `\n", header); Assert.AreEqual(kvp.Value, content); } } [TestMethod] public void ShouldPadToEven() { // Arrange var (filePath, fileContent) = GenerateTestFile(13); try { byte[] headerBytes = new byte[60]; byte[] contentBytes = new byte[14]; var writer = new ArWriter(_outStream); // Act writer.WriteFile(filePath, 123, 456); _outStream.Seek(8, SeekOrigin.Begin); // Assert Assert.AreEqual(8 + headerBytes.Length + contentBytes.Length, _outStream.Length); _outStream.Read(headerBytes, 0, headerBytes.Length); _outStream.Read(contentBytes, 0, contentBytes.Length); string header = Encoding.ASCII.GetString(headerBytes); string content = Encoding.UTF8.GetString(contentBytes); Assert.AreEqual($"{Path.GetFileName(filePath),-16}1677666030 123 456 100644 13 `\n", header); Assert.AreEqual(fileContent + "\n", content); } finally { File.Delete(filePath); } } [TestMethod] [ExpectedException(typeof(ArgumentException))] public void ShouldFailOnFileNameTooLong() { // Arrange var (filePath, _) = GenerateTestFile(); try { string path = Path.GetDirectoryName(filePath); string fileName = Path.GetFileName(filePath); fileName = fileName.PadLeft(20, 'a'); File.Move(filePath, Path.Combine(path, fileName)); filePath = Path.Combine(path, fileName); var writer = new ArWriter(_outStream); // Act writer.WriteFile(filePath, 123, 456); // Assert - Exception Assert.Fail(); } finally { File.Delete(filePath); } } [TestMethod] public void ShouldWriteEmptyOnNegativeUserId() { // Arrange var firstFileKvp = _files.First(); byte[] headerBytes = new byte[60]; byte[] contentBytes = new byte[14]; var writer = new ArWriter(_outStream); // Act writer.WriteFile(firstFileKvp.Key, -123, 456); _outStream.Seek(8, SeekOrigin.Begin); // Assert Assert.AreEqual(8 + headerBytes.Length + contentBytes.Length, _outStream.Length); _outStream.Read(headerBytes, 0, headerBytes.Length); _outStream.Read(contentBytes, 0, contentBytes.Length); string header = Encoding.ASCII.GetString(headerBytes); string content = Encoding.UTF8.GetString(contentBytes); Assert.AreEqual($"{Path.GetFileName(firstFileKvp.Key),-16}1677666030 456 100644 14 `\n", header); Assert.AreEqual(firstFileKvp.Value, content); } [TestMethod] public void ShouldWriteEmptyOnNegativeGroupId() { // Arrange var firstFileKvp = _files.First(); byte[] headerBytes = new byte[60]; byte[] contentBytes = new byte[14]; var writer = new ArWriter(_outStream); // Act writer.WriteFile(firstFileKvp.Key, 123, -456); _outStream.Seek(8, SeekOrigin.Begin); // Assert Assert.AreEqual(8 + headerBytes.Length + contentBytes.Length, _outStream.Length); _outStream.Read(headerBytes, 0, headerBytes.Length); _outStream.Read(contentBytes, 0, contentBytes.Length); string header = Encoding.ASCII.GetString(headerBytes); string content = Encoding.UTF8.GetString(contentBytes); Assert.AreEqual($"{Path.GetFileName(firstFileKvp.Key),-16}1677666030 123 100644 14 `\n", header); Assert.AreEqual(firstFileKvp.Value, content); } [TestMethod] public void ShouldWriteEmptyOnNegativeMode() { // Arrange var firstFileKvp = _files.First(); byte[] headerBytes = new byte[60]; byte[] contentBytes = new byte[14]; var writer = new ArWriter(_outStream); // Act writer.WriteFile(firstFileKvp.Key, 123, 456, -1); _outStream.Seek(8, SeekOrigin.Begin); // Assert Assert.AreEqual(8 + headerBytes.Length + contentBytes.Length, _outStream.Length); _outStream.Read(headerBytes, 0, headerBytes.Length); _outStream.Read(contentBytes, 0, contentBytes.Length); string header = Encoding.ASCII.GetString(headerBytes); string content = Encoding.UTF8.GetString(contentBytes); Assert.AreEqual($"{Path.GetFileName(firstFileKvp.Key),-16}1677666030 123 456 14 `\n", header); Assert.AreEqual(firstFileKvp.Value, content); } [TestMethod] [ExpectedException(typeof(ArgumentException))] public void ShouldFailOnNonWritableStream() { // Arrange using var testStream = new NonWriteStream(); // Act _ = new ArWriter(testStream); // Assert - ArgumentException Assert.Fail(); } private (string filePath, string content) GenerateTestFile(int length = 14) { string filePath = Path.GetTempFileName(); string text = CryptographyHelper.GetRandomString(length); File.WriteAllText(filePath, text); File.SetLastWriteTimeUtc(filePath, _fixedDateTime); return (filePath, text); } private class NonWriteStream : MemoryStream { public NonWriteStream() : base() { } public override bool CanWrite => false; } } }