From f23b802eda1af6bff0317c03c2401acea67d5368 Mon Sep 17 00:00:00 2001 From: Idan Igra Date: Tue, 18 Sep 2018 17:11:46 +0300 Subject: [PATCH 1/2] Support indexer with multiple arguments --- src/Moq/Mock.Generic.cs | 6 ++ src/Moq/Mock.cs | 29 ++++++-- tests/Moq.Tests/CallbacksFixture.cs | 105 +++++++++++++++++++++++++++- 3 files changed, 135 insertions(+), 5 deletions(-) diff --git a/src/Moq/Mock.Generic.cs b/src/Moq/Mock.Generic.cs index f9716a399..a461818e4 100644 --- a/src/Moq/Mock.Generic.cs +++ b/src/Moq/Mock.Generic.cs @@ -344,6 +344,12 @@ public ISetup SetupSet(Action setterExpression) return Mock.SetupSet(this, setterExpression, null); } + /// + public ISetup SetupSetMethod(Expression>> setter) + { + return Mock.SetupSetMethod(this, new Type[] { typeof(T1), typeof(T2) }, setter); + } + /// [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "By design")] [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Property", Justification = "This sets properties, so it's appropriate.")] diff --git a/src/Moq/Mock.cs b/src/Moq/Mock.cs index ed9a3aebb..23662de15 100644 --- a/src/Moq/Mock.cs +++ b/src/Moq/Mock.cs @@ -664,13 +664,12 @@ private static TSetupPhrase SetupSetImpl( matchers[valueIndex] = new MatchExpression(last.Match); - if (arguments.Length == 2) + for (int i = 0; i < arguments.Length - 1; i++) { - // TODO: what about multi-index setters? // Add the index value for the property indexer - values[0] = GetValueExpression(arguments[0], parameters[0].ParameterType); + values[i] = GetValueExpression(arguments[i], parameters[i].ParameterType); // TODO: No matcher supported now for the index - matchers[0] = values[0]; + matchers[i] = values[i]; } var lambda = Expression.Lambda( @@ -683,6 +682,28 @@ private static TSetupPhrase SetupSetImpl( } } + internal static ISetup SetupSetMethod( + Mock mock, + Type[] argumentTypes, + LambdaExpression setter) + where T : class + { + Expression actionParameter = setter.Parameters.Single(); + if (!(setter.Body is InvocationExpression call) || call.Expression != actionParameter) + { + throw new InvalidOperationException("Expected only call to the indexer method given in the lambda parameter"); + } + + PropertyInfo indexer = typeof(T).GetProperty("Item", typeof(TProperty), argumentTypes); + MethodInfo setterMethod = indexer.GetSetMethod(); + ParameterExpression instanceLambdaArg = Expression.Parameter(typeof(T), "inst"); + Expression> callSetter = Expression.Lambda>( + Expression.Call(instanceLambdaArg, setterMethod, call.Arguments), + instanceLambdaArg); + + return Setup(mock, callSetter, condition: null); + } + private static Expression GetValueExpression(object value, Type type) { if (value != null && value.GetType() == type) diff --git a/tests/Moq.Tests/CallbacksFixture.cs b/tests/Moq.Tests/CallbacksFixture.cs index b0f13ff8d..6bf53f67c 100644 --- a/tests/Moq.Tests/CallbacksFixture.cs +++ b/tests/Moq.Tests/CallbacksFixture.cs @@ -474,6 +474,105 @@ public void CallbackWithRefParameterCannotModifyNonRefParameter() Assert.Equal("input", value); } + [Fact] + public void CallbackWithIndexerGetter() + { + int x = default(int); + + var mock = new Mock(); + mock.Setup(f => f[It.IsAny()]) + .Callback(new Action(x_ => x = x_)) + .Returns(32); + + int result = mock.Object[17]; + Assert.Equal(17, x); + Assert.Equal(32, result); + } + + [Fact] + public void CallbackWithIndexerSetter() + { + int x = default(int); + int result = default(int); + + var mock = new Mock(); + mock.SetupSet(f => f[10] = It.IsAny()) + .Callback(new Action((x_, result_) => + { + x = x_; + result = result_; + })); + + mock.Object[10] = 5; + Assert.Equal(10, x); + Assert.Equal(5, result); + } + + [Fact] + public void CallbackWithMultipleArgumentIndexerGetter() + { + int x = default(int); + int y = default(int); + + var mock = new Mock(); + mock.Setup(f => f[It.IsAny(), It.IsAny()]) + .Callback(new Action((x_, y_) => + { + x = x_; + y = y_; + })) + .Returns(14); + + int result = mock.Object[20, 22]; + Assert.Equal(20, x); + Assert.Equal(22, y); + Assert.Equal(14, result); + } + + [Fact] + public void CallbackWithMultipleArgumentIndexerSetterWithoutAny() + { + int x = default(int); + int y = default(int); + int result = default(int); + + var mock = new Mock(); + mock.SetupSet(f => f[3, 13] = It.IsAny()) + .Callback(new Action((x_, y_, result_) => + { + x = x_; + y = y_; + result = result_; + })); + + mock.Object[3, 13] = 2; + Assert.Equal(3, x); + Assert.Equal(13, y); + Assert.Equal(2, result); + } + + [Fact] + public void CallbackWithMultipleArgumentIndexerSetterWithAny() + { + int x = default(int); + int y = default(int); + int result = default(int); + + var mock = new Mock(); + mock.SetupSetMethod(setter => setter(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback(new Action((x_, y_, result_) => + { + x = x_; + y = y_; + result = result_; + })); + + mock.Object[3, 13] = 2; + Assert.Equal(3, x); + Assert.Equal(13, y); + Assert.Equal(2, result); + } + public interface IInterface { void Method(Derived b); @@ -490,7 +589,7 @@ public class Derived : Base private void TraceMe(Base b) { } - + public interface IFoo { void Submit(); @@ -515,6 +614,10 @@ public interface IFoo string Execute(ref string arg1, string arg2); int Value { get; set; } + + int this[int x] { get; set; } + + int this[int x, int y] { get; set; } } public delegate void ExecuteRHandler(ref string arg1); From a899cbc529066f996e5ff19249f76033af0ed51b Mon Sep 17 00:00:00 2001 From: Idan Igra Date: Wed, 26 Sep 2018 17:41:47 +0300 Subject: [PATCH 2/2] Review fixes - reverted changes unrelated to fix of NullReferenceException in multiple argument indexer --- src/Moq/Mock.Generic.cs | 6 --- src/Moq/Mock.cs | 22 ----------- tests/Moq.Tests/CallbacksFixture.cs | 60 +---------------------------- 3 files changed, 1 insertion(+), 87 deletions(-) diff --git a/src/Moq/Mock.Generic.cs b/src/Moq/Mock.Generic.cs index a461818e4..f9716a399 100644 --- a/src/Moq/Mock.Generic.cs +++ b/src/Moq/Mock.Generic.cs @@ -344,12 +344,6 @@ public ISetup SetupSet(Action setterExpression) return Mock.SetupSet(this, setterExpression, null); } - /// - public ISetup SetupSetMethod(Expression>> setter) - { - return Mock.SetupSetMethod(this, new Type[] { typeof(T1), typeof(T2) }, setter); - } - /// [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "By design")] [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Property", Justification = "This sets properties, so it's appropriate.")] diff --git a/src/Moq/Mock.cs b/src/Moq/Mock.cs index 23662de15..ea4b4cc16 100644 --- a/src/Moq/Mock.cs +++ b/src/Moq/Mock.cs @@ -682,28 +682,6 @@ private static TSetupPhrase SetupSetImpl( } } - internal static ISetup SetupSetMethod( - Mock mock, - Type[] argumentTypes, - LambdaExpression setter) - where T : class - { - Expression actionParameter = setter.Parameters.Single(); - if (!(setter.Body is InvocationExpression call) || call.Expression != actionParameter) - { - throw new InvalidOperationException("Expected only call to the indexer method given in the lambda parameter"); - } - - PropertyInfo indexer = typeof(T).GetProperty("Item", typeof(TProperty), argumentTypes); - MethodInfo setterMethod = indexer.GetSetMethod(); - ParameterExpression instanceLambdaArg = Expression.Parameter(typeof(T), "inst"); - Expression> callSetter = Expression.Lambda>( - Expression.Call(instanceLambdaArg, setterMethod, call.Arguments), - instanceLambdaArg); - - return Setup(mock, callSetter, condition: null); - } - private static Expression GetValueExpression(object value, Type type) { if (value != null && value.GetType() == type) diff --git a/tests/Moq.Tests/CallbacksFixture.cs b/tests/Moq.Tests/CallbacksFixture.cs index 6bf53f67c..2e7a59359 100644 --- a/tests/Moq.Tests/CallbacksFixture.cs +++ b/tests/Moq.Tests/CallbacksFixture.cs @@ -474,21 +474,6 @@ public void CallbackWithRefParameterCannotModifyNonRefParameter() Assert.Equal("input", value); } - [Fact] - public void CallbackWithIndexerGetter() - { - int x = default(int); - - var mock = new Mock(); - mock.Setup(f => f[It.IsAny()]) - .Callback(new Action(x_ => x = x_)) - .Returns(32); - - int result = mock.Object[17]; - Assert.Equal(17, x); - Assert.Equal(32, result); - } - [Fact] public void CallbackWithIndexerSetter() { @@ -508,27 +493,6 @@ public void CallbackWithIndexerSetter() Assert.Equal(5, result); } - [Fact] - public void CallbackWithMultipleArgumentIndexerGetter() - { - int x = default(int); - int y = default(int); - - var mock = new Mock(); - mock.Setup(f => f[It.IsAny(), It.IsAny()]) - .Callback(new Action((x_, y_) => - { - x = x_; - y = y_; - })) - .Returns(14); - - int result = mock.Object[20, 22]; - Assert.Equal(20, x); - Assert.Equal(22, y); - Assert.Equal(14, result); - } - [Fact] public void CallbackWithMultipleArgumentIndexerSetterWithoutAny() { @@ -551,28 +515,6 @@ public void CallbackWithMultipleArgumentIndexerSetterWithoutAny() Assert.Equal(2, result); } - [Fact] - public void CallbackWithMultipleArgumentIndexerSetterWithAny() - { - int x = default(int); - int y = default(int); - int result = default(int); - - var mock = new Mock(); - mock.SetupSetMethod(setter => setter(It.IsAny(), It.IsAny(), It.IsAny())) - .Callback(new Action((x_, y_, result_) => - { - x = x_; - y = y_; - result = result_; - })); - - mock.Object[3, 13] = 2; - Assert.Equal(3, x); - Assert.Equal(13, y); - Assert.Equal(2, result); - } - public interface IInterface { void Method(Derived b); @@ -589,7 +531,7 @@ public class Derived : Base private void TraceMe(Base b) { } - + public interface IFoo { void Submit();