From ff80e0007512ca630f1fb6693a51681d83d1ff36 Mon Sep 17 00:00:00 2001 From: stakx Date: Sat, 27 Apr 2019 20:19:58 +0200 Subject: [PATCH 1/2] Add tests for `default(Times)` Specify that `default(Times)` should be equivalent to `Times.AtLeast- Once()`, which is the implicit assumption of most public API method overloads that don't explicitly ask for a `Times` instance. While the use of `default(Times)` isn't encouraged, this still needs to be made to work as `Times` is a struct, for which sensible default values are a must. (In previous versions of Moq, `default(Times)` will cause `NullReferenceException`s sooner or later; not too great.) --- tests/Moq.Tests/TimesFixture.cs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/Moq.Tests/TimesFixture.cs b/tests/Moq.Tests/TimesFixture.cs index c496f6a0e..d67f4d669 100644 --- a/tests/Moq.Tests/TimesFixture.cs +++ b/tests/Moq.Tests/TimesFixture.cs @@ -9,6 +9,15 @@ namespace Moq.Tests { public class TimesFixture { + [Theory] + [InlineData(0, false)] + [InlineData(1, true)] + [InlineData(int.MaxValue, true)] + public void default_ranges_between_one_and_MaxValue(int count, bool verifies) + { + Assert.Equal(verifies, default(Times).Verify(count)); + } + [Fact] public void AtLeastOnceRangesBetweenOneAndMaxValue() { @@ -174,6 +183,20 @@ public void OnceChecksOneTime() public class Equality { +#pragma warning disable xUnit2000 // Constants and literals should be the expected argument + [Fact] + public void default_Equals_AtLeastOnce() + { + Assert.Equal(Times.AtLeastOnce(), default(Times)); + } +#pragma warning restore xUnit2000 + + [Fact] + public void default_GetHashCode_equals_AtLeastOnce_GetHashCode() + { + Assert.Equal(Times.AtLeastOnce().GetHashCode(), default(Times).GetHashCode()); + } + [Fact] public void AtMostOnce_equals_Between_0_1_inclusive() { From 4dc244d75b75437e7daacf325dcad0b4b09ee825 Mon Sep 17 00:00:00 2001 From: stakx Date: Sat, 27 Apr 2019 20:32:18 +0200 Subject: [PATCH 2/2] Adjust `default(Times)` to `Times.AtLeastOnce()` --- src/Moq/Times.cs | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/src/Moq/Times.cs b/src/Moq/Times.cs index 98a4b1ad8..7ea1db280 100644 --- a/src/Moq/Times.cs +++ b/src/Moq/Times.cs @@ -2,6 +2,7 @@ // All rights reserved. Licensed under the BSD 3-Clause License; see License.txt. using System; +using System.Diagnostics; using System.Globalization; using Moq.Properties; @@ -22,6 +23,28 @@ private Times(Kind kind, int from, int to) this.kind = kind; } + private void Deconstruct(out int from, out int to) + { + if (this.kind == default) + { + // This branch makes `default(Times)` equivalent to `Times.AtLeastOnce()`, + // which is the implicit default across Moq's API for overloads that don't + // accept a `Times` instance. While user code shouldn't use `default(Times)` + // (but instead either specify `Times` explicitly or not at all), it is + // easy enough to correct: + + Debug.Assert(this.kind == Kind.AtLeastOnce); + + from = 1; + to = int.MaxValue; + } + else + { + from = this.from; + to = this.to; + } + } + /// public static Times AtLeast(int callCount) { @@ -115,7 +138,9 @@ public static Times Once() /// public bool Equals(Times other) { - return this.from == other.from && this.to == other.to; + var (from, to) = this; + var (otherFrom, otherTo) = other; + return from == otherFrom && to == otherTo; } /// @@ -127,7 +152,8 @@ public override bool Equals(object obj) /// public override int GetHashCode() { - return this.from.GetHashCode() ^ this.to.GetHashCode(); + var (from, to) = this; + return from.GetHashCode() ^ to.GetHashCode(); } /// @@ -144,8 +170,13 @@ public override int GetHashCode() internal string GetExceptionMessage(int callCount) { - var from = this.kind == Kind.BetweenExclusive ? this.from - 1 : this.from; - var to = this.kind == Kind.BetweenExclusive ? this.to + 1 : this.to; + var (from, to) = this; + + if (this.kind == Kind.BetweenExclusive) + { + --from; + ++to; + } string message = null; switch (this.kind) @@ -166,13 +197,14 @@ internal string GetExceptionMessage(int callCount) internal bool Verify(int callCount) { - return this.from <= callCount && callCount <= this.to; + var (from, to) = this; + return from <= callCount && callCount <= to; } private enum Kind { - AtLeast, AtLeastOnce, + AtLeast, AtMost, AtMostOnce, BetweenExclusive,