Skip to content

Commit

Permalink
Make CaptureMatch immutable
Browse files Browse the repository at this point in the history
  • Loading branch information
ocoanet committed Jun 7, 2019
1 parent b6fa016 commit 6a65ed5
Show file tree
Hide file tree
Showing 12 changed files with 29 additions and 101 deletions.
4 changes: 2 additions & 2 deletions src/Moq/Capture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public static class Capture
/// </example>
public static T In<T>(ICollection<T> collection)
{
var match = new CaptureMatch<T>(collection.Add, captureOnSuccessOnly: true);
var match = new CaptureMatch<T>(collection.Add);
return With(match);
}

Expand All @@ -53,7 +53,7 @@ public static T In<T>(ICollection<T> collection)
/// </example>
public static T In<T>(IList<T> collection, Expression<Func<T, bool>> predicate)
{
var match = new CaptureMatch<T>(collection.Add, predicate, captureOnSuccessOnly: true);
var match = new CaptureMatch<T>(collection.Add, predicate);
return With(match);
}

Expand Down
91 changes: 6 additions & 85 deletions src/Moq/CaptureMatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,14 @@ namespace Moq
/// <typeparam name="T"></typeparam>
public class CaptureMatch<T> : Match<T>
{
private readonly Matcher matcher;
private static readonly Predicate<T> matchAllPredicate = _ => true;

/// <summary>
/// Initializes an instance of the capture match.
/// </summary>
/// <param name="captureCallback">An action to run on captured value</param>
public CaptureMatch(Action<T> captureCallback)
: this(captureCallback, false)
{
}

/// <summary>
/// Initializes an instance of the capture match.
/// </summary>
/// <param name="captureCallback">An action to run on captured value</param>
/// <param name="captureOnSuccessOnly">
/// Indicates whether <paramref name="captureCallback"/> should be invoked on parameter evaluation
/// or only after all parameters were successfully evaluated.
/// </param>
public CaptureMatch(Action<T> captureCallback, bool captureOnSuccessOnly)
: this(new Matcher(captureCallback, null, captureOnSuccessOnly))
: base(matchAllPredicate, () => It.IsAny<T>(), captureCallback)
{
}

Expand All @@ -42,80 +29,14 @@ public CaptureMatch(Action<T> captureCallback, bool captureOnSuccessOnly)
/// <param name="captureCallback">An action to run on captured value</param>
/// <param name="predicate">A predicate used to filter captured parameters</param>
public CaptureMatch(Action<T> captureCallback, Expression<Func<T, bool>> predicate)
: this(captureCallback, predicate, false)
{
}

/// <summary>
/// Initializes an instance of the capture match.
/// </summary>
/// <param name="captureCallback">An action to run on captured value</param>
/// <param name="predicate">A predicate used to filter captured parameters</param>
/// <param name="captureOnSuccessOnly">
/// Indicates whether <paramref name="captureCallback"/> should be invoked on parameter evaluation
/// or only after all parameters were successfully evaluated.
/// </param>
public CaptureMatch(Action<T> captureCallback, Expression<Func<T, bool>> predicate, bool captureOnSuccessOnly)
: this(new Matcher(captureCallback, predicate, captureOnSuccessOnly))
: base(BuildCondition(predicate), () => It.Is(predicate), captureCallback)
{
}

private CaptureMatch(Matcher matcher)
: base(matcher.Evaluate, matcher.RenderExpression)
private static Predicate<T> BuildCondition(Expression<Func<T, bool>> predicateExpression)
{
this.matcher = matcher;
}

internal override void OnSuccess()
{
this.matcher.OnSuccess();
}

private class Matcher
{
private readonly Action<T> captureCallback;
private readonly Func<T, bool> predicate;
private readonly bool captureOnSuccessOnly;
private T capturedValue;

public Matcher(Action<T> captureCallback, Expression<Func<T, bool>> predicate, bool captureOnSuccessOnly)
{
this.captureCallback = captureCallback;

if (predicate != null)
{
this.predicate = predicate.CompileUsingExpressionCompiler();
this.RenderExpression = () => It.Is(predicate);
}
else
{
this.predicate = _ => true;
this.RenderExpression = () => It.IsAny<T>();
}

this.captureOnSuccessOnly = captureOnSuccessOnly;
}

public Expression<Func<T>> RenderExpression { get; }

public bool Evaluate(T value)
{
if (!this.predicate(value))
return false;

if (this.captureOnSuccessOnly)
this.capturedValue = value;
else
this.captureCallback.Invoke(value);

return true;
}

public void OnSuccess()
{
if (this.captureOnSuccessOnly)
this.captureCallback.Invoke(this.capturedValue);
}
var predicate = predicateExpression.CompileUsingExpressionCompiler();
return value => predicate.Invoke(value);
}
}
}
2 changes: 1 addition & 1 deletion src/Moq/IMatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ internal interface IMatcher
{
bool Matches(object value);

void OnSuccess();
void OnSuccess(object value);
}
}
4 changes: 2 additions & 2 deletions src/Moq/InvocationShape.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ public bool IsMatch(Invocation invocation)
}
}

