2019-11-11 06:15:52 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
2019-12-04 01:26:26 +00:00
|
|
|
|
using System.Threading.Tasks;
|
2019-11-11 06:15:52 +00:00
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
using Wabbajack.Common;
|
|
|
|
|
using Wabbajack.Lib.Downloaders;
|
|
|
|
|
using Wabbajack.Lib.NexusApi;
|
2020-03-05 00:02:16 +00:00
|
|
|
|
using Wabbajack.VirtualFileSystem;
|
2020-03-26 13:01:42 +00:00
|
|
|
|
using Xunit;
|
|
|
|
|
using Xunit.Abstractions;
|
2019-11-11 06:15:52 +00:00
|
|
|
|
|
|
|
|
|
namespace Compression.BSA.Test
|
|
|
|
|
{
|
2020-03-31 22:05:36 +00:00
|
|
|
|
public class BSATests : IAsyncLifetime
|
2019-11-11 06:15:52 +00:00
|
|
|
|
{
|
2020-03-26 04:25:48 +00:00
|
|
|
|
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();
|
2020-03-31 22:05:36 +00:00
|
|
|
|
private IDisposable _unsub;
|
2019-11-11 06:15:52 +00:00
|
|
|
|
|
2020-03-26 13:01:42 +00:00
|
|
|
|
public ITestOutputHelper TestContext { get; }
|
2019-11-11 06:15:52 +00:00
|
|
|
|
|
2019-11-17 04:16:42 +00:00
|
|
|
|
private static WorkQueue Queue { get; set; }
|
|
|
|
|
|
2020-03-26 13:01:42 +00:00
|
|
|
|
public BSATests(ITestOutputHelper helper)
|
2019-11-11 06:15:52 +00:00
|
|
|
|
{
|
2020-03-26 13:01:42 +00:00
|
|
|
|
TestContext = helper;
|
2020-03-31 22:05:36 +00:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public async Task InitializeAsync()
|
|
|
|
|
{
|
2019-11-17 04:16:42 +00:00
|
|
|
|
Queue = new WorkQueue();
|
2020-03-31 22:05:36 +00:00
|
|
|
|
_unsub = Utils.LogMessages.Subscribe(f => TestContext.WriteLine(f.ShortDescription));
|
2020-03-29 20:59:22 +00:00
|
|
|
|
_stagingFolder.CreateDirectory();
|
2020-03-31 22:05:36 +00:00
|
|
|
|
await _bsaFolder.DeleteDirectory();
|
2020-03-26 13:01:42 +00:00
|
|
|
|
_bsaFolder.CreateDirectory();
|
2019-11-11 06:15:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-31 22:05:36 +00:00
|
|
|
|
public async Task DisposeAsync()
|
|
|
|
|
{
|
|
|
|
|
await _bsaFolder.DeleteDirectory();
|
|
|
|
|
Queue.Dispose();
|
|
|
|
|
_unsub.Dispose();
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-26 13:01:42 +00:00
|
|
|
|
private static async Task<AbsolutePath> DownloadMod(Game game, int mod)
|
2019-11-11 06:15:52 +00:00
|
|
|
|
{
|
2021-01-06 13:12:16 +00:00
|
|
|
|
var client = DownloadDispatcher.GetInstance<NexusDownloader>();
|
|
|
|
|
await client.Prepare();
|
|
|
|
|
var results = await client.Client!.GetModFiles(game, mod);
|
2020-03-05 00:02:16 +00:00
|
|
|
|
var file = results.files.FirstOrDefault(f => f.is_primary) ??
|
|
|
|
|
results.files.OrderByDescending(f => f.uploaded_timestamp).First();
|
2020-03-26 04:25:48 +00:00
|
|
|
|
var src = _stagingFolder.Combine(file.file_name);
|
2019-11-12 04:35:07 +00:00
|
|
|
|
|
2020-03-26 04:25:48 +00:00
|
|
|
|
if (src.Exists) return src;
|
2019-11-12 04:35:07 +00:00
|
|
|
|
|
2020-03-05 00:02:16 +00:00
|
|
|
|
var state = new NexusDownloader.State
|
|
|
|
|
{
|
2020-04-03 03:57:59 +00:00
|
|
|
|
ModID = mod,
|
2020-03-30 22:26:34 +00:00
|
|
|
|
Game = game,
|
2020-04-03 03:57:59 +00:00
|
|
|
|
FileID = file.file_id
|
2020-03-05 00:02:16 +00:00
|
|
|
|
};
|
|
|
|
|
await state.Download(src);
|
|
|
|
|
return src;
|
2019-11-11 06:15:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-26 13:01:42 +00:00
|
|
|
|
[Theory]
|
2021-01-29 13:14:19 +00:00
|
|
|
|
//[InlineData(Game.SkyrimSpecialEdition, 29194)] // 3D NPCS This fails not sure why
|
2020-03-26 13:01:42 +00:00
|
|
|
|
[InlineData(Game.SkyrimSpecialEdition, 12604)] // SkyUI
|
|
|
|
|
[InlineData(Game.Skyrim, 3863)] // SkyUI
|
|
|
|
|
[InlineData(Game.Skyrim, 51473)] // INeed
|
2020-08-25 01:34:57 +00:00
|
|
|
|
[InlineData(Game.Skyrim, 41634)] // DVA
|
2020-03-26 13:01:42 +00:00
|
|
|
|
[InlineData(Game.Fallout4, 22223)] // 10mm SMG
|
|
|
|
|
[InlineData(Game.Fallout4, 4472)] // True Storms
|
|
|
|
|
[InlineData(Game.Morrowind, 44537)]
|
2020-05-02 20:15:36 +00:00
|
|
|
|
[InlineData(Game.Fallout4, 43474)] // EM 2 Rifle
|
2020-03-26 13:01:42 +00:00
|
|
|
|
public async Task BSACompressionRecompression(Game game, int modid)
|
2019-11-11 06:15:52 +00:00
|
|
|
|
{
|
2020-03-26 13:01:42 +00:00
|
|
|
|
var filename = await DownloadMod(game, modid);
|
|
|
|
|
var folder = _bsaFolder.Combine(game.ToString(), modid.ToString());
|
2020-03-28 04:33:26 +00:00
|
|
|
|
await folder.DeleteDirectory();
|
2020-03-26 13:01:42 +00:00
|
|
|
|
folder.CreateDirectory();
|
2020-10-10 03:02:58 +00:00
|
|
|
|
await FileExtractor2.ExtractAll(Queue, filename, folder);
|
2019-11-11 06:15:52 +00:00
|
|
|
|
|
2020-03-26 13:01:42 +00:00
|
|
|
|
foreach (var bsa in folder.EnumerateFiles().Where(f => Consts.SupportedBSAs.Contains(f.Extension)))
|
2019-11-11 06:15:52 +00:00
|
|
|
|
{
|
2020-03-26 13:01:42 +00:00
|
|
|
|
TestContext.WriteLine($"From {bsa}");
|
|
|
|
|
TestContext.WriteLine("Cleaning Output Dir");
|
2020-03-28 04:33:26 +00:00
|
|
|
|
await _tempDir.DeleteDirectory();
|
2020-03-26 13:01:42 +00:00
|
|
|
|
_tempDir.CreateDirectory();
|
|
|
|
|
|
|
|
|
|
TestContext.WriteLine($"Reading {bsa}");
|
2020-06-27 02:48:33 +00:00
|
|
|
|
await using var tempFolder = await TempFolder.Create();
|
|
|
|
|
var tempFile = tempFolder.Dir.Combine("test.bsa");
|
2020-03-26 13:01:42 +00:00
|
|
|
|
var size = bsa.Size;
|
|
|
|
|
|
2020-06-26 19:01:10 +00:00
|
|
|
|
var a = await BSADispatch.OpenRead(bsa);
|
2020-05-25 17:34:25 +00:00
|
|
|
|
await a.Files.PMap(Queue, async file =>
|
2019-11-11 06:15:52 +00:00
|
|
|
|
{
|
2020-03-26 04:25:48 +00:00
|
|
|
|
var absName = _tempDir.Combine(file.Path);
|
2019-11-11 06:15:52 +00:00
|
|
|
|
ViaJson(file.State);
|
|
|
|
|
|
2020-03-26 04:25:48 +00:00
|
|
|
|
absName.Parent.CreateDirectory();
|
2020-05-25 17:34:25 +00:00
|
|
|
|
await using (var fs = await absName.Create())
|
2019-11-11 06:15:52 +00:00
|
|
|
|
{
|
2020-05-25 17:34:25 +00:00
|
|
|
|
await file.CopyDataTo(fs);
|
2019-11-11 06:15:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-26 13:01:42 +00:00
|
|
|
|
Assert.Equal(file.Size, absName.Size);
|
2019-11-11 06:15:52 +00:00
|
|
|
|
});
|
2020-05-02 20:15:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check Files should be case insensitive
|
|
|
|
|
Assert.Equal(a.Files.Count(), a.Files.Select(f => f.Path).ToHashSet().Count);
|
|
|
|
|
Assert.Equal(a.Files.Count(), a.Files.Select(f => f.Path.ToString().ToLowerInvariant()).ToHashSet().Count);
|
2019-11-11 06:15:52 +00:00
|
|
|
|
|
2020-03-26 13:01:42 +00:00
|
|
|
|
TestContext.WriteLine($"Building {bsa}");
|
2019-11-11 06:15:52 +00:00
|
|
|
|
|
2020-07-20 01:19:56 +00:00
|
|
|
|
await using (var w = await ViaJson(a.State).MakeBuilder(size))
|
2019-11-11 06:15:52 +00:00
|
|
|
|
{
|
2020-04-20 21:36:33 +00:00
|
|
|
|
var streams = await a.Files.PMap(Queue, async file =>
|
2019-11-11 06:15:52 +00:00
|
|
|
|
{
|
2020-03-26 04:25:48 +00:00
|
|
|
|
var absPath = _tempDir.Combine(file.Path);
|
2020-05-25 17:34:25 +00:00
|
|
|
|
var str = await absPath.OpenRead();
|
2020-04-20 21:36:33 +00:00
|
|
|
|
await w.AddFile(ViaJson(file.State), str);
|
2020-03-05 00:02:16 +00:00
|
|
|
|
return str;
|
2019-11-11 06:15:52 +00:00
|
|
|
|
});
|
2020-04-20 21:36:33 +00:00
|
|
|
|
await w.Build(tempFile);
|
2020-03-05 00:02:16 +00:00
|
|
|
|
streams.Do(s => s.Dispose());
|
2019-11-11 06:15:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-26 13:01:42 +00:00
|
|
|
|
TestContext.WriteLine($"Verifying {bsa}");
|
2020-06-26 19:01:10 +00:00
|
|
|
|
var b = await BSADispatch.OpenRead(tempFile);
|
2020-03-26 13:01:42 +00:00
|
|
|
|
TestContext.WriteLine($"Performing A/B tests on {bsa}");
|
2020-04-06 20:48:54 +00:00
|
|
|
|
Assert.Equal(a.State.ToJson(), b.State.ToJson());
|
2019-11-11 06:15:52 +00:00
|
|
|
|
|
2020-03-26 13:01:42 +00:00
|
|
|
|
// Check same number of files
|
|
|
|
|
Assert.Equal(a.Files.Count(), b.Files.Count());
|
2020-05-02 20:15:36 +00:00
|
|
|
|
|
2020-03-26 13:01:42 +00:00
|
|
|
|
|
|
|
|
|
await a.Files.Zip(b.Files, (ai, bi) => (ai, bi))
|
2020-05-26 04:17:23 +00:00
|
|
|
|
.PMap(Queue, async pair =>
|
2020-03-26 13:01:42 +00:00
|
|
|
|
{
|
2020-04-06 20:48:54 +00:00
|
|
|
|
Assert.Equal(pair.ai.State.ToJson(), pair.bi.State.ToJson());
|
2020-03-26 13:01:42 +00:00
|
|
|
|
//Console.WriteLine($" - {pair.ai.Path}");
|
|
|
|
|
Assert.Equal(pair.ai.Path, pair.bi.Path);
|
|
|
|
|
//Equal(pair.ai.Compressed, pair.bi.Compressed);
|
|
|
|
|
Assert.Equal(pair.ai.Size, pair.bi.Size);
|
2021-01-29 13:14:19 +00:00
|
|
|
|
Utils.Log($"Comparing {pair.ai.Path} to {pair.bi.Path}");
|
2020-05-26 04:17:23 +00:00
|
|
|
|
Assert.Equal(await GetData(pair.ai), await GetData(pair.bi));
|
2020-03-26 13:01:42 +00:00
|
|
|
|
});
|
2019-11-11 06:15:52 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-26 04:17:23 +00:00
|
|
|
|
private static async ValueTask<byte[]> GetData(IFile pairAi)
|
2019-11-11 06:15:52 +00:00
|
|
|
|
{
|
2020-05-26 04:17:23 +00:00
|
|
|
|
await using var ms = new MemoryStream();
|
|
|
|
|
await pairAi.CopyDataTo(ms);
|
2020-03-26 13:01:42 +00:00
|
|
|
|
return ms.ToArray();
|
2019-11-11 06:15:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-26 13:01:42 +00:00
|
|
|
|
private static T ViaJson<T>(T i)
|
2019-11-11 06:15:52 +00:00
|
|
|
|
{
|
2020-04-06 20:48:54 +00:00
|
|
|
|
return i.ToJson().FromJsonString<T>();
|
2019-11-11 06:15:52 +00:00
|
|
|
|
}
|
2020-03-31 22:05:36 +00:00
|
|
|
|
|
2019-11-11 06:15:52 +00:00
|
|
|
|
}
|
|
|
|
|
}
|