internal static MockCreationSettings GetSettings(object[] constructorArgs, Behavior?behavior, Type[] additionalMockedInterfaces, bool?mockConstructorCall, IEnumerable <CustomAttributeBuilder> additionalProxyTypeAttributes = null, List <IBehavior> supplementaryBehaviors = null, List <IBehavior> fallbackBehaviors = null, List <object> mixins = null, Expression <Predicate <MethodInfo> > interceptorFilter = null) { if (behavior == null) { behavior = DefaultBehavior; } MockCreationSettings settings = MockCreationSettings.DissectBehavior(behavior.Value, constructorArgs, mockConstructorCall); settings.AdditionalMockedInterfaces = additionalMockedInterfaces; settings.AdditionalProxyTypeAttributes = additionalProxyTypeAttributes; settings.InterceptorFilter = interceptorFilter; if (supplementaryBehaviors != null) { settings.SupplementaryBehaviors = new List <IBehavior>(settings.SupplementaryBehaviors.Concat(supplementaryBehaviors)); } if (fallbackBehaviors != null) { settings.FallbackBehaviors = new List <IBehavior>(settings.FallbackBehaviors.Concat(fallbackBehaviors)); } if (mixins != null) { settings.Mixins = new List <object>(settings.Mixins.Concat(mixins)); } return(settings); }
private ProxyGenerationOptions CreateProxyGenerationOptions(Type type, MockCreationSettings settings, MockMixin mockMixinImpl = null) { var options = new ProxyGenerationOptions(); if (mockMixinImpl != null) { options.AddMixinInstance(mockMixinImpl); } foreach (var mixin in settings.Mixins) { options.AddMixinInstance(mixin); } if (settings.AdditionalProxyTypeAttributes != null) { foreach (var attr in settings.AdditionalProxyTypeAttributes) { options.AdditionalAttributes.Add(attr); } } return(options); }
public object Create(Type type, MocksRepository repository, IMockMixin mockMixinImpl, MockCreationSettings settings, bool createTransparentProxy) { var options = new ProxyGenerationOptions(); options.AddMixinInstance(mockMixinImpl); foreach (var mixin in settings.Mixins) { options.AddMixinInstance(mixin); } if (settings.AdditionalProxyTypeAttributes != null) { foreach (var attr in settings.AdditionalProxyTypeAttributes) { options.AdditionalAttributes.Add(attr); } } var interceptor = createTransparentProxy ? (IInterceptor) new StandardInterceptor() : new DynamicProxyInterceptor(repository); #if SILVERLIGHT options.Hook = new ProxyGenerationHook(false, settings.InterceptorFilter); #else options.Hook = new ProxyGenerationHook(settings.MockConstructorCall, settings.InterceptorFilter); #endif object instance = null; Exception proxyFailure = null; if (type.IsInterface) { if (settings.Args != null && settings.Args.Length > 0) { throw new ArgumentException("Do not supply contructor arguments when mocking an interface or delegate."); } try { instance = generator.CreateInterfaceProxyWithoutTarget(type, settings.AdditionalMockedInterfaces, options, interceptor); } catch (TypeLoadException ex) { proxyFailure = ex; } catch (GeneratorException ex) { proxyFailure = ex; } } else { try { #if SILVERLIGHT if (settings.Args == null || settings.Args.Length == 0) { ConstructorInfo[] constructors = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!constructors.Any(constr => constr.GetParameters().Length == 0)) { var constructorToCall = constructors.FirstOrDefault(); if (constructorToCall != null) { var @params = constructorToCall.GetParameters(); settings.Args = new object[@params.Length]; for (int i = 0; i < @params.Length; ++i) { var p = @params[i]; settings.Args[i] = Convert.IsDBNull(p.DefaultValue) ? p.ParameterType.GetDefaultValue() : p.DefaultValue; } } } } #endif instance = generator.CreateClassProxy(type, settings.AdditionalMockedInterfaces, options, settings.Args, interceptor); } catch (TypeLoadException ex) { proxyFailure = ex; } catch (GeneratorException ex) { proxyFailure = ex; } catch (InvalidProxyConstructorArgumentsException ex) { proxyFailure = ex; if (!settings.MockConstructorCall) { throw new MockException(ex.Message); } } } if (proxyFailure != null) { throw new ProxyFailureException(proxyFailure); } return(instance); }
public ProxyTypeInfo CreateClassProxyType(Type classToProxy, MocksRepository repository, MockCreationSettings settings, MockMixin mockMixinImpl) { var pgo = CreateProxyGenerationOptions(classToProxy, settings, mockMixinImpl); var typeInfo = new ProxyTypeInfo { ProxyType = generator.ProxyBuilder.CreateClassProxyType(classToProxy, Type.EmptyTypes, pgo) }; typeInfo.Mixins.Add(typeof(IInterceptor), new DynamicProxyInterceptor(repository)); foreach (var mixin in pgo.MixinData.MixinInterfaces) { typeInfo.Mixins.Add(mixin, pgo.MixinData.GetMixinInstance(mixin)); } return(typeInfo); }
internal static CallPattern FromExpression(MocksRepository repository, Expression expr) { var callPattern = new CallPattern(); expr = ((LambdaExpression)expr).Body; // the expression may end with a boxing conversion, remove that while (expr.NodeType == ExpressionType.Convert) { expr = ((UnaryExpression)expr).Operand; } Expression target; MethodBase method = null; Expression[] args; // We're parsing either a property/field expression or a method call. // parse the top of the expression tree and extract the expressions // that will turn into the constituents of the call pattern. if (expr is MemberExpression) { var memberExpr = (MemberExpression)expr; if (!(memberExpr.Member is PropertyInfo)) { throw new MockException("Fields cannot be mocked, only properties."); } var property = (PropertyInfo)memberExpr.Member; target = memberExpr.Expression; method = property.GetGetMethod(true); args = null; } else if (expr is MethodCallExpression) { var methodCall = (MethodCallExpression)expr; method = methodCall.Method; target = methodCall.Object; args = methodCall.Arguments.ToArray(); if (target != null && !target.Type.IsInterface && !target.Type.IsProxy() && target.Type != method.DeclaringType) { method = MockingUtil.GetConcreteImplementer((MethodInfo)method, target.Type); } } else if (expr is NewExpression) { #if !PORTABLE if (ProfilerInterceptor.IsReJitEnabled) { throw new MockException("Mocking the new operator is not avaiable with OnDemand option enabled. Please use .IgnoreInstance()"); } #endif var newExpr = (NewExpression)expr; method = newExpr.Constructor; if (method == null && newExpr.Type.IsValueType) { throw new MockException("Empty constructor of value type is not associated with runnable code and cannot be intercepted."); } target = null; args = newExpr.Arguments.ToArray(); } else if (expr is InvocationExpression) { var invocation = (InvocationExpression)expr; target = invocation.Expression; args = invocation.Arguments.ToArray(); } else if (expr.NodeType == ExpressionType.Assign) { var binary = (BinaryExpression)expr; if (binary.Left is MemberExpression) { MemberExpression memberExpr = (MemberExpression)binary.Left; if (!(memberExpr.Member is PropertyInfo)) { throw new MockException("Fields cannot be mocked, only properties."); } var property = (PropertyInfo)memberExpr.Member; target = memberExpr.Expression; method = property.GetSetMethod(true); args = new[] { binary.Right }; } else if (binary.Left is IndexExpression) { IndexExpression indexExpr = (IndexExpression)binary.Left; target = indexExpr.Object; method = indexExpr.Indexer.GetSetMethod(true); args = indexExpr.Arguments.Concat(new[] { binary.Right }).ToArray(); } else { throw new MockException("Left-hand of assignment is not a member or indexer."); } } else if (expr is IndexExpression) { var index = (IndexExpression)expr; target = index.Object; var property = index.Indexer; method = property.GetGetMethod(true); args = index.Arguments.ToArray(); } else { throw new MockException("The expression does not represent a method call, property access, new expression or a delegate invocation."); } // Create the matcher for the instance part of the call pattern. // If the base of the target expression is a new expression (new T()), // or null (e.g. (null as T) or ((T) null)), then use AnyMatcher for the instance part, // otherwise evaluate the instance expression and use a value matcher with the evaluated result. var rootTarget = expr; Expression prevToRoot = null; while (true) { var memberExpr = rootTarget as MemberExpression; if (memberExpr != null && memberExpr.Expression != null && memberExpr.Member is PropertyInfo) { prevToRoot = rootTarget; rootTarget = memberExpr.Expression; continue; } var callExpr = rootTarget as MethodCallExpression; if (callExpr != null && callExpr.Object != null) { prevToRoot = rootTarget; rootTarget = callExpr.Object; continue; } if (rootTarget != null && (rootTarget.NodeType == ExpressionType.Convert || rootTarget.NodeType == ExpressionType.TypeAs)) { rootTarget = ((UnaryExpression)rootTarget).Operand; continue; } if (rootTarget is InvocationExpression) { prevToRoot = rootTarget; rootTarget = ((InvocationExpression)rootTarget).Expression; continue; } if (rootTarget is BinaryExpression) { prevToRoot = rootTarget; rootTarget = ((BinaryExpression)rootTarget).Left; continue; } if (rootTarget is IndexExpression) { prevToRoot = rootTarget; rootTarget = ((IndexExpression)rootTarget).Object; continue; } break; } object targetMockObject = null; Type targetMockType = null; bool isStatic = false; var rootMatcher = MocksRepository.TryCreateMatcherFromArgMember(rootTarget); if (rootMatcher != null) { callPattern.InstanceMatcher = rootMatcher; } else if (rootTarget is MemberExpression) { var memberExpr = (MemberExpression)rootTarget; targetMockObject = memberExpr.Member is FieldInfo?memberExpr.EvaluateExpression() : memberExpr.Expression != null?memberExpr.Expression.EvaluateExpression() : null; targetMockType = memberExpr.Member is FieldInfo ? memberExpr.Type : memberExpr.Member.DeclaringType; var asPropertyInfo = memberExpr.Member as PropertyInfo; isStatic = asPropertyInfo != null ? (asPropertyInfo.GetGetMethod(true) ?? asPropertyInfo.GetSetMethod(true)).IsStatic : false; } else if (rootTarget is MethodCallExpression) { var methodCallExpr = (MethodCallExpression)rootTarget; targetMockObject = methodCallExpr.Object != null?methodCallExpr.Object.EvaluateExpression() : null; targetMockType = methodCallExpr.Method.DeclaringType; isStatic = methodCallExpr.Method.IsStatic; } else if (rootTarget is NewExpression) { callPattern.InstanceMatcher = new AnyMatcher(); } else if (rootTarget is ConstantExpression) { var constant = (ConstantExpression)rootTarget; if (constant.Value == null) { callPattern.InstanceMatcher = new AnyMatcher(); } else { if (constant.Type.IsCompilerGenerated() && prevToRoot != null && prevToRoot.Type != typeof(void)) { targetMockObject = prevToRoot.EvaluateExpression(); targetMockType = prevToRoot.Type; } else { targetMockObject = constant.Value; targetMockType = constant.Type; } } } if (targetMockObject != null) { targetMockType = targetMockObject.GetType(); } if (callPattern.InstanceMatcher != null && prevToRoot != expr && prevToRoot != null) { throw new MockException("Using a matcher for the root member together with recursive mocking is not supported. Arrange the property or method of the root member in a separate statement."); } if (callPattern.InstanceMatcher == null) { // TODO: implicit creation of mock mixins shouldn't explicitly refer to behaviors, but // should get them from some configuration made outside the Core. Debug.Assert(targetMockObject != null || targetMockType != null); MockingUtil.UnwrapDelegateTarget(ref targetMockObject); var mixin = MocksRepository.GetMockMixin(targetMockObject, targetMockType); if (mixin == null) { if (isStatic) { MockCreationSettings settings = MockCreationSettings.GetSettings(Behavior.CallOriginal); repository.InterceptStatics(targetMockType, settings, false); } else if (targetMockObject != null) { MockCreationSettings settings = MockCreationSettings.GetSettings(Behavior.CallOriginal); repository.CreateExternalMockMixin(targetMockType, targetMockObject, settings); } } var targetValue = target != null?target.EvaluateExpression() : null; var delgMethod = MockingUtil.UnwrapDelegateTarget(ref targetValue); if (delgMethod != null) { method = delgMethod.GetInheritanceChain().First(m => !m.DeclaringType.IsProxy()); } callPattern.InstanceMatcher = new ReferenceMatcher(targetValue); } // now we have the method part of the call pattern Debug.Assert(method != null); callPattern.SetMethod(method, checkCompatibility: true); //Finally, construct the arguments part of the call pattern. using (repository.StartArrangeArgMatching()) { bool hasParams = false; bool hasSingleValueInParams = false; if (args != null && args.Length > 0) { var lastParameter = method.GetParameters().Last(); if (Attribute.IsDefined(lastParameter, typeof(ParamArrayAttribute)) && args.Last() is NewArrayExpression) { hasParams = true; var paramsArg = (NewArrayExpression)args.Last(); args = args.Take(args.Length - 1).Concat(paramsArg.Expressions).ToArray(); if (paramsArg.Expressions.Count == 1) { hasSingleValueInParams = true; } } foreach (var argument in args) { callPattern.ArgumentMatchers.Add(MocksRepository.CreateMatcherForArgument(argument)); } if (hasParams) { int paramsCount = method.GetParameters().Count(); if (hasSingleValueInParams) { IMatcher matcher = callPattern.ArgumentMatchers[paramsCount - 1]; ITypedMatcher typeMatcher = matcher as ITypedMatcher; if (typeMatcher != null && typeMatcher.Type != method.GetParameters().Last().ParameterType) { callPattern.ArgumentMatchers[paramsCount - 1] = new ParamsMatcher(new IMatcher[] { matcher }); } } else { IEnumerable <IMatcher> paramMatchers = callPattern.ArgumentMatchers.Skip(paramsCount - 1).Take(callPattern.ArgumentMatchers.Count - paramsCount + 1); callPattern.ArgumentMatchers = callPattern.ArgumentMatchers.Take(paramsCount - 1).ToList(); callPattern.ArgumentMatchers.Add(new ParamsMatcher(paramMatchers.ToArray())); } } } } MethodBase methodFromCallPattern = repository.GetMethodFromCallPattern(callPattern); callPattern.AdjustForExtensionMethod(); callPattern.SetMethod(methodFromCallPattern, checkCompatibility: false); return(callPattern); }
/// <summary> /// Creates instance <see cref="MockCreationSettings"/> for the specified behavior. /// </summary> /// <param name="behavior">Behavior for which to create the settings</param> /// <returns><see cref="MockCreationSettings"/> instance</returns> internal static MockCreationSettings GetSettings(Behavior behavior) { MockCreationSettings settings = MockCreationSettings.DissectBehavior(behavior, constructorArgs: null, mockConstructorCall: null); return(settings); }