From 025b840625016ff349a881b64c09ccdfef8e4492 Mon Sep 17 00:00:00 2001 From: peelz Date: Thu, 15 Sep 2022 18:09:32 -0400 Subject: [PATCH] Fix test concurrency issues --- .../SharedSource/LuaCs/LuaCsHook.cs | 18 +- .../BarotraumaTest/LuaCs/HookPatchHelpers.cs | 119 ++++++++ .../BarotraumaTest/LuaCs/HookPatchTests.cs | 289 ++++++++---------- .../BarotraumaTest/LuaCs/LuaCsFixture.cs | 31 ++ 4 files changed, 284 insertions(+), 173 deletions(-) create mode 100644 Barotrauma/BarotraumaTest/LuaCs/HookPatchHelpers.cs create mode 100644 Barotrauma/BarotraumaTest/LuaCs/LuaCsFixture.cs diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs index 08729c8f2..e1f3e8c84 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs @@ -91,7 +91,7 @@ namespace Barotrauma } // Copied from https://github.com/evilfactory/moonsharp/blob/5264656c6442e783f3c75082cce69a93d66d4cc0/src/MoonSharp.Interpreter/Interop/Converters/ScriptToClrConversions.cs#L79-L99 - private static MethodInfo HasImplicitConversion(Type baseType, Type targetType) + private static MethodInfo GetImplicitOperatorMethod(Type baseType, Type targetType) { try { @@ -101,12 +101,12 @@ namespace Barotrauma { if (baseType.BaseType != null) { - return HasImplicitConversion(baseType.BaseType, targetType); + return GetImplicitOperatorMethod(baseType.BaseType, targetType); } if (targetType.BaseType != null) { - return HasImplicitConversion(baseType, targetType.BaseType); + return GetImplicitOperatorMethod(baseType, targetType.BaseType); } return null; @@ -141,18 +141,18 @@ namespace Barotrauma il.Call(typeof(object).GetMethod("GetType")); il.StoreLocal(baseType); - // IL: var implicitConversionMethod = SigilExtensions.HasImplicitConversion(baseType, ); - var implicitConversionMethod = il.DeclareLocal(typeof(MethodInfo), $"cast_implicitConversionMethod_{guid}"); + // IL: var implicitOperatorMethod = SigilExtensions.GetImplicitOperatorMethod(baseType, ); + var implicitOperatorMethod = il.DeclareLocal(typeof(MethodInfo), $"cast_implicitOperatorMethod_{guid}"); il.LoadLocal(baseType); il.LoadType(targetType); - il.Call(typeof(SigilExtensions).GetMethod(nameof(HasImplicitConversion), BindingFlags.NonPublic | BindingFlags.Static)); - il.StoreLocal(implicitConversionMethod); + il.Call(typeof(SigilExtensions).GetMethod(nameof(GetImplicitOperatorMethod), BindingFlags.NonPublic | BindingFlags.Static)); + il.StoreLocal(implicitOperatorMethod); // IL: castValue; var castValue = il.DeclareLocal(targetType, $"cast_castValue_{guid}"); // IL: if (implicitConversionMethod != null) - il.LoadLocal(implicitConversionMethod); + il.LoadLocal(implicitOperatorMethod); il.Branch((il) => { // IL: var methodInvokeParams = new object[1]; @@ -168,7 +168,7 @@ namespace Barotrauma il.StoreElement(); // IL: castValue = ()implicitConversionMethod.Invoke(null, methodInvokeParams); - il.LoadLocal(implicitConversionMethod); + il.LoadLocal(implicitOperatorMethod); il.LoadNull(); // first parameter is null because implicit cast operators are static il.LoadLocal(methodInvokeParams); il.Call(typeof(MethodInfo).GetMethod("Invoke", new[] { typeof(object), typeof(object[]) })); diff --git a/Barotrauma/BarotraumaTest/LuaCs/HookPatchHelpers.cs b/Barotrauma/BarotraumaTest/LuaCs/HookPatchHelpers.cs new file mode 100644 index 000000000..c48510345 --- /dev/null +++ b/Barotrauma/BarotraumaTest/LuaCs/HookPatchHelpers.cs @@ -0,0 +1,119 @@ +using Barotrauma; +using MoonSharp.Interpreter; +using System; +using System.Collections.Concurrent; +using System.Threading; +using Xunit; + +namespace TestProject.LuaCs +{ + internal static class HookPatchHelpers + { + public class PatchHandle : IDisposable + { + private readonly Action disposeAction; + + public PatchHandle(string patchId, Action disposeAction) + { + this.disposeAction = disposeAction; + this.PatchId = patchId; + } + + public string PatchId { get; } + + public void Dispose() => disposeAction(); + } + + public static PatchHandle AddPrefix(this LuaCsSetup luaCs, string body, string methodName = "Run", string? patchId = null) + { + var className = typeof(T).FullName; + DynValue returnValue; + if (patchId != null) + { + returnValue = luaCs.Lua.DoString(@$" + return Hook.Patch('{patchId}', '{className}', '{methodName}', function(instance, ptable) + {body} + end, Hook.HookMethodType.Before) + "); + } + else + { + returnValue = luaCs.Lua.DoString(@$" + return Hook.Patch('{className}', '{methodName}', function(instance, ptable) + {body} + end, Hook.HookMethodType.Before) + "); + } + + Assert.Equal(DataType.String, returnValue.Type); + return new(returnValue.String, () => luaCs.RemovePrefix(returnValue.String, methodName)); + } + + public static PatchHandle AddPostfix(this LuaCsSetup luaCs, string body, string methodName = "Run", string? patchId = null) + { + var className = typeof(T).FullName; + DynValue returnValue; + if (patchId != null) + { + returnValue = luaCs.Lua.DoString(@$" + return Hook.Patch('{patchId}', '{className}', '{methodName}', function(instance, ptable) + {body} + end, Hook.HookMethodType.After) + "); + } + else + { + returnValue = luaCs.Lua.DoString(@$" + return Hook.Patch('{className}', '{methodName}', function(instance, ptable) + {body} + end, Hook.HookMethodType.After) + "); + } + return new(returnValue.String, () => luaCs.RemovePostfix(returnValue.String, methodName)); + } + + public static bool RemovePrefix(this LuaCsSetup luaCs, string patchId, string methodName = "Run") + { + var className = typeof(T).FullName; + var returnValue = luaCs.Lua.DoString($@" + return Hook.RemovePatch('{patchId}', '{className}', '{methodName}', Hook.HookMethodType.Before) + "); + Assert.Equal(DataType.Boolean, returnValue.Type); + return returnValue.Boolean; + } + + public static bool RemovePostfix(this LuaCsSetup luaCs, string patchId, string methodName = "Run") + { + var className = typeof(T).FullName; + var returnValue = luaCs.Lua.DoString($@" + return Hook.RemovePatch('{patchId}', '{className}', '{methodName}', Hook.HookMethodType.After) + "); + Assert.Equal(DataType.Boolean, returnValue.Type); + return returnValue.Boolean; + } + + public class PatchTargetHandle : IDisposable + { + private readonly SemaphoreSlim @lock; + + public PatchTargetHandle(SemaphoreSlim @lock) + { + this.@lock = @lock; + } + + public void Dispose() => @lock.Release(); + } + + private static readonly ConcurrentDictionary PatchTargetLocks = new(); + + public static PatchTargetHandle LockPatchTarget() + { + if (!PatchTargetLocks.TryGetValue(typeof(T), out var @lock)) + { + PatchTargetLocks[typeof(T)] = @lock = new SemaphoreSlim(1); + } + @lock.Wait(); + return new(@lock); + } + } +} diff --git a/Barotrauma/BarotraumaTest/LuaCs/HookPatchTests.cs b/Barotrauma/BarotraumaTest/LuaCs/HookPatchTests.cs index 9a27df57c..e215421ac 100644 --- a/Barotrauma/BarotraumaTest/LuaCs/HookPatchTests.cs +++ b/Barotrauma/BarotraumaTest/LuaCs/HookPatchTests.cs @@ -1,104 +1,44 @@ using Barotrauma; using Microsoft.Xna.Framework; using MoonSharp.Interpreter; -using System.Runtime.ExceptionServices; using Xunit; using Xunit.Abstractions; namespace TestProject.LuaCs { - public class HookPatchTests + [Collection("LuaCs")] + public class HookPatchTests : IClassFixture { - private readonly LuaCsSetup luaCs = new(); + private readonly LuaCsSetup luaCs; - public HookPatchTests(ITestOutputHelper output) + public HookPatchTests(LuaCsFixture luaCsFixture, ITestOutputHelper output) { - UserData.RegisterType(); - UserData.RegisterType(); - UserData.RegisterType(); - UserData.RegisterType(); - UserData.RegisterType(); - UserData.RegisterType(); - UserData.RegisterType(); - UserData.RegisterType(); - UserData.RegisterType(); + // XXX: we can't have multiple instances of LuaCs patching the + // same methods, otherwise we get script ownership exceptions. + luaCs = luaCsFixture.LuaCs; luaCs.MessageLogger = (prefix, o) => { o ??= "null"; - output.WriteLine(prefix + o); - }; - luaCs.ExceptionHandler = (ex, _) => - { - // Pretend we never caught the exception in the first place - // (this allows us to preserve the stack trace) - var di = ExceptionDispatchInfo.Capture(ex); - di.Throw(); + output?.WriteLine(prefix + o); }; + + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + luaCs.Initialize(); luaCs.Lua.Globals["TestValueType"] = UserData.CreateStatic(); luaCs.Lua.Globals["InterfaceImplementingType"] = UserData.CreateStatic(); } - private DynValue AddPrefix(string body, string testMethod = "Run", string? patchId = null) - { - var className = typeof(T).FullName; - if (patchId != null) - { - return luaCs.Lua.DoString(@$" - return Hook.Patch('{patchId}', '{className}', '{testMethod}', function(instance, ptable) - {body} - end, Hook.HookMethodType.Before) - "); - } - else - { - return luaCs.Lua.DoString(@$" - return Hook.Patch('{className}', '{testMethod}', function(instance, ptable) - {body} - end, Hook.HookMethodType.Before) - "); - } - } - - private DynValue AddPostfix(string body, string testMethod = "Run", string? patchId = null) - { - var className = typeof(T).FullName; - if (patchId != null) - { - return luaCs.Lua.DoString(@$" - return Hook.Patch('{patchId}', '{className}', '{testMethod}', function(instance, ptable) - {body} - end, Hook.HookMethodType.After) - "); - } - else - { - return luaCs.Lua.DoString(@$" - return Hook.Patch('{className}', '{testMethod}', function(instance, ptable) - {body} - end, Hook.HookMethodType.After) - "); - } - } - - private DynValue RemovePrefix(string patchName, string testMethod = "Run") - { - var className = typeof(T).FullName; - return luaCs.Lua.DoString($@" - return Hook.RemovePatch('{patchName}', '{className}', '{testMethod}', Hook.HookMethodType.Before) - "); - } - - private DynValue RemovePostfix(string patchName, string testMethod = "Run") - { - var className = typeof(T).FullName; - return luaCs.Lua.DoString($@" - return Hook.RemovePatch('{patchName}', '{className}', '{testMethod}', Hook.HookMethodType.After) - "); - } - - public class PatchTarget1 + private class PatchTargetSimple { public bool ran; @@ -111,8 +51,9 @@ namespace TestProject.LuaCs [Fact] public void TestFullMethodReplacement() { - var target = new PatchTarget1(); - AddPrefix("ptable.PreventExecution = true"); + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetSimple(); + using var patchHandle = luaCs.AddPrefix("ptable.PreventExecution = true"); target.Run(); Assert.False(target.ran); } @@ -120,8 +61,9 @@ namespace TestProject.LuaCs [Fact] public void TestOverrideExistingPatch() { - var target = new PatchTarget1(); - AddPrefix(@" + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetSimple(); + using var patchHandle = luaCs.AddPrefix(@" ptable.PreventExecution = true originalPatchRan = true ", patchId: "test"); @@ -134,7 +76,7 @@ namespace TestProject.LuaCs luaCs.Lua.Globals["originalPatchRan"] = false; // Replace the existing prefix, but don't prevent execution this time - AddPrefix("replacementPatchRan = true", patchId: "test"); + luaCs.AddPrefix("replacementPatchRan = true", patchId: "test"); target.Run(); Assert.True(target.ran); @@ -148,19 +90,20 @@ namespace TestProject.LuaCs [Fact] public void TestRemovePrefix() { - var target = new PatchTarget1(); - var patchId = AddPrefix(@" + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetSimple(); + using (var patchHandle = luaCs.AddPrefix(@" ptable.PreventExecution = true patchRan = true - "); - target.Run(); - Assert.False(target.ran); - Assert.True(luaCs.Lua.Globals["patchRan"] as bool?); + ")) + { + target.Run(); + Assert.False(target.ran); + Assert.True(luaCs.Lua.Globals["patchRan"] as bool?); - luaCs.Lua.Globals["patchRan"] = false; + luaCs.Lua.Globals["patchRan"] = false; + } - Assert.Equal(DataType.String, patchId.Type); - RemovePrefix(patchId.String); target.Run(); Assert.True(target.ran); Assert.False(luaCs.Lua.Globals["patchRan"] as bool?); @@ -169,19 +112,20 @@ namespace TestProject.LuaCs [Fact] public void TestRemovePostfix() { - var target = new PatchTarget1(); - var patchId = AddPostfix(@" + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetSimple(); + using (var patchHandle = luaCs.AddPostfix(@" patchRan = true - "); - target.Run(); - Assert.True(target.ran); - Assert.True(luaCs.Lua.Globals["patchRan"] as bool?); + ")) + { + target.Run(); + Assert.True(target.ran); + Assert.True(luaCs.Lua.Globals["patchRan"] as bool?); - target.ran = false; - luaCs.Lua.Globals["patchRan"] = false; + target.ran = false; + luaCs.Lua.Globals["patchRan"] = false; + } - Assert.Equal(DataType.String, patchId.Type); - RemovePostfix(patchId.String); target.Run(); Assert.True(target.ran); Assert.False(luaCs.Lua.Globals["patchRan"] as bool?); @@ -197,7 +141,7 @@ namespace TestProject.LuaCs } } - public class PatchTarget2 + private class PatchTargetReturnsObject { public bool ran; @@ -213,7 +157,7 @@ namespace TestProject.LuaCs int GetFoo(); } - public class InterfaceImplementingType : IBogusInterface + private class InterfaceImplementingType : IBogusInterface { private readonly int foo; @@ -228,8 +172,9 @@ namespace TestProject.LuaCs [Fact] public void TestReturnBoxed() { - var target = new PatchTarget2(); - AddPrefix(@" + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetReturnsObject(); + using var patchHandle = luaCs.AddPrefix(@" ptable.PreventExecution = true return 123 "); @@ -241,9 +186,10 @@ namespace TestProject.LuaCs [Fact] public void TestReturnVoid() { - var target = new PatchTarget2(); + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetReturnsObject(); // This should have no effect - AddPrefix("return"); + using var patchHandle = luaCs.AddPrefix("return"); var returnValue = target.Run(); Assert.True(target.ran); Assert.Equal(5, returnValue); @@ -252,9 +198,10 @@ namespace TestProject.LuaCs [Fact] public void TestReturnNil() { - var target = new PatchTarget2(); + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetReturnsObject(); // This should modify the return value to "null" - AddPostfix("return nil"); + using var patchHandle = luaCs.AddPostfix("return nil"); var returnValue = target.Run(); Assert.True(target.ran); Assert.Null(returnValue); @@ -263,8 +210,9 @@ namespace TestProject.LuaCs [Fact] public void TestReturnValueType() { - var target = new PatchTarget2(); - AddPostfix(@" + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetReturnsObject(); + using var patchHandle = luaCs.AddPostfix(@" return TestValueType.__new(100) "); var returnValue = target.Run(); @@ -273,7 +221,7 @@ namespace TestProject.LuaCs Assert.Equal(100, ((TestValueType)returnValue).foo); } - public class PatchTarget3 + private class PatchTargetReturnsInterface { public bool ran; @@ -287,8 +235,9 @@ namespace TestProject.LuaCs [Fact] public void TestReturnInterfaceImplementingType() { - var target = new PatchTarget3(); - AddPostfix(@" + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetReturnsInterface(); + using var patchHandle = luaCs.AddPostfix(@" return InterfaceImplementingType.__new(100); "); var returnValue = target.Run()!; @@ -296,7 +245,7 @@ namespace TestProject.LuaCs Assert.Equal(100, returnValue.GetFoo()); } - public class PatchTarget4 + private class PatchTargetModifyParams { public bool ran; @@ -310,8 +259,9 @@ namespace TestProject.LuaCs [Fact] public void TestModifyParameters() { - var target = new PatchTarget4(); - AddPrefix(@" + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetModifyParams(); + using var patchHandle = luaCs.AddPrefix(@" ptable['a'] = Int32(100) ptable['b'] = 'abc' ptable['refByte'] = Byte(4) @@ -322,7 +272,7 @@ namespace TestProject.LuaCs Assert.Equal("100abc4", outString); } - public class PatchTarget5 + private class PatchTargetVector2 { public bool ran; @@ -336,15 +286,16 @@ namespace TestProject.LuaCs [Fact] public void TestParameterValueType() { - var target = new PatchTarget5(); - AddPrefix("patchRan = true"); + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetVector2(); + using var patchHandle = luaCs.AddPrefix("patchRan = true"); var returnValue = target.Run(new Vector2(1, 2)); Assert.True(target.ran); Assert.True(luaCs.Lua.Globals["patchRan"] as bool?); Assert.Equal("{X:1 Y:2}", returnValue); } - public class PatchTarget6 + private class PatchTargetNumbers { public bool ran; @@ -410,120 +361,130 @@ namespace TestProject.LuaCs } [Fact] - public void TestCastPrimitiveWrapperSByte() + public void TestCastSByte() { - var target = new PatchTarget6(); - AddPrefix(@" + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetNumbers(); + using var patchHandle = luaCs.AddPrefix(@" ptable['v'] = SByte(-6) - ", testMethod: nameof(PatchTarget6.RunSByte)); + ", methodName: nameof(PatchTargetNumbers.RunSByte)); var returnValue = target.RunSByte(-5); Assert.True(target.ran); Assert.Equal(-6, returnValue); } [Fact] - public void TestCastPrimitiveWrapperByte() + public void TestCastByte() { - var target = new PatchTarget6(); - AddPrefix(@" + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetNumbers(); + using var patchHandle = luaCs.AddPrefix(@" ptable['v'] = Byte(6) - ", testMethod: nameof(PatchTarget6.RunByte)); + ", methodName: nameof(PatchTargetNumbers.RunByte)); var returnValue = target.RunByte(5); Assert.True(target.ran); Assert.Equal(6, returnValue); } [Fact] - public void TestCastPrimitiveWrapperInt16() + public void TestCastInt16() { - var target = new PatchTarget6(); - AddPrefix(@" + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetNumbers(); + using var patchHandle = luaCs.AddPrefix(@" ptable['v'] = Int16(-25000) - ", testMethod: nameof(PatchTarget6.RunInt16)); + ", methodName: nameof(PatchTargetNumbers.RunInt16)); var returnValue = target.RunInt16(30000); Assert.True(target.ran); Assert.Equal(-25000, returnValue); } [Fact] - public void TestCastPrimitiveWrapperUInt16() + public void TestCastUInt16() { - var target = new PatchTarget6(); - AddPrefix(@" + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetNumbers(); + using var patchHandle = luaCs.AddPrefix(@" ptable['v'] = UInt16(60000) - ", testMethod: nameof(PatchTarget6.RunUInt16)); + ", methodName: nameof(PatchTargetNumbers.RunUInt16)); var returnValue = target.RunUInt16(50000); Assert.True(target.ran); Assert.Equal(60000, returnValue); } [Fact] - public void TestCastPrimitiveWrapperInt32() + public void TestCastInt32() { - var target = new PatchTarget6(); - AddPrefix(@" + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetNumbers(); + using var patchHandle = luaCs.AddPrefix(@" ptable['v'] = Int32('7FFFFF00', 16) - ", testMethod: nameof(PatchTarget6.RunInt32)); + ", methodName: nameof(PatchTargetNumbers.RunInt32)); var returnValue = target.RunInt32(900000); Assert.True(target.ran); Assert.Equal(0x7FFFFF00, returnValue); } [Fact] - public void TestCastPrimitiveWrapperUInt32() + public void TestCastUInt32() { - var target = new PatchTarget6(); - AddPrefix(@" + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetNumbers(); + using var patchHandle = luaCs.AddPrefix(@" ptable['v'] = UInt32('AFFFFFFF', 16) - ", testMethod: nameof(PatchTarget6.RunUInt32)); + ", methodName: nameof(PatchTargetNumbers.RunUInt32)); var returnValue = target.RunUInt32(300500); Assert.True(target.ran); Assert.Equal(0xAFFFFFFF, returnValue); } [Fact] - public void TestCastPrimitiveWrapperInt64() + public void TestCastInt64() { - var target = new PatchTarget6(); - AddPrefix(@" + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetNumbers(); + using var patchHandle = luaCs.AddPrefix(@" ptable['v'] = Int64('7555555555555555', 16) - ", testMethod: nameof(PatchTarget6.RunInt64)); + ", methodName: nameof(PatchTargetNumbers.RunInt64)); var returnValue = target.RunInt64(0x7FFFFFFF00000000); Assert.True(target.ran); Assert.Equal(0x7555555555555555, returnValue); } [Fact] - public void TestCastPrimitiveWrapperUInt64() + public void TestCastUInt64() { - var target = new PatchTarget6(); - AddPrefix(@" + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetNumbers(); + using var patchHandle = luaCs.AddPrefix(@" ptable['v'] = UInt64('F555555555555555', 16) - ", testMethod: nameof(PatchTarget6.RunUInt64)); + ", methodName: nameof(PatchTargetNumbers.RunUInt64)); var returnValue = target.RunUInt64(0xFFFFFFFF00000000); Assert.True(target.ran); Assert.Equal(0xF555555555555555, returnValue); } [Fact] - public void TestCastPrimitiveWrapperSingle() + public void TestCastSingle() { - var target = new PatchTarget6(); - AddPrefix(@" + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetNumbers(); + using var patchHandle = luaCs.AddPrefix(@" ptable['v'] = Single(123.456) - ", testMethod: nameof(PatchTarget6.RunSingle)); + ", methodName: nameof(PatchTargetNumbers.RunSingle)); var returnValue = target.RunSingle(111.111f); Assert.True(target.ran); Assert.Equal(123.456f, returnValue); } [Fact] - public void TestCastPrimitiveWrapperDouble() + public void TestCastDouble() { - var target = new PatchTarget6(); - AddPrefix(@" + using var patchTargetHandle = HookPatchHelpers.LockPatchTarget(); + var target = new PatchTargetNumbers(); + using var patchHandle = luaCs.AddPrefix(@" ptable['v'] = Double(123.456) - ", testMethod: nameof(PatchTarget6.RunDouble)); + ", methodName: nameof(PatchTargetNumbers.RunDouble)); var returnValue = target.RunDouble(111.111d); Assert.True(target.ran); Assert.Equal(123.456d, returnValue); diff --git a/Barotrauma/BarotraumaTest/LuaCs/LuaCsFixture.cs b/Barotrauma/BarotraumaTest/LuaCs/LuaCsFixture.cs new file mode 100644 index 000000000..28a0dd9f2 --- /dev/null +++ b/Barotrauma/BarotraumaTest/LuaCs/LuaCsFixture.cs @@ -0,0 +1,31 @@ +using Barotrauma; +using System; +using System.Runtime.ExceptionServices; + +namespace TestProject.LuaCs +{ + /// + /// Shared LuaCsSetup instance. + /// + /// + /// Don't use this unless you need to test logic that makes use of + /// a shared state (static variables). + /// + public class LuaCsFixture : IDisposable + { + public LuaCsFixture() + { + LuaCs.ExceptionHandler = (ex, _) => + { + // Pretend we never caught the exception in the first place + // (this allows us to preserve the stack trace) + var di = ExceptionDispatchInfo.Capture(ex); + di.Throw(); + }; + } + + internal LuaCsSetup LuaCs { get; } = new(); + + void IDisposable.Dispose() => LuaCs.Stop(); + } +}