1 // ****************************************************************
\r
2 // Copyright 2007, Charlie Poole
\r
3 // This is free software licensed under the NUnit license. You may
\r
4 // obtain a copy of the license at http://nunit.org/?p=license&r=2.4
\r
5 // ****************************************************************
\r
8 using System.Collections;
\r
10 namespace NUnit.Framework.Constraints
\r
12 #region CollectionConstraint
\r
14 /// CollectionConstraint is the abstract base class for
\r
15 /// constraints that operate on collections.
\r
17 public abstract class CollectionConstraint : Constraint
\r
19 protected static bool IsEmpty( IEnumerable enumerable )
\r
21 ICollection collection = enumerable as ICollection;
\r
22 if ( collection != null )
\r
23 return collection.Count == 0;
\r
25 return !enumerable.GetEnumerator().MoveNext();
\r
29 /// CollectionTally counts (tallies) the number of
\r
30 /// occurences of each object in one or more enuerations.
\r
32 protected internal class CollectionTally
\r
34 // Internal hash used to count occurences
\r
35 private Hashtable hash = new Hashtable();
\r
37 // We use this for any null entries found, since
\r
38 // the key to a hash may not be null.
\r
39 static object NULL = new object();
\r
41 private int getTally(object obj)
\r
43 if ( obj == null ) obj = NULL;
\r
44 object val = hash[obj];
\r
45 return val == null ? 0 : (int)val;
\r
48 private void setTally(object obj, int tally)
\r
50 if ( obj == null ) obj = NULL;
\r
55 /// Construct a CollectionTally object from a collection
\r
57 /// <param name="c"></param>
\r
58 public CollectionTally( IEnumerable c )
\r
60 foreach( object obj in c )
\r
61 setTally( obj, getTally( obj ) + 1 );
\r
65 /// Remove the counts for a collection from the tally,
\r
66 /// so long as their are sufficient items to remove.
\r
67 /// The tallies are not permitted to become negative.
\r
69 /// <param name="c">The collection to remove</param>
\r
70 /// <returns>True if there were enough items to remove, otherwise false</returns>
\r
71 public bool CanRemove( IEnumerable c )
\r
73 foreach( object obj in c )
\r
75 int tally = getTally(obj);
\r
77 setTally(obj, tally - 1 );
\r
86 /// Test whether all the counts are equal to a given value
\r
88 /// <param name="count">The value to be looked for</param>
\r
89 /// <returns>True if all counts are equal to the value, otherwise false</returns>
\r
90 public bool AllCountsEqualTo( int count )
\r
92 foreach( DictionaryEntry entry in hash )
\r
93 if ( (int)entry.Value != count )
\r
100 /// Get the count of the number of times an object is present in the tally
\r
102 public int this[object obj]
\r
104 get { return getTally(obj); }
\r
109 /// Test whether the constraint is satisfied by a given value
\r
111 /// <param name="actual">The value to be tested</param>
\r
112 /// <returns>True for success, false for failure</returns>
\r
113 public override bool Matches(object actual)
\r
115 this.actual = actual;
\r
117 IEnumerable enumerable = actual as IEnumerable;
\r
118 if ( enumerable == null )
\r
119 throw new ArgumentException( "The actual value must be an IEnumerable", "actual" );
\r
121 return doMatch( enumerable );
\r
125 /// Protected method to be implemented by derived classes
\r
127 /// <param name="collection"></param>
\r
128 /// <returns></returns>
\r
129 protected abstract bool doMatch(IEnumerable collection);
\r
133 #region EmptyCollectionConstraint
\r
135 /// EmptyCollectionConstraint tests whether a colletion is empty.
\r
137 public class EmptyCollectionConstraint : CollectionConstraint
\r
140 /// Check that the collection is empty
\r
142 /// <param name="collection"></param>
\r
143 /// <returns></returns>
\r
144 protected override bool doMatch(IEnumerable collection)
\r
146 return IsEmpty( collection );
\r
150 /// Write the constraint description to a MessageWriter
\r
152 /// <param name="writer"></param>
\r
153 public override void WriteDescriptionTo(MessageWriter writer)
\r
155 writer.Write( "<empty>" );
\r
160 #region UniqueItemsConstraint
\r
162 /// UniqueItemsConstraint tests whether all the items in a
\r
163 /// collection are unique.
\r
165 public class UniqueItemsConstraint : CollectionConstraint
\r
168 /// Check that all items are unique.
\r
170 /// <param name="actual"></param>
\r
171 /// <returns></returns>
\r
172 protected override bool doMatch(IEnumerable actual)
\r
174 return new CollectionTally( actual ).AllCountsEqualTo( 1 );
\r
178 /// Write a description of this constraint to a MessageWriter
\r
180 /// <param name="writer"></param>
\r
181 public override void WriteDescriptionTo(MessageWriter writer)
\r
183 writer.Write("all items unique");
\r
188 #region CollectionContainsConstraint
\r
190 /// CollectionContainsConstraint is used to test whether a collection
\r
191 /// contains an expected object as a member.
\r
193 public class CollectionContainsConstraint : CollectionConstraint
\r
195 private object expected;
\r
198 /// Construct a CollectionContainsConstraint
\r
200 /// <param name="expected"></param>
\r
201 public CollectionContainsConstraint(object expected)
\r
203 this.expected = expected;
\r
207 /// Test whether the expected item is contained in the collection
\r
209 /// <param name="actual"></param>
\r
210 /// <returns></returns>
\r
211 protected override bool doMatch(IEnumerable actual)
\r
213 foreach (object obj in actual)
\r
214 if ( Object.Equals( obj, expected ) )
\r
221 /// Write a descripton of the constraint to a MessageWriter
\r
223 /// <param name="writer"></param>
\r
224 public override void WriteDescriptionTo(MessageWriter writer)
\r
226 writer.WritePredicate( "collection containing" );
\r
227 writer.WriteExpectedValue(expected);
\r
232 #region CollectionEquivalentConstraint
\r
234 /// CollectionEquivalentCOnstraint is used to determine whether two
\r
235 /// collections are equivalent.
\r
237 public class CollectionEquivalentConstraint : CollectionConstraint
\r
239 private IEnumerable expected;
\r
242 /// Construct a CollectionEquivalentConstraint
\r
244 /// <param name="expected"></param>
\r
245 public CollectionEquivalentConstraint(IEnumerable expected)
\r
247 this.expected = expected;
\r
251 /// Test whether two collections are equivalent
\r
253 /// <param name="actual"></param>
\r
254 /// <returns></returns>
\r
255 protected override bool doMatch(IEnumerable actual)
\r
257 // This is just an optimization
\r
258 if( expected is ICollection && actual is ICollection )
\r
259 if( ((ICollection)actual).Count != ((ICollection)expected).Count )
\r
262 CollectionTally tally = new CollectionTally( expected );
\r
263 return tally.CanRemove( actual ) && tally.AllCountsEqualTo( 0 );
\r
267 /// Write a description of this constraint to a MessageWriter
\r
269 /// <param name="writer"></param>
\r
270 public override void WriteDescriptionTo(MessageWriter writer)
\r
272 writer.WritePredicate("equivalent to");
\r
273 writer.WriteExpectedValue(expected);
\r
278 #region CollectionSubsetConstraint
\r
280 /// CollectionSubsetConstraint is used to determine whether
\r
281 /// one collection is a subset of another
\r
283 public class CollectionSubsetConstraint : CollectionConstraint
\r
285 private IEnumerable expected;
\r
288 /// Construct a CollectionSubsetConstraint
\r
290 /// <param name="expected">The collection that the actual value is expected to be a subset of</param>
\r
291 public CollectionSubsetConstraint(IEnumerable expected)
\r
293 this.expected = expected;
\r
297 /// Test whether the actual collection is a subset of
\r
298 /// the expected collection provided.
\r
300 /// <param name="actual"></param>
\r
301 /// <returns></returns>
\r
302 protected override bool doMatch(IEnumerable actual)
\r
304 return new CollectionTally( expected ).CanRemove( actual );
\r
308 /// Write a description of this constraint to a MessageWriter
\r
310 /// <param name="writer"></param>
\r
311 public override void WriteDescriptionTo(MessageWriter writer)
\r
313 writer.WritePredicate( "subset of" );
\r
314 writer.WriteExpectedValue(expected);
\r