1 // ***********************************************************************
2 // Copyright (c) 2009 Charlie Poole
3 //
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:
11 //
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 //
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 // ***********************************************************************
23 
24 using System;
25 using System.Collections;
26 
27 namespace NUnit.Framework.Constraints
28 {
29     /// <summary>
30     /// ConstraintExpression represents a compound constraint in the
31     /// process of being constructed from a series of syntactic elements.
32     ///
33     /// Individual elements are appended to the expression as they are
34     /// reognized. Once an actual Constraint is appended, the expression
35     /// returns a resolvable Constraint.
36     /// </summary>
37     public class ConstraintExpression : ConstraintExpressionBase
38     {
39         /// <summary>
40         /// Initializes a new instance of the <see cref="T:ConstraintExpression"/> class.
41         /// </summary>
ConstraintExpression()42         public ConstraintExpression() { }
43 
44         /// <summary>
45         /// Initializes a new instance of the <see cref="T:ConstraintExpression"/>
46         /// class passing in a ConstraintBuilder, which may be pre-populated.
47         /// </summary>
48         /// <param name="builder">The builder.</param>
ConstraintExpression(ConstraintBuilder builder)49         public ConstraintExpression(ConstraintBuilder builder)
50             : base( builder ) { }
51 
52         #region Not
53 
54         /// <summary>
55         /// Returns a ConstraintExpression that negates any
56         /// following constraint.
57         /// </summary>
58         public ConstraintExpression Not
59         {
60             get { return this.Append(new NotOperator()); }
61         }
62 
63         /// <summary>
64         /// Returns a ConstraintExpression that negates any
65         /// following constraint.
66         /// </summary>
67         public ConstraintExpression No
68         {
69             get { return this.Append(new NotOperator()); }
70         }
71 
72         #endregion
73 
74         #region All
75 
76         /// <summary>
77         /// Returns a ConstraintExpression, which will apply
78         /// the following constraint to all members of a collection,
79         /// succeeding if all of them succeed.
80         /// </summary>
81         public ConstraintExpression All
82         {
83             get { return this.Append(new AllOperator()); }
84         }
85 
86         #endregion
87 
88         #region Some
89 
90         /// <summary>
91         /// Returns a ConstraintExpression, which will apply
92         /// the following constraint to all members of a collection,
93         /// succeeding if at least one of them succeeds.
94         /// </summary>
95         public ConstraintExpression Some
96         {
97             get { return this.Append(new SomeOperator()); }
98         }
99 
100         #endregion
101 
102         #region None
103 
104         /// <summary>
105         /// Returns a ConstraintExpression, which will apply
106         /// the following constraint to all members of a collection,
107         /// succeeding if all of them fail.
108         /// </summary>
109         public ConstraintExpression None
110         {
111             get { return this.Append(new NoneOperator()); }
112         }
113 
114         #endregion
115 
116 		#region Exactly(n)
117 
118         /// <summary>
119         /// Returns a ConstraintExpression, which will apply
120         /// the following constraint to all members of a collection,
121         /// succeeding only if a specified number of them succeed.
122         /// </summary>
Exactly(int expectedCount)123         public ConstraintExpression Exactly(int expectedCount)
124         {
125             return this.Append(new ExactCountOperator(expectedCount));
126         }
127 
128 		#endregion
129 
130         #region Property
131 
132         /// <summary>
133         /// Returns a new PropertyConstraintExpression, which will either
134         /// test for the existence of the named property on the object
135         /// being tested or apply any following constraint to that property.
136         /// </summary>
Property(string name)137         public ResolvableConstraintExpression Property(string name)
138         {
139             return this.Append(new PropOperator(name));
140         }
141 
142         #endregion
143 
144         #region Length
145 
146         /// <summary>
147         /// Returns a new ConstraintExpression, which will apply the following
148         /// constraint to the Length property of the object being tested.
149         /// </summary>
150         public ResolvableConstraintExpression Length
151         {
152             get { return Property("Length"); }
153         }
154 
155         #endregion
156 
157         #region Count
158 
159         /// <summary>
160         /// Returns a new ConstraintExpression, which will apply the following
161         /// constraint to the Count property of the object being tested.
162         /// </summary>
163         public ResolvableConstraintExpression Count
164         {
165             get { return Property("Count"); }
166         }
167 
168         #endregion
169 
170         #region Message
171 
172         /// <summary>
173         /// Returns a new ConstraintExpression, which will apply the following
174         /// constraint to the Message property of the object being tested.
175         /// </summary>
176         public ResolvableConstraintExpression Message
177         {
178             get { return Property("Message"); }
179         }
180 
181         #endregion
182 
183         #region InnerException
184 
185         /// <summary>
186         /// Returns a new ConstraintExpression, which will apply the following
187         /// constraint to the InnerException property of the object being tested.
188         /// </summary>
189         public ResolvableConstraintExpression InnerException
190         {
191             get { return Property("InnerException"); }
192         }
193 
194         #endregion
195 
196         #region Attribute
197 
198         /// <summary>
199         /// Returns a new AttributeConstraint checking for the
200         /// presence of a particular attribute on an object.
201         /// </summary>
Attribute(Type expectedType)202         public ResolvableConstraintExpression Attribute(Type expectedType)
203         {
204             return this.Append(new AttributeOperator(expectedType));
205         }
206 
207 #if CLR_2_0 || CLR_4_0
208         /// <summary>
209         /// Returns a new AttributeConstraint checking for the
210         /// presence of a particular attribute on an object.
211         /// </summary>
Attribute()212         public ResolvableConstraintExpression Attribute<T>()
213         {
214             return Attribute(typeof(T));
215         }
216 #endif
217 
218         #endregion
219 
220         #region With
221 
222         /// <summary>
223         /// With is currently a NOP - reserved for future use.
224         /// </summary>
225         public ConstraintExpression With
226         {
227             get { return this.Append(new WithOperator()); }
228         }
229 
230         #endregion
231 
232         #region Matches
233 
234         /// <summary>
235         /// Returns the constraint provided as an argument - used to allow custom
236         /// custom constraints to easily participate in the syntax.
237         /// </summary>
Matches(Constraint constraint)238         public Constraint Matches(Constraint constraint)
239         {
240             return this.Append(constraint);
241         }
242 
243 #if CLR_2_0 || CLR_4_0
244         /// <summary>
245         /// Returns the constraint provided as an argument - used to allow custom
246         /// custom constraints to easily participate in the syntax.
247         /// </summary>
Matches(Predicate<T> predicate)248         public Constraint Matches<T>(Predicate<T> predicate)
249         {
250             return this.Append(new PredicateConstraint<T>(predicate));
251         }
252 #endif
253 
254         #endregion
255 
256         #region Null
257 
258         /// <summary>
259         /// Returns a constraint that tests for null
260         /// </summary>
261         public NullConstraint Null
262         {
263             get { return (NullConstraint)this.Append(new NullConstraint()); }
264         }
265 
266         #endregion
267 
268         #region True
269 
270         /// <summary>
271         /// Returns a constraint that tests for True
272         /// </summary>
273         public TrueConstraint True
274         {
275             get { return (TrueConstraint)this.Append(new TrueConstraint()); }
276         }
277 
278         #endregion
279 
280         #region False
281 
282         /// <summary>
283         /// Returns a constraint that tests for False
284         /// </summary>
285         public FalseConstraint False
286         {
287             get { return (FalseConstraint)this.Append(new FalseConstraint()); }
288         }
289 
290         #endregion
291 
292         #region Positive
293 
294         /// <summary>
295         /// Returns a constraint that tests for a positive value
296         /// </summary>
297         public GreaterThanConstraint Positive
298         {
299             get { return (GreaterThanConstraint)this.Append(new GreaterThanConstraint(0)); }
300         }
301 
302         #endregion
303 
304         #region Negative
305 
306         /// <summary>
307         /// Returns a constraint that tests for a negative value
308         /// </summary>
309         public LessThanConstraint Negative
310         {
311             get { return (LessThanConstraint)this.Append(new LessThanConstraint(0)); }
312         }
313 
314         #endregion
315 
316         #region NaN
317 
318         /// <summary>
319         /// Returns a constraint that tests for NaN
320         /// </summary>
321         public NaNConstraint NaN
322         {
323             get { return (NaNConstraint)this.Append(new NaNConstraint()); }
324         }
325 
326         #endregion
327 
328         #region Empty
329 
330         /// <summary>
331         /// Returns a constraint that tests for empty
332         /// </summary>
333         public EmptyConstraint Empty
334         {
335             get { return (EmptyConstraint)this.Append(new EmptyConstraint()); }
336         }
337 
338         #endregion
339 
340         #region Unique
341 
342         /// <summary>
343         /// Returns a constraint that tests whether a collection
344         /// contains all unique items.
345         /// </summary>
346         public UniqueItemsConstraint Unique
347         {
348             get { return (UniqueItemsConstraint)this.Append(new UniqueItemsConstraint()); }
349         }
350 
351         #endregion
352 
353         #region BinarySerializable
354 
355 #if !NETCF && !SILVERLIGHT
356         /// <summary>
357         /// Returns a constraint that tests whether an object graph is serializable in binary format.
358         /// </summary>
359         public BinarySerializableConstraint BinarySerializable
360         {
361             get { return (BinarySerializableConstraint)this.Append(new BinarySerializableConstraint()); }
362         }
363 #endif
364 
365         #endregion
366 
367         #region XmlSerializable
368 
369 #if !SILVERLIGHT
370         /// <summary>
371         /// Returns a constraint that tests whether an object graph is serializable in xml format.
372         /// </summary>
373         public XmlSerializableConstraint XmlSerializable
374         {
375             get { return (XmlSerializableConstraint)this.Append(new XmlSerializableConstraint()); }
376         }
377 #endif
378 
379         #endregion
380 
381         #region EqualTo
382 
383         /// <summary>
384         /// Returns a constraint that tests two items for equality
385         /// </summary>
EqualTo(object expected)386         public EqualConstraint EqualTo(object expected)
387         {
388             return (EqualConstraint)this.Append(new EqualConstraint(expected));
389         }
390 
391         #endregion
392 
393         #region SameAs
394 
395         /// <summary>
396         /// Returns a constraint that tests that two references are the same object
397         /// </summary>
SameAs(object expected)398         public SameAsConstraint SameAs(object expected)
399         {
400             return (SameAsConstraint)this.Append(new SameAsConstraint(expected));
401         }
402 
403         #endregion
404 
405         #region GreaterThan
406 
407         /// <summary>
408         /// Returns a constraint that tests whether the
409         /// actual value is greater than the suppled argument
410         /// </summary>
GreaterThan(object expected)411         public GreaterThanConstraint GreaterThan(object expected)
412         {
413             return (GreaterThanConstraint)this.Append(new GreaterThanConstraint(expected));
414         }
415 
416         #endregion
417 
418         #region GreaterThanOrEqualTo
419 
420         /// <summary>
421         /// Returns a constraint that tests whether the
422         /// actual value is greater than or equal to the suppled argument
423         /// </summary>
GreaterThanOrEqualTo(object expected)424         public GreaterThanOrEqualConstraint GreaterThanOrEqualTo(object expected)
425         {
426             return (GreaterThanOrEqualConstraint)this.Append(new GreaterThanOrEqualConstraint(expected));
427         }
428 
429         /// <summary>
430         /// Returns a constraint that tests whether the
431         /// actual value is greater than or equal to the suppled argument
432         /// </summary>
AtLeast(object expected)433         public GreaterThanOrEqualConstraint AtLeast(object expected)
434         {
435             return (GreaterThanOrEqualConstraint)this.Append(new GreaterThanOrEqualConstraint(expected));
436         }
437 
438         #endregion
439 
440         #region LessThan
441 
442         /// <summary>
443         /// Returns a constraint that tests whether the
444         /// actual value is less than the suppled argument
445         /// </summary>
LessThan(object expected)446         public LessThanConstraint LessThan(object expected)
447         {
448             return (LessThanConstraint)this.Append(new LessThanConstraint(expected));
449         }
450 
451         #endregion
452 
453         #region LessThanOrEqualTo
454 
455         /// <summary>
456         /// Returns a constraint that tests whether the
457         /// actual value is less than or equal to the suppled argument
458         /// </summary>
LessThanOrEqualTo(object expected)459         public LessThanOrEqualConstraint LessThanOrEqualTo(object expected)
460         {
461             return (LessThanOrEqualConstraint)this.Append(new LessThanOrEqualConstraint(expected));
462         }
463 
464         /// <summary>
465         /// Returns a constraint that tests whether the
466         /// actual value is less than or equal to the suppled argument
467         /// </summary>
AtMost(object expected)468         public LessThanOrEqualConstraint AtMost(object expected)
469         {
470             return (LessThanOrEqualConstraint)this.Append(new LessThanOrEqualConstraint(expected));
471         }
472 
473         #endregion
474 
475         #region TypeOf
476 
477         /// <summary>
478         /// Returns a constraint that tests whether the actual
479         /// value is of the exact type supplied as an argument.
480         /// </summary>
TypeOf(Type expectedType)481         public ExactTypeConstraint TypeOf(Type expectedType)
482         {
483             return (ExactTypeConstraint)this.Append(new ExactTypeConstraint(expectedType));
484         }
485 
486 #if CLR_2_0 || CLR_4_0
487         /// <summary>
488         /// Returns a constraint that tests whether the actual
489         /// value is of the exact type supplied as an argument.
490         /// </summary>
TypeOf()491         public ExactTypeConstraint TypeOf<T>()
492         {
493             return (ExactTypeConstraint)this.Append(new ExactTypeConstraint(typeof(T)));
494         }
495 #endif
496 
497         #endregion
498 
499         #region InstanceOf
500 
501         /// <summary>
502         /// Returns a constraint that tests whether the actual value
503         /// is of the type supplied as an argument or a derived type.
504         /// </summary>
InstanceOf(Type expectedType)505         public InstanceOfTypeConstraint InstanceOf(Type expectedType)
506         {
507             return (InstanceOfTypeConstraint)this.Append(new InstanceOfTypeConstraint(expectedType));
508         }
509 
510 #if CLR_2_0 || CLR_4_0
511         /// <summary>
512         /// Returns a constraint that tests whether the actual value
513         /// is of the type supplied as an argument or a derived type.
514         /// </summary>
InstanceOf()515         public InstanceOfTypeConstraint InstanceOf<T>()
516         {
517             return (InstanceOfTypeConstraint)this.Append(new InstanceOfTypeConstraint(typeof(T)));
518         }
519 #endif
520 
521         #endregion
522 
523         #region AssignableFrom
524 
525         /// <summary>
526         /// Returns a constraint that tests whether the actual value
527         /// is assignable from the type supplied as an argument.
528         /// </summary>
AssignableFrom(Type expectedType)529         public AssignableFromConstraint AssignableFrom(Type expectedType)
530         {
531             return (AssignableFromConstraint)this.Append(new AssignableFromConstraint(expectedType));
532         }
533 
534 #if CLR_2_0 || CLR_4_0
535         /// <summary>
536         /// Returns a constraint that tests whether the actual value
537         /// is assignable from the type supplied as an argument.
538         /// </summary>
AssignableFrom()539         public AssignableFromConstraint AssignableFrom<T>()
540         {
541             return (AssignableFromConstraint)this.Append(new AssignableFromConstraint(typeof(T)));
542         }
543 #endif
544 
545         #endregion
546 
547         #region AssignableTo
548 
549         /// <summary>
550         /// Returns a constraint that tests whether the actual value
551         /// is assignable from the type supplied as an argument.
552         /// </summary>
AssignableTo(Type expectedType)553         public AssignableToConstraint AssignableTo(Type expectedType)
554         {
555             return (AssignableToConstraint)this.Append(new AssignableToConstraint(expectedType));
556         }
557 
558 #if CLR_2_0 || CLR_4_0
559         /// <summary>
560         /// Returns a constraint that tests whether the actual value
561         /// is assignable from the type supplied as an argument.
562         /// </summary>
AssignableTo()563         public AssignableToConstraint AssignableTo<T>()
564         {
565             return (AssignableToConstraint)this.Append(new AssignableToConstraint(typeof(T)));
566         }
567 #endif
568 
569         #endregion
570 
571         #region EquivalentTo
572 
573         /// <summary>
574         /// Returns a constraint that tests whether the actual value
575         /// is a collection containing the same elements as the
576         /// collection supplied as an argument.
577         /// </summary>
EquivalentTo(IEnumerable expected)578         public CollectionEquivalentConstraint EquivalentTo(IEnumerable expected)
579         {
580             return (CollectionEquivalentConstraint)this.Append(new CollectionEquivalentConstraint(expected));
581         }
582 
583         #endregion
584 
585         #region SubsetOf
586 
587         /// <summary>
588         /// Returns a constraint that tests whether the actual value
589         /// is a subset of the collection supplied as an argument.
590         /// </summary>
SubsetOf(IEnumerable expected)591         public CollectionSubsetConstraint SubsetOf(IEnumerable expected)
592         {
593             return (CollectionSubsetConstraint)this.Append(new CollectionSubsetConstraint(expected));
594         }
595 
596         #endregion
597 
598         #region Ordered
599 
600         /// <summary>
601         /// Returns a constraint that tests whether a collection is ordered
602         /// </summary>
603         public CollectionOrderedConstraint Ordered
604         {
605             get { return (CollectionOrderedConstraint)this.Append(new CollectionOrderedConstraint()); }
606         }
607 
608         #endregion
609 
610         #region Member
611 
612         /// <summary>
613         /// Returns a new CollectionContainsConstraint checking for the
614         /// presence of a particular object in the collection.
615         /// </summary>
Member(object expected)616         public CollectionContainsConstraint Member(object expected)
617         {
618             return (CollectionContainsConstraint)this.Append(new CollectionContainsConstraint(expected));
619         }
620 
621         /// <summary>
622         /// Returns a new CollectionContainsConstraint checking for the
623         /// presence of a particular object in the collection.
624         /// </summary>
Contains(object expected)625         public CollectionContainsConstraint Contains(object expected)
626         {
627             return (CollectionContainsConstraint)this.Append(new CollectionContainsConstraint(expected));
628         }
629 
630         #endregion
631 
632         #region Contains
633 
634         /// <summary>
635         /// Returns a new ContainsConstraint. This constraint
636         /// will, in turn, make use of the appropriate second-level
637         /// constraint, depending on the type of the actual argument.
638         /// This overload is only used if the item sought is a string,
639         /// since any other type implies that we are looking for a
640         /// collection member.
641         /// </summary>
Contains(string expected)642         public ContainsConstraint Contains(string expected)
643         {
644             return (ContainsConstraint)this.Append(new ContainsConstraint(expected));
645         }
646 
647         #endregion
648 
649         #region StringContaining
650 
651         /// <summary>
652         /// Returns a constraint that succeeds if the actual
653         /// value contains the substring supplied as an argument.
654         /// </summary>
StringContaining(string expected)655         public SubstringConstraint StringContaining(string expected)
656         {
657             return (SubstringConstraint)this.Append(new SubstringConstraint(expected));
658         }
659 
660         /// <summary>
661         /// Returns a constraint that succeeds if the actual
662         /// value contains the substring supplied as an argument.
663         /// </summary>
ContainsSubstring(string expected)664         public SubstringConstraint ContainsSubstring(string expected)
665         {
666             return (SubstringConstraint)this.Append(new SubstringConstraint(expected));
667         }
668 
669         #endregion
670 
671         #region StartsWith
672 
673         /// <summary>
674         /// Returns a constraint that succeeds if the actual
675         /// value starts with the substring supplied as an argument.
676         /// </summary>
StartsWith(string expected)677         public StartsWithConstraint StartsWith(string expected)
678         {
679             return (StartsWithConstraint)this.Append(new StartsWithConstraint(expected));
680         }
681 
682         /// <summary>
683         /// Returns a constraint that succeeds if the actual
684         /// value starts with the substring supplied as an argument.
685         /// </summary>
StringStarting(string expected)686         public StartsWithConstraint StringStarting(string expected)
687         {
688             return (StartsWithConstraint)this.Append(new StartsWithConstraint(expected));
689         }
690 
691         #endregion
692 
693         #region EndsWith
694 
695         /// <summary>
696         /// Returns a constraint that succeeds if the actual
697         /// value ends with the substring supplied as an argument.
698         /// </summary>
EndsWith(string expected)699         public EndsWithConstraint EndsWith(string expected)
700         {
701             return (EndsWithConstraint)this.Append(new EndsWithConstraint(expected));
702         }
703 
704         /// <summary>
705         /// Returns a constraint that succeeds if the actual
706         /// value ends with the substring supplied as an argument.
707         /// </summary>
StringEnding(string expected)708         public EndsWithConstraint StringEnding(string expected)
709         {
710             return (EndsWithConstraint)this.Append(new EndsWithConstraint(expected));
711         }
712 
713         #endregion
714 
715         #region Matches
716 
717 #if !NETCF
718         /// <summary>
719         /// Returns a constraint that succeeds if the actual
720         /// value matches the regular expression supplied as an argument.
721         /// </summary>
Matches(string pattern)722         public RegexConstraint Matches(string pattern)
723         {
724             return (RegexConstraint)this.Append(new RegexConstraint(pattern));
725         }
726 
727         /// <summary>
728         /// Returns a constraint that succeeds if the actual
729         /// value matches the regular expression supplied as an argument.
730         /// </summary>
StringMatching(string pattern)731         public RegexConstraint StringMatching(string pattern)
732         {
733             return (RegexConstraint)this.Append(new RegexConstraint(pattern));
734         }
735 #endif
736 
737         #endregion
738 
739         #region SamePath
740 
741         /// <summary>
742         /// Returns a constraint that tests whether the path provided
743         /// is the same as an expected path after canonicalization.
744         /// </summary>
SamePath(string expected)745         public SamePathConstraint SamePath(string expected)
746         {
747             return (SamePathConstraint)this.Append(new SamePathConstraint(expected));
748         }
749 
750         #endregion
751 
752         #region SubPath
753 
754         /// <summary>
755         /// Returns a constraint that tests whether the path provided
756         /// is the same path or under an expected path after canonicalization.
757         /// </summary>
SubPath(string expected)758         public SubPathConstraint SubPath(string expected)
759         {
760             return (SubPathConstraint)this.Append(new SubPathConstraint(expected));
761         }
762 
763         #endregion
764 
765         #region SamePathOrUnder
766 
767         /// <summary>
768         /// Returns a constraint that tests whether the path provided
769         /// is the same path or under an expected path after canonicalization.
770         /// </summary>
SamePathOrUnder(string expected)771         public SamePathOrUnderConstraint SamePathOrUnder(string expected)
772         {
773             return (SamePathOrUnderConstraint)this.Append(new SamePathOrUnderConstraint(expected));
774         }
775 
776         #endregion
777 
778         #region InRange
779 
780 #if CLR_2_0 || CLR_4_0
781         /// <summary>
782         /// Returns a constraint that tests whether the actual value falls
783         /// within a specified range.
784         /// </summary>
785         public RangeConstraint<T> InRange<T>(T from, T to) where T : IComparable<T>
786         {
787             return (RangeConstraint<T>)this.Append(new RangeConstraint<T>(from, to));
788         }
789 #else
790         /// <summary>
791         /// Returns a constraint that tests whether the actual value falls
792         /// within a specified range.
793         /// </summary>
InRange(IComparable from, IComparable to)794         public RangeConstraint InRange(IComparable from, IComparable to)
795         {
796             return (RangeConstraint)this.Append(new RangeConstraint(from, to));
797         }
798 #endif
799 
800         #endregion
801 
802     }
803 }
804