WIP: Added packing of AR and TAR
This commit is contained in:
231
UnitTests/Common/Packing/Ar/ArReaderTests.cs
Normal file
231
UnitTests/Common/Packing/Ar/ArReaderTests.cs
Normal file
@@ -0,0 +1,231 @@
|
||||
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<string, ArFileInfo> files;
|
||||
|
||||
private Stream inStream;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
files = new Dictionary<string, ArFileInfo>
|
||||
{
|
||||
{
|
||||
"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("!<arch>\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("!<arch>\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<ArFileInfo>();
|
||||
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<string, string>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private class OverrideStream : MemoryStream
|
||||
{
|
||||
public override bool CanWrite => CanWriteOR;
|
||||
|
||||
public bool CanWriteOR { get; set; }
|
||||
|
||||
public override bool CanSeek => CanSeekOR;
|
||||
|
||||
public bool CanSeekOR { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
306
UnitTests/Common/Packing/Ar/ArWriterTests.cs
Normal file
306
UnitTests/Common/Packing/Ar/ArWriterTests.cs
Normal file
@@ -0,0 +1,306 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
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 ArWriterTests
|
||||
{
|
||||
private readonly DateTime fixedDateTime = new(2023, 03, 01, 10, 20, 30, 0, DateTimeKind.Utc);
|
||||
|
||||
private readonly Dictionary<string, string> files = new();
|
||||
|
||||
private Stream 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("!<arch>\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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user