2019-11-14 22:22:53 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO.Compression;
|
|
|
|
|
using System.Linq;
|
2020-04-17 03:52:19 +00:00
|
|
|
|
using System.Text;
|
2019-11-14 22:22:53 +00:00
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using Wabbajack.Common;
|
2020-11-02 04:06:18 +00:00
|
|
|
|
using Wabbajack.Lib;
|
|
|
|
|
using Wabbajack.Lib.Downloaders;
|
2020-03-24 12:21:19 +00:00
|
|
|
|
using Xunit;
|
|
|
|
|
using Xunit.Abstractions;
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
|
|
|
|
namespace Wabbajack.VirtualFileSystem.Test
|
|
|
|
|
{
|
2020-04-11 19:00:52 +00:00
|
|
|
|
public class VFSTests : IAsyncLifetime
|
2019-11-14 22:22:53 +00:00
|
|
|
|
{
|
2020-03-24 02:46:30 +00:00
|
|
|
|
private static readonly AbsolutePath VFS_TEST_DIR = "vfs_test_dir".ToPath().RelativeToEntryPoint();
|
|
|
|
|
private static readonly AbsolutePath TEST_ZIP = "test.zip".RelativeTo(VFS_TEST_DIR);
|
|
|
|
|
private static readonly AbsolutePath TEST_TXT = "test.txt".RelativeTo(VFS_TEST_DIR);
|
2020-03-24 21:42:28 +00:00
|
|
|
|
private static readonly AbsolutePath ARCHIVE_TEST_TXT = "archive/test.txt".RelativeTo(VFS_TEST_DIR);
|
2019-11-14 22:22:53 +00:00
|
|
|
|
private Context context;
|
|
|
|
|
|
2020-03-24 12:21:19 +00:00
|
|
|
|
private readonly ITestOutputHelper _helper;
|
2020-09-09 11:28:34 +00:00
|
|
|
|
private IDisposable _unsub;
|
2020-04-11 19:00:52 +00:00
|
|
|
|
private WorkQueue Queue { get; } = new WorkQueue();
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2020-04-11 04:22:10 +00:00
|
|
|
|
public VFSTests(ITestOutputHelper helper)
|
2019-11-14 22:22:53 +00:00
|
|
|
|
{
|
2020-03-24 12:21:19 +00:00
|
|
|
|
_helper = helper;
|
2020-09-09 11:28:34 +00:00
|
|
|
|
_unsub = Utils.LogMessages.Subscribe(f =>
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
_helper.WriteLine(f.ShortDescription);
|
|
|
|
|
}
|
2020-10-01 03:50:09 +00:00
|
|
|
|
catch (Exception)
|
2020-09-09 11:28:34 +00:00
|
|
|
|
{
|
|
|
|
|
// ignored
|
|
|
|
|
}
|
|
|
|
|
});
|
2019-11-17 04:16:42 +00:00
|
|
|
|
context = new Context(Queue);
|
2019-11-14 22:22:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-11 19:00:52 +00:00
|
|
|
|
public async Task InitializeAsync()
|
2020-04-10 19:35:47 +00:00
|
|
|
|
{
|
|
|
|
|
await VFS_TEST_DIR.DeleteDirectory();
|
|
|
|
|
VFS_TEST_DIR.CreateDirectory();
|
2020-04-11 19:00:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task DisposeAsync()
|
|
|
|
|
{
|
2020-09-09 11:28:34 +00:00
|
|
|
|
_unsub.Dispose();
|
2020-04-11 19:00:52 +00:00
|
|
|
|
await VFS_TEST_DIR.DeleteDirectory();
|
2020-04-10 19:35:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-24 12:21:19 +00:00
|
|
|
|
[Fact]
|
2019-12-04 01:26:26 +00:00
|
|
|
|
public async Task FilesAreIndexed()
|
2019-11-14 22:22:53 +00:00
|
|
|
|
{
|
2020-03-24 02:46:30 +00:00
|
|
|
|
await AddFile(TEST_TXT, "This is a test");
|
2019-12-04 01:26:26 +00:00
|
|
|
|
await AddTestRoot();
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2020-03-23 23:03:26 +00:00
|
|
|
|
var file = context.Index.ByRootPath["test.txt".ToPath().RelativeTo(VFS_TEST_DIR)];
|
2020-03-24 12:21:19 +00:00
|
|
|
|
Assert.NotNull(file);
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2020-03-24 12:21:19 +00:00
|
|
|
|
Assert.Equal(14, file.Size);
|
|
|
|
|
Assert.Equal(file.Hash, Hash.FromBase64("qX0GZvIaTKM="));
|
2019-11-14 22:22:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-24 12:21:19 +00:00
|
|
|
|
|
2019-12-04 01:26:26 +00:00
|
|
|
|
private async Task AddTestRoot()
|
2019-11-14 22:22:53 +00:00
|
|
|
|
{
|
2020-03-23 23:03:26 +00:00
|
|
|
|
await context.AddRoot(VFS_TEST_DIR);
|
|
|
|
|
await context.WriteToFile("vfs_cache.bin".RelativeTo(VFS_TEST_DIR));
|
|
|
|
|
await context.IntegrateFromFile( "vfs_cache.bin".RelativeTo(VFS_TEST_DIR));
|
2019-11-14 22:22:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-24 21:42:28 +00:00
|
|
|
|
[Fact]
|
2019-11-14 22:22:53 +00:00
|
|
|
|
public async Task ArchiveContentsAreIndexed()
|
|
|
|
|
{
|
2020-03-24 02:46:30 +00:00
|
|
|
|
await AddFile(ARCHIVE_TEST_TXT, "This is a test");
|
2020-04-10 19:33:09 +00:00
|
|
|
|
await ZipUpFolder(ARCHIVE_TEST_TXT.Parent, TEST_ZIP);
|
2019-12-04 01:26:26 +00:00
|
|
|
|
await AddTestRoot();
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2020-03-24 02:46:30 +00:00
|
|
|
|
var absPath = "test.zip".RelativeTo(VFS_TEST_DIR);
|
|
|
|
|
var file = context.Index.ByRootPath[absPath];
|
2020-03-24 21:42:28 +00:00
|
|
|
|
Assert.NotNull(file);
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2020-03-24 21:42:28 +00:00
|
|
|
|
Assert.Equal(128, file.Size);
|
2020-05-25 16:30:47 +00:00
|
|
|
|
Assert.Equal(await absPath.FileHashAsync(), file.Hash);
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2020-03-24 21:42:28 +00:00
|
|
|
|
Assert.True(file.IsArchive);
|
2020-03-23 23:03:26 +00:00
|
|
|
|
var innerFile = file.Children.First();
|
2020-03-24 21:42:28 +00:00
|
|
|
|
Assert.Equal(14, innerFile.Size);
|
|
|
|
|
Assert.Equal(Hash.FromBase64("qX0GZvIaTKM="), innerFile.Hash);
|
|
|
|
|
Assert.Same(file, file.Children.First().Parent);
|
2019-11-14 22:22:53 +00:00
|
|
|
|
}
|
2020-03-24 21:42:28 +00:00
|
|
|
|
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2020-03-24 21:42:28 +00:00
|
|
|
|
[Fact]
|
2019-11-14 22:22:53 +00:00
|
|
|
|
public async Task DuplicateFileHashes()
|
|
|
|
|
{
|
2020-03-24 02:46:30 +00:00
|
|
|
|
await AddFile(ARCHIVE_TEST_TXT, "This is a test");
|
2020-04-10 19:33:09 +00:00
|
|
|
|
await ZipUpFolder(ARCHIVE_TEST_TXT.Parent, TEST_ZIP);
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2020-03-24 02:46:30 +00:00
|
|
|
|
await AddFile(TEST_TXT, "This is a test");
|
2019-12-04 01:26:26 +00:00
|
|
|
|
await AddTestRoot();
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
|
|
|
|
|
2020-03-22 15:50:53 +00:00
|
|
|
|
var files = context.Index.ByHash[Hash.FromBase64("qX0GZvIaTKM=")];
|
2020-03-24 21:42:28 +00:00
|
|
|
|
Assert.Equal(2, files.Count());
|
2019-11-14 22:22:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-24 21:42:28 +00:00
|
|
|
|
[Fact]
|
2019-11-14 22:22:53 +00:00
|
|
|
|
public async Task DeletedFilesAreRemoved()
|
|
|
|
|
{
|
2020-03-24 02:46:30 +00:00
|
|
|
|
await AddFile(TEST_TXT, "This is a test");
|
2019-12-04 01:26:26 +00:00
|
|
|
|
await AddTestRoot();
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2020-03-24 02:46:30 +00:00
|
|
|
|
var file = context.Index.ByRootPath[TEST_TXT];
|
2020-03-24 21:42:28 +00:00
|
|
|
|
Assert.NotNull(file);
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2020-03-24 21:42:28 +00:00
|
|
|
|
Assert.Equal(14, file.Size);
|
|
|
|
|
Assert.Equal(Hash.FromBase64("qX0GZvIaTKM="), file.Hash);
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2020-05-26 11:31:11 +00:00
|
|
|
|
await TEST_TXT.DeleteAsync();
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2019-12-04 01:26:26 +00:00
|
|
|
|
await AddTestRoot();
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2020-03-24 21:42:28 +00:00
|
|
|
|
Assert.DoesNotContain(TEST_TXT, context.Index.AllFiles.Select(f => f.AbsoluteName));
|
2019-11-14 22:22:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-24 21:42:28 +00:00
|
|
|
|
[Fact]
|
2019-12-04 01:26:26 +00:00
|
|
|
|
public async Task UnmodifiedFilesAreNotReIndexed()
|
2019-11-14 22:22:53 +00:00
|
|
|
|
{
|
2020-03-24 02:46:30 +00:00
|
|
|
|
await AddFile(TEST_TXT, "This is a test");
|
2019-12-04 01:26:26 +00:00
|
|
|
|
await AddTestRoot();
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2020-03-24 02:46:30 +00:00
|
|
|
|
var old_file = context.Index.ByRootPath[TEST_TXT];
|
2019-11-14 22:22:53 +00:00
|
|
|
|
var old_time = old_file.LastAnalyzed;
|
|
|
|
|
|
2019-12-04 01:26:26 +00:00
|
|
|
|
await AddTestRoot();
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2020-03-24 02:46:30 +00:00
|
|
|
|
var new_file = context.Index.ByRootPath[TEST_TXT];
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2020-03-24 21:42:28 +00:00
|
|
|
|
Assert.Equal(old_time, new_file.LastAnalyzed);
|
2019-11-14 22:22:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-24 21:42:28 +00:00
|
|
|
|
[Fact]
|
2019-12-04 01:26:26 +00:00
|
|
|
|
public async Task CanStageSimpleArchives()
|
2019-11-14 22:22:53 +00:00
|
|
|
|
{
|
2020-03-24 02:46:30 +00:00
|
|
|
|
await AddFile(ARCHIVE_TEST_TXT, "This is a test");
|
2020-04-10 19:33:09 +00:00
|
|
|
|
await ZipUpFolder(ARCHIVE_TEST_TXT.Parent, TEST_ZIP);
|
2019-12-04 01:26:26 +00:00
|
|
|
|
await AddTestRoot();
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2020-03-24 21:42:28 +00:00
|
|
|
|
var res = new FullPath(TEST_ZIP, new[] {(RelativePath)"test.txt"});
|
2020-09-04 21:00:29 +00:00
|
|
|
|
var files = new [] {context.Index.ByFullPath[res]};
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2020-09-04 21:00:29 +00:00
|
|
|
|
var queue = new WorkQueue();
|
|
|
|
|
await context.Extract(queue, files.ToHashSet(), async (file, factory) =>
|
|
|
|
|
{
|
|
|
|
|
await using var s = await factory.GetStream();
|
|
|
|
|
Assert.Equal("This is a test", await s.ReadAllTextAsync());
|
|
|
|
|
});
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-24 21:42:28 +00:00
|
|
|
|
[Fact]
|
2019-12-04 01:26:26 +00:00
|
|
|
|
public async Task CanStageNestedArchives()
|
2019-11-14 22:22:53 +00:00
|
|
|
|
{
|
2020-03-24 02:46:30 +00:00
|
|
|
|
await AddFile(ARCHIVE_TEST_TXT, "This is a test");
|
2020-04-10 19:33:09 +00:00
|
|
|
|
await ZipUpFolder(ARCHIVE_TEST_TXT.Parent, TEST_ZIP);
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2020-03-24 02:46:30 +00:00
|
|
|
|
var inner_dir = @"archive\other\dir".RelativeTo(VFS_TEST_DIR);
|
|
|
|
|
inner_dir.CreateDirectory();
|
2020-05-25 16:30:47 +00:00
|
|
|
|
await TEST_ZIP.MoveToAsync( @"archive\other\dir\nested.zip".RelativeTo(VFS_TEST_DIR));
|
2020-04-10 19:33:09 +00:00
|
|
|
|
await ZipUpFolder(ARCHIVE_TEST_TXT.Parent, TEST_ZIP);
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2019-12-04 01:26:26 +00:00
|
|
|
|
await AddTestRoot();
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2020-03-22 15:50:53 +00:00
|
|
|
|
var files = context.Index.ByHash[Hash.FromBase64("qX0GZvIaTKM=")];
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
2020-09-04 21:00:29 +00:00
|
|
|
|
var queue = new WorkQueue();
|
|
|
|
|
await context.Extract(queue, files.ToHashSet(), async (file, factory) =>
|
2020-04-17 03:52:19 +00:00
|
|
|
|
{
|
2020-09-04 21:00:29 +00:00
|
|
|
|
await using var s = await factory.GetStream();
|
|
|
|
|
Assert.Equal("This is a test", await s.ReadAllTextAsync());
|
|
|
|
|
});
|
2019-11-14 22:22:53 +00:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-02 04:06:18 +00:00
|
|
|
|
|
|
|
|
|
[Theory]
|
|
|
|
|
[InlineData(Game.SkyrimSpecialEdition, 20035, 130759)] // Lucian
|
|
|
|
|
public async Task CanAnalyzeMods(Game game, int modid, int fileId)
|
|
|
|
|
{
|
|
|
|
|
await using var tmpFolder = await TempFolder.Create();
|
|
|
|
|
|
|
|
|
|
var path = await FileExtractorTests.DownloadMod(game, modid, fileId);
|
|
|
|
|
await path.CopyToAsync(path.FileName.RelativeTo(tmpFolder.Dir));
|
|
|
|
|
|
|
|
|
|
var context = new Context(Queue);
|
|
|
|
|
await context.AddRoot(tmpFolder.Dir);
|
|
|
|
|
|
|
|
|
|
Assert.True(context.Index.ByFullPath.Count >= 3);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-24 02:46:30 +00:00
|
|
|
|
private static async Task AddFile(AbsolutePath filename, string text)
|
2019-11-14 22:22:53 +00:00
|
|
|
|
{
|
2020-03-24 02:46:30 +00:00
|
|
|
|
filename.Parent.CreateDirectory();
|
|
|
|
|
await filename.WriteAllTextAsync(text);
|
2019-11-14 22:22:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-10 19:33:09 +00:00
|
|
|
|
private static async Task ZipUpFolder(AbsolutePath folder, AbsolutePath output)
|
2019-11-14 22:22:53 +00:00
|
|
|
|
{
|
2020-03-24 02:46:30 +00:00
|
|
|
|
ZipFile.CreateFromDirectory((string)folder, (string)output);
|
2020-04-10 19:33:09 +00:00
|
|
|
|
await folder.DeleteDirectory();
|
2019-11-14 22:22:53 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-11-23 17:37:24 +00:00
|
|
|
|
}
|