Ejemplo n.º 1
0
        protected SetupWithOutParameterSupport(FluentSetup fluentSetup, Mock mock, InvocationShape expectation)
            : base(fluentSetup, mock, expectation)
        {
            Debug.Assert(expectation != null);

            this.outValues = GetOutValues(expectation.Arguments, expectation.Method.GetParameters());
        }
Ejemplo n.º 2
0
        internal static void VerifySet(Mock mock, Delegate setterExpression, Times times, string failMessage)
        {
            var(targetMock, expression, method, value) = SetupSetImpl(mock, setterExpression);
            var expectation = new InvocationShape(method, value);

            VerifyCalls(targetMock, expectation, expression, times, failMessage);
        }
Ejemplo n.º 3
0
Archivo: Setup.cs Proyecto: yunly/moq4
        protected Setup(InvocationShape expectation, LambdaExpression expression)
        {
            Debug.Assert(expression != null);

            this.expectation = expectation;
            this.expression  = expression;
        }
Ejemplo n.º 4
0
 /// <remarks>
 ///   Only use this constructor when you know that the specified <paramref name="method"/> has no `out` parameters,
 ///   and when you want to avoid the <see cref="MatcherFactory"/>-related overhead of the other constructor overload.
 /// </remarks>
 public MethodCall(Mock mock, Condition condition, LambdaExpression originalExpression, MethodInfo method, IMatcher[] argumentMatchers)
 {
     this.condition          = condition;
     this.expectation        = new InvocationShape(method, argumentMatchers);
     this.mock               = mock;
     this.originalExpression = originalExpression;
     this.outValues          = null;
 }
Ejemplo n.º 5
0
        protected Setup(Mock mock, InvocationShape expectation)
        {
            Debug.Assert(mock != null);
            Debug.Assert(expectation != null);

            this.expectation = expectation;
            this.mock        = mock;
        }
Ejemplo n.º 6
0
        public InnerMockSetup(Expression originalExpression, Mock mock, InvocationShape expectation, object returnValue)
            : base(originalExpression, mock, expectation)
        {
            Debug.Assert(Awaitable.TryGetResultRecursive(returnValue) is IMocked);

            this.returnValue = returnValue;

            this.MarkAsVerifiable();
        }
Ejemplo n.º 7
0
        protected Setup(FluentSetup fluentSetup, Mock mock, InvocationShape expectation)
        {
            Debug.Assert(mock != null);
            Debug.Assert(expectation != null);

            this.fluentSetup = fluentSetup;
            this.expectation = expectation;
            this.mock        = mock;
        }
Ejemplo n.º 8
0
        protected Setup(Expression originalExpression, Mock mock, InvocationShape expectation)
        {
            Debug.Assert(mock != null);
            Debug.Assert(expectation != null);

            this.originalExpression = originalExpression;
            this.expectation        = expectation;
            this.mock = mock;
        }
Ejemplo n.º 9
0
        public InnerMockSetup(Expression originalExpression, Mock mock, InvocationShape expectation, object returnValue)
            : base(originalExpression, mock, expectation)
        {
            Debug.Assert(Unwrap.ResultIfCompletedTask(returnValue) is IMocked);

            this.returnValue = returnValue;

            this.MarkAsVerifiable();
        }
Ejemplo n.º 10
0
        public MethodCall(Mock mock, Condition condition, LambdaExpression originalExpression, MethodInfo method, IReadOnlyList <Expression> arguments)
        {
            this.condition          = condition;
            this.expectation        = new InvocationShape(method, arguments);
            this.mock               = mock;
            this.originalExpression = originalExpression;
            this.outValues          = GetOutValues(arguments, method.GetParameters());

            this.SetFileInfo();
        }
Ejemplo n.º 11
0
        internal static void VerifyGet(Mock mock, LambdaExpression expression, Times times, string failMessage)
        {
            var method = expression.ToPropertyInfo().GetGetMethod(true);

            ThrowIfVerifyExpressionInvolvesUnsupportedMember(expression, method);

            var expectation = new InvocationShape(method, new IMatcher[0]);

            VerifyCalls(GetTargetMock(((MemberExpression)expression.Body).Expression, mock), expectation, expression, times, failMessage);
        }
