using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using AMWD.Common.Packing.Ar; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace UnitTests.Common.Packing.Ar { [TestClass] [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] public class ArReaderTests { private readonly DateTime _fixedDateTime = new(2023, 03, 01, 10, 20, 30, 0, DateTimeKind.Utc); private Dictionary _files; private Stream inStream; [TestInitialize] public void Initialize() { _files = new Dictionary { { "abcd.tmp", new ArFileInfo { FileName = "abcd.tmp", FileSize = 14, GroupId = 456, Mode = 33188, ModifyTime = _fixedDateTime, UserId = 123 } }, { "efgh.tmp", new ArFileInfo { FileName = "efgh.tmp", FileSize = 14, GroupId = 456, Mode = 33188, ModifyTime = _fixedDateTime, UserId = 123 } }, { "ijkl.tmp", new ArFileInfo { FileName = "ijkl.tmp", FileSize = 13, GroupId = 456, Mode = 33188, ModifyTime = _fixedDateTime, UserId = 123 } } }; inStream = new MemoryStream(); inStream.Write(Encoding.ASCII.GetBytes("!\n")); foreach (var file in _files) { int unixSeconds = (int)file.Value.ModifyTime.Subtract(DateTime.UnixEpoch).TotalSeconds; inStream.Write(Encoding.ASCII.GetBytes($"{file.Key,-16}{unixSeconds,-12}123 456 100644 {file.Value.FileSize,-10}`\n")); inStream.Write(Encoding.UTF8.GetBytes(new string('a', (int)file.Value.FileSize))); if (file.Value.FileSize % 2 != 0) inStream.Write(Encoding.ASCII.GetBytes("\n")); } inStream.Seek(0, SeekOrigin.Begin); } [TestCleanup] public void Cleanup() { inStream.Dispose(); inStream = null; } [TestMethod] public void ShouldInitializeArchive() { // Arrange inStream.Dispose(); inStream = new MemoryStream(); inStream.Write(Encoding.ASCII.GetBytes("!\n")); inStream.Seek(0, SeekOrigin.Begin); // Act var reader = new ArReader(inStream); // Assert Assert.IsNotNull(reader); Assert.IsFalse(reader.GetFileList().Any()); } [TestMethod] public void ShouldInitializeWithFiles() { // Arrange // Act var reader = new ArReader(inStream); // Assert Assert.IsNotNull(reader); Assert.IsTrue(reader.GetFileList().Any()); } [TestMethod] public void ShouldListFileNames() { // Arrange var reader = new ArReader(inStream); // Act var fileList = reader.GetFileList().ToList(); // Assert Assert.IsNotNull(reader); Assert.AreEqual(_files.Count, fileList.Count); foreach (string name in _files.Keys) Assert.IsTrue(fileList.Contains(name)); } [TestMethod] public void ShouldReturnValidFileInfo() { // Arrange var infos = new List(); var reader = new ArReader(inStream); // Act foreach (string name in _files.Keys) infos.Add(reader.GetFileInfo(name)); // Assert Assert.IsNotNull(reader); Assert.AreEqual(_files.Count, infos.Count); foreach (var expected in _files.Values) { var actual = infos.Single(fi => fi.FileName == expected.FileName); Assert.AreEqual(expected.FileName, actual.FileName); Assert.AreEqual(expected.FileSize, actual.FileSize); Assert.AreEqual(expected.GroupId, actual.GroupId); Assert.AreEqual(expected.Mode, actual.Mode); Assert.AreEqual(expected.ModifyTime, actual.ModifyTime); Assert.AreEqual(expected.UserId, actual.UserId); } } [TestMethod] public void ShouldReturnValidFileContent() { // Arrange var contents = new Dictionary(); var reader = new ArReader(inStream); // Act foreach (string name in _files.Keys) { using var ms = new MemoryStream(); reader.ReadFile(name, ms); ms.Seek(0, SeekOrigin.Begin); contents.Add(name, Encoding.UTF8.GetString(ms.ToArray())); } // Assert Assert.IsNotNull(reader); Assert.AreEqual(_files.Count, contents.Count); foreach (var expected in _files.Values) { string content = contents[expected.FileName]; if (expected.FileSize % 2 != 0) Assert.AreEqual(13, content.Length); else Assert.AreEqual(14, content.Length); Assert.AreEqual(new string('a', (int)expected.FileSize), content); } } [TestMethod] public void ShouldWriteFileToDisk() { // Arrange string tmpFile = Path.GetTempFileName(); var reader = new ArReader(inStream); try { // Act using var ms = new MemoryStream(); reader.ReadFile("abcd.tmp", ms); reader.ReadFile("abcd.tmp", tmpFile); // Assert CollectionAssert.AreEqual(ms.ToArray(), File.ReadAllBytes(tmpFile)); } finally { File.Delete(tmpFile); } } [TestMethod] [ExpectedException(typeof(ArgumentException))] public void ShouldThrowExceptionOnMissingRead() { // Arrange using var stream = new OverrideStream(); stream.CanReadOR = false; stream.CanSeekOR = true; stream.CanWriteOR = true; // Act var reader = new ArReader(stream); // Assert - ArgumentException Assert.Fail(); } [TestMethod] [ExpectedException(typeof(ArgumentException))] public void ShouldThrowExceptionOnMissingSeek() { // Arrange using var stream = new OverrideStream(); stream.CanReadOR = true; stream.CanSeekOR = false; stream.CanWriteOR = true; // Act var reader = new ArReader(stream); // Assert - ArgumentException Assert.Fail(); } [TestMethod] [ExpectedException(typeof(ArgumentException))] public void ShouldThrowExceptionOnMissingWrite() { // Arrange using var stream = new OverrideStream(); stream.CanReadOR = true; stream.CanSeekOR = true; stream.CanWriteOR = false; var reader = new ArReader(inStream); // Act reader.ReadFile("abcd.tmp", stream); // Assert - ArgumentException Assert.Fail(); } [TestMethod] [ExpectedException(typeof(FormatException))] public void ShouldThrowExceptionOnInvalidArchive() { // Arrange inStream.Seek(8, SeekOrigin.Begin); // Act _ = new ArReader(inStream); // Assert - FormatException Assert.Fail(); } [TestMethod] [ExpectedException(typeof(FormatException))] public void ShouldThrowExceptionOnInvalidMagic() { // Arrange inStream.Seek(0, SeekOrigin.End); inStream.Write(Encoding.ASCII.GetBytes($"{"foo.bar",-16}{"123456789",-12}123 456 100644 {"0",-10}´\n")); inStream.Seek(0, SeekOrigin.Begin); // Act _ = new ArReader(inStream); // Assert - FormatException Assert.Fail(); } [TestMethod] public void ShouldWriteNothingToStreamForMissingFile() { // Arrange var reader = new ArReader(inStream); // Act using var ms = new MemoryStream(); reader.ReadFile("foo.bar", ms); ms.Seek(0, SeekOrigin.Begin); // Assert Assert.AreEqual(0, ms.Length); } [TestMethod] public void ShouldWriteNothingToDiskForMissingFile() { // Arrange string tmpFile = Path.GetTempFileName(); var reader = new ArReader(inStream); try { // Act reader.ReadFile("foo.bar", tmpFile); // Assert Assert.AreEqual(0, new FileInfo(tmpFile).Length); } finally { File.Delete(tmpFile); } } private class OverrideStream : MemoryStream { public override bool CanWrite => CanWriteOR; public bool CanWriteOR { get; set; } public override bool CanSeek => CanSeekOR; public bool CanSeekOR { get; set; } public override bool CanRead => CanReadOR; public bool CanReadOR { get; set; } } } }