amqp-spec 0.2.7 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY CHANGED
@@ -10,10 +10,6 @@
10
10
 
11
11
  * Minimal functionality implemented
12
12
 
13
- == 0.0.3 / 2010-10-13
14
-
15
- * Bunch of non-working specs added
16
-
17
13
  == 0.0.4 / 2010-10-14
18
14
 
19
15
  * Problems with default_options resolved
@@ -30,22 +26,10 @@
30
26
 
31
27
  * Make sure Thread.current[:mq] state is cleaned after each example
32
28
 
33
- == 0.1.4 / 2010-10-15
34
-
35
- * Problematic tests all fixed
36
-
37
29
  == 0.1.7 / 2010-10-15
38
30
 
39
31
  * em interface improved for timeout arg
40
32
 
41
- == 0.1.8 / 2010-10-15
42
-
43
- * Add AMQP as a dependency
44
-
45
- == 0.1.9 / 2010-10-15
46
-
47
- * SpecHelper class methods refactored
48
-
49
33
  == 0.1.11 / 2010-10-18
50
34
 
51
35
  * Optional delay added to done
@@ -54,30 +38,22 @@
54
38
 
55
39
  * Rspec 2 support added
56
40
 
57
- == 0.2.1 / 2010-10-28
58
-
59
- * RSpec2-specific examples added
60
-
61
41
  == 0.2.2 / 2010-10-31
62
42
 
63
43
  * Metadata in Rspec 1
64
44
 
65
- == 0.2.3 / 2010-10-31
66
-
67
- * Documentation cleaned up
68
-
69
- == 0.2.4 / 2010-11-01
45
+ == 0.2.7 / 2010-11-04
70
46
 
71
- * Release
47
+ * Make AMQP.cleanup_state more thorough
72
48
 
73
- == 0.2.5 / 2010-11-04
49
+ == 0.2.8 / 2010-11-15
74
50
 
75
- * Minor Gemfile update
51
+ * syncronize method added for wrapping async calls
76
52
 
77
- == 0.2.6 / 2010-11-04
53
+ == 0.2.9 / 2010-11-16
78
54
 
79
- * Fixed subtly failing specs
55
+ * Fallback to a separate default timeout
80
56
 
81
- == 0.2.7 / 2010-11-04
57
+ == 0.3.0 / 2010-11-16
82
58
 
83
- * Make AMQP.cleanup_state more thorough
59
+ * Hooks em_before/em_after implemented
data/README.rdoc CHANGED
@@ -8,52 +8,56 @@ Simple API for writing asynchronous EventMachine/AMQP specs. Supports RSpec and
8
8
 
9
9
  == Description
10
10
 
