XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Security.Cryptography;
using System.Text.Json;
using XFEExtension.NetCore.XUnit.Attributes;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Plugins;
namespace XFEEDToolBox.Test;

[TestFixture]
public class PluginTests
{
    [Test] public async Task HandshakeRetriesDroppedLegacyReplyAndKeepsCapabilitiesFalse()
    {
        var id=Random.Shared.Next(10000000,19000000);using var timeout=new CancellationTokenSource(TimeSpan.FromSeconds(5));
        var server=Task.Run(async()=>
        {
            for(var attempt=0;attempt<3;attempt++)
            {
                await using var pipe=new System.IO.Pipes.NamedPipeServerStream($"XFEEDToolBox.Plugin.{id}",System.IO.Pipes.PipeDirection.InOut,1,System.IO.Pipes.PipeTransmissionMode.Byte,System.IO.Pipes.PipeOptions.Asynchronous);
                await pipe.WaitForConnectionAsync(timeout.Token);var buffer=new byte[128];await pipe.ReadAsync(buffer,timeout.Token);
                if(attempt<2)continue;
                await pipe.WriteAsync(System.Text.Encoding.UTF8.GetBytes($"{{\"schema\":1,\"name\":\"XFEEDToolBox.Plugin\",\"processId\":{id},\"loaded\":true,\"active\":true}}\n"),timeout.Token);
                pipe.WaitForPipeDrain();
            }
        },timeout.Token);
        var result=await GamePluginManager.ProbeAsync(id,token:timeout.Token);await server;
        Check.That(result.Loaded&&result.Active&&!result.SupportsNavigation&&!result.SupportsTrade&&!result.SupportsRelog);
    }
    private sealed class Fixture:IDisposable
    {
        public readonly TemporaryDirectory Directory=new();public string Game{get;}public string Loader{get;}public GamePluginManager Manager{get;}
        public Fixture()
        {
            Game=Directory.File("EliteDangerous64.exe");Loader=Directory.File("dinput8.dll");File.WriteAllText(Game,"owned test game image");
            var package=Directory.File("package");System.IO.Directory.CreateDirectory(package);var dll=Path.Combine(package,"dinput8.dll");File.WriteAllText(dll,"owned test plugin");
            var p=new PluginPackage{Id=GamePluginManager.PluginId,Version="test",Loader="dinput8.dll",Sha256=Hash(dll),SupportedGameSha256=Hash(Game)};
            File.WriteAllText(Path.Combine(package,"package.json"),JsonSerializer.Serialize(p,ToolboxSettings.JsonOptions));Manager=new(package);
        }
        public void Dispose()=>Directory.Dispose();
        private static string Hash(string path)=>Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(path)));
    }
    private static async Task Fails(Func<Task> action){try{await action();}catch(InvalidOperationException){return;}throw new InvalidOperationException("Expected refusal");}
    [Test] public async Task InstallAndUninstallPreserveChineseComponentAndGameImage()
    {
        using var f=new Fixture();var chinese=f.Directory.File("dbghelp.dll");File.WriteAllText(chinese,"existing Chinese component");var before=File.ReadAllText(f.Game);
        Check.That((await f.Manager.InstallAsync(f.Game)).Installed);Check.That((await f.Manager.UninstallAsync(f.Game)).Installed==false);
        Check.Equal("existing Chinese component",File.ReadAllText(chinese));Check.Equal(before,File.ReadAllText(f.Game));Check.That(!File.Exists(f.Loader));
    }
    [Test] public async Task ExistingThirdPartyLoaderIsNeverOverwritten()
    {
        using var f=new Fixture();File.WriteAllText(f.Loader,"other companion");await Fails(()=>f.Manager.InstallAsync(f.Game));Check.Equal("other companion",File.ReadAllText(f.Loader));
    }
    [Test] public async Task OwnedPluginCanUpdateAndLockedUpdatePreservesInstallation()
    {
        using var f=new Fixture();await f.Manager.InstallAsync(f.Game);
        var package=f.Directory.File("package");var dll=Path.Combine(package,"dinput8.dll");
        File.WriteAllText(dll,"updated owned test plugin");
        var data=JsonSerializer.Deserialize<PluginPackage>(File.ReadAllText(Path.Combine(package,"package.json")),ToolboxSettings.JsonOptions)!;
        data=data with{Version="next",Sha256=Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(dll)))};
        File.WriteAllText(Path.Combine(package,"package.json"),JsonSerializer.Serialize(data,ToolboxSettings.JsonOptions));
        using(var held=new FileStream(f.Loader,FileMode.Open,FileAccess.Read,FileShare.ReadWrite))
        {await Fails(()=>f.Manager.InstallAsync(f.Game));Check.Equal("owned test plugin",File.ReadAllText(f.Loader));Check.Equal("test",(await f.Manager.InspectAsync(f.Game)).Installation!.Version);}
        var updated=await f.Manager.InstallAsync(f.Game);Check.Equal("next",updated.Installation!.Version);Check.Equal("updated owned test plugin",File.ReadAllText(f.Loader));
        Check.Equal("owned test game image",File.ReadAllText(f.Game));
    }
    [Test] public async Task ModifiedOwnedFileIsPreservedOnUninstall()
    {
        using var f=new Fixture();await f.Manager.InstallAsync(f.Game);File.WriteAllText(f.Loader,"changed by user");await Fails(()=>f.Manager.UninstallAsync(f.Game));Check.Equal("changed by user",File.ReadAllText(f.Loader));
    }
    [Test] public async Task VerifiedGameUpdateRefreshesInstallationHashWithEitherChangedOrUnchangedLoader()
    {
        using var f=new Fixture();await f.Manager.InstallAsync(f.Game);
        var package=f.Directory.File("package");var manifest=Path.Combine(package,"package.json");var dll=Path.Combine(package,"dinput8.dll");
        for(var update=0;update<2;update++)
        {
            File.WriteAllText(f.Game,"updated game build "+update);
            if(update==0)File.WriteAllText(dll,"new compatible loader");
            var data=JsonSerializer.Deserialize<PluginPackage>(File.ReadAllText(manifest),ToolboxSettings.JsonOptions)!;
            data=data with{Version="new-build-"+update,SupportedGameSha256=Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(f.Game))),Sha256=Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(dll)))};
            File.WriteAllText(manifest,JsonSerializer.Serialize(data,ToolboxSettings.JsonOptions));
            var actual=await f.Manager.InstallAsync(f.Game);
            Check.Equal(data.SupportedGameSha256,actual.Installation!.GameSha256);Check.Equal(data.Version,actual.Installation.Version);
            Check.Equal(data.Sha256,actual.Installation.LoaderSha256);Check.That(actual.Installed);
        }
    }
    [Test] public async Task OwnedUpdateRetriesTheShortMappedFileLockAfterProcessExit()
    {
        using var f=new Fixture();await f.Manager.InstallAsync(f.Game);
        var package=f.Directory.File("package");var dll=Path.Combine(package,"dinput8.dll");File.WriteAllText(dll,"replacement after exit");
        var data=JsonSerializer.Deserialize<PluginPackage>(File.ReadAllText(Path.Combine(package,"package.json")),ToolboxSettings.JsonOptions)!;
        data=data with{Version="after-exit",Sha256=Convert.ToHexStringLower(SHA256.HashData(File.ReadAllBytes(dll)))};
        File.WriteAllText(Path.Combine(package,"package.json"),JsonSerializer.Serialize(data,ToolboxSettings.JsonOptions));
        var held=new FileStream(f.Loader,FileMode.Open,FileAccess.Read,FileShare.ReadWrite);
        var release=Task.Run(async()=>{await Task.Delay(400);held.Dispose();});
        try
        {
            var updated=await f.Manager.InstallAsync(f.Game);
            Check.Equal("after-exit",updated.Installation!.Version);Check.Equal("replacement after exit",File.ReadAllText(f.Loader));
            Check.That(!File.Exists(f.Directory.File(".xfeedtoolbox-plugin/loader.previous")));
        }
        finally{held.Dispose();await release;}
    }
    [Test] public async Task LoadedFileCanBeDisabledAndCleanupCompletedLater()
    {
        using var f=new Fixture();await f.Manager.InstallAsync(f.Game);
        using(var held=new FileStream(f.Loader,FileMode.Open,FileAccess.Read,FileShare.ReadWrite))
        {var state=await f.Manager.UninstallAsync(f.Game);Check.That(state.PendingRemoval);Check.That(File.Exists(f.Directory.File(".xfeedtoolbox-plugin/disabled")));}
        var cleaned=await f.Manager.UninstallAsync(f.Game);Check.That(!cleaned.Installed&&!cleaned.PendingRemoval);
    }
}