static void Check(object expected, object actual, ShouldLookLikeOptions options, Pair <string> path, HashSet <object> visitedExpecteds) { if (actual == null && expected == null) { return; } if (actual == null || expected == null) { Reject(expected, actual, path, options); } if (visitedExpecteds.Contains(expected)) { return; } visitedExpecteds.Add(expected); var type = actual.GetType(); if (expected.GetType() != type) { Reject(expected, actual, path, options); } if (type.IsValueType || actual is string) { if (!actual.Equals(expected)) { Reject(expected, actual, path, options); } return; } if (actual is System.Collections.IEnumerable) { var allItems = ToObjectList(expected).Concat(ToObjectList(actual)).Where(x => x != null).ToList(); if (!allItems.Any()) { return; } var concreteItemType = allItems.First().GetType(); var enumOption = options.EnumerableOptions.FirstOrDefault(x => x.ItemType.IsAssignableFrom(concreteItemType)) ?? new EnumerableOption(); if (enumOption.Comparison == EnumerableComparison.Strict) { CheckEnumerableStrict(expected, actual, options, path, visitedExpecteds); } else { CheckEnumerableUnordered(expected, actual, options, enumOption, path, visitedExpecteds); } return; } var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy); foreach (var fi in fields) { Check(fi.GetValue(expected), fi.GetValue(actual), options, fi.Name.Cons(path), visitedExpecteds); } var propsWithGetters = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy) .Select(pi => new { Prop = pi, Get = pi.GetGetMethod(false) }).Where(z => z.Get != null); foreach (var z in propsWithGetters) { object propExpected = null; object propActual = null; try { propExpected = z.Get.Invoke(expected, new object[0]); propActual = z.Get.Invoke(actual, new object[0]); } catch (Exception) { Reject(propExpected, propActual, z.Prop.Name.Cons(path), options); return; } Check(propExpected, propActual, options, z.Prop.Name.Cons(path), visitedExpecteds); } }
static void CheckEnumerableStrict(object expected, object actual, ShouldLookLikeOptions options, Pair <string> path, HashSet <object> visitedExpecteds) { var expectedObjects = ToObjectList(expected); var actualObjects = ToObjectList(actual); if (expectedObjects.Count != actualObjects.Count) { Reject(expected, actual, path, options); } var q = expectedObjects.Zip(actualObjects, (e, a) => new { Expected = e, Actual = a }); int c = 0; foreach (var z in q) { Check(z.Expected, z.Actual, options, Bracketize(c).Cons(path), visitedExpecteds); c++; } }