amqp-spec 0.3.3 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY CHANGED
@@ -69,3 +69,7 @@
69
69
  == 0.3.3 / 2010-11-24
70
70
 
71
71
  * Spec timeout refactored
72
+
73
+ == 0.3.4 / 2010-11-26
74
+
75
+ * amqp_before/after hooks added
@@ -10,20 +10,21 @@ Simple API for writing asynchronous EventMachine/AMQP specs. Supports RSpec and
10
10
 
11
11
  EventMachine-based code, including synchronous {AMQP library}[http://github.com/tmm1/amqp]
12
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.
13
+ Mocks[http://github.com/danielsdeleo/moqueue]
14
+ or {synchronous libraries}[http://github.com/celldee/bunny]
15
+ instead of EM-based libraries in unit tests. This is not always an option, however -
16
+ sometimes your code just has to run inside the event loop, and you want to test a real
17
+ thing, not just mocks.
17
18
 
18
19
  EM-Spec[http://github.com/tmm1/em-spec] gem made it easier to write evented specs, but it
19
20
  has several drawbacks. First, it is not easy to manage both EM.run and AMQP.start loops
20
21
  at the same time. Second, AMQP is not properly stopped and deactivated upon exceptions and
21
22
  timeouts, resulting in state leak between examples and multiple mystereous failures.
22
23
 
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.
24
+ AMQP-Spec is based on EM-Spec code but makes it easier to test AMQP event loops
25
+ specifically. API is very similar to EM-Spec's, only a bit extended. The final goal is to
26
+ make writing AMQP specs reasonably pleasant experience and dispel the notion that evented
27
+ AMQP-based libs are impossible to unit-test.
27
28
 
28
29
  Mind you, you still have to properly manage your AMQP broker in order to prevent broker
29
30
  state from leaking between examples. You can try to combine AMQP-Spec and
@@ -45,8 +46,15 @@ this is different from EM-Spec where default_timeout is effectively a global set
45
46
 
46
47
  In order to setup/teardown EM state before/after your examples, you'll need to use
47
48
  *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.
49
- If you are using
49
+ *before*/*after* hooks but run inside the EM event loop before/after your example block.
50
+ If you are using #amqp method, *em_before* hook will run just BEFORE AMQP connection is
51
+ attempted, and *em_after* is run after AMQP is stopped.
52
+
53
+ Sometimes, may want to setup/teardown state inside AMQP connection (inside block given to
54
+ AMQP.start): for example, to make sure that the connection is established before your
55
+ example runs, or pre-declare some queues and exchanges common for all examples.
56
+ In this case, please use *amqp_before* and *amqp_after* hooks. These hooks run inside
57
+ the AMQP.start block just before/after your example block.
50
58
 
51
59
  require "amqp-spec/rspec"
52
60
 
@@ -96,9 +104,12 @@ will run in its separate EM loop that you'll need to shut down either manually (
96
104
  via timeout. Essentially, this means that any EM-related state that you'd like to set up or
97
105
  tear down using these hooks will be lost as example itself will run in a different EM loop.
98
106
 
99
- In short, you should avoid using *before/after* if you include AMQP::Spec - instead, use
100
- *em_before/em_after* hooks that run inside the EM event loop. One more note: you don't need
101
- to call #done inside *em_before/em_after*, otherwise it'll shut down the reactor.
107
+ In short, you should avoid using *before*/*after* if you include AMQP::Spec - instead, use
108
+ *em_before*/*em_after* or *amqp_before*/*amqp_after* hooks that run inside the EM event
109
+ loop.
110
+
111
+ One more note: you don't need to call #done inside evented hooks, otherwise it'll shut down
112
+ the EM reactor.
102
113
 
103
114
 
104
115
  describe AMQP do
@@ -133,8 +144,9 @@ to call #done inside *em_before/em_after*, otherwise it'll shut down the reactor
133
144
  end
134
145
 
135
146
  Finally, you can include AMQP::EMSpec in your describe block. This will run all the group
136
- examples inside em block instead of amqp. Non-evented *before/after* hooks should be finished
137
- with #done, same as when including AMQP::Spec, and same caution about using them applies.
147
+ examples inside em block instead of amqp. Non-evented *before*/*after* hooks should be
148
+ finished with #done, same as when including AMQP::Spec, and same caution about using them
149
+ applies.
138
150
 
139
151
  describe AMQP do
140
152
  include AMQP::EMSpec
@@ -189,14 +201,14 @@ Take the following spec as an example:
189
201
  Seems like a straightforward spec: you subscribe to a message queue, you set expectations
190
202
  inside your subscribe block, then you publish into this queue, then you call done. What may
191
203
  be wrong with it? Well, if you happen to use this spec against live AMQP broker, everything
192
- may be wrong. First, communication delays. There is no guarantee that by the time you publish
193
- your message, the queue have been either created or subscribed to. There is also no guarantee
194
- that your subscriber received the message by the time you are unsubscribing and deleting your
195
- queue. Second, sequence of your blocks. Remember, they are delayed callbacks! Don't just
196
- assume your previous block is already executed when you start your new asynchronous action.
197
- In this spec, when done is called, it stops everything before your subscribe callback even
198
- has a chance to fire. As a result, you'll get a PASSING spec even though your expectation
199
- was never executed!
204
+ may be wrong. First, communication delays. There is no guarantee that by the time you
205
+ publish your message, the queue have been either created or subscribed to. There is also
206
+ no guarantee that your subscriber received the message by the time you are unsubscribing
207
+ and deleting your queue. Second, sequence of your blocks. Remember they are delayed
208
+ callbacks! Don't just assume your previous block is already executed when you start your
209
+ new asynchronous action. In this spec, when done is called, it stops everything before your
210
+ subscribe callback even has a chance to fire. As a result, you'll get a PASSING spec even
211
+ though your expectation was never executed!
200
212
 
201
213
  How to improve this spec? Allow some time for async actions to finish: either use EM timers
202
214
  or pass :nowait=>false to your asynch calls to force them into synchronicity. Keep in mind
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.3
1
+ 0.3.4
@@ -57,7 +57,7 @@ module AMQP
57
57
  def run_em_loop
58
58
  begin
59
59
  EM.run do
60
- run_em_hooks :before
60
+ run_em_hooks :em_before
61
61
 
62
62
  @spec_exception = nil
63
63
  timeout(@opts[:spec_timeout]) if @opts[:spec_timeout]
@@ -70,7 +70,7 @@ module AMQP
70
70
  end
71
71
  rescue Exception => @spec_exception
72
72
  # p "Outside loop, caught #{@spec_exception}"
73
- run_em_hooks :after # Event loop broken, but we still need to run em_after hooks
73
+ run_em_hooks :em_after # Event loop broken, but we still need to run em_after hooks
74
74
  ensure
75
75
  finish_example
76
76
  end
@@ -79,7 +79,7 @@ module AMQP
79
79
  # Stops EM event loop. It is called from #done
80
80
  #
81
81
  def finish_em_loop
82
- run_em_hooks :after
82
+ run_em_hooks :em_after
83
83
  EM.stop_event_loop if EM.reactor_running?
84
84
  end
85
85
 
@@ -88,7 +88,6 @@ module AMQP
88
88
  # Descendant classes may redefine to clean up type-specific state.
89
89
  #
90
90
  def finish_example
91
- # p @spec_exception if @spec_exception
92
91
  raise @spec_exception if @spec_exception
93
92
  end
94
93
 
@@ -123,7 +122,10 @@ module AMQP
123
122
  # Run @block inside the AMQP.start loop
124
123
  def run
125
124
  run_em_loop do
126
- AMQP.start_connection @opts, &@block
125
+ AMQP.start_connection @opts, do
126
+ run_em_hooks :amqp_before
127
+ @block.call
128
+ end
127
129
  end
128
130
  end
129
131
 
@@ -134,11 +136,14 @@ module AMQP
134
136
  super(delay) do
135
137
  yield if block_given?
136
138
  EM.next_tick do
139
+ run_em_hooks :amqp_after
137
140
  if AMQP.conn and not AMQP.closing
138
141
  AMQP.stop_connection do
142
+ AMQP.cleanup_state
139
143
  finish_em_loop
140
144
  end
141
145
  else
146
+ AMQP.cleanup_state
142
147
  finish_em_loop
143
148
  end
144
149
  end
@@ -69,21 +69,35 @@ module AMQP
69
69
  # Add before hook that will run inside EM event loop
70
70
  def em_before scope = :each, &block
71
71
  raise ArgumentError, "em_before only supports :each scope" unless :each == scope
72
- em_hooks[:before] << block
72
+ em_hooks[:em_before] << block
73
73
  end
74
74
 
75
75
  # Add after hook that will run inside EM event loop
76
76
  def em_after scope = :each, &block
77
77
  raise ArgumentError, "em_after only supports :each scope" unless :each == scope
78
- em_hooks[:after].unshift block
78
+ em_hooks[:em_after].unshift block
79
+ end
80
+
81
+ # Add before hook that will run inside AMQP connection (AMQP.start loop)
82
+ def amqp_before scope = :each, &block
83
+ raise ArgumentError, "amqp_before only supports :each scope" unless :each == scope
84
+ em_hooks[:amqp_before] << block
85
+ end
86
+
87
+ # Add after hook that will run inside AMQP connection (AMQP.start loop)
88
+ def amqp_after scope = :each, &block
89
+ raise ArgumentError, "amqp_after only supports :each scope" unless :each == scope
90
+ em_hooks[:amqp_after].unshift block
79
91
  end
80
92
 
81
93
  # Collection of evented hooks for THIS example group
82
94
  def em_hooks
83
95
  metadata[:em_hooks] ||= {}
84
96
  metadata[:em_hooks][self] ||=
85
- {before: (superclass.em_hooks[:before].clone rescue []),
86
- after: (superclass.em_hooks[:after].clone rescue [])}
97
+ {em_before: (superclass.em_hooks[:em_before].clone rescue []),
98
+ em_after: (superclass.em_hooks[:em_after].clone rescue []),
99
+ amqp_before: (superclass.em_hooks[:amqp_before].clone rescue []),
100
+ amqp_after: (superclass.em_hooks[:amqp_after].clone rescue [])}
87
101
  end
88
102
  end
89
103
 
@@ -1,5 +1,19 @@
1
1
  require 'spec_helper'
2
2
 
3
+ def hook symbol=nil, reactor, connection
4
+ @hooks_called << symbol.to_sym if symbol
5
+ if :reactor_running == reactor
6
+ EM.reactor_running?.should be_true
7
+ else
8
+ EM.reactor_running?.should be_false
9
+ end
10
+ if :amqp_connected == connection
11
+ AMQP.conn.should be_connected
12
+ else
13
+ AMQP.conn and AMQP.conn.should_not be_connected
14
+ end
15
+ end
16
+
3
17
  shared_examples_for 'hooked em specs' do
4
18
  it 'should execute em_before' do
5
19
  em do
@@ -34,6 +48,8 @@ shared_examples_for 'hooked amqp specs' do
34
48
  amqp do
35
49
  @hooks_called.should include :em_before
36
50
  @hooks_called.should_not include :em_after
51
+ @hooks_called.should include :amqp_before
52
+ @hooks_called.should_not include :amqp_after
37
53
  done
38
54
  end
39
55
  end
@@ -65,13 +81,14 @@ describe AMQP::SpecHelper, ".em_before/.em_after" do
65
81
  include AMQP::SpecHelper
66
82
  default_options AMQP_OPTS if defined? AMQP_OPTS
67
83
 
68
- before { @hooks_called << :before }
69
-
70
- em_before { @hooks_called << :em_before }
71
- em_after { @hooks_called << :em_after }
84
+ before { hook :before, :reactor_not_running, :amqp_not_connected }
85
+ em_before { hook :em_before, :reactor_running, :amqp_not_connected }
86
+ em_after { hook :em_after, :reactor_running, :amqp_not_connected }
72
87
 
73
88
  context 'for non-evented specs' do
74
- after { @hooks_called.should == [:before] }
89
+ after {
90
+ @hooks_called.should == [:before]
91
+ hook :reactor_not_running, :amqp_not_connected }
75
92
 
76
93
  it 'should NOT execute em_before or em_after' do
77
94
  @hooks_called.should_not include :em_before
@@ -90,7 +107,9 @@ describe AMQP::SpecHelper, ".em_before/.em_after" do
90
107
  end # context 'for non-evented specs'
91
108
 
92
109
  context 'for evented specs' do #, pending: true do
93
- after { @hooks_called.should include :before, :em_before, :em_after }
110
+ after {
111
+ @hooks_called.should include :before, :em_before, :em_after
112
+ hook :reactor_not_running, :amqp_not_connected }
94
113
 
95
114
  context 'with em block' do
96
115
 
@@ -106,32 +125,35 @@ describe AMQP::SpecHelper, ".em_before/.em_after" do
106
125
  it 'should not run hooks from unrelated group' do
107
126
  em do
108
127
  @hooks_called.should_not include :amqp_context_em_before,
109
- :amqp_context_before
128
+ :amqp_context_before,
129
+ :amqp_before,
130
+ :context_amqp_before
110
131
  done
111
132
  end
112
133
  end
113
134
 
114
135
  context 'inside nested example group' do
115
- before { @hooks_called << :context_before }
116
- em_before { @hooks_called << :context_em_before }
117
- em_after { @hooks_called << :context_em_after }
136
+ before { hook :context_before, :reactor_not_running, :amqp_not_connected }
137
+ em_before { hook :context_em_before, :reactor_running, :amqp_not_connected }
138
+ em_after { hook :context_em_after, :reactor_running, :amqp_not_connected }
118
139
 
119
140
  after { @hooks_called.should include :before,
120
141
  :context_before,
121
142
  :em_before,
122
143
  :context_em_before,
123
144
  :context_em_after,
124
- :em_after }
145
+ :em_after
146
+ hook :reactor_not_running, :amqp_not_connected
147
+ }
125
148
 
126
149
  it_should_behave_like 'hooked em specs'
127
150
 
128
- it 'should fire both nested :before hooks' do
151
+ it 'should fire all nested :before hooks, but no :after hooks' do
129
152
  em do
130
- @hooks_called.should include :before,
131
- :context_before,
132
- :em_before,
133
- :context_em_before
134
- @hooks_called.should_not include :em_after, :context_em_after
153
+ @hooks_called.should == [:before,
154
+ :context_before,
155
+ :em_before,
156
+ :context_em_before]
135
157
  done
136
158
  end
137
159
  end
@@ -140,13 +162,16 @@ describe AMQP::SpecHelper, ".em_before/.em_after" do
140
162
  end # context 'with em block'
141
163
 
142
164
  context 'with amqp block' do
165
+ amqp_before { hook :amqp_before, :reactor_running, :amqp_connected }
166
+ amqp_after { hook :amqp_after, :reactor_running, :amqp_connected }
143
167
 
144
168
  it_should_behave_like 'hooked amqp specs'
145
169
 
146
170
  it 'should not run nested em hooks' do
147
171
  amqp do
148
- @hooks_called.should_not include :amqp_context_em_before,
149
- :amqp_context_before
172
+ @hooks_called.should_not include :amqp_context_before,
173
+ :amqp_context_em_before,
174
+ :context_amqp_before
150
175
  done
151
176
  end
152
177
  end
@@ -159,16 +184,23 @@ describe AMQP::SpecHelper, ".em_before/.em_after" do
159
184
  end
160
185
 
161
186
  context 'inside nested example group' do
162
- before { @hooks_called << :amqp_context_before; $debug = true }
163
- em_before { @hooks_called << :amqp_context_em_before }
164
- em_after { @hooks_called << :amqp_context_em_after }
187
+ before { hook :amqp_context_before, :reactor_not_running, :amqp_not_connected }
188
+ em_before { hook :amqp_context_em_before, :reactor_running, :amqp_not_connected }
189
+ em_after { hook :amqp_context_em_after, :reactor_running, :amqp_not_connected }
190
+ amqp_before { hook :context_amqp_before, :reactor_running, :amqp_connected }
191
+ amqp_after { hook :context_amqp_after, :reactor_running, :amqp_connected }
165
192
 
166
193
  after { @hooks_called.should == [:before,
167
194
  :amqp_context_before,
168
195
  :em_before,
169
196
  :amqp_context_em_before,
197
+ :amqp_before,
198
+ :context_amqp_before,
199
+ :context_amqp_after,
200
+ :amqp_after,
170
201
  :amqp_context_em_after,
171
- :em_after] ; $debug = nil}
202
+ :em_after]
203
+ hook :reactor_not_running, :amqp_not_connected }
172
204
 
173
205
  it_should_behave_like 'hooked amqp specs'
174
206
 
@@ -177,7 +209,9 @@ describe AMQP::SpecHelper, ".em_before/.em_after" do
177
209
  @hooks_called.should == [:before,
178
210
  :amqp_context_before,
179
211
  :em_before,
180
- :amqp_context_em_before]
212
+ :amqp_context_em_before,
213
+ :amqp_before,
214
+ :context_amqp_before]
181
215
  done
182
216
  end
183
217
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 3
8
- - 3
9
- version: 0.3.3
8
+ - 4
9
+ version: 0.3.4
10
10
  platform: ruby
11
11
  authors:
12
12
  - Arvicco
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-11-24 00:00:00 +03:00
17
+ date: 2010-11-26 00:00:00 +03:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency