<PackageReference Include="NUnit" Version="3.0.0-rc-3" />

NUnitEqualityComparer

public class NUnitEqualityComparer
NUnitEqualityComparer encapsulates NUnit's handling of equality tests between objects.
using NUnit.Framework.Compatibility; using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Reflection; namespace NUnit.Framework.Constraints { public class NUnitEqualityComparer { public class FailurePoint { public long Position; public object ExpectedValue; public object ActualValue; public bool ExpectedHasData; public bool ActualHasData; } private bool caseInsensitive; private bool compareAsCollection; private List<EqualityAdapter> externalComparers = new List<EqualityAdapter>(); private List<FailurePoint> failurePoints; private static readonly int BUFFER_SIZE = 4096; public static NUnitEqualityComparer Default => new NUnitEqualityComparer(); public bool IgnoreCase { get { return caseInsensitive; } set { caseInsensitive = value; } } public bool CompareAsCollection { get { return compareAsCollection; } set { compareAsCollection = value; } } public IList<EqualityAdapter> ExternalComparers => externalComparers; public IList<FailurePoint> FailurePoints => failurePoints; public bool WithSameOffset { get; set; } public bool AreEqual(object x, object y, ref Tolerance tolerance) { failurePoints = new List<FailurePoint>(); if (x == null && y == null) return true; if (x == null || y == null) return false; if (object.ReferenceEquals(x, y)) return true; Type type = x.GetType(); Type type2 = y.GetType(); Type left = TypeExtensions.GetTypeInfo(type).IsGenericType ? type.GetGenericTypeDefinition() : null; Type left2 = TypeExtensions.GetTypeInfo(type2).IsGenericType ? type2.GetGenericTypeDefinition() : null; EqualityAdapter externalComparer = GetExternalComparer(x, y); if (externalComparer != null) return externalComparer.AreEqual(x, y); if (type.IsArray && type2.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 DictionaryEntry && y is DictionaryEntry) return DictionaryEntriesEqual((DictionaryEntry)x, (DictionaryEntry)y, ref tolerance); if (left == typeof(KeyValuePair<, >) && left2 == typeof(KeyValuePair<, >)) { Tolerance tolerance2 = Tolerance.Exact; object value = type.GetProperty("Key").GetValue(x, null); object value2 = type2.GetProperty("Key").GetValue(y, null); object value3 = type.GetProperty("Value").GetValue(x, null); object value4 = type2.GetProperty("Value").GetValue(y, null); if (AreEqual(value, value2, ref tolerance2)) return AreEqual(value3, value4, ref tolerance); return false; } if (x is string && y is string) return StringsEqual((string)x, (string)y); if (x is IEnumerable && y is IEnumerable) return EnumerablesEqual((IEnumerable)x, (IEnumerable)y, ref tolerance); if (x is Stream && y is Stream) return StreamsEqual((Stream)x, (Stream)y); if (x is char && y is char) return CharsEqual((char)x, (char)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 (x is DateTimeOffset && y is DateTimeOffset) { DateTimeOffset left3 = (DateTimeOffset)x; DateTimeOffset right = (DateTimeOffset)y; bool flag; if (tolerance != null && tolerance.Value is TimeSpan) { TimeSpan t = (TimeSpan)tolerance.Value; flag = ((left3 - right).Duration() <= t); } else flag = (left3 == right); if (flag && WithSameOffset) flag = (left3.Offset == right.Offset); return flag; } if (tolerance != null && tolerance.Value is TimeSpan) { TimeSpan t2 = (TimeSpan)tolerance.Value; if (x is DateTime && y is DateTime) return ((DateTime)x - (DateTime)y).Duration() <= t2; if (x is TimeSpan && y is TimeSpan) return ((TimeSpan)x - (TimeSpan)y).Duration() <= t2; } if (FirstImplementsIEquatableOfSecond(type, type2)) return InvokeFirstIEquatableEqualsSecond(x, y); if (type != type2 && FirstImplementsIEquatableOfSecond(type2, type)) return InvokeFirstIEquatableEqualsSecond(y, x); return x.Equals(y); } private static bool FirstImplementsIEquatableOfSecond(Type first, Type second) { foreach (Type equatableGenericArgument in GetEquatableGenericArguments(first)) { if (equatableGenericArgument.IsAssignableFrom(second)) return true; } return false; } private static IList<Type> GetEquatableGenericArguments(Type type) { List<Type> list = new List<Type>(); Type[] interfaces = type.GetInterfaces(); foreach (Type type2 in interfaces) { if (TypeExtensions.GetTypeInfo(type2).IsGenericType && type2.GetGenericTypeDefinition().Equals(typeof(IEquatable<>))) list.Add(type2.GetGenericArguments()[0]); } return list; } private static bool InvokeFirstIEquatableEqualsSecond(object first, object second) { MethodInfo correctGenericEqualsMethod = GetCorrectGenericEqualsMethod(first.GetType(), second.GetType()); if (!(correctGenericEqualsMethod != (MethodInfo)null)) return false; return (bool)correctGenericEqualsMethod.Invoke(first, new object[1] { second }); } private static MethodInfo GetCorrectGenericEqualsMethod(Type first, Type second) { MethodInfo[] methods = first.GetMethods(); MethodInfo[] array = methods; foreach (MethodInfo methodInfo in array) { if (methodInfo.Name == "Equals") { ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 1 && parameters[0].ParameterType != typeof(object) && parameters[0].ParameterType.IsAssignableFrom(second)) return methodInfo; } } return null; } private EqualityAdapter GetExternalComparer(object x, object y) { foreach (EqualityAdapter externalComparer in externalComparers) { if (externalComparer.CanCompare(x, y)) return externalComparer; } return null; } private bool ArraysEqual(Array x, Array y, ref Tolerance tolerance) { int rank = x.Rank; if (rank != y.Rank) return false; for (int i = 1; i < rank; i++) { if (x.GetLength(i) != y.GetLength(i)) return false; } return EnumerablesEqual(x, y, ref tolerance); } private bool DictionariesEqual(IDictionary x, IDictionary y, ref Tolerance tolerance) { if (x.Count != y.Count) return false; CollectionTally collectionTally = new CollectionTally(this, x.Keys); if (!collectionTally.TryRemove(y.Keys) || collectionTally.Count > 0) return false; foreach (object key in x.Keys) { if (!AreEqual(x[key], y[key], ref tolerance)) return false; } return true; } private bool DictionaryEntriesEqual(DictionaryEntry x, DictionaryEntry y, ref Tolerance tolerance) { Tolerance tolerance2 = Tolerance.Exact; if (AreEqual(x.Key, y.Key, ref tolerance2)) return AreEqual(x.Value, y.Value, ref tolerance); return false; } private bool CollectionsEqual(ICollection x, ICollection y, ref Tolerance tolerance) { IEnumerator enumerator = null; IEnumerator enumerator2 = null; try { enumerator = x.GetEnumerator(); enumerator2 = y.GetEnumerator(); int num = 0; bool flag; bool flag2; while (true) { flag = enumerator.MoveNext(); flag2 = enumerator2.MoveNext(); if (!flag && !flag2) return true; if (flag != flag2 || !AreEqual(enumerator.Current, enumerator2.Current, ref tolerance)) break; num++; } FailurePoint failurePoint = new FailurePoint(); failurePoint.Position = num; failurePoint.ExpectedHasData = flag; if (flag) failurePoint.ExpectedValue = enumerator.Current; failurePoint.ActualHasData = flag2; if (flag2) failurePoint.ActualValue = enumerator2.Current; failurePoints.Insert(0, failurePoint); return false; } finally { (enumerator as IDisposable)?.Dispose(); (enumerator2 as IDisposable)?.Dispose(); } } private bool StringsEqual(string x, string y) { string text = caseInsensitive ? x.ToLower() : x; string value = caseInsensitive ? y.ToLower() : y; return text.Equals(value); } private bool CharsEqual(char x, char y) { char c = caseInsensitive ? char.ToLower(x) : x; char c2 = caseInsensitive ? char.ToLower(y) : y; return c == c2; } private bool EnumerablesEqual(IEnumerable x, IEnumerable y, ref Tolerance tolerance) { IEnumerator enumerator = null; IEnumerator enumerator2 = null; try { enumerator = x.GetEnumerator(); enumerator2 = y.GetEnumerator(); int num = 0; bool flag; bool flag2; while (true) { flag = enumerator.MoveNext(); flag2 = enumerator2.MoveNext(); if (!flag && !flag2) return true; if (flag != flag2 || !AreEqual(enumerator.Current, enumerator2.Current, ref tolerance)) break; num++; } FailurePoint failurePoint = new FailurePoint(); failurePoint.Position = num; failurePoint.ExpectedHasData = flag; if (flag) failurePoint.ExpectedValue = enumerator.Current; failurePoint.ActualHasData = flag2; if (flag2) failurePoint.ActualValue = enumerator2.Current; failurePoints.Insert(0, failurePoint); return false; } finally { (enumerator as IDisposable)?.Dispose(); (enumerator2 as IDisposable)?.Dispose(); } } private static bool DirectoriesEqual(DirectoryInfo x, DirectoryInfo y) { if (x.Attributes != y.Attributes || x.CreationTime != y.CreationTime || x.LastAccessTime != y.LastAccessTime) return false; return new SamePathConstraint(x.FullName).ApplyTo(y.FullName).IsSuccess; } private bool StreamsEqual(Stream x, Stream y) { if (x == y) return true; if (!x.CanRead) throw new ArgumentException("Stream is not readable", "expected"); if (!y.CanRead) throw new ArgumentException("Stream is not readable", "actual"); if (!x.CanSeek) throw new ArgumentException("Stream is not seekable", "expected"); if (!y.CanSeek) throw new ArgumentException("Stream is not seekable", "actual"); if (x.Length != y.Length) return false; byte[] array = new byte[BUFFER_SIZE]; byte[] array2 = new byte[BUFFER_SIZE]; BinaryReader binaryReader = new BinaryReader(x); BinaryReader binaryReader2 = new BinaryReader(y); long position = x.Position; long position2 = y.Position; try { binaryReader.BaseStream.Seek(0, SeekOrigin.Begin); binaryReader2.BaseStream.Seek(0, SeekOrigin.Begin); for (long num = 0; num < x.Length; num += BUFFER_SIZE) { binaryReader.Read(array, 0, BUFFER_SIZE); binaryReader2.Read(array2, 0, BUFFER_SIZE); for (int i = 0; i < BUFFER_SIZE; i++) { if (array[i] != array2[i]) { FailurePoint failurePoint = new FailurePoint(); failurePoint.Position = num + i; failurePoint.ExpectedHasData = true; failurePoint.ExpectedValue = array[i]; failurePoint.ActualHasData = true; failurePoint.ActualValue = array2[i]; failurePoints.Insert(0, failurePoint); return false; } } } } finally { x.Position = position; y.Position = position2; } return true; } } }