Skip to content

added overload to Verify to accept Times as a Method Group #34

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 28, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions Source.Silverlight/Moq.Silverlight.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,15 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\Lib\Castle\bin-SL4\Castle.Core.dll</HintPath>
</Reference>
<!--<Reference Include="Castle.Core, Version=1.2.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<!--<Reference Include="Castle.Core, Version=1.2.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\Lib\Castle\sl-30\Castle.Core.dll</HintPath>
</Reference>
<Reference Include="Castle.DynamicProxy2, Version=1.2.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\Lib\Castle\sl-30\Castle.DynamicProxy2.dll</HintPath>
</Reference>-->
<Reference Include="mscorlib" />
<Reference Include="mscorlib" />
<Reference Include="system" />
<Reference Include="System.Core" />
</ItemGroup>
Expand Down Expand Up @@ -138,6 +138,9 @@
<Compile Include="..\Source\IHideObjectMembers.cs">
<Link>IHideObjectMembers.cs</Link>
</Compile>
<Compile Include="..\Source\IInterceptStrategy.cs">
<Link>IInterceptStrategy.cs</Link>
</Compile>
<Compile Include="..\Source\IMatcher.cs">
<Link>IMatcher.cs</Link>
</Compile>
Expand All @@ -147,6 +150,9 @@
<Compile Include="..\Source\Interceptor.cs">
<Link>Interceptor.cs</Link>
</Compile>
<Compile Include="..\Source\InterceptorStrategies.cs">
<Link>InterceptorStrategies.cs</Link>
</Compile>
<Compile Include="..\Source\IProxyCall.cs">
<Link>IProxyCall.cs</Link>
</Compile>
Expand Down
52 changes: 52 additions & 0 deletions Source/IInterceptStrategy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Moq.Proxy;

namespace Moq
{

internal enum InterceptionAction
{
Continue,Stop
}
internal interface IInterceptStrategy
{
/// <summary>
/// Handle interception
/// </summary>
/// <param name="invocation">the current invocation context</param>
/// <param name="ctx">shared data among the strategies during an interception</param>
/// <returns>true if further interception has to be processed, otherwise false</returns>
InterceptionAction HandleIntercept(ICallContext invocation, InterceptStrategyContext ctx);

}

internal class InterceptStrategyContext
{
public InterceptStrategyContext(Mock Mock
, Type targetType
, Dictionary<string, List<Delegate>> invocationLists
, List<ICallContext> actualInvocations
, MockBehavior behavior
, List<IProxyCall> orderedCalls
)
{
this.Behavior = behavior;
this.Mock = Mock;
this.InvocationLists = invocationLists;
this.ActualInvocations = actualInvocations;
this.TargetType = targetType;
this.OrderedCalls = orderedCalls;
}
public Mock Mock {get;private set;}
public Type TargetType { get; private set; }
public Dictionary<string, List<Delegate>> InvocationLists { get; private set; }
public List<ICallContext> ActualInvocations { get; private set; }
public MockBehavior Behavior { get; private set; }
public List<IProxyCall> OrderedCalls { get; private set; }
public IProxyCall CurrentCall { get; set; }
}

}
216 changes: 25 additions & 191 deletions Source/Interceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,207 +130,41 @@ public void AddCall(IProxyCall call, SetupKind kind)
orderedCalls.Add(call);
}

private IEnumerable<IInterceptStrategy> InterceptionStrategies()
{
yield return new HandleDestructor();
yield return new HandleTracking();
yield return new CheckMockMixing();
yield return new AddActualInvocation();
yield return new ExtractProxyCall();
yield return new ExecuteCall();
yield return new InvokeBase();
yield return new HandleMockRecursion();
}