Ejemplo n.º 12
0
        internal static void VerifyVoid(Mock mock, LambdaExpression expression, Times times, string failMessage)
        {
            Guard.NotNull(times, nameof(times));

            var(obj, method, args) = expression.GetCallInfo(mock);
            ThrowIfVerifyExpressionInvolvesUnsupportedMember(expression, method);

            var expectation = new InvocationShape(method, args);

            VerifyCalls(GetTargetMock(obj, mock), expectation, expression, times, failMessage);
        }
Ejemplo n.º 13
0
        public MethodCall(FluentSetup fluentSetup, Mock mock, Condition condition, InvocationShape expectation)
            : base(fluentSetup, mock, expectation)
        {
            this.condition = condition;
            this.flags     = expectation.Method.ReturnType != typeof(void) ? Flags.MethodIsNonVoid : 0;

            if ((mock.Switches & Switches.CollectDiagnosticFileInfoForSetups) != 0)
            {
                this.declarationSite = GetUserCodeCallSite();
            }
        }
Ejemplo n.º 14
0
        protected SetupWithOutParameterSupport(
            Expression originalExpression,
            Mock mock,
            InvocationShape expectation
            ) : base(originalExpression, mock, expectation)
        {
            Debug.Assert(expectation != null);

            this.outValues = GetOutValues(
                expectation.Arguments,
                expectation.Method.GetParameters()
                );
        }
Ejemplo n.º 15
0
        public MethodCall(
            Expression originalExpression,
            Mock mock,
            Condition condition,
            InvocationShape expectation
            ) : base(originalExpression, mock, expectation)
        {
            this.condition = condition;

            if ((mock.Switches & Switches.CollectDiagnosticFileInfoForSetups) != 0)
            {
                this.declarationSite = GetUserCodeCallSite();
            }
        }
Ejemplo n.º 16
0
        public MethodCall(Mock mock, Condition condition, InvocationShape expectation)
            : base(expectation)
        {
            this.condition = condition;
            this.flags     = expectation.Method.ReturnType != typeof(void) ? Flags.MethodIsNonVoid : 0;
            this.mock      = mock;

#if FEATURE_CALLERINFO
            if ((mock.Switches & Switches.CollectDiagnosticFileInfoForSetups) != 0)
            {
                this.declarationSite = GetUserCodeCallSite();
            }
#endif
        }
Ejemplo n.º 17
0
        internal static void VerifySet <T, TProperty>(
            Mock mock,
            Expression <Func <T, TProperty> > expression,
            Times times,
            string failMessage)
            where T : class
        {
            var method = expression.ToPropertyInfo().SetMethod;

            ThrowIfVerifyExpressionInvolvesUnsupportedMember(expression, method);

            var expectation = new InvocationShape(method, new IMatcher[] { AnyMatcher.Instance });

            VerifyCalls(GetTargetMock(((MemberExpression)expression.Body).Expression, mock), expectation, expression, times, failMessage);
        }
Ejemplo n.º 18
0
        public static bool TryFind(this IEnumerable <Setup> innerMockSetups, InvocationShape expectation, out Setup setup)
        {
            Debug.Assert(innerMockSetups.All(s => s.ReturnsInnerMock(out _)));

            foreach (Setup innerMockSetup in innerMockSetups)
            {
                if (innerMockSetup.Expectation.Equals(expectation))
                {
                    setup = innerMockSetup;
                    return(true);
                }
            }

            setup = default;
            return(false);
        }
Ejemplo n.º 19
0
        internal static void Verify <T, TResult>(
            Mock <T> mock,
            Expression <Func <T, TResult> > expression,
            Times times,
            string failMessage)
            where T : class
        {
            Guard.NotNull(times, nameof(times));

            if (expression.IsProperty())
            {
                VerifyGet <T, TResult>(mock, expression, times, failMessage);
            }
            else
            {
                var(obj, method, args) = expression.GetCallInfo(mock);
                ThrowIfVerifyExpressionInvolvesUnsupportedMember(expression, method);

                var expectation = new InvocationShape(method, args);
                VerifyCalls(GetTargetMock(obj, mock), expectation, expression, times, failMessage);
            }
        }
