using System; using System.Globalization; using System.Threading; using UnitTestSharp; namespace Amphora.UnitTests { public class DurationTests : TestFixture { public static Duration OfTicks(long ticks) { return Duration.FromTicks(ticks); } public class ConstantsTests : TestFixture { public void Zero_IsTheDefault() { CheckEqual(default(Duration), Duration.Zero); CheckEqual(0L, Duration.Zero.Ticks); } public void MaxValue_IsTheLongestFiniteLength() { CheckEqual(long.MaxValue - 1L, Duration.MaxValue.Ticks); CheckTrue(Duration.MaxValue.IsFinite); } public void MinValue_IsTheNegationOfMaxValue() { CheckEqual(-Duration.MaxValue, Duration.MinValue); CheckEqual(-(long.MaxValue - 1L), Duration.MinValue.Ticks); } public void Forever_IsLongerThanMaxValue() { CheckTrue(Duration.Forever > Duration.MaxValue); CheckTrue(Duration.Forever.IsInfinite); } } public class FromTicksLongTests : TestFixture { public void WholeTicks_AreKeptExactly() { CheckEqual(123456789012345L, Duration.FromTicks(123456789012345L).Ticks); CheckEqual(-5L, Duration.FromTicks(-5L).Ticks); } public void LargestFiniteCount_IsMaxValue() { CheckEqual(Duration.MaxValue, Duration.FromTicks(long.MaxValue - 1L)); } public void LargestLong_IsMaxValueAndNotInfinite() { CheckEqual(Duration.MaxValue, Duration.FromTicks(long.MaxValue)); CheckTrue(Duration.FromTicks(long.MaxValue).IsFinite); } public void SmallestFiniteCount_IsMinValue() { CheckEqual(Duration.MinValue, Duration.FromTicks(long.MinValue + 2L)); } public void CountsBelowTheFiniteRange_AreMinValueAndNotInfinite() { CheckEqual(Duration.MinValue, Duration.FromTicks(long.MinValue + 1L)); CheckEqual(Duration.MinValue, Duration.FromTicks(long.MinValue)); CheckTrue(Duration.FromTicks(long.MinValue).IsFinite); } } public class FromTicksDoubleTests : TestFixture { public void RoundsToTheNearestTick() { CheckEqual(1L, Duration.FromTicks(1.4).Ticks); CheckEqual(2L, Duration.FromTicks(1.6).Ticks); CheckEqual(-2L, Duration.FromTicks(-1.6).Ticks); } public void Infinity_IsForever() { CheckEqual(Duration.Forever, Duration.FromTicks(double.PositiveInfinity)); CheckEqual(-Duration.Forever, Duration.FromTicks(double.NegativeInfinity)); } public void NumbersPastTheRange_AreTheFiniteExtremes() { CheckEqual(Duration.MaxValue, Duration.FromTicks(9.3e18)); CheckEqual(Duration.MinValue, Duration.FromTicks(-9.3e18)); CheckEqual(Duration.MaxValue, Duration.FromTicks(double.MaxValue)); CheckEqual(Duration.MinValue, Duration.FromTicks(double.MinValue)); } public void TwoToThe63_IsMaxValue() { CheckEqual(Duration.MaxValue, Duration.FromTicks(9223372036854775808.0)); CheckEqual(Duration.MinValue, Duration.FromTicks(-9223372036854775808.0)); } public void NumberJustInsideTheRange_IsFinite() { CheckTrue(Duration.FromTicks(9.2e18).IsFinite); } public void NotANumber_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Duration.FromTicks(double.NaN); } } public class FromMillisecondsTests : TestFixture { public void ScalesToTicks() { CheckEqual(15000L, Duration.FromMilliseconds(1.5).Ticks); } public void NotANumber_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Duration.FromMilliseconds(double.NaN); } } public class FromSecondsTests : TestFixture { public void ScalesToTicks() { CheckEqual(25000000L, Duration.FromSeconds(2.5).Ticks); } public void Infinity_IsForever() { CheckEqual(Duration.Forever, Duration.FromSeconds(double.PositiveInfinity)); } public void HugeNumber_IsAFiniteExtremeNotAWrap() { CheckEqual(Duration.MaxValue, Duration.FromSeconds(1e300)); CheckEqual(Duration.MinValue, Duration.FromSeconds(-1e300)); } public void NumberThatOverflowsOnlyWhenScaled_IsAFiniteExtreme() { CheckEqual(Duration.MaxValue, Duration.FromSeconds(double.MaxValue)); CheckEqual(Duration.MinValue, Duration.FromSeconds(double.MinValue)); } public void NotANumber_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Duration.FromSeconds(double.NaN); } } public class FromMinutesTests : TestFixture { public void ScalesToTicks() { CheckEqual(900000000L, Duration.FromMinutes(1.5).Ticks); } public void NotANumber_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Duration.FromMinutes(double.NaN); } } public class FromHoursTests : TestFixture { public void ScalesToTicks() { CheckEqual(54000000000L, Duration.FromHours(1.5).Ticks); } public void NotANumber_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Duration.FromHours(double.NaN); } } public class FromDaysTests : TestFixture { public void ScalesToTicks() { CheckEqual(1296000000000L, Duration.FromDays(1.5).Ticks); } public void ThirtyThousandYears_IsMaxValue() { CheckEqual(Duration.MaxValue, Duration.FromDays(365.25 * 30000.0)); } public void TwentyThousandYears_IsFinite() { CheckTrue(Duration.FromDays(365.25 * 20000.0).IsFinite); } public void NotANumber_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Duration.FromDays(double.NaN); } } public class TicksTests : TestFixture { public void Forever_Throws() { CheckThrow(typeof(InvalidOperationException)); long ignored = Duration.Forever.Ticks; } public void NegativeForever_Throws() { CheckThrow(typeof(InvalidOperationException)); long ignored = (-Duration.Forever).Ticks; } } public class InfinityTests : TestFixture { public void FiniteLengths_AreFinite() { CheckTrue(Duration.Zero.IsFinite); CheckFalse(Duration.Zero.IsInfinite); CheckTrue(Duration.MinValue.IsFinite); } public void BothForevers_AreInfinite() { CheckTrue(Duration.Forever.IsInfinite); CheckTrue((-Duration.Forever).IsInfinite); CheckFalse(Duration.Forever.IsFinite); } } public class TotalTests : TestFixture { public void ConvertEachUnit() { Duration length = Duration.FromSeconds(90.0); CheckEqual(90000.0, length.TotalMilliseconds); CheckEqual(90.0, length.TotalSeconds); CheckEqual(1.5, length.TotalMinutes); CheckEqual(0.025, length.TotalHours); CheckEqual(90.0 / 86400.0, length.TotalDays); } public void NegativeLength_GivesNegativeTotals() { CheckEqual(-2.5, Duration.FromSeconds(-2.5).TotalSeconds); } public void Forever_IsPositiveInfinityInEveryUnit() { CheckEqual(double.PositiveInfinity, Duration.Forever.TotalMilliseconds); CheckEqual(double.PositiveInfinity, Duration.Forever.TotalSeconds); CheckEqual(double.PositiveInfinity, Duration.Forever.TotalMinutes); CheckEqual(double.PositiveInfinity, Duration.Forever.TotalHours); CheckEqual(double.PositiveInfinity, Duration.Forever.TotalDays); } public void NegativeForever_IsNegativeInfinity() { CheckEqual(double.NegativeInfinity, (-Duration.Forever).TotalSeconds); } } public class NegateTests : TestFixture { public void FlipsTheSign() { CheckEqual(OfTicks(-5L), -OfTicks(5L)); CheckEqual(OfTicks(5L), -OfTicks(-5L)); } public void Zero_IsZero() { CheckEqual(Duration.Zero, -Duration.Zero); } public void TheInfinities_SwapPlaces() { CheckEqual(Duration.Forever, -(-Duration.Forever)); CheckTrue(-Duration.Forever < Duration.Zero); } public void TheFiniteExtremes_SwapPlaces() { CheckEqual(Duration.MinValue, -Duration.MaxValue); CheckEqual(Duration.MaxValue, -Duration.MinValue); } } public class AddTests : TestFixture { public void AddsTicks() { CheckEqual(OfTicks(7L), OfTicks(3L) + OfTicks(4L)); CheckEqual(OfTicks(-1L), OfTicks(3L) + OfTicks(-4L)); } public void ForeverAndAFiniteLength_IsForever() { CheckEqual(Duration.Forever, Duration.Forever + OfTicks(5L)); CheckEqual(Duration.Forever, OfTicks(5L) + Duration.Forever); CheckEqual(Duration.Forever, Duration.Forever + Duration.MinValue); } public void NegativeForeverAndAFiniteLength_IsNegativeForever() { CheckEqual(-Duration.Forever, OfTicks(5L) + (-Duration.Forever)); CheckEqual(-Duration.Forever, (-Duration.Forever) + Duration.MaxValue); } public void ForeverAndForever_IsForever() { CheckEqual(Duration.Forever, Duration.Forever + Duration.Forever); CheckEqual(-Duration.Forever, (-Duration.Forever) + (-Duration.Forever)); } public void ForeverAndNegativeForever_Throws() { CheckThrow(typeof(InvalidOperationException)); Duration ignored = Duration.Forever + (-Duration.Forever); } public void NegativeForeverAndForever_Throws() { CheckThrow(typeof(InvalidOperationException)); Duration ignored = (-Duration.Forever) + Duration.Forever; } public void PastTheTop_IsMaxValueNotAWrap() { CheckEqual(Duration.MaxValue, Duration.MaxValue + OfTicks(1L)); CheckEqual(Duration.MaxValue, Duration.MaxValue + Duration.MaxValue); } public void PastTheBottom_IsMinValueNotAWrap() { CheckEqual(Duration.MinValue, Duration.MinValue + OfTicks(-1L)); CheckEqual(Duration.MinValue, Duration.MinValue + Duration.MinValue); } public void ExtremesOfOppositeSigns_CancelExactly() { CheckEqual(Duration.Zero, Duration.MaxValue + Duration.MinValue); } public void ASumLandingOnTheTopFiniteValue_IsFinite() { CheckEqual(Duration.MaxValue, OfTicks(long.MaxValue - 2L) + OfTicks(1L)); } } public class SubtractTests : TestFixture { public void SubtractsTicks() { CheckEqual(OfTicks(-1L), OfTicks(3L) - OfTicks(4L)); } public void ForeverMinusAFiniteLength_IsForever() { CheckEqual(Duration.Forever, Duration.Forever - OfTicks(5L)); } public void AFiniteLengthMinusForever_IsNegativeForever() { CheckEqual(-Duration.Forever, OfTicks(5L) - Duration.Forever); } public void ForeverMinusNegativeForever_IsForever() { CheckEqual(Duration.Forever, Duration.Forever - (-Duration.Forever)); } public void ForeverMinusForever_Throws() { CheckThrow(typeof(InvalidOperationException)); Duration ignored = Duration.Forever - Duration.Forever; } public void NegativeForeverMinusNegativeForever_Throws() { CheckThrow(typeof(InvalidOperationException)); Duration ignored = (-Duration.Forever) - (-Duration.Forever); } public void PastTheBottom_IsMinValue() { CheckEqual(Duration.MinValue, Duration.MinValue - OfTicks(1L)); } public void PastTheTop_IsMaxValue() { CheckEqual(Duration.MaxValue, Duration.MaxValue - OfTicks(-1L)); } public void ZeroMinusMinValue_IsMaxValue() { CheckEqual(Duration.MaxValue, Duration.Zero - Duration.MinValue); } public void AMomentFromNowMinusNow_IsTheMoment() { Duration now = Duration.FromDays(1000.0); CheckEqual(Duration.FromSeconds(3.0), (now + Duration.FromSeconds(3.0)) - now); } } public class MultiplyTests : TestFixture { public void ScalesTheLength() { CheckEqual(Duration.FromSeconds(2.5), Duration.FromSeconds(1.0) * 2.5); CheckEqual(Duration.FromSeconds(2.5), 2.5 * Duration.FromSeconds(1.0)); } public void RoundsToTheNearestTick() { CheckEqual(OfTicks(1L), OfTicks(7L) * 0.2); CheckEqual(OfTicks(2L), OfTicks(8L) * 0.2); } public void ANegativeFactor_FlipsTheSign() { CheckEqual(OfTicks(-6L), OfTicks(3L) * -2.0); } public void FactorOfOne_KeepsEvenTheFiniteExtremes() { CheckEqual(Duration.MaxValue, Duration.MaxValue * 1.0); CheckEqual(Duration.MinValue, 1.0 * Duration.MinValue); } public void ZeroFactor_IsZero() { CheckEqual(Duration.Zero, Duration.FromDays(5.0) * 0.0); } public void PastTheRange_IsAFiniteExtreme() { CheckEqual(Duration.MaxValue, Duration.MaxValue * 2.0); CheckEqual(Duration.MinValue, Duration.MaxValue * -2.0); CheckEqual(Duration.MaxValue, Duration.MinValue * -2.0); } public void HugeFactorOnAFiniteLength_IsAFiniteExtreme() { CheckEqual(Duration.MaxValue, OfTicks(5L) * double.MaxValue); CheckEqual(Duration.MinValue, OfTicks(5L) * -double.MaxValue); CheckEqual(Duration.MaxValue, Duration.MaxValue * double.MaxValue); } public void AnInfiniteFactor_GivesAnInfinity() { CheckEqual(Duration.Forever, OfTicks(1L) * double.PositiveInfinity); CheckEqual(-Duration.Forever, OfTicks(1L) * double.NegativeInfinity); } public void ForeverTimesAPositiveFactor_IsForever() { CheckEqual(Duration.Forever, Duration.Forever * 0.001); } public void ForeverTimesANegativeFactor_IsNegativeForever() { CheckEqual(-Duration.Forever, Duration.Forever * -2.0); CheckEqual(Duration.Forever, (-Duration.Forever) * -2.0); } public void ForeverTimesZero_Throws() { CheckThrow(typeof(InvalidOperationException)); Duration ignored = Duration.Forever * 0.0; } public void ZeroTimesInfinity_Throws() { CheckThrow(typeof(InvalidOperationException)); Duration ignored = Duration.Zero * double.PositiveInfinity; } public void NotANumberFactor_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Duration ignored = OfTicks(3L) * double.NaN; } } public class DivideByDoubleTests : TestFixture { public void DividesTheLength() { CheckEqual(Duration.FromSeconds(1.5), Duration.FromSeconds(3.0) / 2.0); } public void RoundsToTheNearestTick() { CheckEqual(OfTicks(2L), OfTicks(5L) / 3.0); } public void DivisorOfOne_KeepsEvenTheFiniteExtremes() { CheckEqual(Duration.MaxValue, Duration.MaxValue / 1.0); CheckEqual(Duration.MinValue, Duration.MinValue / 1.0); } public void ADivisorUnderOne_Lengthens() { CheckEqual(OfTicks(2L), OfTicks(1L) / 0.5); } public void ANegativeDivisor_FlipsTheSign() { CheckEqual(OfTicks(-3L), OfTicks(6L) / -2.0); } public void PastTheRange_IsAFiniteExtreme() { CheckEqual(Duration.MaxValue, Duration.MaxValue / 0.25); CheckEqual(Duration.MinValue, Duration.MaxValue / -0.25); } public void TinyDivisorOnAFiniteLength_IsAFiniteExtremeNotInfinite() { CheckEqual(Duration.MaxValue, OfTicks(5L) / 1e-300); CheckEqual(Duration.MinValue, OfTicks(-5L) / 1e-300); CheckEqual(Duration.MinValue, OfTicks(5L) / -1e-300); CheckEqual(Duration.MaxValue, Duration.MaxValue / 1e-300); } public void ForeverDividedByAFiniteNumber_KeepsItsSignOrFlipsIt() { CheckEqual(Duration.Forever, Duration.Forever / 2.0); CheckEqual(-Duration.Forever, Duration.Forever / -2.0); } public void ADivisorOfInfinity_IsZero() { CheckEqual(Duration.Zero, OfTicks(5L) / double.PositiveInfinity); } public void ForeverDividedByInfinity_Throws() { CheckThrow(typeof(InvalidOperationException)); Duration ignored = Duration.Forever / double.PositiveInfinity; } public void Zero_Throws() { CheckThrow(typeof(DivideByZeroException)); Duration ignored = OfTicks(5L) / 0.0; } public void NotANumber_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); Duration ignored = OfTicks(5L) / double.NaN; } } public class DivideByDurationTests : TestFixture { public void GivesHowManyTimesItFits() { CheckEqual(2.0, Duration.FromSeconds(3.0) / Duration.FromSeconds(1.5)); CheckEqual(-0.5, Duration.FromSeconds(-1.0) / Duration.FromSeconds(2.0)); } public void ForeverOverAFiniteLength_IsInfinity() { CheckEqual(double.PositiveInfinity, Duration.Forever / OfTicks(1L)); CheckEqual(double.NegativeInfinity, (-Duration.Forever) / OfTicks(1L)); CheckEqual(double.NegativeInfinity, Duration.Forever / OfTicks(-1L)); } public void AFiniteLengthOverForever_IsZero() { CheckEqual(0.0, OfTicks(5L) / Duration.Forever); } public void ForeverOverForever_Throws() { CheckThrow(typeof(InvalidOperationException)); double ignored = Duration.Forever / Duration.Forever; } public void ZeroDivisor_Throws() { CheckThrow(typeof(DivideByZeroException)); double ignored = OfTicks(5L) / Duration.Zero; } public void ZeroOverZero_Throws() { CheckThrow(typeof(DivideByZeroException)); double ignored = Duration.Zero / Duration.Zero; } } public class OrderingTests : TestFixture { private static readonly Duration[] Ascending = { -Duration.Forever, Duration.MinValue, OfTicks(-1L), Duration.Zero, OfTicks(1L), Duration.MaxValue, Duration.Forever, }; public void EveryLengthIsOrderedAfterTheOnesBeforeIt() { for (int low = 0; low < Ascending.Length; low++) { for (int high = low + 1; high < Ascending.Length; high++) { CheckTrue(Ascending[low] < Ascending[high]); CheckTrue(Ascending[low] <= Ascending[high]); CheckTrue(Ascending[high] > Ascending[low]); CheckTrue(Ascending[high] >= Ascending[low]); CheckTrue(Ascending[low] != Ascending[high]); CheckFalse(Ascending[low] == Ascending[high]); CheckEqual(-1, Math.Sign(Ascending[low].CompareTo(Ascending[high]))); CheckEqual(1, Math.Sign(Ascending[high].CompareTo(Ascending[low]))); } } } public void ALengthEqualsItself() { foreach (Duration length in Ascending) { Duration copy = length; CheckTrue(length == copy); CheckTrue(length <= copy); CheckTrue(length >= copy); CheckFalse(length < copy); CheckFalse(length > copy); CheckEqual(0, length.CompareTo(copy)); } } } public class MinMaxAbsTests : TestFixture { public void Min_IsTheShorter() { CheckEqual(OfTicks(2L), Duration.Min(OfTicks(2L), OfTicks(3L))); CheckEqual(OfTicks(2L), Duration.Min(OfTicks(3L), OfTicks(2L))); CheckEqual(-Duration.Forever, Duration.Min(Duration.Zero, -Duration.Forever)); } public void Max_IsTheLonger() { CheckEqual(OfTicks(3L), Duration.Max(OfTicks(2L), OfTicks(3L))); CheckEqual(OfTicks(3L), Duration.Max(OfTicks(3L), OfTicks(2L))); CheckEqual(Duration.Forever, Duration.Max(Duration.Zero, Duration.Forever)); } public void Abs_DropsTheSign() { CheckEqual(OfTicks(5L), Duration.Abs(OfTicks(-5L))); CheckEqual(OfTicks(5L), Duration.Abs(OfTicks(5L))); CheckEqual(Duration.Zero, Duration.Abs(Duration.Zero)); } public void Abs_OfNegativeForever_IsForever() { CheckEqual(Duration.Forever, Duration.Abs(-Duration.Forever)); } public void Abs_OfMinValue_IsMaxValue() { CheckEqual(Duration.MaxValue, Duration.Abs(Duration.MinValue)); } } public class EqualsTests : TestFixture { public void ObjectOfTheSameLength_IsEqual() { CheckTrue(OfTicks(5L).Equals((object)OfTicks(5L))); } public void ObjectOfAnotherLength_IsNotEqual() { CheckFalse(OfTicks(5L).Equals((object)OfTicks(6L))); } public void ObjectOfAnotherType_IsNotEqual() { CheckFalse(OfTicks(5L).Equals((object)5L)); CheckFalse(OfTicks(5L).Equals(null)); } public void EqualLengths_HaveEqualHashCodes() { CheckEqual(Duration.FromSeconds(1.5).GetHashCode(), Duration.FromMilliseconds(1500.0).GetHashCode()); } } public class ToStringTests : TestFixture { private static readonly long[] Samples = { 0L, 1L, -1L, 15000000L, -5400000000L, 2000000000000L, 123456789L, long.MaxValue - 1L, long.MinValue + 2L, }; public void Default_IsTheConstantFormatOfTimeSpan() { CheckEqual("00:00:12.5000000", Duration.FromSeconds(12.5).ToString()); CheckEqual("1.02:03:04.5000000", ((Duration)new TimeSpan(1, 2, 3, 4, 500)).ToString()); CheckEqual("-00:00:01.5000000", Duration.FromSeconds(-1.5).ToString()); } public void NullAndEmptyFormats_AreTheConstantFormat() { Duration length = Duration.FromSeconds(12.5); CheckEqual(length.ToString(), length.ToString(null)); CheckEqual(length.ToString(), length.ToString(string.Empty)); } public void StandardFormats_MatchTimeSpanForEverySample() { foreach (long ticks in Samples) { foreach (string format in new[] { "c", "g", "G" }) { CheckEqual(TimeSpan.FromTicks(ticks).ToString(format), OfTicks(ticks).ToString(format)); } } } public void CustomFormat_MatchesTimeSpan() { Duration length = new TimeSpan(0, 1, 2, 3, 0); CheckEqual("01:02:03", length.ToString(@"hh\:mm\:ss")); } public void CultureSensitiveFormat_UsesTheGivenCulture() { var french = new CultureInfo("fr-FR"); TimeSpan span = TimeSpan.FromSeconds(12.5); CheckEqual(span.ToString("g", french), ((Duration)span).ToString("g", french)); CheckEqual("0:00:12,5", ((Duration)span).ToString("g", french)); } public void TheInfinities_AreNamedWhateverTheFormat() { CheckEqual("Forever", Duration.Forever.ToString()); CheckEqual("-Forever", (-Duration.Forever).ToString()); CheckEqual("Forever", Duration.Forever.ToString("g")); CheckEqual("-Forever", (-Duration.Forever).ToString("not a format")); } public void UnknownFormat_Throws() { CheckThrow(typeof(FormatException)); Duration.Zero.ToString("not a format"); } public void IsIFormattable() { IFormattable length = Duration.FromSeconds(12.5); CheckEqual("00:00:12.5000000", length.ToString("c", null)); } } public class ParseTests : TestFixture { public void ReadsTheFormsTimeSpanReads() { CheckEqual((Duration)TimeSpan.Parse("12.03:04:05.5"), Duration.Parse("12.03:04:05.5")); CheckEqual(Duration.FromSeconds(90.0), Duration.Parse("00:01:30")); } public void ReadsTheNamesOfTheInfinitiesWhateverTheCase() { CheckEqual(Duration.Forever, Duration.Parse("Forever")); CheckEqual(Duration.Forever, Duration.Parse(" forever ")); CheckEqual(-Duration.Forever, Duration.Parse("-Forever")); CheckEqual(-Duration.Forever, Duration.Parse("-FOREVER")); } public void MinusOneMillisecond_IsAnOrdinaryLengthNotForever() { CheckEqual(-10000L, Duration.Parse("-00:00:00.0010000").Ticks); } public void TheTimeSpanExtremes_AreTheFiniteExtremes() { CheckEqual(Duration.MaxValue, Duration.Parse(TimeSpan.MaxValue.ToString())); CheckEqual(Duration.MinValue, Duration.Parse(TimeSpan.MinValue.ToString())); } public void WithACulture_ReadsItsSeparators() { CheckEqual(Duration.FromSeconds(12.5), Duration.Parse("0:00:12,5", new CultureInfo("fr-FR"))); } public void ToStringOutput_ParsesBackToTheSameLength() { foreach (long ticks in new[] { 0L, 1L, -1L, 15000000L, -5400000000L, long.MaxValue - 1L }) { CheckEqual(OfTicks(ticks), Duration.Parse(OfTicks(ticks).ToString())); } CheckEqual(Duration.MinValue, Duration.Parse(Duration.MinValue.ToString())); CheckEqual(Duration.Forever, Duration.Parse(Duration.Forever.ToString())); CheckEqual(-Duration.Forever, Duration.Parse((-Duration.Forever).ToString())); } public void NotALength_Throws() { CheckThrow(typeof(FormatException)); Duration.Parse("soon"); } public void NoText_Throws() { CheckThrow(typeof(ArgumentNullException)); Duration.Parse(null); } public void MoreDaysThanATimeSpanHolds_Throws() { CheckThrow(typeof(OverflowException)); Duration.Parse("11000000.00:00:00"); } } public class TryParseTests : TestFixture { public void ALength_IsReturned() { CheckTrue(Duration.TryParse("00:01:30", out Duration length)); CheckEqual(Duration.FromSeconds(90.0), length); } public void TheInfinities_AreReturned() { CheckTrue(Duration.TryParse("Forever", out Duration forever)); CheckEqual(Duration.Forever, forever); CheckTrue(Duration.TryParse("-Forever", out Duration negative)); CheckEqual(-Duration.Forever, negative); } public void NotALength_IsFalseWithZero() { CheckFalse(Duration.TryParse("soon", out Duration length)); CheckEqual(Duration.Zero, length); } public void NoText_IsFalse() { CheckFalse(Duration.TryParse(null, out Duration length)); CheckEqual(Duration.Zero, length); } public void MoreDaysThanATimeSpanHolds_IsFalse() { CheckFalse(Duration.TryParse("11000000.00:00:00", out Duration length)); } public void WithACulture_ReadsItsSeparators() { CheckTrue(Duration.TryParse("0:00:12,5", new CultureInfo("fr-FR"), out Duration length)); CheckEqual(Duration.FromSeconds(12.5), length); } } public class SaturationTests : TestFixture { private static readonly long MaxTicks = long.MaxValue - 1L; private static readonly long MinTicks = long.MinValue + 2L; // Both ends, one step in from each end, the halves and the middle, where a wrap would show. private static readonly long[] Samples = { MinTicks, MinTicks + 1L, -4611686018427387904L, -4611686018427387903L, -1L, 0L, 1L, 4611686018427387903L, 4611686018427387904L, MaxTicks - 1L, MaxTicks, }; private static readonly double[] Factors = { 2.0, -2.0, 0.5, -0.5, 1e10, -1e10, 1e300, -1e300, double.MaxValue, -double.MaxValue, 1e-10, 1e-300, }; private static long Clamped(decimal value) { if (value > MaxTicks) { return MaxTicks; } if (value < MinTicks) { return MinTicks; } return (long)value; } public void Sum_IsTheExactSumClampedToTheFiniteRange() { foreach (long left in Samples) { foreach (long right in Samples) { Duration sum = OfTicks(left) + OfTicks(right); CheckTrue(sum.IsFinite); CheckEqual(Clamped((decimal)left + (decimal)right), sum.Ticks); } } } public void Difference_IsTheExactDifferenceClampedToTheFiniteRange() { foreach (long left in Samples) { foreach (long right in Samples) { Duration difference = OfTicks(left) - OfTicks(right); CheckTrue(difference.IsFinite); CheckEqual(Clamped((decimal)left - (decimal)right), difference.Ticks); } } } public void Negation_IsExactAndNeverLeavesTheFiniteRange() { foreach (long ticks in Samples) { Duration negated = -OfTicks(ticks); CheckTrue(negated.IsFinite); CheckEqual(-ticks, negated.Ticks); } } public void Abs_NeverLeavesTheFiniteRange() { foreach (long ticks in Samples) { Duration absolute = Duration.Abs(OfTicks(ticks)); CheckTrue(absolute.IsFinite); CheckEqual(Math.Abs((decimal)ticks), (decimal)absolute.Ticks); } } public void ScalingAFiniteLength_StaysFiniteKeepsItsSignAndNeverShrinksWhenScaledUp() { foreach (long ticks in Samples) { foreach (double factor in Factors) { Duration scaled = OfTicks(ticks) * factor; CheckTrue(scaled.IsFinite); CheckEqual(scaled, factor * OfTicks(ticks)); CheckTrue(KeepsSignAndDirection(ticks, factor, scaled.Ticks)); } } } public void DividingAFiniteLength_StaysFiniteKeepsItsSignAndNeverShrinksWhenDividedDown() { foreach (long ticks in Samples) { foreach (double divisor in new[] { 0.5, -0.5, 1e-10, -1e-10, 1e-300, -1e-300, 2.0, -2.0 }) { Duration divided = OfTicks(ticks) / divisor; CheckTrue(divided.IsFinite); CheckTrue(KeepsSignAndDirection(ticks, 1.0 / divisor, divided.Ticks)); } } } // A factor of 1 or more in magnitude never shrinks a length, and one under 1 never grows it. The sign is // the product of the signs, unless a small factor rounds the result to zero. private static bool KeepsSignAndDirection(long ticks, double factor, long result) { decimal before = Math.Abs((decimal)ticks); decimal after = Math.Abs((decimal)result); if (Math.Abs(factor) >= 1.0 && after < before) { return false; } if (Math.Abs(factor) < 1.0 && after > before) { return false; } if (result == 0L) { return Math.Abs(factor) < 1.0 || ticks == 0L; } return Math.Sign(result) == Math.Sign(ticks) * Math.Sign(factor); } public void Factories_PastTheRange_AreTheFiniteExtremes() { CheckEqual(Duration.MaxValue, Duration.FromMilliseconds(1e300)); CheckEqual(Duration.MaxValue, Duration.FromSeconds(1e300)); CheckEqual(Duration.MaxValue, Duration.FromMinutes(1e300)); CheckEqual(Duration.MaxValue, Duration.FromHours(1e300)); CheckEqual(Duration.MaxValue, Duration.FromDays(1e300)); CheckEqual(Duration.MinValue, Duration.FromMilliseconds(-1e300)); CheckEqual(Duration.MinValue, Duration.FromSeconds(-1e300)); CheckEqual(Duration.MinValue, Duration.FromMinutes(-1e300)); CheckEqual(Duration.MinValue, Duration.FromHours(-1e300)); CheckEqual(Duration.MinValue, Duration.FromDays(-1e300)); } public void FromTicksNearTheEnds_IsMonotoneAndFinite() { long[] counts = { long.MinValue, long.MinValue + 1L, long.MinValue + 2L, long.MinValue + 3L, -1L, 0L, 1L, long.MaxValue - 2L, long.MaxValue - 1L, long.MaxValue, }; for (int i = 0; i < counts.Length; i++) { CheckTrue(Duration.FromTicks(counts[i]).IsFinite); if (i > 0) { CheckTrue(Duration.FromTicks(counts[i - 1]) <= Duration.FromTicks(counts[i])); } } } public void AddingPastTheTopRepeatedly_StaysAtMaxValue() { Duration total = Duration.Zero; Duration step = Duration.FromTicks(MaxTicks / 3L); for (int i = 0; i < 10; i++) { total += step; } CheckEqual(Duration.MaxValue, total); } public void SubtractingPastTheBottomRepeatedly_StaysAtMinValue() { Duration total = Duration.Zero; Duration step = Duration.FromTicks(MaxTicks / 3L); for (int i = 0; i < 10; i++) { total -= step; } CheckEqual(Duration.MinValue, total); } public void SaturationIsNotUndoneByTheOppositeOperation() { Duration one = Duration.FromTicks(1L); CheckEqual(Duration.FromTicks(MaxTicks - 1L), (Duration.MaxValue + one) - one); CheckEqual(Duration.FromTicks(MinTicks + 1L), (Duration.MinValue - one) + one); } public void AnInfiniteInputIsTheOnlyWayToAnInfiniteResult() { CheckTrue((Duration.MaxValue + Duration.Forever).IsInfinite); CheckTrue((Duration.MinValue - Duration.Forever).IsInfinite); CheckTrue((Duration.Forever * 2.0).IsInfinite); CheckTrue((Duration.FromTicks(1L) * double.PositiveInfinity).IsInfinite); CheckTrue((Duration.FromTicks(1L) * double.MaxValue).IsFinite); CheckTrue(Duration.FromSeconds(double.PositiveInfinity).IsInfinite); CheckTrue(Duration.FromSeconds(double.MaxValue).IsFinite); } } public class FromTimeSpanTests : TestFixture { public void KeepsEveryTick() { CheckEqual(OfTicks(19L), (Duration)TimeSpan.FromTicks(19L)); CheckEqual(OfTicks(-19L), (Duration)TimeSpan.FromTicks(-19L)); CheckEqual(15000000L, ((Duration)TimeSpan.FromSeconds(1.5)).Ticks); } public void InfiniteTimeSpan_IsForever() { CheckEqual(Duration.Forever, (Duration)Timeout.InfiniteTimeSpan); } public void OtherNegativeTimeSpan_IsAnOrdinaryNegativeLength() { CheckEqual(OfTicks(-20000L), (Duration)TimeSpan.FromMilliseconds(-2.0)); } public void TheTimeSpanExtremes_AreTheFiniteExtremes() { CheckEqual(Duration.MaxValue, (Duration)TimeSpan.MaxValue); CheckEqual(Duration.MinValue, (Duration)TimeSpan.MinValue); CheckTrue(((Duration)TimeSpan.MaxValue).IsFinite); CheckTrue(((Duration)TimeSpan.MinValue).IsFinite); } public void TheTimeSpansJustInsideTheExtremes_AreTheFiniteExtremes() { CheckEqual(Duration.MaxValue, (Duration)TimeSpan.FromTicks(long.MaxValue - 1L)); CheckEqual(Duration.MinValue, (Duration)TimeSpan.FromTicks(long.MinValue + 2L)); CheckEqual(Duration.MinValue, (Duration)TimeSpan.FromTicks(long.MinValue + 1L)); } public void CanBePassedWhereADurationIsWanted() { CheckEqual(Duration.FromSeconds(3.0), Duration.Max(TimeSpan.FromSeconds(3.0), Duration.Zero)); } } public class ToTimeSpanTests : TestFixture { public void KeepsEveryTick() { CheckEqual(TimeSpan.FromTicks(19L), OfTicks(19L).ToTimeSpan()); CheckEqual(TimeSpan.FromSeconds(-1.5), Duration.FromSeconds(-1.5).ToTimeSpan()); } public void Forever_IsInfiniteTimeSpan() { CheckEqual(Timeout.InfiniteTimeSpan, Duration.Forever.ToTimeSpan()); } public void NegativeForever_Throws() { CheckThrow(typeof(OverflowException)); (-Duration.Forever).ToTimeSpan(); } public void TheFiniteExtremes_Convert() { CheckEqual(TimeSpan.FromTicks(long.MaxValue - 1L), Duration.MaxValue.ToTimeSpan()); CheckEqual(TimeSpan.FromTicks(long.MinValue + 2L), Duration.MinValue.ToTimeSpan()); } public void AnOverflowedSum_ConvertsAsTheLongestFiniteTimeSpan() { CheckEqual(TimeSpan.FromTicks(long.MaxValue - 1L), (Duration.MaxValue + OfTicks(1L)).ToTimeSpan()); } } } }