amqp-spec 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/HISTORY ADDED
@@ -0,0 +1,19 @@
1
+ == 0.0.0 / 2010-10-13
2
+
3
+ * Birthday!
4
+
5
+ == 0.0.1 / 2010-10-13
6
+
7
+ * Initial code import from EM-Spec and AMQPHelper
8
+
9
+ == 0.0.2 / 2010-10-13
10
+
11
+ * Minimal functionality implemented
12
+
13
+ == 0.0.3 / 2010-10-13
14
+
15
+ * Bunch of non-working specs added
16
+
17
+ == 0.0.4 / 2010-10-14
18
+
19
+ * Problems with default_options resolved
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Arvicco
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,169 @@
1
+ = amqp-spec
2
+ by: Arvicco
3
+ url: http://github.com/arvicco/amqp-spec
4
+
5
+ == Summary
6
+
7
+ Simple API for testing asynchronous EventMachine/AMQP code.
8
+
9
+ == Description
10
+
11
+ EventMachine-based code, including synchronous {AMQP library}[http://github.com/tmm1/amqp] is
12
+ notoriously difficult to test. To the point that many people recommend using either
13
+ Mocks[http://github.com/danielsdeleo/moqueue] or {synchronous libraries}[http://github.com/celldee/bunny]
14
+ instead of EM-based libraries in unit tests. This is not always an option, however - sometimes your code
15
+ is supposed to run inside event loop, and you want to test a real thing, not mocks.
16
+
17
+ EM-Spec[http://github.com/tmm1/em-spec] gem made it easier to write evented specs, but it has several drawbacks.
18
+ First, it is not easy to manage both EM.run and AMQP.start loops at the same time. Second, AMQP is not
19
+ properly stopped and deactivated upon exceptions and timeouts, resulting in AMQP library state leak
20
+ between examples and multiple mystereous failures.
21
+
22
+ AMQP-Spec is built upon EM-Spec code but makes it easier to test AMQP event loops specifically. API is
23
+ very similar to EM-Spec, only a bit extended. The final goal is to make writing AMQP specs reasonably
24
+ pleasant experience and dispel the notion that evented AMQP-based libs are impossible to unit-test.
25
+
26
+ Mind you, you still have to properly manage your AMQP broker in order to prevent broker state from leaking
27
+ between examples. You can try to combine AMQP-Spec and Moqueue[http://github.com/danielsdeleo/moqueue]
28
+ if you want to abstract away actual broker interactions, but still specify some event-based expectations.
29
+
30
+ ==Rspec
31
+ There are several ways to use amqp-spec. To use it as a helper, include AMQP::SpecHelper in your describe block.
32
+ You then use either 'amqp' or 'em' methods to wrap your evented test code. Inside the amqp/em block, you must call
33
+ #done after your expectations. Everything works normally otherwise. You can use default_timeout and default_options
34
+ macros to avoid manually setting AMQP options in each example. However, if you DO manually set options inside
35
+ the example, they override the defaults. Only one set of default options and default timeout is used across groups,
36
+ it is not possible to have separate defaults for separate groups.
37
+
38
+ require "amqp-spec/rspec"
39
+ describe AMQP do
40
+ include AMQP::SpecHelper
41
+
42
+ before(:each) do
43
+ puts EM.reactor_running?
44
+ end
45
+
46
+ it "works normally when not using #amqp or #em" do
47
+ 1.should == 1
48
+ end
49
+
50
+ it "makes testing evented code easy with #em" do
51
+ em do
52
+ start = Time.now
53
+
54
+ EM.add_timer(0.5){
55
+ (Time.now-start).should be_close( 0.5, 0.1 )
56
+ done
57
+ }
58
+ end
59
+ end
60
+
61
+ it "runs AMQP.start loop with options given to #amqp" do
62
+ amqp(:host => 'my.amqp.broker.org', :port => '21118')do
63
+ AMQP.conn.should be_connected
64
+ done
65
+ end
66
+ end
67
+
68
+ it "optionally raises timeout exception if your loop hangs for some reason" do
69
+ proc {
70
+ amqp(:spec_timeout => 3){}
71
+ }.should raise_error SpecTimeoutExceededError
72
+ end
73
+
74
+ default_timeout 1 # Can be used to set default :spec_timeout for all your amqp-based specs
75
+ end
76
+
77
+ Another option is to include AMQP::Spec in your describe block. This will patch Rspec so that all of your
78
+ examples run inside an amqp block automatically. A word of caution about before{} and after{} hooks in your example
79
+ groups including AMQP::Spec. Each of these hooks will run in its separate EM loop that you'll need to shut down
80
+ either manually (done) or via default_timeout. Essentially, this means that any EM-related state that you'd like
81
+ to setup/teardown using these hooks will be lost as each example will run in a separate thread. In order to
82
+ run setup/teardown hooks inside the EM loop, you'll need to use before_amqp{} and after_amqp{} hooks that run
83
+ inside the EM loop but before/after AMQP loop (these hooks are currently not implemented)
84
+
85
+ require "amqp-spec/rspec"
86
+ describe AMQP do
87
+ include AMQP::Spec
88
+
89
+ before(:each) do
90
+ puts EM.reactor_running?
91
+ done
92
+ end
93
+
94
+ default_options :host => 'my.amqp.broker.org', :port => '21118'
95
+ # Can be used to set default options for all your (implied) amqp{} event loops
96
+
97
+ it "requires a call to #done in every example" do
98
+ 1.should == 1
99
+ done
100
+ end
101
+
102
+ it "runs test code in an amqp block automatically" do
103
+ start = Time.now
104
+
105
+ EM.add_timer(0.5){
106
+ (Time.now-start).should be_close( 0.5, 0.1 )
107
+ done
108
+ }
109
+ end
110
+
111
+ it "runs AMQP.start loop with default_options" do
112
+ AMQP.conn.should be_connected
113
+ done
114
+ end
115
+
116
+ it "raises timeout exception ONLY if default_timeout was set" do
117
+ proc{}.should raise_error SpecTimeoutExceededError
118
+ end
119
+ end
120
+
121
+ Finally, you can include AMQP::EMSpec in your describe block. This will run all the group examples
122
+ inside em block instead of amqp. before{} and after{} hooks should be finished with 'done', same as
123
+ when including AMQP::Spec, and same caution about using them applies.
124
+
125
+ require "amqp-spec/rspec"
126
+ describe AMQP do
127
+ include AMQP::EMSpec
128
+
129
+ it "requires a call to #done in every example" do
130
+ 1.should == 1
131
+ done
132
+ end
133
+
134
+ it "runs test code in an amqp block automatically" do
135
+ start = Time.now
136
+
137
+ EM.add_timer(0.5){
138
+ (Time.now-start).should be_close( 0.5, 0.1 )
139
+ done
140
+ }
141
+ end
142
+
143
+ default_timeout 1
144
+ # Default spec timeouts can be used same as with AMQP::Spec, default_options (if defined) are not used
145
+ end
146
+
147
+
148
+ ==Bacon
149
+
150
+ ...
151
+
152
+ ==Test::Unit
153
+
154
+ ...
155
+
156
+ ==Limitations
157
+
158
+ AMQP-Spec can be currently used with rspec only. I suppose, there is nothing special in extending EM-Spec's
159
+ test unit and bacon support, I just do not have experience dealing with these platforms. Another limitation,
160
+ it uses native Fibers and therefore not compatible with Ruby 1.8. Again, it seems possible to rewrite it in
161
+ 1.8-compatible style, with string evals and Fiber backport, but I'd rather leave this work to someone else.
162
+
163
+ Any help improving this library is greatly appreciated...
164
+
165
+ == LICENSE:
166
+ Copyright (c) 2010 Arvicco.
167
+ Original EM-Spec code copyright (c) 2008 Aman Gupta (tmm1)
168
+
169
+ See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ require 'pathname'
2
+ NAME = 'amqp-spec'
3
+ BASE_PATH = Pathname.new(__FILE__).dirname
4
+ LIB_PATH = BASE_PATH + 'lib'
5
+ PKG_PATH = BASE_PATH + 'pkg'
6
+ DOC_PATH = BASE_PATH + 'rdoc'
7
+
8
+ $LOAD_PATH.unshift LIB_PATH.to_s
9
+ require 'version'
10
+
11
+ CLASS_NAME = AMQP::Spec
12
+ VERSION = CLASS_NAME::VERSION
13
+
14
+ begin
15
+ require 'rake'
16
+ rescue LoadError
17
+ require 'rubygems'
18
+ gem 'rake', '~> 0.8.3.1'
19
+ require 'rake'
20
+ end
21
+
22
+ # Load rakefile tasks
23
+ Dir['tasks/*.rake'].sort.each { |file| load file }
24
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.4
data/lib/amqp-spec.rb ADDED
@@ -0,0 +1,27 @@
1
+ require 'version'
2
+
3
+ module AMQP
4
+ module Spec
5
+
6
+ # require "bundler"
7
+ # Bundler.setup
8
+
9
+ # Requires ruby source file(s). Accepts either single filename/glob or Array of filenames/globs.
10
+ # Accepts following options:
11
+ # :*file*:: Lib(s) required relative to this file - defaults to __FILE__
12
+ # :*dir*:: Required lib(s) located under this dir name - defaults to gem name
13
+ #
14
+ def self.require_libs(libs, opts={})
15
+ file = Pathname.new(opts[:file] || __FILE__)
16
+ [libs].flatten.each do |lib|
17
+ name = file.dirname + (opts[:dir] || file.basename('.*')) + lib.gsub(/(?<!.rb)$/, '.rb')
18
+ Pathname.glob(name.to_s).sort.each { |rb| require rb }
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ # Require all ruby source files located under directory lib/amqp-spec
25
+ # If you need files in specific order, you should specify it here before the glob
26
+ AMQP::Spec.require_libs %W[**/*]
27
+
@@ -0,0 +1,195 @@
1
+ require 'mq'
2
+ require 'fiber' unless Fiber.respond_to?(:current)
3
+
4
+ # You can include one of the following modules into your example groups:
5
+ # AMQP::SpecHelper
6
+ # AMQP::Spec
7
+ #
8
+ # AMQP::SpecHelper module defines 'ampq' method that can be safely used inside your specs(examples)
9
+ # to test expectations inside running AMQP.start loop. Each loop is running in a separate Fiber,
10
+ # and you can control for timeouts using either :spec_timeout option given to amqp method,
11
+ # or setting default timeout with class method default_timeout(timeout).
12
+ #
13
+ # If you include AMQP::Spec module into your example group, each example of this group will run
14
+ # inside AMQP.start loop without the need to explicitly call 'amqp'. In order to provide options
15
+ # to AMQP loop, default_options class method is defined. Remember, when using AMQP::Specs, you
16
+ # will have a single set of AMQP.start options for all your examples.
17
+ #
18
+ # In order to stop AMQP loop, you should call 'done' AFTER you are sure that your example is finished.
19
+ # For example, if you are using subscribe block that tests expectations on messages, 'done' should be
20
+ # probably called at the end of this block.
21
+ #
22
+ # TODO: Define 'async' method wrapping async requests and returning results... 'async_loop' too for subscribe?
23
+ # TODO: 'evented_before', 'evented_after' that will be run inside EM before the example
24
+ module AMQP
25
+ module SpecHelper
26
+
27
+ SpecTimeoutExceededError = Class.new(RuntimeError)
28
+
29
+ def self.included(example_group)
30
+ ::Spec::Example::ExampleGroup.instance_exec do
31
+ unless defined? default_spec_timeout
32
+
33
+ @@_em_default_options = {}
34
+ @@_em_default_timeout = nil
35
+
36
+ def self.default_spec_timeout(spec_timeout=nil)
37
+ if spec_timeout
38
+ @@_em_default_timeout = spec_timeout
39
+ else
40
+ @@_em_default_timeout
41
+ end
42
+ end
43
+ alias default_timeout default_spec_timeout
44
+
45
+ def self.default_options(opts=nil)
46
+ if opts
47
+ @@_em_default_options = opts
48
+ else
49
+ @@_em_default_options
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ # Yields to given block inside EM.run and AMQP.start loops. This method takes any option that is
57
+ # also accepted by EventMachine::connect. Also, options for AMQP.start include:
58
+ # * :user => String (default ‘guest’) - The username as defined by the AMQP server.
59
+ # * :pass => String (default ‘guest’) - The password for the associated :user as defined by the AMQP server.
60
+ # * :vhost => String (default ’/’) - The virtual host as defined by the AMQP server.
61
+ # * :timeout => Numeric (default nil) - *Connection* timeout, measured in seconds.
62
+ # * :logging => true | false (default false) - Toggle the extremely verbose AMQP logging.
63
+ #
64
+ # In addition to EM and AMQP options, :spec_timeout option (in seconds) is used to force spec to timeout
65
+ # if something goes wrong and EM/AMQP loop hangs for some reason. SpecTimeoutExceededError is raised.
66
+
67
+ def amqp opts={}, &block
68
+ opts = @@_em_default_options.merge opts
69
+ EM.run do
70
+ # begin ?
71
+ @_em_spec_with_amqp = true
72
+ @_em_spec_exception = nil
73
+ spec_timeout = opts.delete(:spec_timeout) || @@_em_default_timeout
74
+ timeout(spec_timeout) if spec_timeout
75
+ @_em_spec_fiber = Fiber.new do
76
+ begin
77
+ amqp_start opts, &block
78
+ rescue Exception => @_em_spec_exception
79
+ p @_em_spec_exception
80
+ done
81
+ end
82
+ Fiber.yield
83
+ end
84
+
85
+ @_em_spec_fiber.resume
86
+ # raise @_em_spec_exception if @_em_spec_exception
87
+ end
88
+ end
89
+
90
+ # Yields to block inside EM loop, :spec_timeout option (in seconds) is used to force spec to timeout
91
+ # if something goes wrong and EM/AMQP loop hangs for some reason. SpecTimeoutExceededError is raised.
92
+ # TODO: accept :spec_timeout =>1 as a Hash for compatibility with amqp interface
93
+ def em(spec_timeout = @@_em_default_timeout, &block)
94
+ EM.run do
95
+ @_em_spec_with_amqp = false
96
+ @_em_spec_exception = nil
97
+ timeout(spec_timeout) if spec_timeout
98
+ @_em_spec_fiber = Fiber.new do
99
+ begin
100
+ block.call
101
+ rescue Exception => @_em_spec_exception
102
+ done
103
+ end
104
+ Fiber.yield
105
+ end
106
+
107
+ @_em_spec_fiber.resume
108
+ end
109
+ end
110
+
111
+ # Sets timeout for current spec
112
+ def timeout(spec_timeout)
113
+ EM.cancel_timer(@_em_timer) if @_em_timer
114
+ @_em_timer = EM.add_timer(spec_timeout) do
115
+ @_em_spec_exception = SpecTimeoutExceededError.new
116
+ done
117
+ end
118
+ end
119
+
120
+ # Stops AMQP and EM event loop
121
+ def done
122
+ EM.next_tick do
123
+ if @_em_spec_with_amqp
124
+ amqp_stop(@_em_spec_exception) do
125
+ finish_em_spec_fiber
126
+ end
127
+ else
128
+ finish_em_spec_fiber
129
+ raise @_em_spec_exception if @_em_spec_exception
130
+ end
131
+ end
132
+ end
133
+
134
+ private
135
+
136
+ def finish_em_spec_fiber
137
+ EM.stop_event_loop if EM.reactor_running?
138
+ # p Thread.current, Thread.current[:mq], __LINE__
139
+ @_em_spec_fiber.resume if @_em_spec_fiber.alive?
140
+ end
141
+
142
+ # Private method that initializes AMQP client/connection without starting another EM loop
143
+ def amqp_start opts={}, &block
144
+ AMQP.instance_exec do
145
+ # p Thread.current, Thread.current[:mq]
146
+ puts "!!!!!!!!! Existing connection: #{@conn}" if @conn
147
+ @conn = connect opts
148
+ # @conn ||= connect opts
149
+ @conn.callback(&block) if block
150
+ end
151
+ end
152
+
153
+ # Private method that closes AMQP connection and raises optional
154
+ # exception AFTER the AMQP connection is 100% closed
155
+ def amqp_stop exception
156
+ if AMQP.conn and not AMQP.closing
157
+ AMQP.instance_exec do #(@_em_spec_exception) do |exception|
158
+ @closing = true
159
+ @conn.close {
160
+ yield if block_given?
161
+ @conn = nil
162
+ @closing = false
163
+ raise exception if exception
164
+ }
165
+ end
166
+ end
167
+ end
168
+ end
169
+
170
+ module Spec
171
+ def self.included(cls)
172
+ cls.send(:include, SpecHelper)
173
+ end
174
+
175
+ def instance_eval(&block)
176
+ amqp do
177
+ super(&block)
178
+ end
179
+ end
180
+ end
181
+
182
+ module EMSpec
183
+ def self.included(cls)
184
+ cls.send(:include, SpecHelper)
185
+ end
186
+
187
+ def instance_eval(&block)
188
+ em do
189
+ super(&block)
190
+ end
191
+ end
192
+ end
193
+ end
194
+
195
+
data/lib/version.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'pathname'
2
+
3
+ module AMQP
4
+ module Spec
5
+
6
+ VERSION_FILE = Pathname.new(__FILE__).dirname + '../VERSION' # :nodoc:
7
+ VERSION = VERSION_FILE.exist? ? VERSION_FILE.read.strip : nil
8
+
9
+ end
10
+ end
data/spec/amqp.yml ADDED
@@ -0,0 +1,28 @@
1
+ # AMQP client configuration file
2
+
3
+ # These values will be used to configure the ampq gem, any values
4
+ # omitted will let the gem use it's own defaults.
5
+
6
+ # The configuration specifies the following keys:
7
+ # * user - Username for the broker
8
+ # * pass - Password for the broker
9
+ # * host - Hostname where the broker is running
10
+ # * vhost - Vhost to connect to
11
+ # * port - Port where the broker is running
12
+ # * ssl - Use ssl or not
13
+ # * timeout - Timeout
14
+
15
+ defaults: &defaults
16
+ user: guest
17
+ pass: guest
18
+ host: 10.211.55.2
19
+ vhost: /
20
+
21
+ development:
22
+ <<: *defaults
23
+
24
+ test:
25
+ <<: *defaults
26
+
27
+ production:
28
+ <<: *defaults
@@ -0,0 +1,62 @@
1
+ require_relative 'spec_helper.rb'
2
+
3
+ context 'Following examples should all be failing:' do
4
+ describe EventMachine, " when running failing examples" do
5
+ include AMQP::EMSpec
6
+
7
+ it "should not bubble failures beyond rspec" do
8
+ EM.add_timer(0.1) do
9
+ :should_not_bubble.should == :failures
10
+ done
11
+ end
12
+ end
13
+
14
+ it "should not block on failure" do
15
+ 1.should == 2
16
+ end
17
+ end
18
+
19
+ describe EventMachine, " when testing with AMQP::EMSpec with a maximum execution time per test" do
20
+
21
+ include AMQP::EMSpec
22
+ it 'should timeout before reaching done' do
23
+ EM.add_timer(2) {
24
+ done
25
+ }
26
+ end
27
+
28
+ it 'should timeout before reaching done' do
29
+ timeout(0.3)
30
+ EM.add_timer(0.6) {
31
+ done
32
+ }
33
+ end
34
+ end
35
+
36
+ describe AMQP, " when testing with AMQP::Spec with a maximum execution time per test" do
37
+
38
+ include AMQP::Spec
39
+
40
+ default_timeout 1
41
+
42
+ it 'should timeout before reaching done' do
43
+ EM.add_timer(2) {
44
+ done
45
+ }
46
+ end
47
+
48
+ it 'should timeout before reaching done' do
49
+ timeout(0.2)
50
+ EM.add_timer(0.5) {
51
+ done
52
+ }
53
+ end
54
+
55
+ it 'should fail due to timeout, not hang up' do
56
+ timeout(0.2)
57
+ end
58
+
59
+ it 'should fail due to default timeout, not hang up' do
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,273 @@
1
+ require_relative 'spec_helper.rb'
2
+
3
+ describe 'Rspec' do
4
+ it 'should work as normal without AMQP-Spec' do
5
+ 1.should == 1
6
+ end
7
+ end
8
+
9
+ context 'Evented AMQP specs' do
10
+ describe AMQP, " when testing with AMQP::SpecHelper" do
11
+ include AMQP::SpecHelper
12
+ after(:each) do
13
+ EM.reactor_running?.should == false
14
+ AMQP.conn.should be_nil
15
+ end
16
+
17
+ default_options AMQP_OPTS if defined? AMQP_OPTS
18
+ default_timeout 1 # Can be used to set default :spec_timeout for all your amqp-based specs
19
+
20
+ p default_timeout, default_options
21
+
22
+ it "should not require a call to done when #em/#amqp is not used" do
23
+ 1.should == 1
24
+ end
25
+
26
+ it "should properly work" do
27
+ amqp do
28
+ done
29
+ end
30
+ end
31
+
32
+ it "should have timers" do
33
+ amqp do
34
+ start = Time.now
35
+
36
+ EM.add_timer(0.5) {
37
+ (Time.now-start).should be_close(0.5, 0.1)
38
+ done
39
+ }
40
+ end
41
+ end
42
+
43
+ it "should run AMQP.start loop with options given to #amqp" do
44
+ amqp(:vhost => '/', :user => 'guest') do
45
+ AMQP.conn.should be_connected
46
+ done
47
+ end
48
+ end
49
+
50
+ it "should properly close AMQP connection if block completes normally" do
51
+ amqp do
52
+ AMQP.conn.should be_connected
53
+ done
54
+ end
55
+ AMQP.conn.should be_nil
56
+ end
57
+
58
+ it 'should have deferrables' do
59
+ amqp do
60
+ defr = EM::DefaultDeferrable.new
61
+ defr.timeout(0.5)
62
+ defr.errback {
63
+ done
64
+ }
65
+ end
66
+ end
67
+
68
+ context 'on exceptions/failures' do
69
+ # default_timeout 1 # Can be used to set default :spec_timeout for all your amqp-based specs
70
+
71
+
72
+ end
73
+ end
74
+
75
+ describe AMQP, " when testing with AMQP::Spec" do
76
+ include AMQP::Spec
77
+ after(:each) do
78
+ EM.reactor_running?.should == true
79
+ done
80
+ end
81
+
82
+ it 'should work' do
83
+ done
84
+ end
85
+
86
+ it 'should have timers' do
87
+ start = Time.now
88
+
89
+ EM.add_timer(0.5) {
90
+ (Time.now-start).should be_close(0.5, 0.1)
91
+ done
92
+ }
93
+ end
94
+
95
+ it 'should have periodic timers' do
96
+ num = 0
97
+ start = Time.now
98
+
99
+ timer = EM.add_periodic_timer(0.25) {
100
+ if (num += 1) == 2
101
+ (Time.now-start).should be_close(0.5, 0.1)
102
+ EM.cancel_timer timer
103
+ done
104
+ end
105
+ }
106
+ end
107
+
108
+ it 'should have deferrables' do
109
+ defr = EM::DefaultDeferrable.new
110
+ defr.timeout(0.5)
111
+ defr.errback {
112
+ done
113
+ }
114
+ end
115
+ end
116
+
117
+ shared_examples_for 'timeout examples' do
118
+ include AMQP::SpecHelper
119
+ before(:each) { @start = Time.now }
120
+
121
+ it 'should timeout before reaching done because of default spec timeout' do
122
+ proc {
123
+ amqp do
124
+ EM.add_timer(2) { done }
125
+ end
126
+ }.should raise_error SpecTimeoutExceededError
127
+ (Time.now-@start).should be_close(1.0, 0.1)
128
+ end
129
+
130
+ it 'should timeout before reaching done because of explicit in-loop timeout' do
131
+ proc {
132
+ amqp do
133
+ timeout(0.2)
134
+ EM.add_timer(0.5) { done }
135
+ end
136
+ }.should raise_error SpecTimeoutExceededError
137
+ (Time.now-@start).should be_close(0.2, 0.1)
138
+ end
139
+
140
+ specify "spec timeout given in amqp options has higher priority than default" do
141
+ proc {
142
+ amqp(:spec_timeout => 0.2) {}
143
+ }.should raise_error SpecTimeoutExceededError
144
+ (Time.now-@start).should be_close(0.2, 0.1)
145
+ end
146
+
147
+ specify "but timeout call inside amqp loop has even higher priority" do
148
+ proc {
149
+ amqp(:spec_timeout => 0.5) { timeout(0.2) }
150
+ }.should raise_error SpecTimeoutExceededError
151
+ (Time.now-@start).should be_close(0.2, 0.1)
152
+ end
153
+
154
+ specify "AMQP connection should not leak between examples" do
155
+ AMQP.conn.should be_nil
156
+ end
157
+ end
158
+
159
+ describe AMQP, " when testing with AMQP::SpecHelper with spec timeouts" do
160
+ it_should_behave_like 'timeout examples'
161
+
162
+ context 'inside embedded context / example group' do
163
+ it_should_behave_like 'timeout examples'
164
+ # it "should properly timeout " do
165
+ # amqp do
166
+ # timeout(0.2)
167
+ # end
168
+ # end
169
+ # it "should properly timeout inside embedded context/describe blocks" do
170
+ # amqp do
171
+ # end
172
+ # end
173
+ end
174
+ end
175
+
176
+ # PROBLEMATIC !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
177
+ describe AMQP, " when testing with AMQP::SpecHelper with spec timeouts" do
178
+ include AMQP::SpecHelper
179
+
180
+ it "should fail to provide context to next spec" do
181
+ begin
182
+ amqp do
183
+ :this.should == :fail
184
+ end
185
+ rescue => e
186
+ p e
187
+ end
188
+ end
189
+
190
+ it "should properly close AMQP connection after Rspec failures" do
191
+ AMQP.conn.should == nil
192
+ end
193
+ end
194
+
195
+ # describe MQ, " when MQ.queue or MQ.fanout etc is trying to access Thread-local mq across examples" do
196
+ # include AMQP::SpecHelper
197
+ #
198
+ # default_timeout 1
199
+ #
200
+ # it 'sends data to queue' do
201
+ # amqp do
202
+ # q = MQ.new.queue("test_sink")
203
+ # q.subscribe do |hdr, data|
204
+ # p hdr, data
205
+ # EM.next_tick {
206
+ # q.unsubscribe; q.delete
207
+ # AMQP.stop { EM.stop_event_loop }
208
+ # }
209
+ # end
210
+ # EM.add_timer(0.2) do
211
+ # p Thread.current, Thread.current[:mq]
212
+ # MQ.queue('test_sink').publish 'data' # MQ.new. !!!!!!!!!!!
213
+ # end
214
+ # end
215
+ # end
216
+ #
217
+ # it 'sends data to queue' do
218
+ # amqp do
219
+ # q = MQ.new.queue("test_sink")
220
+ # q.subscribe do |hdr, data|
221
+ # p hdr, data
222
+ # EM.next_tick {
223
+ # q.unsubscribe; q.delete
224
+ # AMQP.stop { EM.stop_event_loop }
225
+ # }
226
+ # end
227
+ # EM.add_timer(0.2) do
228
+ # p Thread.current, Thread.current[:mq]
229
+ # MQ.queue('test_sink').publish 'data' # MQ.new. !!!!!!!!!!!
230
+ # end
231
+ # end
232
+ # end
233
+ #
234
+ # end
235
+ end
236
+
237
+ context '!!!!!!!!!!! LEAKING !!!!!!!!!!!!!!!!!!' do
238
+ describe EventMachine, " when running failing examples" do
239
+ include AMQP::SpecHelper
240
+
241
+ it "should not bubble failures beyond rspec" do
242
+ amqp do
243
+ EM.add_timer(0.1) do
244
+ :should_not_bubble.should == :failures
245
+ done
246
+ end
247
+ end
248
+ AMQP.conn.should == nil
249
+ end
250
+
251
+ it "should not block on failure" do
252
+ 1.should == 2
253
+ end
254
+ end
255
+
256
+ describe EventMachine, " when testing with AMQP::Spec with a maximum execution time per test" do
257
+
258
+ include AMQP::Spec
259
+
260
+ it 'should timeout before reaching done' do
261
+ EM.add_timer(2) {
262
+ done
263
+ }
264
+ end
265
+ end
266
+ end
267
+
268
+
269
+ describe "Rspec", " when running an example group after another group that uses AMQP-Spec " do
270
+ it "should work normally" do
271
+ :does_not_hang.should_not be_false
272
+ end
273
+ end
@@ -0,0 +1,67 @@
1
+ require_relative 'spec_helper.rb'
2
+
3
+ context 'Plain EM, no AMQP' do
4
+ describe EventMachine, " when testing with AMQP::SpecHelper" do
5
+ include AMQP::SpecHelper
6
+
7
+ it "should not require a call to done when #em is not used" do
8
+ 1.should == 1
9
+ end
10
+
11
+ it "should have timers" do
12
+ em do
13
+ start = Time.now
14
+
15
+ EM.add_timer(0.5) {
16
+ (Time.now-start).should be_close(0.5, 0.1)
17
+ done
18
+ }
19
+ end
20
+ end
21
+ end
22
+
23
+ describe EventMachine, " when testing with AMQP::Spec" do
24
+ include AMQP::EMSpec
25
+
26
+ it 'should work' do
27
+ done
28
+ end
29
+
30
+ it 'should have timers' do
31
+ start = Time.now
32
+
33
+ EM.add_timer(0.5) {
34
+ (Time.now-start).should be_close(0.5, 0.1)
35
+ done
36
+ }
37
+ end
38
+
39
+ it 'should have periodic timers' do
40
+ num = 0
41
+ start = Time.now
42
+
43
+ timer = EM.add_periodic_timer(0.2) {
44
+ if (num += 1) == 2
45
+ (Time.now-start).should be_close(0.4, 0.1)
46
+ EM.__send__ :cancel_timer, timer
47
+ done
48
+ end
49
+ }
50
+ end
51
+
52
+ it 'should have deferrables' do
53
+ defr = EM::DefaultDeferrable.new
54
+ defr.timeout(0.5)
55
+ defr.errback {
56
+ done
57
+ }
58
+ end
59
+
60
+ end
61
+ end
62
+
63
+ describe "Rspec", " when running an example group after groups that uses EM specs " do
64
+ it "should work normally" do
65
+ :does_not_hang.should_not be_false
66
+ end
67
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format nested
@@ -0,0 +1,35 @@
1
+ #$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+
3
+ $LOAD_PATH << "." unless $LOAD_PATH.include? "." # moronic 1.9.2 breaks things bad
4
+
5
+ require 'spec'
6
+ require 'yaml'
7
+
8
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'amqp-spec', 'rspec.rb')
9
+
10
+ amqp_config = File.dirname(__FILE__) + '/amqp.yml'
11
+ if File.exists? amqp_config
12
+
13
+ class Hash
14
+ def symbolize_keys
15
+ self.inject({}) { |result, (key, value)|
16
+ new_key = case key
17
+ when String then
18
+ key.to_sym
19
+ else
20
+ key
21
+ end
22
+ new_value = case value
23
+ when Hash then
24
+ value.symbolize_keys
25
+ else
26
+ value
27
+ end
28
+ result[new_key] = new_value
29
+ result
30
+ }
31
+ end
32
+ end
33
+
34
+ AMQP_OPTS = YAML::load_file(amqp_config).symbolize_keys[:test]
35
+ end
data/tasks/common.rake ADDED
@@ -0,0 +1,18 @@
1
+ #task :default => 'test:run'
2
+ #task 'gem:release' => 'test:run'
3
+
4
+ task :notes do
5
+ puts 'Output annotations (TBD)'
6
+ end
7
+
8
+ #Bundler not ready for prime time just yet
9
+ #desc 'Bundle dependencies'
10
+ #task :bundle do
11
+ # output = `bundle check 2>&1`
12
+ #
13
+ # unless $?.to_i == 0
14
+ # puts output
15
+ # system "bundle install"
16
+ # puts
17
+ # end
18
+ #end
data/tasks/doc.rake ADDED
@@ -0,0 +1,14 @@
1
+ desc 'Alias to doc:rdoc'
2
+ task :doc => 'doc:rdoc'
3
+
4
+ namespace :doc do
5
+ require 'rake/rdoctask'
6
+ Rake::RDocTask.new do |rdoc|
7
+ # Rake::RDocTask.new(:rdoc => "rdoc", :clobber_rdoc => "clobber", :rerdoc => "rerdoc") do |rdoc|
8
+ rdoc.rdoc_dir = DOC_PATH.basename.to_s
9
+ rdoc.title = "#{NAME} #{VERSION} Documentation"
10
+ rdoc.main = "README.doc"
11
+ rdoc.rdoc_files.include('README*')
12
+ rdoc.rdoc_files.include('lib/**/*.rb')
13
+ end
14
+ end
data/tasks/gem.rake ADDED
@@ -0,0 +1,40 @@
1
+ desc "Alias to gem:release"
2
+ task :release => 'gem:release'
3
+
4
+ desc "Alias to gem:install"
5
+ task :install => 'gem:install'
6
+
7
+ desc "Alias to gem:build"
8
+ task :gem => 'gem:build'
9
+
10
+ namespace :gem do
11
+ gem_file = "#{NAME}-#{VERSION}.gem"
12
+
13
+ desc "(Re-)Build gem"
14
+ task :build do
15
+ puts "Remove existing gem package"
16
+ rm_rf PKG_PATH
17
+ puts "Build new gem package"
18
+ system "gem build #{NAME}.gemspec"
19
+ puts "Move built gem to package dir"
20
+ mkdir_p PKG_PATH
21
+ mv gem_file, PKG_PATH
22
+ end
23
+
24
+ desc "Cleanup already installed gem(s)"
25
+ task :cleanup do
26
+ puts "Cleaning up installed gem(s)"
27
+ system "gem cleanup #{NAME}"
28
+ end
29
+
30
+ desc "Build and install gem"
31
+ task :install => :build do
32
+ system "gem install #{PKG_PATH}/#{gem_file}"
33
+ end
34
+
35
+ desc "Build and push gem to Gemcutter"
36
+ task :release => [:build, 'git:tag'] do
37
+ puts "Pushing gem to Gemcutter"
38
+ system "gem push #{PKG_PATH}/#{gem_file}"
39
+ end
40
+ end
data/tasks/git.rake ADDED
@@ -0,0 +1,34 @@
1
+ desc "Alias to git:commit"
2
+ task :git => 'git:commit'
3
+
4
+ namespace :git do
5
+
6
+ desc "Stage and commit your work [with message]"
7
+ task :commit, [:message] do |t, args|
8
+ puts "Staging new (unversioned) files"
9
+ system "git add --all"
10
+ if args.message
11
+ puts "Committing with message: #{args.message}"
12
+ system %Q[git commit -a -m "#{args.message}" --author arvicco]
13
+ else
14
+ puts "Committing"
15
+ system %Q[git commit -a -m "No message" --author arvicco]
16
+ end
17
+ end
18
+
19
+ desc "Push local changes to Github"
20
+ task :push => :commit do
21
+ puts "Pushing local changes to remote"
22
+ system "git push"
23
+ end
24
+
25
+ desc "Create (release) tag on Github"
26
+ task :tag => :push do
27
+ tag = VERSION
28
+ puts "Creating git tag: #{tag}"
29
+ system %Q{git tag -a -m "Release tag #{tag}" #{tag}}
30
+ puts "Pushing #{tag} to remote"
31
+ system "git push origin #{tag}"
32
+ end
33
+
34
+ end
data/tasks/spec.rake ADDED
@@ -0,0 +1,19 @@
1
+ desc 'Alias to spec:spec'
2
+ task :spec => 'spec:spec'
3
+
4
+ namespace :spec do
5
+ require 'spec/rake/spectask'
6
+
7
+ desc "Run all specs"
8
+ Spec::Rake::SpecTask.new(:spec) do |t|
9
+ t.spec_opts = ['--options', %Q{"#{BASE_PATH}/spec/spec.opts"}]
10
+ t.spec_files = FileList['spec/**/*_spec.rb']
11
+ end
12
+
13
+ desc "Run specs with RCov"
14
+ Spec::Rake::SpecTask.new(:rcov) do |t|
15
+ t.spec_files = FileList['spec/**/*_spec.rb']
16
+ t.rcov = true
17
+ t.rcov_opts = ['--exclude', 'spec']
18
+ end
19
+ end
@@ -0,0 +1,71 @@
1
+ class Version
2
+ attr_accessor :major, :minor, :patch, :build
3
+
4
+ def initialize(version_string)
5
+ raise "Invalid version #{version_string}" unless version_string =~ /^(\d+)\.(\d+)\.(\d+)(?:\.(.*?))?$/
6
+ @major = $1.to_i
7
+ @minor = $2.to_i
8
+ @patch = $3.to_i
9
+ @build = $4
10
+ end
11
+
12
+ def bump_major(x)
13
+ @major += x.to_i
14
+ @minor = 0
15
+ @patch = 0
16
+ @build = nil
17
+ end
18
+
19
+ def bump_minor(x)
20
+ @minor += x.to_i
21
+ @patch = 0
22
+ @build = nil
23
+ end
24
+
25
+ def bump_patch(x)
26
+ @patch += x.to_i
27
+ @build = nil
28
+ end
29
+
30
+ def update(major, minor, patch, build=nil)
31
+ @major = major
32
+ @minor = minor
33
+ @patch = patch
34
+ @build = build
35
+ end
36
+
37
+ def write(desc = nil)
38
+ CLASS_NAME::VERSION_FILE.open('w') {|file| file.puts to_s }
39
+ (BASE_PATH + 'HISTORY').open('a') do |file|
40
+ file.puts "\n== #{to_s} / #{Time.now.strftime '%Y-%m-%d'}\n"
41
+ file.puts "\n* #{desc}\n" if desc
42
+ end
43
+ end
44
+
45
+ def to_s
46
+ [major, minor, patch, build].compact.join('.')
47
+ end
48
+ end
49
+
50
+ desc 'Set version: [x.y.z] - explicitly, [1/10/100] - bump major/minor/patch, [.build] - build'
51
+ task :version, [:command, :desc] do |t, args|
52
+ version = Version.new(VERSION)
53
+ case args.command
54
+ when /^(\d+)\.(\d+)\.(\d+)(?:\.(.*?))?$/ # Set version explicitly
55
+ version.update($1, $2, $3, $4)
56
+ when /^\.(.*?)$/ # Set build
57
+ version.build = $1
58
+ when /^(\d{1})$/ # Bump patch
59
+ version.bump_patch $1
60
+ when /^(\d{1})0$/ # Bump minor
61
+ version.bump_minor $1
62
+ when /^(\d{1})00$/ # Bump major
63
+ version.bump_major $1
64
+ else # Unknown command, just display VERSION
65
+ puts "#{NAME} #{version}"
66
+ next
67
+ end
68
+
69
+ puts "Writing version #{version} to VERSION file"
70
+ version.write args.desc
71
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: amqp-spec
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 4
10
+ version: 0.0.4
11
+ platform: ruby
12
+ authors:
13
+ - Arvicco
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-14 00:00:00 +04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rspec
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 13
30
+ segments:
31
+ - 1
32
+ - 2
33
+ - 9
34
+ version: 1.2.9
35
+ type: :development
36
+ version_requirements: *id001
37
+ description: Simple API for writing (asynchronous) AMQP specs
38
+ email: arvitallian@gmail.com
39
+ executables: []
40
+
41
+ extensions: []
42
+
43
+ extra_rdoc_files:
44
+ - LICENSE
45
+ - HISTORY
46
+ - README.rdoc
47
+ files:
48
+ - lib/amqp-spec/rspec.rb
49
+ - lib/amqp-spec.rb
50
+ - lib/version.rb
51
+ - spec/amqp.yml
52
+ - spec/failing_rspec_spec.rb
53
+ - spec/rspec_amqp_spec.rb
54
+ - spec/rspec_em_spec.rb
55
+ - spec/spec.opts
56
+ - spec/spec_helper.rb
57
+ - tasks/common.rake
58
+ - tasks/doc.rake
59
+ - tasks/gem.rake
60
+ - tasks/git.rake
61
+ - tasks/spec.rake
62
+ - tasks/version.rake
63
+ - Rakefile
64
+ - README.rdoc
65
+ - LICENSE
66
+ - VERSION
67
+ - HISTORY
68
+ - .gitignore
69
+ has_rdoc: true
70
+ homepage: http://github.com/arvicco/amqp-spec
71
+ licenses: []
72
+
73
+ post_install_message:
74
+ rdoc_options:
75
+ - --charset
76
+ - UTF-8
77
+ - --main
78
+ - README.rdoc
79
+ - --title
80
+ - amqp-spec
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ hash: 3
89
+ segments:
90
+ - 0
91
+ version: "0"
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ hash: 3
98
+ segments:
99
+ - 0
100
+ version: "0"
101
+ requirements: []
102
+
103
+ rubyforge_project:
104
+ rubygems_version: 1.3.7
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: Simple API for writing (asynchronous) AMQP specs
108
+ test_files:
109
+ - spec/amqp.yml
110
+ - spec/failing_rspec_spec.rb
111
+ - spec/rspec_amqp_spec.rb
112
+ - spec/rspec_em_spec.rb
113
+ - spec/spec.opts
114
+ - spec/spec_helper.rb