From 51bad949ec87a8b34015f351193cb79a04249bbe Mon Sep 17 00:00:00 2001 From: Timothy Baldridge Date: Wed, 25 Mar 2020 22:25:48 -0600 Subject: [PATCH] Moved Wabbajack.Commons tests into Wabbajack.Commons.Test out of Wabbajack.Test. They all pass --- Compression.BSA.Test/BSATests.cs | 57 ++--- .../AsyncLockTests.cs | 19 +- .../EncryptedDataTests.cs | 12 +- .../HttpClientTests.cs | 9 +- .../IniTests.cs | 16 +- Wabbajack.Common.Test/MiscTests.cs | 31 +++ Wabbajack.Common.Test/PMapTests.cs | 206 ++++++++++++++++ .../TaskExtTests.cs | 16 +- Wabbajack.Common.Test/TestUtils.cs | 24 ++ .../UtilsTests.cs | 44 ++-- .../Wabbajack.Common.Test.csproj | 20 ++ .../WorkQueueTests.cs | 128 +++++----- Wabbajack.Common/WorkQueue.cs | 9 +- Wabbajack.Test/ACompilerTest.cs | 1 - Wabbajack.Test/MiscTests.cs | 31 --- Wabbajack.Test/PMapTests.cs | 230 ------------------ Wabbajack.Test/TestUtils.cs | 30 ++- Wabbajack.Test/Wabbajack.Test.csproj | 11 +- .../Wabbajack.VirtualFileSystem.Test.csproj | 6 +- Wabbajack.sln | 16 +- 20 files changed, 457 insertions(+), 459 deletions(-) rename {Wabbajack.Test => Wabbajack.Common.Test}/AsyncLockTests.cs (83%) rename {Wabbajack.Test => Wabbajack.Common.Test}/EncryptedDataTests.cs (73%) rename {Wabbajack.Test => Wabbajack.Common.Test}/HttpClientTests.cs (56%) rename {Wabbajack.Test => Wabbajack.Common.Test}/IniTests.cs (64%) create mode 100644 Wabbajack.Common.Test/MiscTests.cs create mode 100644 Wabbajack.Common.Test/PMapTests.cs rename {Wabbajack.Test => Wabbajack.Common.Test}/TaskExtTests.cs (65%) create mode 100644 Wabbajack.Common.Test/TestUtils.cs rename {Wabbajack.Test => Wabbajack.Common.Test}/UtilsTests.cs (53%) create mode 100644 Wabbajack.Common.Test/Wabbajack.Common.Test.csproj rename {Wabbajack.Test => Wabbajack.Common.Test}/WorkQueueTests.cs (70%) delete mode 100644 Wabbajack.Test/MiscTests.cs delete mode 100644 Wabbajack.Test/PMapTests.cs diff --git a/Compression.BSA.Test/BSATests.cs b/Compression.BSA.Test/BSATests.cs index 7e61777c..2aa4cc97 100644 --- a/Compression.BSA.Test/BSATests.cs +++ b/Compression.BSA.Test/BSATests.cs @@ -20,10 +20,10 @@ namespace Compression.BSA.Test [TestClass] public class BSATests { - private static string _stagingFolder = "NexusDownloads"; - private static string _bsaFolder = "BSAs"; - private static string _testDir = "BSA Test Dir"; - private static string _tempDir = "BSA Temp Dir"; + private static AbsolutePath _stagingFolder = ((RelativePath)"NexusDownloads").RelativeToEntryPoint(); + private static AbsolutePath _bsaFolder = ((RelativePath)"BSAs").RelativeToEntryPoint(); + private static AbsolutePath _testDir = ((RelativePath)"BSA Test Dir").RelativeToEntryPoint(); + private static AbsolutePath _tempDir = ((RelativePath)"BSA Temp Dir").RelativeToEntryPoint(); public TestContext TestContext { get; set; } @@ -34,11 +34,8 @@ namespace Compression.BSA.Test { Queue = new WorkQueue(); Utils.LogMessages.Subscribe(f => testContext.WriteLine(f.ShortDescription)); - if (!Directory.Exists(_stagingFolder)) - Directory.CreateDirectory(_stagingFolder); - - if (!Directory.Exists(_bsaFolder)) - Directory.CreateDirectory(_bsaFolder); + _stagingFolder.DeleteDirectory(); + _bsaFolder.DeleteDirectory(); var modIDs = new[] { @@ -53,22 +50,21 @@ namespace Compression.BSA.Test await Task.WhenAll(modIDs.Select(async (info) => { var filename = await DownloadMod(info); - var folder = Path.Combine(_bsaFolder, info.Item1.ToString(), info.Item2.ToString()); - if (!Directory.Exists(folder)) - Directory.CreateDirectory(folder); + var folder = _bsaFolder.Combine(info.Item1.ToString(), info.Item2.ToString()); + folder.CreateDirectory(); await FileExtractor.ExtractAll(Queue, filename, folder); })); } - private static async Task DownloadMod((Game, int) info) + private static async Task DownloadMod((Game, int) info) { using var client = await NexusApiClient.Get(); var results = await client.GetModFiles(info.Item1, info.Item2); var file = results.files.FirstOrDefault(f => f.is_primary) ?? results.files.OrderByDescending(f => f.uploaded_timestamp).First(); - var src = Path.Combine(_stagingFolder, file.file_name); + var src = _stagingFolder.Combine(file.file_name); - if (File.Exists(src)) return src; + if (src.Exists) return src; var state = new NexusDownloader.State { @@ -82,41 +78,38 @@ namespace Compression.BSA.Test public static IEnumerable BSAs() { - return Directory.EnumerateFiles(_bsaFolder, "*", DirectoryEnumerationOptions.Recursive) - .Where(f => Consts.SupportedBSAs.Contains(Path.GetExtension(f))) + return _bsaFolder.EnumerateFiles() + .Where(f => Consts.SupportedBSAs.Contains(f.Extension)) .Select(nm => new object[] {nm}); } [TestMethod] [DataTestMethod] [DynamicData(nameof(BSAs), DynamicDataSourceType.Method)] - public async Task BSACompressionRecompression(string bsa) + public async Task BSACompressionRecompression(AbsolutePath bsa) { TestContext.WriteLine($"From {bsa}"); TestContext.WriteLine("Cleaning Output Dir"); - if (Directory.Exists(_tempDir)) Utils.DeleteDirectory(_tempDir); - Directory.CreateDirectory(_tempDir); - + _tempDir.DeleteDirectory(); + _tempDir.CreateDirectory(); + TestContext.WriteLine($"Reading {bsa}"); - string tempFile = Path.Combine("tmp.bsa"); - var size = File.GetSize(bsa); + var tempFile = ((RelativePath)"tmp.bsa").RelativeToEntryPoint(); + var size = bsa.Size; using (var a = BSADispatch.OpenRead(bsa)) { await a.Files.PMap(Queue, file => { - var absName = Path.Combine(_tempDir, file.Path); + var absName = _tempDir.Combine(file.Path); ViaJson(file.State); - if (!Directory.Exists(Path.GetDirectoryName(absName))) - Directory.CreateDirectory(Path.GetDirectoryName(absName)); - - - using (var fs = File.Open(absName, FileMode.Create)) + absName.Parent.CreateDirectory(); + using (var fs = absName.Create()) { file.CopyDataTo(fs); } - Assert.AreEqual(file.Size, new FileInfo(absName).Length); + Assert.AreEqual(file.Size, absName.Size); }); Console.WriteLine($"Building {bsa}"); @@ -125,8 +118,8 @@ namespace Compression.BSA.Test { var streams = await a.Files.PMap(Queue, file => { - var absPath = Path.Combine(_tempDir, file.Path); - var str = File.OpenRead(absPath); + var absPath = _tempDir.Combine(file.Path); + var str = absPath.OpenRead(); w.AddFile(ViaJson(file.State), str); return str; }); diff --git a/Wabbajack.Test/AsyncLockTests.cs b/Wabbajack.Common.Test/AsyncLockTests.cs similarity index 83% rename from Wabbajack.Test/AsyncLockTests.cs rename to Wabbajack.Common.Test/AsyncLockTests.cs index dbcb912b..48ba84de 100644 --- a/Wabbajack.Test/AsyncLockTests.cs +++ b/Wabbajack.Common.Test/AsyncLockTests.cs @@ -1,17 +1,12 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Wabbajack.Common; +using Xunit; -namespace Wabbajack.Test +namespace Wabbajack.Common.Test { - [TestClass] public class AsyncLockTests { - [TestMethod] + [Fact] public async Task Typical() { var asyncLock = new AsyncLock(); @@ -29,20 +24,20 @@ namespace Wabbajack.Test await Task.Delay(200); using (await asyncLock.Wait()) { - Assert.IsTrue(firstRun); + Assert.True(firstRun); } }); await Task.WhenAll(first, second); } - [TestMethod] + [Fact] public async Task Exception() { var asyncLock = new AsyncLock(); bool firstRun = false; bool secondRun = false; // Throw exception inside a lock - await Assert.ThrowsExceptionAsync(() => + await Assert.ThrowsAsync(() => { return Task.Run(async () => { @@ -62,7 +57,7 @@ namespace Wabbajack.Test await Task.Delay(200); using (await asyncLock.Wait()) { - Assert.IsTrue(firstRun); + Assert.True(firstRun); secondRun = true; } }), diff --git a/Wabbajack.Test/EncryptedDataTests.cs b/Wabbajack.Common.Test/EncryptedDataTests.cs similarity index 73% rename from Wabbajack.Test/EncryptedDataTests.cs rename to Wabbajack.Common.Test/EncryptedDataTests.cs index 02c3974e..05ccc609 100644 --- a/Wabbajack.Test/EncryptedDataTests.cs +++ b/Wabbajack.Common.Test/EncryptedDataTests.cs @@ -1,18 +1,14 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Threading.Tasks; -using System.Windows; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Wabbajack.Common; +using Xunit; -namespace Wabbajack.Test +namespace Wabbajack.Common.Test { - [TestClass] public class EncryptedDataTests { - [TestMethod] + [Fact] public async Task CanDetectNewEncryptedData() { var test_string = Guid.NewGuid().ToString(); @@ -28,7 +24,7 @@ namespace Wabbajack.Test test_string.ToEcryptedJson(test_string); await Task.Delay(100); - CollectionAssert.Contains(data, test_string); + Assert.Contains(test_string, data); } diff --git a/Wabbajack.Test/HttpClientTests.cs b/Wabbajack.Common.Test/HttpClientTests.cs similarity index 56% rename from Wabbajack.Test/HttpClientTests.cs rename to Wabbajack.Common.Test/HttpClientTests.cs index f43715ba..aa33c8fe 100644 --- a/Wabbajack.Test/HttpClientTests.cs +++ b/Wabbajack.Common.Test/HttpClientTests.cs @@ -1,18 +1,17 @@ using System.Net.Http; using System.Threading.Tasks; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Xunit; -namespace Wabbajack.Test +namespace Wabbajack.Common.Test { - [TestClass] public class HttpClientTests { - [TestMethod] + [Fact] public async Task DoesntReuseHttpMessages() { var client = new Common.Http.Client(); // If we reuse the HTTP message this will throw a invalid operation exception - await Assert.ThrowsExceptionAsync(async () => await client.GetAsync("http://blerg.blaz.bloz.buz")); + await Assert.ThrowsAsync(async () => await client.GetAsync("http://blerg.blaz.bloz.buz")); } } } diff --git a/Wabbajack.Test/IniTests.cs b/Wabbajack.Common.Test/IniTests.cs similarity index 64% rename from Wabbajack.Test/IniTests.cs rename to Wabbajack.Common.Test/IniTests.cs index 6ad26a1d..9e298029 100644 --- a/Wabbajack.Test/IniTests.cs +++ b/Wabbajack.Common.Test/IniTests.cs @@ -1,25 +1,23 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Wabbajack.Common; +using Xunit; -namespace Wabbajack.Test +namespace Wabbajack.Common.Test { - [TestClass] public class IniTests { - [TestMethod] + [Fact] public void TestByteArrayParsing() { - Assert.AreEqual("bar", @"[General] + Assert.Equal("bar", @"[General] foo = bar".LoadIniString().General.foo); - Assert.AreEqual("baz\\bar", @"[General] + Assert.Equal("baz\\bar", @"[General] foo = baz\\bar".LoadIniString().General.foo); - Assert.AreEqual("bar", @"[General] + Assert.Equal("bar", @"[General] foo = @ByteArray(bar)".LoadIniString().General.foo); - Assert.AreEqual("foo\\h̴̹͚̎é̶̘͙̐l̶͕̔͑p̴̯̋͂m̶̞̮͘͠e̸͉͙͆̄\\baz", @"[General] + Assert.Equal("foo\\h̴̹͚̎é̶̘͙̐l̶͕̔͑p̴̯̋͂m̶̞̮͘͠e̸͉͙͆̄\\baz", @"[General] foo = @ByteArray(foo\\\x68\xcc\xb4\xcc\x8e\xcc\xb9\xcd\x9a\x65\xcc\xb6\xcd\x81\xcc\x90\xcc\x98\xcd\x99\x6c\xcc\xb6\xcc\x94\xcd\x91\xcd\x95\x70\xcc\xb4\xcc\x8b\xcd\x82\xcc\xaf\x6d\xcc\xb6\xcd\x98\xcd\xa0\xcc\x9e\xcc\xae\x65\xcc\xb8\xcd\x86\xcc\x84\xcd\x89\xcd\x99\\baz)".LoadIniString().General.foo); } diff --git a/Wabbajack.Common.Test/MiscTests.cs b/Wabbajack.Common.Test/MiscTests.cs new file mode 100644 index 00000000..eb22d2b3 --- /dev/null +++ b/Wabbajack.Common.Test/MiscTests.cs @@ -0,0 +1,31 @@ +using System.Threading.Tasks; +using Alphaleonis.Win32.Filesystem; +using Xunit; + +namespace Wabbajack.Common.Test +{ + public class MiscTests + { + [Fact] + public void TestDiskSpeed() + { + using (var queue = new WorkQueue()) + { + var speed = Utils.TestDiskSpeed(queue, @".\"); + } + } + + [Fact] + public async Task TestHash() + { + var testFile = ((RelativePath)"text.data").RelativeToEntryPoint(); + const string data = "Cheese for Everyone!"; + await testFile.WriteAllTextAsync(data); + File.WriteAllText("test.data", data); + Assert.Equal(Hash.FromBase64("eSIyd+KOG3s="), testFile.FileHashCached()); + Assert.True(Utils.TryGetHashCache(testFile, out var fileHash)); + Assert.Equal(Hash.FromBase64("eSIyd+KOG3s="), fileHash); + Assert.NotEqual("eSIyd+KOG3s=", await testFile.WithExtension(Consts.HashFileExtension).ReadAllTextAsync()); + } + } +} diff --git a/Wabbajack.Common.Test/PMapTests.cs b/Wabbajack.Common.Test/PMapTests.cs new file mode 100644 index 00000000..5486bbf8 --- /dev/null +++ b/Wabbajack.Common.Test/PMapTests.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Wabbajack.Common.Test +{ + public class PMapTests + { + const int TypicalThreadCount = 6; + const int TypicalDelayMS = 50; + const int TimeoutSeconds = 15; + + [Fact] + public async Task Typical_Action() + { + using var queue = new WorkQueue(TypicalThreadCount); + var input = Enumerable.Range(0, TypicalThreadCount * 2).ToArray(); + var output = new List(); + var workTask = Utils.PMap(Enumerable.Range(0, TypicalThreadCount * 2), queue, (item) => + { + Assert.True(WorkQueue.WorkerThread); + Thread.Sleep(TypicalDelayMS); + lock (output) + { + output.Add(item); + } + }); + await workTask.TimeoutButContinue(TimeSpan.FromSeconds(TimeoutSeconds), () => throw new TimeoutException()); + Assert.True(input.SequenceEqual(output.OrderBy(i => i))); + } + + [Fact] + public async Task Typical_Func() + { + using var queue = new WorkQueue(TypicalThreadCount); + var input = Enumerable.Range(0, TypicalThreadCount * 2).ToArray(); + var workTask = Utils.PMap(Enumerable.Range(0, TypicalThreadCount * 2), queue, (item) => + { + Assert.True(WorkQueue.WorkerThread); + Thread.Sleep(TypicalDelayMS); + return item.ToString(); + }); + var results = await workTask.TimeoutButContinue(TimeSpan.FromSeconds(TimeoutSeconds), () => throw new TimeoutException()); + Assert.True(input.Select(i => i.ToString()).SequenceEqual(results)); + } + + [Fact] + public async Task Typical_Task() + { + using var queue = new WorkQueue(TypicalThreadCount); + var input = Enumerable.Range(0, TypicalThreadCount * 2).ToArray(); + var output = new List(); + var workTask = Enumerable.Range(0, TypicalThreadCount * 2) + .PMap(queue, async (item) => + { + Assert.True(WorkQueue.WorkerThread); + await Task.Delay(TypicalDelayMS); + lock (output) + { + output.Add(item); + } + }); + await workTask.TimeoutButContinue(TimeSpan.FromSeconds(TimeoutSeconds), () => throw new TimeoutException()); + Assert.True(input.SequenceEqual(output.OrderBy(i => i))); + } + + [Fact] + public async Task Typical_TaskReturn() + { + using var queue = new WorkQueue(TypicalThreadCount); + var input = Enumerable.Range(0, TypicalThreadCount * 2).ToArray(); + var workTask = Enumerable.Range(0, TypicalThreadCount * 2) + .PMap(queue, async (item) => + { + Assert.True(WorkQueue.WorkerThread); + await Task.Delay(TypicalDelayMS); + return item.ToString(); + }); + var results = await workTask.TimeoutButContinue(TimeSpan.FromSeconds(TimeoutSeconds), () => throw new TimeoutException()); + Assert.True(input.Select(i => i.ToString()).SequenceEqual(results)); + } + + [Fact] + public async Task NestedAction() + { + using var queue = new WorkQueue(TypicalThreadCount); + var input = Enumerable.Range(0, TypicalThreadCount * 2).ToArray(); + var inputConstructedResults = input.SelectMany(i => Enumerable.Range(i * 100, TypicalThreadCount * 2)); + var output = new List(); + var workTask = Enumerable.Range(0, TypicalThreadCount * 2) + .PMap(queue, async (item) => + { + Assert.True(WorkQueue.WorkerThread); + await Enumerable.Range(item * 100, TypicalThreadCount * 2) + .PMap(queue, async (subItem) => + { + Assert.True(WorkQueue.WorkerThread); + Thread.Sleep(TypicalDelayMS); + lock (output) + { + output.Add(subItem); + } + }); + }); + await workTask.TimeoutButContinue(TimeSpan.FromSeconds(TimeoutSeconds), () => throw new TimeoutException()); + Assert.True(inputConstructedResults.SequenceEqual(output.OrderBy(i => i))); + } + + [Fact] + public async Task Nested_Func() + { + using var queue = new WorkQueue(TypicalThreadCount); + var input = Enumerable.Range(0, TypicalThreadCount * 2).ToArray(); + var inputConstructedResults = input.SelectMany(i => Enumerable.Range(i * 100, TypicalThreadCount * 2)); + var workTask = Enumerable.Range(0, TypicalThreadCount * 2) + .PMap(queue, async (item) => + { + Assert.True(WorkQueue.WorkerThread); + return await Utils.PMap(Enumerable.Range(item * 100, TypicalThreadCount * 2), queue, (subItem) => + { + Assert.True(WorkQueue.WorkerThread); + Thread.Sleep(TypicalDelayMS); + return subItem; + }); + }); + var results = await workTask.TimeoutButContinue(TimeSpan.FromSeconds(TimeoutSeconds), () => throw new TimeoutException()); + Assert.True(inputConstructedResults.SequenceEqual(results.SelectMany(i => i))); + } + + [Fact] + public async Task Nested_Task() + { + using var queue = new WorkQueue(TypicalThreadCount); + var input = Enumerable.Range(0, TypicalThreadCount * 2).ToArray(); + var inputConstructedResults = input.SelectMany(i => Enumerable.Range(i * 100, TypicalThreadCount * 2)); + var output = new List(); + var workTask = Enumerable.Range(0, TypicalThreadCount * 2) + .PMap(queue, async (item) => + { + Assert.True(WorkQueue.WorkerThread); + await Enumerable.Range(item * 100, TypicalThreadCount * 2) + .PMap(queue, async (subItem) => + { + Assert.True(WorkQueue.WorkerThread); + await Task.Delay(TypicalDelayMS); + lock (output) + { + output.Add(subItem); + } + }); + }); + await workTask.TimeoutButContinue(TimeSpan.FromSeconds(TimeoutSeconds), () => throw new TimeoutException()); + Assert.True(inputConstructedResults.SequenceEqual(output.OrderBy(i => i))); + } + + [Fact] + public async Task Nested_TaskReturn() + { + using var queue = new WorkQueue(TypicalThreadCount); + var input = Enumerable.Range(0, TypicalThreadCount * 2).ToArray(); + var inputConstructedResults = input.SelectMany(i => Enumerable.Range(i * 100, TypicalThreadCount * 2)); + var workTask = Enumerable.Range(0, TypicalThreadCount * 2) + .PMap(queue, async (item) => + { + Assert.True(WorkQueue.WorkerThread); + return await Enumerable.Range(item * 100, TypicalThreadCount * 2) + .PMap(queue, async (subItem) => + { + Assert.True(WorkQueue.WorkerThread); + await Task.Delay(TypicalDelayMS); + return subItem; + }); + }); + var results = await workTask.TimeoutButContinue(TimeSpan.FromSeconds(TimeoutSeconds), () => throw new TimeoutException()); + Assert.True(inputConstructedResults.SequenceEqual(results.SelectMany(i => i))); + } + + [Fact] + public async Task Nested_BackgroundThreadsInvolved() + { + using var queue = new WorkQueue(TypicalThreadCount); + var input = Enumerable.Range(0, TypicalThreadCount * 2).ToArray(); + var inputConstructedResults = input.SelectMany(i => Enumerable.Range(i * 100, TypicalThreadCount * 2)); + var workTask = Enumerable.Range(0, TypicalThreadCount * 2) + .PMap(queue, async (item) => + { + Assert.True(WorkQueue.WorkerThread); + return await Enumerable.Range(item * 100, TypicalThreadCount * 2) + .PMap(queue, async (subItem) => + { + return await Task.Run(async () => + { + Assert.True(WorkQueue.WorkerThread); + await Task.Delay(TypicalDelayMS); + return subItem; + }); + }); + }); + var results = await workTask.TimeoutButContinue(TimeSpan.FromSeconds(TimeoutSeconds), () => throw new TimeoutException()); + Assert.True(inputConstructedResults.SequenceEqual(results.SelectMany(i => i))); + } + } +} diff --git a/Wabbajack.Test/TaskExtTests.cs b/Wabbajack.Common.Test/TaskExtTests.cs similarity index 65% rename from Wabbajack.Test/TaskExtTests.cs rename to Wabbajack.Common.Test/TaskExtTests.cs index 72ca85c2..564a2b6e 100644 --- a/Wabbajack.Test/TaskExtTests.cs +++ b/Wabbajack.Common.Test/TaskExtTests.cs @@ -1,29 +1,25 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Xunit; -namespace Wabbajack.Test +namespace Wabbajack.Common.Test { - [TestClass] public class TaskExtTests { - [TestMethod] + [Fact] public async Task TimeoutButContinue_Typical() { bool timedOut = false; await Task.Delay(100).TimeoutButContinue(TimeSpan.FromSeconds(1), () => timedOut = true); - Assert.IsFalse(timedOut); + Assert.False(timedOut); } - [TestMethod] + [Fact] public async Task TimeoutButContinue_TimedOut() { bool timedOut = false; await Task.Delay(300).TimeoutButContinue(TimeSpan.FromMilliseconds(100), () => timedOut = true); - Assert.IsTrue(timedOut); + Assert.True(timedOut); } } } diff --git a/Wabbajack.Common.Test/TestUtils.cs b/Wabbajack.Common.Test/TestUtils.cs new file mode 100644 index 00000000..dcd39e4f --- /dev/null +++ b/Wabbajack.Common.Test/TestUtils.cs @@ -0,0 +1,24 @@ +using System; + +namespace Wabbajack.Common.Test +{ + public static class TestUtils + { + private static Random _random = new Random(); + + public static byte[] RandomData(int? size = null, int maxSize = 1024) + { + if (size == null) + size = _random.Next(1, maxSize); + var arr = new byte[(int) size]; + _random.NextBytes(arr); + return arr; + } + + public static object RandomOne(params object[] opts) + { + return opts[_random.Next(0, opts.Length)]; + } + + } +} diff --git a/Wabbajack.Test/UtilsTests.cs b/Wabbajack.Common.Test/UtilsTests.cs similarity index 53% rename from Wabbajack.Test/UtilsTests.cs rename to Wabbajack.Common.Test/UtilsTests.cs index 8bde6f9c..12ff62e7 100644 --- a/Wabbajack.Test/UtilsTests.cs +++ b/Wabbajack.Common.Test/UtilsTests.cs @@ -2,33 +2,29 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; using System.Threading.Tasks; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Wabbajack.Common; +using Xunit; -namespace Wabbajack.Test +namespace Wabbajack.Common.Test { - [TestClass] public class UtilsTests { - [TestMethod] + [Fact] public void IsInPathTests() { - Assert.IsTrue("c:\\foo\\bar.exe".IsInPath("c:\\foo")); - Assert.IsFalse("c:\\foo\\bar.exe".IsInPath("c:\\fo")); - Assert.IsTrue("c:\\Foo\\bar.exe".IsInPath("c:\\foo")); - Assert.IsTrue("c:\\foo\\bar.exe".IsInPath("c:\\Foo")); - Assert.IsTrue("c:\\foo\\bar.exe".IsInPath("c:\\fOo")); - Assert.IsTrue("c:\\foo\\bar.exe".IsInPath("c:\\foo\\")); - Assert.IsTrue("c:\\foo\\bar\\".IsInPath("c:\\foo\\")); + Assert.True("c:\\foo\\bar.exe".IsInPath("c:\\foo")); + Assert.False("c:\\foo\\bar.exe".IsInPath("c:\\fo")); + Assert.True("c:\\Foo\\bar.exe".IsInPath("c:\\foo")); + Assert.True("c:\\foo\\bar.exe".IsInPath("c:\\Foo")); + Assert.True("c:\\foo\\bar.exe".IsInPath("c:\\fOo")); + Assert.True("c:\\foo\\bar.exe".IsInPath("c:\\foo\\")); + Assert.True("c:\\foo\\bar\\".IsInPath("c:\\foo\\")); } - [TestMethod] - [DataTestMethod] - [DynamicData(nameof(PatchData), DynamicDataSourceType.Method)] + [Theory] + [ClassData(typeof(PatchData))] public async Task DiffCreateAndApply(byte[] src, byte[] dest, DiffMethod method) { await using var ms = new MemoryStream(); @@ -51,7 +47,7 @@ namespace Wabbajack.Test var patch = ms.ToArray(); await using var resultStream = new MemoryStream(); Utils.ApplyPatch(new MemoryStream(src), () => new MemoryStream(patch), resultStream); - CollectionAssert.AreEqual(dest, resultStream.ToArray()); + Assert.Equal(dest, resultStream.ToArray()); } @@ -61,10 +57,18 @@ namespace Wabbajack.Test BSDiff, OctoDiff } - public static IEnumerable PatchData() + public class PatchData : TheoryData { - var maxSize = 1024 * 1024 * 8; - return Enumerable.Range(0, 10).Select(x => new[] {TestUtils.RandomData(maxSize:maxSize), TestUtils.RandomData(maxSize:maxSize), TestUtils.RandomeOne(DiffMethod.Default, DiffMethod.OctoDiff, DiffMethod.BSDiff)}); + public PatchData() + { + var maxSize = 64; + Enumerable.Range(0, 10).Do(x => + + { + Add(TestUtils.RandomData(maxSize: maxSize), TestUtils.RandomData(maxSize: maxSize), + (DiffMethod)TestUtils.RandomOne(DiffMethod.Default, DiffMethod.OctoDiff, DiffMethod.BSDiff)); + }); + } } } } diff --git a/Wabbajack.Common.Test/Wabbajack.Common.Test.csproj b/Wabbajack.Common.Test/Wabbajack.Common.Test.csproj new file mode 100644 index 00000000..164f303f --- /dev/null +++ b/Wabbajack.Common.Test/Wabbajack.Common.Test.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp3.1 + x64 + win10-x64 + false + + + + + + + + + + + + + diff --git a/Wabbajack.Test/WorkQueueTests.cs b/Wabbajack.Common.Test/WorkQueueTests.cs similarity index 70% rename from Wabbajack.Test/WorkQueueTests.cs rename to Wabbajack.Common.Test/WorkQueueTests.cs index 1f50b2fe..ee0a324d 100644 --- a/Wabbajack.Test/WorkQueueTests.cs +++ b/Wabbajack.Common.Test/WorkQueueTests.cs @@ -1,17 +1,13 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Reactive.Linq; using System.Reactive.Subjects; -using System.Text; using System.Threading; using System.Threading.Tasks; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Wabbajack.Common; +using Xunit; -namespace Wabbajack.Test +namespace Wabbajack.Common.Test { - [TestClass] public class WorkQueueTests { #region DynamicNumThreads @@ -20,144 +16,144 @@ namespace Wabbajack.Test const int Small = 4; public TimeSpan PollMS => TimeSpan.FromSeconds(1); - [TestMethod] + [Fact] public void DynamicNumThreads_Typical() { using (var queue = new WorkQueue()) { - Assert.AreEqual(Environment.ProcessorCount, queue.DesiredNumWorkers); - Assert.AreEqual(Environment.ProcessorCount, queue._tasks.Count); + Assert.Equal(Environment.ProcessorCount, queue.DesiredNumWorkers); + Assert.Equal(Environment.ProcessorCount, queue._tasks.Count); } } - [TestMethod] + [Fact] public void DynamicNumThreads_Increased() { var subj = new BehaviorSubject(Small); using (var queue = new WorkQueue(subj)) { - Assert.AreEqual(Small, queue.DesiredNumWorkers); - Assert.AreEqual(Small, queue._tasks.Count); + Assert.Equal(Small, queue.DesiredNumWorkers); + Assert.Equal(Small, queue._tasks.Count); subj.OnNext(Large); - Assert.AreEqual(Large, queue.DesiredNumWorkers); - Assert.AreEqual(Large, queue._tasks.Count); + Assert.Equal(Large, queue.DesiredNumWorkers); + Assert.Equal(Large, queue._tasks.Count); } } - [TestMethod] + [Fact] public void DynamicNumThreads_EmptyObs() { using (var queue = new WorkQueue(Observable.Empty())) { - Assert.AreEqual(0, queue.DesiredNumWorkers); - Assert.AreEqual(0, queue._tasks.Count); + Assert.Equal(0, queue.DesiredNumWorkers); + Assert.Equal(0, queue._tasks.Count); } } - [TestMethod] + [Fact] public async Task DynamicNumThreads_Decreased() { var subj = new BehaviorSubject(Large); using (var queue = new WorkQueue(subj)) { - Assert.AreEqual(Large, queue.DesiredNumWorkers); - Assert.AreEqual(Large, queue._tasks.Count); + Assert.Equal(Large, queue.DesiredNumWorkers); + Assert.Equal(Large, queue._tasks.Count); subj.OnNext(Small); - Assert.AreEqual(Small, queue.DesiredNumWorkers); + Assert.Equal(Small, queue.DesiredNumWorkers); // Tasks don't go down immediately - Assert.AreEqual(Large, queue._tasks.Count); + Assert.Equal(Large, queue._tasks.Count); // After things re-poll, they should be cleaned await Task.Delay(PollMS * 2); - Assert.AreEqual(Small, queue._tasks.Count); + Assert.Equal(Small, queue._tasks.Count); } } - [TestMethod] + [Fact] public async Task DynamicNumThreads_IncreasedWhileWorking() { var subj = new BehaviorSubject(Small); var tcs = new TaskCompletionSource(); using (var queue = new WorkQueue(subj)) { - Assert.AreEqual(Small, queue.DesiredNumWorkers); - Assert.AreEqual(Small, queue._tasks.Count); + Assert.Equal(Small, queue.DesiredNumWorkers); + Assert.Equal(Small, queue._tasks.Count); Enumerable.Range(0, Small).Do(_ => queue.QueueTask(() => tcs.Task)); subj.OnNext(Large); - Assert.AreEqual(Large, queue.DesiredNumWorkers); - Assert.AreEqual(Large, queue._tasks.Count); + Assert.Equal(Large, queue.DesiredNumWorkers); + Assert.Equal(Large, queue._tasks.Count); Task.Run(() => tcs.SetResult(true)).FireAndForget(); - Assert.AreEqual(Large, queue.DesiredNumWorkers); - Assert.AreEqual(Large, queue._tasks.Count); + Assert.Equal(Large, queue.DesiredNumWorkers); + Assert.Equal(Large, queue._tasks.Count); await Task.Delay(PollMS * 2); - Assert.AreEqual(Large, queue.DesiredNumWorkers); - Assert.AreEqual(Large, queue._tasks.Count); + Assert.Equal(Large, queue.DesiredNumWorkers); + Assert.Equal(Large, queue._tasks.Count); } } - [TestMethod] + [Fact] public async Task DynamicNumThreads_DecreasedWhileWorking() { var subj = new BehaviorSubject(Large); var tcs = new TaskCompletionSource(); using (var queue = new WorkQueue(subj)) { - Assert.AreEqual(Large, queue.DesiredNumWorkers); - Assert.AreEqual(Large, queue._tasks.Count); + Assert.Equal(Large, queue.DesiredNumWorkers); + Assert.Equal(Large, queue._tasks.Count); Enumerable.Range(0, Large).Do(_ => queue.QueueTask(() => tcs.Task)); subj.OnNext(Small); - Assert.AreEqual(Small, queue.DesiredNumWorkers); - Assert.AreEqual(Large, queue._tasks.Count); + Assert.Equal(Small, queue.DesiredNumWorkers); + Assert.Equal(Large, queue._tasks.Count); // After things re-poll, they should still be working at max await Task.Delay(PollMS * 2); - Assert.AreEqual(Large, queue._tasks.Count); + Assert.Equal(Large, queue._tasks.Count); // Complete, repoll, and check again Task.Run(() => tcs.SetResult(true)).FireAndForget(); await Task.Delay(PollMS * 2); - Assert.AreEqual(Small, queue._tasks.Count); + Assert.Equal(Small, queue._tasks.Count); } } - [TestMethod] + [Fact] public async Task DynamicNumThreads_IncreasedThenDecreased() { var subj = new BehaviorSubject(Small); using (var queue = new WorkQueue(subj)) { - Assert.AreEqual(Small, queue.DesiredNumWorkers); - Assert.AreEqual(Small, queue._tasks.Count); + Assert.Equal(Small, queue.DesiredNumWorkers); + Assert.Equal(Small, queue._tasks.Count); subj.OnNext(Large); - Assert.AreEqual(Large, queue.DesiredNumWorkers); - Assert.AreEqual(Large, queue._tasks.Count); + Assert.Equal(Large, queue.DesiredNumWorkers); + Assert.Equal(Large, queue._tasks.Count); subj.OnNext(Small); // Still large number of threads, as not immediate - Assert.AreEqual(Small, queue.DesiredNumWorkers); - Assert.AreEqual(Large, queue._tasks.Count); + Assert.Equal(Small, queue.DesiredNumWorkers); + Assert.Equal(Large, queue._tasks.Count); // After things re-poll, they should still be working at max await Task.Delay(PollMS * 2); - Assert.AreEqual(Small, queue.DesiredNumWorkers); - Assert.AreEqual(Small, queue._tasks.Count); + Assert.Equal(Small, queue.DesiredNumWorkers); + Assert.Equal(Small, queue._tasks.Count); } } - [TestMethod] + [Fact] public async Task DynamicNumThreads_DecreasedThenIncreased() { var subj = new BehaviorSubject(Large); using (var queue = new WorkQueue(subj)) { - Assert.AreEqual(Large, queue.DesiredNumWorkers); - Assert.AreEqual(Large, queue._tasks.Count); + Assert.Equal(Large, queue.DesiredNumWorkers); + Assert.Equal(Large, queue._tasks.Count); subj.OnNext(Small); - Assert.AreEqual(Small, queue.DesiredNumWorkers); - Assert.AreEqual(Large, queue._tasks.Count); + Assert.Equal(Small, queue.DesiredNumWorkers); + Assert.Equal(Large, queue._tasks.Count); subj.OnNext(Large); // New threads allocated immediately - Assert.AreEqual(Large, queue.DesiredNumWorkers); - Assert.AreEqual(Large, queue._tasks.Count); + Assert.Equal(Large, queue.DesiredNumWorkers); + Assert.Equal(Large, queue._tasks.Count); // After things re-poll, still here await Task.Delay(PollMS * 2); - Assert.AreEqual(Large, queue.DesiredNumWorkers); - Assert.AreEqual(Large, queue._tasks.Count); + Assert.Equal(Large, queue.DesiredNumWorkers); + Assert.Equal(Large, queue._tasks.Count); } } #endregion @@ -172,7 +168,7 @@ namespace Wabbajack.Test /// The solution to this is just make sure that any work done relating to WorkQueue be done within its own Task.Run() call, so that if it that thread /// "takes over" a workqueue loop, it doesn't matter as it was a threadpool thread anyway. /// - [TestMethod] + [Fact] public async Task Deadlock() { var task = Task.Run(async () => @@ -186,8 +182,8 @@ namespace Wabbajack.Test tcs.SetResult(true); } }); - var completed = Task.WhenAny(Task.Delay(3000), task); - Assert.ReferenceEquals(completed, task); + var completed = await Task.WhenAny(Task.Delay(3000), task); + Assert.Equal(completed, task); } #endregion @@ -218,8 +214,8 @@ namespace Wabbajack.Test object lockObj = new object(); using (var queue = new WorkQueue(subj)) { - Assert.AreEqual(Large, queue.DesiredNumWorkers); - Assert.AreEqual(Large, queue._tasks.Count); + Assert.Equal(Large, queue.DesiredNumWorkers); + Assert.Equal(Large, queue._tasks.Count); bool[] workStartedArray = new bool[Large]; async Task Job(int num, bool[] b) @@ -240,7 +236,7 @@ namespace Wabbajack.Test // Show that all jobs are started lock (lockObj) { - Assert.AreEqual(Large, workStartedArray.Where(i => i).Count()); + Assert.Equal(Large, workStartedArray.Where(i => i).Count()); } await Task.Delay(15000); @@ -251,8 +247,8 @@ namespace Wabbajack.Test // that kicked it off and is in charge of the continuation tasks. // Parallel worker Tasks have now coalesced into a single thread Task.Run(() => tcs.SetResult(true)).FireAndForget(); - Assert.AreEqual(Large, queue.DesiredNumWorkers); - Assert.AreEqual(Large, queue._tasks.Count); + Assert.Equal(Large, queue.DesiredNumWorkers); + Assert.Equal(Large, queue._tasks.Count); await Task.Delay(10000); @@ -264,7 +260,7 @@ namespace Wabbajack.Test // Show that only one job was started/worked on (by our one coalesced worker thread) lock (lockObj) { - Assert.AreEqual(1, secondWorkStartedArray.Where(i => i).Count()); + Assert.Equal(1, secondWorkStartedArray.Where(i => i).Count()); } } } diff --git a/Wabbajack.Common/WorkQueue.cs b/Wabbajack.Common/WorkQueue.cs index d5f30055..bc794504 100644 --- a/Wabbajack.Common/WorkQueue.cs +++ b/Wabbajack.Common/WorkQueue.cs @@ -16,7 +16,7 @@ namespace Wabbajack.Common { public class WorkQueue : IDisposable { - internal AsyncBlockingCollection> Queue = new AsyncBlockingCollection>(); + internal BlockingCollection> Queue = new BlockingCollection>(new ConcurrentStack>()); public const int UnassignedCpuId = 0; @@ -31,7 +31,8 @@ namespace Wabbajack.Common public IObservable Status => _Status; private int _nextCpuID = 1; // Start at 1, as 0 is "Unassigned" - internal Dictionary _tasks = new Dictionary(); + // Public for testing reasons + public Dictionary _tasks = new Dictionary(); public int DesiredNumWorkers { get; private set; } = 0; private CancellationTokenSource _shutdown = new CancellationTokenSource(); @@ -50,7 +51,7 @@ namespace Wabbajack.Common private readonly Subject> _activeNumThreadsObservable = new Subject>(); - public TimeSpan PollMS = TimeSpan.FromMilliseconds(200); + public const int PollMS = 200; /// /// Creates a WorkQueue with the given number of threads @@ -124,7 +125,7 @@ namespace Wabbajack.Common bool got; try { - (got, f) = await Queue.TryTake(PollMS, _shutdown.Token); + got = Queue.TryTake(out f, PollMS, _shutdown.Token); } catch (Exception) { diff --git a/Wabbajack.Test/ACompilerTest.cs b/Wabbajack.Test/ACompilerTest.cs index 776b2265..1fc97e5e 100644 --- a/Wabbajack.Test/ACompilerTest.cs +++ b/Wabbajack.Test/ACompilerTest.cs @@ -4,7 +4,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Wabbajack.Common; using Wabbajack.Lib; using Wabbajack.Lib.LibCefHelpers; -using Wabbajack.Util; namespace Wabbajack.Test { diff --git a/Wabbajack.Test/MiscTests.cs b/Wabbajack.Test/MiscTests.cs deleted file mode 100644 index d8cd8a07..00000000 --- a/Wabbajack.Test/MiscTests.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Alphaleonis.Win32.Filesystem; -using Wabbajack.Common; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Utils = Wabbajack.Common.Utils; - -namespace Wabbajack.Test -{ - [TestClass] - public class MiscTests - { - [TestMethod] - public void TestDiskSpeed() - { - using (var queue = new WorkQueue()) - { - var speed = Utils.TestDiskSpeed(queue, @".\"); - } - } - - [TestMethod] - public void TestHash() - { - const string data = "Cheese for Everyone!"; - File.WriteAllText("test.data", data); - Assert.AreEqual("eSIyd+KOG3s=", "test.data".FileHashCached(), "Hash is cached"); - Assert.IsTrue(Utils.TryGetHashCache("test.data", out var fileHash), "New caching method is invoked"); - Assert.AreEqual("eSIyd+KOG3s=", fileHash, "The correct hash value is cached"); - Assert.AreNotEqual("eSIyd+KOG3s=", File.ReadAllText("test.data" + Consts.HashFileExtension), "We don't store the hash in plaintext"); - } - } -} diff --git a/Wabbajack.Test/PMapTests.cs b/Wabbajack.Test/PMapTests.cs deleted file mode 100644 index 40681c6e..00000000 --- a/Wabbajack.Test/PMapTests.cs +++ /dev/null @@ -1,230 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Wabbajack.Common; - -namespace Wabbajack.Test -{ - [TestClass] - public class PMapTests - { - const int TypicalThreadCount = 6; - const int TypicalDelayMS = 50; - const int TimeoutSeconds = 15; - - [TestMethod] - public async Task Typical_Action() - { - using (var queue = new WorkQueue(TypicalThreadCount)) - { - var input = Enumerable.Range(0, TypicalThreadCount * 2).ToArray(); - var output = new List(); - var workTask = Enumerable.Range(0, TypicalThreadCount * 2) - .PMap(queue, (item) => - { - Assert.IsTrue(WorkQueue.WorkerThread); - Thread.Sleep(TypicalDelayMS); - lock (output) - { - output.Add(item); - } - }); - await workTask.TimeoutButContinue(TimeSpan.FromSeconds(TimeoutSeconds), () => throw new TimeoutException()); - Assert.IsTrue(input.SequenceEqual(output.OrderBy(i => i))); - } - } - - [TestMethod] - public async Task Typical_Func() - { - using (var queue = new WorkQueue(TypicalThreadCount)) - { - var input = Enumerable.Range(0, TypicalThreadCount * 2).ToArray(); - var workTask = Enumerable.Range(0, TypicalThreadCount * 2) - .PMap(queue, (item) => - { - Assert.IsTrue(WorkQueue.WorkerThread); - Thread.Sleep(TypicalDelayMS); - return item.ToString(); - }); - var results = await workTask.TimeoutButContinue(TimeSpan.FromSeconds(TimeoutSeconds), () => throw new TimeoutException()); - Assert.IsTrue(input.Select(i => i.ToString()).SequenceEqual(results)); - } - } - - [TestMethod] - public async Task Typical_Task() - { - using (var queue = new WorkQueue(TypicalThreadCount)) - { - var input = Enumerable.Range(0, TypicalThreadCount * 2).ToArray(); - var output = new List(); - var workTask = Enumerable.Range(0, TypicalThreadCount * 2) - .PMap(queue, async (item) => - { - Assert.IsTrue(WorkQueue.WorkerThread); - await Task.Delay(TypicalDelayMS); - lock (output) - { - output.Add(item); - } - }); - await workTask.TimeoutButContinue(TimeSpan.FromSeconds(TimeoutSeconds), () => throw new TimeoutException()); - Assert.IsTrue(input.SequenceEqual(output.OrderBy(i => i))); - } - } - - [TestMethod] - public async Task Typical_TaskReturn() - { - using (var queue = new WorkQueue(TypicalThreadCount)) - { - var input = Enumerable.Range(0, TypicalThreadCount * 2).ToArray(); - var workTask = Enumerable.Range(0, TypicalThreadCount * 2) - .PMap(queue, async (item) => - { - Assert.IsTrue(WorkQueue.WorkerThread); - await Task.Delay(TypicalDelayMS); - return item.ToString(); - }); - var results = await workTask.TimeoutButContinue(TimeSpan.FromSeconds(TimeoutSeconds), () => throw new TimeoutException()); - Assert.IsTrue(input.Select(i => i.ToString()).SequenceEqual(results)); - } - } - - [TestMethod] - public async Task NestedAction() - { - using (var queue = new WorkQueue(TypicalThreadCount)) - { - var input = Enumerable.Range(0, TypicalThreadCount * 2).ToArray(); - var inputConstructedResults = input.SelectMany(i => Enumerable.Range(i * 100, TypicalThreadCount * 2)); - var output = new List(); - var workTask = Enumerable.Range(0, TypicalThreadCount * 2) - .PMap(queue, async (item) => - { - Assert.IsTrue(WorkQueue.WorkerThread); - await Enumerable.Range(item * 100, TypicalThreadCount * 2) - .PMap(queue, async (subItem) => - { - Assert.IsTrue(WorkQueue.WorkerThread); - Thread.Sleep(TypicalDelayMS); - lock (output) - { - output.Add(subItem); - } - }); - }); - await workTask.TimeoutButContinue(TimeSpan.FromSeconds(TimeoutSeconds), () => throw new TimeoutException()); - Assert.IsTrue(inputConstructedResults.SequenceEqual(output.OrderBy(i => i))); - } - } - - [TestMethod] - public async Task Nested_Func() - { - using (var queue = new WorkQueue(TypicalThreadCount)) - { - var input = Enumerable.Range(0, TypicalThreadCount * 2).ToArray(); - var inputConstructedResults = input.SelectMany(i => Enumerable.Range(i * 100, TypicalThreadCount * 2)); - var workTask = Enumerable.Range(0, TypicalThreadCount * 2) - .PMap(queue, async (item) => - { - Assert.IsTrue(WorkQueue.WorkerThread); - return await Enumerable.Range(item * 100, TypicalThreadCount * 2) - .PMap(queue, (subItem) => - { - Assert.IsTrue(WorkQueue.WorkerThread); - Thread.Sleep(TypicalDelayMS); - return subItem; - }); - }); - var results = await workTask.TimeoutButContinue(TimeSpan.FromSeconds(TimeoutSeconds), () => throw new TimeoutException()); - Assert.IsTrue(inputConstructedResults.SequenceEqual(results.SelectMany(i => i))); - } - } - - [TestMethod] - public async Task Nested_Task() - { - using (var queue = new WorkQueue(TypicalThreadCount)) - { - var input = Enumerable.Range(0, TypicalThreadCount * 2).ToArray(); - var inputConstructedResults = input.SelectMany(i => Enumerable.Range(i * 100, TypicalThreadCount * 2)); - var output = new List(); - var workTask = Enumerable.Range(0, TypicalThreadCount * 2) - .PMap(queue, async (item) => - { - Assert.IsTrue(WorkQueue.WorkerThread); - await Enumerable.Range(item * 100, TypicalThreadCount * 2) - .PMap(queue, async (subItem) => - { - Assert.IsTrue(WorkQueue.WorkerThread); - await Task.Delay(TypicalDelayMS); - lock (output) - { - output.Add(subItem); - } - }); - }); - await workTask.TimeoutButContinue(TimeSpan.FromSeconds(TimeoutSeconds), () => throw new TimeoutException()); - Assert.IsTrue(inputConstructedResults.SequenceEqual(output.OrderBy(i => i))); - } - } - - [TestMethod] - public async Task Nested_TaskReturn() - { - using (var queue = new WorkQueue(TypicalThreadCount)) - { - var input = Enumerable.Range(0, TypicalThreadCount * 2).ToArray(); - var inputConstructedResults = input.SelectMany(i => Enumerable.Range(i * 100, TypicalThreadCount * 2)); - var workTask = Enumerable.Range(0, TypicalThreadCount * 2) - .PMap(queue, async (item) => - { - Assert.IsTrue(WorkQueue.WorkerThread); - return await Enumerable.Range(item * 100, TypicalThreadCount * 2) - .PMap(queue, async (subItem) => - { - Assert.IsTrue(WorkQueue.WorkerThread); - await Task.Delay(TypicalDelayMS); - return subItem; - }); - }); - var results = await workTask.TimeoutButContinue(TimeSpan.FromSeconds(TimeoutSeconds), () => throw new TimeoutException()); - Assert.IsTrue(inputConstructedResults.SequenceEqual(results.SelectMany(i => i))); - } - } - - [TestMethod] - public async Task Nested_BackgroundThreadsInvolved() - { - using (var queue = new WorkQueue(TypicalThreadCount)) - { - var input = Enumerable.Range(0, TypicalThreadCount * 2).ToArray(); - var inputConstructedResults = input.SelectMany(i => Enumerable.Range(i * 100, TypicalThreadCount * 2)); - var workTask = Enumerable.Range(0, TypicalThreadCount * 2) - .PMap(queue, async (item) => - { - Assert.IsTrue(WorkQueue.WorkerThread); - return await Enumerable.Range(item * 100, TypicalThreadCount * 2) - .PMap(queue, async (subItem) => - { - return await Task.Run(async () => - { - Assert.IsTrue(WorkQueue.WorkerThread); - await Task.Delay(TypicalDelayMS); - return subItem; - }); - }); - }); - var results = await workTask.TimeoutButContinue(TimeSpan.FromSeconds(TimeoutSeconds), () => throw new TimeoutException()); - Assert.IsTrue(inputConstructedResults.SequenceEqual(results.SelectMany(i => i))); - } - } - } -} diff --git a/Wabbajack.Test/TestUtils.cs b/Wabbajack.Test/TestUtils.cs index e94dfe3c..9709c4bf 100644 --- a/Wabbajack.Test/TestUtils.cs +++ b/Wabbajack.Test/TestUtils.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; +using System.Threading.Tasks; using Alphaleonis.Win32.Filesystem; using Microsoft.VisualStudio.TestTools.UnitTesting; using Wabbajack.Common; @@ -22,37 +23,37 @@ namespace Wabbajack.Test WorkingDirectory = Path.Combine(Directory.GetCurrentDirectory(), "tmp_data"); } - public string WorkingDirectory { get;} + public AbsolutePath WorkingDirectory { get;} public string ID { get; } public Random RNG => _rng; public Game Game { get; set; } - public string TestFolder => Path.Combine(WorkingDirectory, ID); - public string GameFolder => Path.Combine(WorkingDirectory, ID, "game_folder"); + public AbsolutePath TestFolder => WorkingDirectory.Combine(ID); + public AbsolutePath GameFolder => WorkingDirectory.Combine(ID, "game_folder"); - public string MO2Folder => Path.Combine(WorkingDirectory, ID, "mo2_folder"); - public string ModsFolder => Path.Combine(MO2Folder, Consts.MO2ModFolderName); - public string DownloadsFolder => Path.Combine(MO2Folder, "downloads"); + public AbsolutePath MO2Folder => WorkingDirectory.Combine(ID, "mo2_folder"); + public AbsolutePath ModsFolder => MO2Folder.Combine(Consts.MO2ModFolderName); + public AbsolutePath DownloadsFolder => MO2Folder.Combine("downloads"); - public string InstallFolder => Path.Combine(TestFolder, "installed"); + public AbsolutePath InstallFolder => TestFolder.Combine("installed"); public HashSet Profiles = new HashSet(); public List Mods = new List(); - public void Configure() + public async Task Configure() { - File.WriteAllLines(Path.Combine(MO2Folder, "ModOrganizer.ini"), new [] + await MO2Folder.Combine("ModOrganizer.ini").WriteAllLinesAsync(new [] { "[General]", $"gameName={Game.MetaData().MO2Name}", - $"gamePath={GameFolder.Replace("\\", "\\\\")}", + $"gamePath={((string)GameFolder).Replace("\\", "\\\\")}", $"download_directory={DownloadsFolder}" }); - Directory.CreateDirectory(DownloadsFolder); - Directory.CreateDirectory(Path.Combine(GameFolder, "Data")); + DownloadsFolder.CreateDirectory(); + GameFolder.Combine("Data").CreateDirectory(); Profiles.Do(profile => { @@ -260,9 +261,6 @@ namespace Wabbajack.Test return full_path; } - public static object RandomeOne(params object[] opts) - { - return opts[_rng.Next(0, opts.Length)]; - } + } } diff --git a/Wabbajack.Test/Wabbajack.Test.csproj b/Wabbajack.Test/Wabbajack.Test.csproj index 346a823d..f2f9015c 100644 --- a/Wabbajack.Test/Wabbajack.Test.csproj +++ b/Wabbajack.Test/Wabbajack.Test.csproj @@ -1,10 +1,10 @@  - - netcoreapp3.1 - x64 - win10-x64 - + + netcoreapp3.1 + x64 + win10-x64 + x64 @@ -26,7 +26,6 @@ - diff --git a/Wabbajack.VirtualFileSystem.Test/Wabbajack.VirtualFileSystem.Test.csproj b/Wabbajack.VirtualFileSystem.Test/Wabbajack.VirtualFileSystem.Test.csproj index 0472acfb..f7eccf45 100644 --- a/Wabbajack.VirtualFileSystem.Test/Wabbajack.VirtualFileSystem.Test.csproj +++ b/Wabbajack.VirtualFileSystem.Test/Wabbajack.VirtualFileSystem.Test.csproj @@ -8,9 +8,9 @@ - - - + + + diff --git a/Wabbajack.sln b/Wabbajack.sln index 59bd6edc..bac714d9 100644 --- a/Wabbajack.sln +++ b/Wabbajack.sln @@ -38,6 +38,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wabbajack.CLI", "Wabbajack. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wabbajack.Launcher", "Wabbajack.Launcher\Wabbajack.Launcher.csproj", "{D6856DBF-C959-4867-A8A8-343DA2D2715E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wabbajack.Common.Test", "Wabbajack.Common.Test\Wabbajack.Common.Test.csproj", "{BA8A3E49-60D2-4BA2-B285-CB09FFDB6D32}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -76,12 +78,6 @@ Global {5D6A2EAF-6604-4C51-8AE2-A746B4BC5E3E}.Release|Any CPU.ActiveCfg = Release|x64 {5D6A2EAF-6604-4C51-8AE2-A746B4BC5E3E}.Release|x64.ActiveCfg = Release|x64 {5D6A2EAF-6604-4C51-8AE2-A746B4BC5E3E}.Release|x64.Build.0 = Release|x64 - {37E4D421-8FD3-4D57-8F3A-7A511D6ED5C5}.Debug|Any CPU.ActiveCfg = Debug|x64 - {37E4D421-8FD3-4D57-8F3A-7A511D6ED5C5}.Debug|x64.ActiveCfg = Debug|x64 - {37E4D421-8FD3-4D57-8F3A-7A511D6ED5C5}.Debug|x64.Build.0 = Debug|x64 - {37E4D421-8FD3-4D57-8F3A-7A511D6ED5C5}.Release|Any CPU.ActiveCfg = Release|x64 - {37E4D421-8FD3-4D57-8F3A-7A511D6ED5C5}.Release|x64.ActiveCfg = Release|x64 - {37E4D421-8FD3-4D57-8F3A-7A511D6ED5C5}.Release|x64.Build.0 = Release|x64 {DE18D89E-39C5-48FD-8E42-16235E3C4593}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DE18D89E-39C5-48FD-8E42-16235E3C4593}.Debug|Any CPU.Build.0 = Debug|Any CPU {DE18D89E-39C5-48FD-8E42-16235E3C4593}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -128,6 +124,14 @@ Global {D6856DBF-C959-4867-A8A8-343DA2D2715E}.Release|Any CPU.Build.0 = Release|Any CPU {D6856DBF-C959-4867-A8A8-343DA2D2715E}.Release|x64.ActiveCfg = Release|Any CPU {D6856DBF-C959-4867-A8A8-343DA2D2715E}.Release|x64.Build.0 = Release|Any CPU + {BA8A3E49-60D2-4BA2-B285-CB09FFDB6D32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BA8A3E49-60D2-4BA2-B285-CB09FFDB6D32}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BA8A3E49-60D2-4BA2-B285-CB09FFDB6D32}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BA8A3E49-60D2-4BA2-B285-CB09FFDB6D32}.Release|Any CPU.Build.0 = Release|Any CPU + {BA8A3E49-60D2-4BA2-B285-CB09FFDB6D32}.Release|x64.ActiveCfg = Release|Any CPU + {BA8A3E49-60D2-4BA2-B285-CB09FFDB6D32}.Release|x64.Build.0 = Release|Any CPU + {BA8A3E49-60D2-4BA2-B285-CB09FFDB6D32}.Debug|x64.ActiveCfg = Debug|x64 + {BA8A3E49-60D2-4BA2-B285-CB09FFDB6D32}.Debug|x64.Build.0 = Debug|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE