private static IEnumerable <SpecificationException> ShouldBeLikeInternal(object obj, object expected, string nodeName, HashSet <ReferentialEqualityTuple> visited) { // Stop at already checked <actual,expected>-pairs to prevent infinite loops (cycles in object graphs). Additionally // this also avoids re-equality-evaluation for already compared pairs. var objExpectedTuple = new ReferentialEqualityTuple(obj, expected); if (visited.Contains(objExpectedTuple)) { return(Enumerable.Empty <SpecificationException>()); } visited.Add(objExpectedTuple); ObjectGraphHelper.INode expectedNode = null; var nodeType = typeof(ObjectGraphHelper.LiteralNode); if (obj != null && expected != null) { expectedNode = ObjectGraphHelper.GetGraph(expected); nodeType = expectedNode.GetType(); } if (nodeType == typeof(ObjectGraphHelper.LiteralNode)) { try { obj.ShouldEqual(expected); } catch (SpecificationException ex) { return(new[] { NewException($"{{0}}:{Environment.NewLine}{ex.Message}", nodeName) }); } return(Enumerable.Empty <SpecificationException>()); } if (nodeType == typeof(ObjectGraphHelper.SequenceNode)) { if (obj == null) { var errorMessage = PrettyPrintingExtensions.FormatErrorMessage(null, expected); return(new[] { NewException($"{{0}}:{Environment.NewLine}{errorMessage}", nodeName) }); } var actualNode = ObjectGraphHelper.GetGraph(obj); if (actualNode.GetType() != typeof(ObjectGraphHelper.SequenceNode)) { var errorMessage = $" Expected: Array or Sequence{Environment.NewLine} But was: {obj.GetType()}"; return(new[] { NewException($"{{0}}:{Environment.NewLine}{errorMessage}", nodeName) }); } var expectedValues = ((ObjectGraphHelper.SequenceNode)expectedNode)?.ValueGetters.ToArray(); var actualValues = ((ObjectGraphHelper.SequenceNode)actualNode).ValueGetters.ToArray(); var expectedCount = expectedValues?.Length ?? 0; var actualCount = actualValues.Length; if (expectedCount != actualCount) { var errorMessage = string.Format(" Expected: Sequence length of {1}{0} But was: {2}", Environment.NewLine, expectedCount, actualCount); return(new[] { NewException($"{{0}}:{Environment.NewLine}{errorMessage}", nodeName) }); } return(Enumerable.Range(0, expectedCount) .SelectMany(i => ShouldBeLikeInternal(actualValues.ElementAt(i)(), expectedValues?.ElementAt(i)(), $"{nodeName}[{i}]", visited))); } if (nodeType == typeof(ObjectGraphHelper.KeyValueNode)) { var actualNode = ObjectGraphHelper.GetGraph(obj); if (actualNode.GetType() != typeof(ObjectGraphHelper.KeyValueNode)) { var errorMessage = $" Expected: Class{Environment.NewLine} But was: {obj?.GetType()}"; return(new[] { NewException($"{{0}}:{Environment.NewLine}{errorMessage}", nodeName) }); } var expectedKeyValues = ((ObjectGraphHelper.KeyValueNode)expectedNode)?.KeyValues; var actualKeyValues = ((ObjectGraphHelper.KeyValueNode)actualNode).KeyValues; return(expectedKeyValues?.SelectMany(kv => { var fullNodeName = string.IsNullOrEmpty(nodeName) ? kv.Name : $"{nodeName}.{kv.Name}"; var actualKeyValue = actualKeyValues.SingleOrDefault(k => k.Name == kv.Name); if (actualKeyValue == null) { var errorMessage = string.Format(" Expected: {1}{0} But was: Not Defined", Environment.NewLine, kv.ValueGetter().ToUsefulString()); return new[] { NewException($"{{0}}:{Environment.NewLine}{errorMessage}", fullNodeName) }; } return ShouldBeLikeInternal(actualKeyValue.ValueGetter(), kv.ValueGetter(), fullNodeName, visited); })); } throw new InvalidOperationException("Unknown node type"); }
static IEnumerable <SpecificationException> ShouldBeLikeInternal(object obj, object expected, string nodeName, List <object> visited) { if (IsReferenceTypeNotNullOrString(obj) && IsReferenceTypeNotNullOrString(expected)) { if (visited.Any(o => ReferenceEquals(o, expected))) { return(Enumerable.Empty <SpecificationException>()); } visited.Add(expected); } ObjectGraphHelper.INode expectedNode = null; var nodeType = typeof(ObjectGraphHelper.LiteralNode); if (obj != null && expected != null) { expectedNode = ObjectGraphHelper.GetGraph(expected); nodeType = expectedNode.GetType(); } if (nodeType == typeof(ObjectGraphHelper.LiteralNode)) { try { obj.ShouldEqual(expected); } catch (SpecificationException ex) { return(new[] { NewException(string.Format("{{0}}:{0}{1}", Environment.NewLine, ex.Message), nodeName) }); } return(Enumerable.Empty <SpecificationException>()); } else if (nodeType == typeof(ObjectGraphHelper.SequenceNode)) { if (obj == null) { var errorMessage = PrettyPrintingExtensions.FormatErrorMessage(null, expected); return(new[] { NewException(string.Format("{{0}}:{0}{1}", Environment.NewLine, errorMessage), nodeName) }); } var actualNode = ObjectGraphHelper.GetGraph(obj); if (actualNode.GetType() != typeof(ObjectGraphHelper.SequenceNode)) { var errorMessage = string.Format(" Expected: Array or Sequence{0} But was: {1}", Environment.NewLine, obj.GetType()); return(new[] { NewException(string.Format("{{0}}:{0}{1}", Environment.NewLine, errorMessage), nodeName) }); } var expectedValues = ((ObjectGraphHelper.SequenceNode)expectedNode).ValueGetters; var actualValues = ((ObjectGraphHelper.SequenceNode)actualNode).ValueGetters; var expectedCount = Enumerable.Count <Func <object> >(expectedValues); var actualCount = Enumerable.Count <Func <object> >(actualValues); if (expectedCount != actualCount) { var errorMessage = string.Format(" Expected: Sequence length of {1}{0} But was: {2}", Environment.NewLine, expectedCount, actualCount); return(new[] { NewException(string.Format("{{0}}:{0}{1}", Environment.NewLine, errorMessage), nodeName) }); } return(Enumerable.Range(0, expectedCount) .SelectMany(i => ShouldBeLikeInternal(Enumerable.ElementAt <Func <object> >(actualValues, i)(), Enumerable.ElementAt <Func <object> >(expectedValues, i)(), string.Format("{0}[{1}]", nodeName, i), visited))); } else if (nodeType == typeof(ObjectGraphHelper.KeyValueNode)) { var actualNode = ObjectGraphHelper.GetGraph(obj); if (actualNode.GetType() != typeof(ObjectGraphHelper.KeyValueNode)) { var errorMessage = string.Format(" Expected: Class{0} But was: {1}", Environment.NewLine, obj.GetType()); return(new[] { NewException(string.Format("{{0}}:{0}{1}", Environment.NewLine, errorMessage), nodeName) }); } var expectedKeyValues = ((ObjectGraphHelper.KeyValueNode)expectedNode).KeyValues; var actualKeyValues = ((ObjectGraphHelper.KeyValueNode)actualNode).KeyValues; return(expectedKeyValues .SelectMany(kv => { var fullNodeName = string.IsNullOrEmpty(nodeName) ? kv.Name : string.Format("{0}.{1}", nodeName, kv.Name); var actualKeyValue = actualKeyValues.SingleOrDefault(k => k.Name == kv.Name); if (actualKeyValue == null) { var errorMessage = string.Format(" Expected: {1}{0} But was: Not Defined", Environment.NewLine, kv.ValueGetter().ToUsefulString()); return new[] { NewException(string.Format("{{0}}:{0}{1}", Environment.NewLine, errorMessage), fullNodeName) }; } return ShouldBeLikeInternal(actualKeyValue.ValueGetter(), kv.ValueGetter(), fullNodeName, visited); })); } else { throw new InvalidOperationException("Unknown node type"); } }
static IEnumerable <SpecificationException> ShouldBeLikeObjectGraphInternal( object obj, object expected, string nodeName, double tolerance) { ObjectGraphHelper.INode expectedNode = null; var nodeType = typeof(ObjectGraphHelper.LiteralNode); if (expected != null) { expectedNode = ObjectGraphHelper.GetGraph(expected); nodeType = expectedNode.GetType(); } if (nodeType == typeof(ObjectGraphHelper.LiteralNode)) { try { if (obj is double) { ((double)obj).ShouldBeCloseTo((double)expected, tolerance); } else if (obj is float) { ((float)obj).ShouldBeCloseTo((float)expected, (float)tolerance); } else { obj.ShouldEqual(expected); } } catch (SpecificationException ex) { return(new[] { NewException(string.Format("{{0}}:{0}{1}", Environment.NewLine, ex.Message), nodeName) }); } return(Enumerable.Empty <SpecificationException>()); } if (nodeType == typeof(ObjectGraphHelper.SequenceNode)) { if (obj == null) { var errorMessage = PrettyPrintingExtensions.FormatErrorMessage(null, expected); return(new[] { NewException(string.Format("{{0}}:{0}{1}", Environment.NewLine, errorMessage), nodeName) }); } var actualNode = ObjectGraphHelper.GetGraph(obj); if (actualNode.GetType() != typeof(ObjectGraphHelper.SequenceNode)) { var errorMessage = string.Format( " Expected: Array or Sequence{0} But was: {1}", Environment.NewLine, obj.GetType()); return(new[] { NewException(string.Format("{{0}}:{0}{1}", Environment.NewLine, errorMessage), nodeName) }); } var expectedValues = ((ObjectGraphHelper.SequenceNode)expectedNode).ValueGetters; var actualValues = ((ObjectGraphHelper.SequenceNode)actualNode).ValueGetters; var expectedCount = expectedValues.Count(); var actualCount = actualValues.Count(); if (expectedCount != actualCount) { var errorMessage = string.Format( " Expected: Sequence length of {1}{0} But was: {2}", Environment.NewLine, expectedCount, actualCount); return(new[] { NewException(string.Format("{{0}}:{0}{1}", Environment.NewLine, errorMessage), nodeName) }); } return (Enumerable.Range(0, expectedCount) .SelectMany( i => ShouldBeLikeObjectGraphInternal( actualValues.ElementAt(i)(), expectedValues.ElementAt(i)(), string.Format("{0}[{1}]", nodeName, i), tolerance))); } if (nodeType == typeof(ObjectGraphHelper.KeyValueNode)) { var actualNode = ObjectGraphHelper.GetGraph(obj); if (actualNode.GetType() != typeof(ObjectGraphHelper.KeyValueNode)) { var errorMessage = string.Format( " Expected: Class{0} But was: {1}", Environment.NewLine, obj.GetType()); return(new[] { NewException(string.Format("{{0}}:{0}{1}", Environment.NewLine, errorMessage), nodeName) }); } var expectedKeyValues = ((ObjectGraphHelper.KeyValueNode)expectedNode).KeyValues; var actualKeyValues = ((ObjectGraphHelper.KeyValueNode)actualNode).KeyValues; return(expectedKeyValues.SelectMany( kv => { var fullNodeName = string.IsNullOrEmpty(nodeName) ? kv.Name : string.Format("{0}.{1}", nodeName, kv.Name); var actualKeyValue = actualKeyValues.SingleOrDefault(k => k.Name == kv.Name); if (actualKeyValue == null) { var errorMessage = string.Format( " Expected: {1}{0} But was: Not Defined", Environment.NewLine, ToUsefulString(kv.ValueGetter())); return new[] { NewException( string.Format("{{0}}:{0}{1}", Environment.NewLine, errorMessage), fullNodeName) }; } return ShouldBeLikeObjectGraphInternal( actualKeyValue.ValueGetter(), kv.ValueGetter(), fullNodeName, tolerance); })); } throw new InvalidOperationException("Unknown node type"); }