Skip to content

Commit

Permalink
Remember already verified mocks ("graph coloring")
Browse files Browse the repository at this point in the history
  • Loading branch information
stakx committed Apr 28, 2020
1 parent b562624 commit 7d3df94
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 6 deletions.
16 changes: 13 additions & 3 deletions src/Moq/Mock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -295,14 +295,24 @@ public void VerifyAll()

private void Verify(Func<ISetup, bool> predicate)
{
if (!this.TryVerify(predicate, out var error) && error.IsVerificationError)
var verifiedMocks = new HashSet<Mock>();

if (!this.TryVerify(predicate, verifiedMocks, out var error) && error.IsVerificationError)
{
throw error;
}
}

internal bool TryVerify(Func<ISetup, bool> predicate, out MockException error)
internal bool TryVerify(Func<ISetup, bool> predicate, HashSet<Mock> verifiedMocks, out MockException error)
{
if (verifiedMocks.Add(this) == false)
{
// This mock has already been verified; don't verify it again.
// (We can end up here e.g. when there are loops in the inner mock object graph.)
error = null;
return true;
}

foreach (Invocation invocation in this.MutableInvocations)
{
invocation.MarkAsVerifiedIfMatchedBy(predicate);
Expand All @@ -312,7 +322,7 @@ internal bool TryVerify(Func<ISetup, bool> predicate, out MockException error)

foreach (var setup in this.MutableSetups.ToArray(predicate))
{
if (predicate(setup) && !setup.TryVerify(recursive: true, predicate, out var e) && e.IsVerificationError)
if (predicate(setup) && !setup.TryVerify(recursive: true, predicate, verifiedMocks, out var e) && e.IsVerificationError)
{
errors.Add(e);
}
Expand Down
12 changes: 9 additions & 3 deletions src/Moq/Setup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// All rights reserved. Licensed under the BSD 3-Clause License; see License.txt.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Reflection;
Expand Down Expand Up @@ -127,6 +128,9 @@ public override string ToString()
/// <param name="predicate">
/// Specifies which setups should be verified.
/// </param>
/// <param name="verifiedMocks">
/// The set of mocks that have already been verified.
/// </param>
/// <param name="error">
/// If this setup or any of its inner mock (if present and known) failed verification,
/// this <see langword="out"/> parameter will receive a <see cref="MockException"/> describing the verification error(s).
Expand All @@ -135,7 +139,7 @@ public override string ToString()
/// <see langword="true"/> if verification succeeded without any errors;
/// otherwise, <see langword="false"/>.
/// </returns>
public bool TryVerify(bool recursive, Func<ISetup, bool> predicate, out MockException error)
internal bool TryVerify(bool recursive, Func<ISetup, bool> predicate, HashSet<Mock> verifiedMocks, out MockException error)
{
MockException e;

Expand All @@ -147,7 +151,7 @@ public bool TryVerify(bool recursive, Func<ISetup, bool> predicate, out MockExce
}

// optionally verify setups of inner mock (if present and known):
if (recursive && this.InnerMock?.TryVerify(predicate, out e) == false && e.IsVerificationError)
if (recursive && this.InnerMock?.TryVerify(predicate, verifiedMocks, out e) == false && e.IsVerificationError)
{
error = MockException.FromInnerMockOf(this, e);
return false;
Expand Down Expand Up @@ -186,12 +190,14 @@ public void VerifyAll()

private void Verify(bool recursive, Func<ISetup, bool> predicate)
{
var verifiedMocks = new HashSet<Mock>();

foreach (Invocation invocation in this.mock.MutableInvocations)
{
invocation.MarkAsVerifiedIfMatchedBy(setup => setup == this);
}

if (!this.TryVerify(recursive, predicate, out var error) && error.IsVerificationError)
if (!this.TryVerify(recursive, predicate, verifiedMocks, out var error) && error.IsVerificationError)
{
throw error;
}
Expand Down

0 comments on commit 7d3df94

Please sign in to comment.