[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
public void Intercept(ICallContext invocation)
{
if (invocation.Method.IsDestructor())

lock (Mock) // this solves issue #249
{
return;
}

// Track current invocation if we're in "record" mode in a fluent invocation context.
if (FluentMockContext.IsActive)
{
FluentMockContext.Current.Add(this.Mock, invocation);
}
lock (Mock) // this solves issue #249, but actually worsen method complexity :(
{
// TODO: too many ifs in this method.
// see how to refactor with strategies.
if (invocation.Method.DeclaringType.IsGenericType &&
invocation.Method.DeclaringType.GetGenericTypeDefinition() == typeof(IMocked<>))
{
// "Mixin" of IMocked<T>.Mock
invocation.ReturnValue = this.Mock;
return;
}
else if (invocation.Method.DeclaringType == typeof(IMocked))
{
// "Mixin" of IMocked.Mock
invocation.ReturnValue = this.Mock;
return;
}

// Special case for events.
if (!FluentMockContext.IsActive)
{
if (invocation.Method.IsEventAttach())
{
var delegateInstance = (Delegate)invocation.Arguments[0];
// TODO: validate we can get the event?
var eventInfo = this.GetEventFromName(invocation.Method.Name.Substring(4));

if (this.Mock.CallBase)
{
invocation.InvokeBase();
}
else if (delegateInstance != null)
{
this.AddEventHandler(eventInfo, (Delegate)invocation.Arguments[0]);
}

return;
}
else if (invocation.Method.IsEventDetach())
{
var delegateInstance = (Delegate)invocation.Arguments[0];
// TODO: validate we can get the event?
var eventInfo = this.GetEventFromName(invocation.Method.Name.Substring(7));

if (this.Mock.CallBase)
{
invocation.InvokeBase();
}
else if (delegateInstance != null)
{
this.RemoveEventHandler(eventInfo, (Delegate)invocation.Arguments[0]);
}

return;
}

// Save to support Verify[expression] pattern.
// In a fluent invocation context, which is a recorder-like
// mode we use to evaluate delegates by actually running them,
// we don't want to count the invocation, or actually run
// previous setups.
actualInvocations.Add(invocation);
}

var call = FluentMockContext.IsActive ? (IProxyCall)null : orderedCalls.LastOrDefault(c => c.Matches(invocation));
if (call == null && !FluentMockContext.IsActive && behavior == MockBehavior.Strict)
{
throw new MockException(MockException.ExceptionReason.NoSetup, behavior, invocation);
}

if (call != null)
{
call.SetOutParameters(invocation);

// We first execute, as there may be a Throws
// and therefore we might never get to the
// next line.
call.Execute(invocation);
ThrowIfReturnValueRequired(call, invocation);
}
else if (invocation.Method.DeclaringType == typeof(object))
var interceptionContext = new InterceptStrategyContext(Mock
, targetType
, invocationLists
, actualInvocations
, behavior
, orderedCalls
);
foreach (var strategy in InterceptionStrategies())
{
// Invoke underlying implementation.
invocation.InvokeBase();
}
else if (invocation.Method.DeclaringType.IsClass && !invocation.Method.IsAbstract && this.Mock.CallBase)
{
// For mocked classes, if the target method was not abstract,
// invoke directly.
// Will only get here for Loose behavior.
// TODO: we may want to provide a way to skip this by the user.
invocation.InvokeBase();
}
else if (invocation.Method != null && invocation.Method.ReturnType != null &&
invocation.Method.ReturnType != typeof(void))
{
Mock recursiveMock;
if (this.Mock.InnerMocks.TryGetValue(invocation.Method, out recursiveMock))
if (InterceptionAction.Stop == strategy.HandleIntercept(invocation, interceptionContext))
{
invocation.ReturnValue = recursiveMock.Object;
}
else
{
invocation.ReturnValue = this.Mock.DefaultValueProvider.ProvideDefault(invocation.Method);
break;
}
}
}
}

/// <summary>
/// Get an eventInfo for a given event name. Search type ancestors depth first if necessary.
/// </summary>
/// <param name="eventName">Name of the event, with the set_ or get_ prefix already removed</param>
private EventInfo GetEventFromName(string eventName)
{
var depthFirstProgress = new Queue<Type>(this.Mock.ImplementedInterfaces.Skip(1));
depthFirstProgress.Enqueue(targetType);
while (depthFirstProgress.Count > 0)
{
var currentType = depthFirstProgress.Dequeue();
var eventInfo = currentType.GetEvent(eventName);
if (eventInfo != null)
{
return eventInfo;
}

foreach (var implementedType in GetAncestorTypes(currentType))
{
depthFirstProgress.Enqueue(implementedType);
}
}

return null;
}

/// <summary>
/// Given a type return all of its ancestors, both types and interfaces.
/// </summary>
/// <param name="initialType">The type to find immediate ancestors of</param>
private static IEnumerable<Type> GetAncestorTypes(Type initialType)
{
var baseType = initialType.BaseType;
if (baseType != null)
{
return new[] { baseType };
}

return initialType.GetInterfaces();
}

private void ThrowIfReturnValueRequired(IProxyCall call, ICallContext invocation)
{
if (behavior != MockBehavior.Loose &&
invocation.Method != null &&
invocation.Method.ReturnType != null &&
invocation.Method.ReturnType != typeof(void))
{
var methodCall = call as MethodCallReturn;
if (methodCall == null || !methodCall.HasReturnValue)
{
throw new MockException(
MockException.ExceptionReason.ReturnValueRequired,
behavior,
invocation);
}
}
}

internal void AddEventHandler(EventInfo ev, Delegate handler)
{
List<Delegate> handlers;
if (!this.invocationLists.TryGetValue(ev.Name, out handlers))
{
handlers = new List<Delegate>();
invocationLists.Add(ev.Name, handlers);
}

handlers.Add(handler);
}

internal void RemoveEventHandler(EventInfo ev, Delegate handler)
{
List<Delegate> handlers;
if (this.invocationLists.TryGetValue(ev.Name, out handlers))
{
handlers.Remove(handler);
}
}


internal IEnumerable<Delegate> GetInvocationList(EventInfo ev)
{
Expand Down
Loading