1 // ***********************************************************************
2 // Copyright (c) 2007 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
29 using NUnit.Framework.Internal;
30 using NUnit.Compatibility;
31 using System.Collections;
33 using System.Reflection;
35 namespace NUnit.Framework.Constraints
38 /// Delegate used to delay evaluation of the actual value
39 /// to be used in evaluating a constraint
41 public delegate TActual ActualValueDelegate<TActual>();
44 /// The Constraint class is the base of all built-in constraints
45 /// within NUnit. It provides the operator overloads used to combine
48 public abstract class Constraint : IConstraint
50 Lazy<string> _displayName;
55 /// Construct a constraint with optional arguments
57 /// <param name="args">Arguments to be saved</param>
58 protected Constraint(params object[] args)
62 _displayName = new Lazy<string>(() =>
64 var type = this.GetType();
65 var displayName = type.Name;
66 if (type.GetTypeInfo().IsGenericType)
67 displayName = displayName.Substring(0, displayName.Length - 2);
68 if (displayName.EndsWith("Constraint", StringComparison.Ordinal))
69 displayName = displayName.Substring(0, displayName.Length - 10);
79 /// The display name of this Constraint for use by ToString().
80 /// The default value is the name of the constraint with
81 /// trailing "Constraint" removed. Derived classes may set
82 /// this to another name in their constructors.
84 public virtual string DisplayName { get { return _displayName.Value; } }
87 /// The Description of what this constraint tests, for
88 /// use in messages and in the ConstraintResult.
90 public virtual string Description { get; protected set; }
93 /// Arguments provided to this Constraint, for use in
94 /// formatting the description.
96 public object[] Arguments { get; private set; }
99 /// The ConstraintBuilder holding this constraint
101 public ConstraintBuilder Builder { get; set; }
105 #region Abstract and Virtual Methods
108 /// Applies the constraint to an actual value, returning a ConstraintResult.
110 /// <param name="actual">The value to be tested</param>
111 /// <returns>A ConstraintResult</returns>
112 public abstract ConstraintResult ApplyTo<TActual>(TActual actual);
115 /// Applies the constraint to an ActualValueDelegate that returns
116 /// the value to be tested. The default implementation simply evaluates
117 /// the delegate but derived classes may override it to provide for
118 /// delayed processing.
120 /// <param name="del">An ActualValueDelegate</param>
121 /// <returns>A ConstraintResult</returns>
122 public virtual ConstraintResult ApplyTo<TActual>(ActualValueDelegate<TActual> del)
124 #if NET_4_0 || NET_4_5 || PORTABLE
125 if (AsyncInvocationRegion.IsAsyncOperation(del))
126 using (var region = AsyncInvocationRegion.Create(del))
127 return ApplyTo(region.WaitForPendingOperationsToComplete(del()));
129 return ApplyTo(GetTestObject(del));
132 #pragma warning disable 3006
134 /// Test whether the constraint is satisfied by a given reference.
135 /// The default implementation simply dereferences the value but
136 /// derived classes may override it to provide for delayed processing.
138 /// <param name="actual">A reference to the value to be tested</param>
139 /// <returns>A ConstraintResult</returns>
140 public virtual ConstraintResult ApplyTo<TActual>(ref TActual actual)
142 return ApplyTo(actual);
144 #pragma warning restore 3006
147 /// Retrieves the value to be tested from an ActualValueDelegate.
148 /// The default implementation simply evaluates the delegate but derived
149 /// classes may override it to provide for delayed processing.
151 /// <param name="del">An ActualValueDelegate</param>
152 /// <returns>Delegate evaluation result</returns>
153 protected virtual object GetTestObject<TActual>(ActualValueDelegate<TActual> del)
160 #region ToString Override
163 /// Default override of ToString returns the constraint DisplayName
164 /// followed by any arguments within angle brackets.
166 /// <returns></returns>
167 public override string ToString()
169 string rep = GetStringRepresentation();
171 return this.Builder == null ? rep : string.Format("<unresolved {0}>", rep);
175 /// Returns the string representation of this constraint
177 protected virtual string GetStringRepresentation()
179 System.Text.StringBuilder sb = new System.Text.StringBuilder();
182 sb.Append(DisplayName.ToLower());
184 foreach (object arg in Arguments)
187 sb.Append(_displayable(arg));
192 return sb.ToString();
195 private static string _displayable(object o)
197 if (o == null) return "null";
199 string fmt = o is string ? "\"{0}\"" : "{0}";
200 return string.Format(System.Globalization.CultureInfo.InvariantCulture, fmt, o);
205 #region Operator Overloads
208 /// This operator creates a constraint that is satisfied only if both
209 /// argument constraints are satisfied.
211 public static Constraint operator &(Constraint left, Constraint right)
213 IResolveConstraint l = (IResolveConstraint)left;
214 IResolveConstraint r = (IResolveConstraint)right;
215 return new AndConstraint(l.Resolve(), r.Resolve());
219 /// This operator creates a constraint that is satisfied if either
220 /// of the argument constraints is satisfied.
222 public static Constraint operator |(Constraint left, Constraint right)
224 IResolveConstraint l = (IResolveConstraint)left;
225 IResolveConstraint r = (IResolveConstraint)right;
226 return new OrConstraint(l.Resolve(), r.Resolve());
230 /// This operator creates a constraint that is satisfied if the
231 /// argument constraint is not satisfied.
233 public static Constraint operator !(Constraint constraint)
235 IResolveConstraint r = (IResolveConstraint)constraint;
236 return new NotConstraint(r.Resolve());
241 #region Binary Operators
244 /// Returns a ConstraintExpression by appending And
245 /// to the current constraint.
247 public ConstraintExpression And
251 ConstraintBuilder builder = this.Builder;
254 builder = new ConstraintBuilder();
255 builder.Append(this);
258 builder.Append(new AndOperator());
260 return new ConstraintExpression(builder);
265 /// Returns a ConstraintExpression by appending And
266 /// to the current constraint.
268 public ConstraintExpression With
270 get { return this.And; }
274 /// Returns a ConstraintExpression by appending Or
275 /// to the current constraint.
277 public ConstraintExpression Or
281 ConstraintBuilder builder = this.Builder;
284 builder = new ConstraintBuilder();
285 builder.Append(this);
288 builder.Append(new OrOperator());
290 return new ConstraintExpression(builder);
296 #region After Modifier
300 /// Returns a DelayedConstraint with the specified delay time.
302 /// <param name="delayInMilliseconds">The delay in milliseconds.</param>
303 /// <returns></returns>
304 public DelayedConstraint After(int delayInMilliseconds)
306 return new DelayedConstraint(
307 Builder == null ? this : Builder.Resolve(),
308 delayInMilliseconds);
312 /// Returns a DelayedConstraint with the specified delay time
313 /// and polling interval.
315 /// <param name="delayInMilliseconds">The delay in milliseconds.</param>
316 /// <param name="pollingInterval">The interval at which to test the constraint.</param>
317 /// <returns></returns>
318 public DelayedConstraint After(int delayInMilliseconds, int pollingInterval)
320 return new DelayedConstraint(
321 Builder == null ? this : Builder.Resolve(),
329 #region IResolveConstraint Members
332 /// Resolves any pending operators and returns the resolved constraint.
334 IConstraint IResolveConstraint.Resolve()
336 return Builder == null ? this : Builder.Resolve();