PageRenderTime 50ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/src/NUnit/framework/Constraints/ConstraintOperators.cs

#
C# | 480 lines | 228 code | 45 blank | 207 comment | 9 complexity | c30275040a2c556ee7f8a8a3f8c9b2f4 MD5 | raw file
Possible License(s): GPL-2.0
  1. // ****************************************************************
  2. // Copyright 2008, 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. #if NET_2_0
  9. using System.Collections.Generic;
  10. #endif
  11. namespace NUnit.Framework.Constraints
  12. {
  13. #region ConstraintOperator Base Class
  14. /// <summary>
  15. /// The ConstraintOperator class is used internally by a
  16. /// ConstraintBuilder to represent an operator that
  17. /// modifies or combines constraints.
  18. ///
  19. /// Constraint operators use left and right precedence
  20. /// values to determine whether the top operator on the
  21. /// stack should be reduced before pushing a new operator.
  22. /// </summary>
  23. public abstract class ConstraintOperator
  24. {
  25. private object leftContext;
  26. private object rightContext;
  27. /// <summary>
  28. /// The precedence value used when the operator
  29. /// is about to be pushed to the stack.
  30. /// </summary>
  31. protected int left_precedence;
  32. /// <summary>
  33. /// The precedence value used when the operator
  34. /// is on the top of the stack.
  35. /// </summary>
  36. protected int right_precedence;
  37. /// <summary>
  38. /// The syntax element preceding this operator
  39. /// </summary>
  40. public object LeftContext
  41. {
  42. get { return leftContext; }
  43. set { leftContext = value; }
  44. }
  45. /// <summary>
  46. /// The syntax element folowing this operator
  47. /// </summary>
  48. public object RightContext
  49. {
  50. get { return rightContext; }
  51. set { rightContext = value; }
  52. }
  53. /// <summary>
  54. /// The precedence value used when the operator
  55. /// is about to be pushed to the stack.
  56. /// </summary>
  57. public virtual int LeftPrecedence
  58. {
  59. get { return left_precedence; }
  60. }
  61. /// <summary>
  62. /// The precedence value used when the operator
  63. /// is on the top of the stack.
  64. /// </summary>
  65. public virtual int RightPrecedence
  66. {
  67. get { return right_precedence; }
  68. }
  69. /// <summary>
  70. /// Reduce produces a constraint from the operator and
  71. /// any arguments. It takes the arguments from the constraint
  72. /// stack and pushes the resulting constraint on it.
  73. /// </summary>
  74. /// <param name="stack"></param>
  75. public abstract void Reduce(ConstraintBuilder.ConstraintStack stack);
  76. }
  77. #endregion
  78. #region Prefix Operators
  79. #region PrefixOperator
  80. /// <summary>
  81. /// PrefixOperator takes a single constraint and modifies
  82. /// it's action in some way.
  83. /// </summary>
  84. public abstract class PrefixOperator : ConstraintOperator
  85. {
  86. /// <summary>
  87. /// Reduce produces a constraint from the operator and
  88. /// any arguments. It takes the arguments from the constraint
  89. /// stack and pushes the resulting constraint on it.
  90. /// </summary>
  91. /// <param name="stack"></param>
  92. public override void Reduce(ConstraintBuilder.ConstraintStack stack)
  93. {
  94. stack.Push(ApplyPrefix(stack.Pop()));
  95. }
  96. /// <summary>
  97. /// Returns the constraint created by applying this
  98. /// prefix to another constraint.
  99. /// </summary>
  100. /// <param name="constraint"></param>
  101. /// <returns></returns>
  102. public abstract Constraint ApplyPrefix(Constraint constraint);
  103. }
  104. #endregion
  105. #region NotOperator
  106. /// <summary>
  107. /// Negates the test of the constraint it wraps.
  108. /// </summary>
  109. public class NotOperator : PrefixOperator
  110. {
  111. /// <summary>
  112. /// Constructs a new NotOperator
  113. /// </summary>
  114. public NotOperator()
  115. {
  116. // Not stacks on anything and only allows other
  117. // prefix ops to stack on top of it.
  118. this.left_precedence = this.right_precedence = 1;
  119. }
  120. /// <summary>
  121. /// Returns a NotConstraint applied to its argument.
  122. /// </summary>
  123. public override Constraint ApplyPrefix(Constraint constraint)
  124. {
  125. return new NotConstraint(constraint);
  126. }
  127. }
  128. #endregion
  129. #region Collection Operators
  130. /// <summary>
  131. /// Abstract base for operators that indicate how to
  132. /// apply a constraint to items in a collection.
  133. /// </summary>
  134. public abstract class CollectionOperator : PrefixOperator
  135. {
  136. /// <summary>
  137. /// Constructs a CollectionOperator
  138. /// </summary>
  139. public CollectionOperator()
  140. {
  141. // Collection Operators stack on everything
  142. // and allow all other ops to stack on them
  143. this.left_precedence = 1;
  144. this.right_precedence = 10;
  145. }
  146. }
  147. /// <summary>
  148. /// Represents a constraint that succeeds if all the
  149. /// members of a collection match a base constraint.
  150. /// </summary>
  151. public class AllOperator : CollectionOperator
  152. {
  153. /// <summary>
  154. /// Returns a constraint that will apply the argument
  155. /// to the members of a collection, succeeding if
  156. /// they all succeed.
  157. /// </summary>
  158. public override Constraint ApplyPrefix(Constraint constraint)
  159. {
  160. return new AllItemsConstraint(constraint);
  161. }
  162. }
  163. /// <summary>
  164. /// Represents a constraint that succeeds if any of the
  165. /// members of a collection match a base constraint.
  166. /// </summary>
  167. public class SomeOperator : CollectionOperator
  168. {
  169. /// <summary>
  170. /// Returns a constraint that will apply the argument
  171. /// to the members of a collection, succeeding if
  172. /// any of them succeed.
  173. /// </summary>
  174. public override Constraint ApplyPrefix(Constraint constraint)
  175. {
  176. return new SomeItemsConstraint(constraint);
  177. }
  178. }
  179. /// <summary>
  180. /// Represents a constraint that succeeds if none of the
  181. /// members of a collection match a base constraint.
  182. /// </summary>
  183. public class NoneOperator : CollectionOperator
  184. {
  185. /// <summary>
  186. /// Returns a constraint that will apply the argument
  187. /// to the members of a collection, succeeding if
  188. /// none of them succeed.
  189. /// </summary>
  190. public override Constraint ApplyPrefix(Constraint constraint)
  191. {
  192. return new NoItemConstraint(constraint);
  193. }
  194. }
  195. #endregion
  196. #region WithOperator
  197. /// <summary>
  198. /// Represents a constraint that simply wraps the
  199. /// constraint provided as an argument, without any
  200. /// further functionality, but which modifes the
  201. /// order of evaluation because of its precedence.
  202. /// </summary>
  203. public class WithOperator : PrefixOperator
  204. {
  205. /// <summary>
  206. /// Constructor for the WithOperator
  207. /// </summary>
  208. public WithOperator()
  209. {
  210. this.left_precedence = 1;
  211. this.right_precedence = 4;
  212. }
  213. /// <summary>
  214. /// Returns a constraint that wraps its argument
  215. /// </summary>
  216. public override Constraint ApplyPrefix(Constraint constraint)
  217. {
  218. return constraint;
  219. }
  220. }
  221. #endregion
  222. #region SelfResolving Operators
  223. #region SelfResolvingOperator
  224. /// <summary>
  225. /// Abstract base class for operators that are able to reduce to a
  226. /// constraint whether or not another syntactic element follows.
  227. /// </summary>
  228. public abstract class SelfResolvingOperator : ConstraintOperator
  229. {
  230. }
  231. #endregion
  232. #region PropOperator
  233. /// <summary>
  234. /// Operator used to test for the presence of a named Property
  235. /// on an object and optionally apply further tests to the
  236. /// value of that property.
  237. /// </summary>
  238. public class PropOperator : SelfResolvingOperator
  239. {
  240. private string name;
  241. /// <summary>
  242. /// Gets the name of the property to which the operator applies
  243. /// </summary>
  244. public string Name
  245. {
  246. get { return name; }
  247. }
  248. /// <summary>
  249. /// Constructs a PropOperator for a particular named property
  250. /// </summary>
  251. public PropOperator(string name)
  252. {
  253. this.name = name;
  254. // Prop stacks on anything and allows only
  255. // prefix operators to stack on it.
  256. this.left_precedence = this.right_precedence = 1;
  257. }
  258. /// <summary>
  259. /// Reduce produces a constraint from the operator and
  260. /// any arguments. It takes the arguments from the constraint
  261. /// stack and pushes the resulting constraint on it.
  262. /// </summary>
  263. /// <param name="stack"></param>
  264. public override void Reduce(ConstraintBuilder.ConstraintStack stack)
  265. {
  266. if (RightContext == null || RightContext is BinaryOperator)
  267. stack.Push(new PropertyExistsConstraint(name));
  268. else
  269. stack.Push(new PropertyConstraint(name, stack.Pop()));
  270. }
  271. }
  272. #endregion
  273. #region AttributeOperator
  274. /// <summary>
  275. /// Operator that tests for the presence of a particular attribute
  276. /// on a type and optionally applies further tests to the attribute.
  277. /// </summary>
  278. public class AttributeOperator : SelfResolvingOperator
  279. {
  280. private Type type;
  281. /// <summary>
  282. /// Construct an AttributeOperator for a particular Type
  283. /// </summary>
  284. /// <param name="type">The Type of attribute tested</param>
  285. public AttributeOperator(Type type)
  286. {
  287. this.type = type;
  288. // Attribute stacks on anything and allows only
  289. // prefix operators to stack on it.
  290. this.left_precedence = this.right_precedence = 1;
  291. }
  292. /// <summary>
  293. /// Reduce produces a constraint from the operator and
  294. /// any arguments. It takes the arguments from the constraint
  295. /// stack and pushes the resulting constraint on it.
  296. /// </summary>
  297. public override void Reduce(ConstraintBuilder.ConstraintStack stack)
  298. {
  299. if (RightContext == null || RightContext is BinaryOperator)
  300. stack.Push(new AttributeExistsConstraint(type));
  301. else
  302. stack.Push(new AttributeConstraint(type, stack.Pop()));
  303. }
  304. }
  305. #endregion
  306. #region ThrowsOperator
  307. /// <summary>
  308. /// Operator that tests that an exception is thrown and
  309. /// optionally applies further tests to the exception.
  310. /// </summary>
  311. public class ThrowsOperator : SelfResolvingOperator
  312. {
  313. /// <summary>
  314. /// Construct a ThrowsOperator
  315. /// </summary>
  316. public ThrowsOperator()
  317. {
  318. // ThrowsOperator stacks on everything but
  319. // it's always the first item on the stack
  320. // anyway. It is evaluated last of all ops.
  321. this.left_precedence = 1;
  322. this.right_precedence = 100;
  323. }
  324. /// <summary>
  325. /// Reduce produces a constraint from the operator and
  326. /// any arguments. It takes the arguments from the constraint
  327. /// stack and pushes the resulting constraint on it.
  328. /// </summary>
  329. public override void Reduce(ConstraintBuilder.ConstraintStack stack)
  330. {
  331. if (RightContext == null || RightContext is BinaryOperator)
  332. stack.Push(new ThrowsConstraint(null));
  333. else
  334. stack.Push(new ThrowsConstraint(stack.Pop()));
  335. }
  336. }
  337. #endregion
  338. #endregion
  339. #endregion
  340. #region Binary Operators
  341. #region BinaryOperator
  342. /// <summary>
  343. /// Abstract base class for all binary operators
  344. /// </summary>
  345. public abstract class BinaryOperator : ConstraintOperator
  346. {
  347. /// <summary>
  348. /// Reduce produces a constraint from the operator and
  349. /// any arguments. It takes the arguments from the constraint
  350. /// stack and pushes the resulting constraint on it.
  351. /// </summary>
  352. /// <param name="stack"></param>
  353. public override void Reduce(ConstraintBuilder.ConstraintStack stack)
  354. {
  355. Constraint right = stack.Pop();
  356. Constraint left = stack.Pop();
  357. stack.Push(ApplyOperator(left, right));
  358. }
  359. /// <summary>
  360. /// Gets the left precedence of the operator
  361. /// </summary>
  362. public override int LeftPrecedence
  363. {
  364. get
  365. {
  366. return RightContext is CollectionOperator
  367. ? base.LeftPrecedence + 10
  368. : base.LeftPrecedence;
  369. }
  370. }
  371. /// <summary>
  372. /// Gets the right precedence of the operator
  373. /// </summary>
  374. public override int RightPrecedence
  375. {
  376. get
  377. {
  378. return RightContext is CollectionOperator
  379. ? base.RightPrecedence + 10
  380. : base.RightPrecedence;
  381. }
  382. }
  383. /// <summary>
  384. /// Abstract method that produces a constraint by applying
  385. /// the operator to its left and right constraint arguments.
  386. /// </summary>
  387. public abstract Constraint ApplyOperator(Constraint left, Constraint right);
  388. }
  389. #endregion
  390. #region AndOperator
  391. /// <summary>
  392. /// Operator that requires both it's arguments to succeed
  393. /// </summary>
  394. public class AndOperator : BinaryOperator
  395. {
  396. /// <summary>
  397. /// Construct an AndOperator
  398. /// </summary>
  399. public AndOperator()
  400. {
  401. this.left_precedence = this.right_precedence = 2;
  402. }
  403. /// <summary>
  404. /// Apply the operator to produce an AndConstraint
  405. /// </summary>
  406. public override Constraint ApplyOperator(Constraint left, Constraint right)
  407. {
  408. return new AndConstraint(left, right);
  409. }
  410. }
  411. #endregion
  412. #region OrOperator
  413. /// <summary>
  414. /// Operator that requires at least one of it's arguments to succeed
  415. /// </summary>
  416. public class OrOperator : BinaryOperator
  417. {
  418. /// <summary>
  419. /// Construct an OrOperator
  420. /// </summary>
  421. public OrOperator()
  422. {
  423. this.left_precedence = this.right_precedence = 3;
  424. }
  425. /// <summary>
  426. /// Apply the operator to produce an OrConstraint
  427. /// </summary>
  428. public override Constraint ApplyOperator(Constraint left, Constraint right)
  429. {
  430. return new OrConstraint(left, right);
  431. }
  432. }
  433. #endregion
  434. #endregion
  435. }