PageRenderTime 26ms CodeModel.GetById 9ms app.highlight 12ms RepoModel.GetById 1ms app.codeStats 1ms

/src/NUnit/framework/Constraints/Constraint.cs

#
C# | 379 lines | 194 code | 39 blank | 146 comment | 17 complexity | 9f28fe896aeac4fe9bf543ac00953ec4 MD5 | raw file
  1// ****************************************************************
  2// Copyright 2007, Charlie Poole
  3// This is free software licensed under the NUnit license. You may
  4// obtain a copy of the license at http://nunit.org
  5// ****************************************************************
  6
  7using System.Collections;
  8
  9namespace NUnit.Framework.Constraints
 10{
 11    /// <summary>
 12    /// Delegate used to delay evaluation of the actual value
 13    /// to be used in evaluating a constraint
 14    /// </summary>
 15    public delegate object ActualValueDelegate();
 16    
 17    /// <summary>
 18    /// The Constraint class is the base of all built-in constraints
 19    /// within NUnit. It provides the operator overloads used to combine 
 20    /// constraints.
 21    /// </summary>
 22    public abstract class Constraint : IResolveConstraint
 23    {
 24        #region UnsetObject Class
 25        /// <summary>
 26        /// Class used to detect any derived constraints
 27        /// that fail to set the actual value in their
 28        /// Matches override.
 29        /// </summary>
 30        private class UnsetObject
 31        {
 32            public override string ToString()
 33            {
 34                return "UNSET";
 35            }
 36        }
 37        #endregion
 38
 39		#region Static and Instance Fields
 40        /// <summary>
 41        /// Static UnsetObject used to detect derived constraints
 42        /// failing to set the actual value.
 43        /// </summary>
 44        protected static object UNSET = new UnsetObject();
 45
 46		/// <summary>
 47        /// The actual value being tested against a constraint
 48        /// </summary>
 49        protected object actual = UNSET;
 50
 51        /// <summary>
 52        /// The display name of this Constraint for use by ToString()
 53        /// </summary>
 54        private string displayName;
 55
 56        /// <summary>
 57        /// Argument fields used by ToString();
 58        /// </summary>
 59        private readonly int argcnt;
 60        private readonly object arg1;
 61        private readonly object arg2;
 62
 63        /// <summary>
 64        /// The builder holding this constraint
 65        /// </summary>
 66        private ConstraintBuilder builder;
 67        #endregion
 68
 69        #region Constructors
 70        /// <summary>
 71        /// Construct a constraint with no arguments
 72        /// </summary>
 73        public Constraint()
 74        {
 75            argcnt = 0;
 76        }
 77
 78        /// <summary>
 79        /// Construct a constraint with one argument
 80        /// </summary>
 81        public Constraint(object arg)
 82        {
 83            argcnt = 1;
 84            this.arg1 = arg;
 85        }
 86
 87        /// <summary>
 88        /// Construct a constraint with two arguments
 89        /// </summary>
 90        public Constraint(object arg1, object arg2)
 91        {
 92            argcnt = 2;
 93            this.arg1 = arg1;
 94            this.arg2 = arg2;
 95        }
 96        #endregion
 97
 98        #region Set Containing ConstraintBuilder
 99        /// <summary>
100        /// Sets the ConstraintBuilder holding this constraint
101        /// </summary>
102        internal void SetBuilder(ConstraintBuilder builder)
103        {
104            this.builder = builder;
105        }
106        #endregion
107
108        #region Properties
109        /// <summary>
110        /// The display name of this Constraint for use by ToString().
111        /// The default value is the name of the constraint with
112        /// trailing "Constraint" removed. Derived classes may set
113        /// this to another name in their constructors.
114        /// </summary>
115        public string DisplayName
116        {
117            get
118            {
119                if (displayName == null)
120                {
121                    displayName = this.GetType().Name.ToLower();
122                    if (displayName.EndsWith("`1") || displayName.EndsWith("`2"))
123                        displayName = displayName.Substring(0, displayName.Length - 2);
124                    if (displayName.EndsWith("constraint"))
125                        displayName = displayName.Substring(0, displayName.Length - 10);
126                }
127
128                return displayName;
129            }
130
131            set { displayName = value; }
132        }
133		#endregion
134
135		#region Abstract and Virtual Methods
136        /// <summary>
137        /// Write the failure message to the MessageWriter provided
138        /// as an argument. The default implementation simply passes
139        /// the constraint and the actual value to the writer, which
140        /// then displays the constraint description and the value.
141        /// 
142        /// Constraints that need to provide additional details,
143        /// such as where the error occured can override this.
144        /// </summary>
145        /// <param name="writer">The MessageWriter on which to display the message</param>
146        public virtual void WriteMessageTo(MessageWriter writer)
147        {
148            writer.DisplayDifferences(this);
149        }
150
151        /// <summary>
152        /// Test whether the constraint is satisfied by a given value
153        /// </summary>
154        /// <param name="actual">The value to be tested</param>
155        /// <returns>True for success, false for failure</returns>
156        public abstract bool Matches(object actual);
157
158        /// <summary>
159        /// Test whether the constraint is satisfied by an
160        /// ActualValueDelegate that returns the value to be tested.
161        /// The default implementation simply evaluates the delegate
162        /// but derived classes may override it to provide for delayed 
163        /// processing.
164        /// </summary>
165        /// <param name="del">An ActualValueDelegate</param>
166        /// <returns>True for success, false for failure</returns>
167        public virtual bool Matches(ActualValueDelegate del)
168        {
169            return Matches(del());
170        }
171
172#if NET_2_0
173        /// <summary>
174        /// Test whether the constraint is satisfied by a given reference.
175        /// The default implementation simply dereferences the value but
176        /// derived classes may override it to provide for delayed processing.
177        /// </summary>
178        /// <param name="actual">A reference to the value to be tested</param>
179        /// <returns>True for success, false for failure</returns>
180        public virtual bool Matches<T>(ref T actual)
181        {
182            return Matches(actual);
183        }
184#else
185		/// <summary>
186		/// Test whether the constraint is satisfied by a given bool reference.
187		/// The default implementation simply dereferences the value but
188		/// derived classes may override it to provide for delayed processing.
189		/// </summary>
190		/// <param name="actual">A reference to the value to be tested</param>
191		/// <returns>True for success, false for failure</returns>
192		public virtual bool Matches(ref bool actual)
193		{
194			return Matches(actual);
195		}
196#endif
197
198        /// <summary>
199        /// Write the constraint description to a MessageWriter
200        /// </summary>
201        /// <param name="writer">The writer on which the description is displayed</param>
202        public abstract void WriteDescriptionTo(MessageWriter writer);
203
204        /// <summary>
205		/// Write the actual value for a failing constraint test to a
206		/// MessageWriter. The default implementation simply writes
207		/// the raw value of actual, leaving it to the writer to
208		/// perform any formatting.
209		/// </summary>
210		/// <param name="writer">The writer on which the actual value is displayed</param>
211		public virtual void WriteActualValueTo(MessageWriter writer)
212		{
213			writer.WriteActualValue( actual );
214		}
215		#endregion
216
217        #region ToString Override
218        /// <summary>
219        /// Default override of ToString returns the constraint DisplayName
220        /// followed by any arguments within angle brackets.
221        /// </summary>
222        /// <returns></returns>
223        public override string ToString()
224        {
225            string rep = GetStringRepresentation();
226
227            return this.builder == null ? rep : string.Format("<unresolved {0}>", rep);
228        }
229
230        /// <summary>
231        /// Returns the string representation of this constraint
232        /// </summary>
233        protected virtual string GetStringRepresentation()
234        {
235            switch (argcnt)
236            {
237                default:
238                case 0:
239                    return string.Format("<{0}>", DisplayName);
240                case 1:
241                    return string.Format("<{0} {1}>", DisplayName, _displayable(arg1));
242                case 2:
243                    return string.Format("<{0} {1} {2}>", DisplayName, _displayable(arg1), _displayable(arg2));
244            }
245        }
246
247        private string _displayable(object o)
248        {
249            if (o == null) return "null";
250
251            string fmt = o is string ? "\"{0}\"" : "{0}";
252            return string.Format(System.Globalization.CultureInfo.InvariantCulture, fmt, o);
253        }
254        #endregion
255
256        #region Operator Overloads
257        /// <summary>
258        /// This operator creates a constraint that is satisfied only if both 
259        /// argument constraints are satisfied.
260        /// </summary>
261        public static Constraint operator &(Constraint left, Constraint right)
262        {
263            IResolveConstraint l = (IResolveConstraint)left;
264            IResolveConstraint r = (IResolveConstraint)right;
265            return new AndConstraint(l.Resolve(), r.Resolve());
266        }
267
268        /// <summary>
269        /// This operator creates a constraint that is satisfied if either 
270        /// of the argument constraints is satisfied.
271        /// </summary>
272        public static Constraint operator |(Constraint left, Constraint right)
273        {
274            IResolveConstraint l = (IResolveConstraint)left;
275            IResolveConstraint r = (IResolveConstraint)right;
276            return new OrConstraint(l.Resolve(), r.Resolve());
277        }
278
279        /// <summary>
280        /// This operator creates a constraint that is satisfied if the 
281        /// argument constraint is not satisfied.
282        /// </summary>
283        public static Constraint operator !(Constraint constraint)
284        {
285            IResolveConstraint r = constraint as IResolveConstraint;
286            return new NotConstraint(r == null ? new NullConstraint() : r.Resolve());
287        }
288        #endregion
289
290        #region Binary Operators
291        /// <summary>
292        /// Returns a ConstraintExpression by appending And
293        /// to the current constraint.
294        /// </summary>
295        public ConstraintExpression And
296        {
297            get
298            {
299                ConstraintBuilder builder = this.builder;
300                if (builder == null)
301                {
302                    builder = new ConstraintBuilder();
303                    builder.Append(this);
304                }
305
306                builder.Append(new AndOperator());
307
308                return new ConstraintExpression(builder);
309            }
310        }
311
312        /// <summary>
313        /// Returns a ConstraintExpression by appending And
314        /// to the current constraint.
315        /// </summary>
316        public ConstraintExpression With
317        {
318            get { return this.And; }
319        }
320
321        /// <summary>
322        /// Returns a ConstraintExpression by appending Or
323        /// to the current constraint.
324        /// </summary>
325        public ConstraintExpression Or
326        {
327            get
328            {
329                ConstraintBuilder builder = this.builder;
330                if (builder == null)
331                {
332                    builder = new ConstraintBuilder();
333                    builder.Append(this);
334                }
335
336                builder.Append(new OrOperator());
337
338                return new ConstraintExpression(builder);
339            }
340        }
341        #endregion
342
343        #region After Modifier
344        /// <summary>
345        /// Returns a DelayedConstraint with the specified delay time.
346        /// </summary>
347        /// <param name="delayInMilliseconds">The delay in milliseconds.</param>
348        /// <returns></returns>
349        public DelayedConstraint After(int delayInMilliseconds)
350        {
351            return new DelayedConstraint(
352                builder == null ? this : builder.Resolve(),
353                delayInMilliseconds);
354        }
355
356        /// <summary>
357        /// Returns a DelayedConstraint with the specified delay time
358        /// and polling interval.
359        /// </summary>
360        /// <param name="delayInMilliseconds">The delay in milliseconds.</param>
361        /// <param name="pollingInterval">The interval at which to test the constraint.</param>
362        /// <returns></returns>
363        public DelayedConstraint After(int delayInMilliseconds, int pollingInterval)
364        {
365            return new DelayedConstraint(
366                builder == null ? this : builder.Resolve(),
367                delayInMilliseconds,
368                pollingInterval);
369        }
370        #endregion
371
372        #region IResolveConstraint Members
373        Constraint IResolveConstraint.Resolve()
374        {
375            return builder == null ? this : builder.Resolve();
376        }
377        #endregion
378    }
379}