Moved Wabbajack.Commons tests into Wabbajack.Commons.Test out of Wabbajack.Test. They all pass

This commit is contained in:
Timothy Baldridge 2020-03-25 22:25:48 -06:00
parent 006b045348
commit 51bad949ec
20 changed files with 457 additions and 459 deletions

View File

@ -20,10 +20,10 @@ namespace Compression.BSA.Test
[TestClass] [TestClass]
public class BSATests public class BSATests
{ {
private static string _stagingFolder = "NexusDownloads"; private static AbsolutePath _stagingFolder = ((RelativePath)"NexusDownloads").RelativeToEntryPoint();
private static string _bsaFolder = "BSAs"; private static AbsolutePath _bsaFolder = ((RelativePath)"BSAs").RelativeToEntryPoint();
private static string _testDir = "BSA Test Dir"; private static AbsolutePath _testDir = ((RelativePath)"BSA Test Dir").RelativeToEntryPoint();
private static string _tempDir = "BSA Temp Dir"; private static AbsolutePath _tempDir = ((RelativePath)"BSA Temp Dir").RelativeToEntryPoint();
public TestContext TestContext { get; set; } public TestContext TestContext { get; set; }
@ -34,11 +34,8 @@ namespace Compression.BSA.Test
{ {
Queue = new WorkQueue(); Queue = new WorkQueue();
Utils.LogMessages.Subscribe(f => testContext.WriteLine(f.ShortDescription)); Utils.LogMessages.Subscribe(f => testContext.WriteLine(f.ShortDescription));
if (!Directory.Exists(_stagingFolder)) _stagingFolder.DeleteDirectory();
Directory.CreateDirectory(_stagingFolder); _bsaFolder.DeleteDirectory();
if (!Directory.Exists(_bsaFolder))
Directory.CreateDirectory(_bsaFolder);
var modIDs = new[] var modIDs = new[]
{ {
@ -53,22 +50,21 @@ namespace Compression.BSA.Test
await Task.WhenAll(modIDs.Select(async (info) => await Task.WhenAll(modIDs.Select(async (info) =>
{ {
var filename = await DownloadMod(info); var filename = await DownloadMod(info);
var folder = Path.Combine(_bsaFolder, info.Item1.ToString(), info.Item2.ToString()); var folder = _bsaFolder.Combine(info.Item1.ToString(), info.Item2.ToString());
if (!Directory.Exists(folder)) folder.CreateDirectory();
Directory.CreateDirectory(folder);
await FileExtractor.ExtractAll(Queue, filename, folder); await FileExtractor.ExtractAll(Queue, filename, folder);
})); }));
} }
private static async Task<string> DownloadMod((Game, int) info) private static async Task<AbsolutePath> DownloadMod((Game, int) info)
{ {
using var client = await NexusApiClient.Get(); using var client = await NexusApiClient.Get();
var results = await client.GetModFiles(info.Item1, info.Item2); var results = await client.GetModFiles(info.Item1, info.Item2);
var file = results.files.FirstOrDefault(f => f.is_primary) ?? var file = results.files.FirstOrDefault(f => f.is_primary) ??
results.files.OrderByDescending(f => f.uploaded_timestamp).First(); 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 var state = new NexusDownloader.State
{ {
@ -82,41 +78,38 @@ namespace Compression.BSA.Test
public static IEnumerable<object[]> BSAs() public static IEnumerable<object[]> BSAs()
{ {
return Directory.EnumerateFiles(_bsaFolder, "*", DirectoryEnumerationOptions.Recursive) return _bsaFolder.EnumerateFiles()
.Where(f => Consts.SupportedBSAs.Contains(Path.GetExtension(f))) .Where(f => Consts.SupportedBSAs.Contains(f.Extension))
.Select(nm => new object[] {nm}); .Select(nm => new object[] {nm});
} }
[TestMethod] [TestMethod]
[DataTestMethod] [DataTestMethod]
[DynamicData(nameof(BSAs), DynamicDataSourceType.Method)] [DynamicData(nameof(BSAs), DynamicDataSourceType.Method)]
public async Task BSACompressionRecompression(string bsa) public async Task BSACompressionRecompression(AbsolutePath bsa)
{ {
TestContext.WriteLine($"From {bsa}"); TestContext.WriteLine($"From {bsa}");
TestContext.WriteLine("Cleaning Output Dir"); TestContext.WriteLine("Cleaning Output Dir");
if (Directory.Exists(_tempDir)) Utils.DeleteDirectory(_tempDir); _tempDir.DeleteDirectory();
Directory.CreateDirectory(_tempDir); _tempDir.CreateDirectory();
TestContext.WriteLine($"Reading {bsa}"); TestContext.WriteLine($"Reading {bsa}");
string tempFile = Path.Combine("tmp.bsa"); var tempFile = ((RelativePath)"tmp.bsa").RelativeToEntryPoint();
var size = File.GetSize(bsa); var size = bsa.Size;
using (var a = BSADispatch.OpenRead(bsa)) using (var a = BSADispatch.OpenRead(bsa))
{ {
await a.Files.PMap(Queue, file => await a.Files.PMap(Queue, file =>
{ {
var absName = Path.Combine(_tempDir, file.Path); var absName = _tempDir.Combine(file.Path);
ViaJson(file.State); ViaJson(file.State);
if (!Directory.Exists(Path.GetDirectoryName(absName))) absName.Parent.CreateDirectory();
Directory.CreateDirectory(Path.GetDirectoryName(absName)); using (var fs = absName.Create())
using (var fs = File.Open(absName, FileMode.Create))
{ {
file.CopyDataTo(fs); file.CopyDataTo(fs);
} }
Assert.AreEqual(file.Size, new FileInfo(absName).Length); Assert.AreEqual(file.Size, absName.Size);
}); });
Console.WriteLine($"Building {bsa}"); Console.WriteLine($"Building {bsa}");
@ -125,8 +118,8 @@ namespace Compression.BSA.Test
{ {
var streams = await a.Files.PMap(Queue, file => var streams = await a.Files.PMap(Queue, file =>
{ {
var absPath = Path.Combine(_tempDir, file.Path); var absPath = _tempDir.Combine(file.Path);
var str = File.OpenRead(absPath); var str = absPath.OpenRead();
w.AddFile(ViaJson(file.State), str); w.AddFile(ViaJson(file.State), str);
return str; return str;
}); });

View File

@ -1,17 +1,12 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Xunit;
using Wabbajack.Common;
namespace Wabbajack.Test namespace Wabbajack.Common.Test
{ {
[TestClass]
public class AsyncLockTests public class AsyncLockTests
{ {
[TestMethod] [Fact]
public async Task Typical() public async Task Typical()
{ {
var asyncLock = new AsyncLock(); var asyncLock = new AsyncLock();
@ -29,20 +24,20 @@ namespace Wabbajack.Test
await Task.Delay(200); await Task.Delay(200);
using (await asyncLock.Wait()) using (await asyncLock.Wait())
{ {
Assert.IsTrue(firstRun); Assert.True(firstRun);
} }
}); });
await Task.WhenAll(first, second); await Task.WhenAll(first, second);
} }
[TestMethod] [Fact]
public async Task Exception() public async Task Exception()
{ {
var asyncLock = new AsyncLock(); var asyncLock = new AsyncLock();
bool firstRun = false; bool firstRun = false;
bool secondRun = false; bool secondRun = false;
// Throw exception inside a lock // Throw exception inside a lock
await Assert.ThrowsExceptionAsync<Exception>(() => await Assert.ThrowsAsync<Exception>(() =>
{ {
return Task.Run(async () => return Task.Run(async () =>
{ {
@ -62,7 +57,7 @@ namespace Wabbajack.Test
await Task.Delay(200); await Task.Delay(200);
using (await asyncLock.Wait()) using (await asyncLock.Wait())
{ {
Assert.IsTrue(firstRun); Assert.True(firstRun);
secondRun = true; secondRun = true;
} }
}), }),

View File

@ -1,18 +1,14 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using Xunit;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Wabbajack.Common;
namespace Wabbajack.Test namespace Wabbajack.Common.Test
{ {
[TestClass]
public class EncryptedDataTests public class EncryptedDataTests
{ {
[TestMethod] [Fact]
public async Task CanDetectNewEncryptedData() public async Task CanDetectNewEncryptedData()
{ {
var test_string = Guid.NewGuid().ToString(); var test_string = Guid.NewGuid().ToString();
@ -28,7 +24,7 @@ namespace Wabbajack.Test
test_string.ToEcryptedJson(test_string); test_string.ToEcryptedJson(test_string);
await Task.Delay(100); await Task.Delay(100);
CollectionAssert.Contains(data, test_string); Assert.Contains(test_string, data);
} }

View File

@ -1,18 +1,17 @@
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Xunit;
namespace Wabbajack.Test namespace Wabbajack.Common.Test
{ {
[TestClass]
public class HttpClientTests public class HttpClientTests
{ {
[TestMethod] [Fact]
public async Task DoesntReuseHttpMessages() public async Task DoesntReuseHttpMessages()
{ {
var client = new Common.Http.Client(); var client = new Common.Http.Client();
// If we reuse the HTTP message this will throw a invalid operation exception // If we reuse the HTTP message this will throw a invalid operation exception
await Assert.ThrowsExceptionAsync<HttpRequestException>(async () => await client.GetAsync("http://blerg.blaz.bloz.buz")); await Assert.ThrowsAsync<HttpRequestException>(async () => await client.GetAsync("http://blerg.blaz.bloz.buz"));
} }
} }
} }

View File

@ -1,25 +1,23 @@
using Microsoft.VisualStudio.TestTools.UnitTesting; using Xunit;
using Wabbajack.Common;
namespace Wabbajack.Test namespace Wabbajack.Common.Test
{ {
[TestClass]
public class IniTests public class IniTests
{ {
[TestMethod] [Fact]
public void TestByteArrayParsing() public void TestByteArrayParsing()
{ {
Assert.AreEqual("bar", @"[General] Assert.Equal("bar", @"[General]
foo = bar".LoadIniString().General.foo); foo = bar".LoadIniString().General.foo);
Assert.AreEqual("baz\\bar", @"[General] Assert.Equal("baz\\bar", @"[General]
foo = baz\\bar".LoadIniString().General.foo); foo = baz\\bar".LoadIniString().General.foo);
Assert.AreEqual("bar", @"[General] Assert.Equal("bar", @"[General]
foo = @ByteArray(bar)".LoadIniString().General.foo); 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); 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);
} }

View File

@ -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());
}
}
}

View File

@ -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<int>();
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<int>();
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<int>();
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<int>();
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)));
}
}
}

View File

@ -1,29 +1,25 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Xunit;
namespace Wabbajack.Test namespace Wabbajack.Common.Test
{ {
[TestClass]
public class TaskExtTests public class TaskExtTests
{ {
[TestMethod] [Fact]
public async Task TimeoutButContinue_Typical() public async Task TimeoutButContinue_Typical()
{ {
bool timedOut = false; bool timedOut = false;
await Task.Delay(100).TimeoutButContinue(TimeSpan.FromSeconds(1), () => timedOut = true); await Task.Delay(100).TimeoutButContinue(TimeSpan.FromSeconds(1), () => timedOut = true);
Assert.IsFalse(timedOut); Assert.False(timedOut);
} }
[TestMethod] [Fact]
public async Task TimeoutButContinue_TimedOut() public async Task TimeoutButContinue_TimedOut()
{ {
bool timedOut = false; bool timedOut = false;
await Task.Delay(300).TimeoutButContinue(TimeSpan.FromMilliseconds(100), () => timedOut = true); await Task.Delay(300).TimeoutButContinue(TimeSpan.FromMilliseconds(100), () => timedOut = true);
Assert.IsTrue(timedOut); Assert.True(timedOut);
} }
} }
} }

View File

@ -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)];
}
}
}

View File

@ -2,33 +2,29 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Xunit;
using Wabbajack.Common;
namespace Wabbajack.Test namespace Wabbajack.Common.Test
{ {
[TestClass]
public class UtilsTests public class UtilsTests
{ {
[TestMethod] [Fact]
public void IsInPathTests() public void IsInPathTests()
{ {
Assert.IsTrue("c:\\foo\\bar.exe".IsInPath("c:\\foo")); Assert.True("c:\\foo\\bar.exe".IsInPath("c:\\foo"));
Assert.IsFalse("c:\\foo\\bar.exe".IsInPath("c:\\fo")); Assert.False("c:\\foo\\bar.exe".IsInPath("c:\\fo"));
Assert.IsTrue("c:\\Foo\\bar.exe".IsInPath("c:\\foo")); Assert.True("c:\\Foo\\bar.exe".IsInPath("c:\\foo"));
Assert.IsTrue("c:\\foo\\bar.exe".IsInPath("c:\\Foo")); Assert.True("c:\\foo\\bar.exe".IsInPath("c:\\Foo"));
Assert.IsTrue("c:\\foo\\bar.exe".IsInPath("c:\\fOo")); Assert.True("c:\\foo\\bar.exe".IsInPath("c:\\fOo"));
Assert.IsTrue("c:\\foo\\bar.exe".IsInPath("c:\\foo\\")); Assert.True("c:\\foo\\bar.exe".IsInPath("c:\\foo\\"));
Assert.IsTrue("c:\\foo\\bar\\".IsInPath("c:\\foo\\")); Assert.True("c:\\foo\\bar\\".IsInPath("c:\\foo\\"));
} }
[TestMethod] [Theory]
[DataTestMethod] [ClassData(typeof(PatchData))]
[DynamicData(nameof(PatchData), DynamicDataSourceType.Method)]
public async Task DiffCreateAndApply(byte[] src, byte[] dest, DiffMethod method) public async Task DiffCreateAndApply(byte[] src, byte[] dest, DiffMethod method)
{ {
await using var ms = new MemoryStream(); await using var ms = new MemoryStream();
@ -51,7 +47,7 @@ namespace Wabbajack.Test
var patch = ms.ToArray(); var patch = ms.ToArray();
await using var resultStream = new MemoryStream(); await using var resultStream = new MemoryStream();
Utils.ApplyPatch(new MemoryStream(src), () => new MemoryStream(patch), resultStream); 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, BSDiff,
OctoDiff OctoDiff
} }
public static IEnumerable<object[]> PatchData() public class PatchData : TheoryData<byte[], byte[], DiffMethod>
{ {
var maxSize = 1024 * 1024 * 8; public PatchData()
return Enumerable.Range(0, 10).Select(x => new[] {TestUtils.RandomData(maxSize:maxSize), TestUtils.RandomData(maxSize:maxSize), TestUtils.RandomeOne(DiffMethod.Default, DiffMethod.OctoDiff, DiffMethod.BSDiff)}); {
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));
});
}
} }
} }
} }

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<Platforms>x64</Platforms>
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.console" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Wabbajack.Common\Wabbajack.Common.csproj" />
</ItemGroup>
</Project>

View File

@ -1,17 +1,13 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Reactive.Subjects; using System.Reactive.Subjects;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Xunit;
using Wabbajack.Common;
namespace Wabbajack.Test namespace Wabbajack.Common.Test
{ {
[TestClass]
public class WorkQueueTests public class WorkQueueTests
{ {
#region DynamicNumThreads #region DynamicNumThreads
@ -20,144 +16,144 @@ namespace Wabbajack.Test
const int Small = 4; const int Small = 4;
public TimeSpan PollMS => TimeSpan.FromSeconds(1); public TimeSpan PollMS => TimeSpan.FromSeconds(1);
[TestMethod] [Fact]
public void DynamicNumThreads_Typical() public void DynamicNumThreads_Typical()
{ {
using (var queue = new WorkQueue()) using (var queue = new WorkQueue())
{ {
Assert.AreEqual(Environment.ProcessorCount, queue.DesiredNumWorkers); Assert.Equal(Environment.ProcessorCount, queue.DesiredNumWorkers);
Assert.AreEqual(Environment.ProcessorCount, queue._tasks.Count); Assert.Equal(Environment.ProcessorCount, queue._tasks.Count);
} }
} }
[TestMethod] [Fact]
public void DynamicNumThreads_Increased() public void DynamicNumThreads_Increased()
{ {
var subj = new BehaviorSubject<int>(Small); var subj = new BehaviorSubject<int>(Small);
using (var queue = new WorkQueue(subj)) using (var queue = new WorkQueue(subj))
{ {
Assert.AreEqual(Small, queue.DesiredNumWorkers); Assert.Equal(Small, queue.DesiredNumWorkers);
Assert.AreEqual(Small, queue._tasks.Count); Assert.Equal(Small, queue._tasks.Count);
subj.OnNext(Large); subj.OnNext(Large);
Assert.AreEqual(Large, queue.DesiredNumWorkers); Assert.Equal(Large, queue.DesiredNumWorkers);
Assert.AreEqual(Large, queue._tasks.Count); Assert.Equal(Large, queue._tasks.Count);
} }
} }
[TestMethod] [Fact]
public void DynamicNumThreads_EmptyObs() public void DynamicNumThreads_EmptyObs()
{ {
using (var queue = new WorkQueue(Observable.Empty<int>())) using (var queue = new WorkQueue(Observable.Empty<int>()))
{ {
Assert.AreEqual(0, queue.DesiredNumWorkers); Assert.Equal(0, queue.DesiredNumWorkers);
Assert.AreEqual(0, queue._tasks.Count); Assert.Equal(0, queue._tasks.Count);
} }
} }
[TestMethod] [Fact]
public async Task DynamicNumThreads_Decreased() public async Task DynamicNumThreads_Decreased()
{ {
var subj = new BehaviorSubject<int>(Large); var subj = new BehaviorSubject<int>(Large);
using (var queue = new WorkQueue(subj)) using (var queue = new WorkQueue(subj))
{ {
Assert.AreEqual(Large, queue.DesiredNumWorkers); Assert.Equal(Large, queue.DesiredNumWorkers);
Assert.AreEqual(Large, queue._tasks.Count); Assert.Equal(Large, queue._tasks.Count);
subj.OnNext(Small); subj.OnNext(Small);
Assert.AreEqual(Small, queue.DesiredNumWorkers); Assert.Equal(Small, queue.DesiredNumWorkers);
// Tasks don't go down immediately // 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 // After things re-poll, they should be cleaned
await Task.Delay(PollMS * 2); await Task.Delay(PollMS * 2);
Assert.AreEqual(Small, queue._tasks.Count); Assert.Equal(Small, queue._tasks.Count);
} }
} }
[TestMethod] [Fact]
public async Task DynamicNumThreads_IncreasedWhileWorking() public async Task DynamicNumThreads_IncreasedWhileWorking()
{ {
var subj = new BehaviorSubject<int>(Small); var subj = new BehaviorSubject<int>(Small);
var tcs = new TaskCompletionSource<bool>(); var tcs = new TaskCompletionSource<bool>();
using (var queue = new WorkQueue(subj)) using (var queue = new WorkQueue(subj))
{ {
Assert.AreEqual(Small, queue.DesiredNumWorkers); Assert.Equal(Small, queue.DesiredNumWorkers);
Assert.AreEqual(Small, queue._tasks.Count); Assert.Equal(Small, queue._tasks.Count);
Enumerable.Range(0, Small).Do(_ => queue.QueueTask(() => tcs.Task)); Enumerable.Range(0, Small).Do(_ => queue.QueueTask(() => tcs.Task));
subj.OnNext(Large); subj.OnNext(Large);
Assert.AreEqual(Large, queue.DesiredNumWorkers); Assert.Equal(Large, queue.DesiredNumWorkers);
Assert.AreEqual(Large, queue._tasks.Count); Assert.Equal(Large, queue._tasks.Count);
Task.Run(() => tcs.SetResult(true)).FireAndForget(); Task.Run(() => tcs.SetResult(true)).FireAndForget();
Assert.AreEqual(Large, queue.DesiredNumWorkers); Assert.Equal(Large, queue.DesiredNumWorkers);
Assert.AreEqual(Large, queue._tasks.Count); Assert.Equal(Large, queue._tasks.Count);
await Task.Delay(PollMS * 2); await Task.Delay(PollMS * 2);
Assert.AreEqual(Large, queue.DesiredNumWorkers); Assert.Equal(Large, queue.DesiredNumWorkers);
Assert.AreEqual(Large, queue._tasks.Count); Assert.Equal(Large, queue._tasks.Count);
} }
} }
[TestMethod] [Fact]
public async Task DynamicNumThreads_DecreasedWhileWorking() public async Task DynamicNumThreads_DecreasedWhileWorking()
{ {
var subj = new BehaviorSubject<int>(Large); var subj = new BehaviorSubject<int>(Large);
var tcs = new TaskCompletionSource<bool>(); var tcs = new TaskCompletionSource<bool>();
using (var queue = new WorkQueue(subj)) using (var queue = new WorkQueue(subj))
{ {
Assert.AreEqual(Large, queue.DesiredNumWorkers); Assert.Equal(Large, queue.DesiredNumWorkers);
Assert.AreEqual(Large, queue._tasks.Count); Assert.Equal(Large, queue._tasks.Count);
Enumerable.Range(0, Large).Do(_ => queue.QueueTask(() => tcs.Task)); Enumerable.Range(0, Large).Do(_ => queue.QueueTask(() => tcs.Task));
subj.OnNext(Small); subj.OnNext(Small);
Assert.AreEqual(Small, queue.DesiredNumWorkers); Assert.Equal(Small, queue.DesiredNumWorkers);
Assert.AreEqual(Large, queue._tasks.Count); Assert.Equal(Large, queue._tasks.Count);
// After things re-poll, they should still be working at max // After things re-poll, they should still be working at max
await Task.Delay(PollMS * 2); await Task.Delay(PollMS * 2);
Assert.AreEqual(Large, queue._tasks.Count); Assert.Equal(Large, queue._tasks.Count);
// Complete, repoll, and check again // Complete, repoll, and check again
Task.Run(() => tcs.SetResult(true)).FireAndForget(); Task.Run(() => tcs.SetResult(true)).FireAndForget();
await Task.Delay(PollMS * 2); await Task.Delay(PollMS * 2);
Assert.AreEqual(Small, queue._tasks.Count); Assert.Equal(Small, queue._tasks.Count);
} }
} }
[TestMethod] [Fact]
public async Task DynamicNumThreads_IncreasedThenDecreased() public async Task DynamicNumThreads_IncreasedThenDecreased()
{ {
var subj = new BehaviorSubject<int>(Small); var subj = new BehaviorSubject<int>(Small);
using (var queue = new WorkQueue(subj)) using (var queue = new WorkQueue(subj))
{ {
Assert.AreEqual(Small, queue.DesiredNumWorkers); Assert.Equal(Small, queue.DesiredNumWorkers);
Assert.AreEqual(Small, queue._tasks.Count); Assert.Equal(Small, queue._tasks.Count);
subj.OnNext(Large); subj.OnNext(Large);
Assert.AreEqual(Large, queue.DesiredNumWorkers); Assert.Equal(Large, queue.DesiredNumWorkers);
Assert.AreEqual(Large, queue._tasks.Count); Assert.Equal(Large, queue._tasks.Count);
subj.OnNext(Small); subj.OnNext(Small);
// Still large number of threads, as not immediate // Still large number of threads, as not immediate
Assert.AreEqual(Small, queue.DesiredNumWorkers); Assert.Equal(Small, queue.DesiredNumWorkers);
Assert.AreEqual(Large, queue._tasks.Count); Assert.Equal(Large, queue._tasks.Count);
// After things re-poll, they should still be working at max // After things re-poll, they should still be working at max
await Task.Delay(PollMS * 2); await Task.Delay(PollMS * 2);
Assert.AreEqual(Small, queue.DesiredNumWorkers); Assert.Equal(Small, queue.DesiredNumWorkers);
Assert.AreEqual(Small, queue._tasks.Count); Assert.Equal(Small, queue._tasks.Count);
} }
} }
[TestMethod] [Fact]
public async Task DynamicNumThreads_DecreasedThenIncreased() public async Task DynamicNumThreads_DecreasedThenIncreased()
{ {
var subj = new BehaviorSubject<int>(Large); var subj = new BehaviorSubject<int>(Large);
using (var queue = new WorkQueue(subj)) using (var queue = new WorkQueue(subj))
{ {
Assert.AreEqual(Large, queue.DesiredNumWorkers); Assert.Equal(Large, queue.DesiredNumWorkers);
Assert.AreEqual(Large, queue._tasks.Count); Assert.Equal(Large, queue._tasks.Count);
subj.OnNext(Small); subj.OnNext(Small);
Assert.AreEqual(Small, queue.DesiredNumWorkers); Assert.Equal(Small, queue.DesiredNumWorkers);
Assert.AreEqual(Large, queue._tasks.Count); Assert.Equal(Large, queue._tasks.Count);
subj.OnNext(Large); subj.OnNext(Large);
// New threads allocated immediately // New threads allocated immediately
Assert.AreEqual(Large, queue.DesiredNumWorkers); Assert.Equal(Large, queue.DesiredNumWorkers);
Assert.AreEqual(Large, queue._tasks.Count); Assert.Equal(Large, queue._tasks.Count);
// After things re-poll, still here // After things re-poll, still here
await Task.Delay(PollMS * 2); await Task.Delay(PollMS * 2);
Assert.AreEqual(Large, queue.DesiredNumWorkers); Assert.Equal(Large, queue.DesiredNumWorkers);
Assert.AreEqual(Large, queue._tasks.Count); Assert.Equal(Large, queue._tasks.Count);
} }
} }
#endregion #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 /// 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. /// "takes over" a workqueue loop, it doesn't matter as it was a threadpool thread anyway.
/// </summary> /// </summary>
[TestMethod] [Fact]
public async Task Deadlock() public async Task Deadlock()
{ {
var task = Task.Run(async () => var task = Task.Run(async () =>
@ -186,8 +182,8 @@ namespace Wabbajack.Test
tcs.SetResult(true); tcs.SetResult(true);
} }
}); });
var completed = Task.WhenAny(Task.Delay(3000), task); var completed = await Task.WhenAny(Task.Delay(3000), task);
Assert.ReferenceEquals(completed, task); Assert.Equal(completed, task);
} }
#endregion #endregion
@ -218,8 +214,8 @@ namespace Wabbajack.Test
object lockObj = new object(); object lockObj = new object();
using (var queue = new WorkQueue(subj)) using (var queue = new WorkQueue(subj))
{ {
Assert.AreEqual(Large, queue.DesiredNumWorkers); Assert.Equal(Large, queue.DesiredNumWorkers);
Assert.AreEqual(Large, queue._tasks.Count); Assert.Equal(Large, queue._tasks.Count);
bool[] workStartedArray = new bool[Large]; bool[] workStartedArray = new bool[Large];
async Task Job(int num, bool[] b) async Task Job(int num, bool[] b)
@ -240,7 +236,7 @@ namespace Wabbajack.Test
// Show that all jobs are started // Show that all jobs are started
lock (lockObj) lock (lockObj)
{ {
Assert.AreEqual(Large, workStartedArray.Where(i => i).Count()); Assert.Equal(Large, workStartedArray.Where(i => i).Count());
} }
await Task.Delay(15000); await Task.Delay(15000);
@ -251,8 +247,8 @@ namespace Wabbajack.Test
// that kicked it off and is in charge of the continuation tasks. // that kicked it off and is in charge of the continuation tasks.
// Parallel worker Tasks have now coalesced into a single thread // Parallel worker Tasks have now coalesced into a single thread
Task.Run(() => tcs.SetResult(true)).FireAndForget(); Task.Run(() => tcs.SetResult(true)).FireAndForget();
Assert.AreEqual(Large, queue.DesiredNumWorkers); Assert.Equal(Large, queue.DesiredNumWorkers);
Assert.AreEqual(Large, queue._tasks.Count); Assert.Equal(Large, queue._tasks.Count);
await Task.Delay(10000); 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) // Show that only one job was started/worked on (by our one coalesced worker thread)
lock (lockObj) lock (lockObj)
{ {
Assert.AreEqual(1, secondWorkStartedArray.Where(i => i).Count()); Assert.Equal(1, secondWorkStartedArray.Where(i => i).Count());
} }
} }
} }

View File

@ -16,7 +16,7 @@ namespace Wabbajack.Common
{ {
public class WorkQueue : IDisposable public class WorkQueue : IDisposable
{ {
internal AsyncBlockingCollection<Func<Task>> Queue = new AsyncBlockingCollection<Func<Task>>(); internal BlockingCollection<Func<Task>> Queue = new BlockingCollection<Func<Task>>(new ConcurrentStack<Func<Task>>());
public const int UnassignedCpuId = 0; public const int UnassignedCpuId = 0;
@ -31,7 +31,8 @@ namespace Wabbajack.Common
public IObservable<CPUStatus> Status => _Status; public IObservable<CPUStatus> Status => _Status;
private int _nextCpuID = 1; // Start at 1, as 0 is "Unassigned" private int _nextCpuID = 1; // Start at 1, as 0 is "Unassigned"
internal Dictionary<int, Task> _tasks = new Dictionary<int, Task>(); // Public for testing reasons
public Dictionary<int, Task> _tasks = new Dictionary<int, Task>();
public int DesiredNumWorkers { get; private set; } = 0; public int DesiredNumWorkers { get; private set; } = 0;
private CancellationTokenSource _shutdown = new CancellationTokenSource(); private CancellationTokenSource _shutdown = new CancellationTokenSource();
@ -50,7 +51,7 @@ namespace Wabbajack.Common
private readonly Subject<IObservable<int>> _activeNumThreadsObservable = new Subject<IObservable<int>>(); private readonly Subject<IObservable<int>> _activeNumThreadsObservable = new Subject<IObservable<int>>();
public TimeSpan PollMS = TimeSpan.FromMilliseconds(200); public const int PollMS = 200;
/// <summary> /// <summary>
/// Creates a WorkQueue with the given number of threads /// Creates a WorkQueue with the given number of threads
@ -124,7 +125,7 @@ namespace Wabbajack.Common
bool got; bool got;
try try
{ {
(got, f) = await Queue.TryTake(PollMS, _shutdown.Token); got = Queue.TryTake(out f, PollMS, _shutdown.Token);
} }
catch (Exception) catch (Exception)
{ {

View File

@ -4,7 +4,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
using Wabbajack.Common; using Wabbajack.Common;
using Wabbajack.Lib; using Wabbajack.Lib;
using Wabbajack.Lib.LibCefHelpers; using Wabbajack.Lib.LibCefHelpers;
using Wabbajack.Util;
namespace Wabbajack.Test namespace Wabbajack.Test
{ {

View File

@ -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");
}
}
}

View File

@ -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<int>();
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<int>();
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<int>();
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<int>();
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)));
}
}
}
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Alphaleonis.Win32.Filesystem; using Alphaleonis.Win32.Filesystem;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using Wabbajack.Common; using Wabbajack.Common;
@ -22,37 +23,37 @@ namespace Wabbajack.Test
WorkingDirectory = Path.Combine(Directory.GetCurrentDirectory(), "tmp_data"); WorkingDirectory = Path.Combine(Directory.GetCurrentDirectory(), "tmp_data");
} }
public string WorkingDirectory { get;} public AbsolutePath WorkingDirectory { get;}
public string ID { get; } public string ID { get; }
public Random RNG => _rng; public Random RNG => _rng;
public Game Game { get; set; } public Game Game { get; set; }
public string TestFolder => Path.Combine(WorkingDirectory, ID); public AbsolutePath TestFolder => WorkingDirectory.Combine(ID);
public string GameFolder => Path.Combine(WorkingDirectory, ID, "game_folder"); public AbsolutePath GameFolder => WorkingDirectory.Combine(ID, "game_folder");
public string MO2Folder => Path.Combine(WorkingDirectory, ID, "mo2_folder"); public AbsolutePath MO2Folder => WorkingDirectory.Combine(ID, "mo2_folder");
public string ModsFolder => Path.Combine(MO2Folder, Consts.MO2ModFolderName); public AbsolutePath ModsFolder => MO2Folder.Combine(Consts.MO2ModFolderName);
public string DownloadsFolder => Path.Combine(MO2Folder, "downloads"); public AbsolutePath DownloadsFolder => MO2Folder.Combine("downloads");
public string InstallFolder => Path.Combine(TestFolder, "installed"); public AbsolutePath InstallFolder => TestFolder.Combine("installed");
public HashSet<string> Profiles = new HashSet<string>(); public HashSet<string> Profiles = new HashSet<string>();
public List<string> Mods = new List<string>(); public List<string> Mods = new List<string>();
public void Configure() public async Task Configure()
{ {
File.WriteAllLines(Path.Combine(MO2Folder, "ModOrganizer.ini"), new [] await MO2Folder.Combine("ModOrganizer.ini").WriteAllLinesAsync(new []
{ {
"[General]", "[General]",
$"gameName={Game.MetaData().MO2Name}", $"gameName={Game.MetaData().MO2Name}",
$"gamePath={GameFolder.Replace("\\", "\\\\")}", $"gamePath={((string)GameFolder).Replace("\\", "\\\\")}",
$"download_directory={DownloadsFolder}" $"download_directory={DownloadsFolder}"
}); });
Directory.CreateDirectory(DownloadsFolder); DownloadsFolder.CreateDirectory();
Directory.CreateDirectory(Path.Combine(GameFolder, "Data")); GameFolder.Combine("Data").CreateDirectory();
Profiles.Do(profile => Profiles.Do(profile =>
{ {
@ -260,9 +261,6 @@ namespace Wabbajack.Test
return full_path; return full_path;
} }
public static object RandomeOne(params object[] opts)
{
return opts[_rng.Next(0, opts.Length)];
}
} }
} }

View File

@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<Platforms>x64</Platforms> <Platforms>x64</Platforms>
<RuntimeIdentifier>win10-x64</RuntimeIdentifier> <RuntimeIdentifier>win10-x64</RuntimeIdentifier>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
@ -26,7 +26,6 @@
<ProjectReference Include="..\Wabbajack.Common.CSP\Wabbajack.Common.CSP.csproj" /> <ProjectReference Include="..\Wabbajack.Common.CSP\Wabbajack.Common.CSP.csproj" />
<ProjectReference Include="..\Wabbajack.Common\Wabbajack.Common.csproj" /> <ProjectReference Include="..\Wabbajack.Common\Wabbajack.Common.csproj" />
<ProjectReference Include="..\Wabbajack.Lib\Wabbajack.Lib.csproj" /> <ProjectReference Include="..\Wabbajack.Lib\Wabbajack.Lib.csproj" />
<ProjectReference Include="..\Wabbajack\Wabbajack.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -8,9 +8,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.console" Version="2.4.1" /> <PackageReference Include="xunit.runner.console" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -38,6 +38,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wabbajack.CLI", "Wabbajack.
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wabbajack.Launcher", "Wabbajack.Launcher\Wabbajack.Launcher.csproj", "{D6856DBF-C959-4867-A8A8-343DA2D2715E}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wabbajack.Launcher", "Wabbajack.Launcher\Wabbajack.Launcher.csproj", "{D6856DBF-C959-4867-A8A8-343DA2D2715E}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wabbajack.Common.Test", "Wabbajack.Common.Test\Wabbajack.Common.Test.csproj", "{BA8A3E49-60D2-4BA2-B285-CB09FFDB6D32}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU 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|Any CPU.ActiveCfg = Release|x64
{5D6A2EAF-6604-4C51-8AE2-A746B4BC5E3E}.Release|x64.ActiveCfg = Release|x64 {5D6A2EAF-6604-4C51-8AE2-A746B4BC5E3E}.Release|x64.ActiveCfg = Release|x64
{5D6A2EAF-6604-4C51-8AE2-A746B4BC5E3E}.Release|x64.Build.0 = 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.ActiveCfg = Debug|Any CPU
{DE18D89E-39C5-48FD-8E42-16235E3C4593}.Debug|Any CPU.Build.0 = 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 {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|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.ActiveCfg = Release|Any CPU
{D6856DBF-C959-4867-A8A8-343DA2D2715E}.Release|x64.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE