amqp 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/.gitignore +3 -2
  2. data/CHANGELOG +25 -0
  3. data/Gemfile +4 -2
  4. data/README.md +2 -0
  5. data/{amqp.todo → TODO} +1 -3
  6. data/amqp.gemspec +3 -3
  7. data/bin/irb +2 -2
  8. data/bin/jenkins.sh +25 -0
  9. data/bin/set_test_suite_realms_up.sh +21 -0
  10. data/doc/EXAMPLE_01_PINGPONG +1 -1
  11. data/doc/EXAMPLE_02_CLOCK +1 -1
  12. data/doc/EXAMPLE_03_STOCKS +1 -1
  13. data/doc/EXAMPLE_04_MULTICLOCK +1 -1
  14. data/doc/EXAMPLE_05_ACK +1 -1
  15. data/doc/EXAMPLE_05_POP +1 -1
  16. data/doc/EXAMPLE_06_HASHTABLE +1 -1
  17. data/examples/{mq/ack.rb → ack.rb} +6 -6
  18. data/examples/{mq/automatic_binding_for_default_direct_exchange.rb → automatic_binding_for_default_direct_exchange.rb} +4 -4
  19. data/examples/{mq/callbacks.rb → callbacks.rb} +2 -2
  20. data/examples/{mq/clock.rb → clock.rb} +5 -5
  21. data/examples/{mq/hashtable.rb → hashtable.rb} +4 -4
  22. data/examples/{mq/internal.rb → internal.rb} +5 -5
  23. data/examples/{mq/logger.rb → logger.rb} +5 -5
  24. data/examples/{mq/multiclock.rb → multiclock.rb} +4 -4
  25. data/examples/{mq/pingpong.rb → pingpong.rb} +5 -5
  26. data/examples/{mq/pop.rb → pop.rb} +3 -3
  27. data/examples/{mq/primes-simple.rb → primes-simple.rb} +0 -0
  28. data/examples/{mq/primes.rb → primes.rb} +6 -6
  29. data/examples/{amqp/simple.rb → simple.rb} +1 -1
  30. data/examples/{mq/stocks.rb → stocks.rb} +5 -5
  31. data/lib/amqp.rb +8 -112
  32. data/lib/amqp/basic_client.rb +58 -0
  33. data/lib/amqp/channel.rb +937 -0
  34. data/lib/amqp/client.rb +72 -79
  35. data/lib/{mq → amqp}/collection.rb +12 -2
  36. data/lib/amqp/connection.rb +115 -0
  37. data/lib/amqp/exceptions.rb +18 -0
  38. data/lib/{mq → amqp}/exchange.rb +32 -34
  39. data/lib/{ext → amqp/ext}/em.rb +1 -1
  40. data/lib/{ext → amqp/ext}/emfork.rb +0 -0
  41. data/lib/amqp/frame.rb +3 -3
  42. data/lib/{mq → amqp}/header.rb +5 -11
  43. data/lib/{mq → amqp}/logger.rb +2 -2
  44. data/lib/amqp/protocol.rb +2 -2
  45. data/lib/{mq → amqp}/queue.rb +20 -17
  46. data/lib/{mq → amqp}/rpc.rb +20 -8
  47. data/lib/amqp/server.rb +1 -1
  48. data/lib/amqp/version.rb +1 -1
  49. data/lib/mq.rb +20 -964
  50. data/protocol/codegen.rb +1 -1
  51. data/research/api.rb +3 -3
  52. data/research/primes-forked.rb +5 -5
  53. data/research/primes-processes.rb +5 -5
  54. data/research/primes-threaded.rb +5 -5
  55. data/spec/integration/authentication_spec.rb +114 -0
  56. data/spec/integration/automatic_binding_for_default_direct_exchange_spec.rb +13 -12
  57. data/spec/{unit/mq → integration}/channel_close_spec.rb +2 -2
  58. data/spec/{unit/mq → integration}/exchange_declaration_spec.rb +26 -14
  59. data/spec/{unit/mq → integration}/queue_declaration_spec.rb +4 -4
  60. data/spec/integration/queue_exclusivity_spec.rb +95 -0
  61. data/spec/integration/reply_queue_communication_spec.rb +63 -0
  62. data/spec/integration/store_and_forward_spec.rb +121 -0
  63. data/spec/integration/topic_subscription_spec.rb +193 -0
  64. data/spec/integration/workload_distribution_spec.rb +245 -0
  65. data/spec/spec_helper.rb +16 -32
  66. data/spec/unit/{mq/mq_basic_spec.rb → amqp/basic_spec.rb} +4 -4
  67. data/spec/unit/{mq → amqp}/collection_spec.rb +22 -7
  68. data/spec/unit/amqp/connection_spec.rb +116 -0
  69. data/spec/unit/amqp/frame_spec.rb +18 -18
  70. data/spec/unit/amqp/protocol_spec.rb +9 -11
  71. metadata +54 -49
  72. data/lib/ext/blankslate.rb +0 -9
  73. data/spec/mq_helper.rb +0 -70
  74. data/spec/unit/amqp/client_spec.rb +0 -472
  75. data/spec/unit/amqp/misc_spec.rb +0 -123
  76. data/spec/unit/mq/misc_spec.rb +0 -228
  77. data/spec/unit/mq/queue_spec.rb +0 -71
@@ -0,0 +1,245 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "spec_helper"
3
+
4
+ describe "Workload distribution" do
5
+
6
+ #
7
+ # Environment
8
+ #
9
+
10
+ include AMQP::Spec
11
+ include AMQP::SpecHelper
12
+
13
+ em_before { AMQP.cleanup_state }
14
+ em_after { AMQP.cleanup_state }
15
+
16
+ default_options AMQP_OPTS
17
+ default_timeout 5
18
+
19
+ amqp_before do
20
+ @channel = AMQP::Channel.new
21
+ @channel.should be_open
22
+ end
23
+
24
+ after(:all) do
25
+ AMQP.cleanup_state
26
+ done
27
+ end
28
+
29
+
30
+ #
31
+ # Examples
32
+ #
33
+
34
+ context "that uses fanout exchange" do
35
+ amqp_before :each do
36
+ @exchange = @channel.fanout("amqpgem.integration.multicast.fanout", :auto_delete => true)
37
+ end
38
+
39
+ context "with three bound queues" do
40
+ amqp_before :each do
41
+ @queue1 = @channel.queue("amqpgem.integration.multicast.queue1", :auto_delete => true)
42
+ @queue2 = @channel.queue("amqpgem.integration.multicast.queue2", :auto_delete => true)
43
+ @queue3 = @channel.queue("amqpgem.integration.multicast.queue3", :auto_delete => true)
44
+
45
+ @queues = [@queue1, @queue2, @queue3]
46
+
47
+ @sent_values = Array.new
48
+
49
+ @queue1.bind(@exchange).subscribe do |payload|
50
+ @received_messages[@queue1.name].push(payload.to_i)
51
+ end # subscribe
52
+
53
+ @queue2.bind(@exchange).subscribe do |payload|
54
+ @received_messages[@queue2.name].push(payload.to_i)
55
+ end # subscribe
56
+
57
+ @queue3.bind(@exchange).subscribe do |payload|
58
+ @received_messages[@queue3.name].push(payload.to_i)
59
+ end # subscribe
60
+ end
61
+
62
+ amqp_before :each do
63
+ @received_messages = {
64
+ @queue1.name => [],
65
+ @queue2.name => [],
66
+ @queue3.name => []
67
+ }
68
+
69
+ @expected_number_of_messages = {
70
+ @queue1.name => 100,
71
+ @queue2.name => 100,
72
+ @queue3.name => 100
73
+ }
74
+ end
75
+
76
+ amqp_after :each do
77
+ @sent_values.clear
78
+ end
79
+
80
+
81
+ context "and messages are published as non-mandatory" do
82
+ it "routes all messages to all bound queues" do
83
+ 100.times do
84
+ dispatched_data = rand(5_000_000)
85
+ @sent_values.push(dispatched_data)
86
+
87
+ @exchange.publish(dispatched_data, :mandatory => false)
88
+ end
89
+
90
+ # for Rubinius, it is surprisingly slow on this workload
91
+ done(1.5) {
92
+ [@queue1, @queue2, @queue3].each do |q|
93
+ @received_messages[q.name].size.should == @expected_number_of_messages[q.name]
94
+
95
+ # this one is ordering assertion
96
+ @received_messages[q.name].should == @sent_values
97
+ end
98
+ }
99
+ end # it
100
+ end # context
101
+
102
+
103
+
104
+ context "and messages are published as mandatory" do
105
+ it "routes all messages to all bound queues" do
106
+ 100.times do
107
+ dispatched_data = rand(5_000_000)
108
+ @sent_values.push(dispatched_data)
109
+
110
+ @exchange.publish(dispatched_data, :mandatory => true)
111
+ end
112
+
113
+ # 6 seconds are for Rubinius, it is surprisingly slow on this workload
114
+ done(1.5) {
115
+ [@queue1, @queue2, @queue3].each do |q|
116
+ @received_messages[q.name].size.should == @expected_number_of_messages[q.name]
117
+
118
+ # this one is ordering assertion
119
+ @received_messages[q.name].should == @sent_values
120
+ end
121
+ }
122
+ end # it
123
+ end # context
124
+
125
+
126
+
127
+ context "and messages are published as non-persistent" do
128
+ it "routes all messages to all bound queues" do
129
+ 100.times do
130
+ dispatched_data = rand(5_000_000)
131
+ @sent_values.push(dispatched_data)
132
+
133
+ @exchange.publish(dispatched_data, :persistent => false)
134
+ end
135
+
136
+ # 6 seconds are for Rubinius, it is surprisingly slow on this workload
137
+ done(1.5) {
138
+ [@queue1, @queue2, @queue3].each do |q|
139
+ @received_messages[q.name].size.should == @expected_number_of_messages[q.name]
140
+
141
+ # this one is ordering assertion
142
+ @received_messages[q.name].should == @sent_values
143
+ end
144
+ }
145
+ end # it
146
+ end # context
147
+
148
+
149
+
150
+ context "and messages are published as persistent" do
151
+ it "routes all messages to all bound queues" do
152
+ 100.times do
153
+ dispatched_data = rand(5_000_000)
154
+ @sent_values.push(dispatched_data)
155
+
156
+ @exchange.publish(dispatched_data, :persistent => true)
157
+ end
158
+
159
+ # 6 seconds are for Rubinius, it is surprisingly slow on this workload
160
+ done(1.5) {
161
+ [@queue1, @queue2, @queue3].each do |q|
162
+ @received_messages[q.name].size.should == @expected_number_of_messages[q.name]
163
+
164
+ # this one is ordering assertion
165
+ @received_messages[q.name].should == @sent_values
166
+ end
167
+ }
168
+ end # it
169
+ end # context
170
+
171
+
172
+ context "and messages are published as non-immediate" do
173
+ it "routes all messages to all bound queues" do
174
+ 100.times do
175
+ dispatched_data = rand(5_000_000)
176
+ @sent_values.push(dispatched_data)
177
+
178
+ @exchange.publish(dispatched_data, :immediate => false)
179
+ end
180
+
181
+ # 6 seconds are for Rubinius, it is surprisingly slow on this workload
182
+ done(1.5) {
183
+ [@queue1, @queue2, @queue3].each do |q|
184
+ @received_messages[q.name].size.should == @expected_number_of_messages[q.name]
185
+
186
+ # this one is ordering assertion
187
+ @received_messages[q.name].should == @sent_values
188
+ end
189
+ }
190
+ end # it
191
+ end # context
192
+
193
+
194
+
195
+ context "and messages are published as immediate" do
196
+ it "may get a Basic.Return back"
197
+ end # context
198
+
199
+
200
+
201
+ context "and messages are published WITHOUT routing key" do
202
+ it "routes all messages to all bound queues" do
203
+ 100.times do
204
+ dispatched_data = rand(5_000_000)
205
+ @sent_values.push(dispatched_data)
206
+
207
+ @exchange.publish(dispatched_data)
208
+ end
209
+
210
+ # 6 seconds are for Rubinius, it is surprisingly slow on this workload
211
+ done(1.5) {
212
+ [@queue1, @queue2, @queue3].each do |q|
213
+ @received_messages[q.name].size.should == @expected_number_of_messages[q.name]
214
+
215
+ # this one is ordering assertion
216
+ @received_messages[q.name].should == @sent_values
217
+ end
218
+ }
219
+ end # it
220
+ end # context
221
+
222
+
223
+ context "and messages are published WITH routing key that matches name of one of the queues" do
224
+ it "routes all messages to all bound queues" do
225
+ 100.times do
226
+ dispatched_data = rand(5_000_000)
227
+ @sent_values.push(dispatched_data)
228
+
229
+ @exchange.publish(dispatched_data, :routing_key => @queues.sample.name)
230
+ end
231
+
232
+ # 6 seconds are for Rubinius, it is surprisingly slow on this workload
233
+ done(1.5) {
234
+ [@queue1, @queue2, @queue3].each do |q|
235
+ @received_messages[q.name].size.should == @expected_number_of_messages[q.name]
236
+
237
+ # this one is ordering assertion
238
+ @received_messages[q.name].should == @sent_values
239
+ end
240
+ }
241
+ end # it
242
+ end # context
243
+ end # context
244
+ end # context
245
+ end # describe
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # -*- coding: utf-8 -*-
2
2
 
