From 5648e6d13d4b41f662c6c0b4e71e67bce788bfcb Mon Sep 17 00:00:00 2001 From: stakx Date: Tue, 21 Nov 2017 23:05:24 +0100 Subject: [PATCH 1/3] Add failing regression test --- Moq.Tests/Regressions/IssueReportsFixture.cs | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Moq.Tests/Regressions/IssueReportsFixture.cs b/Moq.Tests/Regressions/IssueReportsFixture.cs index 51dc7c591..94228ae40 100644 --- a/Moq.Tests/Regressions/IssueReportsFixture.cs +++ b/Moq.Tests/Regressions/IssueReportsFixture.cs @@ -891,6 +891,27 @@ public void strict_mock_accepts_null_as_nullable_guid_value() #endregion // #184 + #region 224 + + public class Issue224 + { + [Fact] + public void Delegate_invocation_in_Mock_Of_does_not_cause_infinite_loop() + { + var infiniteLoopTimeout = TimeSpan.FromSeconds(5); + + var timedOut = !Task.Run(() => + { + var fn = Mock.Of>(f => f() == 42); + Assert.Equal(42, fn()); + }).Wait(infiniteLoopTimeout); + + Assert.False(timedOut); + } + } + + #endregion + #region 239 public class Issue239 From 481bbd0dc594cac179beee46954661fee936348d Mon Sep 17 00:00:00 2001 From: stakx Date: Tue, 21 Nov 2017 22:29:28 +0100 Subject: [PATCH 2/3] Fix infinite loop w/ delegates in `Mock.Of` --- Source/Linq/MockSetupsBuilder.cs | 51 ++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/Source/Linq/MockSetupsBuilder.cs b/Source/Linq/MockSetupsBuilder.cs index 90ebe556d..c98fe45b5 100644 --- a/Source/Linq/MockSetupsBuilder.cs +++ b/Source/Linq/MockSetupsBuilder.cs @@ -169,6 +169,17 @@ private static Expression ConvertToSetup(Expression left, Expression right) return ConvertToSetup(method.Object, method, right); + case ExpressionType.Invoke: + var invocation = (InvocationExpression)left; + if (invocation.Expression is ParameterExpression && typeof(Delegate).IsAssignableFrom(invocation.Expression.Type)) + { + return ConvertToSetup(invocation, right); + } + else + { + break; + } + case ExpressionType.Convert: var left1 = (UnaryExpression)left; return ConvertToSetup(left1.Operand, Expression.Convert(right, left1.Operand.Type)); @@ -177,6 +188,46 @@ private static Expression ConvertToSetup(Expression left, Expression right) return null; } + private static Expression ConvertToSetup(InvocationExpression invocation, Expression right) + { + // transforms a delegate invocation expression such as `f(...) == x` (where `invocation` := `f(...)` and `right` := `x`) + // to `Mock.Get(f).Setup(f' => f'(...)).Returns(x) != null` (which in turn will get incorporated into a query + // `CreateMocks().First(f => ...)`. + + var delegateParameter = invocation.Expression; + + var mockGetMethod = + typeof(Mock) + .GetMethod("Get", BindingFlags.Public | BindingFlags.Static) + .MakeGenericMethod(delegateParameter.Type); + + var mockGetCall = Expression.Call(mockGetMethod, delegateParameter); + + var setupMethod = + typeof(Mock<>) + .MakeGenericType(delegateParameter.Type) + .GetMethods("Setup") + .Single(m => m.IsGenericMethod) + .MakeGenericMethod(right.Type); + + var setupCall = Expression.Call( + mockGetCall, + setupMethod, + Expression.Lambda(invocation, invocation.Expression as ParameterExpression)); + + var returnsMethod = + typeof(IReturns<,>) + .MakeGenericType(delegateParameter.Type, right.Type) + .GetMethod("Returns", new[] { right.Type }); + + var returnsCall = Expression.Call( + setupCall, + returnsMethod, + right); + + return Expression.NotEqual(returnsCall, Expression.Constant(null)); + } + private static Expression ConvertToSetupProperty(Expression targetObject, Expression left, Expression right) { // TODO: throw if target is a static class? From c66640ef237487dc7769b657d64463aad4e50fcb Mon Sep 17 00:00:00 2001 From: stakx Date: Tue, 21 Nov 2017 23:12:41 +0100 Subject: [PATCH 3/3] Update the changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f7688213..38e942e19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ The format is loosely based on [Keep a Changelog](http://keepachangelog.com/en/1 * Prevent Moq from relying on a mock's implementation of `IEnumerable` (@stakx, #510) * Verification leaked internal `MockVerificationException` type; remove it (@stakx, #511) * Custom matcher properties not printed correctly in error messages (@stakx, #517) +* Infinite loop when invoking delegate in `Mock.Of` setup expression (@stakx, #528) #### Obsoleted