/// <summary> /// Gets a value indicating whether this step can handle the current subject and/or expectation. /// </summary> public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config) { Type subjectType = config.GetSubjectType(context); return ((subjectType != null) && subjectType.IsEnum()) || ((context.Expectation != null) && context.Expectation.GetType().IsEnum()); }
public void AssertEqualityUsing(IEquivalencyValidationContext context) { if (ContinueRecursion(context.SelectedMemberPath)) { AssertionScope scope = AssertionScope.Current; scope.AddNonReportable("context", context.IsRoot ? "subject" : context.SelectedMemberDescription); scope.AddNonReportable("subject", context.Subject); scope.AddNonReportable("expectation", context.Expectation); var objectTracker = scope.Get<ObjectTracker>("objects"); if (!objectTracker.IsCyclicReference(new ObjectReference(context.Subject, context.SelectedMemberPath))) { bool wasHandled = AssertionOptions.EquivalencySteps .Where(s => s.CanHandle(context, config)) .Any(step => step.Handle(context, this, config)); if (!wasHandled) { Execute.Assertion.FailWith( "No IEquivalencyStep was found to handle the context. " + "This is likely a bug in Fluent Assertions."); } } } }
public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { return config.UserEquivalencySteps .Where(s => s.CanHandle(context, config)) .Any(step => step.Handle(context, parent, config)); }
public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { bool expectationIsNotNull = AssertionScope.Current .ForCondition(!ReferenceEquals(context.Expectation, null)) .FailWith( "Expected {context:subject} to be <null>, but found {0}.", context.Subject); bool subjectIsNotNull = AssertionScope.Current.ForCondition( !ReferenceEquals(context.Subject, null)) .FailWith( "Expected {context:object} to be {0}{reason}, but found {1}.", context.Expectation, context.Subject); IEnumerable<SelectedMemberInfo> selectedMembers = GetSelectedMembers(context, config).ToArray(); if (context.IsRoot && !selectedMembers.Any()) { throw new InvalidOperationException( "No members were found for comparison. " + "Please specify some members to include in the comparison or choose a more meaningful assertion."); } if (expectationIsNotNull && subjectIsNotNull) { foreach (var selectedMemberInfo in selectedMembers) { AssertMemberEquality(context, parent, selectedMemberInfo, config); } } return true; }
private static bool AreComparable(IEquivalencyValidationContext context, Array subjectAsArray) { return IsArray(context.Expectation) && HaveSameRank(context.Expectation, subjectAsArray) && HaveSameDimensions(context.Expectation, subjectAsArray); }
public bool Handle( IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { var equivalencyValidationContext = CreateAdjustedCopy(context); return eqivalencyStep.Handle(equivalencyValidationContext, parent, config); }
private static SelectedMemberInfo FindMatchFor(SelectedMemberInfo selectedMemberInfo, IEquivalencyValidationContext context, IEquivalencyAssertionOptions config) { var query = from rule in config.MatchingRules let match = rule.Match(selectedMemberInfo, context.Expectation, context.SelectedMemberDescription, config) where match != null select match; return query.FirstOrDefault(); }
/// <summary> /// Gets a value indicating whether this step can handle the current subject and/or expectation. /// </summary> public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config) { Type type = config.GetSubjectType(context); return (type != null) && (type != typeof (object)) && config.IsValueType(type) && !type.IsArray; }
private void CompareByValue(IEquivalencyValidationContext context) { var subjectType = Enum.GetUnderlyingType(context.Subject.GetType()); var subjectUnderlyingValue = Convert.ChangeType(context.Subject, subjectType, CultureInfo.InvariantCulture); var expectationType = Enum.GetUnderlyingType(context.Expectation.GetType()); var expectationUnderlyingValue = Convert.ChangeType(context.Expectation, expectationType, CultureInfo.InvariantCulture); subjectUnderlyingValue.Should().Be(expectationUnderlyingValue, context.Reason, context.ReasonArgs); }
private static void AssertMemberEquality(IEquivalencyValidationContext context, IEquivalencyValidator parent, SelectedMemberInfo selectedMemberInfo, IEquivalencyAssertionOptions config) { var matchingMember = FindMatchFor(selectedMemberInfo, context, config); if (matchingMember != null) { var nestedContext = context.CreateForNestedMember(selectedMemberInfo, matchingMember); if (nestedContext != null) { parent.AssertEqualityUsing(nestedContext); } } }
internal IEnumerable<SelectedMemberInfo> GetSelectedMembers(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config) { IEnumerable<SelectedMemberInfo> members = Enumerable.Empty<SelectedMemberInfo>(); foreach (var selectionRule in config.SelectionRules) { members = selectionRule.SelectMembers(members, context, config); } return members; }
private static EquivalencyValidationContext CreateAdjustedCopy(IEquivalencyValidationContext context) { return new EquivalencyValidationContext { CompileTimeType = context.CompileTimeType, Expectation = context.Expectation, SelectedMemberDescription = context.SelectedMemberDescription, SelectedMemberInfo = context.SelectedMemberInfo, SelectedMemberPath = CollectionMemberSubjectInfo.GetAdjustedPropertyPath(context.SelectedMemberPath), Because = context.Because, BecauseArgs = context.BecauseArgs, Subject = context.Subject }; }
/// <summary> /// Applies a step as part of the task to compare two objects for structural equality. /// </summary> /// <value> /// Should return <c>true</c> if the subject matches the expectation or if no additional assertions /// have to be executed. Should return <c>false</c> otherwise. /// </value> /// <remarks> /// May throw when preconditions are not met or if it detects mismatching data. /// </remarks> public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { if (AssertExpectationIsCollection(context.Expectation)) { var validator = new EnumerableEquivalencyValidator(parent, context) { Recursive = context.IsRoot || config.IsRecursive, OrderingRules = config.OrderingRules }; validator.Execute(ToArray(context.Subject), ToArray(context.Expectation)); } return true; }
/// <summary> /// Applies a step as part of the task to compare two objects for structural equality. /// </summary> /// <value> /// Should return <c>true</c> if the subject matches the expectation or if no additional assertions /// have to be executed. Should return <c>false</c> otherwise. /// </value> /// <remarks> /// May throw when preconditions are not met or if it detects mismatching data. /// </remarks> public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { Type subjectType = config.GetSubjectType(context); Type[] interfaces = GetIEnumerableInterfaces(subjectType); bool multipleInterfaces = (interfaces.Count() > 1); if (multipleInterfaces) { IEnumerable<Type> enumerableTypes = interfaces.Select( type => type.GetGenericArguments().Single()); AssertionScope.Current.FailWith( String.Format( "{{context:Subject}} is enumerable for more than one type. " + "It is not known which type should be use for equivalence.{0}" + "IEnumerable is implemented for the following types: {1}", Environment.NewLine, String.Join(", ", enumerableTypes))); } if (AssertExpectationIsCollection(context.Expectation)) { var validator = new EnumerableEquivalencyValidator(parent, context) { Recursive = context.IsRoot || config.IsRecursive, OrderingRules = config.OrderingRules }; Type typeOfEnumeration = GetTypeOfEnumeration(subjectType); Expression subjectToArray = ToArray(context.Subject, typeOfEnumeration); Expression expectationToArray = Expression.Constant(EnumerableEquivalencyStep.ToArray(context.Expectation)); MethodCallExpression executeExpression = Expression.Call( Expression.Constant(validator), ExpressionExtensions.GetMethodName(() => validator.Execute<object>(null,null)), new Type[] { typeOfEnumeration }, subjectToArray, expectationToArray); Expression.Lambda(executeExpression).Compile().DynamicInvoke(); } return true; }
public bool AssertEquality(IEquivalencyValidationContext context) { if (context.RuntimeType != typeof (double)) { return false; } var expectation = context.Expectation.As<double>(); var actual = context.Subject.As<double>(); var delta = actual - expectation; Execute.Assertion.ForCondition(Math.Abs(delta) <= TOLERANCE) .BecauseOf(string.Empty) .FailWith(string.Format( "Expected property {0} to be {1} (+/- {2}), but found {3}. Differed by {4}.", context.PropertyInfo.Name, expectation, TOLERANCE, actual, delta )); return true; }
/// <summary> /// Applies a step as part of the task to compare two objects for structural equality. /// </summary> /// <value> /// Should return <c>true</c> if the subject matches the expectation or if no additional assertions /// have to be executed. Should return <c>false</c> otherwise. /// </value> /// <remarks> /// May throw when preconditions are not met or if it detects mismatching data. /// </remarks> public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator structuralEqualityValidator, IEquivalencyAssertionOptions config) { if (!ReferenceEquals(context.Expectation, null) && !ReferenceEquals(context.Subject, null) && !context.Subject.GetType().IsSameOrInherits(context.Expectation.GetType())) { Type expectationType = context.Expectation.GetType(); object convertedSubject; if (TryChangeType(context.Subject, expectationType, out convertedSubject)) { var newContext = context.CreateWithDifferentSubject(convertedSubject, expectationType); structuralEqualityValidator.AssertEqualityUsing(newContext); return true; } } return false; }
/// <summary> /// Applies a step as part of the task to compare two objects for structural equality. /// </summary> /// <value> /// Should return <c>true</c> if the subject matches the expectation or if no additional assertions /// have to be executed. Should return <c>false</c> otherwise. /// </value> /// <remarks> /// May throw when preconditions are not met or if it detects mismatching data. /// </remarks> public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { switch (config.EnumEquivalencyHandling) { case EnumEquivalencyHandling.ByValue: CompareByValue(context); break; case EnumEquivalencyHandling.ByName: context.Subject.ToString() .Should() .Be(context.Expectation.ToString(), context.Reason, context.ReasonArgs); break; default: throw new InvalidOperationException(string.Format("Don't know how to handle {0}", config.EnumEquivalencyHandling)); } return true; }
internal static IEquivalencyValidationContext CreateForCollectionItem <T>(this IEquivalencyValidationContext context, string index, object subject, T expectation) { string memberDescription = "[" + index + "]"; string propertyPath = (context.SelectedMemberDescription.Length == 0) ? "item" : context.SelectedMemberDescription + String.Empty; return(new EquivalencyValidationContext { SelectedMemberInfo = context.SelectedMemberInfo, Subject = subject, Expectation = expectation, SelectedMemberPath = context.SelectedMemberPath.Combine(memberDescription, String.Empty), SelectedMemberDescription = propertyPath + memberDescription, Because = context.Because, BecauseArgs = context.BecauseArgs, CompileTimeType = typeof(T), RootIsCollection = context.RootIsCollection, Tracer = context.Tracer }); }
internal static IEquivalencyValidationContext CreateForNestedMember(this IEquivalencyValidationContext context, SelectedMemberInfo nestedMember, SelectedMemberInfo matchingProperty) { string memberDescription = nestedMember.Name; string propertyPath = (context.SelectedMemberDescription.Length == 0) ? "member " : context.SelectedMemberDescription + "."; return(new EquivalencyValidationContext { SelectedMemberInfo = nestedMember, Subject = matchingProperty.GetValue(context.Subject, null), Expectation = nestedMember.GetValue(context.Expectation, null), SelectedMemberPath = context.SelectedMemberPath.Combine(memberDescription, "."), SelectedMemberDescription = propertyPath + memberDescription, Because = context.Because, BecauseArgs = context.BecauseArgs, CompileTimeType = nestedMember.MemberType, RootIsCollection = context.RootIsCollection, Tracer = context.Tracer }); }
public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationContext context, IEquivalencyValidator nestedValidator) { if (!IsCollection(comparands.GetExpectedType(context.Options))) { return(EquivalencyResult.ContinueWithNext); } if (AssertSubjectIsCollection(comparands.Subject)) { var validator = new EnumerableEquivalencyValidator(nestedValidator, context) { Recursive = context.CurrentNode.IsRoot || context.Options.IsRecursive, OrderingRules = context.Options.OrderingRules }; validator.Execute(ToArray(comparands.Subject), ToArray(comparands.Expectation)); } return(EquivalencyResult.AssertionCompleted); }
/// <summary> /// Applies a step as part of the task to compare two objects for structural equality. /// </summary> /// <value> /// Should return <c>true</c> if the subject matches the expectation or if no additional assertions /// have to be executed. Should return <c>false</c> otherwise. /// </value> /// <remarks> /// May throw when preconditions are not met or if it detects mismatching data. /// </remarks> public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { switch (config.EnumEquivalencyHandling) { case EnumEquivalencyHandling.ByValue: HandleByValue(context); break; case EnumEquivalencyHandling.ByName: HandleByName(context); break; default: throw new InvalidOperationException(string.Format("Do not know how to handle {0}", config.EnumEquivalencyHandling)); } return(true); }
#pragma warning restore SA1110 public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationContext context, IEquivalencyValidator nestedValidator) { Type expectedType = comparands.GetExpectedType(context.Options); if (comparands.Expectation is null || !IsGenericCollection(expectedType)) { return(EquivalencyResult.ContinueWithNext); } Type[] interfaceTypes = GetIEnumerableInterfaces(expectedType); AssertionScope.Current .ForCondition(interfaceTypes.Length == 1) .FailWith(() => new FailReason("{context:Expectation} implements {0}, so cannot determine which one " + "to use for asserting the equivalency of the collection. ", interfaceTypes.Select(type => "IEnumerable<" + type.GetGenericArguments().Single() + ">"))); if (AssertSubjectIsCollection(comparands.Subject)) { var validator = new EnumerableEquivalencyValidator(nestedValidator, context) { Recursive = context.CurrentNode.IsRoot || context.Options.IsRecursive, OrderingRules = context.Options.OrderingRules }; Type typeOfEnumeration = GetTypeOfEnumeration(expectedType); var subjectAsArray = EnumerableEquivalencyStep.ToArray(comparands.Subject); try { HandleMethod.MakeGenericMethod(typeOfEnumeration).Invoke(null, new[] { validator, subjectAsArray, comparands.Expectation }); } catch (TargetInvocationException e) { e.Unwrap().Throw(); } } return(EquivalencyResult.AssertionCompleted); }
public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationContext context, IEquivalencyValidator nestedValidator) { if (!context.CurrentNode.IsRoot && !context.Options.IsRecursive) { return(EquivalencyResult.ContinueWithNext); } bool expectationIsNotNull = AssertionScope.Current .ForCondition(comparands.Expectation is not null) .BecauseOf(context.Reason) .FailWith( "Expected {context:subject} to be <null>{reason}, but found {0}.", comparands.Subject); bool subjectIsNotNull = AssertionScope.Current .ForCondition(comparands.Subject is not null) .BecauseOf(context.Reason) .FailWith( "Expected {context:object} to be {0}{reason}, but found {1}.", comparands.Expectation, comparands.Subject); if (expectationIsNotNull && subjectIsNotNull) { IMember[] selectedMembers = GetMembersFromExpectation(context.CurrentNode, comparands, context.Options).ToArray(); if (context.CurrentNode.IsRoot && !selectedMembers.Any()) { throw new InvalidOperationException( "No members were found for comparison. " + "Please specify some members to include in the comparison or choose a more meaningful assertion."); } foreach (IMember selectedMember in selectedMembers) { AssertMemberEquality(comparands, context, nestedValidator, selectedMember, context.Options); } } return(EquivalencyResult.AssertionCompleted); }
protected override EquivalencyResult OnHandle(Comparands comparands, IEquivalencyValidationContext context, IEquivalencyValidator nestedValidator) { var subject = comparands.Subject as DataRelation; var expectation = comparands.Expectation as DataRelation; if (expectation is null) { if (subject is not null) { AssertionScope.Current.FailWith("Expected {context:DataRelation} to be null, but found {0}", subject); } } else { if (subject is null) { if (comparands.Subject is null) { AssertionScope.Current.FailWith("Expected {context:DataRelation} value to be non-null, but found null"); } else { AssertionScope.Current.FailWith("Expected {context:DataRelation} of type {0}, but found {1} instead", expectation.GetType(), comparands.Subject.GetType()); } } else { var selectedMembers = GetMembersFromExpectation(context.CurrentNode, comparands, context.Options) .ToDictionary(member => member.Name); CompareScalarProperties(subject, expectation, selectedMembers); CompareCollections(context, comparands, nestedValidator, context.Options, selectedMembers); CompareRelationConstraints(context, nestedValidator, subject, expectation, selectedMembers); } } return(EquivalencyResult.AssertionCompleted); }
/// <summary> /// Defines how a subject's property is compared for equality with the same property of the expectation. /// </summary> /// <param name="subjectProperty"> /// Provides details about the subject's property. /// </param> /// <param name="subject"> /// The value of the subject's property. /// </param> /// <param name="expectation"> /// The value of a property on expectation object that was identified /// </param> /// <returns> /// Returns <c>true</c> if the rule was applied correctly and the assertion didn't cause any exceptions. /// Returns <c>false</c> if this rule doesn't support the subject's type. /// Throws if the rule did support the data type but assertion fails. /// </returns> public bool AssertEquality(IEquivalencyValidationContext context) { if (predicate(context)) { bool succeeded = AssertionScope.Current .ForCondition(ReferenceEquals(context.Expectation, null) || context.Expectation.GetType().IsSameOrInherits(typeof(TSubject))) .FailWith("Expected " + context.PropertyDescription + " to be a {0}{reason}, but found a {1}", context.Expectation.GetType(), context.PropertyInfo.PropertyType); if (succeeded) { action(new AssertionContext(context.PropertyInfo, (TSubject)context.Subject, (TSubject)context.Expectation, context.Reason, context.ReasonArgs)); } return(true); } return(false); }
public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { if (ReferenceEquals(context.Subject, context.Expectation)) { return(true); } FieldInfo[] fieldInfos = typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public); if (!fieldInfos.All(fi => fi.FieldType == typeof(float) || fi.FieldType == typeof(double))) { throw new InvalidOperationException("Cannot compare vectors. One or more fields is not a float or double."); } List <T> vectors = ((IEnumerable <T>)context.Subject).ToList(); List <T> expectedVectors = ((IEnumerable <T>)context.Expectation).ToList(); if (vectors.Count != expectedVectors.Count) { return(false); } for (int i = 0; i < vectors.Count; i++) { T left = vectors[i]; T right = expectedVectors[i]; foreach (FieldInfo fieldInfo in fieldInfos) { double leftValue = Convert.ToDouble(fieldInfo.GetValue(left)); double rightValue = Convert.ToDouble(fieldInfo.GetValue(right)); if (!NearlyEqual(leftValue, rightValue, _epsilon)) { return(false); } } } return(true); }
/// <summary> /// Applies a step as part of the task to compare two objects for structural equality. /// </summary> /// <value> /// Should return <c>true</c> if the subject matches the expectation or if no additional assertions /// have to be executed. Should return <c>false</c> otherwise. /// </value> /// <remarks> /// May throw when preconditions are not met or if it detects mismatching data. /// </remarks> public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { Type subjectType = config.GetSubjectType(context); var interfaceTypes = GetIEnumerableInterfaces(subjectType) .Select(type => "IEnumerable<" + type.GetGenericArguments().Single() + ">") .ToList(); AssertionScope.Current .ForCondition(interfaceTypes.Count() == 1) .FailWith("{context:Subject} implements {0}, so cannot determine which one " + "to use for asserting the equivalency of the collection. ", interfaceTypes); if (AssertExpectationIsCollection(context.Expectation, context.Subject)) { var validator = new EnumerableEquivalencyValidator(parent, context) { Recursive = context.IsRoot || config.IsRecursive, OrderingRules = config.OrderingRules }; Type typeOfEnumeration = GetTypeOfEnumeration(subjectType); Expression subjectToArray = ToArray(context.Subject, typeOfEnumeration); Expression expectationToArray = Expression.Constant(EnumerableEquivalencyStep.ToArray(context.Expectation)); MethodCallExpression executeExpression = Expression.Call( Expression.Constant(validator), ExpressionExtensions.GetMethodName(() => validator.Execute <object>(null, null)), new[] { typeOfEnumeration }, subjectToArray, expectationToArray); Expression.Lambda(executeExpression).Compile().DynamicInvoke(); } return(true); }
/// <summary> /// Applies a step as part of the task to compare two objects for structural equality. /// </summary> /// <value> /// Should return <c>true</c> if the subject matches the expectation or if no additional assertions /// have to be executed. Should return <c>false</c> otherwise. /// </value> /// <remarks> /// May throw when preconditions are not met or if it detects mismatching data. /// </remarks> public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { Type subjectType = config.GetSubjectType(context); var interfaceTypes = GetIEnumerableInterfaces(subjectType) .Select(type => "IEnumerable<" + type.GetGenericArguments().Single() + ">") .ToList(); AssertionScope.Current .ForCondition(interfaceTypes.Count() == 1) .FailWith("{context:Subject} implements {0}, so cannot determine which one " + "to use for asserting the equivalency of the collection. ", interfaceTypes); if (AssertExpectationIsCollection(context.Expectation, context.Subject)) { var validator = new EnumerableEquivalencyValidator(parent, context) { Recursive = context.IsRoot || config.IsRecursive, OrderingRules = config.OrderingRules }; Type typeOfEnumeration = GetTypeOfEnumeration(subjectType); Expression subjectToArray = ToArray(context.Subject, typeOfEnumeration); Expression expectationToArray = Expression.Constant(EnumerableEquivalencyStep.ToArray(context.Expectation)); MethodCallExpression executeExpression = Expression.Call( Expression.Constant(validator), ExpressionExtensions.GetMethodName(() => validator.Execute<object>(null, null)), new[] {typeOfEnumeration}, subjectToArray, expectationToArray); Expression.Lambda(executeExpression).Compile().DynamicInvoke(); } return true; }
/// <summary> /// Applies a step as part of the task to compare two objects for structural equality. /// </summary> /// <value> /// Should return <c>true</c> if the subject matches the expectation or if no additional assertions /// have to be executed. Should return <c>false</c> otherwise. /// </value> /// <remarks> /// May throw when preconditions are not met or if it detects mismatching data. /// </remarks> public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { if (!ValidateAgainstNulls(context)) { return(true); } bool expectationIsString = ValidateAgainstType <string>(context); if (expectationIsString) { string subject = (string)context.Subject; string expectation = (context.Expectation == null) ? null : (string)context.Expectation; subject.Should() .Be(expectation, context.Reason, context.ReasonArgs); } return(true); }
/// <summary> /// Applies a step as part of the task to compare two objects for structural equality. /// </summary> /// <value> /// Should return <c>true</c> if the subject matches the expectation or if no additional assertions /// have to be executed. Should return <c>false</c> otherwise. /// </value> /// <remarks> /// May throw when preconditions are not met or if it detects mismatching data. /// </remarks> public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { switch (config.EnumEquivalencyHandling) { case EnumEquivalencyHandling.ByValue: CompareByValue(context); break; case EnumEquivalencyHandling.ByName: context.Subject.ToString() .Should() .Be(context.Expectation.ToString(), context.Reason, context.ReasonArgs); break; default: throw new InvalidOperationException(string.Format("Don't know how to handle {0}", config.EnumEquivalencyHandling)); } return(true); }
protected override EquivalencyResult OnHandle(Comparands comparands, IEquivalencyValidationContext context, IEquivalencyValidator nestedValidator) { if (comparands.Subject is not Constraint) { AssertionScope.Current .FailWith("Expected {context:constraint} to be a value of type Constraint, but found {0}", comparands.Subject.GetType()); } else { var subject = (Constraint)comparands.Subject; var expectation = (Constraint)comparands.Expectation; var selectedMembers = GetMembersFromExpectation(comparands, context.CurrentNode, context.Options) .ToDictionary(member => member.Name); CompareCommonProperties(context, nestedValidator, context.Options, subject, expectation, selectedMembers); bool matchingType = subject.GetType() == expectation.GetType(); AssertionScope.Current .ForCondition(matchingType) .FailWith("Expected {context:constraint} to be of type {0}, but found {1}", expectation.GetType(), subject.GetType()); if (matchingType) { if ((subject is UniqueConstraint subjectUniqueConstraint) && (expectation is UniqueConstraint expectationUniqueConstraint)) { CompareConstraints(nestedValidator, context, subjectUniqueConstraint, expectationUniqueConstraint, selectedMembers); } else if ((subject is ForeignKeyConstraint subjectForeignKeyConstraint) && (expectation is ForeignKeyConstraint expectationForeignKeyConstraint)) { CompareConstraints(nestedValidator, context, subjectForeignKeyConstraint, expectationForeignKeyConstraint, selectedMembers); }
internal static IEquivalencyValidationContext CreateForDictionaryItem <TKey, TValue>( this IEquivalencyValidationContext context, TKey key, TValue subject, object expectation) { string memberDescription = "[" + key + "]"; string propertyPath = (context.SelectedMemberDescription.Length == 0) ? "pair" : context.SelectedMemberDescription + String.Empty; return(new EquivalencyValidationContext { SelectedMemberInfo = context.SelectedMemberInfo, Subject = subject, Expectation = expectation, SelectedMemberPath = context.SelectedMemberPath.Combine(memberDescription, String.Empty), SelectedMemberDescription = propertyPath + memberDescription, Because = context.Because, BecauseArgs = context.BecauseArgs, CompileTimeType = typeof(TValue), RootIsCollection = context.RootIsCollection }); }
private static void CompareExtendedProperties(Comparands comparands, IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config, Dictionary <string, IMember> selectedMembers) { foreach (var collectionName in new[] { nameof(DataSet.ExtendedProperties), nameof(DataSet.Relations) }) { if (selectedMembers.TryGetValue(collectionName, out IMember expectationMember)) { IMember matchingMember = FindMatchFor(expectationMember, comparands.Subject, context.CurrentNode, config); if (matchingMember is not null) { var nestedComparands = new Comparands { Subject = matchingMember.GetValue(comparands.Subject), Expectation = expectationMember.GetValue(comparands.Expectation), CompileTimeType = expectationMember.Type }; IEquivalencyValidationContext nestedContext = context.AsNestedMember(expectationMember); parent.RecursivelyAssertEquality(nestedComparands, nestedContext); } } } }
/// <summary> /// Defines how a subject's property is compared for equality with the same property of the expectation. /// </summary> /// <param name="subjectProperty"> /// Provides details about the subject's property. /// </param> /// <param name="subject"> /// The value of the subject's property. /// </param> /// <param name="expectation"> /// The value of a property on expectation object that was identified /// </param> /// <returns> /// Returns <c>true</c> if the rule was applied correctly and the assertion didn't cause any exceptions. /// Returns <c>false</c> if this rule doesn't support the subject's type. /// Throws if the rule did support the data type but assertion fails. /// </returns> public bool AssertEquality(IEquivalencyValidationContext context) { if (predicate(context)) { bool expectationisNull = ReferenceEquals(context.Expectation, null); bool succeeded = AssertionScope.Current .ForCondition(expectationisNull || context.Expectation.GetType().IsSameOrInherits(typeof(TSubject))) .FailWith("Expected " + context.SelectedMemberDescription + " to be a {0}{reason}, but found a {1}", !expectationisNull ? context.Expectation.GetType() : null, context.SelectedMemberInfo.MemberType); if (succeeded) { action(AssertionContext <TSubject> .CreateFromEquivalencyValidationContext(context)); } return(true); } return(false); }
/// <summary> /// Applies a step as part of the task to compare two objects for structural equality. /// </summary> /// <value> /// Should return <c>true</c> if the subject matches the expectation or if no additional assertions /// have to be executed. Should return <c>false</c> otherwise. /// </value> /// <remarks> /// May throw when preconditions are not met or if it detects mismatching data. /// </remarks> public virtual bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { var subject = (IDictionary)context.Subject; var expectation = context.Expectation as IDictionary; if (PreconditionsAreMet(context, expectation, subject)) { foreach (object key in subject.Keys) { if (config.IsRecursive) { parent.AssertEqualityUsing(context.CreateForDictionaryItem(key, subject[key], expectation[key])); } else { subject[key].Should().Be(expectation[key], context.Because, context.BecauseArgs); } } } return(true); }
public void AssertEqualityUsing(IEquivalencyValidationContext context) { if (ContinueRecursion(context.SelectedMemberPath)) { AssertionScope scope = AssertionScope.Current; scope.Context = (context.SelectedMemberDescription.Length == 0) ? scope.Context : context.SelectedMemberDescription; scope.AddNonReportable("subject", context.Subject); scope.AddNonReportable("expectation", context.Expectation); var objectTracker = scope.Get <CyclicReferenceDetector>("objects"); bool isComplexType = IsComplexType(context.Expectation); var objectReference = new ObjectReference(context.Expectation, context.SelectedMemberPath, isComplexType); if (!objectTracker.IsCyclicReference(objectReference)) { bool wasHandled = false; foreach (var step in AssertionOptions.EquivalencySteps) { if (step.CanHandle(context, config)) { if (step.Handle(context, this, config)) { wasHandled = true; break; } } } if (!wasHandled) { Execute.Assertion.FailWith( "No IEquivalencyStep was found to handle the context. " + "This is likely a bug in Fluent Assertions."); } } } }
private static void CompareRelationConstraints(IEquivalencyValidationContext context, IEquivalencyValidator parent, DataRelation subject, DataRelation expectation, Dictionary <string, IMember> selectedMembers) { CompareDataRelationConstraints( parent, context, subject, expectation, selectedMembers, "Child", selectedMembers.ContainsKey(nameof(DataRelation.ChildTable)), selectedMembers.ContainsKey(nameof(DataRelation.ChildColumns)), selectedMembers.ContainsKey(nameof(DataRelation.ChildKeyConstraint)), r => r.ChildColumns, r => r.ChildTable); CompareDataRelationConstraints( parent, context, subject, expectation, selectedMembers, "Parent", selectedMembers.ContainsKey(nameof(DataRelation.ParentTable)), selectedMembers.ContainsKey(nameof(DataRelation.ParentColumns)), selectedMembers.ContainsKey(nameof(DataRelation.ParentKeyConstraint)), r => r.ParentColumns, r => r.ParentTable); }
/// <summary> /// Applies a step as part of the task to compare two objects for structural equality. /// </summary> /// <value> /// Should return <c>true</c> if the subject matches the expectation or if no additional assertions /// have to be executed. Should return <c>false</c> otherwise. /// </value> /// <remarks> /// May throw when preconditions are not met or if it detects mismatching data. /// </remarks> public virtual bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { var subject = (IDictionary)context.Subject; var expectation = context.Expectation as IDictionary; if (PreconditionsAreMet(context, expectation, subject)) { foreach (object key in subject.Keys) { if (config.IsRecursive) { parent.AssertEqualityUsing(context.CreateForDictionaryItem(key, subject[key], expectation[key])); } else { subject[key].Should().Be(expectation[key], context.Reason, context.ReasonArgs); } } } return true; }
/// <summary> /// Applies a step as part of the task to compare two objects for structural equality. /// </summary> /// <value> /// Should return <c>true</c> if the subject matches the expectation or if no additional assertions /// have to be executed. Should return <c>false</c> otherwise. /// </value> /// <remarks> /// May throw when preconditions are not met or if it detects mismatching data. /// </remarks> public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { switch (config.EnumEquivalencyHandling) { case EnumEquivalencyHandling.ByValue: long subjectsUnderlyingValue = Convert.ToInt64(context.Subject); long expectationsUnderlyingValue = Convert.ToInt64(context.Expectation); subjectsUnderlyingValue.Should().Be(expectationsUnderlyingValue, context.Because, context.BecauseArgs); break; case EnumEquivalencyHandling.ByName: context.Subject.ToString().Should().Be(context.Expectation.ToString(), context.Because, context.BecauseArgs); break; default: throw new InvalidOperationException(string.Format("Don't know how to handle {0}", config.EnumEquivalencyHandling)); } return true; }
private static void CompareDataRelationConstraints( IEquivalencyValidator parent, IEquivalencyValidationContext context, DataRelation subject, DataRelation expectation, Dictionary <string, IMember> selectedMembers, string relationDirection, bool compareTable, bool compareColumns, bool compareKeyConstraint, Func <DataRelation, DataColumn[]> getColumns, Func <DataRelation, DataTable> getOtherTable) { if (compareColumns) { CompareDataRelationColumns(subject, expectation, getColumns); } if (compareTable) { CompareDataRelationTable(subject, expectation, getOtherTable); } if (compareKeyConstraint) { CompareDataRelationKeyConstraint(subject, expectation, parent, context, selectedMembers, relationDirection); } }
/// <summary> /// Applies a step as part of the task to compare two objects for structural equality. /// </summary> /// <value> /// Should return <c>true</c> if the subject matches the expectation or if no additional assertions /// have to be executed. Should return <c>false</c> otherwise. /// </value> /// <remarks> /// May throw when preconditions are not met or if it detects mismatching data. /// </remarks> public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator structuralEqualityValidator, IEquivalencyAssertionOptions config) { if (!ReferenceEquals(context.Expectation, null) && !ReferenceEquals(context.Subject, null) && !context.Subject.GetType().IsSameOrInherits(context.Expectation.GetType())) { Type expectationType = context.Expectation.GetType(); object convertedSubject; if (TryChangeType(context.Subject, expectationType, out convertedSubject)) { context.TraceSingle(path => $"Converted subject {context.Subject} at {path} to {expectationType}"); var newContext = context.CreateWithDifferentSubject(convertedSubject, expectationType); structuralEqualityValidator.AssertEqualityUsing(newContext); return(true); } context.TraceSingle(path => $"Subject {context.Subject} at {path} could not be converted to {expectationType}"); } return(false); }
private static void AssertDictionaryEquivalence(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { Type expectationType = config.GetExpectationType(context); string methodName = ExpressionExtensions.GetMethodName( () => AssertDictionaryEquivalence <object, object, object, object>(null, null, null, null, null)); MethodCallExpression assertDictionaryEquivalence = Expression.Call( typeof(GenericDictionaryEquivalencyStep), methodName, GetDictionaryTypeArguments(context.Subject.GetType()) .Concat(GetDictionaryTypeArguments(expectationType)) .ToArray(), Expression.Constant(context), Expression.Constant(parent), Expression.Constant(config), Expression.Constant(context.Subject, GetIDictionaryInterface(context.Subject.GetType())), Expression.Constant(context.Expectation, GetIDictionaryInterface(expectationType))); Expression.Lambda(assertDictionaryEquivalence).Compile().DynamicInvoke(); }
public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { Array expectationAsArray = (Array)context.Expectation; if (AreComparable(context, expectationAsArray)) { Digit digit = BuildDigitsRepresentingAllIndices(expectationAsArray); do { var subject = ((Array)context.Subject).GetValue(digit.Indices); IEquivalencyValidationContext itemContext = context.CreateForCollectionItem( string.Join(",", digit.Indices), subject, expectationAsArray.GetValue(digit.Indices)); parent.AssertEqualityUsing(itemContext); }while (digit.Increment()); } return(true); }
/// <summary> /// Applies a step as part of the task to compare two objects for structural equality. /// </summary> /// <value> /// Should return <c>true</c> if the subject matches the expectation or if no additional assertions /// have to be executed. Should return <c>false</c> otherwise. /// </value> /// <remarks> /// May throw when preconditions are not met or if it detects mismatching data. /// </remarks> public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { switch (config.EnumEquivalencyHandling) { case EnumEquivalencyHandling.ByValue: decimal?subjectsUnderlyingValue = (context.Subject != null) ? Convert.ToDecimal(context.Subject) : (decimal?)null; decimal?expectationsUnderlyingValue = (context.Expectation != null) ? Convert.ToDecimal(context.Expectation) : (decimal?)null; subjectsUnderlyingValue.Should().Be(expectationsUnderlyingValue, context.Because, context.BecauseArgs); break; case EnumEquivalencyHandling.ByName: context.Subject.ToString().Should().Be(context.Expectation.ToString(), context.Because, context.BecauseArgs); break; default: throw new InvalidOperationException(string.Format("Don't know how to handle {0}", config.EnumEquivalencyHandling)); } return(true); }
/// <summary> /// Applies a step as part of the task to compare two objects for structural equality. /// </summary> /// <value> /// Should return <c>true</c> if the subject matches the expectation or if no additional assertions /// have to be executed. Should return <c>false</c> otherwise. /// </value> /// <remarks> /// May throw when preconditions are not met or if it detects mismatching data. /// </remarks> public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { Type expectedType = config.GetExpectationType(context); Type[] interfaceTypes = GetIEnumerableInterfaces(expectedType); AssertionScope.Current .ForCondition(interfaceTypes.Length == 1) .FailWith(() => new FailReason("{context:Expectation} implements {0}, so cannot determine which one " + "to use for asserting the equivalency of the collection. ", interfaceTypes.Select(type => "IEnumerable<" + type.GetGenericArguments().Single() + ">"))); if (AssertSubjectIsCollection(context.Subject)) { var validator = new EnumerableEquivalencyValidator(parent, context) { Recursive = context.IsRoot || config.IsRecursive, OrderingRules = config.OrderingRules }; Type typeOfEnumeration = GetTypeOfEnumeration(expectedType); var subjectAsArray = EnumerableEquivalencyStep.ToArray(context.Subject); try { HandleMethod.MakeGenericMethod(typeOfEnumeration).Invoke(null, new[] { validator, subjectAsArray, context.Expectation }); } catch (TargetInvocationException e) { throw e.Unwrap(); } } return(true); }
public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { bool success = false; using (var scope = new AssertionScope()) { // Try without conversion if (AppliesTo(context)) { success = ExecuteAssertion(context); } bool converted = false; if (!success && converter.CanHandle(context, config)) { // Convert into a child context context = context.Clone(); converter.Handle(context, parent, config); converted = true; } if (converted && AppliesTo(context)) { // Try again after conversion success = ExecuteAssertion(context); if (success) { // If the assertion succeeded after conversion, discard the failures from // the previous attempt. If it didn't, let the scope throw with those failures. scope.Discard(); } } } return(success); }
private static void AssertMemberEquality(Comparands comparands, IEquivalencyValidationContext context, IEquivalencyValidator parent, IMember selectedMember, IEquivalencyAssertionOptions options) { IMember matchingMember = FindMatchFor(selectedMember, context.CurrentNode, comparands.Subject, options); if (matchingMember is not null) { var nestedComparands = new Comparands { Subject = matchingMember.GetValue(comparands.Subject), Expectation = selectedMember.GetValue(comparands.Expectation), CompileTimeType = selectedMember.Type }; if (selectedMember.Name != matchingMember.Name) { // In case the matching process selected a different member on the subject, // adjust the current member so that assertion failures report the proper name. selectedMember.Name = matchingMember.Name; } parent.RecursivelyAssertEquality(nestedComparands, context.AsNestedMember(selectedMember)); } }
public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationContext context, IEquivalencyValidator nestedValidator) { if (!comparands.GetExpectedType(context.Options).IsEnum) { return(EquivalencyResult.ContinueWithNext); } bool succeeded = Execute.Assertion .ForCondition(comparands.Subject?.GetType().IsEnum == true) .FailWith(() => { decimal?expectationsUnderlyingValue = ExtractDecimal(comparands.Expectation); string expectationName = GetDisplayNameForEnumComparison(comparands.Expectation, expectationsUnderlyingValue); return(new FailReason($"Expected {{context:enum}} to be equivalent to {expectationName}{{reason}}, but found {{0}}.", comparands.Subject)); }); if (succeeded) { switch (context.Options.EnumEquivalencyHandling) { case EnumEquivalencyHandling.ByValue: HandleByValue(comparands); break; case EnumEquivalencyHandling.ByName: HandleByName(comparands); break; default: throw new InvalidOperationException($"Do not know how to handle {context.Options.EnumEquivalencyHandling}"); } } return(EquivalencyResult.AssertionCompleted); }
private static void CompareCollections(Comparands comparands, IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config, Dictionary <string, IMember> selectedMembers) { // Note: The collections here are listed in the XML documentation for the DataTable.BeEquivalentTo extension // method in DataTableAssertions.cs. If this ever needs to change, keep them in sync. var collectionNames = new[] { nameof(DataTable.ChildRelations), nameof(DataTable.Columns), nameof(DataTable.Constraints), nameof(DataTable.ExtendedProperties), nameof(DataTable.ParentRelations), nameof(DataTable.PrimaryKey), nameof(DataTable.Rows), }; foreach (var collectionName in collectionNames) { if (selectedMembers.TryGetValue(collectionName, out IMember expectationMember)) { IMember matchingMember = FindMatchFor(expectationMember, comparands.Subject, context.CurrentNode, config); if (matchingMember is not null) { IEquivalencyValidationContext nestedContext = context.AsNestedMember(expectationMember); var nestedComparands = new Comparands { Subject = matchingMember.GetValue(comparands.Subject), Expectation = expectationMember.GetValue(comparands.Expectation), CompileTimeType = expectationMember.Type }; parent.RecursivelyAssertEquality(nestedComparands, nestedContext); } } } }
public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { Array subjectAsArray = (Array) context.Subject; if (AreComparable(context, subjectAsArray)) { Digit digit = BuildDigitsRepresentingAllIndices(subjectAsArray); do { var expectation = ((Array) context.Expectation).GetValue(digit.Indices); IEquivalencyValidationContext itemContext = context.CreateForCollectionItem( string.Join(",", digit.Indices), subjectAsArray.GetValue(digit.Indices), expectation); parent.AssertEqualityUsing(itemContext); } while (digit.Increment()); } return true; }
/// <summary> /// Gets a value indicating whether this step can handle the verificationScope subject and/or expectation. /// </summary> public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config) { var subjectType = config.GetSubjectType(context); return (context.Subject != null) && IsGenericCollection(subjectType); }
/// <summary> /// Gets a value indicating whether this step can handle the current subject and/or expectation. /// </summary> public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config) { return !config.IsRecursive && !context.IsRoot; }
public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config) { return eqivalencyStep.CanHandle(CreateAdjustedCopy(context), config); }
public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config) { return (context.SelectedMemberInfo != null); }
private static bool PreconditionsAreMet(IEquivalencyValidationContext context, IDictionary expectation, IDictionary subject) { return (AssertIsDictionary(expectation) && AssertSameLength(expectation, subject)); }
public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator parent, IEquivalencyAssertionOptions config) { return assertionRule.AssertEquality(context); }
/// <summary> /// Gets a value indicating whether this step can handle the current subject and/or expectation. /// </summary> public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config) { Type subjectType = config.GetSubjectType(context); return typeof(IDictionary).IsAssignableFrom(subjectType); }
/// <summary> /// Applies a step as part of the task to compare two objects for structural equality. /// </summary> /// <value> /// Should return <c>true</c> if the subject matches the expectation or if no additional assertions /// have to be executed. Should return <c>false</c> otherwise. /// </value> /// <remarks> /// May throw when preconditions are not met or if it detects mismatching data. /// </remarks> public bool Handle(IEquivalencyValidationContext context, IEquivalencyValidator structuralEqualityValidator, IEquivalencyAssertionOptions config) { context.Subject.Should().Be(context.Expectation, context.Because, context.BecauseArgs); return true; }
public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config) { return(true); }
/// <summary> /// Gets a value indicating whether this step can handle the verificationScope subject and/or expectation. /// </summary> public bool CanHandle(IEquivalencyValidationContext context, IEquivalencyAssertionOptions config) { Type subjectType = config.GetSubjectType(context); return IsCollection(subjectType); }