/Lib/EmitMapper.Sources/Silverlight/NUnit.SL/framework.sl/Constraints/CollectionConstraints.cs

https://github.com/antonsamarsky/emitmapper-tools · C# · 608 lines · 324 code · 69 blank · 215 comment · 36 complexity · c8c06b1b299331e9a45e43c87ccdb5de 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. using System;
  7. using System.Collections;
  8. using System.Reflection;
  9. using System.Text;
  10. #if NET_2_0
  11. using System.Collections.Generic;
  12. #endif
  13. namespace NUnit.Framework.Constraints
  14. {
  15. #region CollectionConstraint
  16. /// <summary>
  17. /// CollectionConstraint is the abstract base class for
  18. /// constraints that operate on collections.
  19. /// </summary>
  20. public abstract class CollectionConstraint : Constraint
  21. {
  22. /// <summary>
  23. /// Construct an empty CollectionConstraint
  24. /// </summary>
  25. public CollectionConstraint() { }
  26. /// <summary>
  27. /// Construct a CollectionConstraint
  28. /// </summary>
  29. /// <param name="arg"></param>
  30. public CollectionConstraint(object arg) : base(arg) { }
  31. /// <summary>
  32. /// Determines whether the specified enumerable is empty.
  33. /// </summary>
  34. /// <param name="enumerable">The enumerable.</param>
  35. /// <returns>
  36. /// <c>true</c> if the specified enumerable is empty; otherwise, <c>false</c>.
  37. /// </returns>
  38. protected static bool IsEmpty( IEnumerable enumerable )
  39. {
  40. ICollection collection = enumerable as ICollection;
  41. if ( collection != null )
  42. return collection.Count == 0;
  43. else
  44. return !enumerable.GetEnumerator().MoveNext();
  45. }
  46. /// <summary>
  47. /// Test whether the constraint is satisfied by a given value
  48. /// </summary>
  49. /// <param name="actual">The value to be tested</param>
  50. /// <returns>True for success, false for failure</returns>
  51. public override bool Matches(object actual)
  52. {
  53. this.actual = actual;
  54. IEnumerable enumerable = actual as IEnumerable;
  55. if ( enumerable == null )
  56. throw new ArgumentException( "The actual value must be an IEnumerable", "actual" );
  57. return doMatch( enumerable );
  58. }
  59. /// <summary>
  60. /// Protected method to be implemented by derived classes
  61. /// </summary>
  62. /// <param name="collection"></param>
  63. /// <returns></returns>
  64. protected abstract bool doMatch(IEnumerable collection);
  65. }
  66. #endregion
  67. #region CollectionItemsEqualConstraint
  68. /// <summary>
  69. /// CollectionItemsEqualConstraint is the abstract base class for all
  70. /// collection constraints that apply some notion of item equality
  71. /// as a part of their operation.
  72. /// </summary>
  73. public abstract class CollectionItemsEqualConstraint : CollectionConstraint
  74. {
  75. private NUnitEqualityComparer comparer = NUnitEqualityComparer.Default;
  76. /// <summary>
  77. /// Construct an empty CollectionConstraint
  78. /// </summary>
  79. public CollectionItemsEqualConstraint() { }
  80. /// <summary>
  81. /// Construct a CollectionConstraint
  82. /// </summary>
  83. /// <param name="arg"></param>
  84. public CollectionItemsEqualConstraint(object arg) : base(arg) { }
  85. #region Modifiers
  86. /// <summary>
  87. /// Flag the constraint to ignore case and return self.
  88. /// </summary>
  89. public CollectionItemsEqualConstraint IgnoreCase
  90. {
  91. get
  92. {
  93. comparer.IgnoreCase = true;
  94. return this;
  95. }
  96. }
  97. /// <summary>
  98. /// Flag the constraint to use the supplied IComparer object.
  99. /// </summary>
  100. /// <param name="comparer">The IComparer object to use.</param>
  101. /// <returns>Self.</returns>
  102. public CollectionItemsEqualConstraint Using(IComparer comparer)
  103. {
  104. this.comparer.ExternalComparer = EqualityAdapter.For(comparer);
  105. return this;
  106. }
  107. #if NET_2_0
  108. /// <summary>
  109. /// Flag the constraint to use the supplied IComparer object.
  110. /// </summary>
  111. /// <param name="comparer">The IComparer object to use.</param>
  112. /// <returns>Self.</returns>
  113. public CollectionItemsEqualConstraint Using<T>(IComparer<T> comparer)
  114. {
  115. this.comparer.ExternalComparer = EqualityAdapter.For(comparer);
  116. return this;
  117. }
  118. /// <summary>
  119. /// Flag the constraint to use the supplied Comparison object.
  120. /// </summary>
  121. /// <param name="comparer">The IComparer object to use.</param>
  122. /// <returns>Self.</returns>
  123. public CollectionItemsEqualConstraint Using<T>(Comparison<T> comparer)
  124. {
  125. this.comparer.ExternalComparer = EqualityAdapter.For(comparer);
  126. return this;
  127. }
  128. /// <summary>
  129. /// Flag the constraint to use the supplied IEqualityComparer object.
  130. /// </summary>
  131. /// <param name="comparer">The IComparer object to use.</param>
  132. /// <returns>Self.</returns>
  133. public CollectionItemsEqualConstraint Using(IEqualityComparer comparer)
  134. {
  135. this.comparer.ExternalComparer = EqualityAdapter.For(comparer);
  136. return this;
  137. }
  138. /// <summary>
  139. /// Flag the constraint to use the supplied IEqualityComparer object.
  140. /// </summary>
  141. /// <param name="comparer">The IComparer object to use.</param>
  142. /// <returns>Self.</returns>
  143. public CollectionItemsEqualConstraint Using<T>(IEqualityComparer<T> comparer)
  144. {
  145. this.comparer.ExternalComparer = EqualityAdapter.For(comparer);
  146. return this;
  147. }
  148. #endif
  149. #endregion
  150. /// <summary>
  151. /// Compares two collection members for equality
  152. /// </summary>
  153. protected bool ItemsEqual(object x, object y)
  154. {
  155. return comparer.ObjectsEqual(x, y);
  156. }
  157. /// <summary>
  158. /// Return a new CollectionTally for use in making tests
  159. /// </summary>
  160. /// <param name="c">The collection to be included in the tally</param>
  161. protected CollectionTally Tally(IEnumerable c)
  162. {
  163. return new CollectionTally(comparer, c);
  164. }
  165. /// <summary>
  166. /// CollectionTally counts (tallies) the number of
  167. /// occurences of each object in one or more enumerations.
  168. /// </summary>
  169. protected internal class CollectionTally
  170. {
  171. // Internal list used to track occurences
  172. private List<object> list = new List<object>();
  173. private NUnitEqualityComparer comparer;
  174. /// <summary>
  175. /// Construct a CollectionTally object from a comparer and a collection
  176. /// </summary>
  177. public CollectionTally(NUnitEqualityComparer comparer, IEnumerable c)
  178. {
  179. this.comparer = comparer;
  180. foreach (object o in c)
  181. list.Add(o);
  182. }
  183. /// <summary>
  184. /// The number of objects remaining in the tally
  185. /// </summary>
  186. public int Count
  187. {
  188. get { return list.Count; }
  189. }
  190. private bool ItemsEqual(object expected, object actual)
  191. {
  192. return comparer.ObjectsEqual(expected, actual);
  193. }
  194. /// <summary>
  195. /// Try to remove an object from the tally
  196. /// </summary>
  197. /// <param name="o">The object to remove</param>
  198. /// <returns>True if successful, false if the object was not found</returns>
  199. public bool TryRemove(object o)
  200. {
  201. for (int index = 0; index < list.Count; index++)
  202. if (ItemsEqual(list[index], o))
  203. {
  204. list.RemoveAt(index);
  205. return true;
  206. }
  207. return false;
  208. }
  209. /// <summary>
  210. /// Try to remove a set of objects from the tally
  211. /// </summary>
  212. /// <param name="c">The objects to remove</param>
  213. /// <returns>True if successful, false if any object was not found</returns>
  214. public bool TryRemove(IEnumerable c)
  215. {
  216. foreach (object o in c)
  217. if (!TryRemove(o))
  218. return false;
  219. return true;
  220. }
  221. }
  222. }
  223. #endregion
  224. #region EmptyCollectionConstraint
  225. /// <summary>
  226. /// EmptyCollectionConstraint tests whether a collection is empty.
  227. /// </summary>
  228. public class EmptyCollectionConstraint : CollectionConstraint
  229. {
  230. /// <summary>
  231. /// Check that the collection is empty
  232. /// </summary>
  233. /// <param name="collection"></param>
  234. /// <returns></returns>
  235. protected override bool doMatch(IEnumerable collection)
  236. {
  237. return IsEmpty( collection );
  238. }
  239. /// <summary>
  240. /// Write the constraint description to a MessageWriter
  241. /// </summary>
  242. /// <param name="writer"></param>
  243. public override void WriteDescriptionTo(MessageWriter writer)
  244. {
  245. writer.Write( "<empty>" );
  246. }
  247. }
  248. #endregion
  249. #region UniqueItemsConstraint
  250. /// <summary>
  251. /// UniqueItemsConstraint tests whether all the items in a
  252. /// collection are unique.
  253. /// </summary>
  254. public class UniqueItemsConstraint : CollectionItemsEqualConstraint
  255. {
  256. /// <summary>
  257. /// Check that all items are unique.
  258. /// </summary>
  259. /// <param name="actual"></param>
  260. /// <returns></returns>
  261. protected override bool doMatch(IEnumerable actual)
  262. {
  263. List<object> list = new List<object>();
  264. foreach (object o1 in actual)
  265. {
  266. foreach( object o2 in list )
  267. if ( ItemsEqual(o1, o2) )
  268. return false;
  269. list.Add(o1);
  270. }
  271. return true;
  272. }
  273. /// <summary>
  274. /// Write a description of this constraint to a MessageWriter
  275. /// </summary>
  276. /// <param name="writer"></param>
  277. public override void WriteDescriptionTo(MessageWriter writer)
  278. {
  279. writer.Write("all items unique");
  280. }
  281. }
  282. #endregion
  283. #region CollectionContainsConstraint
  284. /// <summary>
  285. /// CollectionContainsConstraint is used to test whether a collection
  286. /// contains an expected object as a member.
  287. /// </summary>
  288. public class CollectionContainsConstraint : CollectionItemsEqualConstraint
  289. {
  290. private object expected;
  291. /// <summary>
  292. /// Construct a CollectionContainsConstraint
  293. /// </summary>
  294. /// <param name="expected"></param>
  295. public CollectionContainsConstraint(object expected)
  296. : base(expected)
  297. {
  298. this.expected = expected;
  299. this.DisplayName = "contains";
  300. }
  301. /// <summary>
  302. /// Test whether the expected item is contained in the collection
  303. /// </summary>
  304. /// <param name="actual"></param>
  305. /// <returns></returns>
  306. protected override bool doMatch(IEnumerable actual)
  307. {
  308. foreach (object obj in actual)
  309. if (ItemsEqual(obj, expected))
  310. return true;
  311. return false;
  312. }
  313. /// <summary>
  314. /// Write a descripton of the constraint to a MessageWriter
  315. /// </summary>
  316. /// <param name="writer"></param>
  317. public override void WriteDescriptionTo(MessageWriter writer)
  318. {
  319. writer.WritePredicate("collection containing");
  320. writer.WriteExpectedValue(expected);
  321. }
  322. }
  323. #endregion
  324. #region CollectionEquivalentConstraint
  325. /// <summary>
  326. /// CollectionEquivalentCOnstraint is used to determine whether two
  327. /// collections are equivalent.
  328. /// </summary>
  329. public class CollectionEquivalentConstraint : CollectionItemsEqualConstraint
  330. {
  331. private IEnumerable expected;
  332. /// <summary>
  333. /// Construct a CollectionEquivalentConstraint
  334. /// </summary>
  335. /// <param name="expected"></param>
  336. public CollectionEquivalentConstraint(IEnumerable expected) : base(expected)
  337. {
  338. this.expected = expected;
  339. this.DisplayName = "equivalent";
  340. }
  341. /// <summary>
  342. /// Test whether two collections are equivalent
  343. /// </summary>
  344. /// <param name="actual"></param>
  345. /// <returns></returns>
  346. protected override bool doMatch(IEnumerable actual)
  347. {
  348. // This is just an optimization
  349. if( expected is ICollection && actual is ICollection )
  350. if( ((ICollection)actual).Count != ((ICollection)expected).Count )
  351. return false;
  352. CollectionTally tally = Tally(expected);
  353. return tally.TryRemove(actual) && tally.Count == 0;
  354. }
  355. /// <summary>
  356. /// Write a description of this constraint to a MessageWriter
  357. /// </summary>
  358. /// <param name="writer"></param>
  359. public override void WriteDescriptionTo(MessageWriter writer)
  360. {
  361. writer.WritePredicate("equivalent to");
  362. writer.WriteExpectedValue(expected);
  363. }
  364. }
  365. #endregion
  366. #region CollectionSubsetConstraint
  367. /// <summary>
  368. /// CollectionSubsetConstraint is used to determine whether
  369. /// one collection is a subset of another
  370. /// </summary>
  371. public class CollectionSubsetConstraint : CollectionItemsEqualConstraint
  372. {
  373. private IEnumerable expected;
  374. /// <summary>
  375. /// Construct a CollectionSubsetConstraint
  376. /// </summary>
  377. /// <param name="expected">The collection that the actual value is expected to be a subset of</param>
  378. public CollectionSubsetConstraint(IEnumerable expected) : base(expected)
  379. {
  380. this.expected = expected;
  381. this.DisplayName = "subsetof";
  382. }
  383. /// <summary>
  384. /// Test whether the actual collection is a subset of
  385. /// the expected collection provided.
  386. /// </summary>
  387. /// <param name="actual"></param>
  388. /// <returns></returns>
  389. protected override bool doMatch(IEnumerable actual)
  390. {
  391. return Tally(expected).TryRemove( actual );
  392. }
  393. /// <summary>
  394. /// Write a description of this constraint to a MessageWriter
  395. /// </summary>
  396. /// <param name="writer"></param>
  397. public override void WriteDescriptionTo(MessageWriter writer)
  398. {
  399. writer.WritePredicate( "subset of" );
  400. writer.WriteExpectedValue(expected);
  401. }
  402. }
  403. #endregion
  404. #region CollectionOrderedConstraint
  405. /// <summary>
  406. /// CollectionOrderedConstraint is used to test whether a collection is ordered.
  407. /// </summary>
  408. public class CollectionOrderedConstraint : CollectionConstraint
  409. {
  410. private ComparisonAdapter comparer = ComparisonAdapter.Default;
  411. private string comparerName;
  412. private string propertyName;
  413. private bool descending;
  414. /// <summary>
  415. /// Construct a CollectionOrderedConstraint
  416. /// </summary>
  417. public CollectionOrderedConstraint()
  418. {
  419. this.DisplayName = "ordered";
  420. }
  421. ///<summary>
  422. /// If used performs a reverse comparison
  423. ///</summary>
  424. public CollectionOrderedConstraint Descending
  425. {
  426. get
  427. {
  428. descending = true;
  429. return this;
  430. }
  431. }
  432. /// <summary>
  433. /// Modifies the constraint to use an IComparer and returns self.
  434. /// </summary>
  435. public CollectionOrderedConstraint Using(IComparer comparer)
  436. {
  437. this.comparer = ComparisonAdapter.For( comparer );
  438. this.comparerName = comparer.GetType().FullName;
  439. return this;
  440. }
  441. #if NET_2_0
  442. /// <summary>
  443. /// Modifies the constraint to use an IComparer&lt;T&gt; and returns self.
  444. /// </summary>
  445. public CollectionOrderedConstraint Using<T>(IComparer<T> comparer)
  446. {
  447. this.comparer = ComparisonAdapter.For(comparer);
  448. this.comparerName = comparer.GetType().FullName;
  449. return this;
  450. }
  451. /// <summary>
  452. /// Modifies the constraint to use a Comparison&lt;T&gt; and returns self.
  453. /// </summary>
  454. public CollectionOrderedConstraint Using<T>(Comparison<T> comparer)
  455. {
  456. this.comparer = ComparisonAdapter.For(comparer);
  457. this.comparerName = comparer.GetType().FullName;
  458. return this;
  459. }
  460. #endif
  461. /// <summary>
  462. /// Modifies the constraint to test ordering by the value of
  463. /// a specified property and returns self.
  464. /// </summary>
  465. public CollectionOrderedConstraint By(string propertyName)
  466. {
  467. this.propertyName = propertyName;
  468. return this;
  469. }
  470. /// <summary>
  471. /// Test whether the collection is ordered
  472. /// </summary>
  473. /// <param name="actual"></param>
  474. /// <returns></returns>
  475. protected override bool doMatch(IEnumerable actual)
  476. {
  477. object previous = null;
  478. int index = 0;
  479. foreach(object obj in actual)
  480. {
  481. object objToCompare = obj;
  482. if (obj == null)
  483. throw new ArgumentNullException("actual", "Null value at index " + index.ToString());
  484. if (this.propertyName != null)
  485. {
  486. PropertyInfo prop = obj.GetType().GetProperty(propertyName);
  487. objToCompare = prop.GetValue(obj, null);
  488. if (objToCompare == null)
  489. throw new ArgumentNullException("actual", "Null property value at index " + index.ToString());
  490. }
  491. if (previous != null)
  492. {
  493. //int comparisonResult = comparer.Compare(al[i], al[i + 1]);
  494. int comparisonResult = comparer.Compare(previous, objToCompare);
  495. if (descending && comparisonResult < 0)
  496. return false;
  497. if (!descending && comparisonResult > 0)
  498. return false;
  499. }
  500. previous = objToCompare;
  501. index++;
  502. }
  503. return true;
  504. }
  505. /// <summary>
  506. /// Write a description of the constraint to a MessageWriter
  507. /// </summary>
  508. /// <param name="writer"></param>
  509. public override void WriteDescriptionTo(MessageWriter writer)
  510. {
  511. if (propertyName == null)
  512. writer.Write("collection ordered");
  513. else
  514. {
  515. writer.WritePredicate("collection ordered by");
  516. writer.WriteExpectedValue(propertyName);
  517. }
  518. if (descending)
  519. writer.WriteModifier("descending");
  520. }
  521. /// <summary>
  522. /// Returns the string representation of the constraint.
  523. /// </summary>
  524. /// <returns></returns>
  525. public override string ToString()
  526. {
  527. StringBuilder sb = new StringBuilder("<ordered");
  528. if (propertyName != null)
  529. sb.Append("by " + propertyName);
  530. if (descending)
  531. sb.Append(" descending");
  532. if (comparerName != null)
  533. sb.Append(" " + comparerName);
  534. sb.Append(">");
  535. return sb.ToString();
  536. }
  537. }
  538. #endregion
  539. }