From 7d3df945f40ec3793e3935a2cf030727ab6857d3 Mon Sep 17 00:00:00 2001 From: stakx Date: Tue, 28 Apr 2020 19:54:15 +0200 Subject: [PATCH] Remember already verified mocks ("graph coloring") --- src/Moq/Mock.cs | 16 +++++++++++++--- src/Moq/Setup.cs | 12 +++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/Moq/Mock.cs b/src/Moq/Mock.cs index cec7f1e6a..e426d618b 100644 --- a/src/Moq/Mock.cs +++ b/src/Moq/Mock.cs @@ -295,14 +295,24 @@ public void VerifyAll() private void Verify(Func predicate) { - if (!this.TryVerify(predicate, out var error) && error.IsVerificationError) + var verifiedMocks = new HashSet(); + + if (!this.TryVerify(predicate, verifiedMocks, out var error) && error.IsVerificationError) { throw error; } } - internal bool TryVerify(Func predicate, out MockException error) + internal bool TryVerify(Func predicate, HashSet 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); @@ -312,7 +322,7 @@ internal bool TryVerify(Func 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); } diff --git a/src/Moq/Setup.cs b/src/Moq/Setup.cs index f1f8d9765..f6ea7c38f 100644 --- a/src/Moq/Setup.cs +++ b/src/Moq/Setup.cs @@ -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; @@ -127,6 +128,9 @@ public override string ToString() /// /// Specifies which setups should be verified. /// + /// + /// The set of mocks that have already been verified. + /// /// /// If this setup or any of its inner mock (if present and known) failed verification, /// this parameter will receive a describing the verification error(s). @@ -135,7 +139,7 @@ public override string ToString() /// if verification succeeded without any errors; /// otherwise, . /// - public bool TryVerify(bool recursive, Func predicate, out MockException error) + internal bool TryVerify(bool recursive, Func predicate, HashSet verifiedMocks, out MockException error) { MockException e; @@ -147,7 +151,7 @@ public bool TryVerify(bool recursive, Func 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; @@ -186,12 +190,14 @@ public void VerifyAll() private void Verify(bool recursive, Func predicate) { + var verifiedMocks = new HashSet(); + 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; }