summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAidan Skinner <aidan@apache.org>2008-04-14 10:45:18 +0000
committerAidan Skinner <aidan@apache.org>2008-04-14 10:45:18 +0000
commita2b46ef60b260d928c884a5e2b40c1c635a21746 (patch)
tree74395169d8480cf7abdec2ecf0d732396a6954c4
parent5c1648acbc1742c0e19603dca5574e6b3c71f0d7 (diff)
downloadqpid-python-a2b46ef60b260d928c884a5e2b40c1c635a21746.tar.gz
QPID-832 sync ruby from trunk
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/branches/thegreatmerge@647728 13f79535-47bb-0310-9956-ffa450edef68
-rwxr-xr-xqpid/ruby/LICENSE.txt203
-rw-r--r--qpid/ruby/NOTICE.txt19
-rw-r--r--qpid/ruby/RELEASE_NOTES19
-rw-r--r--qpid/ruby/qpid.rb25
-rw-r--r--qpid/ruby/qpid/client.rb135
-rw-r--r--qpid/ruby/qpid/codec.rb257
-rw-r--r--qpid/ruby/qpid/connection.rb254
-rw-r--r--qpid/ruby/qpid/fields.rb49
-rw-r--r--qpid/ruby/qpid/peer.rb287
-rw-r--r--qpid/ruby/qpid/queue.rb52
-rw-r--r--qpid/ruby/qpid/spec.rb289
-rw-r--r--qpid/ruby/qpid/test.rb38
-rw-r--r--qpid/ruby/qpid/traverse.rb64
-rwxr-xr-xqpid/ruby/run-tests4
-rw-r--r--qpid/ruby/tests/basic.rb69
-rw-r--r--qpid/ruby/tests/channel.rb48
16 files changed, 1812 insertions, 0 deletions
diff --git a/qpid/ruby/LICENSE.txt b/qpid/ruby/LICENSE.txt
new file mode 100755
index 0000000000..6b0b1270ff
--- /dev/null
+++ b/qpid/ruby/LICENSE.txt
@@ -0,0 +1,203 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/qpid/ruby/NOTICE.txt b/qpid/ruby/NOTICE.txt
new file mode 100644
index 0000000000..fff2bca45c
--- /dev/null
+++ b/qpid/ruby/NOTICE.txt
@@ -0,0 +1,19 @@
+=========================================================================
+== NOTICE file corresponding to the section 4 d of ==
+== the Apache License, Version 2.0, ==
+== in this case for the Apache Qpid distribution. ==
+=========================================================================
+
+This product includes software developed by the Apache Software Foundation
+(http://www.apache.org/).
+
+Please read the LICENSE.txt file present in the root directory of this
+distribution.
+
+
+Aside from contributions to the Apache Qpid project, this software also
+includes (binary only):
+
+ - None at this time.
+
+
diff --git a/qpid/ruby/RELEASE_NOTES b/qpid/ruby/RELEASE_NOTES
new file mode 100644
index 0000000000..5ea0bd8eec
--- /dev/null
+++ b/qpid/ruby/RELEASE_NOTES
@@ -0,0 +1,19 @@
+Apache Incubator Qpid Ruby M2 Release Notes
+---------------------------------------------
+
+The Qpid M2 release contains support the for AMQP 0-8 specification.
+You can access the 0-8 specification using the following link.
+http://www.amqp.org/tikiwiki/tiki-index.php?page=Download
+
+For full details of Qpid capabilities, as they currently stand, see our
+detailed project documentation at:
+
+http://cwiki.apache.org/confluence/pages/viewpage.action?pageId=28284
+
+Please take time to go through the README file provided with the distro.
+
+
+Known Issues/Outstanding Work
+-----------------------------
+
+Bug QPID-467 Complete Interop Testing
diff --git a/qpid/ruby/qpid.rb b/qpid/ruby/qpid.rb
new file mode 100644
index 0000000000..25cd26f362
--- /dev/null
+++ b/qpid/ruby/qpid.rb
@@ -0,0 +1,25 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require "qpid/client"
+require "qpid/queue"
+require "qpid/codec"
+require "qpid/connection"
+require "qpid/peer"
+require "qpid/spec"
diff --git a/qpid/ruby/qpid/client.rb b/qpid/ruby/qpid/client.rb
new file mode 100644
index 0000000000..f10f2e564b
--- /dev/null
+++ b/qpid/ruby/qpid/client.rb
@@ -0,0 +1,135 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require "thread"
+require "qpid/peer"
+require "qpid/queue"
+
+module Qpid
+
+ class Client
+ def initialize(host, port, spec, vhost = "/")
+ @host = host
+ @port = port
+ @spec = spec
+ @vhost = vhost
+
+ @mechanism = nil
+ @response = nil
+ @locale = nil
+
+ @queues = {}
+ @mutex = Mutex.new()
+
+ @closed = false
+ @code = nil
+ @started = ConditionVariable.new()
+
+ @conn = Connection.new(@host, @port, @spec)
+ @peer = Peer.new(@conn, ClientDelegate.new(self))
+ end
+
+ attr_reader :mechanism, :response, :locale
+
+ def closed?; @closed end
+ def closed=(value); @closed = value end
+ def code; @code end
+
+ def wait()
+ @mutex.synchronize do
+ @started.wait(@mutex)
+ end
+ raise EOFError.new() if closed?
+ end
+
+ def signal_start()
+ @started.broadcast()
+ end
+
+ def queue(key)
+ @mutex.synchronize do
+ q = @queues[key]
+ if q.nil?
+ q = Queue.new()
+ @queues[key] = q
+ end
+ return q
+ end
+ end
+
+ def start(response, mechanism="AMQPLAIN", locale="en_US")
+ @response = response
+ @mechanism = mechanism
+ @locale = locale
+
+ @conn.connect()
+ @conn.init()
+ @peer.start()
+ wait()
+ channel(0).connection_open(@vhost)
+ end
+
+ def channel(id)
+ return @peer.channel(id)
+ end
+
+ def close(msg = nil)
+ @closed = true
+ @code = msg
+ @peer.close()
+ end
+ end
+
+ class ClientDelegate
+ include Delegate
+
+ def initialize(client)
+ @client = client
+ end
+
+ def connection_start(ch, msg)
+ ch.connection_start_ok(:mechanism => @client.mechanism,
+ :response => @client.response,
+ :locale => @client.locale)
+ end
+
+ def connection_tune(ch, msg)
+ ch.connection_tune_ok(*msg.fields)
+ @client.signal_start()
+ end
+
+ def connection_close(ch, msg)
+ puts "CONNECTION CLOSED: #{msg.args.join(", ")}"
+ @client.close(msg)
+ end
+
+ def channel_close(ch, msg)
+ puts "CHANNEL[#{ch.id}] CLOSED: #{msg.args.join(", ")}"
+ ch.channel_close_ok()
+ ch.close()
+ end
+
+ def basic_deliver(ch, msg)
+ queue = @client.queue(msg.consumer_tag)
+ queue << msg
+ end
+
+ end
+
+end
diff --git a/qpid/ruby/qpid/codec.rb b/qpid/ruby/qpid/codec.rb
new file mode 100644
index 0000000000..8d80e10aee
--- /dev/null
+++ b/qpid/ruby/qpid/codec.rb
@@ -0,0 +1,257 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+module Codec
+ # is there a better way to do this?
+ class StringWriter
+
+ def initialize(str = "")
+ @str = str
+ end
+
+ def write(value)
+ @str << value
+ end
+
+ def to_s()
+ return @str
+ end
+
+ end
+
+ class EOF < Exception; end
+
+ class Encoder
+
+ def initialize(out)
+ @out = out
+ @bits = []
+ end
+
+ attr_reader(:out)
+
+ def encode(type, value)
+ send(type, value)
+ end
+
+ def bit(b)
+ @bits << b
+ end
+
+ def octet(o)
+ pack("C", o)
+ end
+
+ def short(s)
+ pack("n", s)
+ end
+
+ def long(l)
+ pack("N", l)
+ end
+
+ def longlong(l)
+ lower = l & 0xffffffff
+ upper = (l & ~0xffffffff) >> 32
+ long(upper)
+ long(lower)
+ end
+
+ def shortstr(s)
+ # shortstr is actually octetstr
+ octet(s.length)
+ write(s)
+ end
+
+ def longstr(s)
+ case s
+ when Hash
+ table(s)
+ else
+ long(s.length)
+ write(s)
+ end
+ end
+
+ def table(t)
+ t = {} if t.nil?
+ enc = Encoder.new(StringWriter.new())
+ t.each {|key, value|
+ enc.shortstr(key)
+ # I offer this chicken to the gods of polymorphism. May they
+ # choke on it.
+ case value
+ when String
+ type = :longstr
+ desc = "S"
+ when Numeric
+ type = :long
+ desc = "I"
+ else
+ raise Exception.new("unknown table value: #{value.class}")
+ end
+ enc.write(desc)
+ enc.encode(type, value)
+ }
+ longstr(enc.out.to_s())
+ end
+
+ def write(str)
+ flushbits()
+ @out.write(str)
+# puts "OUT #{str.inspect()}"
+ end
+
+ def pack(fmt, *args)
+ write(args.pack(fmt))
+ end
+
+ def flush()
+ flushbits()
+ end
+
+ private
+
+ def flushbits()
+ if @bits.empty? then return end
+
+ bytes = []
+ index = 0
+ @bits.each {|b|
+ bytes << 0 if index == 0
+ if b then bytes[-1] |= 1 << index end
+ index = (index + 1) % 8
+ }
+ @bits.clear()
+ bytes.each {|b|
+ octet(b)
+ }
+ end
+
+ end
+
+ class StringReader
+
+ def initialize(str)
+ @str = str
+ @index = 0
+ end
+
+ def read(n)
+ result = @str[@index, n]
+ @index += result.length
+ return result
+ end
+
+ end
+
+ class Decoder
+
+ def initialize(_in)
+ @in = _in
+ @bits = []
+ end
+
+ def decode(type)
+ return send(type)
+ end
+
+ def bit()
+ if @bits.empty?
+ byte = octet()
+ 7.downto(0) {|i|
+ @bits << (byte[i] == 1)
+ }
+ end
+ return @bits.pop()
+ end
+
+ def octet()
+ return unpack("C", 1)
+ end
+
+ def short()
+ return unpack("n", 2)
+ end
+
+ def long()
+ return unpack("N", 4)
+ end
+
+ def longlong()
+ upper = long()
+ lower = long()
+ return upper << 32 | lower
+ end
+
+ def shortstr()
+ # shortstr is actually octetstr
+ return read(octet())
+ end
+
+ def longstr()
+ return read(long())
+ end
+
+ def table()
+ dec = Decoder.new(StringReader.new(longstr()))
+ result = {}
+ while true
+ begin
+ key = dec.shortstr()
+ rescue EOF
+ break
+ end
+ desc = dec.read(1)
+ case desc
+ when "S"
+ value = dec.longstr()
+ when "I"
+ value = dec.long()
+ else
+ raise Exception.new("unrecognized descriminator: #{desc.inspect()}")
+ end
+ result[key] = value
+ end
+ return result
+ end
+
+ def read(n)
+ return "" if n == 0
+ result = @in.read(n)
+ if result.nil? or result.empty?
+ raise EOF.new()
+ else
+# puts " IN #{result.inspect()}"
+ return result
+ end
+ end
+
+ def unpack(fmt, size)
+ result = read(size).unpack(fmt)
+ if result.length == 1
+ return result[0]
+ else
+ return result
+ end
+ end
+
+ end
+
+end
diff --git a/qpid/ruby/qpid/connection.rb b/qpid/ruby/qpid/connection.rb
new file mode 100644
index 0000000000..f6ee9cf1e4
--- /dev/null
+++ b/qpid/ruby/qpid/connection.rb
@@ -0,0 +1,254 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require "socket"
+require "qpid/codec"
+
+include Codec
+
+module Qpid
+
+ class Connection
+
+ def initialize(host, port, spec)
+ @host = host
+ @port = port
+ @spec = spec
+ end
+
+ attr_reader(:host, :port, :spec)
+
+ def connect()
+ @sock = TCPSocket.open(@host, @port)
+ @out = Encoder.new(@sock)
+ @in = Decoder.new(@sock)
+ end
+
+ def init()
+ @out.write("AMQP")
+ [1, 1, @spec.major, @spec.minor].each {|o|
+ @out.octet(o)
+ }
+ end
+
+ def write(frame)
+# puts "OUT #{frame.inspect()}"
+ @out.octet(@spec.constants[frame.payload.type].id)
+ @out.short(frame.channel)
+ frame.payload.encode(@out)
+ @out.octet(frame_end)
+ end
+
+ def read()
+ type = @spec.constants[@in.octet()].name
+ channel = @in.short()
+ payload = Payload.decode(type, @spec, @in)
+ oct = @in.octet()
+ if oct != frame_end
+ raise Exception.new("framing error: expected #{frame_end}, got #{oct}")
+ end
+ frame = Frame.new(channel, payload)
+# puts " IN #{frame.inspect}"
+ return frame
+ end
+
+ private
+
+ def frame_end
+ @spec.constants[:"frame_end"].id
+ end
+
+ end
+
+ class Frame
+
+ def initialize(channel, payload)
+ @channel = channel
+ @payload = payload
+ end
+
+ attr_reader(:channel, :payload)
+
+ end
+
+ class Payload
+
+ TYPES = {}
+
+ def Payload.singleton_method_added(name)
+ if name == :type
+ TYPES[type] = self
+ end
+ end
+
+ def Payload.decode(type, spec, dec)
+ klass = TYPES[type]
+ klass.decode(spec, dec)
+ end
+
+ end
+
+ class Method < Payload
+
+ def initialize(method, args)
+ if args.size != method.fields.size
+ raise ArgumentError.new("argument mismatch #{method} #{args}")
+ end
+ @method = method
+ @args = args
+ end
+
+ attr_reader(:method, :args)
+
+ def Method.type; :frame_method end
+
+ def type; Method.type end
+
+ def encode(encoder)
+ buf = StringWriter.new()
+ enc = Encoder.new(buf)
+ enc.short(@method.parent.id)
+ enc.short(@method.id)
+ @method.fields.zip(self.args).each {|f, a|
+ if a.nil?; a = f.default end
+ enc.encode(f.type, a)
+ }
+ enc.flush()
+ encoder.longstr(buf.to_s)
+ end
+
+ def Method.decode(spec, decoder)
+ buf = decoder.longstr()
+ dec = Decoder.new(StringReader.new(buf))
+ klass = spec.classes[dec.short()]
+ meth = klass.methods[dec.short()]
+ args = meth.fields.map {|f| dec.decode(f.type)}
+ return Method.new(meth, args)
+ end
+
+ def inspect(); "#{method.qname}(#{args.join(", ")})" end
+
+ end
+
+ class Header < Payload
+
+ def Header.type; :frame_header end
+
+ def initialize(klass, weight, size, properties)
+ @klass = klass
+ @weight = weight
+ @size = size
+ @properties = properties
+ end
+
+ attr_reader :weight, :size, :properties
+
+ def type; Header.type end
+
+ def encode(encoder)
+ buf = StringWriter.new()
+ enc = Encoder.new(buf)
+ enc.short(@klass.id)
+ enc.short(@weight)
+ enc.longlong(@size)
+
+ # property flags
+ nprops = @klass.fields.size
+ flags = 0
+ 0.upto(nprops - 1) do |i|
+ f = @klass.fields[i]
+ flags <<= 1
+ flags |= 1 unless @properties[f.name].nil?
+ # the last bit indicates more flags
+ if i > 0 and (i % 15) == 0
+ flags <<= 1
+ if nprops > (i + 1)
+ flags |= 1
+ enc.short(flags)
+ flags = 0
+ end
+ end
+ end
+ flags <<= ((16 - (nprops % 15)) % 16)
+ enc.short(flags)
+
+ # properties
+ @klass.fields.each do |f|
+ v = @properties[f.name]
+ enc.encode(f.type, v) unless v.nil?
+ end
+ enc.flush()
+ encoder.longstr(buf.to_s)
+ end
+
+ def Header.decode(spec, decoder)
+ dec = Decoder.new(StringReader.new(decoder.longstr()))
+ klass = spec.classes[dec.short()]
+ weight = dec.short()
+ size = dec.longlong()
+
+ # property flags
+ bits = []
+ while true
+ flags = dec.short()
+ 15.downto(1) do |i|
+ if flags >> i & 0x1 != 0
+ bits << true
+ else
+ bits << false
+ end
+ end
+ break if flags & 0x1 == 0
+ end
+
+ # properties
+ properties = {}
+ bits.zip(klass.fields).each do |b, f|
+ properties[f.name] = dec.decode(f.type) if b
+ end
+ return Header.new(klass, weight, size, properties)
+ end
+
+ def inspect(); "#{@klass.name}(#{@properties.inspect()})" end
+
+ end
+
+ class Body < Payload
+
+ def Body.type; :frame_body end
+
+ def type; Body.type end
+
+ def initialize(content)
+ @content = content
+ end
+
+ attr_reader :content
+
+ def encode(enc)
+ enc.longstr(@content)
+ end
+
+ def Body.decode(spec, dec)
+ return Body.new(dec.longstr())
+ end
+
+ end
+
+end
diff --git a/qpid/ruby/qpid/fields.rb b/qpid/ruby/qpid/fields.rb
new file mode 100644
index 0000000000..91484af850
--- /dev/null
+++ b/qpid/ruby/qpid/fields.rb
@@ -0,0 +1,49 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+class Class
+ def fields(*fields)
+ module_eval {
+ def initialize(*args, &block)
+ args = init_fields(*args)
+
+ if respond_to? :init
+ init(*args) {|*a| yield(*a)}
+ elsif args.any?
+ raise ArgumentException.new("extra arguments: #{args}")
+ end
+ end
+ }
+
+ vars = fields.map {|f| :"@#{f.to_s().chomp("?")}"}
+
+ define_method(:init_fields) {|*args|
+ vars.each {|v|
+ instance_variable_set(v, args.shift())
+ }
+ args
+ }
+
+ vars.each_index {|i|
+ define_method(fields[i]) {
+ instance_variable_get(vars[i])
+ }
+ }
+ end
+end
diff --git a/qpid/ruby/qpid/peer.rb b/qpid/ruby/qpid/peer.rb
new file mode 100644
index 0000000000..320808fdc6
--- /dev/null
+++ b/qpid/ruby/qpid/peer.rb
@@ -0,0 +1,287 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require "thread"
+require "qpid/queue"
+require "qpid/connection"
+require "qpid/fields"
+
+module Qpid
+
+ class Peer
+
+ def initialize(conn, delegate)
+ @conn = conn
+ @delegate = delegate
+ @outgoing = Queue.new()
+ @work = Queue.new()
+ @channels = {}
+ @mutex = Mutex.new()
+ end
+
+ def channel(id)
+ @mutex.synchronize do
+ ch = @channels[id]
+ if ch.nil?
+ ch = Channel.new(id, self, @outgoing, @conn.spec)
+ @channels[id] = ch
+ end
+ return ch
+ end
+ end
+
+ def channel_delete(id)
+ @channels.delete(id)
+ end
+
+ def start()
+ spawn(:writer)
+ spawn(:reader)
+ spawn(:worker)
+ end
+
+ def close()
+ @mutex.synchronize do
+ @channels.each_value do |ch|
+ ch.close()
+ @outgoing.close()
+ @work.close()
+ end
+ end
+ end
+
+ private
+
+ def spawn(method, *args)
+ Thread.new do
+ begin
+ send(method, *args)
+ # is this the standard way to catch any exception?
+ rescue Closed => e
+ puts "#{method} #{e}"
+ rescue Object => e
+ print e
+ e.backtrace.each do |line|
+ print "\n ", line
+ end
+ print "\n"
+ end
+ end
+ end
+
+ def reader()
+ while true
+ frame = @conn.read()
+ ch = channel(frame.channel)
+ ch.dispatch(frame, @work)
+ end
+ end
+
+ def writer()
+ while true
+ @conn.write(@outgoing.pop())
+ end
+ end
+
+ def worker()
+ while true
+ dispatch(@work.pop())
+ end
+ end
+
+ def dispatch(queue)
+ frame = queue.pop()
+ ch = channel(frame.channel)
+ payload = frame.payload
+ if payload.method.content?
+ content = Qpid::read_content(queue)
+ else
+ content = nil
+ end
+
+ message = Message.new(payload.method, payload.args, content)
+ @delegate.dispatch(ch, message)
+ end
+
+ end
+
+ class Channel
+ def initialize(id, peer, outgoing, spec)
+ @id = id
+ @peer = peer
+ @outgoing = outgoing
+ @spec = spec
+ @incoming = Queue.new()
+ @responses = Queue.new()
+ @queue = nil
+ @closed = false
+ end
+
+ attr_reader :id
+
+ def closed?; @closed end
+
+ def close()
+ return if closed?
+ @peer.channel_delete(@id)
+ @closed = true
+ @incoming.close()
+ @responses.close()
+ end
+
+ def dispatch(frame, work)
+ payload = frame.payload
+ case payload
+ when Method
+ if payload.method.response?
+ @queue = @responses
+ else
+ @queue = @incoming
+ work << @incoming
+ end
+ end
+ @queue << frame
+ end
+
+ def method_missing(name, *args)
+ method = @spec.find_method(name)
+ if method.nil?
+ raise NoMethodError.new("undefined method '#{name}' for #{self}:#{self.class}")
+ end
+
+ if args.size == 1 and args[0].instance_of? Hash
+ kwargs = args[0]
+ invoke_args = method.fields.map do |f|
+ kwargs[f.name]
+ end
+ content = kwargs[:content]
+ else
+ invoke_args = []
+ method.fields.each do |f|
+ if args.any?
+ invoke_args << args.shift()
+ else
+ invoke_args << f.default
+ end
+ end
+ if method.content? and args.any?
+ content = args.shift()
+ else
+ content = nil
+ end
+ if args.any? then raise ArgumentError.new("#{args.size} extr arguments") end
+ end
+ return invoke(method, invoke_args, content)
+ end
+
+ def invoke(method, args, content = nil)
+ raise Closed() if closed?
+ frame = Frame.new(@id, Method.new(method, args))
+ @outgoing << frame
+
+ if method.content?
+ content = Content.new() if content.nil?
+ write_content(method.parent, content, @outgoing)
+ end
+
+ nowait = false
+ f = method.fields[:"nowait"]
+ nowait = args[method.fields.index(f)] unless f.nil?
+
+ unless nowait or method.responses.empty?
+ resp = @responses.pop().payload
+ if resp.method.content?
+ content = read_content(@responses)
+ else
+ content = nil
+ end
+ if method.responses.include? resp.method
+ return Message.new(resp.method, resp.args, content)
+ else
+ # XXX: ValueError doesn't actually exist
+ raise ValueError.new(resp)
+ end
+ end
+ end
+
+ def write_content(klass, content, queue)
+ size = content.size
+ header = Frame.new(@id, Header.new(klass, content.weight, size, content.headers))
+ queue << header
+ content.children.each {|child| write_content(klass, child, queue)}
+ queue << Frame.new(@id, Body.new(content.body)) if size > 0
+ end
+
+ end
+
+ def Qpid.read_content(queue)
+ frame = queue.pop()
+ header = frame.payload
+ children = []
+ 1.upto(header.weight) { children << read_content(queue) }
+ size = header.size
+ read = 0
+ buf = ""
+ while read < size
+ body = queue.pop()
+ content = body.payload.content
+ buf << content
+ read += content.size
+ end
+ buf.freeze()
+ return Content.new(header.properties.clone(), buf, children)
+ end
+
+ class Content
+ def initialize(headers = {}, body = "", children = [])
+ @headers = headers
+ @body = body
+ @children = children
+ end
+
+ attr_reader :headers, :body, :children
+
+ def size; body.size end
+ def weight; children.size end
+
+ def [](key); @headers[key] end
+ def []=(key, value); @headers[key] = value end
+ end
+
+ class Message
+ fields(:method, :args, :content)
+
+ alias fields args
+
+ def method_missing(name)
+ return args[@method.fields[name].id]
+ end
+
+ def inspect()
+ "#{method.qname}(#{args.join(", ")})"
+ end
+ end
+
+ module Delegate
+ def dispatch(ch, msg)
+ send(msg.method.qname, ch, msg)
+ end
+ end
+
+end
diff --git a/qpid/ruby/qpid/queue.rb b/qpid/ruby/qpid/queue.rb
new file mode 100644
index 0000000000..350310882f
--- /dev/null
+++ b/qpid/ruby/qpid/queue.rb
@@ -0,0 +1,52 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require "thread"
+
+module Qpid
+
+ class Closed < Exception; end
+
+ class Queue < Queue
+
+ @@END = Object.new()
+
+ def close()
+ # sentinal to indicate the end of the queue
+ self << @@END
+ end
+
+ def pop(*args)
+ result = super(*args)
+ if @@END.equal? result
+ # we put another sentinal on the end in case there are
+ # subsequent calls to pop by this or other threads
+ self << @@END
+ raise Closed.new()
+ else
+ return result
+ end
+ end
+
+ alias shift pop
+ alias deq pop
+
+ end
+
+end
diff --git a/qpid/ruby/qpid/spec.rb b/qpid/ruby/qpid/spec.rb
new file mode 100644
index 0000000000..9a04f584d0
--- /dev/null
+++ b/qpid/ruby/qpid/spec.rb
@@ -0,0 +1,289 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require "set"
+require "rexml/document"
+require "qpid/fields"
+require "qpid/traverse"
+
+module Spec
+
+ include REXML
+
+ class Container < Array
+
+ def initialize()
+ @cache = {}
+ end
+
+ def [](key)
+ return @cache[key] if @cache.include?(key)
+
+ case key
+ when String
+ value = find {|x| x.name == key.intern()}
+ when Symbol
+ value = find {|x| x.name == key}
+ when Integer
+ value = find {|x| x.id == key}
+ else
+ raise Exception.new("invalid key: #{key}")
+ end
+
+ @cache[key] = value
+ return value
+ end
+
+ end
+
+ class Root
+ fields(:major, :minor, :classes, :constants, :domains)
+
+ def find_method(name)
+ classes.each do |c|
+ c.methods.each do |m|
+ if name == m.qname
+ return m
+ end
+ end
+ end
+
+ return nil
+ end
+ end
+
+ class Constant
+ fields(:name, :id, :type, :docs)
+ end
+
+ class Domain
+ fields(:name, :type)
+ end
+
+ class Class
+ fields(:name, :id, :handler, :fields, :methods, :docs)
+ end
+
+ class Method
+ fields(:name, :id, :content?, :responses, :synchronous?, :fields,
+ :docs)
+
+ def init()
+ @response = false
+ end
+
+ attr :parent, true
+
+ def response?; @response end
+ def response=(b); @response = b end
+
+ def qname
+ :"#{parent.name}_#{name}"
+ end
+ end
+
+ class Field
+ fields(:name, :id, :type, :docs)
+
+ def default
+ case type
+ when :bit then false
+ when :octet, :short, :long, :longlong then 0
+ when :shortstr, :longstr then ""
+ when :table then {}
+ end
+ end
+
+ end
+
+ class Doc
+ fields(:type, :text)
+ end
+
+ class Reference
+
+ fields(:name)
+
+ def init(&block)
+ @resolver = block
+ end
+
+ def resolve(spec, klass)
+ @resolver.call(spec, klass)
+ end
+
+ end
+
+ class Loader
+
+ def initialize()
+ @stack = []
+ end
+
+ def load(obj)
+ case obj
+ when String
+ elem = @stack[-1]
+ result = Container.new()
+ elem.elements.each(obj) {|e|
+ @index = result.size
+ result << load(e)
+ }
+ @index = nil
+ return result
+ else
+ elem = obj
+ @stack << elem
+ begin
+ result = send(:"load_#{elem.name}")
+ ensure
+ @stack.pop()
+ end
+ return result
+ end
+ end
+
+ def element
+ @stack[-1]
+ end
+
+ def text
+ element.text
+ end
+
+ def attr(name, type = :string, default = nil)
+ value = element.attributes[name]
+ value = value.strip() unless value.nil?
+ value = nil unless value.nil? or value.any?
+ if value.nil? and not default.nil? then
+ default
+ else
+ send(:"parse_#{type}", value)
+ end
+ end
+
+ def parse_int(value)
+ value.to_i
+ end
+
+ TRUE = ["yes", "true", "1"].to_set
+ FALSE = ["no", "false", "0", nil].to_set
+
+ def parse_bool(value)
+ if TRUE.include?(value)
+ true
+ elsif FALSE.include?(value)
+ false
+ else
+ raise Exception.new("parse error, expecting boolean: #{value}")
+ end
+ end
+
+ def parse_string(value)
+ value.to_s
+ end
+
+ def parse_symbol(value)
+ value.intern() unless value.nil?
+ end
+
+ def parse_name(value)
+ value.gsub(/[\s-]/, '_').intern() unless value.nil?
+ end
+
+ def load_amqp()
+ Root.new(attr("major", :int), attr("minor", :int), load("class"),
+ load("constant"), load("domain"))
+ end
+
+ def load_class()
+ Class.new(attr("name", :name), attr("index", :int), attr("handler", :name),
+ load("field"), load("method"), load("doc"))
+ end
+
+ def load_method()
+ Method.new(attr("name", :name), attr("index", :int),
+ attr("content", :bool), load("response"),
+ attr("synchronous", :bool), load("field"), load("docs"))
+ end
+
+ def load_response()
+ name = attr("name", :name)
+ Reference.new {|spec, klass|
+ response = klass.methods[name]
+ if response.nil?
+ raise Exception.new("no such method: #{name}")
+ end
+ response
+ }
+ end
+
+ def load_field()
+ type = attr("type", :name)
+ if type.nil?
+ domain = attr("domain", :name)
+ type = Reference.new {|spec, klass|
+ spec.domains[domain].type
+ }
+ end
+ Field.new(attr("name", :name), @index, type, load("docs"))
+ end
+
+ def load_constant()
+ Constant.new(attr("name", :name), attr("value", :int), attr("class", :name),
+ load("doc"))
+ end
+
+ def load_domain()
+ Domain.new(attr("name", :name), attr("type", :name))
+ end
+
+ def load_doc()
+ Doc.new(attr("type", :symbol), text)
+ end
+
+ end
+
+ def Spec.load(spec)
+ case spec
+ when String
+ spec = File.new(spec)
+ end
+ doc = Document.new(spec)
+ spec = Loader.new().load(doc.root)
+ spec.classes.each do |klass|
+ klass.traverse! do |o|
+ case o
+ when Reference
+ o.resolve(spec, klass)
+ else
+ o
+ end
+ end
+ klass.methods.each do |m|
+ m.parent = klass
+ m.responses.each do |r|
+ r.response = true
+ end
+ end
+ end
+ spec
+ end
+
+end
diff --git a/qpid/ruby/qpid/test.rb b/qpid/ruby/qpid/test.rb
new file mode 100644
index 0000000000..f8107143ab
--- /dev/null
+++ b/qpid/ruby/qpid/test.rb
@@ -0,0 +1,38 @@
+
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#/
+
+require "qpid/spec"
+require "qpid/client"
+
+module Qpid
+
+ module Test
+
+ def connect()
+ spec = Spec.load("../specs/amqp.0-8.xml")
+ c = Client.new("0.0.0.0", 5672, spec)
+ c.start({"LOGIN" => "guest", "PASSWORD" => "guest"})
+ return c
+ end
+
+ end
+
+end
diff --git a/qpid/ruby/qpid/traverse.rb b/qpid/ruby/qpid/traverse.rb
new file mode 100644
index 0000000000..67358a7eb1
--- /dev/null
+++ b/qpid/ruby/qpid/traverse.rb
@@ -0,0 +1,64 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+class Object
+
+ public
+
+ def traverse()
+ traverse! {|o| yield(o); o}
+ end
+
+ def traverse_children!()
+ instance_variables.each {|v|
+ value = instance_variable_get(v)
+ replacement = yield(value)
+ instance_variable_set(v, replacement) unless replacement.equal? value
+ }
+ end
+
+ def traverse!(replacements = {})
+ return replacements[__id__] if replacements.has_key? __id__
+ replacement = yield(self)
+ replacements[__id__] = replacement
+ traverse_children! {|o| o.traverse!(replacements) {|c| yield(c)}}
+ return replacement
+ end
+
+end
+
+class Array
+ def traverse_children!()
+ map! {|o| yield(o)}
+ end
+end
+
+class Hash
+ def traverse_children!()
+ mods = {}
+ each_pair {|k, v|
+ key = yield(k)
+ value = yield(v)
+ mods[key] = value unless key.equal? k and value.equal? v
+ delete(k) unless key.equal? k
+ }
+
+ merge!(mods)
+ end
+end
diff --git a/qpid/ruby/run-tests b/qpid/ruby/run-tests
new file mode 100755
index 0000000000..b4c51a75ed
--- /dev/null
+++ b/qpid/ruby/run-tests
@@ -0,0 +1,4 @@
+#!/usr/bin/ruby
+
+require "tests/channel"
+require "tests/basic"
diff --git a/qpid/ruby/tests/basic.rb b/qpid/ruby/tests/basic.rb
new file mode 100644
index 0000000000..0018050fe2
--- /dev/null
+++ b/qpid/ruby/tests/basic.rb
@@ -0,0 +1,69 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require "test/unit"
+require "qpid/test"
+require "qpid"
+
+class Basic < Test::Unit::TestCase
+
+ include Qpid::Test
+
+ def publish(body, headers = {})
+ cli = connect()
+ ch = cli.channel(1)
+ ch.channel_open()
+ content = Qpid::Content.new(headers, body)
+ ch.basic_publish(:content => content)
+ msg = ch.channel_close()
+ assert msg.method.qname == :channel_close_ok
+ end
+
+ def consume(body, headers = {})
+ cli = connect()
+ ch = cli.channel(1)
+ ch.channel_open()
+ ch.queue_declare(:queue => "test-queue")
+ ch.queue_bind(:queue_name => "test-queue")
+ ch.basic_consume(:queue => "test-queue", :consumer_tag => "ctag")
+ content = Qpid::Content.new(headers, body)
+ ch.basic_publish(:routing_key => "test-queue", :content => content)
+ queue = cli.queue("ctag")
+ msg = queue.pop()
+ assert content.headers == msg.content.headers
+ assert content.body == msg.content.body
+ assert content.children == msg.content.children
+ ch.basic_ack(msg.delivery_tag)
+ msg = ch.channel_close()
+ assert msg.method.qname == :channel_close_ok
+ end
+
+ def test_publish(); publish("hello world") end
+
+ def test_publish_empty(); publish("") end
+
+ def test_publish_headers(); publish("hello world", :content_type => "text/plain") end
+
+ def test_consume(); consume("hello world") end
+
+ def test_consume_empty(); consume("") end
+
+ def test_consume_headers(); consume("hello_world", :content_type => "text/plain") end
+
+end
diff --git a/qpid/ruby/tests/channel.rb b/qpid/ruby/tests/channel.rb
new file mode 100644
index 0000000000..31c5f19d92
--- /dev/null
+++ b/qpid/ruby/tests/channel.rb
@@ -0,0 +1,48 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require "test/unit"
+require "qpid/test"
+require "qpid"
+
+class Channel < Test::Unit::TestCase
+
+ include Qpid::Test
+
+ def test_channel_open_close()
+ c = connect()
+ ch = c.channel(1)
+ msg = ch.channel_open()
+ assert msg.method.qname == :channel_open_ok
+ msg = ch.channel_close()
+ assert msg.method.qname == :channel_close_ok
+ end
+
+ def test_channel_close()
+ c = connect()
+ ch = c.channel(1)
+ begin
+ ch.channel_close()
+ rescue Qpid::Closed => e
+ assert c.code.method.qname == :connection_close
+ assert c.code.reply_code == 504
+ end
+ end
+
+end