Skip to content

Commit

Permalink
Merge pull request #1035 from stakx/refactor-to-behaviors
Browse files Browse the repository at this point in the history
Convert `SequenceSetup` to use `Behavior`s extracted from `MethodCall`
  • Loading branch information
stakx committed Jun 28, 2020
2 parents 7b696ff + 00a96d0 commit be325ce
Show file tree
Hide file tree
Showing 20 changed files with 512 additions and 493 deletions.
14 changes: 14 additions & 0 deletions src/Moq/Behavior.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD, and Contributors.
// All rights reserved. Licensed under the BSD 3-Clause License; see License.txt.

namespace Moq
{
internal abstract class Behavior
{
protected Behavior()
{
}

public abstract void Execute(Invocation invocation);
}
}
25 changes: 25 additions & 0 deletions src/Moq/Behaviors/Callback.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD, and Contributors.
// All rights reserved. Licensed under the BSD 3-Clause License; see License.txt.

using System;
using System.Diagnostics;

namespace Moq.Behaviors
{
internal sealed class Callback : Behavior
{
private readonly Action<IInvocation> callback;

public Callback(Action<IInvocation> callback)
{
Debug.Assert(callback != null);

this.callback = callback;
}

public override void Execute(Invocation invocation)
{
this.callback.Invoke(invocation);
}
}
}
41 changes: 41 additions & 0 deletions src/Moq/Behaviors/LimitInvocationCount.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD, and Contributors.
// All rights reserved. Licensed under the BSD 3-Clause License; see License.txt.

namespace Moq.Behaviors
{
internal sealed class LimitInvocationCount : Behavior
{
private readonly MethodCall setup;
private readonly int maxCount;
private int count;

public LimitInvocationCount(MethodCall setup, int maxCount)
{
this.setup = setup;
this.maxCount = maxCount;
this.count = 0;
}

public void Reset()
{
this.count = 0;
}

public override void Execute(Invocation invocation)
{
++this.count;

if (this.count > this.maxCount)
{
if (this.maxCount == 1)
{
throw MockException.MoreThanOneCall(this.setup, this.count);
}
else
{
throw MockException.MoreThanNCalls(this.setup, this.maxCount, this.count);
}
}
}
}
}
18 changes: 18 additions & 0 deletions src/Moq/Behaviors/NoOp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD, and Contributors.
// All rights reserved. Licensed under the BSD 3-Clause License; see License.txt.

namespace Moq.Behaviors
{
internal sealed class NoOp : Behavior
{
public static readonly NoOp Instance = new NoOp();

private NoOp()
{
}

public override void Execute(Invocation invocation)
{
}
}
}
53 changes: 53 additions & 0 deletions src/Moq/Behaviors/RaiseEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD, and Contributors.
// All rights reserved. Licensed under the BSD 3-Clause License; see License.txt.

using System;
using System.Diagnostics;
using System.Linq.Expressions;

namespace Moq.Behaviors
{
internal sealed class RaiseEvent : Behavior
{
private Mock mock;
private LambdaExpression expression;
private Delegate eventArgsFunc;
private object[] eventArgsParams;

public RaiseEvent(Mock mock, LambdaExpression expression, Delegate eventArgsFunc, object[] eventArgsParams)
{
Debug.Assert(mock != null);
Debug.Assert(expression != null);
Debug.Assert(eventArgsFunc != null ^ eventArgsParams != null);

this.mock = mock;
this.expression = expression;
this.eventArgsFunc = eventArgsFunc;
this.eventArgsParams = eventArgsParams;
}

public override void Execute(Invocation invocation)
{
object[] args;

if (this.eventArgsParams != null)
{
args = this.eventArgsParams;
}
else
{
var argsFuncType = this.eventArgsFunc.GetType();
if (argsFuncType.IsGenericType && argsFuncType.GetGenericArguments().Length == 1)
{
args = new object[] { this.mock.Object, this.eventArgsFunc.InvokePreserveStack() };
}
else
{
args = new object[] { this.mock.Object, this.eventArgsFunc.InvokePreserveStack(invocation.Arguments) };
}
}

Mock.RaiseEvent(this.mock, this.expression, this.expression.Split(), args);
}
}
}
19 changes: 19 additions & 0 deletions src/Moq/Behaviors/ReturnBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD, and Contributors.
// All rights reserved. Licensed under the BSD 3-Clause License; see License.txt.

