From dfb2974781b4fec7e7f55137a4e8a350298b9317 Mon Sep 17 00:00:00 2001 From: stakx Date: Thu, 8 Aug 2019 15:07:19 +0200 Subject: [PATCH] Format method parameter type list more accurately This ensures that `in`, `ref`, `out`, and `params` modifiers are in- cluded in the formatting of parameter type lists. --- src/Moq/Extensions.cs | 6 +++ src/Moq/MethodCall.cs | 6 +-- src/Moq/StringBuilderExtensions.cs | 48 +++++++++++++++++++ .../StringBuilderExtensionsFixture.cs | 45 +++++++++++++++++ 4 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 tests/Moq.Tests/StringBuilderExtensionsFixture.cs diff --git a/src/Moq/Extensions.cs b/src/Moq/Extensions.cs index 5b53a0824..13c686b81 100644 --- a/src/Moq/Extensions.cs +++ b/src/Moq/Extensions.cs @@ -10,6 +10,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; +using System.Text; using Moq.Properties; @@ -188,6 +189,11 @@ public static bool CompareTo(this TTypes types, TOtherTypes return true; } + public static string GetParameterTypeList(this MethodInfo method) + { + return new StringBuilder().AppendParameterTypeList(method.GetParameters()).ToString(); + } + public static ParameterTypes GetParameterTypes(this MethodInfo method) { return new ParameterTypes(method.GetParameters()); diff --git a/src/Moq/MethodCall.cs b/src/Moq/MethodCall.cs index 3fa3360c6..20511313e 100644 --- a/src/Moq/MethodCall.cs +++ b/src/Moq/MethodCall.cs @@ -187,14 +187,12 @@ public void SetCallbackResponse(Delegate callback) var expectedParamTypes = this.Method.GetParameterTypes(); if (!callback.CompareParameterTypesTo(expectedParamTypes)) { - // TODO: the following won't properly distinguish between `in`, `ref`, and `out` parameters! - var actualParams = callback.GetMethodInfo().GetParameters(); throw new ArgumentException( string.Format( CultureInfo.CurrentCulture, Resources.InvalidCallbackParameterMismatch, - string.Join(", ", expectedParamTypes.Select(p => p.GetFormattedName()).ToArray()), - string.Join(", ", actualParams.Select(p => p.ParameterType.GetFormattedName()).ToArray()))); + this.Method.GetParameterTypeList(), + callback.GetMethodInfo().GetParameterTypeList())); } if (callback.GetMethodInfo().ReturnType != typeof(void)) diff --git a/src/Moq/StringBuilderExtensions.cs b/src/Moq/StringBuilderExtensions.cs index 6e650017b..b236c35dd 100644 --- a/src/Moq/StringBuilderExtensions.cs +++ b/src/Moq/StringBuilderExtensions.cs @@ -74,6 +74,54 @@ public static StringBuilder AppendNameOf(this StringBuilder stringBuilder, Type return stringBuilder.AppendFormattedName(type); } + public static StringBuilder AppendParameterType(this StringBuilder stringBuilder, ParameterInfo parameter) + { + var parameterType = parameter.ParameterType; + + if (parameterType.IsByRef) + { + switch (parameter.Attributes & (ParameterAttributes.In | ParameterAttributes.Out)) + { + case ParameterAttributes.In: + stringBuilder.Append("in "); + break; + + case ParameterAttributes.Out: + stringBuilder.Append("out "); + break; + + case ParameterAttributes.In | ParameterAttributes.Out: + default: + stringBuilder.Append("ref "); + break; + } + + parameterType = parameterType.GetElementType(); + } + + if (parameterType.IsArray && parameter.IsDefined(typeof(ParamArrayAttribute), true)) + { + stringBuilder.Append("params "); + } + + return stringBuilder.AppendFormattedName(parameterType); + } + + public static StringBuilder AppendParameterTypeList(this StringBuilder stringBuilder, ParameterInfo[] parameters) + { + for (int i = 0; i < parameters.Length; ++i) + { + if (i > 0) + { + stringBuilder.Append(", "); + } + + stringBuilder.AppendParameterType(parameters[i]); + } + + return stringBuilder; + } + public static StringBuilder AppendValueOf(this StringBuilder stringBuilder, object obj) { if (obj == null) diff --git a/tests/Moq.Tests/StringBuilderExtensionsFixture.cs b/tests/Moq.Tests/StringBuilderExtensionsFixture.cs new file mode 100644 index 000000000..cf18c7303 --- /dev/null +++ b/tests/Moq.Tests/StringBuilderExtensionsFixture.cs @@ -0,0 +1,45 @@ +// Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD. +// All rights reserved. Licensed under the BSD 3-Clause License; see License.txt. + +using System.Text; + +using Xunit; + +namespace Moq.Tests +{ + public class StringBuilderExtensionsFixture + { + [Theory] + [InlineData(nameof(IMethods.Empty), "")] + [InlineData(nameof(IMethods.Int), "int")] + [InlineData(nameof(IMethods.IntAndString), "int, string")] + [InlineData(nameof(IMethods.InInt), "in int")] + [InlineData(nameof(IMethods.RefInt), "ref int")] + [InlineData(nameof(IMethods.OutInt), "out int")] + [InlineData(nameof(IMethods.BoolAndParamsString), "bool, params string[]")] + public void AppendParameterList_formats_parameter_lists_correctly(string methodName, string expected) + { + var actual = GetFormattedParameterListOf(methodName); + Assert.Equal(expected, actual); + } + + private string GetFormattedParameterListOf(string methodName) + { + var stringBuilder = new StringBuilder(); + var method = typeof(IMethods).GetMethod(methodName); + stringBuilder.AppendParameterTypeList(method.GetParameters()); + return stringBuilder.ToString(); + } + + public interface IMethods + { + void Empty(); + void Int(int arg1); + void IntAndString(int arg1, string arg2); + void InInt(in int arg1); + void RefInt(ref int arg1); + void OutInt(out int arg1); + void BoolAndParamsString(bool arg1, params string[] arg2); + } + } +}