2023-04-12 14:06:04 +00:00
|
|
|
|
using Logging;
|
|
|
|
|
using NUnit.Framework;
|
2023-05-03 12:55:26 +00:00
|
|
|
|
using Utils;
|
2023-04-12 14:06:04 +00:00
|
|
|
|
|
|
|
|
|
namespace DistTestCore
|
|
|
|
|
{
|
|
|
|
|
public interface IFileManager
|
|
|
|
|
{
|
2023-06-04 06:59:51 +00:00
|
|
|
|
TestFile CreateEmptyTestFile(string label = "");
|
|
|
|
|
TestFile GenerateTestFile(ByteSize size, string label = "");
|
2023-04-12 14:06:04 +00:00
|
|
|
|
void DeleteAllTestFiles();
|
2023-05-29 07:13:38 +00:00
|
|
|
|
void PushFileSet();
|
|
|
|
|
void PopFileSet();
|
2023-04-12 14:06:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class FileManager : IFileManager
|
|
|
|
|
{
|
2023-06-07 07:59:00 +00:00
|
|
|
|
public const int ChunkSize = 1024 * 1024 * 100;
|
2023-05-03 12:55:26 +00:00
|
|
|
|
private static NumberSource folderNumberSource = new NumberSource(0);
|
2023-04-12 14:06:04 +00:00
|
|
|
|
private readonly Random random = new Random();
|
2023-06-21 06:28:40 +00:00
|
|
|
|
private readonly BaseLog log;
|
2023-04-12 14:06:04 +00:00
|
|
|
|
private readonly string folder;
|
2023-05-29 07:13:38 +00:00
|
|
|
|
private readonly List<List<TestFile>> fileSetStack = new List<List<TestFile>>();
|
2023-04-12 14:06:04 +00:00
|
|
|
|
|
2023-06-21 06:28:40 +00:00
|
|
|
|
public FileManager(BaseLog log, Configuration configuration)
|
2023-04-12 14:06:04 +00:00
|
|
|
|
{
|
2023-05-03 12:55:26 +00:00
|
|
|
|
folder = Path.Combine(configuration.GetFileManagerFolder(), folderNumberSource.GetNextNumber().ToString("D5"));
|
2023-04-12 14:06:04 +00:00
|
|
|
|
|
2023-04-17 07:10:39 +00:00
|
|
|
|
EnsureDirectory();
|
2023-04-12 14:06:04 +00:00
|
|
|
|
this.log = log;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-04 06:59:51 +00:00
|
|
|
|
public TestFile CreateEmptyTestFile(string label = "")
|
2023-04-12 14:06:04 +00:00
|
|
|
|
{
|
2023-06-04 06:59:51 +00:00
|
|
|
|
var path = Path.Combine(folder, Guid.NewGuid().ToString() + "_test.bin");
|
|
|
|
|
var result = new TestFile(log, path, label);
|
2023-04-12 14:06:04 +00:00
|
|
|
|
File.Create(result.Filename).Close();
|
2023-05-29 07:13:38 +00:00
|
|
|
|
if (fileSetStack.Any()) fileSetStack.Last().Add(result);
|
2023-04-12 14:06:04 +00:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-04 06:59:51 +00:00
|
|
|
|
public TestFile GenerateTestFile(ByteSize size, string label)
|
2023-04-12 14:06:04 +00:00
|
|
|
|
{
|
2023-06-07 07:59:00 +00:00
|
|
|
|
var sw = Stopwatch.Begin(log);
|
|
|
|
|
var result = GenerateFile(size, label);
|
|
|
|
|
sw.End($"Generated file '{result.Describe()}'.");
|
2023-04-12 14:06:04 +00:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void DeleteAllTestFiles()
|
|
|
|
|
{
|
2023-04-17 07:10:39 +00:00
|
|
|
|
DeleteDirectory();
|
2023-04-12 14:06:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-05-29 07:13:38 +00:00
|
|
|
|
public void PushFileSet()
|
|
|
|
|
{
|
|
|
|
|
fileSetStack.Add(new List<TestFile>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void PopFileSet()
|
|
|
|
|
{
|
|
|
|
|
if (!fileSetStack.Any()) return;
|
|
|
|
|
var pop = fileSetStack.Last();
|
|
|
|
|
fileSetStack.Remove(pop);
|
|
|
|
|
|
|
|
|
|
foreach (var file in pop)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
File.Delete(file.Filename);
|
|
|
|
|
}
|
|
|
|
|
catch { }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-07 07:59:00 +00:00
|
|
|
|
private TestFile GenerateFile(ByteSize size, string label)
|
|
|
|
|
{
|
|
|
|
|
var result = CreateEmptyTestFile(label);
|
|
|
|
|
CheckSpaceAvailable(result, size);
|
|
|
|
|
|
|
|
|
|
GenerateFileBytes(result, size);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CheckSpaceAvailable(TestFile testFile, ByteSize size)
|
|
|
|
|
{
|
|
|
|
|
var file = new FileInfo(testFile.Filename);
|
|
|
|
|
var drive = new DriveInfo(file.Directory!.Root.FullName);
|
|
|
|
|
|
|
|
|
|
var spaceAvailable = drive.TotalFreeSpace;
|
|
|
|
|
|
|
|
|
|
if (spaceAvailable < size.SizeInBytes)
|
|
|
|
|
{
|
|
|
|
|
var msg = $"Inconclusive: Not enough disk space to perform test. " +
|
|
|
|
|
$"{Formatter.FormatByteSize(size.SizeInBytes)} required. " +
|
|
|
|
|
$"{Formatter.FormatByteSize(spaceAvailable)} available.";
|
|
|
|
|
|
|
|
|
|
log.Log(msg);
|
|
|
|
|
Assert.Inconclusive(msg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-12 14:06:04 +00:00
|
|
|
|
private void GenerateFileBytes(TestFile result, ByteSize size)
|
|
|
|
|
{
|
|
|
|
|
long bytesLeft = size.SizeInBytes;
|
2023-06-07 07:59:00 +00:00
|
|
|
|
int chunkSize = ChunkSize;
|
2023-04-12 14:06:04 +00:00
|
|
|
|
while (bytesLeft > 0)
|
|
|
|
|
{
|
2023-06-07 07:59:00 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var length = Math.Min(bytesLeft, chunkSize);
|
|
|
|
|
AppendRandomBytesToFile(result, length);
|
|
|
|
|
bytesLeft -= length;
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
chunkSize = chunkSize / 2;
|
|
|
|
|
if (chunkSize < 1024) throw;
|
|
|
|
|
}
|
2023-04-12 14:06:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void AppendRandomBytesToFile(TestFile result, long length)
|
|
|
|
|
{
|
|
|
|
|
var bytes = new byte[length];
|
|
|
|
|
random.NextBytes(bytes);
|
|
|
|
|
using var stream = new FileStream(result.Filename, FileMode.Append);
|
|
|
|
|
stream.Write(bytes, 0, bytes.Length);
|
|
|
|
|
}
|
2023-04-17 07:10:39 +00:00
|
|
|
|
|
|
|
|
|
private void EnsureDirectory()
|
|
|
|
|
{
|
|
|
|
|
if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DeleteDirectory()
|
|
|
|
|
{
|
|
|
|
|
Directory.Delete(folder, true);
|
|
|
|
|
}
|
2023-04-12 14:06:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class TestFile
|
|
|
|
|
{
|
2023-06-21 06:28:40 +00:00
|
|
|
|
private readonly BaseLog log;
|
2023-06-04 06:59:51 +00:00
|
|
|
|
|
2023-06-21 06:28:40 +00:00
|
|
|
|
public TestFile(BaseLog log, string filename, string label)
|
2023-04-12 14:06:04 +00:00
|
|
|
|
{
|
2023-06-04 06:59:51 +00:00
|
|
|
|
this.log = log;
|
2023-04-12 14:06:04 +00:00
|
|
|
|
Filename = filename;
|
2023-06-04 06:59:51 +00:00
|
|
|
|
Label = label;
|
2023-04-12 14:06:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string Filename { get; }
|
2023-06-04 06:59:51 +00:00
|
|
|
|
public string Label { get; }
|
2023-04-12 14:06:04 +00:00
|
|
|
|
|
|
|
|
|
public void AssertIsEqual(TestFile? actual)
|
2023-06-07 08:56:25 +00:00
|
|
|
|
{
|
|
|
|
|
var sw = Stopwatch.Begin(log);
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
AssertEqual(actual);
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
sw.End($"{nameof(TestFile)}.{nameof(AssertIsEqual)}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string Describe()
|
|
|
|
|
{
|
2023-06-08 11:23:26 +00:00
|
|
|
|
var sizePostfix = $" ({Formatter.FormatByteSize(GetFileSize())})";
|
|
|
|
|
if (!string.IsNullOrEmpty(Label)) return Label + sizePostfix;
|
|
|
|
|
return $"'{Filename}'{sizePostfix}";
|
2023-06-07 08:56:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void AssertEqual(TestFile? actual)
|
2023-04-12 14:06:04 +00:00
|
|
|
|
{
|
|
|
|
|
if (actual == null) Assert.Fail("TestFile is null.");
|
|
|
|
|
if (actual == this || actual!.Filename == Filename) Assert.Fail("TestFile is compared to itself.");
|
|
|
|
|
|
2023-06-08 11:23:26 +00:00
|
|
|
|
Assert.That(actual.GetFileSize(), Is.EqualTo(GetFileSize()), "Files are not of equal length.");
|
2023-04-12 14:06:04 +00:00
|
|
|
|
|
|
|
|
|
using var streamExpected = new FileStream(Filename, FileMode.Open, FileAccess.Read);
|
|
|
|
|
using var streamActual = new FileStream(actual.Filename, FileMode.Open, FileAccess.Read);
|
|
|
|
|
|
|
|
|
|
var bytesExpected = new byte[FileManager.ChunkSize];
|
|
|
|
|
var bytesActual = new byte[FileManager.ChunkSize];
|
|
|
|
|
|
|
|
|
|
var readExpected = 0;
|
|
|
|
|
var readActual = 0;
|
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
readExpected = streamExpected.Read(bytesExpected, 0, FileManager.ChunkSize);
|
|
|
|
|
readActual = streamActual.Read(bytesActual, 0, FileManager.ChunkSize);
|
|
|
|
|
|
2023-06-04 06:59:51 +00:00
|
|
|
|
if (readExpected == 0 && readActual == 0)
|
|
|
|
|
{
|
|
|
|
|
log.Log($"OK: '{Describe()}' is equal to '{actual.Describe()}'.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-06-07 08:56:25 +00:00
|
|
|
|
|
2023-04-12 14:06:04 +00:00
|
|
|
|
Assert.That(readActual, Is.EqualTo(readExpected), "Unable to read buffers of equal length.");
|
2023-06-04 06:59:51 +00:00
|
|
|
|
|
2023-06-07 08:56:25 +00:00
|
|
|
|
for (var i = 0; i < readActual; i++)
|
|
|
|
|
{
|
|
|
|
|
if (bytesExpected[i] != bytesActual[i]) Assert.Fail("File contents not equal.");
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-06-07 07:32:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private long GetFileSize()
|
|
|
|
|
{
|
|
|
|
|
var info = new FileInfo(Filename);
|
|
|
|
|
return info.Length;
|
2023-06-04 06:59:51 +00:00
|
|
|
|
}
|
2023-04-12 14:06:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|