Ejemplo n.º 20
0
        private static void VerifyCalls(
            Mock targetMock,
            InvocationShape expectation,
            LambdaExpression expression,
            Times times,
            string failMessage)
        {
            var allInvocations          = targetMock.MutableInvocations.ToArray();
            var matchingInvocations     = allInvocations.Where(expectation.IsMatch).ToArray();
            var matchingInvocationCount = matchingInvocations.Length;

            if (!times.Verify(matchingInvocationCount))
            {
                Setup[] setups;
                if (targetMock.IsDelegateMock)
                {
                    // For delegate mocks, there's no need to compare methods as for regular mocks (below)
                    // since there's only one single method, so include all setups unconditionally.
                    setups = targetMock.Setups.ToArrayLive(s => true);
                }
                else
                {
                    setups = targetMock.Setups.ToArrayLive(oc => AreSameMethod(oc.Expression, expression));
                }
                throw MockException.NoMatchingCalls(failMessage, setups, allInvocations, expression, times, matchingInvocationCount);
            }
            else
            {
                foreach (var matchingInvocation in matchingInvocations)
                {
                    matchingInvocation.MarkAsVerified();
                }
            }

            bool AreSameMethod(LambdaExpression l, LambdaExpression r) =>
            l.Body is MethodCallExpression lc && r.Body is MethodCallExpression rc && lc.Method == rc.Method;
        }
Ejemplo n.º 21
0
Archivo: Setup.cs Proyecto: si-shj/moq4
        protected Setup(InvocationShape expectation)
        {
            Debug.Assert(expectation != null);

            this.expectation = expectation;
        }
