lib/amqp-spec/rspec.rb in amqp-spec-0.2.1 vs lib/amqp-spec/rspec.rb in amqp-spec-0.2.3
- old
+ new
@@ -1,97 +1,85 @@
require 'fiber' unless Fiber.respond_to?(:current)
-require 'mq'
+require 'amqp-spec/amqp'
# You can include one of the following modules into your example groups:
-# AMQP::SpecHelper
-# AMQP::Spec
+# AMQP::SpecHelper,
+# AMQP::Spec,
+# AMQP::EMSpec.
#
-# AMQP::SpecHelper module defines 'ampq' method that can be safely used inside your specs(examples)
+# AMQP::SpecHelper module defines #ampq method that can be safely used inside your specs(examples)
# to test expectations inside running AMQP.start loop. Each loop is running in a separate Fiber,
-# and you can control for timeouts using either :spec_timeout option given to amqp method,
-# or setting default timeout with class method default_timeout(timeout).
+# and you can control for timeouts using either :spec_timeout option given to #amqp method,
+# or setting default timeout with class method default_timeout(timeout). In addition to #amqp
+# method, you can use #em method - it creates plain EM.run loop without starting AMQP.
#
# If you include AMQP::Spec module into your example group, each example of this group will run
# inside AMQP.start loop without the need to explicitly call 'amqp'. In order to provide options
# to AMQP loop, default_options class method is defined. Remember, when using AMQP::Specs, you
# will have a single set of AMQP.start options for all your examples.
#
-# In order to stop AMQP loop, you should call 'done' AFTER you are sure that your example is finished.
+# Including AMQP::EMSpec module into your example group, each example of this group will run
+# inside EM.run loop without the need to explicitly call 'em'.
+#
+# In order to stop AMQP/EM loop, you should call 'done' AFTER you are sure that your example is finished.
# For example, if you are using subscribe block that tests expectations on messages, 'done' should be
# probably called at the end of this block.
#
-# TODO: Define 'async' method wrapping async requests and returning results... 'async_loop' too for subscribe block?
-# TODO: 'evented_before', 'evented_after' that will be run inside EM before the example
module AMQP
-
- # Initializes new AMQP client/connection without starting another EM loop
- def self.start_connection(opts={}, &block)
-# puts "!!!!!!!!! Existing connection: #{@conn}" if @conn
- @conn = connect opts
- @conn.callback(&block) if block
- end
-
- # Closes AMQP connection and raises optional exception AFTER the AMQP connection is 100% closed
- def self.stop_connection
- if AMQP.conn and not AMQP.closing
-# MQ.reset ?
- @closing = true
- @conn.close {
- yield if block_given?
- cleanup_state
- }
- end
- end
-
- def self.cleanup_state
-# MQ.reset ?
- Thread.current[:mq] = nil
- Thread.current[:mq_id] = nil
- @conn = nil
- @closing = false
- end
-
+ # AMQP::SpecHelper module defines #ampq method that can be safely used inside your specs(examples)
+ # to test expectations inside running AMQP.start loop. Each loop is running in a separate Fiber,
+ # and you can control for timeouts using either :spec_timeout option given to #amqp method,
+ # or setting default timeout with class method default_timeout(timeout). In addition to #amqp
+ # method, you can use #em method - it creates plain EM.run loop without starting AMQP.
+ #
+ # TODO: Define 'async' method wrapping async requests and returning results... 'async_loop' too for subscribe block?
+ # TODO: 'evented_before', 'evented_after' that will be run inside EM before the example
+ #
+ # noinspection RubyArgCount
module SpecHelper
SpecTimeoutExceededError = Class.new(RuntimeError)
- def self.included(example_group)
+ # Class methods (macros) for example group that includes SpecHelper
+ #
+ module GroupMethods
+ unless respond_to?(:metadata)
+ # Hacking in metadata into RSpec1 to imitate Rspec2's metadata.
+ # You can add to metadata Hash to pass options into examples and
+ # nested groups.
+ #
+ def metadata
+ @metadata ||= superclass.metadata.dup rescue {}
+ end
+ end
- extended_class = defined?(RSpec) ? example_group : ::Spec::Example::ExampleGroup
- unless extended_class.respond_to? :default_timeout
- extended_class.instance_exec do
- if defined?(RSpec)
- metadata[:em_default_options] = {}
- metadata[:em_default_timeout] = nil
+ # Sets/retrieves default timeout for running evented specs for this
+ # example group and its nested groups.
+ #
+ def default_timeout(spec_timeout=nil)
+ metadata[:em_default_timeout] = spec_timeout if spec_timeout
+ metadata[:em_default_timeout]
+ end
- def self.default_timeout(spec_timeout=nil)
- metadata[:em_default_timeout] = spec_timeout if spec_timeout
- metadata[:em_default_timeout]
- end
+ # Sets/retrieves default AMQP.start options for this example group
+ # and its nested groups.
+ #
+ def default_options(opts=nil)
+ metadata[:em_default_options] = opts if opts
+ metadata[:em_default_options]
+ end
+ end
- def self.default_options(opts=nil)
- metadata[:em_default_options] = opts if opts
- metadata[:em_default_options]
- end
- else
- @@_em_default_options = {}
- @@_em_default_timeout = nil
-
- def self.default_timeout(spec_timeout=nil)
- @@_em_default_timeout = spec_timeout if spec_timeout
- @@_em_default_timeout
- end
-
- def self.default_options(opts=nil)
- @@_em_default_options = opts if opts
- @@_em_default_options
- end
- end
- end
+ def self.included(example_group)
+ unless example_group.respond_to? :default_timeout
+ example_group.extend(GroupMethods)
+ example_group.metadata[:em_default_options] = {}
+ example_group.metadata[:em_default_timeout] = nil
end
end
+
# Yields to given block inside EM.run and AMQP.start loops. This method takes any option that is
# also accepted by EventMachine::connect. Also, options for AMQP.start include:
# * :user => String (default ‘guest’) - The username as defined by the AMQP server.
# * :pass => String (default ‘guest’) - The password for the associated :user as defined by the AMQP server.
# * :vhost => String (default ’/’) - The virtual host as defined by the AMQP server.
@@ -105,41 +93,40 @@
opts = self.class.default_options.merge opts
begin
EM.run do
@_em_spec_with_amqp = true
@_em_spec_exception = nil
- spec_timeout = opts.delete(:spec_timeout) || self.class.default_timeout
+ spec_timeout = opts.delete(:spec_timeout) || self.class.default_timeout
timeout(spec_timeout) if spec_timeout
- @_em_spec_fiber = Fiber.new do
+ @_em_spec_fiber = Fiber.new do
begin
AMQP.start_connection opts, &block
rescue Exception => @_em_spec_exception
-# p "inner", @_em_spec_exception
done
end
Fiber.yield
end
@_em_spec_fiber.resume
end
rescue Exception => outer_spec_exception
-# p "outer", outer_spec_exception unless outer_spec_exception.is_a? SpecTimeoutExceededError
# Make sure AMQP state is cleaned even after Rspec failures
AMQP.cleanup_state
raise outer_spec_exception
end
end
# Yields to block inside EM loop, :spec_timeout option (in seconds) is used to force spec to timeout
# if something goes wrong and EM/AMQP loop hangs for some reason. SpecTimeoutExceededError is raised.
+ #
def em(spec_timeout = self.class.default_timeout, &block)
spec_timeout = spec_timeout[:spec_timeout] || self.class.default_timeout if spec_timeout.is_a?(Hash)
EM.run do
@_em_spec_with_amqp = false
@_em_spec_exception = nil
timeout(spec_timeout) if spec_timeout
- @_em_spec_fiber = Fiber.new do
+ @_em_spec_fiber = Fiber.new do
begin
block.call
rescue Exception => @_em_spec_exception
done
end
@@ -148,11 +135,12 @@
@_em_spec_fiber.resume
end
end
- # Sets timeout for current spec
+ # Sets timeout for current running example
+ #
def timeout(spec_timeout)
EM.cancel_timer(@_em_timer) if @_em_timer
@_em_timer = EM.add_timer(spec_timeout) do
@_em_spec_exception = SpecTimeoutExceededError.new
done
@@ -165,10 +153,11 @@
# For amqp specs, stops AMQP and cleans up AMQP state.
#
# You may pass delay (in seconds) to done. If you do so, please keep in mind
# that your (default or explicit) spec timeout may fire before your delayed done
# callback is due, leading to SpecTimeoutExceededError
+ #
def done(delay=nil)
done_proc = proc do
yield if block_given?
EM.next_tick do
if @_em_spec_with_amqp
@@ -189,35 +178,50 @@
else
done_proc.call
end
end
+ # Retrieves metadata passed in from enclosing example groups
+ #
+ def metadata
+ @metadata ||= self.class.metadata.dup rescue {}
+ end
+
private
# Stops EM loop, executes optional block, finishes off fiber and raises exception if any
+ #
def finish_em_spec_fiber
EM.stop_event_loop if EM.reactor_running?
yield if block_given?
@_em_spec_fiber.resume if @_em_spec_fiber.alive?
raise @_em_spec_exception if @_em_spec_exception
end
end
+ # If you include AMQP::Spec module into your example group, each example of this group will run
+ # inside AMQP.start loop without the need to explicitly call 'amqp'. In order to provide options
+ # to AMQP loop, default_options class method is defined. Remember, when using AMQP::Specs, you
+ # will have a single set of AMQP.start options for all your examples.
+ #
module Spec
- def self.included(cls)
- cls.send(:include, SpecHelper)
+ def self.included(example_group)
+ example_group.send(:include, SpecHelper)
end
def instance_eval(&block)
amqp do
super(&block)
end
end
end
+ # Including AMQP::EMSpec module into your example group, each example of this group will run
+ # inside EM.run loop without the need to explicitly call 'em'.
+ #
module EMSpec
- def self.included(cls)
- cls.send(:include, SpecHelper)
+ def self.included(example_group)
+ example_group.send(:include, SpecHelper)
end
def instance_eval(&block)
em do
super(&block)