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,
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()
{