3
3
  $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
4
4
 
@@ -6,7 +6,8 @@ require 'bundler'
6
6
  Bundler.setup
7
7
  Bundler.require :default, :test
8
8
 
9
- require "mq"
9
+ require "amqp-spec"
10
+ require "amqp"
10
11
 
11
12
  amqp_config = File.dirname(__FILE__) + '/amqp.yml'
12
13
 
@@ -26,8 +27,12 @@ else
26
27
  AMQP_OPTS = {:host => 'localhost', :port => 5672}
27
28
  end
28
29
 
30
+ #
31
+ # Ruby version-specific
32
+ #
29
33
 
30
- if RUBY_VERSION == "1.8.7"
34
+ case RUBY_VERSION
35
+ when "1.8.7" then
31
36
  module ArrayExtensions
32
37
  def sample
33
38
  self.choice
@@ -37,18 +42,17 @@ if RUBY_VERSION == "1.8.7"
37
42
  class Array
38
43
  include ArrayExtensions
39
44
  end
45
+ when "1.8.6" then
46
+ raise "Ruby 1.8.6 is not supported. Sorry, pal. Time to move on beyond One True Ruby. Yes, time flies by."
47
+ when /^1.9/ then
48
+ puts "Encoding.default_internal was #{Encoding.default_internal || 'not set'}, switching to #{Encoding::UTF_8}"
49
+ Encoding.default_internal = Encoding::UTF_8
50
+
51
+ puts "Encoding.default_external was #{Encoding.default_internal || 'not set'}, switching to #{Encoding::UTF_8}"
52
+ Encoding.default_external = Encoding::UTF_8
40
53
  end
