private static AggregatedDifference ScanEnumeration(IEnumerable firstItem, IEnumerable otherItem, Func <int, string> namingCallback, ICollection <object> firstSeen) { var index = 0; var mayBeEquivalent = true; var expected = new List <KeyValuePair <object, int> >(); var unexpected = new List <KeyValuePair <object, int> >(); var aggregatedDifferences = new Dictionary <int, AggregatedDifference>(); var valueDifferences = new AggregatedDifference(); var scanner = otherItem.GetEnumerator(); foreach (var item in firstItem) { var firstItemName = namingCallback(index); if (!scanner.MoveNext()) { valueDifferences.Add(DifferenceDetails.WasNotExpected(firstItemName, item, index)); unexpected.Add(new KeyValuePair <object, int>(item, index)); break; } var aggregatedDifference = ValueDifference(item, firstItemName, scanner.Current, index, firstSeen); if (aggregatedDifference.IsDifferent) { aggregatedDifferences.Add(index, aggregatedDifference); if (!aggregatedDifference.IsEquivalent) { // try to see it was at a different position var indexOrigin = expected.FindIndex(pair => FluentEquivalent(pair.Key, item)); if (indexOrigin >= 0) { // we found the value at another index valueDifferences.Add(DifferenceDetails.WasFoundElseWhere(firstItemName, item, index, expected[indexOrigin].Value)); expected.RemoveAt(indexOrigin); aggregatedDifferences.Remove(indexOrigin); aggregatedDifferences.Remove(index); } else { unexpected.Add(new KeyValuePair <object, int>(item, index)); } // what about the expected value var indexOther = unexpected.FindIndex(pair => FluentEquivalent(pair.Key, scanner.Current)); if (indexOther >= 0) { valueDifferences.Add(DifferenceDetails.WasFoundElseWhere(firstItemName, unexpected[indexOther].Key, unexpected[indexOther].Value, index)); aggregatedDifferences.Remove(unexpected[indexOther].Value); unexpected.RemoveAt(indexOther); } else { expected.Add(new KeyValuePair <object, int>(scanner.Current, index)); } } } index++; } if (scanner.MoveNext()) { valueDifferences.Add(DifferenceDetails.WasNotFound(namingCallback(index), scanner.Current, index)); mayBeEquivalent = false; } foreach (var differencesValue in aggregatedDifferences.Values) { valueDifferences.Merge(differencesValue); } for (var i = 0; i < Math.Min(unexpected.Count, expected.Count); i++) { //aggregatedDifferences.Remove(unexpected[i].Value); valueDifferences.Add(DifferenceDetails.WasFoundInsteadOf(namingCallback(unexpected[i].Value), unexpected[i].Key, expected[i].Key)); } if (mayBeEquivalent && valueDifferences.IsDifferent) { valueDifferences.IsEquivalent = expected.Count == 0 && unexpected.Count == 0; } return(valueDifferences); }
private static AggregatedDifference ValueDifferenceDictionary(IReadOnlyDictionary <object, object> sutDico, string sutName, IReadOnlyDictionary <object, object> expectedDico, ICollection <object> firstItemsSeen) { // TODO: improve error messages var valueDifferences = new AggregatedDifference { IsEquivalent = true }; var actualKeyIterator = sutDico.Keys.GetEnumerator(); var expectedKeyIterator = expectedDico.Keys.GetEnumerator(); var stillExpectedKeys = true; var stillActualKeys = true; var index = 0; for (; ;) { stillExpectedKeys = stillExpectedKeys && expectedKeyIterator.MoveNext(); stillActualKeys = stillActualKeys && actualKeyIterator.MoveNext(); if (!stillExpectedKeys) { // no more expected keys if (!stillActualKeys) { // we're done break; } // the sut has extra key(s) valueDifferences.Add(DifferenceDetails.WasNotExpected($"{sutName}[{actualKeyIterator.Current.ToStringProperlyFormatted()}]", sutDico[actualKeyIterator.Current], index)); valueDifferences.IsEquivalent = false; } else if (!stillActualKeys) { // key not found valueDifferences.IsEquivalent = false; valueDifferences.Add(DifferenceDetails.WasNotFound($"{sutName}[{expectedKeyIterator.Current.ToStringProperlyFormatted()}]", expectedDico[expectedKeyIterator.Current], 0)); } else { var actualKey = actualKeyIterator.Current; var actualKeyName = $"{sutName} key[{index}]"; var itemDiffs = ValueDifference(actualKey, actualKeyName, expectedKeyIterator.Current, index, firstItemsSeen); if (!itemDiffs.IsDifferent) { // same key, check the values itemDiffs = ValueDifference(sutDico[actualKey], $"{sutName}[{actualKey.ToStringProperlyFormatted()}]", expectedDico[actualKey], index, firstItemsSeen); valueDifferences.IsEquivalent &= (!itemDiffs.IsDifferent || itemDiffs.IsEquivalent); } else //if (valueDifferences.IsEquivalent) { // check if the dictionaries are equivalent anyway var expectedIndex = expectedDico.ContainsKey(actualKey) ? expectedDico.Keys.ToList().FindIndex(x => x == actualKey) : -1; if (expectedIndex >= 0) { itemDiffs = ValueDifference(sutDico[actualKey], $"{sutName}[{actualKey.ToStringProperlyFormatted()}]", expectedDico[actualKey], index, firstItemsSeen); valueDifferences.IsEquivalent &= itemDiffs.IsEquivalent || !itemDiffs.IsDifferent; valueDifferences.Add( DifferenceDetails.WasFoundElseWhere($"{sutName} entry {actualKey.ToStringProperlyFormatted()}", expectedDico[actualKey], index, expectedIndex)); } else { valueDifferences.Add(DifferenceDetails.WasNotExpected($"{sutName}'s key {actualKey.ToStringProperlyFormatted()}", sutDico[actualKey], index)); valueDifferences.IsEquivalent = false; } } valueDifferences.Merge(itemDiffs); } index++; } return(valueDifferences); }