/// <summary> /// Compare an object of type URI /// </summary> /// <param name="object1"></param> /// <param name="object2"></param> /// <param name="breadCrumb"></param> private void CompareUri(object object1, object object2, string breadCrumb) { Uri uri1 = object1 as Uri; Uri uri2 = object2 as Uri; //This should never happen, null check happens one level up if (uri1 == null || uri2 == null) return; if (uri1.OriginalString != uri2.OriginalString) { Difference difference = new Difference { ExpectedName = ExpectedName, ActualName = ActualName, PropertyName = breadCrumb, Object1Value = NiceString(uri1.OriginalString), Object2Value = NiceString(uri2.OriginalString), ChildPropertyName = "OriginalString" }; Differences.Add(difference); } }
/// <summary> /// Compare a timespan struct /// </summary> /// <param name="object1"></param> /// <param name="object2"></param> /// <param name="breadCrumb"></param> private void CompareTimespan(object object1, object object2, string breadCrumb) { if (((TimeSpan)object1).Ticks != ((TimeSpan)object2).Ticks) { Difference difference = new Difference { ExpectedName = ExpectedName, ActualName = ActualName, PropertyName = breadCrumb, Object1Value = ((TimeSpan)object1).Ticks.ToString(CultureInfo.InvariantCulture), Object2Value = ((TimeSpan)object1).Ticks.ToString(CultureInfo.InvariantCulture), ChildPropertyName = "Ticks" }; Differences.Add(difference); } }
/// <summary> /// Compare an object of type Type (Runtime type) /// </summary> /// <param name="object1"></param> /// <param name="object2"></param> /// <param name="breadCrumb"></param> private void CompareType(object object1, object object2, string breadCrumb) { Type t1 = (Type)object1; Type t2 = (Type)object2; if (t1.FullName != t2.FullName) { Difference difference = new Difference { ExpectedName = ExpectedName, ActualName = ActualName, PropertyName = breadCrumb, Object1Value = t1.FullName, Object2Value = t2.FullName, ChildPropertyName = "FullName" }; Differences.Add(difference); } }
/// <summary> /// Compare a pointer struct /// </summary> /// <param name="object1"></param> /// <param name="object2"></param> /// <param name="breadCrumb"></param> private void ComparePointer(object object1, object object2, string breadCrumb) { if ((object1 is IntPtr && object2 is IntPtr && ((IntPtr)object1) != ((IntPtr)object2)) || (object1 is UIntPtr && object2 is UIntPtr && ((UIntPtr)object1) != ((UIntPtr)object2))) { Difference difference = new Difference { ExpectedName = ExpectedName, ActualName = ActualName, PropertyName = breadCrumb }; Differences.Add(difference); } }
/// <summary> /// Compare a simple type /// </summary> /// <param name="object1"></param> /// <param name="object2"></param> /// <param name="breadCrumb"></param> private void CompareSimpleType(object object1, object object2, string breadCrumb) { //This should never happen, null check happens one level up if (object2 == null || object1 == null) return; IComparable valOne = object1 as IComparable; if (valOne == null) throw new Exception("Expected value does not implement IComparable"); if (valOne.CompareTo(object2) != 0) { Difference difference = new Difference { ExpectedName = ExpectedName, ActualName = ActualName, PropertyName = breadCrumb, Object1Value = object1.ToString(), Object2Value = object2.ToString() }; Differences.Add(difference); } }
/// <summary> /// Compare an integer indexer /// </summary> /// <param name="info"></param> /// <param name="object1"></param> /// <param name="object2"></param> /// <param name="breadCrumb"></param> private void CompareIndexer(PropertyInfo info, object object1, object object2, string breadCrumb) { string currentCrumb; int indexerCount1 = (int)info.ReflectedType.GetProperty("Count").GetGetMethod().Invoke(object1, new object[] { }); int indexerCount2 = (int)info.ReflectedType.GetProperty("Count").GetGetMethod().Invoke(object2, new object[] { }); //Indexers must be the same length if (indexerCount1 != indexerCount2) { currentCrumb = AddBreadCrumb(breadCrumb, info.Name, string.Empty, -1); Difference difference = new Difference { ExpectedName = ExpectedName, ActualName = ActualName, PropertyName = currentCrumb, Object1Value = indexerCount1.ToString(CultureInfo.InvariantCulture), Object2Value = indexerCount2.ToString(CultureInfo.InvariantCulture), ChildPropertyName = "Count" }; Differences.Add(difference); if (Differences.Count >= MaxDifferences) return; } // Run on indexer for (int i = 0; i < indexerCount1; i++) { currentCrumb = AddBreadCrumb(breadCrumb, info.Name, string.Empty, i); object objectValue1 = info.GetValue(object1, new object[] { i }); object objectValue2 = info.GetValue(object2, new object[] { i }); Compare(objectValue1, objectValue2, currentCrumb); if (Differences.Count >= MaxDifferences) return; } }
private void CompareIpEndPoint(object object1, object object2, string breadCrumb) { IPEndPoint ipEndPoint1 = object1 as IPEndPoint; IPEndPoint ipEndPoint2 = object2 as IPEndPoint; //Null check happens above if (ipEndPoint1 == null || ipEndPoint2 == null) return; if (ipEndPoint1.Port != ipEndPoint2.Port) { Difference difference = new Difference { ExpectedName = ExpectedName, ActualName = ActualName, PropertyName = breadCrumb, Object1Value = ipEndPoint1.Port.ToString(CultureInfo.InvariantCulture), Object2Value = ipEndPoint2.Port.ToString(CultureInfo.InvariantCulture), ChildPropertyName = "Port" }; Differences.Add(difference); } if (Differences.Count >= MaxDifferences) return; if (ipEndPoint1.Address.ToString() != ipEndPoint2.Address.ToString()) { Difference difference = new Difference { ExpectedName = ExpectedName, ActualName = ActualName, PropertyName = breadCrumb, Object1Value = ipEndPoint1.Address.ToString(), Object2Value = ipEndPoint2.Address.ToString(), ChildPropertyName = "Address" }; Differences.Add(difference); } }
/// <summary> /// Compare a HashSet /// </summary> /// <param name="object1"></param> /// <param name="object2"></param> /// <param name="breadCrumb"></param> private void CompareHashSet(object object1, object object2, string breadCrumb) { try { _parents.Add(object1); _parents.Add(object2); Type t1 = object1.GetType(); //Get count by reflection since we can't cast it to HashSet<> int hashSet1Count = (int)GetPropertyValue(t1, object1, "Count"); int hashSet2Count = (int)GetPropertyValue(t1, object2, "Count"); //Objects must be the same length if (hashSet1Count != hashSet2Count) { Difference difference = new Difference { ExpectedName = ExpectedName, ActualName = ActualName, PropertyName = breadCrumb, Object1Value = hashSet1Count.ToString(CultureInfo.InvariantCulture), Object2Value = hashSet2Count.ToString(CultureInfo.InvariantCulture), ChildPropertyName = "Count" }; Differences.Add(difference); if (Differences.Count >= MaxDifferences) return; } //Get enumerators by reflection MethodInfo methodInfo = GetMethod(t1, "GetEnumerator"); IEnumerator enumerator1 = (IEnumerator)methodInfo.Invoke(object1, null); IEnumerator enumerator2 = (IEnumerator)methodInfo.Invoke(object2, null); int count = 0; while (enumerator1.MoveNext() && enumerator2.MoveNext()) { string currentBreadCrumb = AddBreadCrumb(breadCrumb, string.Empty, string.Empty, count); Compare(enumerator1.Current, enumerator2.Current, currentBreadCrumb); if (Differences.Count >= MaxDifferences) return; count++; } } finally { _parents.Remove(object1); _parents.Remove(object2); } }
/// <summary> /// Compare an array or something that implements IList /// </summary> /// <param name="object1"></param> /// <param name="object2"></param> /// <param name="breadCrumb"></param> private void CompareIList(object object1, object object2, string breadCrumb) { IList ilist1 = object1 as IList; IList ilist2 = object2 as IList; //This should never happen, null check happens one level up if (ilist1 == null || ilist2 == null) return; try { _parents.Add(object1); _parents.Add(object2); //Objects must be the same length if (ilist1.Count != ilist2.Count) { Difference difference = new Difference { ExpectedName = ExpectedName, ActualName = ActualName, PropertyName = breadCrumb, Object1Value = ilist1.Count.ToString(CultureInfo.InvariantCulture), Object2Value = ilist2.Count.ToString(CultureInfo.InvariantCulture), ChildPropertyName = "Count" }; Differences.Add(difference); if (Differences.Count >= MaxDifferences) return; } IEnumerator enumerator1 = ilist1.GetEnumerator(); IEnumerator enumerator2 = ilist2.GetEnumerator(); int count = 0; while (enumerator1.MoveNext() && enumerator2.MoveNext()) { string currentBreadCrumb = AddBreadCrumb(breadCrumb, string.Empty, string.Empty, count); Compare(enumerator1.Current, enumerator2.Current, currentBreadCrumb); if (Differences.Count >= MaxDifferences) return; count++; } } finally { _parents.Remove(object1); _parents.Remove(object2); } }
/// <summary> /// Compare an enumeration /// </summary> /// <param name="object1"></param> /// <param name="object2"></param> /// <param name="breadCrumb"></param> private void CompareEnum(object object1, object object2, string breadCrumb) { if (object1.ToString() != object2.ToString()) { string currentBreadCrumb = AddBreadCrumb(breadCrumb, object1.GetType().Name, string.Empty, -1); Difference difference = new Difference { ExpectedName = ExpectedName, ActualName = ActualName, PropertyName = currentBreadCrumb, Object1Value = object1.ToString(), Object2Value = object2.ToString() }; Differences.Add(difference); } }
/// <summary> /// Compare two objects /// </summary> /// <param name="object1">The first object to compare</param> /// <param name="object2">The second object to compare</param> /// <param name="breadCrumb">Where we are in the object hiearchy</param> private void Compare(object object1, object object2, string breadCrumb) { //If both null return true if (object1 == null && object2 == null) return; //Check if one of them is null if (object1 == null) { Difference difference = new Difference { ExpectedName = ExpectedName, ActualName = ActualName, PropertyName = breadCrumb, Object1Value = "(null)", Object2Value = NiceString(object2) }; Differences.Add(difference); return; } if (object2 == null) { Difference difference = new Difference { ExpectedName = ExpectedName, ActualName = ActualName, PropertyName = breadCrumb, Object1Value = NiceString(object1), Object2Value = "(null)" }; Differences.Add(difference); return; } Type t1 = object1.GetType(); Type t2 = object2.GetType(); //Objects must be the same type if (t1 != t2 && !IgnoreObjectTypes) { Difference difference = new Difference { ExpectedName = ExpectedName, ActualName = ActualName, PropertyName = breadCrumb, Object1Value = t1.FullName, Object2Value = t2.FullName, ChildPropertyName = "GetType()", MessagePrefix = "Different Types" }; Differences.Add(difference); return; } if (IsUseCustomTypeComparer(t1)) { CompareWithCustomComparer(object1, object2, breadCrumb); } else if (IsTypeOfType(t1)) { CompareType(object1, object2, breadCrumb); } else if (IsIPEndPoint(t1)) { CompareIpEndPoint(object1, object2, breadCrumb); } #if !SILVERLIGHT else if (IsDataset(t1)) { CompareDataset(object1, object2, breadCrumb); } else if (IsDataTable(t1)) { CompareDataTable(object1, object2, breadCrumb); } else if (IsDataRow(t1)) { CompareDataRow(object1, object2, breadCrumb); } #endif else if (IsIList(t1)) //This will do arrays, multi-dimensional arrays and generic lists { CompareIList(object1, object2, breadCrumb); } else if (IsHashSet(t1)) { CompareHashSet(object1, object2, breadCrumb); } else if (IsIDictionary(t1)) { CompareIDictionary(object1, object2, breadCrumb); } else if (IsEnum(t1)) { CompareEnum(object1, object2, breadCrumb); } else if (IsPointer(t1)) { ComparePointer(object1, object2, breadCrumb); } else if (IsUri(t1)) { CompareUri(object1, object2, breadCrumb); } else if (IsSimpleType(t1)) { CompareSimpleType(object1, object2, breadCrumb); } else if (IsClass(t1)) { CompareClass(object1, object2, breadCrumb); } else if (IsTimespan(t1)) { CompareTimespan(object1, object2, breadCrumb); } else if (IsStruct(t1)) { CompareStruct(object1, object2, breadCrumb); } else { throw new NotSupportedException("Cannot compare object of type " + t1.Name); } }
/// <summary> /// Compare all rows in a data table /// </summary> /// <param name="object1"></param> /// <param name="object2"></param> /// <param name="breadCrumb"></param> private void CompareDataTable(object object1, object object2, string breadCrumb) { DataTable dataTable1 = object1 as DataTable; DataTable dataTable2 = object2 as DataTable; //This should never happen, null check happens one level up if (dataTable1 == null || dataTable2 == null) return; //Only compare specific table names if (ElementsToInclude.Count > 0 && !ElementsToInclude.Contains(dataTable1.TableName)) return; //If we should ignore it, skip it if (ElementsToInclude.Count == 0 && ElementsToIgnore.Contains(dataTable1.TableName)) return; //There must be the same amount of rows in the datatable if (dataTable1.Rows.Count != dataTable2.Rows.Count) { Difference difference = new Difference { ExpectedName = ExpectedName, ActualName = ActualName, PropertyName = breadCrumb, Object1Value = dataTable1.Rows.Count.ToString(CultureInfo.InvariantCulture), Object2Value = dataTable2.Rows.Count.ToString(CultureInfo.InvariantCulture), ChildPropertyName = "Rows.Count" }; Differences.Add(difference); if (Differences.Count >= MaxDifferences) return; } //There must be the same amount of columns in the datatable if (dataTable1.Columns.Count != dataTable2.Columns.Count) { Difference difference = new Difference { ExpectedName = ExpectedName, ActualName = ActualName, PropertyName = breadCrumb, Object1Value = dataTable1.Columns.Count.ToString(CultureInfo.InvariantCulture), Object2Value = dataTable2.Columns.Count.ToString(CultureInfo.InvariantCulture), ChildPropertyName = "Columns.Count" }; Differences.Add(difference); if (Differences.Count >= MaxDifferences) return; } for (int i = 0; i < Math.Min(dataTable1.Rows.Count, dataTable2.Rows.Count); i++) { string currentBreadCrumb = AddBreadCrumb(breadCrumb, "Rows", string.Empty, i); CompareDataRow(dataTable1.Rows[i], dataTable2.Rows[i], currentBreadCrumb); if (Differences.Count >= MaxDifferences) return; } }
/// <summary> /// Compare all tables and all rows in all tables /// </summary> /// <param name="object1"></param> /// <param name="object2"></param> /// <param name="breadCrumb"></param> private void CompareDataset(object object1, object object2, string breadCrumb) { DataSet dataSet1 = object1 as DataSet; DataSet dataSet2 = object2 as DataSet; //This should never happen, null check happens one level up if (dataSet1 == null || dataSet2 == null) return; //There must be the same amount of tables in the dataset if (dataSet1.Tables.Count != dataSet2.Tables.Count) { Difference difference = new Difference { ExpectedName = ExpectedName, ActualName = ActualName, PropertyName = breadCrumb, Object1Value = dataSet1.Tables.Count.ToString(CultureInfo.InvariantCulture), Object2Value = dataSet2.Tables.Count.ToString(CultureInfo.InvariantCulture), ChildPropertyName = "Tables.Count" }; Differences.Add(difference); if (Differences.Count >= MaxDifferences) return; } for (int i = 0; i < Math.Min(dataSet1.Tables.Count, dataSet2.Tables.Count); i++) { string currentBreadCrumb = AddBreadCrumb(breadCrumb, "Tables", string.Empty, dataSet1.Tables[i].TableName); CompareDataTable(dataSet1.Tables[i], dataSet2.Tables[i], currentBreadCrumb); if (Differences.Count >= MaxDifferences) return; } }
/// <summary> /// Compare all columns in a data row /// </summary> /// <param name="object1"></param> /// <param name="object2"></param> /// <param name="breadCrumb"></param> private void CompareDataRow(object object1, object object2, string breadCrumb) { DataRow dataRow1 = object1 as DataRow; DataRow dataRow2 = object2 as DataRow; //This should never happen, null check happens one level up if (dataRow1 == null || dataRow2 == null) return; for (int i = 0; i < dataRow1.Table.Columns.Count; i++) { //Only compare specific column names if (ElementsToInclude.Count > 0 && !ElementsToInclude.Contains(dataRow1.Table.Columns[i].ColumnName)) continue; //If we should ignore it, skip it if (ElementsToInclude.Count == 0 && ElementsToIgnore.Contains(dataRow1.Table.Columns[i].ColumnName)) continue; //If we should ignore read only, skip it if (!CompareReadOnly && dataRow1.Table.Columns[i].ReadOnly) continue; //Both are null if (dataRow1.IsNull(i) && dataRow2.IsNull(i)) continue; string currentBreadCrumb = AddBreadCrumb(breadCrumb, string.Empty, string.Empty, dataRow1.Table.Columns[i].ColumnName); //Check if one of them is null if (dataRow1.IsNull(i)) { Difference difference = new Difference { ExpectedName = ExpectedName, ActualName = ActualName, PropertyName = currentBreadCrumb, Object1Value = "(null)", Object2Value = NiceString(object2) }; Differences.Add(difference); return; } if (dataRow2.IsNull(i)) { Difference difference = new Difference { ExpectedName = ExpectedName, ActualName = ActualName, PropertyName = currentBreadCrumb, Object1Value = NiceString(object1), Object2Value = "(null)" }; Differences.Add(difference); return; } Compare(dataRow1[i], dataRow2[i], currentBreadCrumb); if (Differences.Count >= MaxDifferences) return; } }