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 +8 -32
- data/README.rdoc +84 -70
- data/VERSION +1 -1
- data/lib/amqp-spec.rb +1 -1
- data/lib/amqp-spec/amqp.rb +5 -4
- data/lib/amqp-spec/evented_example.rb +160 -0
- data/lib/amqp-spec/rspec.rb +91 -138
- data/spec/em_hooks_spec.rb +156 -0
- data/spec/failing_rspec_spec.rb +1 -5
- data/spec/problematic_rspec_spec.rb +2 -3
- data/spec/rspec_amqp_spec.rb +3 -3
- data/spec/rspec_em_spec.rb +1 -1
- data/spec/shared_examples.rb +16 -17
- data/spec/spec_helper.rb +4 -0
- metadata +7 -9
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.
|
66
|
-
|
67
|
-
* Documentation cleaned up
|
68
|
-
|
69
|
-
== 0.2.4 / 2010-11-01
|
45
|
+
== 0.2.7 / 2010-11-04
|
70
46
|
|
71
|
-
*
|
47
|
+
* Make AMQP.cleanup_state more thorough
|
72
48
|
|
73
|
-
== 0.2.
|
49
|
+
== 0.2.8 / 2010-11-15
|
74
50
|
|
75
|
-
*
|
51
|
+
* syncronize method added for wrapping async calls
|
76
52
|
|
77
|
-
== 0.2.
|
53
|
+
== 0.2.9 / 2010-11-16
|
78
54
|
|
79
|
-
*
|
55
|
+
* Fallback to a separate default timeout
|
80
56
|
|
81
|
-
== 0.
|
57
|
+
== 0.3.0 / 2010-11-16
|
82
58
|
|
83
|
-
*
|
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]
|
12
|
-
notoriously difficult to test. To the point that many people recommend using either
|
13
|
-
Mocks[http://github.com/danielsdeleo/moqueue] or {synchronous
|
14
|
-
instead of EM-based libraries in unit tests. This is not always an option, however -
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
33
|
-
You then use either 'amqp' or 'em' methods to wrap your evented
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
39
|
-
|
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 =>
|
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
|
88
|
-
examples run inside an amqp block automatically. A word of caution about
|
89
|
-
groups including AMQP::Spec. Each of these hooks
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
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
|
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
|
126
|
-
inside em block instead of amqp. before
|
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
|
161
|
-
methods are turned into real callbacks, intended to fire some time later.
|
162
|
-
the actual execution path of your code, when your blocks
|
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
|
178
|
-
your subscribe block, then you publish into this queue, then you call done. What may
|
179
|
-
Well, if you happen to use this spec against live AMQP broker, everything
|
180
|
-
First, communication delays. There is no guarantee that by the time you publish
|
181
|
-
have been either created or subscribed to. There is also no guarantee
|
182
|
-
the message by the time you are unsubscribing and deleting your
|
183
|
-
Second, sequence of your blocks. Remember, they are delayed callbacks! Don't just
|
184
|
-
block is already executed when you start your new asynchronous action.
|
185
|
-
it stops everything before your subscribe callback even
|
186
|
-
PASSING spec even though your expectation
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
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
|
212
|
-
test unit and bacon support, I just do not have experience dealing with
|
213
|
-
it uses native Fibers and therefore not compatible with
|
214
|
-
1.8
|
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.
|
1
|
+
0.3.0
|
data/lib/amqp-spec.rb
CHANGED
data/lib/amqp-spec/amqp.rb
CHANGED
@@ -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
|
30
|
-
@closing
|
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
|
data/lib/amqp-spec/rspec.rb
CHANGED
@@ -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
|
10
|
-
# to test
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
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
|
16
|
-
# inside AMQP.start loop without the need to explicitly call 'amqp'. In order to
|
17
|
-
# to AMQP loop, default_options
|
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
|
24
|
-
#
|
25
|
-
#
|
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
|
-
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
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
|
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?
|
46
|
-
# Hacking in metadata into RSpec1 to imitate Rspec2's metadata.
|
47
|
-
#
|
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
|
59
|
-
metadata[:
|
60
|
-
metadata[:
|
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
|
67
|
-
metadata[:
|
68
|
-
metadata[:
|
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
|
87
|
+
def self.included example_group
|
73
88
|
unless example_group.respond_to? :default_timeout
|
74
|
-
example_group.extend
|
75
|
-
example_group.metadata[:
|
76
|
-
example_group.metadata[:
|
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
|
-
#
|
82
|
-
#
|
83
|
-
# * :
|
84
|
-
# * :
|
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 =>
|
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
|
90
|
-
# if something goes wrong and EM/AMQP loop hangs for some
|
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
|
-
|
95
|
-
|
96
|
-
|
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
|
119
|
-
# if something goes wrong and EM/AMQP loop hangs for some
|
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
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
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
|
160
|
-
|
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
|
-
#
|
141
|
+
# Manually sets timeout for currently running example
|
184
142
|
#
|
185
|
-
def
|
186
|
-
@
|
143
|
+
def timeout *args
|
144
|
+
@evented_example.timeout *args
|
187
145
|
end
|
188
146
|
|
189
|
-
|
147
|
+
end # module SpecHelper
|
190
148
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
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
|
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"
|
data/spec/failing_rspec_spec.rb
CHANGED
@@ -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
|
data/spec/rspec_amqp_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
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}
|
31
|
-
|
30
|
+
puts "Default timeout: #{default_timeout}"
|
31
|
+
puts "Default options :#{default_options}"
|
32
32
|
|
33
33
|
it_should_behave_like 'SpecHelper examples'
|
34
34
|
|
data/spec/rspec_em_spec.rb
CHANGED
data/spec/shared_examples.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
135
|
-
|
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
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
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
|
-
|
144
|
-
|
144
|
+
context 'deeply embedded context can set up separate defaults' do
|
145
|
+
default_timeout 0.5
|
145
146
|
|
146
|
-
|
147
|
-
|
148
|
-
|
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
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
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
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-
|
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
|