1 // ***********************************************************************
2 // Copyright (c) 2009 Charlie Poole
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 // ***********************************************************************
25 #define NUNIT_FRAMEWORK
30 using System.Collections;
31 using System.Runtime.CompilerServices;
33 namespace NUnit.Framework.Constraints
36 /// ConstraintExpression represents a compound constraint in the
37 /// process of being constructed from a series of syntactic elements.
39 /// Individual elements are appended to the expression as they are
40 /// reorganized. When a constraint is appended, it is returned as the
41 /// value of the operation so that modifiers may be applied. However,
42 /// any partially built expression is attached to the constraint for
43 /// later resolution. When an operator is appended, the partial
44 /// expression is returned. If it's a self-resolving operator, then
45 /// a ResolvableConstraintExpression is returned.
47 public class ConstraintExpression
49 #region Instance Fields
52 /// The ConstraintBuilder holding the elements recognized so far
54 protected ConstraintBuilder builder;
61 /// Initializes a new instance of the <see cref="ConstraintExpression"/> class.
63 public ConstraintExpression()
65 this.builder = new ConstraintBuilder();
69 /// Initializes a new instance of the <see cref="ConstraintExpression"/>
70 /// class passing in a ConstraintBuilder, which may be pre-populated.
72 /// <param name="builder">The builder.</param>
73 public ConstraintExpression(ConstraintBuilder builder)
75 this.builder = builder;
83 /// Returns a string representation of the expression as it
84 /// currently stands. This should only be used for testing,
85 /// since it has the side-effect of resolving the expression.
87 /// <returns></returns>
88 public override string ToString()
90 return builder.Resolve().ToString();
95 #region Append Methods
98 /// Appends an operator to the expression and returns the
99 /// resulting expression itself.
101 public ConstraintExpression Append(ConstraintOperator op)
108 /// Appends a self-resolving operator to the expression and
109 /// returns a new ResolvableConstraintExpression.
111 public ResolvableConstraintExpression Append(SelfResolvingOperator op)
114 return new ResolvableConstraintExpression(builder);
118 /// Appends a constraint to the expression and returns that
119 /// constraint, which is associated with the current state
120 /// of the expression being built. Note that the constraint
121 /// is not reduced at this time. For example, if there
122 /// is a NotOperator on the stack we don't reduce and
123 /// return a NotConstraint. The original constraint must
124 /// be returned because it may support modifiers that
125 /// are yet to be applied.
127 public Constraint Append(Constraint constraint)
129 builder.Append(constraint);
138 /// Returns a ConstraintExpression that negates any
139 /// following constraint.
141 public ConstraintExpression Not
143 get { return this.Append(new NotOperator()); }
147 /// Returns a ConstraintExpression that negates any
148 /// following constraint.
150 public ConstraintExpression No
152 get { return this.Append(new NotOperator()); }
160 /// Returns a ConstraintExpression, which will apply
161 /// the following constraint to all members of a collection,
162 /// succeeding if all of them succeed.
164 public ConstraintExpression All
166 get { return this.Append(new AllOperator()); }
174 /// Returns a ConstraintExpression, which will apply
175 /// the following constraint to all members of a collection,
176 /// succeeding if at least one of them succeeds.
178 public ConstraintExpression Some
180 get { return this.Append(new SomeOperator()); }
188 /// Returns a ConstraintExpression, which will apply
189 /// the following constraint to all members of a collection,
190 /// succeeding if all of them fail.
192 public ConstraintExpression None
194 get { return this.Append(new NoneOperator()); }
202 /// Returns a ConstraintExpression, which will apply
203 /// the following constraint to all members of a collection,
204 /// succeeding only if a specified number of them succeed.
206 public ConstraintExpression Exactly(int expectedCount)
208 return this.Append(new ExactCountOperator(expectedCount));
216 /// Returns a new PropertyConstraintExpression, which will either
217 /// test for the existence of the named property on the object
218 /// being tested or apply any following constraint to that property.
220 public ResolvableConstraintExpression Property(string name)
222 return this.Append(new PropOperator(name));
230 /// Returns a new ConstraintExpression, which will apply the following
231 /// constraint to the Length property of the object being tested.
233 public ResolvableConstraintExpression Length
235 get { return Property("Length"); }
243 /// Returns a new ConstraintExpression, which will apply the following
244 /// constraint to the Count property of the object being tested.
246 public ResolvableConstraintExpression Count
248 get { return Property("Count"); }
256 /// Returns a new ConstraintExpression, which will apply the following
257 /// constraint to the Message property of the object being tested.
259 public ResolvableConstraintExpression Message
261 get { return Property("Message"); }
266 #region InnerException
269 /// Returns a new ConstraintExpression, which will apply the following
270 /// constraint to the InnerException property of the object being tested.
272 public ResolvableConstraintExpression InnerException
274 get { return Property("InnerException"); }
282 /// Returns a new AttributeConstraint checking for the
283 /// presence of a particular attribute on an object.
285 public ResolvableConstraintExpression Attribute(Type expectedType)
287 return this.Append(new AttributeOperator(expectedType));
291 /// Returns a new AttributeConstraint checking for the
292 /// presence of a particular attribute on an object.
294 public ResolvableConstraintExpression Attribute<TExpected>()
296 return Attribute(typeof(TExpected));
304 /// With is currently a NOP - reserved for future use.
306 public ConstraintExpression With
308 get { return this.Append(new WithOperator()); }
316 /// Returns the constraint provided as an argument - used to allow custom
317 /// custom constraints to easily participate in the syntax.
319 public Constraint Matches(IResolveConstraint constraint)
321 return this.Append((Constraint)constraint.Resolve());
325 /// Returns the constraint provided as an argument - used to allow custom
326 /// custom constraints to easily participate in the syntax.
328 public Constraint Matches<TActual>(Predicate<TActual> predicate)
330 return this.Append(new PredicateConstraint<TActual>(predicate));
338 /// Returns a constraint that tests for null
340 public NullConstraint Null
342 get { return (NullConstraint)this.Append(new NullConstraint()); }
350 /// Returns a constraint that tests for True
352 public TrueConstraint True
354 get { return (TrueConstraint)this.Append(new TrueConstraint()); }
362 /// Returns a constraint that tests for False
364 public FalseConstraint False
366 get { return (FalseConstraint)this.Append(new FalseConstraint()); }
374 /// Returns a constraint that tests for a positive value
376 public GreaterThanConstraint Positive
378 get { return (GreaterThanConstraint)this.Append(new GreaterThanConstraint(0)); }
386 /// Returns a constraint that tests for a negative value
388 public LessThanConstraint Negative
390 get { return (LessThanConstraint)this.Append(new LessThanConstraint(0)); }
398 /// Returns a constraint that tests if item is equal to zero
400 public EqualConstraint Zero
402 get { return (EqualConstraint)this.Append(new EqualConstraint(0)); }
410 /// Returns a constraint that tests for NaN
412 public NaNConstraint NaN
414 get { return (NaNConstraint)this.Append(new NaNConstraint()); }
422 /// Returns a constraint that tests for empty
424 public EmptyConstraint Empty
426 get { return (EmptyConstraint)this.Append(new EmptyConstraint()); }
434 /// Returns a constraint that tests whether a collection
435 /// contains all unique items.
437 public UniqueItemsConstraint Unique
439 get { return (UniqueItemsConstraint)this.Append(new UniqueItemsConstraint()); }
444 #region BinarySerializable
446 #if !NETCF && !SILVERLIGHT && !PORTABLE
448 /// Returns a constraint that tests whether an object graph is serializable in binary format.
450 public BinarySerializableConstraint BinarySerializable
452 get { return (BinarySerializableConstraint)this.Append(new BinarySerializableConstraint()); }
458 #region XmlSerializable
460 #if !SILVERLIGHT && !PORTABLE
462 /// Returns a constraint that tests whether an object graph is serializable in xml format.
464 public XmlSerializableConstraint XmlSerializable
466 get { return (XmlSerializableConstraint)this.Append(new XmlSerializableConstraint()); }
475 /// Returns a constraint that tests two items for equality
477 public EqualConstraint EqualTo(object expected)
479 return (EqualConstraint)this.Append(new EqualConstraint(expected));
487 /// Returns a constraint that tests that two references are the same object
489 public SameAsConstraint SameAs(object expected)
491 return (SameAsConstraint)this.Append(new SameAsConstraint(expected));
499 /// Returns a constraint that tests whether the
500 /// actual value is greater than the supplied argument
502 public GreaterThanConstraint GreaterThan(object expected)
504 return (GreaterThanConstraint)this.Append(new GreaterThanConstraint(expected));
509 #region GreaterThanOrEqualTo
512 /// Returns a constraint that tests whether the
513 /// actual value is greater than or equal to the supplied argument
515 public GreaterThanOrEqualConstraint GreaterThanOrEqualTo(object expected)
517 return (GreaterThanOrEqualConstraint)this.Append(new GreaterThanOrEqualConstraint(expected));
521 /// Returns a constraint that tests whether the
522 /// actual value is greater than or equal to the supplied argument
524 public GreaterThanOrEqualConstraint AtLeast(object expected)
526 return (GreaterThanOrEqualConstraint)this.Append(new GreaterThanOrEqualConstraint(expected));
534 /// Returns a constraint that tests whether the
535 /// actual value is less than the supplied argument
537 public LessThanConstraint LessThan(object expected)
539 return (LessThanConstraint)this.Append(new LessThanConstraint(expected));
544 #region LessThanOrEqualTo
547 /// Returns a constraint that tests whether the
548 /// actual value is less than or equal to the supplied argument
550 public LessThanOrEqualConstraint LessThanOrEqualTo(object expected)
552 return (LessThanOrEqualConstraint)this.Append(new LessThanOrEqualConstraint(expected));
556 /// Returns a constraint that tests whether the
557 /// actual value is less than or equal to the supplied argument
559 public LessThanOrEqualConstraint AtMost(object expected)
561 return (LessThanOrEqualConstraint)this.Append(new LessThanOrEqualConstraint(expected));
569 /// Returns a constraint that tests whether the actual
570 /// value is of the exact type supplied as an argument.
572 public ExactTypeConstraint TypeOf(Type expectedType)
574 return (ExactTypeConstraint)this.Append(new ExactTypeConstraint(expectedType));
578 /// Returns a constraint that tests whether the actual
579 /// value is of the exact type supplied as an argument.
581 public ExactTypeConstraint TypeOf<TExpected>()
583 return (ExactTypeConstraint)this.Append(new ExactTypeConstraint(typeof(TExpected)));
591 /// Returns a constraint that tests whether the actual value
592 /// is of the type supplied as an argument or a derived type.
594 public InstanceOfTypeConstraint InstanceOf(Type expectedType)
596 return (InstanceOfTypeConstraint)this.Append(new InstanceOfTypeConstraint(expectedType));
600 /// Returns a constraint that tests whether the actual value
601 /// is of the type supplied as an argument or a derived type.
603 public InstanceOfTypeConstraint InstanceOf<TExpected>()
605 return (InstanceOfTypeConstraint)this.Append(new InstanceOfTypeConstraint(typeof(TExpected)));
610 #region AssignableFrom
613 /// Returns a constraint that tests whether the actual value
614 /// is assignable from the type supplied as an argument.
616 public AssignableFromConstraint AssignableFrom(Type expectedType)
618 return (AssignableFromConstraint)this.Append(new AssignableFromConstraint(expectedType));
622 /// Returns a constraint that tests whether the actual value
623 /// is assignable from the type supplied as an argument.
625 public AssignableFromConstraint AssignableFrom<TExpected>()
627 return (AssignableFromConstraint)this.Append(new AssignableFromConstraint(typeof(TExpected)));
635 /// Returns a constraint that tests whether the actual value
636 /// is assignable from the type supplied as an argument.
638 public AssignableToConstraint AssignableTo(Type expectedType)
640 return (AssignableToConstraint)this.Append(new AssignableToConstraint(expectedType));
644 /// Returns a constraint that tests whether the actual value
645 /// is assignable from the type supplied as an argument.
647 public AssignableToConstraint AssignableTo<TExpected>()
649 return (AssignableToConstraint)this.Append(new AssignableToConstraint(typeof(TExpected)));
657 /// Returns a constraint that tests whether the actual value
658 /// is a collection containing the same elements as the
659 /// collection supplied as an argument.
661 public CollectionEquivalentConstraint EquivalentTo(IEnumerable expected)
663 return (CollectionEquivalentConstraint)this.Append(new CollectionEquivalentConstraint(expected));
671 /// Returns a constraint that tests whether the actual value
672 /// is a subset of the collection supplied as an argument.
674 public CollectionSubsetConstraint SubsetOf(IEnumerable expected)
676 return (CollectionSubsetConstraint)this.Append(new CollectionSubsetConstraint(expected));
684 /// Returns a constraint that tests whether the actual value
685 /// is a superset of the collection supplied as an argument.
687 public CollectionSupersetConstraint SupersetOf(IEnumerable expected)
689 return (CollectionSupersetConstraint)this.Append(new CollectionSupersetConstraint(expected));
697 /// Returns a constraint that tests whether a collection is ordered
699 public CollectionOrderedConstraint Ordered
701 get { return (CollectionOrderedConstraint)this.Append(new CollectionOrderedConstraint()); }
709 /// Returns a new CollectionContainsConstraint checking for the
710 /// presence of a particular object in the collection.
712 public CollectionContainsConstraint Member(object expected)
714 return (CollectionContainsConstraint)this.Append(new CollectionContainsConstraint(expected));
722 /// Returns a new CollectionContainsConstraint checking for the
723 /// presence of a particular object in the collection.
725 public CollectionContainsConstraint Contains(object expected)
727 return (CollectionContainsConstraint)this.Append(new CollectionContainsConstraint(expected));
731 /// Returns a new ContainsConstraint. This constraint
732 /// will, in turn, make use of the appropriate second-level
733 /// constraint, depending on the type of the actual argument.
734 /// This overload is only used if the item sought is a string,
735 /// since any other type implies that we are looking for a
736 /// collection member.
738 public ContainsConstraint Contains(string expected)
740 return (ContainsConstraint)this.Append(new ContainsConstraint(expected));
744 /// Returns a new ContainsConstraint. This constraint
745 /// will, in turn, make use of the appropriate second-level
746 /// constraint, depending on the type of the actual argument.
747 /// This overload is only used if the item sought is a string,
748 /// since any other type implies that we are looking for a
749 /// collection member.
751 public ContainsConstraint Contain(string expected)
753 return (ContainsConstraint)this.Append(new ContainsConstraint(expected));
758 #region StringContaining
761 /// Returns a constraint that succeeds if the actual
762 /// value contains the substring supplied as an argument.
764 [Obsolete("Deprecated, use Contains")]
765 public SubstringConstraint StringContaining(string expected)
767 return (SubstringConstraint)this.Append(new SubstringConstraint(expected));
771 /// Returns a constraint that succeeds if the actual
772 /// value contains the substring supplied as an argument.
774 [Obsolete("Deprecated, use Contains")]
775 public SubstringConstraint ContainsSubstring(string expected)
777 return (SubstringConstraint)this.Append(new SubstringConstraint(expected));
785 /// Returns a constraint that succeeds if the actual
786 /// value starts with the substring supplied as an argument.
788 public StartsWithConstraint StartWith(string expected)
790 return (StartsWithConstraint)this.Append(new StartsWithConstraint(expected));
794 /// Returns a constraint that succeeds if the actual
795 /// value starts with the substring supplied as an argument.
797 public StartsWithConstraint StartsWith(string expected)
799 return (StartsWithConstraint)this.Append(new StartsWithConstraint(expected));
803 /// Returns a constraint that succeeds if the actual
804 /// value starts with the substring supplied as an argument.
806 [Obsolete("Deprecated, use Does.StartWith or StartsWith")]
807 public StartsWithConstraint StringStarting(string expected)
809 return (StartsWithConstraint)this.Append(new StartsWithConstraint(expected));
817 /// Returns a constraint that succeeds if the actual
818 /// value ends with the substring supplied as an argument.
820 public EndsWithConstraint EndWith(string expected)
822 return (EndsWithConstraint)this.Append(new EndsWithConstraint(expected));
826 /// Returns a constraint that succeeds if the actual
827 /// value ends with the substring supplied as an argument.
829 public EndsWithConstraint EndsWith(string expected)
831 return (EndsWithConstraint)this.Append(new EndsWithConstraint(expected));
835 /// Returns a constraint that succeeds if the actual
836 /// value ends with the substring supplied as an argument.
838 [Obsolete("Deprecated, use Does.EndWith or EndsWith")]
839 public EndsWithConstraint StringEnding(string expected)
841 return (EndsWithConstraint)this.Append(new EndsWithConstraint(expected));
849 /// Returns a constraint that succeeds if the actual
850 /// value matches the regular expression supplied as an argument.
852 public RegexConstraint Match(string pattern)
854 return (RegexConstraint)this.Append(new RegexConstraint(pattern));
858 /// Returns a constraint that succeeds if the actual
859 /// value matches the regular expression supplied as an argument.
861 public RegexConstraint Matches(string pattern)
863 return (RegexConstraint)this.Append(new RegexConstraint(pattern));
867 /// Returns a constraint that succeeds if the actual
868 /// value matches the regular expression supplied as an argument.
870 [Obsolete("Deprecated, use Does.Match or Matches")]
871 public RegexConstraint StringMatching(string pattern)
873 return (RegexConstraint)this.Append(new RegexConstraint(pattern));
882 /// Returns a constraint that tests whether the path provided
883 /// is the same as an expected path after canonicalization.
885 public SamePathConstraint SamePath(string expected)
887 return (SamePathConstraint)this.Append(new SamePathConstraint(expected));
895 /// Returns a constraint that tests whether the path provided
896 /// is the a subpath of the expected path after canonicalization.
898 public SubPathConstraint SubPathOf(string expected)
900 return (SubPathConstraint)this.Append(new SubPathConstraint(expected));
905 #region SamePathOrUnder
908 /// Returns a constraint that tests whether the path provided
909 /// is the same path or under an expected path after canonicalization.
911 public SamePathOrUnderConstraint SamePathOrUnder(string expected)
913 return (SamePathOrUnderConstraint)this.Append(new SamePathOrUnderConstraint(expected));
922 /// Returns a constraint that tests whether the actual value falls
923 /// within a specified range.
925 public RangeConstraint InRange(IComparable from, IComparable to)
927 return (RangeConstraint)this.Append(new RangeConstraint(from, to));
934 #if !SILVERLIGHT && !PORTABLE
936 /// Returns a constraint that succeeds if the value
937 /// is a file or directory and it exists.
939 public Constraint Exist
941 get { return Append(new FileOrDirectoryExistsConstraint()); }