qpid_proton 0.19.0 → 0.21.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/examples/README.md +76 -0
  3. data/examples/broker.rb +167 -0
  4. data/examples/client.rb +79 -0
  5. data/examples/direct_recv.rb +61 -0
  6. data/examples/direct_send.rb +67 -0
  7. data/examples/example_test.rb +109 -0
  8. data/examples/helloworld.rb +57 -0
  9. data/examples/server.rb +70 -0
  10. data/examples/simple_recv.rb +57 -0
  11. data/examples/simple_send.rb +63 -0
  12. data/examples/ssl_certs/README.txt +24 -0
  13. data/examples/ssl_certs/tclient-certificate.p12 +0 -0
  14. data/examples/ssl_certs/tclient-certificate.pem +19 -0
  15. data/examples/ssl_certs/tclient-full.p12 +0 -0
  16. data/examples/ssl_certs/tclient-private-key.pem +30 -0
  17. data/examples/ssl_certs/tserver-certificate.p12 +0 -0
  18. data/examples/ssl_certs/tserver-certificate.pem +19 -0
  19. data/examples/ssl_certs/tserver-full.p12 +0 -0
  20. data/examples/ssl_certs/tserver-private-key.pem +30 -0
  21. data/examples/ssl_send.rb +70 -0
  22. data/ext/cproton/cproton.c +105 -74
  23. data/lib/core/container.rb +2 -1
  24. data/lib/core/ssl_domain.rb +1 -1
  25. data/lib/core/uri.rb +15 -9
  26. data/lib/handler/messaging_adapter.rb +20 -5
  27. data/tests/old_examples/broker.rb +200 -0
  28. data/tests/old_examples/client.rb +81 -0
  29. data/tests/old_examples/direct_recv.rb +64 -0
  30. data/tests/old_examples/direct_send.rb +63 -0
  31. data/tests/old_examples/helloworld.rb +72 -0
  32. data/tests/old_examples/helloworld_direct.rb +73 -0
  33. data/tests/old_examples/lib/debugging.rb +25 -0
  34. data/tests/old_examples/lib/driver.rb +68 -0
  35. data/tests/old_examples/lib/qpid_examples.rb +26 -0
  36. data/tests/old_examples/lib/selectable.rb +119 -0
  37. data/tests/old_examples/lib/send_and_receive.rb +89 -0
  38. data/tests/old_examples/old_example_test.rb +107 -0
  39. data/tests/old_examples/recv.rb +23 -0
  40. data/tests/old_examples/send.rb +21 -0
  41. data/tests/old_examples/server.rb +75 -0
  42. data/tests/old_examples/simple_recv.rb +57 -0
  43. data/tests/old_examples/simple_send.rb +54 -0
  44. data/tests/test_connection_driver.rb +134 -0
  45. data/tests/test_container.rb +319 -0
  46. data/tests/test_data.rb +66 -0
  47. data/tests/test_delivery.rb +110 -0
  48. data/tests/test_interop.rb +131 -0
  49. data/tests/test_messaging_adapter.rb +223 -0
  50. data/tests/test_old_adapter.rb +228 -0
  51. data/tests/test_tools.rb +147 -0
  52. data/tests/test_uri.rb +83 -0
  53. metadata +49 -3