foreach (var argumentMatcher in this.argumentMatchers)
for (int i = 0, n = this.argumentMatchers.Length; i < n; ++i)
{
argumentMatcher.OnSuccess();
this.argumentMatchers[i].OnSuccess(arguments[i]);
}

return true;
Expand Down
13 changes: 10 additions & 3 deletions src/Moq/Match.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ internal static TValue Matcher<TValue>()

internal abstract bool Matches(object value);

internal virtual void OnSuccess()
internal virtual void OnSuccess(object value)
{
}

bool IMatcher.Matches(object value) => this.Matches(value);

void IMatcher.OnSuccess() => this.OnSuccess();
void IMatcher.OnSuccess(object value) => this.OnSuccess(value);

internal Expression RenderExpression { get; set; }

Expand Down Expand Up @@ -99,11 +99,13 @@ internal static T Create<T>(Match<T> match)
public class Match<T> : Match, IEquatable<Match<T>>
{
internal Predicate<T> Condition { get; set; }
internal Action<T> SuccessCallback { get; set; }

internal Match(Predicate<T> condition, Expression<Func<T>> renderExpression)
internal Match(Predicate<T> condition, Expression<Func<T>> renderExpression, Action<T> successCallback = null)
{
this.Condition = condition;
this.RenderExpression = renderExpression.Body.Apply(EvaluateCaptures.Rewriter);
this.SuccessCallback = successCallback;
}

internal override bool Matches(object value)
Expand All @@ -130,6 +132,11 @@ internal override bool Matches(object value)
return this.Condition((T)value);
}

internal override void OnSuccess(object value)
{
this.SuccessCallback?.Invoke((T)value);
}

/// <inheritdoc/>
public override bool Equals(object obj)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Moq/Matchers/AnyMatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ private AnyMatcher()

public bool Matches(object value) => true;

public void OnSuccess()
public void OnSuccess(object value)
{
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Moq/Matchers/ConstantMatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public bool Matches(object value)
return false;
}

public void OnSuccess()
public void OnSuccess(object value)
{
}

Expand Down
2 changes: 1 addition & 1 deletion src/Moq/Matchers/ExpressionMatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public bool Matches(object value)
&& ExpressionComparer.Default.Equals(this.expression, valueExpression);
}

public void OnSuccess()
public void OnSuccess(object value)
{
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Moq/Matchers/LazyEvalMatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public bool Matches(object value)
return false;
}

public void OnSuccess()
public void OnSuccess(object value)
{
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Moq/Matchers/MatcherAttributeMatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public bool Matches(object value)
return (bool)validatorMethod.Invoke(instance, args);
}

public void OnSuccess()
public void OnSuccess(object value)
{
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/Moq/Matchers/ParamArrayMatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ public bool Matches(object value)
return true;
}

public void OnSuccess()
public void OnSuccess(object value)
{
foreach (var matcher in this.matchers)
{
matcher.OnSuccess();
matcher.OnSuccess(value);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Moq/Matchers/RefMatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public bool Matches(object value)
: object.ReferenceEquals(this.reference, value);
}

public void OnSuccess()
public void OnSuccess(object value)
{
}
}
Expand Down

0 comments on commit 6a65ed5

Please sign in to comment.