amqp-spec 0.2.7 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|