PageRenderTime 45ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/External.LCA_RESTRICTED/Languages/Ruby/ruby19/lib/ruby/gems/1.9.1/gems/flexmock-0.8.7/lib/flexmock/expectation.rb

http://github.com/IronLanguages/main
Ruby | 476 lines | 226 code | 52 blank | 198 comment | 15 complexity | 5dafdb54db4246692b33596fbc60442b MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. #!/usr/bin/env ruby
  2. #---
  3. # Copyright 2003, 2004, 2005, 2006, 2007 by Jim Weirich (jim@weirichhouse.org).
  4. # All rights reserved.
  5. # Permission is granted for use, copying, modification, distribution,
  6. # and distribution of modified versions of this work as long as the
  7. # above copyright notice is included.
  8. #+++
  9. require 'flexmock/noop'
  10. class FlexMock
  11. ####################################################################
  12. # An Expectation is returned from each +should_receive+ message sent
  13. # to mock object. Each expectation records how a message matching
  14. # the message name (argument to +should_receive+) and the argument
  15. # list (given by +with+) should behave. Mock expectations can be
  16. # recorded by chaining the declaration methods defined in this
  17. # class.
  18. #
  19. # For example:
  20. #
  21. # mock.should_receive(:meth).with(args).and_returns(result)
  22. #
  23. class Expectation
  24. attr_reader :expected_args, :order_number
  25. attr_accessor :mock
  26. # Create an expectation for a method named +sym+.
  27. def initialize(mock, sym)
  28. @mock = mock
  29. @sym = sym
  30. @expected_args = nil
  31. @count_validators = []
  32. @count_validator_class = ExactCountValidator
  33. @actual_count = 0
  34. @return_value = nil
  35. @return_queue = []
  36. @yield_queue = []
  37. @order_number = nil
  38. @global_order_number = nil
  39. @globally = nil
  40. end
  41. def to_s
  42. FlexMock.format_args(@sym, @expected_args)
  43. end
  44. # Verify the current call with the given arguments matches the
  45. # expectations recorded in this object.
  46. def verify_call(*args)
  47. validate_order
  48. @actual_count += 1
  49. perform_yielding(args)
  50. return_value(args)
  51. end
  52. # Public return value (odd name to avoid accidental use as a
  53. # constraint).
  54. def _return_value(args) # :nodoc:
  55. return_value(args)
  56. end
  57. # Find the return value for this expectation. (private version)
  58. def return_value(args)
  59. case @return_queue.size
  60. when 0
  61. block = lambda { |*args| @return_value }
  62. when 1
  63. block = @return_queue.first
  64. else
  65. block = @return_queue.shift
  66. end
  67. block.call(*args)
  68. end
  69. private :return_value
  70. # Yield stored values to any blocks given.
  71. def perform_yielding(args)
  72. @return_value = nil
  73. unless @yield_queue.empty?
  74. block = args.last
  75. values = (@yield_queue.size == 1) ? @yield_queue.first : @yield_queue.shift
  76. if block && block.respond_to?(:call)
  77. @return_value = block.call(*values)
  78. else
  79. fail MockError, "No Block given to mock with 'and_yield' expectation"
  80. end
  81. end
  82. end
  83. private :perform_yielding
  84. # Is this expectation eligible to be called again? It is eligible
  85. # only if all of its count validators agree that it is eligible.
  86. def eligible?
  87. @count_validators.all? { |v| v.eligible?(@actual_count) }
  88. end
  89. # Is this expectation constrained by any call counts?
  90. def call_count_constrained?
  91. ! @count_validators.empty?
  92. end
  93. # Validate that the order
  94. def validate_order
  95. if @order_number
  96. @mock.flexmock_validate_order(to_s, @order_number)
  97. end
  98. if @global_order_number
  99. @mock.flexmock_container.flexmock_validate_order(to_s, @global_order_number)
  100. end
  101. end
  102. private :validate_order
  103. # Validate the correct number of calls have been made. Called by
  104. # the teardown process.
  105. def flexmock_verify
  106. @count_validators.each do |v|
  107. v.validate(@actual_count)
  108. end
  109. end
  110. # Does the argument list match this expectation's argument
  111. # specification.
  112. def match_args(args)
  113. # TODO: Rethink this:
  114. # return false if @expected_args.nil?
  115. return true if @expected_args.nil?
  116. return false if args.size != @expected_args.size
  117. (0...args.size).all? { |i| match_arg(@expected_args[i], args[i]) }
  118. end
  119. # Does the expected argument match the corresponding actual value.
  120. def match_arg(expected, actual)
  121. expected === actual ||
  122. expected == actual ||
  123. ( Regexp === expected && expected === actual.to_s )
  124. end
  125. # Declare that the method should expect the given argument list.
  126. def with(*args)
  127. @expected_args = args
  128. self
  129. end
  130. # Declare that the method should be called with no arguments.
  131. def with_no_args
  132. with
  133. end
  134. # Declare that the method can be called with any number of
  135. # arguments of any type.
  136. def with_any_args
  137. @expected_args = nil
  138. self
  139. end
  140. # :call-seq:
  141. # and_return(value)
  142. # and_return(value, value, ...)
  143. # and_return { |*args| code }
  144. #
  145. # Declare that the method returns a particular value (when the
  146. # argument list is matched).
  147. #
  148. # * If a single value is given, it will be returned for all matching
  149. # calls.
  150. # * If multiple values are given, each value will be returned in turn for
  151. # each successive call. If the number of matching calls is greater
  152. # than the number of values, the last value will be returned for
  153. # the extra matching calls.
  154. # * If a block is given, it is evaluated on each call and its
  155. # value is returned.
  156. #
  157. # For example:
  158. #
  159. # mock.should_receive(:f).returns(12) # returns 12
  160. #
  161. # mock.should_receive(:f).with(String). # returns an
  162. # returns { |str| str.upcase } # upcased string
  163. #
  164. # +returns+ is an alias for +and_return+.
  165. #
  166. def and_return(*args, &block)
  167. if block_given?
  168. @return_queue << block
  169. else
  170. args.each do |arg|
  171. @return_queue << lambda { |*a| arg }
  172. end
  173. end
  174. self
  175. end
  176. alias :returns :and_return # :nodoc:
  177. # Declare that the method returns and undefined object
  178. # (FlexMock.undefined). Since the undefined object will always
  179. # return itself for any message sent to it, it is a good "I don't
  180. # care" value to return for methods that are commonly used in
  181. # method chains.
  182. #
  183. # For example, if m.foo returns the undefined object, then:
  184. #
  185. # m.foo.bar.baz
  186. #
  187. # returns the undefined object without throwing an exception.
  188. #
  189. def and_return_undefined
  190. and_return(FlexMock.undefined)
  191. end
  192. alias :returns_undefined :and_return_undefined
  193. # :call-seq:
  194. # and_yield(value1, value2, ...)
  195. #
  196. # Declare that the mocked method is expected to be given a block
  197. # and that the block will be called with the values supplied to
  198. # yield. If the mock is called multiple times, mulitple
  199. # <tt>and_yield</tt> declarations can be used to supply different
  200. # values on each call.
  201. #
  202. # An error is raised if the mocked method is not called with a
  203. # block.
  204. def and_yield(*yield_values)
  205. @yield_queue << yield_values
  206. end
  207. alias :yields :and_yield
  208. # :call-seq:
  209. # and_raise(an_exception)
  210. # and_raise(SomeException)
  211. # and_raise(SomeException, args, ...)
  212. #
  213. # Declares that the method will raise the given exception (with
  214. # an optional message) when executed.
  215. #
  216. # * If an exception instance is given, then that instance will be
  217. # raised.
  218. #
  219. # * If an exception class is given, the exception raised with be
  220. # an instance of that class constructed with +new+. Any
  221. # additional arguments in the argument list will be passed to
  222. # the +new+ constructor when it is invoked.
  223. #
  224. # +raises+ is an alias for +and_raise+.
  225. #
  226. def and_raise(exception, *args)
  227. and_return { raise exception, *args }
  228. end
  229. alias :raises :and_raise
  230. # :call-seq:
  231. # and_throw(a_symbol)
  232. # and_throw(a_symbol, value)
  233. #
  234. # Declares that the method will throw the given symbol (with an
  235. # optional value) when executed.
  236. #
  237. # +throws+ is an alias for +and_throw+.
  238. #
  239. def and_throw(sym, value=nil)
  240. and_return { throw sym, value }
  241. end
  242. alias :throws :and_throw
  243. # Declare that the method may be called any number of times.
  244. def zero_or_more_times
  245. at_least.never
  246. end
  247. # Declare that the method is called +limit+ times with the
  248. # declared argument list. This may be modified by the +at_least+
  249. # and +at_most+ declarators.
  250. def times(limit)
  251. @count_validators << @count_validator_class.new(self, limit) unless limit.nil?
  252. @count_validator_class = ExactCountValidator
  253. self
  254. end
  255. # Declare that the method is never expected to be called with the
  256. # given argument list. This may be modified by the +at_least+ and
  257. # +at_most+ declarators.
  258. def never
  259. times(0)
  260. end
  261. # Declare that the method is expected to be called exactly once
  262. # with the given argument list. This may be modified by the
  263. # +at_least+ and +at_most+ declarators.
  264. def once
  265. times(1)
  266. end
  267. # Declare that the method is expected to be called exactly twice
  268. # with the given argument list. This may be modified by the
  269. # +at_least+ and +at_most+ declarators.
  270. def twice
  271. times(2)
  272. end
  273. # Modifies the next call count declarator (+times+, +never+,
  274. # +once+ or +twice+) so that the declarator means the method is
  275. # called at least that many times.
  276. #
  277. # E.g. method f must be called at least twice:
  278. #
  279. # mock.should_receive(:f).at_least.twice
  280. #
  281. def at_least
  282. @count_validator_class = AtLeastCountValidator
  283. self
  284. end
  285. # Modifies the next call count declarator (+times+, +never+,
  286. # +once+ or +twice+) so that the declarator means the method is
  287. # called at most that many times.
  288. #
  289. # E.g. method f must be called no more than twice
  290. #
  291. # mock.should_receive(:f).at_most.twice
  292. #
  293. def at_most
  294. @count_validator_class = AtMostCountValidator
  295. self
  296. end
  297. # Declare that the given method must be called in order. All
  298. # ordered method calls must be received in the order specified by
  299. # the ordering of the +should_receive+ messages. Receiving a
  300. # methods out of the specified order will cause a test failure.
  301. #
  302. # If the user needs more fine control over ordering
  303. # (e.g. specifying that a group of messages may be received in any
  304. # order as long as they all come after another group of messages),
  305. # a _group_ _name_ may be specified in the +ordered+ calls. All
  306. # messages within the same group may be received in any order.
  307. #
  308. # For example, in the following, messages +flip+ and +flop+ may be
  309. # received in any order (because they are in the same group), but
  310. # must occur strictly after +start+ but before +end+. The message
  311. # +any_time+ may be received at any time because it is not
  312. # ordered.
  313. #
  314. # m = FlexMock.new
  315. # m.should_receive(:any_time)
  316. # m.should_receive(:start).ordered
  317. # m.should_receive(:flip).ordered(:flip_flop_group)
  318. # m.should_receive(:flop).ordered(:flip_flop_group)
  319. # m.should_receive(:end).ordered
  320. #
  321. def ordered(group_name=nil)
  322. if @globally
  323. @global_order_number = define_ordered(group_name, @mock.flexmock_container)
  324. else
  325. @order_number = define_ordered(group_name, @mock)
  326. end
  327. @globally = false
  328. self
  329. end
  330. # Modifier that changes the next ordered constraint to apply
  331. # globally across all mock objects in the container.
  332. def globally
  333. @globally = true
  334. self
  335. end
  336. # Helper method for defining ordered expectations.
  337. def define_ordered(group_name, ordering)
  338. fail UsageError, "Mock #{@mock.flexmock_name} is not in a container and cannot be globally ordered." if ordering.nil?
  339. if group_name.nil?
  340. result = ordering.flexmock_allocate_order
  341. elsif (num = ordering.flexmock_groups[group_name])
  342. result = num
  343. else
  344. result = ordering.flexmock_allocate_order
  345. ordering.flexmock_groups[group_name] = result
  346. end
  347. result
  348. end
  349. private :define_ordered
  350. def by_default
  351. expectations = mock.flexmock_expectations_for(@sym)
  352. expectations.defaultify_expectation(self) if expectations
  353. end
  354. end
  355. ##########################################################################
  356. # A composite expectation allows several expectations to be grouped into a
  357. # single composite and then apply the same constraints to all expectations
  358. # in the group.
  359. class CompositeExpectation
  360. # Initialize the composite expectation.
  361. def initialize
  362. @expectations = []
  363. end
  364. # Add an expectation to the composite.
  365. def add(expectation)
  366. @expectations << expectation
  367. end
  368. # Apply the constraint method to all expectations in the composite.
  369. def method_missing(sym, *args, &block)
  370. @expectations.each do |expectation|
  371. expectation.send(sym, *args, &block)
  372. end
  373. self
  374. end
  375. # The following methods return a value, so we make an arbitrary choice
  376. # and return the value for the first expectation in the composite.
  377. # Return the order number of the first expectation in the list.
  378. def order_number
  379. @expectations.first.order_number
  380. end
  381. # Return the associated mock object.
  382. def mock
  383. @expectations.first.mock
  384. end
  385. # Start a new method expectation. The following constraints will be
  386. # applied to the new expectation.
  387. def should_receive(*args, &block)
  388. @expectations.first.mock.should_receive(*args, &block)
  389. end
  390. # Return a string representations
  391. def to_s
  392. if @expectations.size > 1
  393. "[" + @expectations.collect { |e| e.to_s }.join(', ') + "]"
  394. else
  395. @expectations.first.to_s
  396. end
  397. end
  398. end
  399. ##########################################################################
  400. # An expectation recorder records any expectations received and plays them
  401. # back on demand. This is used to collect the expectations in the blockless
  402. # version of the new_instances call.
  403. #
  404. class ExpectationRecorder
  405. # Initialize the recorder.
  406. def initialize
  407. @expectations = []
  408. end
  409. # Save any incoming messages to be played back later.
  410. def method_missing(sym, *args, &block)
  411. @expectations << [sym, args, block]
  412. self
  413. end
  414. # Apply the recorded messages to the given object in a chaining fashion
  415. # (i.e. the result of the previous call is used as the target of the next
  416. # call).
  417. def apply(mock)
  418. obj = mock
  419. @expectations.each do |sym, args, block|
  420. obj = obj.send(sym, *args, &block)
  421. end
  422. end
  423. end
  424. end