41
54
 
42
55
 
43
-
44
- # Shorthand for mocking subject's instance variable
45
- def subject_mock(name, as_null = false)
46
- mock = mock(name)
47
- mock.as_null_object if as_null
48
- subject.instance_variable_set(name.to_sym, mock)
49
- mock
50
- end
51
-
52
56
  # Returns Header that should be correctly parsed
53
57
  def basic_header(opts = {})
54
58
  AMQP::Frame::Header.new(
@@ -74,23 +78,3 @@ def test_method_deliver opts = {}
74
78
  AMQP::Protocol::Basic::Deliver.new(
75
79
  :consumer_tag => opts[:consumer_tag] || 'test_consumer'))
76
80
  end
77
-
78
- require "stringio"
79
-
80
- def capture_stdout(&block)
81
- $stdout = StringIO.new
82
- block.call
83
- $stdout.rewind
84
- result = $stdout.read
85
- $stdout = STDOUT
86
- return result
87
- end
88
-
89
- def capture_stderr(&block)
90
- $stderr = StringIO.new
91
- block.call
92
- $stderr.rewind
93
- result = $stderr.read
94
- $stderr = STDOUT
95
- return result
96
- end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe MQ do
5
+ describe AMQP do
6
6
 
7
7
  #
8
8
  # Environment
@@ -13,7 +13,7 @@ describe MQ do
13
13
  default_timeout 5
14
14
 
15
15
  amqp_before do
16
- @channel = MQ.new
16
+ @channel = AMQP::Channel.new
17
17
  end
18
18
 
19
19
 
@@ -25,7 +25,7 @@ describe MQ do
25
25
  describe ".channel" do
26
26
  it 'gives each thread a separate channel' do
27
27
  pending 'This is not implemented in current lib'
28
- class MQ
28
+ module AMQP
29
29
  @@cur_channel = 0
30
30
  end
31
31
 
@@ -36,4 +36,4 @@ describe MQ do
36
36
  done
37
37
  end
38
38
  end
39
- end # describe MQ
39
+ end # describe AMQP
@@ -1,14 +1,14 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  require "spec_helper"
4
- require "mq/collection"
4
+ require "amqp/collection"
5
5
 
6
6
  Item = Struct.new(:name)
7
7
 
8
- describe MQ::Collection do
8
+ describe AMQP::Collection do
9
9
  before do
10
10
  @items = 3.times.map { |int| Item.new("name-#{int}") }
11
- @collection = MQ::Collection.new(@items)
11
+ @collection = AMQP::Collection.new(@items)
12
12
  end
13
13
 
14
14
  it "provides access to items by name" do
@@ -23,7 +23,7 @@ describe MQ::Collection do
23
23
 
24
24
  describe "#<<" do
25
25
  it "should raise IncompatibleItemError if the argument doesn't have method :name" do
26
- lambda { @collection << nil }.should raise_error(MQ::Collection::IncompatibleItemError)
26
+ lambda { @collection << nil }.should raise_error(AMQP::Collection::IncompatibleItemError)
27
27
  end
28
28
 