@@ -0,0 +1,134 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The ASF licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # distributed under the License is distributed on an
12
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
13
+ # KIND, either express or implied. See the License for the
14
+ # specific language governing permissions and limitations
15
+ # under the License.
16
+
17
+
18
+ require 'test_tools'
19
+ require 'minitest/unit'
20
+
21
+ include Qpid::Proton
22
+
23
+ # Test delivery of raw proton events
24
+
25
+ class RawDriverTest < MiniTest::Test
26
+
27
+ # Raw handler to record all on_xxx calls
28
+ class RecordingHandler
29
+ def initialize() @calls =[]; end
30
+ def proton_adapter_class() nil; end # Raw adapter
31
+ attr_reader :calls
32
+
33
+ def method_missing(name, *args) respond_to_missing?(name) ? @calls << name : super; end
34
+ def respond_to_missing?(name, private=false); (/^on_/ =~ name); end
35
+ def respond_to?(name, all=false) super || respond_to_missing?(name); end # For ruby < 1.9.2
36
+ end
37
+
38
+ def test_send_recv
39
+ send_class = Class.new do
40
+ def proton_adapter_class() nil; end # Raw adapter
41
+ attr_reader :outcome
42
+ def on_link_flow(event) event.sender.send Message.new("foo"); end
43
+ def on_delivery(event)
44
+ @outcome = event.delivery.state
45
+ event.connection.close;
46
+ end
47
+ end
48
+
49
+ recv_class = Class.new do
50
+ def proton_adapter_class() nil; end # Raw adapter
51
+ attr_reader :message
52
+ def on_connection_remote_open(event) event.context.open; end
53
+ def on_session_remote_open(event) event.context.open; end
54
+ def on_link_remote_open(event) event.link.open; event.link.flow(1); end
55
+ def on_delivery(event) @message = event.message; event.delivery.accept; end
56
+ end
57
+
58
+ d = DriverPair.new(send_class.new, recv_class.new)
59
+ d.client.connection.open(:container_id => __method__);
60
+ d.client.connection.open_sender()
61
+ d.run
62
+ assert_equal(d.server.handler.message.body, "foo")
63
+ assert_equal Transfer::ACCEPTED, d.client.handler.outcome
64
+ end
65
+
66
+ def test_idle
67
+
68
+ d = DriverPair.new(RecordingHandler.new, RecordingHandler.new)
69
+ ms = 444
70
+ secs = Rational(ms, 1000) # Use rationals to keep it accurate
71
+ opts = {:idle_timeout => secs}
72
+ d.client.transport.apply(opts)
73
+ assert_equal(ms, d.client.transport.idle_timeout) # Transport converts to ms
74
+ d.server.transport.set_server
75
+ d.client.connection.open(opts)
76
+
77
+ start = Time.at(1) # Dummy timeline
78
+ tick = d.run start # Process all IO events
79
+ assert_equal(secs/4, tick - start)
80
+ assert_equal [:on_connection_init, :on_connection_local_open, :on_connection_bound], d.client.handler.calls
81
+ assert_equal [:on_connection_init, :on_connection_bound, :on_connection_remote_open, :on_transport], d.server.handler.calls
82
+ assert_equal (ms), d.client.transport.idle_timeout
83
+ assert_equal (ms/2), d.server.transport.remote_idle_timeout # proton changes the value
84
+ assert_equal (secs/2), d.server.connection.idle_timeout
85
+
86
+ # Now update the time till we get connections closing
87
+ d.each { |x| x.handler.calls.clear }
88
+ d.run(start + secs - 0.001) # Should nothing, timeout not reached
89
+ assert_equal [[],[]], d.collect { |x| x.handler.calls }
90
+ d.run(start + secs*2) # After 2x timeout, connections should close
91
+ assert_equal [[:on_transport_error, :on_transport_tail_closed, :on_transport_head_closed, :on_transport_closed], [:on_connection_remote_close, :on_transport_tail_closed, :on_transport_head_closed, :on_transport_closed]], d.collect { |x| x.handler.calls }
92
+ end
93
+
94
+ # Test each_session/each_link methods both with a block and returning Enumerator
95
+ def test_enumerators
96
+ connection = Connection.new()
97
+ (3.times.collect { connection.open_session }).each { |s|
98
+ s.open_sender; s.open_receiver
99
+ }
100
+
101
+ assert_equal 3, connection.each_session.to_a.size
102
+ assert_equal 6, connection.each_link.to_a.size
103
+
104
+ # Build Session => Set<Links> map using connection link enumerator
105
+ map1 = {}
106
+ connection.each_link { |l| map1[l.session] ||= Set.new; map1[l.session] << l }
107
+ assert_equal 3, map1.size
108
+ map1.each do |session,links|
109
+ assert_equal 2, links.size
110
+ links.each { |l| assert_equal session, l.session }
111
+ end
112
+
113
+ # Build Session => Set<Links> map using connection and session blocks
114
+ map2 = {}
115
+ connection.each_session do |session|
116
+ map2[session] = Set.new
117
+ session.each_link { |l| map2[session] << l }
118
+ end
119
+ assert_equal map1, map2
120
+
121
+ # Build Session => Set<Links> map using connection session and session enumerators
122
+ map3 = Hash[connection.each_session.collect { |s| [s, Set.new(s.each_link)] }]
123
+ assert_equal map1, map3
124
+
125
+ assert_equal [true, true, true], connection.each_sender.collect { |l| l.is_a? Sender }
126
+ assert_equal [true, true, true], connection.each_receiver.collect { |l| l.is_a? Receiver }
127
+ connection.each_session { |session|
128
+ assert_equal [true], session.each_sender.collect { |l| l.is_a? Sender }
129
+ assert_equal [true], session.each_receiver.collect { |l| l.is_a? Receiver }
130
+ }
131
+
132
+
133
+ end
134
+ end
@@ -0,0 +1,319 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The ASF licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+
19
+ require 'test_tools'
20
+ require 'minitest/unit'
21
+ require 'socket'
22
+
23
+ # Container that listens on a random port
24
+ class TestContainer < Qpid::Proton::Container
25
+
26
+ def initialize(handler, lopts=nil, id=nil)
27
+ super handler, id
28
+ @listener = listen_io(TCPServer.open(0), ListenOnceHandler.new(lopts))
29
+ end
30
+
31
+ def port() @listener.to_io.addr[1]; end
32
+ def url() "amqp://:#{port}"; end#
33
+ end
34
+
35
+ class ContainerTest < MiniTest::Test
36
+ include Qpid::Proton
37
+
38
+ def test_simple()
39
+ send_handler = Class.new(MessagingHandler) do
40
+ attr_reader :accepted, :sent
41
+ def on_sendable(sender)
42
+ sender.send Message.new("foo") unless @sent
43
+ @sent = true
44
+ end
45
+
46
+ def on_tracker_accept(tracker)
47
+ @accepted = true
48
+ tracker.connection.close
49
+ end
50
+ end.new
51
+
52
+ receive_handler = Class.new(MessagingHandler) do
53
+ attr_reader :message, :link
54
+ def on_receiver_open(link)
55
+ @link = link
56
+ @link.open
57
+ @link.flow(1)
58
+ end
59
+
60
+ def on_message(delivery, message)
61
+ @message = message;
62
+ delivery.update Disposition::ACCEPTED
63
+ delivery.settle
64
+ end
65
+ end.new
66
+
67
+ c = TestContainer.new(receive_handler, {}, __method__)
68
+ c.connect(c.url, {:handler => send_handler}).open_sender({:name => "testlink"})
69
+ c.run
70
+
71
+ assert send_handler.accepted
72
+ assert_equal "testlink", receive_handler.link.name
73
+ assert_equal "foo", receive_handler.message.body
74
+ assert_equal "test_simple", receive_handler.link.connection.container_id
75
+ end
76
+
77
+ class CloseOnOpenHandler < TestHandler
78
+ def on_connection_open(c) super; c.close; end
79
+ end
80
+
81
+ def test_auto_stop_one
82
+ # A listener and a connection
83
+ start_stop_handler = Class.new do
84
+ def on_container_start(c) @start = c; end
85
+ def on_container_stop(c) @stop = c; end
86
+ attr_reader :start, :stop
87
+ end.new
88
+ c = Container.new(start_stop_handler, __method__)
89
+ threads = 3.times.collect { Thread.new { c.run } }
90
+ sleep(0.01) while c.running < 3
91
+ assert_equal c, start_stop_handler.start
92
+ l = c.listen_io(TCPServer.new(0), ListenOnceHandler.new({ :handler => CloseOnOpenHandler.new}))
93
+ c.connect("amqp://:#{l.to_io.addr[1]}", { :handler => CloseOnOpenHandler.new} )
94
+ threads.each { |t| assert t.join(1) }
95
+ assert_equal c, start_stop_handler.stop
96
+ assert_raises(Container::StoppedError) { c.run }
97
+ end
98
+
99
+ def test_auto_stop_two
100
+ # Connect between different containers
101
+ c1, c2 = Container.new("#{__method__}-1"), Container.new("#{__method__}-2")
102
+ threads = [ Thread.new {c1.run }, Thread.new {c2.run } ]
103
+ l = c2.listen_io(TCPServer.new(0), ListenOnceHandler.new({ :handler => CloseOnOpenHandler.new}))
104
+ c1.connect(l.url, { :handler => CloseOnOpenHandler.new} )
105
+ assert threads.each { |t| t.join(1) }
106
+ assert_raises(Container::StoppedError) { c1.run }
107
+ assert_raises(Container::StoppedError) { c2.connect("") }
108
+ end
109
+
110
+ def test_auto_stop_listener_only
111
+ c = Container.new(__method__)
112
+ # Listener only, external close
113
+ t = Thread.new { c.run }
114
+ l = c.listen_io(TCPServer.new(0))
115
+ l.close
116
+ assert t.join(1)
117
+ end
118
+
119
+ def test_stop_empty
120
+ c = Container.new(__method__)
121
+ threads = 3.times.collect { Thread.new { c.run } }
122
+ sleep(0.01) while c.running < 3
123
+ assert_nil threads[0].join(0.001) # Not stopped
124
+ c.stop
125
+ assert c.stopped
126
+ assert_raises(Container::StoppedError) { c.connect("") }
127
+ assert_raises(Container::StoppedError) { c.run }
128
+ threads.each { |t| assert t.join(1) }
129
+ end
130
+
131
+ def test_stop
132
+ c = Container.new(__method__)
133
+ c.auto_stop = false
134
+
135
+ l = c.listen_io(TCPServer.new(0))
136
+ threads = 3.times.collect { Thread.new { c.run } }
137
+ sleep(0.01) while c.running < 3
138
+ l.close
139
+ assert_nil threads[0].join(0.001) # Not stopped, no auto_stop
140
+
141
+ l = c.listen_io(TCPServer.new(0)) # New listener
142
+ conn = c.connect("amqp://:#{l.to_io.addr[1]}")
143
+ c.stop
144
+ assert c.stopped
145
+ threads.each { |t| assert t.join(1) }
146
+
147
+ assert_raises(Container::StoppedError) { c.run }
148
+ assert_equal 0, c.running
149
+ assert_nil l.condition
150
+ assert_nil conn.condition
151
+ end
152
+
153
+ def test_bad_host
154
+ cont = Container.new(__method__)
155
+ assert_raises (SocketError) { cont.listen("badlisten.example.com:999") }
156
+ assert_raises (SocketError) { c = cont.connect("badconnect.example.com:999") }
157
+ end
158
+
159
+ # Verify that connection options are sent to the peer and available as Connection methods
160
+ def test_connection_options
161
+ # Note: user, password and sasl_xxx options are tested by ContainerSASLTest below
162
+ server_handler = Class.new(MessagingHandler) do
163
+ def on_error(e) raise e.inspect; end
164
+ def on_connection_open(c)
165
+ @connection = c
166
+ c.open({
167
+ :virtual_host => "server.to.client",
168
+ :properties => { :server => :client },
169
+ :offered_capabilities => [ :s1 ],
170
+ :desired_capabilities => [ :s2 ],
171
+ :container_id => "box",
172
+ })
173
+ c.close
174
+ end
175
+ attr_reader :connection
176
+ end.new
177
+ # Transport options must be provided to the listener, by Connection#open it is too late
178
+ cont = TestContainer.new(nil, {
179
+ :handler => server_handler,
180
+ :idle_timeout => 88,
181
+ :max_sessions =>1000,
182
+ :max_frame_size => 8888,
183
+ })
184
+ client = cont.connect(cont.url,
185
+ {:virtual_host => "client.to.server",
186
+ :properties => { :foo => :bar, "str" => "str" },
187
+ :offered_capabilities => [:c1 ],
188
+ :desired_capabilities => ["c2" ],
189
+ :idle_timeout => 42,
190
+ :max_sessions =>100,
191
+ :max_frame_size => 4096,
192
+ :container_id => "bowl"
193
+ })
194
+ cont.run
195
+ c = server_handler.connection
196
+ assert_equal "client.to.server", c.virtual_host
197
+ assert_equal({ :foo => :bar, :str => "str" }, c.properties)
198
+ assert_equal([:c1], c.offered_capabilities)
199
+ assert_equal([:c2], c.desired_capabilities)
200
+ assert_equal 21, c.idle_timeout # Proton divides by 2
201
+ assert_equal 100, c.max_sessions
202
+ assert_equal 4096, c.max_frame_size
203
+ assert_equal "bowl", c.container_id
204
+
205
+ c = client
206
+ assert_equal "server.to.client", c.virtual_host
207
+ assert_equal({ :server => :client }, c.properties)
208
+ assert_equal([:s1], c.offered_capabilities)
209
+ assert_equal([:s2], c.desired_capabilities)
210
+ assert_equal "box", c.container_id
211
+ assert_equal 8888, c.max_frame_size
212
+ assert_equal 44, c.idle_timeout # Proton divides by 2
213
+ assert_equal 100, c.max_sessions
214
+ end
215
+ end
216
+
217
+
218
+ class ContainerSASLTest < MiniTest::Test
219
+ include Qpid::Proton
220
+
221
+ # Handler for test client/server that sets up server and client SASL options
222
+ class SASLHandler < TestHandler
223
+
224
+ def initialize(url="amqp://", opts=nil)
225
+ super()
226
+ @url, @opts = url, opts
227
+ end
228
+
229
+ def on_container_start(container)
230
+ @client = container.connect("#{@url}:#{container.port}", @opts)
231
+ end
232
+
233
+ attr_reader :auth_user
234
+
235
+ def on_connection_open(connection)
236
+ super
237
+ if connection == @client
238
+ connection.close
239
+ else
240
+ @auth_user = connection.user
241
+ end
242
+ end
243
+ end
244
+
245
+ # Generate SASL server configuration files and database, initialize proton SASL
246
+ class SASLConfig
247
+ include Qpid::Proton
248
+ attr_reader :conf_dir, :conf_file, :conf_name, :database
249
+
250
+ def initialize()
251
+ if SASL.extended? # Configure cyrus SASL
252
+ @conf_dir = File.expand_path('sasl_conf')
253
+ @conf_name = "proton-server"
254
+ @database = File.join(@conf_dir, "proton.sasldb")
255
+ @conf_file = File.join(conf_dir,"#{@conf_name}.conf")
256
+ Dir::mkdir(@conf_dir) unless File.directory?(@conf_dir)
257
+ # Same user name in different realms
258
+ make_user("user", "password", "proton") # proton realm
259
+ make_user("user", "default_password") # Default realm
260
+ File.open(@conf_file, 'w') do |f|
261
+ f.write("
262
+ sasldb_path: #{database}
263
+ mech_list: EXTERNAL DIGEST-MD5 SCRAM-SHA-1 CRAM-MD5 PLAIN ANONYMOUS
264
+ ")
265
+ end
266
+ # Tell proton library to use the new configuration
267
+ SASL.config_path = conf_dir
268
+ SASL.config_name = conf_name
269
+ end
270
+ end
271
+
272
+ private
273
+
274
+ SASLPASSWD = (ENV['SASLPASSWD'] or 'saslpasswd2')
275
+
276
+ def make_user(user, password, realm=nil)
277
+ realm_opt = (realm ? "-u #{realm}" : "")
278
+ cmd = "echo '#{password}' | #{SASLPASSWD} -c -p -f #{database} #{realm_opt} #{user}"
279
+ system(cmd) or raise RuntimeError.new("saslpasswd2 failed: #{makepw_cmd}")
280
+ end
281
+ DEFAULT = SASLConfig.new
282
+ end
283
+
284
+ def test_sasl_anonymous()
285
+ s = SASLHandler.new("amqp://", {:sasl_allowed_mechs => "ANONYMOUS"})
286
+ TestContainer.new(s, {:sasl_allowed_mechs => "ANONYMOUS"}, __method__).run
287
+ assert_equal "anonymous", s.connections[0].user
288
+ end
289
+
290
+ def test_sasl_plain_url()
291
+ skip unless SASL.extended?
292
+ # Use default realm with URL, should authenticate with "default_password"
293
+ opts = {:sasl_allowed_mechs => "PLAIN", :sasl_allow_insecure_mechs => true}
294
+ s = SASLHandler.new("amqp://user:default_password@", opts)
295
+ TestContainer.new(s, opts, __method__).run
296
+ assert_equal(2, s.connections.size)
297
+ assert_equal("user", s.auth_user)
298
+ end
299
+
300
+ def test_sasl_plain_options()
301
+ skip unless SASL.extended?
302
+ # Use default realm with connection options, should authenticate with "default_password"
303
+ opts = {:sasl_allowed_mechs => "PLAIN",:sasl_allow_insecure_mechs => true,
304
+ :user => 'user', :password => 'default_password' }
305
+ s = SASLHandler.new("amqp://", opts)
306
+ TestContainer.new(s, {:sasl_allowed_mechs => "PLAIN",:sasl_allow_insecure_mechs => true}, __method__).run
307
+ assert_equal(2, s.connections.size)
308
+ assert_equal("user", s.auth_user)
309
+ end
310
+
311
+ # Ensure we don't allow PLAIN if allow_insecure_mechs = true is not explicitly set
312
+ def test_disallow_insecure()
313
+ # Don't set allow_insecure_mechs, but try to use PLAIN
314
+ s = SASLHandler.new("amqp://user:password@", {:sasl_allowed_mechs => "PLAIN", :sasl_allow_insecure_mechs => true})
315
+ e = assert_raises(TestError) { TestContainer.new(s, {:sasl_allowed_mechs => "PLAIN"}, __method__).run }
316
+ assert_match(/amqp:unauthorized-access.*Authentication failed/, e.to_s)
317
+ end
318
+ end
319
+
@@ -0,0 +1,66 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The ASF licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+
19
+ require 'test_tools'
20
+ require "securerandom"
21
+ require 'qpid_proton'
22
+
23
+ class TestData < MiniTest::Test
24
+ include Qpid::Proton
25
+
26
+ def assert_from_to(*values)
27
+ d = Codec::Data.new
28
+ values.each do |x|
29
+ Codec::Data.from_object(d.impl, x)
30
+ assert_equal x, Codec::Data.to_object(d.impl)
31
+ end
32
+ end
33
+
34
+ def test_from_to
35
+ assert_from_to({ 1 => :one, 2=>:two })
36
+ assert_from_to([{:a => 1, "b" => 2}, 3, 4.4, :five])
37
+ assert_from_to(Types::UniformArray.new(Types::INT, [1, 2, 3, 4]))
38
+ end
39
+
40
+ def rnum(*arg) SecureRandom.random_number(*arg); end
41
+ def rstr(*arg) SecureRandom.base64(*arg); end
42
+
43
+ def test_nil()
44
+ assert_nil((Codec::Data.new << nil).object)
45
+ end
46
+
47
+ def assert_convert(*values)
48
+ values.each { |x| assert_equal x, ((Codec::Data.new << x).object) }
49
+ end
50
+
51
+ def test_bool()
52
+ assert_convert(true, false)
53
+ end
54
+
55
+ def test_float()
56
+ assert_convert(0.0, 1.0, -1.0, 1.23e123, rnum(), rnum(), rnum(), rnum())
57
+ end
58
+
59
+ def test_string()
60
+ assert_convert("", "foo", rstr(100000), rstr(rnum(1000)), rstr(rnum(1000)))
61
+ end
62
+
63
+ def test_symbol()
64
+ assert_convert(:"", :foo, rstr(256).to_sym)
65
+ end
66
+ end
@@ -0,0 +1,110 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The ASF licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+
19
+ require 'minitest/autorun'
20
+ require 'qpid_proton'
21
+ require 'test_tools'
22
+ include Qpid::Proton
23
+
24
+ # Test Delivery and Tracker
25
+ class TestDelivery < MiniTest::Test
26
+
27
+ class NoAutoHandler < MessagingHandler
28
+ def on_sender_open(l) l.open({:auto_settle=>false, :auto_accept=>false}); end
29
+ def on_receiver_open(l) l.open({:auto_settle=>false, :auto_accept=>false}); end
30
+ end
31
+
32
+ class SendHandler < NoAutoHandler
33
+ def initialize(to_send)
34
+ @unsent = to_send
35
+ end
36
+
37
+ def on_connection_open(connection)
38
+ @outcomes = []
39
+ @sender = connection.open_sender("x")
40
+ @unsettled = {} # Awaiting remote settlement
41
+ end
42
+
43
+ attr_reader :outcomes, :unsent, :unsettled
44
+
45
+ def on_sendable(sender)
46
+ return if @unsent.empty?
47
+ m = Message.new(@unsent.shift)
48
+ tracker = sender.send(m)
49
+ @unsettled[tracker] = m
50
+ end
51
+
52
+ def outcome(method, tracker)
53
+ t = tracker
54
+ m = @unsettled.delete(t)
55
+ @outcomes << [m.body, method, t.id, t.state, t.modifications]
56
+ tracker.connection.close if @unsettled.empty?
57
+ end
58
+
59
+ def on_tracker_accept(tracker) outcome(__method__, tracker); end
60
+ def on_tracker_reject(tracker) outcome(__method__, tracker); end
61
+ def on_tracker_release(tracker) outcome(__method__, tracker); end
62
+ def on_tracker_modify(tracker) outcome(__method__, tracker); end
63
+ end
64
+
65
+ class ReceiveHandler < NoAutoHandler
66
+ def initialize
67
+ @received = []
68
+ end
69
+
70
+ attr_reader :received
71
+
72
+ def on_message(delivery, message)
73
+ @received << message.body
74
+ case message.body
75
+ when "accept" then delivery.accept
76
+ when "reject" then delivery.reject
77
+ when "release-really" then delivery.release({:failed=>false}) # AMQP RELEASED
78
+ when "release" then delivery.release # AMQP MODIFIED{ :failed => true }
79
+ when "modify" then delivery.release({:undeliverable => true, :annotations => {:x => 42 }})
80
+ when "modify-empty" then delivery.release({:failed => false, :undeliverable => false, :annotations => {}})
81
+ when "modify-nil" then delivery.release({:failed => false, :undeliverable => false, :annotations => nil})
82
+ when "reject-raise" then raise Reject
83
+ when "release-raise" then raise Release
84
+ else raise inspect
85
+ end
86
+ end
87
+ end
88
+
89
+ def test_outcomes
90
+ rh = ReceiveHandler.new
91
+ sh = SendHandler.new(["accept", "reject", "release-really", "release", "modify", "modify-empty", "modify-nil", "reject-raise", "release-raise"])
92
+ c = Container.new(nil, __method__)
93
+ l = c.listen_io(TCPServer.new(0), ListenOnceHandler.new({ :handler => rh }))
94
+ c.connect(l.url, {:handler => sh})
95
+ c.run
96
+ o = sh.outcomes
97
+ assert_equal ["accept", :on_tracker_accept, "1", Transfer::ACCEPTED, nil], o.shift
98
+ assert_equal ["reject", :on_tracker_reject, "2", Transfer::REJECTED, nil], o.shift
99
+ assert_equal ["release-really", :on_tracker_release, "3", Transfer::RELEASED, nil], o.shift
100
+ assert_equal ["release", :on_tracker_modify, "4", Transfer::MODIFIED, {:failed=>true, :undeliverable=>false, :annotations=>nil}], o.shift
101
+ assert_equal ["modify", :on_tracker_modify, "5", Transfer::MODIFIED, {:failed=>true, :undeliverable=>true, :annotations=>{:x => 42}}], o.shift
102
+ assert_equal ["modify-empty", :on_tracker_release, "6", Transfer::RELEASED, nil], o.shift
103
+ assert_equal ["modify-nil", :on_tracker_release, "7", Transfer::RELEASED, nil], o.shift
104
+ assert_equal ["reject-raise", :on_tracker_reject, "8", Transfer::REJECTED, nil], o.shift
105
+ assert_equal ["release-raise", :on_tracker_modify, "9", Transfer::MODIFIED, {:failed=>true, :undeliverable=>false, :annotations=>nil}], o.shift
106
+ assert_empty o
107
+ assert_equal ["accept", "reject", "release-really", "release", "modify", "modify-empty", "modify-nil", "reject-raise", "release-raise"], rh.received
108
+ assert_empty sh.unsettled
109
+ end
110
+ end