11
- EventMachine-based code, including synchronous {AMQP library}[http://github.com/tmm1/amqp] is
12
- notoriously difficult to test. To the point that many people recommend using either
13
- Mocks[http://github.com/danielsdeleo/moqueue] or {synchronous libraries}[http://github.com/celldee/bunny]
14
- instead of EM-based libraries in unit tests. This is not always an option, however - sometimes your code
15
- is supposed to run inside event loop, and you want to test a real thing, not mocks.
16
-
17
- EM-Spec[http://github.com/tmm1/em-spec] gem made it easier to write evented specs, but it has several drawbacks.
18
- First, it is not easy to manage both EM.run and AMQP.start loops at the same time. Second, AMQP is not
19
- properly stopped and deactivated upon exceptions and timeouts, resulting in AMQP library state leak
20
- between examples and multiple mystereous failures.
21
-
22
- AMQP-Spec is built upon EM-Spec code but makes it easier to test AMQP event loops specifically. API is
23
- very similar to EM-Spec, only a bit extended. The final goal is to make writing AMQP specs reasonably
24
- pleasant experience and dispel the notion that evented AMQP-based libs are impossible to unit-test.
25
-
26
- Mind you, you still have to properly manage your AMQP broker in order to prevent broker state from leaking
27
- between examples. You can try to combine AMQP-Spec and Moqueue[http://github.com/danielsdeleo/moqueue]
28
- if you want to abstract away actual broker interactions, but still specify some event-based expectations.
11
+ EventMachine-based code, including synchronous {AMQP library}[http://github.com/tmm1/amqp]
12
+ is notoriously difficult to test. To the point that many people recommend using either
13
+ Mocks[http://github.com/danielsdeleo/moqueue] or {synchronous}[http://github.com/celldee/bunny]
14
+ libraries instead of EM-based libraries in unit tests. This is not always an option, however -
15
+ sometimes your code just has to run inside the event loop, and you want to test a real thing,
16
+ not mocks.
17
+
18
+ EM-Spec[http://github.com/tmm1/em-spec] gem made it easier to write evented specs, but it
19
+ has several drawbacks. First, it is not easy to manage both EM.run and AMQP.start loops
20
+ at the same time. Second, AMQP is not properly stopped and deactivated upon exceptions and
21
+ timeouts, resulting in state leak between examples and multiple mystereous failures.
22
+
23
+ AMQP-Spec is based on EM-Spec code but makes it easier to test AMQP event loops specifically.
24
+ API is very similar to EM-Spec's, only a bit extended. The final goal is to make writing AMQP
25
+ specs reasonably pleasant experience and dispel the notion that evented AMQP-based libs are
26
+ impossible to unit-test.
27
+
28
+ Mind you, you still have to properly manage your AMQP broker in order to prevent broker
29
+ state from leaking between examples. You can try to combine AMQP-Spec and
30
+ Moqueue[http://github.com/danielsdeleo/moqueue] if you want to abstract away actual broker
31
+ interactions, but still specify some event-based expectations.
29
32
 
30
33
  ==Rspec
31
34
 
32
- There are several ways to use amqp-spec. To use it as a helper, include AMQP::SpecHelper in your describe block.
33
- You then use either 'amqp' or 'em' methods to wrap your evented test code. Inside the amqp/em block, you must call
34
- #done after your expectations. Everything works normally otherwise. You can use default_timeout and default_options
35
- macros to avoid manually setting AMQP options in each example. However, if you DO manually set options inside
36
- the example, they override the defaults.
35
+ There are several ways to use amqp-spec. To use it as a helper, include AMQP::SpecHelper
36
+ in your describe block. You then use either 'amqp' or 'em' methods to wrap your evented
37
+ test code. Inside the amqp/em block, you must call #done after your expectations. Everything
38
+ works normally otherwise. You can set default_timeout and default_options to avoid manually
39
+ setting AMQP options for each example. However, if you DO supply options to #amqp method
40
+ inside the example, they override the defaults.
37
41
 
38
- Default options and default timeout are local for each example group and inherited by its nested groups,
39
- different example groups can have separate defaults. Please note that this is different from em-spec where
40
- default_timeout is effectively a global setting.
42
+ Default options and default timeout are local for each example group and inherited by
43
+ its nested groups, unconnected example groups DO NOT share defaults. Please note that
44
+ this is different from EM-Spec where default_timeout is effectively a global setting.
45
+
46
+ In order to setup/teardown EM state before/after your examples, you'll need to use
47
+ *em_before{}* and *em_after{}* hooks. These hooks are similar to standard RSpec's
48
+ *before/after* hooks but run inside the EM event loop before/after your example block.
41
49
 
42
50
  require "amqp-spec/rspec"
43
51
 
44
52
  describe AMQP do
45
53
  include AMQP::SpecHelper
46
54
 
47
- default_options :host => 'my.amqp.broker.org', :port => '21118'
55
+ default_options = {:host => 'my.amqp.broker.org', :port => '21118'}
48
56
  # Can be used to set default options for your amqp{} event loops
49
57
 
50
- default_timeout 1
58
+ default_timeout = 1
51
59
  # Can be used to set default :spec_timeout for your evented specs
52
60
 
53
- before(:each) do
54
- puts EM.reactor_running?
55
- end
56
-
57
61
  it "works normally when not using #amqp or #em" do
58
62
  1.should == 1
59
63
  end
@@ -78,25 +82,31 @@ default_timeout is effectively a global setting.
78
82
 
79
83
  it "optionally raises timeout exception if your loop hangs for some reason" do
80
84
  proc {
81
- amqp(:spec_timeout => 3){}
85
+ amqp(:spec_timeout => 0.5){}
82
86
  }.should raise_error SpecTimeoutExceededError
83
87
  end
84
88
 
85
89
  end
86
90
 
87
- Another option is to include AMQP::Spec in your describe block. This will patch Rspec so that all of your
88
- examples run inside an amqp block automatically. A word of caution about before{} and after{} hooks in your example
89
- groups including AMQP::Spec. Each of these hooks will run in its separate EM loop that you'll need to shut down
90
- either manually (done) or via default_timeout. Essentially, this means that any EM-related state that you'd like
91
- to setup/teardown using these hooks will be lost as each example will run in a separate EM loop. In order to
92
- run setup/teardown hooks inside the EM loop, you'll need to use before_amqp{} and after_amqp{} hooks that run
93
- inside the EM loop but before/after AMQP loop (these hooks are currently not implemented)
91
+ Another option is to include AMQP::Spec in your describe block. This will patch Rspec so
92
+ that all of your examples run inside an amqp block automatically. A word of caution about
93
+ *before{}*/*after{}* hooks in your example groups including AMQP::Spec. Each of these hooks
94
+ will run in its separate EM loop that you'll need to shut down either manually (done) or
95
+ via timeout. Essentially, this means that any EM-related state that you'd like to set up or
96
+ tear down using these hooks will be lost as example itself will run in a different EM loop.
97
+
98
+ In short, you should avoid using *before/after* if you include AMQP::Spec - instead, use
99
+ *em_before/em_after* hooks that run inside the EM event loop. One more note: you don't need
100
+ to call 'done' inside *em_before/em_after*, otherwise it'll shut down the reactor.
101
+
94
102
 
95
103
  describe AMQP do
96
104
  include AMQP::Spec
97
105
 
98
- default_options :host => 'my.amqp.broker.org', :port => '21118'
99
- default_timeout 1
106
+ default_options = {:host => 'my.amqp.broker.org', :port => '21118'}
107
+ default_timeout = 1
108
+
109
+ em_before { @start = Time.now }
100
110
 
101
111
  it "requires a call to #done in every example" do
102
112
  1.should == 1
@@ -104,10 +114,9 @@ inside the EM loop but before/after AMQP loop (these hooks are currently not imp
104
114
  end
105
115
 
106
116
  it "runs test code in an amqp block automatically" do
107
- start = Time.now
108
117
 
109
118
  EM.add_timer(0.5){
110
- (Time.now-start).should be_close( 0.5, 0.1 )
119
+ (Time.now-@start).should be_close( 0.5, 0.1 )
111
120
  done
112
121
  }
113
122
  end
@@ -122,9 +131,9 @@ inside the EM loop but before/after AMQP loop (these hooks are currently not imp
122
131
  end
123
132
  end
124
133
 
125
- Finally, you can include AMQP::EMSpec in your describe block. This will run all the group examples
126
- inside em block instead of amqp. before{} and after{} hooks should be finished with 'done', same as
127
- when including AMQP::Spec, and same caution about using them applies.
134
+ Finally, you can include AMQP::EMSpec in your describe block. This will run all the group
135
+ examples inside em block instead of amqp. *before/after* hooks should be finished
136
+ with #done, same as when including AMQP::Spec, and same caution about using them applies.
128
137
 
129
138
  describe AMQP do
130
139
  include AMQP::EMSpec
@@ -157,9 +166,11 @@ when including AMQP::Spec, and same caution about using them applies.
157
166
 
158
167
  ==General notes
159
168
 
160
- For a developer new to evented specs, it is not easy to internalize that the blocks given to asynchronous
161
- methods are turned into real callbacks, intended to fire some time later. It is not easy to keep track of
162
- the actual execution path of your code, when your blocks are supposed to fire and in what sequence.
169
+ For a developer new to evented specs, it is not easy to internalize that the blocks given
170
+ to asynchronous methods are turned into real callbacks, intended to fire some time later.
171
+ It is not easy to keep track of the actual execution path of your code, when your blocks
172
+ are supposed to fire and in what sequence.
173
+
163
174
  Take the following spec as an example:
164
175
 
165
176
  it 'receives published message' do
@@ -174,22 +185,24 @@ Take the following spec as an example:
174
185
  end
175
186
  end
176
187
 
177
- Seems like a straightforward spec: you subscribe to a message queue, you set expectations inside
178
- your subscribe block, then you publish into this queue, then you call done. What may be wrong with it?
179
- Well, if you happen to use this spec against live AMQP broker, everything may be wrong.
180
- First, communication delays. There is no guarantee that by the time you publish your message, the queue
181
- have been either created or subscribed to. There is also no guarantee that your subscriber received
182
- the message by the time you are unsubscribing and deleting your queue.
183
- Second, sequence of your blocks. Remember, they are delayed callbacks! Don't just assume your previous
184
- block is already executed when you start your new asynchronous action. In this spec, when done is called,
185
- it stops everything before your subscribe callback even has a chance to fire. As a result, you'll get a
186
- PASSING spec even though your expectation was never executed!
187
-
188
- How to improve this spec? Allow some time for async actions to finish: either use EM timers or pass
189
- :nowait=>false to your asynch calls to force them into synchronicity. Keep in mind the sequence in which
190
- your callbacks are expected to fire - so place your done call at the end of subscribe block in this
191
- example. If you want to be paranoid, you can set flags inside your callbacks and then check that they
192
- actually fired at all AFTER your amqp/em block. Something like this will do the trick:
188
+ Seems like a straightforward spec: you subscribe to a message queue, you set expectations
189
+ inside your subscribe block, then you publish into this queue, then you call done. What may
190
+ be wrong with it? Well, if you happen to use this spec against live AMQP broker, everything
191
+ may be wrong. First, communication delays. There is no guarantee that by the time you publish
192
+ your message, the queue have been either created or subscribed to. There is also no guarantee
193
+ that your subscriber received the message by the time you are unsubscribing and deleting your
194
+ queue. Second, sequence of your blocks. Remember, they are delayed callbacks! Don't just
195
+ assume your previous block is already executed when you start your new asynchronous action.
196
+ In this spec, when done is called, it stops everything before your subscribe callback even
197
+ has a chance to fire. As a result, you'll get a PASSING spec even though your expectation
198
+ was never executed!
199
+
200
+ How to improve this spec? Allow some time for async actions to finish: either use EM timers
201
+ or pass :nowait=>false to your asynch calls to force them into synchronicity. Keep in mind
202
+ the sequence in which your callbacks are expected to fire - so place your done call at the
203
+ end of subscribe block in this example. If you want to be paranoid, you can set flags inside
204
+ your callbacks and then check that they actually fired at all AFTER your amqp/em block.
205
+ Something like this will do the trick:
193
206
 
194
207
  it 'receives published message' do
195
208
  amqp do
@@ -208,10 +221,11 @@ actually fired at all AFTER your amqp/em block. Something like this will do the
208
221
 
209
222
  ==Limitations
210
223
 
211
- AMQP-Spec can be currently used with Rspec only. I suppose, there is nothing special in extending EM-Spec's
212
- test unit and bacon support, I just do not have experience dealing with these platforms. Another limitation,
213
- it uses native Fibers and therefore not compatible with Ruby 1.8. Again, it seems possible to rewrite it in
214
- 1.8-compatible style, with string evals and Fiber backport, but I'd rather leave this work to someone else.
224
+ AMQP-Spec can be currently used with Rspec only. I suppose, there is nothing special in
225
+ extending EM-Spec's test unit and bacon support, I just do not have experience dealing with
226
+ these platforms. Another limitation, it uses native Fibers and therefore not compatible with
227
+ Ruby 1.8. Again, it seems possible to rewrite it in 1.8-compatible style, with string evals
228
+ and Fiber backport, but I'd rather leave this work to someone else.
215
229
 
216
230
  Any help improving this library is greatly appreciated...
217
231
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.7
1
+ 0.3.0
data/lib/amqp-spec.rb CHANGED
@@ -20,5 +20,5 @@ end
20
20
 
21
21
  # Require all ruby source files located under directory lib/amqp-spec
22
22
  # If you need files in specific order, you should specify it here before the glob
23
- AMQP::Spec.require_libs %W[**/*]
23
+ AMQP::Spec.require_libs %W[rspec]
24
24
 
@@ -1,5 +1,6 @@
1
1
  require 'mq'
2
2
 
3
+ # Monkey patching some methods into AMQP to make it more testable
3
4
  module AMQP
4
5
 
5
6
  # Initializes new AMQP client/connection without starting another EM loop
@@ -24,9 +25,9 @@ module AMQP
24
25
  # Cleans up AMQP state after AMQP connection closes
25
26
  def self.cleanup_state
26
27
  # MQ.reset ?
27
- Thread.list.each {|thread| thread[:mq] = nil }
28
- Thread.list.each {|thread| thread[:mq_id] = nil }
29
- @conn = nil
30
- @closing = false
28
+ Thread.list.each { |thread| thread[:mq] = nil }
29
+ Thread.list.each { |thread| thread[:mq_id] = nil }
30
+ @conn = nil
31
+ @closing = false
31
32
  end
32
33
  end
@@ -0,0 +1,160 @@
1
+ require 'fiber' unless Fiber.respond_to?(:current)
2
+
3
+ module AMQP
4
+
5
+ module SpecHelper
6
+
7
+ # Represents example running inside some type of event loop
8
+ class EventedExample
9
+
10
+ # Create new evented example
11
+ def initialize opts = {}, example_group_instance, &block
12
+ @opts, @example_group_instance, @block = opts, example_group_instance, block
13
+ end
14
+
15
+ # Sets timeout for currently running example
16
+ #
17
+ def timeout(spec_timeout)
18
+ EM.cancel_timer(@spec_timer) if @spec_timer
19
+ @spec_timer = EM.add_timer(spec_timeout) do
20
+ @spec_exception = SpecTimeoutExceededError.new "Example timed out"
21
+ done
22
+ end
23
+ end
24
+
25
+ # Breaks the event loop and finishes the spec.
26
+ #
27
+ # This is under-implemented (generic) method that only implements optional delay.
28
+ # It should be given a block that does actual work of finishing up the event loop
29
+ # and cleaning any remaining artifacts.
30
+ #
31
+ # Please redefine it inside descendant class and call super.
32
+ #
33
+ def done delay=nil, &block
34
+ if delay
35
+ EM.add_timer delay, &block
36
+ else
37
+ block.call
38
+ end
39
+ end
40
+
41
+ # Retrieves metadata passed in from enclosing example groups
42
+ #
43
+ def metadata
44
+ @metadata ||= @example_group_instance.metadata.dup rescue {}
45
+ end
46
+
47
+ # Runs hooks of specified type (hopefully, inside the event loop)
48
+ #
49
+ def run_hooks type
50
+ hooks = @example_group_instance.class.em_hooks[type]
51
+ (:before == type ? hooks : hooks.reverse).each do |hook|
52
+ if @example_group_instance.respond_to? :instance_eval_without_event_loop
53
+ @example_group_instance.instance_eval_without_event_loop(&hook)
54
+ else
55
+ @example_group_instance.instance_eval(&hook) #_with_rescue(&hook)
56
+ end
57
+ end
58
+ end
59
+
60
+ # Runs given block inside EM event loop.
61
+ # Double-round exception handler needed because some of the exceptions bubble
62
+ # outside of event loop due to asynchronous nature of evented examples
63
+ #
64
+ def run_em_loop
65
+ begin
66
+ EM.run do
67
+ run_hooks :before
68
+
69
+ @spec_exception = nil
70
+ timeout(@opts[:spec_timeout]) if @opts[:spec_timeout]
71
+ begin
72
+ yield
73
+ rescue Exception => @spec_exception
74
+ # p "Inside loop, caught #{@spec_exception}"
75
+ done # We need to properly terminate the event loop
76
+ end
77
+ end
78
+ rescue Exception => @spec_exception
79
+ # p "Outside loop, caught #{@spec_exception}"
80
+ run_hooks :after # Event loop was broken, but we still need to run em_after hooks
81
+ ensure
82
+ finish_example
83
+ end
84
+ end
85
+
86
+ # Stops EM event loop. It is called from #done
87
+ #
88
+ def finish_em_loop
89
+ run_hooks :after
90
+ EM.stop_event_loop if EM.reactor_running?
91
+ end
92
+
93
+ # Called from #run_event_loop when event loop is stopped, but before example returns.
94
+ # Descendant classes may redefine to clean up type-specific state.
95
+ #
96
+ def finish_example
97
+ raise @spec_exception if @spec_exception
98
+ end
99
+
100
+ end # class EventedExample
101
+
102
+ # Represents spec running inside AMQP.run loop
103
+ class EMExample < EventedExample
104
+
105
+ # Run @block inside the EM.run event loop
106
+ def run
107
+ run_em_loop &@block
108
+ end
109
+
110
+ # Breaks the EM event loop and finishes the spec.
111
+ # Done yields to any given block first, then stops EM event loop.
112
+ #
113
+ def done(delay=nil)
114
+ super(delay) do
115
+ yield if block_given?
116
+ EM.next_tick do
117
+ finish_em_loop
118
+ end
119
+ end
120
+ end # done
121
+ end # class EMExample < EventedExample
122
+
123
+ # Represents spec running inside AMQP.run loop
124
+ class AMQPExample < EventedExample
125
+
126
+ # Run @block inside the AMQP.start loop
127
+ def run
128
+ run_em_loop do
129
+ AMQP.start_connection @opts, &@block
130
+ end
131
+ end
132
+
133
+ # Breaks the event loop and finishes the spec. It yields to any given block first,
134
+ # then stops AMQP, EM event loop and cleans up AMQP state.
135
+ #
136
+ def done(delay=nil)
137
+ super(delay) do
138
+ yield if block_given?
139
+ EM.next_tick do
140
+ if AMQP.conn and not AMQP.closing
141
+ AMQP.stop_connection do
142
+ finish_em_loop
143
+ end
144
+ else
145
+ finish_em_loop
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ # Called from run_event_loop when event loop is finished, before any exceptions
152
+ # is raised or example returns. We ensure AMQP state cleanup here
153
+ def finish_example
154
+ AMQP.cleanup_state
155
+ super
156
+ end
157
+
158
+ end # class AMQPExample < EventedExample
159
+ end # module SpecHelper
160
+ end # module AMQP
@@ -1,51 +1,49 @@
1
- require 'fiber' unless Fiber.respond_to?(:current)
2
1
  require 'amqp-spec/amqp'
2
+ require 'amqp-spec/evented_example'
3
3
 
4
4
  # You can include one of the following modules into your example groups:
5
5
  # AMQP::SpecHelper,
6
6
  # AMQP::Spec,
7
7
  # AMQP::EMSpec.
8
8
  #
9
- # AMQP::SpecHelper module defines #ampq method that can be safely used inside your specs(examples)
10
- # to test expectations inside running AMQP.start loop. Each loop is running in a separate Fiber,
11
- # and you can control for timeouts using either :spec_timeout option given to #amqp method,
12
- # or setting default timeout with class method default_timeout(timeout). In addition to #amqp
13
- # method, you can use #em method - it creates plain EM.run loop without starting AMQP.
9
+ # AMQP::SpecHelper module defines #ampq and #em methods that can be safely used inside
10
+ # your specs (examples) to test code running inside AMQP.start or EM.run loop
11
+ # respectively. Each example is running in a separate event loop,you can control
12
+ # for timeouts either with :spec_timeout option given to #amqp/#em method or setting
13
+ # a default timeout using default_timeout(timeout) macro inside describe/context block.
14
14
  #
15
- # If you include AMQP::Spec module into your example group, each example of this group will run
16
- # inside AMQP.start loop without the need to explicitly call 'amqp'. In order to provide options
17
- # to AMQP loop, default_options class method is defined. Remember, when using AMQP::Specs, you
18
- # will have a single set of AMQP.start options for all your examples.
15
+ # If you include AMQP::Spec module into your example group, each example of this group
16
+ # will run inside AMQP.start loop without the need to explicitly call 'amqp'. In order to
17
+ # provide options to AMQP loop, default_options({opts}) macro is defined.
19
18
  #
20
19
  # Including AMQP::EMSpec module into your example group, each example of this group will run
21
20
  # inside EM.run loop without the need to explicitly call 'em'.
22
21
  #
23
- # In order to stop AMQP/EM loop, you should call 'done' AFTER you are sure that your example is finished.
24
- # For example, if you are using subscribe block that tests expectations on messages, 'done' should be
25
- # probably called at the end of this block.
22
+ # In order to stop AMQP/EM loop, you should call 'done' AFTER you are sure that your
23
+ # example is finished and your expectations executed. For example if you are using
24
+ # subscribe block that tests expectations on messages, 'done' should be probably called
25
+ # at the end of this block.
26
26
  #
27
27
  module AMQP
28
- # AMQP::SpecHelper module defines #ampq method that can be safely used inside your specs(examples)
29
- # to test expectations inside running AMQP.start loop. Each loop is running in a separate Fiber,
30
- # and you can control for timeouts using either :spec_timeout option given to #amqp method,
31
- # or setting default timeout with class method default_timeout(timeout). In addition to #amqp
32
- # method, you can use #em method - it creates plain EM.run loop without starting AMQP.
33
- #
34
- # TODO: Define 'async' method wrapping async requests and returning results... 'async_loop' too for subscribe block?
35
- # TODO: 'evented_before', 'evented_after' that will be run inside EM before the example
28
+
29
+ # AMQP::SpecHelper module defines #ampq and #em methods that can be safely used inside
30
+ # your specs (examples) to test code running inside AMQP.start or EM.run loop
31
+ # respectively. Each example is running in a separate event loop,you can control
32
+ # for timeouts either with :spec_timeout option given to #amqp/#em method or setting
33
+ # a default timeout using default_timeout(timeout) macro inside describe/context block.
36
34
  #
37
35
  # noinspection RubyArgCount
38
36
  module SpecHelper
39
37
 
40
38
  SpecTimeoutExceededError = Class.new(RuntimeError)
41
39
 
42
- # Class methods (macros) for example group that includes SpecHelper
40
+ # Class methods (macros) for any example groups that includes SpecHelper.
41
+ # You can use these methods as macros inside describe/context block.
43
42
  #
44
43
  module GroupMethods
45
- unless respond_to?(:metadata)
46
- # Hacking in metadata into RSpec1 to imitate Rspec2's metadata.
47
- # You can add to metadata Hash to pass options into examples and
48
- # nested groups.
44
+ unless respond_to? :metadata
45
+ # Hacking in metadata into RSpec1 to imitate Rspec2's metadata. Now you can add
46
+ # anything to metadata Hash to pass options into examples and nested groups.
49
47
  #
50
48
  def metadata
51
49
  @metadata ||= superclass.metadata.dup rescue {}
@@ -55,96 +53,76 @@ module AMQP
55
53
  # Sets/retrieves default timeout for running evented specs for this
56
54
  # example group and its nested groups.
57
55
  #
58
- def default_timeout(spec_timeout=nil)
59
- metadata[:em_default_timeout] = spec_timeout if spec_timeout
60
- metadata[:em_default_timeout]
56
+ def default_timeout spec_timeout=nil
57
+ metadata[:em_timeout] = spec_timeout if spec_timeout
58
+ metadata[:em_timeout]
61
59
  end
62
60
 
63
61
  # Sets/retrieves default AMQP.start options for this example group
64
62
  # and its nested groups.
65
63
  #
66
- def default_options(opts=nil)
67
- metadata[:em_default_options] = opts if opts
68
- metadata[:em_default_options]
64
+ def default_options opts=nil
65
+ metadata[:em_defaults] = opts if opts
66
+ metadata[:em_defaults]
67
+ end
68
+
69
+ # Add before hook that will run inside EM event loop
70
+ def em_before scope = :each, &block
71
+ raise ArgumentError, "em_before only supports :each scope" unless :each == scope
72
+ em_hooks[:before] << block
73
+ end
74
+
75
+ # Add after hook that will run inside EM event loop
76
+ def em_after scope = :each, &block
77
+ raise ArgumentError, "em_after only supports :each scope" unless :each == scope
78
+ em_hooks[:after] << block
79
+ end
80
+
81
+ # Collection of evented hooks
82
+ def em_hooks
83
+ metadata[:em_hooks] ||= {:before => [], :after => []}
69
84
  end
70
85
  end
71
86
 
72
- def self.included(example_group)
87
+ def self.included example_group
73
88
  unless example_group.respond_to? :default_timeout
74
- example_group.extend(GroupMethods)
75
- example_group.metadata[:em_default_options] = {}
76
- example_group.metadata[:em_default_timeout] = nil
89
+ example_group.extend GroupMethods
90
+ example_group.metadata[:em_defaults] = {}
91
+ example_group.metadata[:em_timeout] = nil
77
92
  end
78
93
  end
79
94
 
80
-
81
- # Yields to given block inside EM.run and AMQP.start loops. This method takes any option that is
82
- # also accepted by EventMachine::connect. Also, options for AMQP.start include:
83
- # * :user => String (default ‘guest’) - The username as defined by the AMQP server.
84
- # * :pass => String (default ‘guest’) - The password for the associated :user as defined by the AMQP server.
85
- # * :vhost => String (default ’/’) - The virtual host as defined by the AMQP server.
95
+ # Yields to a given block inside EM.run and AMQP.start loops. This method takes
96
+ # any option that is accepted by EventMachine::connect. Options for AMQP.start include:
97
+ # * :user => String (default ‘guest’) - Username as defined by the AMQP server.
98
+ # * :pass => String (default ‘guest’) - Password as defined by the AMQP server.
99
+ # * :vhost => String (default ’/’) - Virtual host as defined by the AMQP server.
86
100
  # * :timeout => Numeric (default nil) - *Connection* timeout, measured in seconds.
87
- # * :logging => true | false (default false) - Toggle the extremely verbose AMQP logging.
101
+ # * :logging => Bool (default false) - Toggle the extremely verbose AMQP logging.
88
102
  #
89
- # In addition to EM and AMQP options, :spec_timeout option (in seconds) is used to force spec to timeout
90
- # if something goes wrong and EM/AMQP loop hangs for some reason. SpecTimeoutExceededError is raised.
103
+ # In addition to EM and AMQP options, :spec_timeout option (in seconds) is used
104
+ # to force spec to timeout if something goes wrong and EM/AMQP loop hangs for some
105
+ # reason. SpecTimeoutExceededError is raised if it happens.
91
106
  #
92
107
  def amqp opts={}, &block
93
108
  opts = self.class.default_options.merge opts
94
- begin
95
- EM.run do
96
- @_em_spec_with_amqp = true
97
- @_em_spec_exception = nil
98
- spec_timeout = opts.delete(:spec_timeout) || self.class.default_timeout
99
- timeout(spec_timeout) if spec_timeout
100
- @_em_spec_fiber = Fiber.new do
101
- begin
102
- AMQP.start_connection opts, &block
103
- rescue Exception => @_em_spec_exception
104
- done
105
- end
106
- Fiber.yield
107
- end
108
-
109
- @_em_spec_fiber.resume
110
- end
111
- rescue Exception => outer_spec_exception
112
- # Make sure AMQP state is cleaned even after Rspec failures
113
- AMQP.cleanup_state
114
- raise outer_spec_exception
115
- end
109
+ opts[:spec_timeout] ||= self.class.default_timeout
110
+ @evented_example = AMQPExample.new(opts, self, &block)
111
+ @evented_example.run
116
112
  end
117
113
 
118
- # Yields to block inside EM loop, :spec_timeout option (in seconds) is used to force spec to timeout
119
- # if something goes wrong and EM/AMQP loop hangs for some reason. SpecTimeoutExceededError is raised.
114
+ # Yields to block inside EM loop, :spec_timeout option (in seconds) is used to
115
+ # force spec to timeout if something goes wrong and EM/AMQP loop hangs for some
116
+ # reason. SpecTimeoutExceededError is raised if it happens.
120
117
  #
121
- def em(spec_timeout = self.class.default_timeout, &block)
122
- spec_timeout = spec_timeout[:spec_timeout] || self.class.default_timeout if spec_timeout.is_a?(Hash)
123
- EM.run do
124
- @_em_spec_with_amqp = false
125
- @_em_spec_exception = nil
126
- timeout(spec_timeout) if spec_timeout
127
- @_em_spec_fiber = Fiber.new do
128
- begin
129
- block.call
130
- rescue Exception => @_em_spec_exception
131
- done
132
- end
133
- Fiber.yield
134
- end
135
-
136
- @_em_spec_fiber.resume
137
- end
138
- end
139
-
140
- # Sets timeout for current running example
141
- #
142
- def timeout(spec_timeout)
143
- EM.cancel_timer(@_em_timer) if @_em_timer
144
- @_em_timer = EM.add_timer(spec_timeout) do
145
- @_em_spec_exception = SpecTimeoutExceededError.new
146
- done
118
+ def em opts = {}, &block
119
+ if opts.is_a? Hash
120
+ opts[:spec_timeout] ||= self.class.default_timeout
121
+ else
122
+ opts = {spec_timeout: opts}
147
123
  end
124
+ @evented_example = EMExample.new(opts, self, &block)
125
+ @evented_example.run
148
126
  end
149
127
 
150
128
  # Breaks the event loop and finishes the spec. This should be called after
@@ -156,58 +134,31 @@ module AMQP
156
134
  # that your (default or explicit) spec timeout may fire before your delayed done
157
135
  # callback is due, leading to SpecTimeoutExceededError
158
136
  #
159
- def done(delay=nil)
160
- done_proc = proc do
161
- yield if block_given?
162
- EM.next_tick do
163
- if @_em_spec_with_amqp
164
- if AMQP.conn and not AMQP.closing
165
- AMQP.stop_connection do
166
- finish_em_spec_fiber { AMQP.cleanup_state }
167
- end
168
- else
169
- finish_em_spec_fiber { AMQP.cleanup_state }
170
- end
171
- else
172
- finish_em_spec_fiber
173
- end
174
- end
175
- end
176
- if delay
177
- EM.add_timer delay, &done_proc
178
- else
179
- done_proc.call
180
- end
137
+ def done *args, &block
138
+ @evented_example.done *args, &block
181
139
  end
182
140
 
183
- # Retrieves metadata passed in from enclosing example groups
141
+ # Manually sets timeout for currently running example
184
142
  #
185
- def metadata
186
- @metadata ||= self.class.metadata.dup rescue {}
143
+ def timeout *args
144
+ @evented_example.timeout *args
187
145
  end
188
146
 
189
- private
147
+ end # module SpecHelper
190
148
 
191
- # Stops EM loop, executes optional block, finishes off fiber and raises exception if any
192
- #
193
- def finish_em_spec_fiber
194
- EM.stop_event_loop if EM.reactor_running?
195
- yield if block_given?
196
- @_em_spec_fiber.resume if @_em_spec_fiber.alive?
197
- raise @_em_spec_exception if @_em_spec_exception
198
- end
199
- end
200
-
201
- # If you include AMQP::Spec module into your example group, each example of this group will run
202
- # inside AMQP.start loop without the need to explicitly call 'amqp'. In order to provide options
203
- # to AMQP loop, default_options class method is defined. Remember, when using AMQP::Specs, you
204
- # will have a single set of AMQP.start options for all your examples.
149
+ # If you include AMQP::Spec module into your example group, each example of this group
150
+ # will run inside AMQP.start loop without the need to explicitly call 'amqp'. In order
151
+ # to provide options to AMQP loop, default_options class method is defined. Remember,
152
+ # when using AMQP::Specs, you'll have a single set of AMQP.start options for all your
153
+ # examples.
205
154
  #
206
155
  module Spec
207
156
  def self.included(example_group)
208
157
  example_group.send(:include, SpecHelper)
209
158
  end
210
159
 
160
+ alias instance_eval_without_event_loop instance_eval
161
+
211
162
  def instance_eval(&block)
212
163
  amqp do
213
164
  super(&block)
@@ -215,18 +166,20 @@ module AMQP
215
166
  end
216
167
  end
217
168
 
218
- # Including AMQP::EMSpec module into your example group, each example of this group will run
219
- # inside EM.run loop without the need to explicitly call 'em'.
169
+ # Including AMQP::EMSpec module into your example group, each example of this group
170
+ # will run inside EM.run loop without the need to explicitly call 'em'.
220
171
  #
221
172
  module EMSpec
222
173
  def self.included(example_group)
223
174
  example_group.send(:include, SpecHelper)
224
175
  end
225
176
 
177
+ alias instance_eval_without_event_loop instance_eval
178
+
226
179
  def instance_eval(&block)
227
180
  em do
228
181
  super(&block)
229
182
  end
230
183
  end
231
184
  end
232
- end
185
+ end
@@ -0,0 +1,156 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for 'hooked em specs' do
4
+ it 'should execute em_before' do
5
+ em do
6
+ @hooks_called.should include :em_before
7
+ @hooks_called.should_not include :em_after
8
+ done
9
+ end
10
+ end
11
+
12
+ it 'should execute em_after if business exception is raised' do
13
+ em do
14
+ expect {
15
+ raise StandardError
16
+ }.to raise_error
17
+ done
18
+ end
19
+ end
20
+
21
+ it 'should execute em_after if RSpec expectation fails' do
22
+ em do
23
+ expect { :this.should == :fail
24
+ }.to raise_error RSPEC::Expectations::ExpectationNotMetError
25
+ done
26
+ end
27
+ end
28
+ end
29
+
30
+ shared_examples_for 'hooked amqp specs' do
31
+ it 'should execute em_before' do
32
+ amqp do
33
+ @hooks_called.should include :em_before
34
+ @hooks_called.should_not include :em_after
35
+ done
36
+ end
37
+ end
38
+
39
+ it 'should execute em_after if business exception is raised' do
40
+ amqp do
41
+ expect {
42
+ raise StandardError
43
+ }.to raise_error
44
+ done
45
+ end
46
+ end
47
+
48
+ it 'should execute em_after if RSpec expectation fails' do
49
+ amqp do
50
+ expect { :this.should == :fail
51
+ }.to raise_error RSPEC::Expectations::ExpectationNotMetError
52
+ done
53
+ end
54
+ end
55
+ end
56
+
57
+ describe AMQP, " with em_before/em_after" do
58
+ before { @hooks_called = [] } #; puts "In before: #{self}"} #
59
+
60
+ describe AMQP, " tested with AMQP::SpecHelper" do
61
+ include AMQP::SpecHelper
62
+ default_options AMQP_OPTS if defined? AMQP_OPTS
63
+
64
+ before { @hooks_called << :before } #; puts "In before 2: #{self}" }
65
+
66
+ em_before { @hooks_called << :em_before } # puts "In em_before: #{self}";
67
+ em_after { @hooks_called << :em_after } # puts "In em_after: #{self}";
68
+
69
+ context 'for non-evented specs' do
70
+ after { @hooks_called.should == [:before] }
71
+
72
+ it 'should NOT execute em_before or em_after' do
73
+ @hooks_called.should_not include :em_before
74
+ @hooks_called.should_not include :em_after
75
+ end
76
+
77
+ it 'should NOT execute em_after if business exception is raised' do
78
+ expect { raise StandardError
79
+ }.to raise_error
80
+ end
81
+
82
+ it 'should execute em_after if RSpec expectation fails' do
83
+ expect { :this.should == :fail
84
+ }.to raise_error RSPEC::Expectations::ExpectationNotMetError
85
+ end
86
+ end # context 'for non-evented specs'
87
+
88
+ context 'for evented specs' do #, pending: true do
89
+ after { @hooks_called.should include :before, :em_before, :em_after }
90
+
91
+ context 'with em block' do
92
+
93
+ it_should_behave_like 'hooked em specs'
94
+
95
+ context 'inside nested example group' do
96
+ before { @hooks_called << :context_before }
97
+ em_before { @hooks_called << :context_em_before }
98
+ em_after { @hooks_called << :context_em_after }
99
+
100
+ after { @hooks_called.should include :before,
101
+ :context_before,
102
+ :em_before,
103
+ :context_em_before,
104
+ :context_em_after,
105
+ :em_after }
106
+
107
+ it 'should fire both nested :before hooks' do
108
+ em do
109
+ @hooks_called.should include :before,
110
+ :context_before,
111
+ :em_before,
112
+ :context_em_before
113
+ @hooks_called.should_not include :em_after, :context_em_after
114
+ done
115
+ end
116
+ end
117
+
118
+ it_should_behave_like 'hooked em specs'
119
+
120
+ end # context 'inside nested example group'
121
+ end # context 'with em block'
122
+
123
+ context 'with amqp block' do
124
+
125
+ it_should_behave_like 'hooked amqp specs'
126
+
127
+ context 'inside nested example group' do
128
+ before { @hooks_called << :context_before }
129
+ em_before { @hooks_called << :context_em_before }
130
+ em_after { @hooks_called << :context_em_after }
131
+
132
+ after { @hooks_called.should include :before,
133
+ :context_before,
134
+ :em_before,
135
+ :context_em_before,
136
+ :context_em_after,
137
+ :em_after }
138
+
139
+ it 'should fire both nested :before hooks' do
140
+ amqp do
141
+ @hooks_called.should include :before,
142
+ :context_before,
143
+ :em_before,
144
+ :context_em_before
145
+ @hooks_called.should_not include :em_after, :context_em_after
146
+ done
147
+ end
148
+ end
149
+
150
+ it_should_behave_like 'hooked amqp specs'
151
+
152
+ end # context 'inside nested example group'
153
+ end # context 'with amqp block'
154
+ end # context 'for evented specs'
155
+ end # describe AMQP, " tested with AMQP::SpecHelper"
156
+ end # describe AMQP, " with em_before/em_after"
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe 'Following examples should all be failing:' do
3
+ describe 'Following 8 examples should all be failing:' do
4
4
  describe EventMachine, " when running failing examples" do
5
5
  include AMQP::EMSpec
6
6
 
@@ -19,8 +19,6 @@ describe 'Following examples should all be failing:' do
19
19
  describe EventMachine, " when testing with AMQP::EMSpec with a maximum execution time per test" do
20
20
  include AMQP::EMSpec
21
21
 
22
- # For RSpec 1, default_timeout and default_options are global
23
- # For RSpec 2, default_timeout and default_options are example-group local, inheritable by nested groups
24
22
  default_timeout 1
25
23
 
26
24
  it 'should timeout before reaching done' do
@@ -37,8 +35,6 @@ describe 'Following examples should all be failing:' do
37
35
 
38
36
  include AMQP::Spec
39
37
 
40
- # For RSpec 1, default_timeout and default_options are global
41
- # For RSpec 2, default_timeout and default_options are example-group local, inheritable by nested groups
42
38
  default_timeout 1
43
39
 
44
40
  it 'should timeout before reaching done' do
@@ -1,5 +1,4 @@
1
- require_relative 'spec_helper.rb'
1
+ require 'spec_helper'
2
2
 
3
3
  describe '!!!!!!!!! LEAKING OR PROBLEMATIC EXAMPLES !!!!!!!!!' do
4
- end
5
-
4
+ end
@@ -1,4 +1,4 @@
1
- require_relative 'spec_helper.rb'
1
+ require 'spec_helper'
2
2
 
3
3
  def publish_and_consume_once(queue_name="test_sink", data="data")
4
4
  amqp do
@@ -27,8 +27,8 @@ describe 'Evented AMQP specs' do
27
27
  default_options AMQP_OPTS if defined? AMQP_OPTS
28
28
  default_timeout 1
29
29
 
30
- puts "Default timeout: #{default_timeout}, Default options:"
31
- p default_options
30
+ puts "Default timeout: #{default_timeout}"
31
+ puts "Default options :#{default_options}"
32
32
 
33
33
  it_should_behave_like 'SpecHelper examples'
34
34
 
@@ -1,4 +1,4 @@
1
- require_relative 'spec_helper.rb'
1
+ require 'spec_helper'
2
2
 
3
3
  describe 'Plain EM, no AMQP' do
4
4
  describe EventMachine, " when testing with AMQP::SpecHelper" do
@@ -47,6 +47,7 @@ shared_examples_for 'SpecHelper examples' do
47
47
  AMQP.conn.should be_nil
48
48
  end
49
49
 
50
+ # TODO: remove dependency on (possibly long) DNS lookup
50
51
  it "should gracefully exit if no AMQP connection was made" do
51
52
  expect {
52
53
  amqp(:host => 'Impossible') do
@@ -58,6 +59,7 @@ shared_examples_for 'SpecHelper examples' do
58
59
  end
59
60
 
60
61
  it_should_behave_like 'done examples'
62
+
61
63
  it_should_behave_like 'timeout examples'
62
64
  end
63
65
 
@@ -101,7 +103,7 @@ shared_examples_for 'timeout examples' do
101
103
 
102
104
  it 'should timeout before reaching done because of default spec timeout' do
103
105
  expect { amqp { EM.add_timer(2) { done } } }.
104
- to raise_error SpecTimeoutExceededError
106
+ to raise_error SpecTimeoutExceededError
105
107
  (Time.now-@start).should be_close(1.0, 0.1)
106
108
  end
107
109
 
@@ -117,13 +119,13 @@ shared_examples_for 'timeout examples' do
117
119
 
118
120
  specify "spec timeout given in amqp options has higher priority than default" do
119
121
  expect { amqp(:spec_timeout => 0.2) {} }.
120
- to raise_error SpecTimeoutExceededError
122
+ to raise_error SpecTimeoutExceededError
121
123
  (Time.now-@start).should be_close(0.2, 0.1)
122
124
  end
123
125
 
124
126
  specify "but timeout call inside amqp loop has even higher priority" do
125
127
  expect { amqp(:spec_timeout => 0.5) { timeout(0.2) } }.
126
- to raise_error SpecTimeoutExceededError
128
+ to raise_error SpecTimeoutExceededError
127
129
  (Time.now-@start).should be_close(0.2, 0.1)
128
130
  end
129
131
 
@@ -131,25 +133,22 @@ shared_examples_for 'timeout examples' do
131
133
  AMQP.conn.should be_nil
132
134
  end
133
135
 
134
- if rspec2?
135
- context 'embedded context can set up separate defaults' do
136
- default_timeout 0.2 # Can be used to set default :spec_timeout for all your amqp-based specs
136
+ context 'embedded context can set up separate defaults' do
137
+ default_timeout 0.2 # Can be used to set default :spec_timeout for all evented specs
137
138
 
138
- specify 'default timeout should be 0.2' do
139
- expect { amqp { EM.add_timer(2) { done } } }.to raise_error SpecTimeoutExceededError
140
- (Time.now-@start).should be_close(0.2, 0.1)
141
- end
139
+ specify 'default timeout should be 0.2' do
140
+ expect { em { EM.add_timer(2) { done } } }.to raise_error SpecTimeoutExceededError
141
+ (Time.now-@start).should be_close(0.2, 0.1)
142
+ end
142
143
 
143
- context 'deeply embedded context can set up separate defaults' do
144
- default_timeout 0.5 # Can be used to set default :spec_timeout for all your amqp-based specs
144
+ context 'deeply embedded context can set up separate defaults' do
145
+ default_timeout 0.5
145
146
 
146
- specify 'default timeout should be 0.5' do
147
- expect { amqp { EM.add_timer(2) { done } } }.to raise_error SpecTimeoutExceededError
148
- (Time.now-@start).should be_close(0.5, 0.1)
149
- end
147
+ specify 'default timeout should be 0.5' do
148
+ expect { amqp { EM.add_timer(2) { done } } }.to raise_error SpecTimeoutExceededError
149
+ (Time.now-@start).should be_close(0.5, 0.1)
150
150
  end
151
151
  end
152
-
153
152
  end
154
153
  end
155
154
 
data/spec/spec_helper.rb CHANGED
@@ -12,6 +12,10 @@ def rspec2?
12
12
  defined?(RSpec)
13
13
  end
14
14
 
15
+ # Done is defined as noop to help share examples between evented and non-evented specs
16
+ def done
17
+ end
18
+
15
19
  RSPEC = rspec2? ? RSpec : Spec
16
20
 
17
21
  amqp_config = File.dirname(__FILE__) + '/amqp.yml'
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amqp-spec
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
5
4
  prerelease: false
6
5
  segments:
7
6
  - 0
8
- - 2
9
- - 7
10
- version: 0.2.7
7
+ - 3
8
+ - 0
9
+ version: 0.3.0
11
10
  platform: ruby
12
11
  authors:
13
12
  - Arvicco
@@ -15,7 +14,7 @@ autorequire:
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2010-11-04 00:00:00 +03:00
17
+ date: 2010-11-16 00:00:00 +03:00
19
18
  default_executable:
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
@@ -26,7 +25,6 @@ dependencies:
26
25
  requirements:
27
26
  - - ">="
28
27
  - !ruby/object:Gem::Version
29
- hash: 13
30
28
  segments:
31
29
  - 1
32
30
  - 2
@@ -42,7 +40,6 @@ dependencies:
42
40
  requirements:
43
41
  - - ~>
44
42
  - !ruby/object:Gem::Version
45
- hash: 9
46
43
  segments:
47
44
  - 0
48
45
  - 6
@@ -62,10 +59,12 @@ extra_rdoc_files:
62
59
  - README.rdoc
63
60
  files:
64
61
  - lib/amqp-spec/amqp.rb
62
+ - lib/amqp-spec/evented_example.rb
65
63
  - lib/amqp-spec/rspec.rb
66
64
  - lib/amqp-spec.rb
67
65
  - lib/version.rb
68
66
  - spec/amqp.yml
67
+ - spec/em_hooks_spec.rb
69
68
  - spec/failing_rspec_spec.rb
70
69
  - spec/problematic_rspec_spec.rb
71
70
  - spec/rspec_amqp_spec.rb
@@ -104,7 +103,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
104
103
  requirements:
105
104
  - - ">="
106
105
  - !ruby/object:Gem::Version
107
- hash: 3
108
106
  segments:
109
107
  - 0
110
108
  version: "0"
@@ -113,7 +111,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
113
111
  requirements:
114
112
  - - ">="
115
113
  - !ruby/object:Gem::Version
116
- hash: 3
117
114
  segments:
118
115
  - 0
119
116
  version: "0"
@@ -126,6 +123,7 @@ specification_version: 3
126
123
  summary: Simple API for writing asynchronous EventMachine/AMQP specs. Supports RSpec and RSpec2.
127
124
  test_files:
128
125
  - spec/amqp.yml
126
+ - spec/em_hooks_spec.rb
129
127
  - spec/failing_rspec_spec.rb
130
128
  - spec/problematic_rspec_spec.rb
131
129
  - spec/rspec_amqp_spec.rb