Ejemplo n.º 22
0
        /// <summary>
        ///   Splits an expression such as `<c>m => m.A.B(x).C[y] = z</c>` into a chain of parts
        ///   that can be set up one at a time:
        ///   <list>
        ///     <item>`<c>m => m.A</c>`</item>,
        ///     <item>`<c>... => ....B(x)</c>`</item>,
        ///     <item>`<c>... => ....C</c>`</item>,
        ///     <item>`<c>... => ...[y] = z</c>`</item>.
        ///   </list>
        ///   <para>
        ///     The split points are chosen such that each part has exactly one associated
        ///     <see cref="MethodInfo"/> and optionally some argument expressions.
        ///   </para>
        /// </summary>
        /// <exception cref="ArgumentException">
        ///   It was not possible to completely split up the expression.
        /// </exception>
        internal static Stack <InvocationShape> Split(this LambdaExpression expression)
        {
            Debug.Assert(expression != null);

            var parts = new Stack <InvocationShape>();

            Expression remainder = expression.Body;

            while (CanSplit(remainder))
            {
                Split(remainder, out remainder, out var part);
                parts.Push(part);
            }

            if (parts.Count > 0 && remainder is ParameterExpression)
            {
                return(parts);
            }
            else
            {
                throw new ArgumentException(
                          string.Format(
                              CultureInfo.CurrentCulture,
                              Resources.UnsupportedExpression,
                              remainder.ToStringFixed()));
            }

            bool CanSplit(Expression e)
            {
                switch (e.NodeType)
                {
                case ExpressionType.Assign:
                case ExpressionType.AddAssign:
                case ExpressionType.SubtractAssign:
                {
                    var assignmentExpression = (BinaryExpression)e;
                    return(CanSplit(assignmentExpression.Left));
                }

                case ExpressionType.Call:
                case ExpressionType.Index:
                {
                    return(true);
                }

                case ExpressionType.Invoke:
                {
                    var invocationExpression = (InvocationExpression)e;
                    return(typeof(Delegate).IsAssignableFrom(invocationExpression.Expression.Type));
                }

                case ExpressionType.MemberAccess:
                {
                    var memberAccessExpression = (MemberExpression)e;
                    return(memberAccessExpression.Member is PropertyInfo);
                }

                case ExpressionType.Parameter:
                default:
                {
                    return(false);
                }
                }
            }

            void Split(Expression e, out Expression r /* remainder */, out InvocationShape p /* part */)
            {
                const string ParameterName = "...";

                switch (e.NodeType)
                {
                case ExpressionType.Assign:                                  // assignment to a property or indexer
                case ExpressionType.AddAssign:                               // subscription of event handler to event
                case ExpressionType.SubtractAssign:                          // unsubscription of event handler from event
                {
                    var assignmentExpression = (BinaryExpression)e;
                    Split(assignmentExpression.Left, out r, out var lhs);
                    PropertyInfo property;
                    if (lhs.Expression.Body is MemberExpression me)
                    {
                        Debug.Assert(me.Member is PropertyInfo);
                        property = (PropertyInfo)me.Member;
                    }
                    else
                    {
                        Debug.Assert(lhs.Expression.Body is IndexExpression);
                        property = ((IndexExpression)lhs.Expression.Body).Indexer;
                    }
                    var parameter = Expression.Parameter(r.Type, r is ParameterExpression ope ? ope.Name : ParameterName);
                    var arguments = new Expression[lhs.Arguments.Count + 1];
                    for (var ai = 0; ai < arguments.Length - 1; ++ai)
                    {
                        arguments[ai] = lhs.Arguments[ai];
                    }
                    arguments[arguments.Length - 1] = assignmentExpression.Right;
                    p = new InvocationShape(
                        expression: Expression.Lambda(
                            Expression.MakeBinary(e.NodeType, lhs.Expression.Body, assignmentExpression.Right),
                            parameter),
                        method: property.GetSetMethod(true),
                        arguments);
                    return;
                }

                case ExpressionType.Call:                          // regular method call
                {
                    var methodCallExpression = (MethodCallExpression)e;
                    if (!methodCallExpression.Method.IsStatic)
                    {
                        r = methodCallExpression.Object;
                        var parameter = Expression.Parameter(r.Type, r is ParameterExpression ope ? ope.Name : ParameterName);
                        var method    = methodCallExpression.Method;
                        var arguments = methodCallExpression.Arguments;
                        p = new InvocationShape(
                            expression: Expression.Lambda(
                                Expression.Call(parameter, method, arguments),
                                parameter),
                            method,
                            arguments);
                    }
                    else
                    {
                        Debug.Assert(methodCallExpression.Method.IsExtensionMethod());
                        Debug.Assert(methodCallExpression.Arguments.Count > 0);
                        r = methodCallExpression.Arguments[0];
                        var parameter = Expression.Parameter(r.Type, r is ParameterExpression ope ? ope.Name : ParameterName);
                        var method    = methodCallExpression.Method;
                        var arguments = methodCallExpression.Arguments.ToArray();
                        arguments[0] = parameter;
                        p            = new InvocationShape(
                            expression: Expression.Lambda(
                                Expression.Call(method, arguments),
                                parameter),
                            method,
                            arguments);
                    }
                    return;
                }

                case ExpressionType.Index:                          // indexer query
                {
                    var indexExpression = (IndexExpression)e;
                    r = indexExpression.Object;
                    var parameter = Expression.Parameter(r.Type, r is ParameterExpression ope ? ope.Name : ParameterName);
                    var indexer   = indexExpression.Indexer;
                    var arguments = indexExpression.Arguments;
                    p = new InvocationShape(
                        expression: Expression.Lambda(
                            Expression.MakeIndex(parameter, indexer, arguments),
                            parameter),
                        method: indexer.GetGetMethod(true),
                        arguments);
                    return;
                }

                case ExpressionType.Invoke:                          // delegate invocation
                {
                    var invocationExpression = (InvocationExpression)e;
                    Debug.Assert(invocationExpression.Expression.Type.IsDelegate());
                    r = invocationExpression.Expression;
                    var parameter = Expression.Parameter(r.Type, r is ParameterExpression ope ? ope.Name : ParameterName);
                    var arguments = invocationExpression.Arguments;
                    p = new InvocationShape(
                        expression: Expression.Lambda(
                            Expression.Invoke(parameter, arguments),
                            parameter),
                        method: r.Type.GetMethod("Invoke", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance),
                        arguments);
                    return;
                }

                case ExpressionType.MemberAccess:                          // property query
                {
                    var memberAccessExpression = (MemberExpression)e;
                    Debug.Assert(memberAccessExpression.Member is PropertyInfo);
                    r = memberAccessExpression.Expression;
                    var parameter = Expression.Parameter(r.Type, r is ParameterExpression ope ? ope.Name : ParameterName);
                    var property  = memberAccessExpression.GetReboundProperty();
                    var method    = property.CanRead ? property.GetGetMethod(true) : property.GetSetMethod(true);
                    //                    ^^^^^^^                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                    // We're in the switch case block for property read access, therefore we prefer the
                    // getter. When a read-write property is being assigned to, we end up here, too, and
                    // select the wrong accessor. However, that doesn't matter because it will be over-
                    // ridden in the above `Assign` case. Finally, if a write-only property is being
                    // assigned to, we fall back to the setter here in order to not end up without a
                    // method at all.
                    p = new InvocationShape(
                        expression: Expression.Lambda(
                            Expression.MakeMemberAccess(parameter, property),
                            parameter),
                        method);
                    return;
                }

                default:
                    Debug.Assert(!CanSplit(e));
                    throw new InvalidOperationException();                              // this should be unreachable
                }
            }
        }
