/// <summary> /// Test two numeric values for equality, performing the usual numeric /// conversions and using a provided or default tolerance. If the tolerance /// provided is Empty, this method may set it to a default tolerance. /// </summary> /// <param name="expected">The expected value</param> /// <param name="actual">The actual value</param> /// <param name="tolerance">A reference to the tolerance in effect</param> /// <returns>True if the values are equal</returns> public static bool AreEqual( object expected, object actual, ref Tolerance tolerance ) { if ( expected is double || actual is double ) return AreEqual( Convert.ToDouble(expected), Convert.ToDouble(actual), ref tolerance ); if ( expected is float || actual is float ) return AreEqual( Convert.ToSingle(expected), Convert.ToSingle(actual), ref tolerance ); if (tolerance.Mode == ToleranceMode.Ulps) throw new InvalidOperationException("Ulps may only be specified for floating point arguments"); if ( expected is decimal || actual is decimal ) return AreEqual( Convert.ToDecimal(expected), Convert.ToDecimal(actual), tolerance ); if (expected is ulong || actual is ulong) return AreEqual(Convert.ToUInt64(expected), Convert.ToUInt64(actual), tolerance ); if ( expected is long || actual is long ) return AreEqual( Convert.ToInt64(expected), Convert.ToInt64(actual), tolerance ); if ( expected is uint || actual is uint ) return AreEqual( Convert.ToUInt32(expected), Convert.ToUInt32(actual), tolerance ); return AreEqual( Convert.ToInt32(expected), Convert.ToInt32(actual), tolerance ); }
private static bool AreEqual( int expected, int actual, Tolerance tolerance ) { switch (tolerance.Mode) { case ToleranceMode.None: return expected.Equals(actual); case ToleranceMode.Linear: int intTolerance = Convert.ToInt32(tolerance.Value); if (intTolerance > 0) return Math.Abs(expected - actual) <= intTolerance; return expected.Equals(actual); case ToleranceMode.Percent: if (expected == 0) return expected.Equals(actual); double relativeError = Math.Abs( (double)(expected - actual) / (double)expected); return (relativeError <= Convert.ToDouble(tolerance.Value) / 100.0); default: throw new ArgumentException("Unknown tolerance mode specified", "mode"); } }
private static bool AreEqual( float expected, float actual, ref Tolerance tolerance ) { if ( float.IsNaN(expected) && float.IsNaN(actual) ) return true; // handle infinity specially since subtracting two infinite values gives // NaN and the following test fails. mono also needs NaN to be handled // specially although ms.net could use either method. if (float.IsInfinity(expected) || float.IsNaN(expected) || float.IsNaN(actual)) { return expected.Equals(actual); } if (tolerance.IsEmpty && GlobalSettings.DefaultFloatingPointTolerance > 0.0d) tolerance = new Tolerance(GlobalSettings.DefaultFloatingPointTolerance); switch (tolerance.Mode) { case ToleranceMode.None: return expected.Equals(actual); case ToleranceMode.Linear: return Math.Abs(expected - actual) <= Convert.ToDouble(tolerance.Value); case ToleranceMode.Percent: if (expected == 0.0f) return expected.Equals(actual); float relativeError = Math.Abs((expected - actual) / expected); return (relativeError <= Convert.ToSingle(tolerance.Value) / 100.0f); #if !NETCF_1_0 case ToleranceMode.Ulps: return FloatingPointNumerics.AreAlmostEqualUlps( expected, actual, Convert.ToInt32(tolerance.Value)); #endif default: throw new ArgumentException("Unknown tolerance mode specified", "mode"); } }
private static bool AreEqual( uint expected, uint actual, Tolerance tolerance ) { switch (tolerance.Mode) { case ToleranceMode.None: return expected.Equals(actual); case ToleranceMode.Linear: uint uintTolerance = Convert.ToUInt32(tolerance.Value); if(uintTolerance > 0) { uint diff = expected >= actual ? expected - actual : actual - expected; return diff <= uintTolerance; } return expected.Equals( actual ); case ToleranceMode.Percent: if(expected == 0u) return expected.Equals(actual); // Can't do a simple Math.Abs() here since it's unsigned uint difference = Math.Max(expected, actual) - Math.Min(expected, actual); double relativeError = Math.Abs((double)difference / (double)expected ); return (relativeError <= Convert.ToDouble(tolerance.Value) / 100.0); default: throw new ArgumentException("Unknown tolerance mode specified", "mode"); } }
/// <summary> /// Display Expected and Actual lines for given values, including /// a tolerance value on the expected line. /// </summary> /// <param name="expected">The expected value</param> /// <param name="actual">The actual value causing the failure</param> /// <param name="tolerance">The tolerance within which the test was made</param> public override void DisplayDifferences(object expected, object actual, Tolerance tolerance) { WriteExpectedLine(expected, tolerance); WriteActualLine(actual); }
/// <summary> /// Write the generic 'Expected' line for a given value /// and tolerance. /// </summary> /// <param name="expected">The expected value</param> /// <param name="tolerance">The tolerance within which the test was made</param> private void WriteExpectedLine(object expected, Tolerance tolerance) { Write(Pfx_Expected); WriteExpectedValue(expected); if (tolerance != null && !tolerance.IsEmpty) { WriteConnector("+/-"); WriteExpectedValue(tolerance.Value); if (tolerance.Mode != ToleranceMode.Linear) Write(" {0}", tolerance.Mode); } WriteLine(); }
/// <summary> /// Display Expected and Actual lines for given values, including /// a tolerance value on the Expected line. /// </summary> /// <param name="expected">The expected value</param> /// <param name="actual">The actual value causing the failure</param> /// <param name="tolerance">The tolerance within which the test was made</param> public abstract void DisplayDifferences(object expected, object actual, Tolerance tolerance);
private bool EnumerablesEqual(IEnumerable x, IEnumerable y, ref Tolerance tolerance) { IEnumerator expectedEnum = x.GetEnumerator(); IEnumerator actualEnum = y.GetEnumerator(); int count; for (count = 0; ; count++) { bool expectedHasData = expectedEnum.MoveNext(); bool actualHasData = actualEnum.MoveNext(); if (!expectedHasData && !actualHasData) return true; if (expectedHasData != actualHasData || !AreEqual(expectedEnum.Current, actualEnum.Current, ref tolerance)) { FailurePoint fp = new FailurePoint(); fp.Position = count; fp.ExpectedHasData = expectedHasData; if (expectedHasData) fp.ExpectedValue = expectedEnum.Current; fp.ActualHasData = actualHasData; if (actualHasData) fp.ActualValue = actualEnum.Current; failurePoints.Insert(0, fp); return false; } } }
private bool DictionariesEqual(IDictionary x, IDictionary y, ref Tolerance tolerance) { if (x.Count != y.Count) return false; CollectionTally tally = new CollectionTally(this, x.Keys); if (!tally.TryRemove(y.Keys) || tally.Count > 0) return false; foreach (object key in x.Keys) if (!AreEqual(x[key], y[key], ref tolerance)) return false; return true; }
/// <summary> /// Helper method to compare two arrays /// </summary> private bool ArraysEqual(Array x, Array y, ref Tolerance tolerance) { int rank = x.Rank; if (rank != y.Rank) return false; for (int r = 1; r < rank; r++) if (x.GetLength(r) != y.GetLength(r)) return false; return EnumerablesEqual((IEnumerable)x, (IEnumerable)y, ref tolerance); }
/// <summary> /// Compares two objects for equality within a tolerance. /// </summary> public bool AreEqual(object x, object y, ref Tolerance tolerance) { this.failurePoints = new ArrayList(); if (x == null && y == null) return true; if (x == null || y == null) return false; if (object.ReferenceEquals(x, y)) return true; Type xType = x.GetType(); Type yType = y.GetType(); EqualityAdapter externalComparer = GetExternalComparer(x, y); if (externalComparer != null) return externalComparer.AreEqual(x, y); if (xType.IsArray && yType.IsArray && !compareAsCollection) return ArraysEqual((Array)x, (Array)y, ref tolerance); if (x is IDictionary && y is IDictionary) return DictionariesEqual((IDictionary)x, (IDictionary)y, ref tolerance); //if (x is ICollection && y is ICollection) // return CollectionsEqual((ICollection)x, (ICollection)y, ref tolerance); if (x is IEnumerable && y is IEnumerable && !(x is string && y is string)) return EnumerablesEqual((IEnumerable)x, (IEnumerable)y, ref tolerance); if (x is string && y is string) return StringsEqual((string)x, (string)y); if (x is Stream && y is Stream) return StreamsEqual((Stream)x, (Stream)y); if (x is DirectoryInfo && y is DirectoryInfo) return DirectoriesEqual((DirectoryInfo)x, (DirectoryInfo)y); if (Numerics.IsNumericType(x) && Numerics.IsNumericType(y)) return Numerics.AreEqual(x, y, ref tolerance); if (tolerance != null && tolerance.Value is TimeSpan) { TimeSpan amount = (TimeSpan)tolerance.Value; if (x is DateTime && y is DateTime) return ((DateTime)x - (DateTime)y).Duration() <= amount; if (x is TimeSpan && y is TimeSpan) return ((TimeSpan)x - (TimeSpan)y).Duration() <= amount; } #if CLR_2_0 || CLR_4_0 if (FirstImplementsIEquatableOfSecond(xType, yType)) return InvokeFirstIEquatableEqualsSecond(x, y); else if (FirstImplementsIEquatableOfSecond(yType, xType)) return InvokeFirstIEquatableEqualsSecond(y, x); #endif return x.Equals(y); }