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