Ejemplo n.º 23
0
        /// <summary>
        ///   Splits an expression such as `<c>m => m.A.B(x).C[y] = z</c>` into a chain of parts
        ///   that can be set up one at a time:
        ///   <list>
        ///     <item>`<c>m => m.A</c>`</item>,
        ///     <item>`<c>... => ....B(x)</c>`</item>,
        ///     <item>`<c>... => ....C</c>`</item>,
        ///     <item>`<c>... => ...[y] = z</c>`</item>.
        ///   </list>
        ///   <para>
        ///     The split points are chosen such that each part has exactly one associated
        ///     <see cref="MethodInfo"/> and optionally some argument expressions.
        ///   </para>
        /// </summary>
        /// <exception cref="ArgumentException">
        ///   It was not possible to completely split up the expression.
        /// </exception>
        internal static Stack <InvocationShape> Split(this LambdaExpression expression)
        {
            Debug.Assert(expression != null);

            var parts = new Stack <InvocationShape>();

            Expression remainder = expression.Body;

            while (CanSplit(remainder))
            {
                Split(remainder, out remainder, out var part);
                parts.Push(part);
            }

            if (parts.Count > 0 && remainder is ParameterExpression)
            {
                return(parts);
            }
            else
            {
                throw new ArgumentException(
                          string.Format(
                              CultureInfo.CurrentCulture,
                              Resources.UnsupportedExpression,
                              remainder.ToStringFixed()));
            }

            void Split(Expression e, out Expression r /* remainder */, out InvocationShape p /* part */, bool assignment = false)
            {
                const string ParameterName = "...";

                switch (e.NodeType)
                {
                case ExpressionType.Assign:                                  // assignment to a property or indexer
                case ExpressionType.AddAssign:                               // subscription of event handler to event
                case ExpressionType.SubtractAssign:                          // unsubscription of event handler from event
                {
                    var assignmentExpression = (BinaryExpression)e;
                    Split(assignmentExpression.Left, out r, out var lhs, assignment: true);
                    var parameter = Expression.Parameter(r.Type, r is ParameterExpression ope ? ope.Name : ParameterName);
                    var arguments = new Expression[lhs.Method.GetParameters().Length];
                    for (var ai = 0; ai < arguments.Length - 1; ++ai)
                    {
                        arguments[ai] = lhs.Arguments[ai];
                    }
                    arguments[arguments.Length - 1] = assignmentExpression.Right;
                    p = new InvocationShape(
                        expression: Expression.Lambda(
                            Expression.MakeBinary(e.NodeType, lhs.Expression.Body, assignmentExpression.Right),
                            parameter),
                        method: lhs.Method,
                        arguments);
                    return;
                }

                case ExpressionType.Call:                          // regular method call
                {
                    var methodCallExpression = (MethodCallExpression)e;

                    if (methodCallExpression.Method.IsGenericMethod)
                    {
                        foreach (var typeArgument in methodCallExpression.Method.GetGenericArguments())
                        {
                            if (typeArgument.IsTypeMatcher(out var typeMatcherType))
                            {
                                Guard.ImplementsTypeMatcherProtocol(typeMatcherType);
                            }
                        }
                    }

                    if (!methodCallExpression.Method.IsStatic)
                    {
                        r = methodCallExpression.Object;
                        var parameter = Expression.Parameter(r.Type, r is ParameterExpression ope ? ope.Name : ParameterName);
                        var method    = methodCallExpression.Method;
                        var arguments = methodCallExpression.Arguments;
                        p = new InvocationShape(
                            expression: Expression.Lambda(
                                Expression.Call(parameter, method, arguments),
                                parameter),
                            method,
                            arguments);
                    }
                    else
                    {
                        Debug.Assert(methodCallExpression.Method.IsExtensionMethod());
                        Debug.Assert(methodCallExpression.Arguments.Count > 0);
                        r = methodCallExpression.Arguments[0];
                        var parameter = Expression.Parameter(r.Type, r is ParameterExpression ope ? ope.Name : ParameterName);
                        var method    = methodCallExpression.Method;
                        var arguments = methodCallExpression.Arguments.ToArray();
                        arguments[0] = parameter;
                        p            = new InvocationShape(
                            expression: Expression.Lambda(
                                Expression.Call(method, arguments),
                                parameter),
                            method,
                            arguments);
                    }
                    return;
                }

                case ExpressionType.Index:                          // indexer query
                {
                    var indexExpression = (IndexExpression)e;
                    r = indexExpression.Object;
                    var parameter = Expression.Parameter(r.Type, r is ParameterExpression ope ? ope.Name : ParameterName);
                    var indexer   = indexExpression.Indexer;
                    var arguments = indexExpression.Arguments;
                    var method    = !assignment && indexer.CanRead(out var getter)  ? getter
                                                           :                indexer.CanWrite(out var setter) ? setter
                                                           :                                                   null;
                    p = new InvocationShape(
                        expression: Expression.Lambda(
                            Expression.MakeIndex(parameter, indexer, arguments),
                            parameter),
                        method,
                        arguments,
                        skipMatcherInitialization: assignment);
                    return;
                }

                case ExpressionType.Invoke:                          // delegate invocation
                {
                    var invocationExpression = (InvocationExpression)e;
                    Debug.Assert(invocationExpression.Expression.Type.IsDelegateType());
                    r = invocationExpression.Expression;
                    var parameter = Expression.Parameter(r.Type, r is ParameterExpression ope ? ope.Name : ParameterName);
                    var arguments = invocationExpression.Arguments;
                    p = new InvocationShape(
                        expression: Expression.Lambda(
                            Expression.Invoke(parameter, arguments),
                            parameter),
                        method: r.Type.GetMethod("Invoke", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance),
                        arguments);
                    return;
                }

                case ExpressionType.MemberAccess:                          // property query
                {
                    var memberAccessExpression = (MemberExpression)e;
                    Debug.Assert(memberAccessExpression.Member is PropertyInfo);
                    r = memberAccessExpression.Expression;
                    var parameter = Expression.Parameter(r.Type, r is ParameterExpression ope ? ope.Name : ParameterName);
                    var property  = memberAccessExpression.GetReboundProperty();
                    var method    = !assignment && property.CanRead(out var getter)  ? getter
                                                           :                property.CanWrite(out var setter) ? setter
                                                           :                                                    null;
                    p = new InvocationShape(
                        expression: Expression.Lambda(
                            Expression.MakeMemberAccess(parameter, property),
                            parameter),
                        method,
                        skipMatcherInitialization: assignment);
                    return;
                }

                default:
                    Debug.Assert(!CanSplit(e));
                    throw new InvalidOperationException();                              // this should be unreachable
                }
            }
        }
