using System; using System.IO; using Xunit; using XWorld.Server.Host; namespace XWorld.Server.Host.Tests { // 字节加载去锁:模块加载期间磁盘 DLL 可被覆盖/删除(同版本覆盖重发的前提) public class ModuleFileLockTests { // 把共享只读夹具 rps/1 拷到独立临时 gamesRoot(不许改 TestGames.Root 原件) private static string CopyRps1ToTempRoot() { string root = Path.Combine(Path.GetTempPath(), "games_" + Guid.NewGuid().ToString("N")); string dst = Path.Combine(root, "rps", "1"); Directory.CreateDirectory(dst); string src = Path.Combine(TestGames.Root, "rps", "1"); foreach (var f in Directory.GetFiles(src)) File.Copy(f, Path.Combine(dst, Path.GetFileName(f))); return root; } [Fact] public void LoadedModule_DoesNotLockDlls_OverwriteAndDeleteSucceed() { string root = CopyRps1ToTempRoot(); var module = new GameModuleLoader().Load(root, "rps", 1); try { // 先构造一次房间:RpsServerRoom 字段初始化器 new RpsLogic() 触发 RPS.Core // 首次解析、经 GameModuleAlc 字节加载进本 ALC —— 此后两个 DLL 的 IL 都在内存。 // (不先触发的话,"删 Core 后 CreateRoom 成功"会被测试工程对 RPS.Core 的 // ProjectReference 经默认上下文回退兜住,测不到被测代码。) Assert.NotNull(module.CreateRoom()); string serverDll = Path.Combine(root, "rps", "1", "RPS.Server.dll"); string coreDll = Path.Combine(root, "rps", "1", "RPS.Core.dll"); // 加载期间覆盖写入 —— LoadFromAssemblyPath 会锁文件抛 IOException,字节加载应成功 byte[] original = File.ReadAllBytes(serverDll); File.WriteAllBytes(serverDll, original); // 加载期间删除(两个都删:IL 已在内存,源文件不再被需要也不被锁定) File.Delete(coreDll); File.Delete(serverDll); Assert.False(File.Exists(coreDll)); Assert.False(File.Exists(serverDll)); // 模块本体仍可用 Assert.NotNull(module.CreateRoom()); } finally { module.Unload(); try { Directory.Delete(root, true); } catch { } } } } }