namespace Moq.Behaviors
{
internal sealed class ReturnBase : Behavior
{
public static readonly ReturnBase Instance = new ReturnBase();

private ReturnBase()
{
}

public override void Execute(Invocation invocation)
{
invocation.ReturnValue = invocation.CallBase();
}
}
}
93 changes: 93 additions & 0 deletions src/Moq/Behaviors/ReturnBaseOrDefaultValue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD, and Contributors.
// All rights reserved. Licensed under the BSD 3-Clause License; see License.txt.

using System.Diagnostics;
using System.Linq;

namespace Moq.Behaviors
{
internal sealed class ReturnBaseOrDefaultValue : Behavior
{
private readonly Mock mock;

public ReturnBaseOrDefaultValue(Mock mock)
{
Debug.Assert(mock != null);

this.mock = mock;
}

public override void Execute(Invocation invocation)
{
Debug.Assert(invocation.Method != null);
Debug.Assert(invocation.Method.ReturnType != null);

var method = invocation.Method;

if (this.mock.CallBase)
{
var declaringType = method.DeclaringType;
if (declaringType.IsInterface)
{
if (this.mock.MockedType.IsInterface)
{
// Case 1: Interface method of an interface proxy.
// There is no base method to call, so fall through.
}
else
{
Debug.Assert(mock.MockedType.IsClass);
Debug.Assert(mock.ImplementsInterface(declaringType));

// Case 2: Explicitly implemented interface method of a class proxy.

if (this.mock.InheritedInterfaces.Contains(declaringType))
{
// Case 2a: Re-implemented interface.
// The base class has its own implementation. Only call base method if it isn't an event accessor.
if (!method.IsEventAddAccessor() && !method.IsEventRemoveAccessor())
{
invocation.ReturnValue = invocation.CallBase();
return;
}
}
else
{
Debug.Assert(this.mock.AdditionalInterfaces.Contains(declaringType));

// Case 2b: Additional interface.
// There is no base method to call, so fall through.
}
}
}
else
{
Debug.Assert(declaringType.IsClass);

// Case 3: Non-interface method of a class proxy.
// Only call base method if it isn't abstract.
if (!method.IsAbstract)
{
invocation.ReturnValue = invocation.CallBase();
return;
}
}
}

if (method.ReturnType != typeof(void))
{
var returnValue = this.mock.GetDefaultValue(method, out var innerMock);
if (innerMock != null && invocation.MatchingSetup == null)
{
var setup = new InnerMockSetup(originalExpression: null, this.mock, expectation: InvocationShape.CreateFrom(invocation), returnValue);
this.mock.MutableSetups.Add(setup);
setup.Execute(invocation);
}
else
{
invocation.ReturnValue = returnValue;
}
}
}
}
}
25 changes: 25 additions & 0 deletions src/Moq/Behaviors/ReturnComputedValue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD, and Contributors.
// All rights reserved. Licensed under the BSD 3-Clause License; see License.txt.

using System;
using System.Diagnostics;

namespace Moq.Behaviors
{
internal sealed class ReturnComputedValue : Behavior
{
private readonly Func<IInvocation, object> valueFactory;

public ReturnComputedValue(Func<IInvocation, object> valueFactory)
{
Debug.Assert(valueFactory != null);

this.valueFactory = valueFactory;
}

public override void Execute(Invocation invocation)
{
invocation.ReturnValue = this.valueFactory.Invoke(invocation);
}
}
}
22 changes: 22 additions & 0 deletions src/Moq/Behaviors/ReturnValue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD, and Contributors.
// All rights reserved. Licensed under the BSD 3-Clause License; see License.txt.

namespace Moq.Behaviors
{
internal sealed class ReturnValue : Behavior
{
private readonly object value;

public ReturnValue(object value)
{
this.value = value;
}

public object Value => this.value;

public override void Execute(Invocation invocation)
{
invocation.ReturnValue = this.value;
}
}
}
25 changes: 25 additions & 0 deletions src/Moq/Behaviors/ThrowException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD, and Contributors.
// All rights reserved. Licensed under the BSD 3-Clause License; see License.txt.

using System;
using System.Diagnostics;

namespace Moq.Behaviors
{
internal sealed class ThrowException : Behavior
{
private readonly Exception exception;

public ThrowException(Exception exception)
{
Debug.Assert(exception != null);

this.exception = exception;
}

public override void Execute(Invocation invocation)
{
throw this.exception;
}
}
}

0 comments on commit be325ce

Please sign in to comment.