29
29
  it "should add an item into the collection" do
@@ -88,7 +88,7 @@ describe MQ::Collection do
88
88
  describe "#add!" do
89
89
  context "when the argument doesn't respond to :name" do
90
90
  it "should raise IncompatibleItemError " do
91
- lambda { @collection.add!(nil) }.should raise_error(MQ::Collection::IncompatibleItemError)
91
+ lambda { @collection.add!(nil) }.should raise_error(AMQP::Collection::IncompatibleItemError)
92
92
  end # it
93
93
  end
94
94
 
@@ -114,7 +114,7 @@ describe MQ::Collection do
114
114
  describe "#<<" do
115
115
  context "when the argument doesn't respond to :name" do
116
116
  it "should raise IncompatibleItemError " do
117
- lambda { @collection << nil }.should raise_error(MQ::Collection::IncompatibleItemError)
117
+ lambda { @collection << nil }.should raise_error(AMQP::Collection::IncompatibleItemError)
118
118
  end # it
119
119
  end # context
120
120
 
@@ -126,4 +126,19 @@ describe MQ::Collection do
126
126
  end
127
127
  end # context
128
128
  end # describe
129
- end # describe MQ::Collection
129
+
130
+
131
+
132
+
133
+
134
+ describe "#delete" do
135
+ it "should remove item with the given name from the collection" do
136
+ items = [Item.new("test-0"), Item.new("test-1")]
137
+ collection = AMQP::Collection.new(items)
138
+ lambda {
139
+ collection.delete("test-1")
140
+ }.should change(collection, :length).by(-1)
141
+ collection.should_not include("test-1")
142
+ end
143
+ end # describe
144
+ end # describe AMQP::Collection
@@ -0,0 +1,116 @@
1
+ require 'spec_helper'
2
+ require 'amqp'
3
+
4
+ describe AMQP, 'class object' do
5
+
6
+ #
7
+ # Environment
8
+ #
9
+
10
+ subject { AMQP }
11
+
12
+ #
13
+ # Examples
14
+ #
15
+
16
+ its(:settings) do
17
+ should == {
18
+ :host => "127.0.0.1",
19
+ :port => 5672,
20
+ :user => "guest",
21
+ :pass => "guest",
22
+ :vhost => "/",
23
+ :timeout => nil,
24
+ :logging => false,
25
+ :ssl => false
26
+ }
27
+ end
28
+
29
+ its(:client) { should == AMQP::BasicClient }
30
+
31
+
32
+
33
+
34
+ describe 'logging' do
35
+ after(:all) do
36
+ AMQP.logging = false
37
+ end
38
+
39
+ it 'is silent by default' do
40
+ AMQP.logging.should be_false
41
+ end
42
+ end # .logging=
43
+
44
+
45
+
46
+
47
+ describe '.start' do
48
+
49
+ #
50
+ # Environment
51
+ #
52
+
53
+ include AMQP::SpecHelper
54
+
55
+ em_before { AMQP.cleanup_state }
56
+ em_after { AMQP.cleanup_state }
57
+
58
+ #
59
+ # Examples
60
+ #
61
+
62
+ it 'yields to given block AFTER connection is established' do
63
+ em do
64
+ AMQP.start AMQP_OPTS do
65
+ @block_fired = true
66
+
67
+ AMQP.connection.should be_connected
68
+ end
69
+ done(0.1) { @block_fired.should be_true }
70
+ end
71
+ end
72
+ end # .start
73
+
74
+
75
+
76
+
77
+ describe '.stop' do
78
+ context "when connection is not established" do
79
+ it 'is a no-op' do
80
+ expect { @res = AMQP.stop }.to_not raise_error
81
+ @res.should be_nil
82
+ end # it
83
+ end # context
84
+
85
+
86
+ context 'with established AMQP connection' do
87
+
88
+ #
89
+ #
90
+ #
91
+
92
+ include AMQP::Spec
93
+ after { AMQP.cleanup_state; done }
94
+ default_options AMQP_OPTS
95
+
96
+ #
97
+ # Examples
98
+ #
99
+
100
+ it 'properly closes AMQP broker connection and fires a callback. Mind the delay!' do
101
+ AMQP.start(AMQP_OPTS)
102
+ AMQP.connection.should be_connected
103
+
104
+ @block_has_fired = false
105
+
106
+ AMQP.stop do
107
+ @block_has_fired = true
108
+ end
109
+ AMQP.connection.should_not be_nil
110
+ done(0.1) do
111
+ @block_has_fired.should be_true
112
+ end
113
+ end # it
114
+ end # context
115
+ end # describe
116
+ end # describe AMQP