public void AddInvocationExecutor(IInvocation invocation, INMockitoSmartParameter[] smartParameters, IInvocationExecutor executor) { var method = invocation.Method; var genericArguments = invocation.GenericArguments; var methodParameters = method.GetParameters(); var invocationArguments = invocation.Arguments; // Flatten params[] of invocation arguments if (methodParameters.Length > 0 && methodParameters.Last().GetCustomAttributes(typeof(ParamArrayAttribute), false).Length > 0 && invocationArguments.Last() != null) { var paramsArray = (Array)invocationArguments.Last(); var newInvocationArguments = new object[invocationArguments.Length + paramsArray.Length - 1]; var cutIndex = invocationArguments.Length - 1; Array.Copy(invocationArguments, newInvocationArguments, cutIndex); Array.Copy(paramsArray, 0, newInvocationArguments, cutIndex, paramsArray.Length); invocationArguments = newInvocationArguments; } // Convert all invocation arguments into eq(arg[i]) smart parameters if (smartParameters.Length == 0) { smartParameters = ConvertInvocationArgumentsToEqualityParameters(invocationArguments); } // Find out/ref parameters, swap them with null and record the index => smart parameter replacement. var refReplacementsByIndex = new List <KeyValuePair <int, object> >(); for (var i = 0; i < methodParameters.Length; i++) { var parameter = methodParameters[i]; if (parameter.Attributes.HasFlag(ParameterAttributes.Out)) { var replacement = invocation.Arguments[i]; smartParameters[i] = null; refReplacementsByIndex.Add(new KeyValuePair <int, object>(i, replacement)); } } var tracker = trackerByArguments.FirstOrDefault(kvp => kvp.Key.Item1 == method && kvp.Key.Item2 == genericArguments && SmartParametersEqual(kvp.Key.Item3, smartParameters)).Value; if (tracker == null) { tracker = new InvocationResultTracker(GetDefaultValue(method.ReturnType), refReplacementsByIndex); var key = new Tuple <MethodInfo, Type[], INMockitoSmartParameter[]>(method, genericArguments, smartParameters); trackerByArguments.Add(new KeyValuePair <Tuple <MethodInfo, Type[], INMockitoSmartParameter[]>, InvocationResultTracker>(key, tracker)); } tracker.AddResult(executor); }
private object GetInvocationResult(IInvocation invocation) { // Flatten params[] of invocation arguments var method = invocation.Method; var methodParameters = method.GetParameters(); var invocationArguments = invocation.Arguments; if (methodParameters.Length > 0 && methodParameters.Last().GetCustomAttributes(typeof(ParamArrayAttribute), false).Length > 0 && invocationArguments.Last() != null) { var paramsArray = (Array)invocationArguments.Last(); var newInvocationArguments = new object[invocationArguments.Length + paramsArray.Length - 1]; var cutIndex = invocationArguments.Length - 1; Array.Copy(invocationArguments, newInvocationArguments, cutIndex); Array.Copy(paramsArray, 0, newInvocationArguments, cutIndex, paramsArray.Length); invocationArguments = newInvocationArguments; } // Try to find our invocation result tracker... can't use dictionary comparers InvocationResultTracker tracker = null; foreach (var kvp in trackerByArguments) { if (invocation.Method == kvp.Key.Item1 && ((invocation.GenericArguments == null && kvp.Key.Item2 == null) || Enumerable.SequenceEqual(invocation.GenericArguments, kvp.Key.Item2)) && ((invocationArguments == null && kvp.Key.Item3 == null) || invocationArguments.Length == kvp.Key.Item3.Length)) { bool invocationMatching = true; for (var i = 0; i < invocationArguments.Length && invocationMatching; i++) { invocationMatching &= kvp.Key.Item3[i] == null || kvp.Key.Item3[i].Test(invocationArguments[i]); } if (invocationMatching) { tracker = kvp.Value; break; } } } var returnValue = GetDefaultValue(invocation.Method.ReturnType); if (tracker != null) { // replace smart parameter with their placeholders var refReplacementsByIndex = tracker.RefReplacementsByIndex; foreach (var kvp in refReplacementsByIndex) { invocation.Arguments[kvp.Key] = kvp.Value; } var currentExecutor = tracker.NextResult(); while (true) { returnValue = currentExecutor.Execute(invocation); if (currentExecutor.IsTerminal) { break; } else { currentExecutor = tracker.NextResult(); } } // replace smart parameters' placeholders with default value, if they're not swapped out foreach (var kvp in refReplacementsByIndex) { if (ReferenceEquals(invocation.Arguments[kvp.Key], kvp.Value)) { var parameterType = invocation.Method.GetParameters()[kvp.Key].ParameterType; // If we have an 'out int', for example, the type is actually a by-ref int&. if (parameterType.IsByRef) { parameterType = parameterType.GetElementType(); } if (parameterType.IsValueType) { invocation.Arguments[kvp.Key] = Activator.CreateInstance(parameterType); } else { invocation.Arguments[kvp.Key] = null; } } } } return(returnValue); }