Ejemplo n.º 24
0
 public SequenceSetup(Expression originalExpression, Mock mock, InvocationShape expectation)
     : base(originalExpression, mock, expectation)
 {
     this.responses = new ConcurrentQueue <Response>();
 }
Ejemplo n.º 25
0
 public SequenceSetup(InvocationShape expectation)
     : base(expectation)
 {
     this.responses = new ConcurrentQueue <Response>();
 }
Ejemplo n.º 26
0
 public SequenceSetup(Expression originalExpression, Mock mock, InvocationShape expectation)
     : base(originalExpression, mock, expectation)
 {
     this.behaviors = new ConcurrentQueue <Behavior>();
 }
Ejemplo n.º 27
0
 public static Setup TryFind(this IEnumerable <Setup> setups, InvocationShape expectation)
 {
     return(setups.FirstOrDefault(setup => setup.Expectation.Equals(expectation)));
 }
Ejemplo n.º 28
0
        /// <summary>
        ///   Splits an expression such as `<c>m => m.A.B(x).C[y] = z</c>` into a chain of parts
        ///   that can be set up one at a time:
        ///   <list>
        ///     <item>`<c>m => m.A</c>`</item>,
        ///     <item>`<c>... => ....B(x)</c>`</item>,
        ///     <item>`<c>... => ....C</c>`</item>,
        ///     <item>`<c>... => ...[y] = z</c>`</item>.
        ///   </list>
        ///   <para>
        ///     The split points are chosen such that each part has exactly one associated
        ///     <see cref="MethodInfo"/> and optionally some argument expressions.
        ///   </para>
        /// </summary>
        /// <exception cref="ArgumentException">
        ///   It was not possible to completely split up the expression.
        /// </exception>
        internal static Stack <InvocationShape> Split(this LambdaExpression expression, bool allowNonOverridableLastProperty = false)
        {
            Debug.Assert(expression != null);

            var parts = new Stack <InvocationShape>();

            Expression remainder = expression.Body;

            while (CanSplit(remainder))
            {
                Split(remainder, out remainder, out var part, allowNonOverridableLastProperty: allowNonOverridableLastProperty && parts.Count == 0);
                parts.Push(part);
            }

            if (parts.Count > 0 && remainder is ParameterExpression)
            {
                return(parts);
            }
            else
            {
                throw new ArgumentException(
                          string.Format(
                              CultureInfo.CurrentCulture,
                              Resources.UnsupportedExpression,
                              remainder.ToStringFixed()));
            }

            void Split(Expression e, out Expression r /* remainder */, out InvocationShape p /* part */, bool assignment = false, bool allowNonOverridableLastProperty = false)
            {
                const string ParameterName = "...";

                switch (e.NodeType)
                {
                case ExpressionType.Assign:                                  // assignment to a property or indexer
                case ExpressionType.AddAssign:                               // subscription of event handler to event
                case ExpressionType.SubtractAssign:                          // unsubscription of event handler from event
                {
                    var assignmentExpression = (BinaryExpression)e;
                    Split(assignmentExpression.Left, out r, out var lhs, assignment: true);
                    var parameter = Expression.Parameter(r.Type, r is ParameterExpression ope ? ope.Name : ParameterName);
                    var arguments = new Expression[lhs.Method.GetParameters().Length];
                    for (var ai = 0; ai < arguments.Length - 1; ++ai)
                    {
                        arguments[ai] = lhs.Arguments[ai];
                    }
                    arguments[arguments.Length - 1] = assignmentExpression.Right;
                    p = new InvocationShape(
                        expression: Expression.Lambda(
                            Expression.MakeBinary(e.NodeType, lhs.Expression.Body, assignmentExpression.Right),
                            parameter),
                        method: lhs.Method,
                        arguments);
                    return;
                }

                case ExpressionType.Call:                          // regular method call
                {
                    var methodCallExpression = (MethodCallExpression)e;

                    if (methodCallExpression.Method.IsGenericMethod)
                    {
                        foreach (var typeArgument in methodCallExpression.Method.GetGenericArguments())
                        {
                            if (typeArgument.IsOrContainsTypeMatcher())
                            {
                                // This is a (somewhat roundabout) way of ensuring that the type matchers used
                                // will be usable. They will not be usable if they don't implement the type
                                // matcher protocol correctly; and `SubstituteTypeMatchers` tests for that, so
                                // we'll reuse its recursive logic instead of having to reimplement our own.
                                _ = typeArgument.SubstituteTypeMatchers(typeArgument);
                            }
                        }
                    }

                    if (!methodCallExpression.Method.IsStatic)
                    {
                        r = methodCallExpression.Object;
                        var parameter = Expression.Parameter(r.Type, r is ParameterExpression ope ? ope.Name : ParameterName);
                        var method    = methodCallExpression.Method;
                        var arguments = methodCallExpression.Arguments;
                        p = new InvocationShape(
                            expression: Expression.Lambda(
                                Expression.Call(parameter, method, arguments),
                                parameter),
                            method,
                            arguments);
                    }
                    else
                    {
                        Debug.Assert(methodCallExpression.Method.IsExtensionMethod());
                        Debug.Assert(methodCallExpression.Arguments.Count > 0);
                        r = methodCallExpression.Arguments[0];
                        var parameter = Expression.Parameter(r.Type, r is ParameterExpression ope ? ope.Name : ParameterName);
                        var method    = methodCallExpression.Method;
                        var arguments = methodCallExpression.Arguments.ToArray();
                        arguments[0] = parameter;
                        p            = new InvocationShape(
                            expression: Expression.Lambda(
                                Expression.Call(method, arguments),
                                parameter),
                            method,
                            arguments);
                    }
                    return;
                }

                case ExpressionType.Index:                          // indexer query
                {
                    var indexExpression = (IndexExpression)e;
                    r = indexExpression.Object;
                    var        parameter = Expression.Parameter(r.Type, r is ParameterExpression ope ? ope.Name : ParameterName);
                    var        indexer   = indexExpression.Indexer;
                    var        arguments = indexExpression.Arguments;
                    MethodInfo method;
                    if (!assignment && indexer.CanRead(out var getter, out var getterIndexer))
                    {
                        method  = getter;
                        indexer = getterIndexer;
                    }
                    else if (indexer.CanWrite(out var setter, out var setterIndexer))
                    {
                        method  = setter;
                        indexer = setterIndexer;
                    }
                    else                              // This should be unreachable.
                    {
                        method = null;
                    }
                    p = new InvocationShape(
                        expression: Expression.Lambda(
                            Expression.MakeIndex(parameter, indexer, arguments),
                            parameter),
                        method,
                        arguments,
                        skipMatcherInitialization: assignment,
                        allowNonOverridable: allowNonOverridableLastProperty);
                    return;
                }

                case ExpressionType.Invoke:                          // delegate invocation
                {
                    var invocationExpression = (InvocationExpression)e;
                    Debug.Assert(invocationExpression.Expression.Type.IsDelegateType());
                    r = invocationExpression.Expression;
                    var parameter = Expression.Parameter(r.Type, r is ParameterExpression ope ? ope.Name : ParameterName);
                    var arguments = invocationExpression.Arguments;
                    p = new InvocationShape(
                        expression: Expression.Lambda(
                            Expression.Invoke(parameter, arguments),
                            parameter),
                        method: r.Type.GetMethod("Invoke", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance),
                        arguments);
                    return;
                }

                case ExpressionType.MemberAccess:                          // property query
                {
                    var memberAccessExpression = (MemberExpression)e;
                    Debug.Assert(memberAccessExpression.Member is PropertyInfo);
                    r = memberAccessExpression.Expression;
                    var        parameter = Expression.Parameter(r.Type, r is ParameterExpression ope ? ope.Name : ParameterName);
                    var        property  = memberAccessExpression.GetReboundProperty();
                    MethodInfo method;
                    if (!assignment && property.CanRead(out var getter, out var getterProperty))
                    {
                        method   = getter;
                        property = getterProperty;
                    }
                    else if (property.CanWrite(out var setter, out var setterProperty))
                    {
                        method   = setter;
                        property = setterProperty;
                    }
                    else                              // This should be unreachable.
                    {
                        method = null;
                    }
                    p = new InvocationShape(
                        expression: Expression.Lambda(
                            Expression.MakeMemberAccess(parameter, property),
                            parameter),
                        method,
                        skipMatcherInitialization: assignment,
                        allowNonOverridable: allowNonOverridableLastProperty);
                    return;
                }

                default:
                    Debug.Assert(!CanSplit(e));
                    throw new InvalidOperationException();                              // this should be unreachable
                }
            }
        }
Ejemplo n.º 29
0
 public SequenceSetup(FluentSetup fluentSetup, Mock mock, InvocationShape expectation)
     : base(fluentSetup, mock, expectation)
 {
     this.responses = new ConcurrentQueue <Response>();
 }