From b2ec59a3f3405c683db5a06586288e53ddefdea8 Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Wed, 23 Nov 2022 12:52:36 +0100 Subject: [PATCH] Fix and tests for invoking static methods --- Tapeti.Tests/Config/SimpleControllerTest.cs | 13 +- ...arser.cs => ConnectionStringParserTest.cs} | 0 Tapeti.Tests/Helpers/ExpressionInvokerTest.cs | 227 ++++++++++++++++++ Tapeti/Helpers/ExpressionInvoker.cs | 13 +- 4 files changed, 247 insertions(+), 6 deletions(-) rename Tapeti.Tests/Helpers/{ConnectionStringParser.cs => ConnectionStringParserTest.cs} (100%) create mode 100644 Tapeti.Tests/Helpers/ExpressionInvokerTest.cs diff --git a/Tapeti.Tests/Config/SimpleControllerTest.cs b/Tapeti.Tests/Config/SimpleControllerTest.cs index fbeb027..8dca8ad 100644 --- a/Tapeti.Tests/Config/SimpleControllerTest.cs +++ b/Tapeti.Tests/Config/SimpleControllerTest.cs @@ -12,12 +12,19 @@ namespace Tapeti.Tests.Config public void RegisterController() { var bindings = GetControllerBindings(); - bindings.Should().HaveCount(1); + bindings.Should().HaveCount(2); var handleSimpleMessageBinding = bindings.Single(b => b is IControllerMethodBinding cmb && cmb.Controller == typeof(TestController) && cmb.Method.Name == "HandleSimpleMessage"); handleSimpleMessageBinding.QueueType.Should().Be(QueueType.Dynamic); + + + var handleSimpleMessageStaticBinding = bindings.Single(b => b is IControllerMethodBinding cmb && + cmb.Controller == typeof(TestController) && + cmb.Method.Name == "HandleSimpleMessageStatic"); + handleSimpleMessageStaticBinding.QueueType.Should().Be(QueueType.Dynamic); + } @@ -35,6 +42,10 @@ namespace Tapeti.Tests.Config public void HandleSimpleMessage(TestMessage message) { } + + public static void HandleSimpleMessageStatic(TestMessage message) + { + } } #pragma warning restore diff --git a/Tapeti.Tests/Helpers/ConnectionStringParser.cs b/Tapeti.Tests/Helpers/ConnectionStringParserTest.cs similarity index 100% rename from Tapeti.Tests/Helpers/ConnectionStringParser.cs rename to Tapeti.Tests/Helpers/ConnectionStringParserTest.cs diff --git a/Tapeti.Tests/Helpers/ExpressionInvokerTest.cs b/Tapeti.Tests/Helpers/ExpressionInvokerTest.cs new file mode 100644 index 0000000..0771dd1 --- /dev/null +++ b/Tapeti.Tests/Helpers/ExpressionInvokerTest.cs @@ -0,0 +1,227 @@ +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using FluentAssertions; +using Tapeti.Helpers; +using Xunit; + +namespace Tapeti.Tests.Helpers +{ + public class ExpressionInvokerTest + { + [Fact] + public void InstanceMethodVoidNoParameters() + { + const string methodName = nameof(InvokeTarget.InstanceMethodVoidNoParameters); + var invoker = InvokerFor(methodName); + + var target = new InvokeTarget(); + invoker.Invoke(target); + + target.Verify(methodName); + } + + + [Fact] + public void InstanceMethodReturnValueNoParameters() + { + const string methodName = nameof(InvokeTarget.InstanceMethodReturnValueNoParameters); + var invoker = InvokerFor(methodName); + + var target = new InvokeTarget(); + var returnValue = invoker.Invoke(target); + + target.Verify(methodName); + returnValue.Should().Be("Hello world!"); + } + + + [Fact] + public void InstanceMethodVoidParameters() + { + const string methodName = nameof(InvokeTarget.InstanceMethodVoidParameters); + var invoker = InvokerFor(methodName); + + var target = new InvokeTarget(); + invoker.Invoke(target, 42); + + target.Verify(methodName, "42"); + } + + + [Fact] + public void InstanceMethodReturnValueParameters() + { + const string methodName = nameof(InvokeTarget.InstanceMethodReturnValueParameters); + var invoker = InvokerFor(methodName); + + var target = new InvokeTarget(); + var returnValue = invoker.Invoke(target, new byte[] { 42, 69 }); + + target.Verify(methodName, "42,69"); + returnValue.Should().Be(true); + } + + + [Fact] + public void StaticMethodVoidNoParameters() + { + InvokeTarget.ResetStatic(); + + const string methodName = nameof(InvokeTarget.StaticMethodVoidNoParameters); + var invoker = InvokerFor(methodName); + + invoker.Invoke(null); + + InvokeTarget.VerifyStatic(methodName); + } + + + [Fact] + public void StaticMethodReturnValueNoParameters() + { + InvokeTarget.ResetStatic(); + + const string methodName = nameof(InvokeTarget.StaticMethodReturnValueNoParameters); + var invoker = InvokerFor(methodName); + + var returnValue = invoker.Invoke(null); + + InvokeTarget.VerifyStatic(methodName); + returnValue.Should().Be("Hello world!"); + } + + + [Fact] + public void StaticMethodVoidParameters() + { + InvokeTarget.ResetStatic(); + + const string methodName = nameof(InvokeTarget.StaticMethodVoidParameters); + var invoker = InvokerFor(methodName); + + invoker.Invoke(null, 42); + + InvokeTarget.VerifyStatic(methodName, "42"); + } + + + [Fact] + public void StaticMethodReturnValueParameters() + { + InvokeTarget.ResetStatic(); + + const string methodName = nameof(InvokeTarget.StaticMethodReturnValueParameters); + var invoker = InvokerFor(methodName); + + var returnValue = invoker.Invoke(null, new byte[] { 42, 69 }); + + InvokeTarget.VerifyStatic(methodName, "42,69"); + returnValue.Should().Be(true); + } + + + private static ExpressionInvoke InvokerFor(string invokeTargetMethodName) + { + var method = typeof(InvokeTarget).GetMethod(invokeTargetMethodName); + return method!.CreateExpressionInvoke(); + } + + + + // ReSharper disable ParameterHidesMember + private class InvokeTarget + { + private static string? staticMethodName; + private static string? staticParameters; + + private string? methodName; + private string? parameters; + + + public void InstanceMethodVoidNoParameters() + { + MethodCalled(); + } + + public string InstanceMethodReturnValueNoParameters() + { + MethodCalled(); + return "Hello world!"; + } + + public void InstanceMethodVoidParameters(int answer) + { + MethodCalled(answer.ToString()); + } + + public bool InstanceMethodReturnValueParameters(IEnumerable values) + { + MethodCalled(string.Join(',', values.Select(v => v.ToString()))); + return true; + } + + + public static void StaticMethodVoidNoParameters() + { + StaticMethodCalled(); + } + + public static string StaticMethodReturnValueNoParameters() + { + StaticMethodCalled(); + return "Hello world!"; + } + + public static void StaticMethodVoidParameters(int answer) + { + StaticMethodCalled(answer.ToString()); + } + + public static bool StaticMethodReturnValueParameters(IEnumerable values) + { + StaticMethodCalled(string.Join(',', values.Select(v => v.ToString()))); + return true; + } + + + private void MethodCalled(string parameters = "", [CallerMemberName]string methodName = "") + { + this.methodName.Should().BeNull(); + this.methodName = methodName; + this.parameters = parameters; + + } + + + public static void ResetStatic() + { + staticMethodName = null; + staticParameters = null; + } + + + private static void StaticMethodCalled(string parameters = "", [CallerMemberName] string methodName = "") + { + staticMethodName.Should().BeNull(); + staticMethodName = methodName; + staticParameters = parameters; + } + + + + public void Verify(string methodName, string parameters = "") + { + this.methodName.Should().Be(methodName); + this.parameters.Should().Be(parameters); + } + + + public static void VerifyStatic(string methodName, string parameters = "") + { + staticMethodName.Should().Be(methodName); + staticParameters.Should().Be(parameters); + } + } + } +} diff --git a/Tapeti/Helpers/ExpressionInvoker.cs b/Tapeti/Helpers/ExpressionInvoker.cs index 4fa8ece..ea573eb 100644 --- a/Tapeti/Helpers/ExpressionInvoker.cs +++ b/Tapeti/Helpers/ExpressionInvoker.cs @@ -44,22 +44,25 @@ namespace Tapeti.Helpers .ToArray(); - var target = Expression.Parameter(typeof(object), "target"); - var castTarget = Expression.Convert(target, method.DeclaringType); - var invoke = Expression.Call(castTarget, method, parameters); + var instanceParameter = Expression.Parameter(typeof(object), "target"); + Expression? instance = method.IsStatic + ? null + : Expression.Convert(instanceParameter, method.DeclaringType); + + var invoke = Expression.Call(instance, method, parameters); Expression lambda; if (method.ReturnType != typeof(void)) { var result = Expression.Convert(invoke, typeof(object)); - lambda = Expression.Lambda(result, target, argsParameter); + lambda = Expression.Lambda(result, instanceParameter, argsParameter); } else { var nullResult = Expression.Constant(null, typeof(object)); var body = Expression.Block(invoke, nullResult); - lambda = Expression.Lambda(body, target, argsParameter); + lambda = Expression.Lambda(body, instanceParameter, argsParameter); } return lambda.Compile();