Merge branch 'master' of git://github.com/mono/mono
[mono.git] / mcs / nunit24 / NUnitFramework / framework / Constraints / CollectionConstraints.cs
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
6 \r
7 using System;\r
8 using System.Collections;\r
9 \r
10 namespace NUnit.Framework.Constraints\r
11 {\r
12     #region CollectionConstraint\r
13     /// <summary>\r
14     /// CollectionConstraint is the abstract base class for\r
15     /// constraints that operate on collections.\r
16     /// </summary>\r
17     public abstract class CollectionConstraint : Constraint\r
18     {\r
19                 protected static bool IsEmpty( IEnumerable enumerable )\r
20                 {\r
21                         ICollection collection = enumerable as ICollection;\r
22                         if ( collection != null )\r
23                                 return collection.Count == 0;\r
24                         else\r
25                                 return !enumerable.GetEnumerator().MoveNext();\r
26                 }\r
27 \r
28                 /// <summary>\r
29                 /// CollectionTally counts (tallies) the number of\r
30                 /// occurences of each object in one or more enuerations.\r
31                 /// </summary>\r
32                 protected internal class CollectionTally\r
33                 {\r
34                         // Internal hash used to count occurences\r
35                         private Hashtable hash = new Hashtable();\r
36 \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
40 \r
41                         private int getTally(object obj)\r
42                         {\r
43                                 if ( obj == null ) obj = NULL;\r
44                                 object val = hash[obj];\r
45                                 return val == null ? 0 : (int)val;\r
46                         }\r
47 \r
48                         private void setTally(object obj, int tally)\r
49                         {\r
50                                 if ( obj == null ) obj = NULL;\r
51                                 hash[obj] = tally;\r
52                         }\r
53 \r
54                         /// <summary>\r
55                         /// Construct a CollectionTally object from a collection\r
56                         /// </summary>\r
57                         /// <param name="c"></param>\r
58                         public CollectionTally( IEnumerable c )\r
59                         {\r
60                                 foreach( object obj in c )\r
61                                         setTally( obj, getTally( obj ) + 1 );\r
62                         }\r
63 \r
64                         /// <summary>\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
68                         /// </summary>\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
72                         {\r
73                                 foreach( object obj in c )\r
74                                 {\r
75                                         int tally = getTally(obj);\r
76                                         if( tally > 0 )\r
77                                                 setTally(obj, tally - 1 );\r
78                                         else\r
79                                                 return false;\r
80                                 }\r
81 \r
82                                 return true;\r
83                         }\r
84 \r
85                         /// <summary>\r
86                         /// Test whether all the counts are equal to a given value\r
87                         /// </summary>\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
91                         {\r
92                                 foreach( DictionaryEntry entry in hash )\r
93                                         if ( (int)entry.Value != count )\r
94                                                 return false;\r
95 \r
96                                 return true;\r
97                         }\r
98 \r
99                         /// <summary>\r
100                         /// Get the count of the number of times an object is present in the tally\r
101                         /// </summary>\r
102                         public int this[object obj]\r
103                         {\r
104                                 get     { return getTally(obj); }\r
105                         }\r
106                 }\r
107 \r
108                 /// <summary>\r
109                 /// Test whether the constraint is satisfied by a given value\r
110                 /// </summary>\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
114                 {\r
115                         this.actual = actual;\r
116 \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
120                 \r
121                         return doMatch( enumerable );\r
122                 }\r
123 \r
124                 /// <summary>\r
125                 /// Protected method to be implemented by derived classes\r
126                 /// </summary>\r
127                 /// <param name="collection"></param>\r
128                 /// <returns></returns>\r
129                 protected abstract bool doMatch(IEnumerable collection);\r
130     }\r
131     #endregion\r
132 \r
133         #region EmptyCollectionConstraint\r
134     /// <summary>\r
135     /// EmptyCollectionConstraint tests whether a colletion is empty. \r
136     /// </summary>\r
137     public class EmptyCollectionConstraint : CollectionConstraint\r
138         {\r
139                 /// <summary>\r
140                 /// Check that the collection is empty\r
141                 /// </summary>\r
142                 /// <param name="collection"></param>\r
143                 /// <returns></returns>\r
144                 protected override bool doMatch(IEnumerable collection)\r
145                 {\r
146                         return IsEmpty( collection );\r
147                 }\r
148         \r
149                 /// <summary>\r
150                 /// Write the constraint description to a MessageWriter\r
151                 /// </summary>\r
152                 /// <param name="writer"></param>\r
153                 public override void WriteDescriptionTo(MessageWriter writer)\r
154                 {\r
155                         writer.Write( "<empty>" );\r
156                 }\r
157         }\r
158         #endregion\r
159 \r
160         #region UniqueItemsConstraint\r
161     /// <summary>\r
162     /// UniqueItemsConstraint tests whether all the items in a \r
163     /// collection are unique.\r
164     /// </summary>\r
165     public class UniqueItemsConstraint : CollectionConstraint\r
166     {\r
167         /// <summary>\r
168         /// Check that all items are unique.\r
169         /// </summary>\r
170         /// <param name="actual"></param>\r
171         /// <returns></returns>\r
172         protected override bool doMatch(IEnumerable actual)\r
173         {\r
174                         return new CollectionTally( actual ).AllCountsEqualTo( 1 );\r
175         }\r
176 \r
177         /// <summary>\r
178         /// Write a description of this constraint to a MessageWriter\r
179         /// </summary>\r
180         /// <param name="writer"></param>\r
181         public override void WriteDescriptionTo(MessageWriter writer)\r
182         {\r
183             writer.Write("all items unique");\r
184         }\r
185     }\r
186     #endregion\r
187 \r
188     #region CollectionContainsConstraint\r
189     /// <summary>\r
190     /// CollectionContainsConstraint is used to test whether a collection\r
191     /// contains an expected object as a member.\r
192     /// </summary>\r
193     public class CollectionContainsConstraint : CollectionConstraint\r
194     {\r
195         private object expected;\r
196 \r
197         /// <summary>\r
198         /// Construct a CollectionContainsConstraint\r
199         /// </summary>\r
200         /// <param name="expected"></param>\r
201         public CollectionContainsConstraint(object expected)\r
202         {\r
203             this.expected = expected;\r
204         }\r
205 \r
206         /// <summary>\r
207         /// Test whether the expected item is contained in the collection\r
208         /// </summary>\r
209         /// <param name="actual"></param>\r
210         /// <returns></returns>\r
211         protected override bool doMatch(IEnumerable actual)\r
212         {\r
213                         foreach (object obj in actual)\r
214                                 if ( Object.Equals( obj, expected ) )\r
215                                         return true;\r
216 \r
217                         return false;\r
218                 }\r
219 \r
220         /// <summary>\r
221         /// Write a descripton of the constraint to a MessageWriter\r
222         /// </summary>\r
223         /// <param name="writer"></param>\r
224         public override void WriteDescriptionTo(MessageWriter writer)\r
225         {\r
226             writer.WritePredicate( "collection containing" );\r
227             writer.WriteExpectedValue(expected);\r
228         }\r
229     }\r
230     #endregion\r
231 \r
232     #region CollectionEquivalentConstraint\r
233     /// <summary>\r
234     /// CollectionEquivalentCOnstraint is used to determine whether two\r
235     /// collections are equivalent.\r
236     /// </summary>\r
237     public class CollectionEquivalentConstraint : CollectionConstraint\r
238     {\r
239         private IEnumerable expected;\r
240 \r
241         /// <summary>\r
242         /// Construct a CollectionEquivalentConstraint\r
243         /// </summary>\r
244         /// <param name="expected"></param>\r
245         public CollectionEquivalentConstraint(IEnumerable expected)\r
246         {\r
247             this.expected = expected;\r
248         }\r
249 \r
250         /// <summary>\r
251         /// Test whether two collections are equivalent\r
252         /// </summary>\r
253         /// <param name="actual"></param>\r
254         /// <returns></returns>\r
255         protected override bool doMatch(IEnumerable actual)\r
256         {\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
260                                         return false;\r
261 \r
262                         CollectionTally tally = new CollectionTally( expected );\r
263                         return tally.CanRemove( actual ) && tally.AllCountsEqualTo( 0 );\r
264         }\r
265 \r
266         /// <summary>\r
267         /// Write a description of this constraint to a MessageWriter\r
268         /// </summary>\r
269         /// <param name="writer"></param>\r
270         public override void WriteDescriptionTo(MessageWriter writer)\r
271         {\r
272             writer.WritePredicate("equivalent to");\r
273             writer.WriteExpectedValue(expected);\r
274         }\r
275     }\r
276     #endregion\r
277 \r
278     #region CollectionSubsetConstraint\r
279     /// <summary>\r
280     /// CollectionSubsetConstraint is used to determine whether\r
281     /// one collection is a subset of another\r
282     /// </summary>\r
283     public class CollectionSubsetConstraint : CollectionConstraint\r
284     {\r
285         private IEnumerable expected;\r
286 \r
287         /// <summary>\r
288         /// Construct a CollectionSubsetConstraint\r
289         /// </summary>\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
292         {\r
293             this.expected = expected;\r
294         }\r
295 \r
296         /// <summary>\r
297         /// Test whether the actual collection is a subset of \r
298         /// the expected collection provided.\r
299         /// </summary>\r
300         /// <param name="actual"></param>\r
301         /// <returns></returns>\r
302         protected override bool doMatch(IEnumerable actual)\r
303         {\r
304                         return new CollectionTally( expected ).CanRemove( actual );\r
305                 }\r
306         \r
307         /// <summary>\r
308         /// Write a description of this constraint to a MessageWriter\r
309         /// </summary>\r
310         /// <param name="writer"></param>\r
311         public override void WriteDescriptionTo(MessageWriter writer)\r
312         {\r
313             writer.WritePredicate( "subset of" );\r
314             writer.WriteExpectedValue(expected);\r
315         }\r
316     }\r
317     #endregion\r
318 }\r