Repository: qpid-interop-test
Updated Branches:
  refs/heads/master [created] 6d0d60388


QPIDIT-1: Intial checkin of Proton Python test shim


Project: http://git-wip-us.apache.org/repos/asf/qpid-interop-test/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-interop-test/commit/6d0d6038
Tree: http://git-wip-us.apache.org/repos/asf/qpid-interop-test/tree/6d0d6038
Diff: http://git-wip-us.apache.org/repos/asf/qpid-interop-test/diff/6d0d6038

Branch: refs/heads/master
Commit: 6d0d603883d3cba8af02152d23b0d72ba79c8afd
Parents: 
Author: Kim van der Riet <[email protected]>
Authored: Thu Jun 11 10:47:50 2015 -0400
Committer: Kim van der Riet <[email protected]>
Committed: Thu Jun 11 10:47:50 2015 -0400

----------------------------------------------------------------------
 .project                                        |  17 +
 .pydevproject                                   |   8 +
 LICENSE                                         | 202 ++++++++++
 QUICKSTART                                      |  59 +++
 README                                          |  63 +++
 STATUS                                          |  48 +++
 etc/proton-python-amqp-types.patch              | 215 ++++++++++
 shims/qpid-proton-python/src/__init__.py        |  18 +
 .../src/proton-python-receive                   | 106 +++++
 shims/qpid-proton-python/src/proton-python-send | 136 +++++++
 src/py/qpid-interop-test/__init__.py            |  22 ++
 src/py/qpid-interop-test/shim_utils.py          | 388 +++++++++++++++++++
 src/py/qpid-interop-test/shim_utils.pyc         | Bin 0 -> 14712 bytes
 .../types/simple_type_tests.py                  | 357 +++++++++++++++++
 14 files changed, 1639 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/6d0d6038/.project
----------------------------------------------------------------------
diff --git a/.project b/.project
new file mode 100644
index 0000000..ea09747
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>qpid-interop-test</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.python.pydev.PyDevBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.python.pydev.pythonNature</nature>
+       </natures>
+</projectDescription>

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/6d0d6038/.pydevproject
----------------------------------------------------------------------
diff --git a/.pydevproject b/.pydevproject
new file mode 100644
index 0000000..037bd25
--- /dev/null
+++ b/.pydevproject
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?eclipse-pydev version="1.0"?><pydev_project>
+<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
+<path>/${PROJECT_DIR_NAME}</path>
+</pydev_pathproperty>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 
2.7</pydev_property>
+<pydev_property 
name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
+</pydev_project>

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/6d0d6038/LICENSE
----------------------------------------------------------------------
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 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.

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/6d0d6038/QUICKSTART
----------------------------------------------------------------------
diff --git a/QUICKSTART b/QUICKSTART
new file mode 100644
index 0000000..cc7d0d9
--- /dev/null
+++ b/QUICKSTART
@@ -0,0 +1,59 @@
+QUICKSTART GUIDE
+================
+
+0. Prerequisites
+----------------
+* qpid-proton built and installed
+* For JMS testing, qpid-jms built and installed
+
+1. Get the source
+-----------------
+git clone https://git-wip-us.apache.org/repos/asf/qpid-interop-test.git
+
+2. Patch Proton and build
+-------------------------
+Proton needs to be patched with a change that adds support for the missing 
AMQP types in the current
+python implementation. This patch will be proposed as a patch to Proton 
itself, and if accepted,
+this part of the procedure will go away. Until then, the patch is located in 
the etc directory as
+proton-python-amqp-types.patch. Copy this to the qpid-proton directory, then:
+
+cd <path/to/>qpid-proton
+patch -p1 proton-python-amqp-types.patch
+cd build/
+make
+sudo make install
+cd ..
+mvn -DskipTests package
+mvn -DskipTests install
+
+3. Build Qpid JMS
+-----------------
+cd <path/to/>qpid-jms
+mvn -DskipTests package
+mvn -DskipTests install
+
+
+3 Prepare the tests
+-------------------
+(ONLY IF NEEDED): Edit src/py/qpid_interop/types/simple_type_tests.py as
+needed, particularly AmqpPrimitiveTypes.TYPE_MAP which controls the AMQP types
+and test values for each type.
+
+4. Run the tests (uninstalled)
+------------------------------
+Note that installation is still to be completed, this section will change to
+reflect installation details when complete.
+
+Assuming proton's make install has been run, from top level qpid-interop-test 
directory:
+export 
PYTHONPATH=/usr/local/lib64/proton/bindings/python:src/py/qpid-interop-test
+export LD_LIBRARY_PATH=/usr/local/lib64
+export QPID_INTEROP_TEST_HOME=<abs path to top level qpid-interop-test 
directory>
+
+Start a broker. If using qpidd:
+qpidd --load-module amqp.so -m yes --auth no --queue-pattern 
qpid-interop.simple_type_tests --default-flow-stop-threshold 0 
--default-flow-resume-threshold 0 --default-queue-limit 0 --log-enable info+
+
+NOTE: YOU MUST USE THE --queue-pattern qpid-interop.simple_type_tests 
parameter with the Qpid broker!
+
+From top level directory:
+./src/py/qpid-interop-test/types/simple_type_tests.py
+

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/6d0d6038/README
----------------------------------------------------------------------
diff --git a/README b/README
new file mode 100644
index 0000000..8fc1d3c
--- /dev/null
+++ b/README
@@ -0,0 +1,63 @@
+Qpid Client Interoperability Test Suite
+---------------------------------------
+
+This directory contains the Qpid Proton Client API test suite.
+
+All files in this directory are under the Apache License - see LICENSE for
+details.
+
+Source Code
+-----------
+Source code may be obtained from Apache git server
+https://git-wip-us.apache.org/repos/asf/qpid-interop-test.git
+
+Documentation
+-------------
+A quickstart guide for building and using this test suite is contained in
+QUICKSTART. Detailed documentation for adding tests and using them are
+(will be) contained in the docs directory.
+
+Issues
+------
+Issues are tracked in the Apache JIRA at
+https://issues.apache.org/jira/browse/QPIDIT/?selectedTab=com.atlassian.jira.jira-projects-plugin:summary-panel
+
+Support
+-------
+Support may be obtained from the qpid-users mailing list
[email protected]
+
+Writing shims for new clients
+-----------------------------
+A detailed description of this process is (will be) contained in the docs
+directory. The very short version of this is as follows:
+
+1. Write a pair of client programs using the client API under test.
+
+The first is a sender which reads the following from the command-line:
+
+<broker address> <amqp type> <test value 1> <test value 2> ...
+
+and is responsible for sending messages containing the test values each in a
+single message in the appropriate AMQP type format.
+
+The second client program is a receiver, and must read the following from the
+command-line
+
+<broker address> <amqp type> <num messages>
+
+and is responsible for receiving <num messages> messages from the broker and
+printing the bodies of the received messages appropriately decoded for type
+<amqp type>. The printed output will contain the amqp type on the first line
+and each received message body on subsequent lines, one message per line.
+
+2. Add a subclass for this client in
+src/py/qpid-interop-test/types/simple_type_test.py
+derived from class Shim and which overrides NAME, ENV (as needed), SHIM_LOC,
+SEND and RECEIVE. SEND and RECEIVE must point to the two clients written
+in step 1 above.
+
+3. Add an instance of your new shim class to SHIM_MAP keyed against its name.
+
+That's it!
+

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/6d0d6038/STATUS
----------------------------------------------------------------------
diff --git a/STATUS b/STATUS
new file mode 100644
index 0000000..40df1b1
--- /dev/null
+++ b/STATUS
@@ -0,0 +1,48 @@
+1. AMQP type tests:
+-------------------
+Main test launcher:
+Python program src/py/qpid-interop-test/types/simple_type_tests.py
+
+The launcher uses "shims", each of which is a simple pair of test programs
+which receive their instructions through the command-line. These are written
+using the target test client API. One program is a sender and is responsible
+for sending test messages, while the other is a receiver and is responsible
+for recieving the test messages and returning them to the launcher.
+
+1.1 Qpid-proton-python shim
+---------------------------
+Status: Working.
+        AMQP types:
+            null - implemented
+            boolean - implemented
+            ubyte - implemented with Proton patch support for this type
+            ushort - implemented with Proton patch support for this type
+            uint - implemented with Proton patch support for this type
+            ulong - implemented
+            byte - implemented with Proton patch support for this type - fails 
in swig interface
+            short - implemented with Proton patch support for this type
+            int - implemented with Proton patch support for this type
+            long - implemented
+            float - implemented with Proton patch support for this type
+            double - implemented
+            decimal32 - implemented with Proton patch support for this type - 
fails in swig interface
+            decimal64 - implemented with Proton patch support for this type - 
fails in swig interface
+            decimal128 - implemented with Proton patch support for this type - 
not working, commented out
+            char - implemented - not working, commented out
+            timestamp - implemented
+            uuid - implemented
+            binary - implemented
+            string - implemented
+            symbol - implemented
+            list - implemented
+            map - implemented - test broken
+            array - not yet implemented - TODO
+
+1.2 Qpid-JMS shim
+-----------------
+Status: In progress, AMQP string type works with Qpid-proton-python.
+
+
+2. AMQP functionality tests:
+----------------------------
+Not yet implemented
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/6d0d6038/etc/proton-python-amqp-types.patch
----------------------------------------------------------------------
diff --git a/etc/proton-python-amqp-types.patch 
b/etc/proton-python-amqp-types.patch
new file mode 100644
index 0000000..eabe25a
--- /dev/null
+++ b/etc/proton-python-amqp-types.patch
@@ -0,0 +1,215 @@
+From bc720193ab06730e8b69b01b9ada314f62660084 Mon Sep 17 00:00:00 2001
+From: Kim van der Riet <[email protected]>
+Date: Tue, 12 May 2015 11:44:54 -0400
+Subject: [PATCH 1924/1924] Added python classes to represent AMQP types not
+ currently supported
+
+---
+ proton-c/bindings/python/proton/__init__.py | 110 +++++++++++++++++++++++-----
+ 1 file changed, 90 insertions(+), 20 deletions(-)
+
+diff --git a/proton-c/bindings/python/proton/__init__.py 
b/proton-c/bindings/python/proton/__init__.py
+index e3cd9e3..26ad1f3 100644
+--- a/proton-c/bindings/python/proton/__init__.py
++++ b/proton-c/bindings/python/proton/__init__.py
+@@ -1275,6 +1275,56 @@ class char(unicode):
+   def __repr__(self):
+     return "char(%s)" % unicode.__repr__(self)
+ 
++class byte(int):
++
++  def __repr__(self):
++    return "byte(%s)" % int.__repr__(self)
++
++class short(int):
++
++  def __repr__(self):
++    return "short(%s)" % int.__repr__(self)
++
++class int32(int):
++
++  def __repr__(self):
++      return "int32(%s)" % int.__repr__(self)
++
++class ubyte(int):
++
++  def __repr__(self):
++    return "ubyte(%s)" % int.__repr__(self)
++
++class ushort(int):
++
++  def __repr__(self):
++    return "ushort(%s)" % int.__repr__(self)
++
++class uint(int):
++
++  def __repr__(self):
++      return "uint(%s)" % int.__repr__(self)
++
++class float32(float):
++
++  def __repr__(self):
++    return "float32(%s)" % float.__repr__(self)
++
++class decimal32(int):
++
++  def __repr__(self):
++    return "decimal32(%s)" % int.__repr__(self)
++
++class decimal64(int):
++
++  def __repr__(self):
++    return "decimal64(%s)" % int.__repr__(self)
++
++class decimal128(bytes):
++
++  def __repr__(self):
++    return "decimal128(%s)" % bytes.__repr__(self)
++
+ class Described(object):
+ 
+   def __init__(self, descriptor, value):
+@@ -1887,42 +1937,42 @@ class Data:
+     If the current node is an unsigned byte, returns its value,
+     returns 0 otherwise.
+     """
+-    return pn_data_get_ubyte(self._data)
++    return ubyte(pn_data_get_ubyte(self._data))
+ 
+   def get_byte(self):
+     """
+     If the current node is a signed byte, returns its value, returns 0
+     otherwise.
+     """
+-    return pn_data_get_byte(self._data)
++    return byte(pn_data_get_byte(self._data))
+ 
+   def get_ushort(self):
+     """
+     If the current node is an unsigned short, returns its value,
+     returns 0 otherwise.
+     """
+-    return pn_data_get_ushort(self._data)
++    return ushort(pn_data_get_ushort(self._data))
+ 
+   def get_short(self):
+     """
+     If the current node is a signed short, returns its value, returns
+     0 otherwise.
+     """
+-    return pn_data_get_short(self._data)
++    return short(pn_data_get_short(self._data))
+ 
+   def get_uint(self):
+     """
+     If the current node is an unsigned int, returns its value, returns
+     0 otherwise.
+     """
+-    return pn_data_get_uint(self._data)
++    return uint(pn_data_get_uint(self._data))
+ 
+   def get_int(self):
+     """
+     If the current node is a signed int, returns its value, returns 0
+     otherwise.
+     """
+-    return pn_data_get_int(self._data)
++    return int32(pn_data_get_int(self._data))
+ 
+   def get_char(self):
+     """
+@@ -1957,7 +2007,7 @@ class Data:
+     If the current node is a float, returns its value, raises 0
+     otherwise.
+     """
+-    return pn_data_get_float(self._data)
++    return float32(pn_data_get_float(self._data))
+ 
+   def get_double(self):
+     """
+@@ -1972,7 +2022,7 @@ class Data:
+     If the current node is a decimal32, returns its value, returns 0
+     otherwise.
+     """
+-    return pn_data_get_decimal32(self._data)
++    return decimal32(pn_data_get_decimal32(self._data))
+ 
+   # XXX: need to convert
+   def get_decimal64(self):
+@@ -1980,7 +2030,7 @@ class Data:
+     If the current node is a decimal64, returns its value, returns 0
+     otherwise.
+     """
+-    return pn_data_get_decimal64(self._data)
++    return decimal64(pn_data_get_decimal64(self._data))
+ 
+   # XXX: need to convert
+   def get_decimal128(self):
+@@ -1988,7 +2038,7 @@ class Data:
+     If the current node is a decimal128, returns its value, returns 0
+     otherwise.
+     """
+-    return pn_data_get_decimal128(self._data)
++    return decimal128(pn_data_get_decimal128(self._data))
+ 
+   def get_uuid(self):
+     """
+@@ -2140,19 +2190,29 @@ class Data:
+   put_mappings = {
+     None.__class__: lambda s, _: s.put_null(),
+     bool: put_bool,
+-    dict: put_dict,
+-    list: put_sequence,
+-    tuple: put_sequence,
+-    unicode: put_string,
+-    bytes: put_binary,
+-    symbol: put_symbol,
++    ubyte: put_ubyte,
++    ushort: put_ushort,
++    uint: put_uint,
++    ulong: put_ulong,
++    byte: put_byte,
++    short: put_short,
++    int32: put_int,
+     int: put_long,
+-    char: put_char,
+     long: put_long,
+-    ulong: put_ulong,
+-    timestamp: put_timestamp,
++    float32: put_float,
+     float: put_double,
++    decimal32: put_decimal32,
++    decimal64: put_decimal64,
++    decimal128: put_decimal128,
++    char: put_char,
++    timestamp: put_timestamp,
+     uuid.UUID: put_uuid,
++    bytes: put_binary,
++    unicode: put_string,
++    symbol: put_symbol,
++    list: put_sequence,
++    tuple: put_sequence,
++    dict: put_dict,
+     Described: put_py_described,
+     Array: put_py_array
+     }
+@@ -3998,5 +4058,15 @@ __all__ = [
+            "dispatch",
+            "symbol",
+            "timestamp",
+-           "ulong"
++           "ulong",
++           "byte",
++           "short",
++           "int32",
++           "ubyte",
++           "ushort",
++           "uint",
++           "float32",
++           "decimal32",
++           "decimal64",
++           "decimal128"
+            ]
+-- 
+1.9.3
+

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/6d0d6038/shims/qpid-proton-python/src/__init__.py
----------------------------------------------------------------------
diff --git a/shims/qpid-proton-python/src/__init__.py 
b/shims/qpid-proton-python/src/__init__.py
new file mode 100644
index 0000000..31d5a2e
--- /dev/null
+++ b/shims/qpid-proton-python/src/__init__.py
@@ -0,0 +1,18 @@
+#
+# 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.
+#

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/6d0d6038/shims/qpid-proton-python/src/proton-python-receive
----------------------------------------------------------------------
diff --git a/shims/qpid-proton-python/src/proton-python-receive 
b/shims/qpid-proton-python/src/proton-python-receive
new file mode 100755
index 0000000..080782d
--- /dev/null
+++ b/shims/qpid-proton-python/src/proton-python-receive
@@ -0,0 +1,106 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+# Issues:
+# * Capturing errors from client or broker
+# * Correct Message body types, use of cproton.Data class?
+
+import sys
+from proton.handlers import MessagingHandler
+from proton.reactor import Container
+from struct import pack, unpack
+
+class ReceiverError(StandardError):
+    def __init__(self, error_message):
+        super(ReceiverError, self).__init__(error_message)
+
+class Receiver(MessagingHandler):
+    def __init__(self, url, amqp_type, expected_num_messages):
+        super(Receiver, self).__init__()
+        self.url = url
+        self.amqp_type = amqp_type
+        self.received_value_list = []
+        self.expected = int(expected_num_messages)
+        self.received = 0
+
+    def get_received_value_list(self):
+        return self.received_value_list
+
+    def on_start(self, event):
+        event.container.create_receiver(self.url)
+
+    def on_message(self, event):
+        if event.message.id and event.message.id < self.received:
+            return # ignore duplicate message
+        if self.expected == 0 or self.received < self.expected:
+            if self.amqp_type == 'null' or \
+               self.amqp_type == 'boolean' or \
+               self.amqp_type == 'ubyte' or \
+               self.amqp_type == 'ushort' or \
+               self.amqp_type == 'uint' or \
+               self.amqp_type == 'ulong' or \
+               self.amqp_type == 'byte' or \
+               self.amqp_type == 'short' or \
+               self.amqp_type == 'int' or \
+               self.amqp_type == 'long' or \
+               self.amqp_type == 'decimal32' or \
+               self.amqp_type == 'decimal64' or \
+               self.amqp_type == 'decimal128' or \
+               self.amqp_type == 'timestamp' or \
+               self.amqp_type == 'uuid':
+                self.received_value_list.append(str(event.message.body))
+            elif self.amqp_type == 'float':
+                self.received_value_list.append('0x%08x' % unpack('!L', 
pack('!f', event.message.body))[0])
+            elif self.amqp_type == 'double':
+                self.received_value_list.append('0x%016x' % unpack('!Q', 
pack('!d', event.message.body))[0])
+            elif self.amqp_type == 'decimal128':
+                
self.received_value_list.append(event.message.body.encode('hex'))
+            elif self.amqp_type == 'char' or \
+                 self.amqp_type == 'binary' or \
+                 self.amqp_type == 'string' or \
+                 self.amqp_type == 'symbol':
+                self.received_value_list.append(event.message.body)
+            elif self.amqp_type == 'list' or \
+                 self.amqp_type == 'map':
+                self.received_value_list.append(event.message.body)
+            else:
+                print 'proton-python-receive: Unsupported AMQP type "%s"' % 
self.amqp_type
+                return
+            self.received += 1
+            if self.received == self.expected:
+                event.receiver.close()
+                event.connection.close()
+
+# --- main ---
+# Args: 1: Broker address (ip-addr:port)
+#       2: Queue name
+#       3: AMQP type
+#       4: Expected number of test values to receive
+try:
+    rcv = Receiver('%s/%s' % (sys.argv[1], sys.argv[2]), sys.argv[3], 
sys.argv[4])
+    Container(rcv).run()
+    print sys.argv[3]
+    for val in rcv.get_received_value_list():
+        print val
+except KeyboardInterrupt:
+    pass
+except Exception as e:
+    print 'proton-python-receive EXCEPTION:', e
+

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/6d0d6038/shims/qpid-proton-python/src/proton-python-send
----------------------------------------------------------------------
diff --git a/shims/qpid-proton-python/src/proton-python-send 
b/shims/qpid-proton-python/src/proton-python-send
new file mode 100755
index 0000000..2b8703d
--- /dev/null
+++ b/shims/qpid-proton-python/src/proton-python-send
@@ -0,0 +1,136 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+# Issues:
+# * Capturing errors from client or broker
+# * Correct Message body types, use of cproton.Data class?
+
+import sys
+from ast import literal_eval
+from proton import byte, char, decimal32, decimal64, decimal128, float32, 
int32, Message, short, symbol, timestamp, ubyte, uint, ulong, ushort
+from proton.handlers import MessagingHandler
+from proton.reactor import Container
+from shim_utils import StrToObj
+from struct import unpack
+from traceback import format_exc
+from uuid import UUID
+
+class Sender(MessagingHandler):
+    def __init__(self, url, amqp_type, test_value_list):
+        super(Sender, self).__init__()
+        self.url = url
+        self.amqp_type = amqp_type
+        self.test_value_list = test_value_list
+        self.sent = 0
+        self.confirmed = 0
+        self.total = len(test_value_list)
+
+    def on_start(self, event):
+        event.container.create_sender(self.url)
+
+    def on_sendable(self, event):
+        if self.sent == 0:
+            for test_value in self.test_value_list:
+                if event.sender.credit:
+                    message = self.create_message(test_value)
+                    if message is not None:
+                        event.sender.send(message)
+                        self.sent += 1
+                    else:
+                        event.connection.close()
+                        return
+
+    def create_message(self, test_value):
+        # Non-string types using literal_eval
+        if self.amqp_type == 'null':
+            return Message(id=(self.sent+1), body=None)
+        elif self.amqp_type == 'boolean':
+            return Message(id=(self.sent+1), body=True if test_value == 'True' 
else False)
+        elif self.amqp_type == 'ubyte':
+            return Message(id=(self.sent+1), 
body=ubyte(literal_eval(test_value)))
+        elif self.amqp_type == 'ushort':
+            return Message(id=(self.sent+1), 
body=ushort(literal_eval(test_value)))
+        elif self.amqp_type == 'uint':
+            return Message(id=(self.sent+1), 
body=uint(literal_eval(test_value)))
+        elif self.amqp_type == 'ulong':
+            return Message(id=(self.sent+1), 
body=ulong(literal_eval(test_value)))
+        elif self.amqp_type == 'byte':
+            return Message(id=(self.sent+1), 
body=byte(literal_eval(test_value)))
+        elif self.amqp_type == 'short':
+            return Message(id=(self.sent+1), 
body=short(literal_eval(test_value)))
+        elif self.amqp_type == 'int':
+            return Message(id=(self.sent+1), 
body=int32(literal_eval(test_value)))
+        elif self.amqp_type == 'long':
+            return Message(id=(self.sent+1), 
body=long(literal_eval(test_value)))
+        elif self.amqp_type == 'float':
+            return Message(id=(self.sent+1), body=float32(unpack('!f', 
test_value[2:].decode('hex'))[0]))
+        elif self.amqp_type == 'double':
+            return Message(id=(self.sent+1), body=unpack('!d', 
test_value[2:].decode('hex'))[0])
+        elif self.amqp_type == 'decimal32':
+            return Message(id=(self.sent+1), 
body=decimal32(literal_eval(test_value)))
+        elif self.amqp_type == 'decimal64':
+            return Message(id=(self.sent+1), 
body=decimal64(literal_eval(test_value)))
+        elif self.amqp_type == 'decimal128':
+            return Message(id=(self.sent+1), 
body=decimal128(test_value.decode('hex')))
+        elif self.amqp_type == 'char':
+            return Message(id=(self.sent+1), body=char(test_value))
+        elif self.amqp_type == 'timestamp':
+            return Message(id=(self.sent+1), 
body=timestamp(literal_eval(test_value)))
+        elif self.amqp_type == 'uuid':
+            return Message(id=(self.sent+1), body=UUID(test_value))
+        elif self.amqp_type == 'binary':
+            return Message(id=(self.sent+1), body=bytes(test_value))
+        elif self.amqp_type == 'string':
+            return Message(id=(self.sent+1), body=unicode(test_value))
+        elif self.amqp_type == 'symbol':
+            return Message(id=(self.sent+1), body=symbol(test_value))
+        elif self.amqp_type == 'list':
+            return Message(id=(self.sent+1), 
body=StrToObj(list(test_value).__iter__()).run())
+        elif self.amqp_type == 'map':
+            return Message(id=(self.sent+1), 
body=StrToObj(list(test_value).__iter__()).run())
+        else:
+            print 'proton-python-send: Unsupported AMQP type "%s"' % 
self.amqp_type
+            return None
+
+    def on_accepted(self, event):
+        self.confirmed += 1
+        if self.confirmed == self.total:
+            event.connection.close()
+
+    def on_disconnected(self, event):
+        self.sent = self.confirmed
+
+    @staticmethod
+    def _map_string_to_map(str_list):
+        return {}
+
+# --- main ---
+# Args: 1: Broker address (ip-addr:port)
+#       2: Queue name
+#       3: AMQP type
+#       4...n: Test values as strings
+try:
+    Container(Sender('%s/%s' % (sys.argv[1], sys.argv[2]), sys.argv[3], 
sys.argv[4:])).run()
+except KeyboardInterrupt:
+    pass
+except Exception as e:
+    print 'proton-python-send EXCEPTION:', e
+    print format_exc()
+        
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/6d0d6038/src/py/qpid-interop-test/__init__.py
----------------------------------------------------------------------
diff --git a/src/py/qpid-interop-test/__init__.py 
b/src/py/qpid-interop-test/__init__.py
new file mode 100644
index 0000000..7cecede
--- /dev/null
+++ b/src/py/qpid-interop-test/__init__.py
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+import shim_utils
+import types
+

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/6d0d6038/src/py/qpid-interop-test/shim_utils.py
----------------------------------------------------------------------
diff --git a/src/py/qpid-interop-test/shim_utils.py 
b/src/py/qpid-interop-test/shim_utils.py
new file mode 100755
index 0000000..ba70654
--- /dev/null
+++ b/src/py/qpid-interop-test/shim_utils.py
@@ -0,0 +1,388 @@
+#!/usr/bin/env python
+
+"""
+Module containing utilities for the shims used in qpid-interop.
+"""
+
+#
+# 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.
+#
+
+import sys
+import proton
+from ast import literal_eval
+from uuid import UUID
+
+class StrToObj(object):
+    """
+    Class StrObj consumes the characters from a string iterator str_itr
+    and interprets the string so as to create the equivalent python
+    object(s) in a manner similar to ast.leteral_eval(). The difference
+    is that this version supports proton types as a part of the
+    interpretation, so that a string such as
+
+    '[1, char('a'), timestamp(1234567)]'
+
+    can be parsed.
+
+    The following compound python types can be handled:
+      [...] list
+      (...) tuple
+      {...} dict
+    which may contain any legal combination of simple python types
+    including strings, embedded lists, tuples and dicts, and the
+    supported proton types. All of the simple types must be supported
+    by ast.leteral_eval() to be supported in this class.
+
+    The following proton types are handled:
+      proton.char
+      proton.symbol
+      proton.timestamp
+      proton.ulong
+
+    Typical usage:
+    -------------
+    For a python string object str containing the string to be parsed,
+    it is necessary to obtain an iterator to the individual characters
+    of the string. This can be done as follows:
+
+    obj = StrToObj(list(str).__iter__()).run()
+
+    where obj will contain an instance of the object described in the
+    string. The run() method starts the process of consuming characters
+    from the iterator and interpreting them.
+
+    This class is also the parent class of a number of more specialized
+    subclasses for handling sub-sections of the string interpretation:
+
+              StrToObj (this class)
+                  ^
+                  |
+       +----------+-------------+
+       |          |             |
+    StrToStr   StrToType    StrToSeq
+                                ^
+                                |
+                  +-------------+-----------+
+                  |             |           |
+              StrToList    StrToTuple    StrToDict
+
+    and which make up all the elements of the composite types needed
+    for this application. Simple types are handled by calling
+    ast.literal_evel(), and the types that can be handled are
+    determined by that function.
+    """
+
+    def __init__(self, str_itr):
+        self._str_itr = str_itr
+        self._done = False
+        self._obj = None
+        self._str_buff = ''
+
+    def get_obj(self):
+        """
+        Return object created as a result of parsing the string
+        supplied to the constructor and using the run() method.
+        """
+        return self._obj
+
+    def run(self):
+        """
+        Starts the process of 'consuming' characters from the supplied
+        string iterator and of interpreting them in the context of the
+        previous characters.
+        """
+        try:
+            while not self._done:
+                char = self._str_itr.next()
+                if not self._process_char(char):
+                    self._str_buff += char
+        except StopIteration:
+            if len(self._str_buff) > 0 and self._obj is None:
+                self._obj = literal_eval(self._str_buff)
+                self._str_buff = ''
+        return self._obj
+
+    def _add_elt(self, elt):
+        """
+        Sets the object itself to parameter elt. This function is
+        frequently overloaded by its subclasses.
+        """
+        self._obj = elt
+
+    def _process_char(self, char):
+        """
+        Consume and process a single character in the context of those
+        that have passed before. This function is overloaded by its
+        subclasses to provide either additional or alternative
+        processing.
+
+        This function checks for the characters that start a list
+        ('['), a dict ('{'), a tuple ('(' when preceded only by
+        whitespace), a string ('\'' or '"') or special proton types
+        ('(' when preceded by a constructor string).
+
+        If any of these chars are found, a new object of the
+        appropriate class is constructed to handle the processing of
+        that type, and when its run() function completes, a constructed
+        object will be returned.
+        """
+        if char == '[':
+            self._add_elt(StrToList(char, self._str_itr).run())
+            return True
+        if char == '{':
+            self._add_elt(StrToDict(char, self._str_itr).run())
+            return True
+        if char == '(':
+            if len(self._str_buff.strip()) == 0:
+                self._add_elt(StrToTuple(char, self._str_itr).run())
+            else:
+                self._add_elt(StrToType(char, self._str_itr,
+                                        self._str_buff).run())
+                self._str_buff = ''
+            return True
+        if char == '\'' or char == '"':
+            str_prefix = None
+            if len(self._str_buff.strip()) > 0:
+                str_prefix = self._str_buff.strip()
+            self._add_elt(StrToStr(char, self._str_itr, str_prefix).run())
+            self._str_buff = ''
+            return True
+        return False
+
+
+class StrToStr(StrToObj):
+    """
+    Class to consume a string delimited by either ' or " chars.
+    """
+
+    def __init__(self, delim_char, str_itr, str_prefix):
+        super(StrToStr, self).__init__(str_itr)
+        self._delim_char = delim_char
+        self._str_prefix = str_prefix
+        self._escape_flag = False
+
+    def _process_char(self, char):
+        """
+        This function processes a python string type, and continues
+        consuming characters until another valid delimiter character
+        ('\'' or '"') is encountered. A delimiter character that is
+        preceded by an escape character ('\\') is excluded.
+
+        The entire string may have a prefix of one or more characters.
+        Only the u prefix (eg u'hello') has any effect; the b prefix
+        has no effect in Python 2.x but paves the way for Python 3.x
+        where it does have significance, and the r prefix affects the
+        display or printing of strings only.
+        """
+        if char == '\\':
+            self._escape_flag = not self._escape_flag
+        elif char == self._delim_char and not self._escape_flag:
+            if self._str_prefix is None:
+                self._obj = self._str_buff
+            elif 'u' in self._str_prefix or 'U' in self._str_prefix:
+                self._obj = unicode(self._str_buff)
+            else:
+                self._obj = self._str_buff # Ignore other prefixes (b, B, r, R)
+            self._str_buff = ''
+            self._done = True
+            return True
+        else:
+            self._escape_flag = False
+        return False
+
+
+class StrToType(StrToObj):
+    """
+    Class for processing special proton python types.
+    """
+
+    def __init__(self, _, str_itr, type_str):
+        super(StrToType, self).__init__(str_itr)
+        self._type_str = type_str.strip()
+        self._val = None
+
+    def _add_elt(self, elt):
+        """
+        Sets the value portion of the type to elt. This is then used
+        in the constructor of the type.
+        """
+        self._val = elt
+
+    def _process_char(self, char):
+        """
+        Process characters to identify the value to the constructor of
+        the proton type being processed. The proton type string is
+        passed to the constructor, and when the '(' char is encountered,
+        this function is used until the matching ')' char is reached.
+
+        The parent StrToObj._process_char() is called first to process
+        any compound types and strings which may be present as a
+        constructor value.
+        """
+        if super(StrToType, self)._process_char(char):
+            return True
+        if char == ')':
+            if len(self._str_buff.strip()) > 0:
+                if self._val is not None:
+                    # This condition should not ever arise, either
+                    # self._val is set OR self._str_buff contains a
+                    # value, but not both.
+                    raise RuntimeError('self._val=%s and self._str_buff=%s' %
+                                       (self._val, self._str_buff))
+                self._val = literal_eval(
+                    self._str_buff[self._str_buff.find('(')+1:])
+            if self._type_str == 'ubyte':
+                self._obj = proton.ubyte(self._val)
+            elif self._type_str == 'ushort':
+                self._obj = proton.ushort(self._val)
+            elif self._type_str == 'uint':
+                self._obj = proton.uint(self._val)
+            elif self._type_str == 'ulong':
+                self._obj = proton.ulong(self._val)
+            elif self._type_str == 'byte':
+                self._obj = proton.byte(self._val)
+            elif self._type_str == 'short':
+                self._obj = proton.short(self._val)
+            elif self._type_str == 'int32':
+                self._obj = proton.int32(self._val)
+            elif self._type_str == 'float32':
+                self._obj = proton.float32(self._val)
+            elif self._type_str == 'decimal32':
+                self._obj = proton.decimal32(self._val)
+            elif self._type_str == 'decimal64':
+                self._obj = proton.decimal64(self._val)
+            elif self._type_str == 'decimal128':
+                self._obj = proton.decimal128(self._val)
+            elif self._type_str == 'char':
+                self._obj = proton.char(self._val)
+            elif self._type_str == 'symbol':
+                self._obj = proton.symbol(self._val)
+            elif self._type_str == 'timestamp':
+                self._obj = proton.timestamp(self._val)
+            elif self._type_str == 'UUID':
+                self._obj = UUID(self._val)
+            else:
+                raise ValueError('StrToType: unknown type \'%s\'' % 
self._type_str)
+            self._str_buff = ''
+            self._done = True
+            return True
+
+
+class StrToSeq(StrToObj):
+    """
+    Class which consumes comma-delimited sequence types such as lists,
+    dicts and tuples.
+    """
+
+    def __init__(self, _, str_itr, close_char, seq_type):
+        super(StrToSeq, self).__init__(str_itr)
+        self._close_char = close_char
+        self._obj = seq_type()
+
+    def _process_char(self, char):
+        """
+        Processing for container sequence types that use a ','
+        character as an element delimiter. Individual elements may be
+        any legal type, including proton types and nested containers.
+        """
+        if super(StrToSeq, self)._process_char(char):
+            return True
+        if char == ',' or char == self._close_char:
+            if char == self._close_char:
+                self._done = True
+            if len(self._str_buff.strip()) > 0:
+                self._add_elt(literal_eval(self._str_buff.strip()))
+            self._str_buff = ''
+            return True
+        return False
+
+class StrToList(StrToSeq):
+    """
+    Class which consumes a list of the form '[...]'.
+    """
+
+    def __init__(self, char, str_itr):
+        super(StrToList, self).__init__(char, str_itr, ']', list)
+
+    def _add_elt(self, elt):
+        """
+        Adds an additional element into the list object.
+        """
+        self._obj.append(elt)
+
+class StrToTuple(StrToSeq):
+    """
+    Class which consumes a tuple of the form '(...)'. Tuples without
+    the enclosing braces are not currently supported, however.
+    """
+
+    def __init__(self, char, str_itr):
+        super(StrToTuple, self).__init__(char, str_itr, ')', tuple)
+
+    def _add_elt(self, elt):
+        """
+        Adds an additional element into the tuple object.
+        """
+        self._obj = self._obj + (elt,)
+
+class StrToDict(StrToSeq):
+    """
+    Class which consumed a dict of the form '{...}'.
+    """
+
+    def __init__(self, c, str_itr):
+        super(StrToDict, self).__init__(c, str_itr, '}', dict)
+        self._key = None
+
+    def _add_elt(self, elt):
+        """
+        Called twice for dicts; the first call sets the key object,
+        and the second call sets the value object and then inserts
+        the entire key:value pair into the map.
+        """
+        if self._key is None:
+            self._key = elt
+        else:
+            self._obj[self._key] = elt
+            self._key = None
+
+    def _process_char(self, c):
+        """
+        Processing of characters for the python dict type using the
+        'key:value' syntax. The key and value may be any legal python
+        or proton type, including embedded containers.
+        """
+        if super(StrToDict, self)._process_char(c):
+            return True
+        if c == ':':
+            if len(self._str_buff.strip()) > 0:
+                self._add_elt(literal_eval(self._str_buff.strip()))
+            self._str_buff = ''
+            return True
+        return False
+
+# --- main ---
+
+# This command-line entry point is for testing only, it does not
+# provide any useful functionality on its own.
+
+#if len(sys.argv) == 2:
+#    print '%r' % StrToObj(list(sys.argv[1]).__iter__()).run()
+#else:
+#    print 'Usage: shim_utils <string>'

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/6d0d6038/src/py/qpid-interop-test/shim_utils.pyc
----------------------------------------------------------------------
diff --git a/src/py/qpid-interop-test/shim_utils.pyc 
b/src/py/qpid-interop-test/shim_utils.pyc
new file mode 100644
index 0000000..49b13d2
Binary files /dev/null and b/src/py/qpid-interop-test/shim_utils.pyc differ

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/6d0d6038/src/py/qpid-interop-test/types/simple_type_tests.py
----------------------------------------------------------------------
diff --git a/src/py/qpid-interop-test/types/simple_type_tests.py 
b/src/py/qpid-interop-test/types/simple_type_tests.py
new file mode 100755
index 0000000..2e3fc20
--- /dev/null
+++ b/src/py/qpid-interop-test/types/simple_type_tests.py
@@ -0,0 +1,357 @@
+#!/usr/bin/env python
+
+"""
+Module to test AMQP primitive types across different APIs
+"""
+
+#
+# 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.
+#
+
+import argparse
+import unittest
+
+from ast import literal_eval
+from itertools import product
+from os import getenv
+from proton import char, int32, symbol, timestamp, ulong
+from shim_utils import StrToObj
+from subprocess import check_output
+from time import mktime, time
+from uuid import UUID, uuid4
+
+QPID_INTEROP_TEST_HOME = getenv('QPID_INTEROP_TEST_HOME') # TODO - propose a 
sensible default when installation details are worked out
+
+
+class SimpleTypeTestError(StandardError):
+    """
+    Error class for use in simpe AMQP type tests
+    """
+    def __init__(self, error_message):
+        super(SimpleTypeTestError, self).__init__(error_message)
+
+
+class AmqpPrimitiveTypes(object):
+    """
+    Class which contains all the described AMQP primitive types and the test 
values to be used in testing.
+    """
+
+    TYPE_MAP = {
+        'null': [None],
+        'boolean': [True, False],
+        'ubyte': [0x0, 0x7f, 0x80, 0xff],
+        'ushort': [0x0, 0x7fff, 0x8000, 0xffff],
+        'uint': [0x0, 0x7fffffff, 0x80000000, 0xffffffff],
+        'ulong': [0x0, 0x01, 0xff, 0x100, 0x7fffffffffffffff, 
0x8000000000000000, 0xffffffffffffffff],
+        'byte': [0x0, 0x7f, 0x80, 0xff],
+        'short': [-0x8000, -0x1, 0x0, 0x7fff],
+        'int': [-0x80000000, -0x1, 0x0, 0x7fffffff],
+        'long': [-0x8000000000000000, -0x81, -0x80, -0x01, 0x0, 0x7f, 0x80, 
0x7fffffffffffffff],
+        # Because of difficulty with rounding of floating point numbers, we 
use the binary representation instead
+        # which should be exact when comparing.
+        'float': ['0x00000000', # 0.0
+                  '0x80000000', # -0.0
+                  '0x40490fdb', # pi (3.14159265359) positive decimal
+                  '0xc02df854', # -e (-2.71828182846) negative decimal
+                  '0x00000001', # Smallest positive denormalized number
+                  '0x80000001', # Smallest negative denormalized number
+                  '0x007fffff', # Largest positive denormalized number
+                  '0x807fffff', # Largest negative denormalized number
+                  '0x00800000', # Smallest positive normalized number
+                  '0x80800000', # Smallest negative normalized number
+                  '0x7f7fffff', # Largest positive normalized number
+                  '0xff7fffff', # Largest negative normalized number
+                  '0x7f800000', # +Infinity
+                  '0xff800000', # -Infinity
+                  '0x7fffffff', # +NaN 
+                  '0xffffffff'], # -NaN 
+        'double': ['0x0000000000000000', # 0.0
+                   '0x8000000000000000', # -0.0
+                   '0x400921fb54442eea', # pi (3.14159265359) positive decimal
+                   '0xc005bf0a8b145fcf', # -e (-2.71828182846) negative decimal
+                   '0x0000000000000001', # Smallest positive denormalized 
number
+                   '0x8000000000000001', # Smallest negative denormalized 
number
+                   '0x000fffffffffffff', # Largest positive denormalized number
+                   '0x800fffffffffffff', # Largest negative denormalized number
+                   '0x0010000000000000', # Smallest positive normalized number
+                   '0x8010000000000000', # Smallest positive normalized number
+                   '0x7fefffffffffffff', # Largest positive normalized number
+                   '0xffefffffffffffff', # Largest negative normalized number
+                   '0x7ff0000000000000', # +Infinity
+                   '0xfff0000000000000', # -Infinity
+                   '0x7fffffffffffffff', # +NaN
+                   '0xffffffffffffffff'], # -NaN
+        'decimal32': [0, 100, -1000],#,
+        'decimal64': [0, 100, -1000],#,
+        #'decimal128': [b'00000000000000000000000000000000',
+        #               b'00000000000000000000000000000100',
+        #               b'0102030405060708090a0b0c0d0e0f00'],
+        #'char': [u'a', u'Z', u'0', u'\x01', u'\x7f'], #TODO: Char value \x00 
causes problems in check_output(), find another solution
+        # timestamp must be in milliseconds since the unix epoch
+        'timestamp': [0, int(mktime((2000, 1, 1, 0, 0, 0, 5, 1, 0))*1000), 
int(time()*1000)],
+        'uuid': [UUID(int=0x0), UUID('00010203-0405-0607-0809-0a0b0c0d0e0f'), 
uuid4()],
+        'binary': [bytes(), bytes(12345), b'Hello, world!', 
b'\x01\x02\x03\x04\x05\xff',
+                   b'The quick brown fox jumped over the lazy cow 0123456789' 
* 1000],
+        # strings must be unicode to comply with AMQP spec
+        'string': [u'', u'Hello, world!', u'"Hello, world!"', u"Charlie's 
peach",
+                   u'The quick brown fox jumped over the lazy cow 0123456789' 
* 1000],
+        'symbol': ['', 'myDomain.123', 'domain.0123456789.' * 1000],
+        'list': [[],
+                 [1, -2, 3.14],
+                 [u'a', u'b', u'c'],
+                 [ulong(12345), timestamp(int(time()*1000)), int32(-25), 
uuid4(), symbol('a.b.c')],
+                 [[], None, [1,2,3], {1:'one', 2:'two', 3:'three', 4:True, 
5:False, 6:None}, True, False, char(u'5')],
+                 [[],[[],[[],[],[]],[]],[]],
+                 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] * 1000],
+        'map': [{}, {1:u'one', 2:u'two'}, {None:None, 1:1, '2':'2', 
True:False, False:True}]#,
+        #'array': [[], [1,2,3], ['Hello', 'world']]
+        }
+
+    @staticmethod
+    def get_type_list():
+        """Return a list of simple AMQP types which this test suite supports"""
+        return AmqpPrimitiveTypes.TYPE_MAP.keys()
+
+    @staticmethod
+    def get_test_value_list(amqp_type):
+        """Return a list of test values to use when testing the supplied AMQP 
type."""
+        if amqp_type not in AmqpPrimitiveTypes.TYPE_MAP.keys():
+            return None
+        return AmqpPrimitiveTypes.TYPE_MAP[amqp_type]
+
+
+class AmqpTypeTestCase(unittest.TestCase):
+    """
+    Abstract base class for AMQP Type test cases
+    """
+
+    def run_test(self, broker_addr, amqp_type, test_value_list, send_shim, 
receive_shim):
+        """
+        Run this test by invoking the shim send method to send the test 
values, followed by the shim receive method
+        to receive the values. Finally, compare the sent values with the 
received values.
+        """
+        if len(test_value_list) > 0:
+            queue_name = 'qpid-interop.simple_type_tests.%s.%s.%s' % 
(amqp_type, send_shim.NAME, receive_shim.NAME)
+            send_error_text = send_shim.send(broker_addr, queue_name, 
amqp_type, test_value_list)
+            if len(send_error_text) > 0:
+                self.fail('Send shim \'%s\':\n%s' % (send_shim.NAME, 
send_error_text))
+            receive_text = receive_shim.receive(broker_addr, queue_name, 
amqp_type, len(test_value_list))
+            if type(receive_text) is list:
+                self.assertEqual(receive_text, test_value_list, msg='\n    
sent:%s\nreceived:%s' % \
+                                 (test_value_list, receive_text))
+            else:
+                self.fail(receive_text)
+        else:
+            self.fail('Type %s has no test values' % amqp_type)
+
+
+def create_testcase_class(broker_addr, amqp_type, test_value_list, 
shim_product):
+    """
+    Class factory function which creates new subclasses to AmqpTypeTestCase.
+    """
+
+    def __repr__(self):
+        """Print the class name"""
+        return self.__class__.__name__
+
+    def add_test_method(cls, broker_addr, send_shim, receive_shim):
+        """Function which creates a new test method in class cls"""
+
+        def inner_test_method(self):
+            self.run_test(self.broker_addr, self.amqp_type, 
self.test_value_list, send_shim, receive_shim)
+
+        inner_test_method.__name__ = 'test_%s_%s' % (send_shim.NAME, 
receive_shim.NAME)
+        inner_test_method.__doc__ = 'AMQP type \'%s\' interop test: %s -> %s' 
% \
+                                    (amqp_type, send_shim.NAME, 
receive_shim.NAME)
+        setattr(cls, inner_test_method.__name__, inner_test_method)
+
+    class_name = amqp_type.title() + 'TestCase'
+    class_dict = {'__name__': class_name,
+                  '__repr__': __repr__,
+                  '__doc__': 'Test case for AMQP 1.0 simple type \'%s\'' % 
amqp_type,
+                  'amqp_type': amqp_type,
+                  'broker_addr': broker_addr,
+                  'test_value_list': test_value_list}
+    new_class = type(class_name, (AmqpTypeTestCase,), class_dict)
+    for send_shim, receive_shim in shim_product:
+        add_test_method(new_class, broker_addr, send_shim, receive_shim)
+    return new_class
+
+
+class Shim(object):
+    """
+    Abstract shim class, parent of all shims.
+    """
+    NAME = None
+    ENV = []
+    SHIM_LOC = None
+    SEND = None
+    RECEIVE = None
+
+    def send(self, broker_addr, queue_name, amqp_type, test_value_list):
+        """
+        Send the values of type amqp_type in test_value_list to queue 
queue_name. Return output (if any) from stdout.
+        """
+        arg_list = [self.SEND, broker_addr, queue_name, amqp_type]
+        for test_value in test_value_list:
+            if amqp_type == 'string' or amqp_type == 'char' or amqp_type == 
'float' or amqp_type == 'double':
+                arg_list.append(test_value) # Not using str() on strings 
preserves the unicode prefix u'...'
+            else:
+                arg_list.append(str(test_value))
+        #print
+        #print '>>>', arg_list
+        return check_output(arg_list)
+
+    def receive(self, broker_addr, queue_name, amqp_type, num_test_values):
+        """
+        Receive num_test_values messages containing type amqp_type from queue 
queue_name. If the first line returned
+        from stdout is the AMQP type, then the rest is assumed to be the 
returned test value list. Otherwise error
+        output is assumed.
+        """
+        try:
+            arg_list = [self.RECEIVE, broker_addr, queue_name, amqp_type, 
str(num_test_values)]
+            #print '>>>', arg_list
+            output = check_output(arg_list)
+            str_tvl = output.split('\n')[0:-1] # remove trailing \n
+            if str_tvl[0] == amqp_type:
+                received_test_value_list = []
+                for stv in str_tvl[1:]:
+                    # Non-string types using literal_eval
+                    if amqp_type == 'null' or \
+                       amqp_type == 'boolean' or \
+                       amqp_type == 'ubyte' or \
+                       amqp_type == 'ushort' or \
+                       amqp_type == 'uint' or \
+                       amqp_type == 'ulong' or \
+                       amqp_type == 'byte' or \
+                       amqp_type == 'short' or \
+                       amqp_type == 'int' or \
+                       amqp_type == 'long' or \
+                       amqp_type == 'decimal32' or \
+                       amqp_type == 'decimal64' or \
+                       amqp_type == 'timestamp':
+                        received_test_value_list.append(literal_eval(stv))
+                    # Non-string types not using literal_evel
+                    elif amqp_type == 'uuid':
+                        received_test_value_list.append(UUID(stv))
+                    elif amqp_type == 'binary':
+                        received_test_value_list.append(bytes(stv))
+                    # String  and float types used as-is
+                    elif amqp_type == 'float' or \
+                         amqp_type == 'double' or \
+                         amqp_type == 'decimal128' or \
+                         amqp_type == 'char' or \
+                         amqp_type == 'string' or \
+                         amqp_type == 'symbol':
+                        received_test_value_list.append(stv)
+                    elif amqp_type == 'list' or \
+                         amqp_type == 'map':
+                        
received_test_value_list.append(StrToObj(list(stv).__iter__()).run())
+                    else:
+                        raise SimpleTypeTestError('ERROR: Shim.receive(): AMQP 
type \'%s\' not implemented' % amqp_type)
+                return received_test_value_list
+            else:
+                return output # return error string
+        except Exception as e:
+            return str(e) + '\n' + output
+
+
+class ProtonPythonShim(Shim):
+    """
+    Shim for qpid-proton Python client
+    """
+    NAME = 'ProtonPython'
+    SHIM_LOC = QPID_INTEROP_TEST_HOME + '/shims/qpid-proton-python/src/'
+    SEND = SHIM_LOC + 'proton-python-send'
+    RECEIVE = SHIM_LOC + 'proton-python-receive'
+
+
+class QpidJmsShim(Shim):
+    """
+    Shim for qpid-jms JMS client
+    """
+    NAME = 'QpidJms'
+    SHIM_LOC = '/shims/qpid-jms/src/main/java/'
+    SEND = SHIM_LOC + 
'org/apache/qpid/qpid-interop-test/shim/ProtonJmsReceiver'
+    RECEIVE = SHIM_LOC + 
'org/apache/qpid/qpid-interop-test/shim/ProtonJmsReceiver'
+
+    
+# SHIM_MAP contains an instance of each client language shim that is to be 
tested as a part of this test. For
+# every shim in this list, a test is dynamically constructed which tests it 
against itself as well as every
+# other shim in the list.
+#
+# As new shims are added, add them into this map to have them included in the 
test cases.
+SHIM_MAP = {ProtonPythonShim.NAME: ProtonPythonShim()}
+
+
+class TestOptions(object):
+    def __init__(self):
+        parser = argparse.ArgumentParser(description='Qpid-interop AMQP client 
interoparability test suite')
+        parser.add_argument('--broker', action='store', 
default='localhost:5672', metavar='BROKER:PORT',
+                            help='Broker against which to run test suite.')
+#        test_group = parser.add_mutually_exclusive_group()
+#        test_group.add_argument('--include-test', action='append', 
metavar='TEST-NAME',
+#                                help='Name of test to include')
+#        test_group.add_argument('--exclude-test', action='append', 
metavar='TEST-NAME',
+#                                help='Name of test to exclude')
+#        type_group = test_group.add_mutually_exclusive_group()
+#        type_group.add_argument('--include-type', action='append', 
metavar='AMQP-TYPE',
+#                                help='Name of AMQP type to include. Supported 
types:\n%s' %
+#                                sorted(AmqpPrimitiveTypes.TYPE_MAP.keys()))
+        parser.add_argument('--exclude-type', action='append', 
metavar='AMQP-TYPE',
+                            help='Name of AMQP type to exclude. Supported 
types:\n%s' %
+                            sorted(AmqpPrimitiveTypes.TYPE_MAP.keys()))
+#        shim_group = test_group.add_mutually_exclusive_group()
+#        shim_group.add_argument('--include-shim', action='append', 
metavar='SHIM-NAME',
+#                                help='Name of shim to include. Supported 
shims:\n%s' % SHIM_NAMES)
+        parser.add_argument('--exclude-shim', action='append', 
metavar='SHIM-NAME',
+                             help='Name of shim to exclude. Supported 
shims:\n%s' % sorted(SHIM_MAP.keys()))
+        self.args = parser.parse_args()
+        
+
+#--- Main program start ---
+
+if __name__ == '__main__':
+
+    args = TestOptions().args
+    #print 'args:', args
+
+    # TEST_CASE_CLASSES is a list that collects all the test classes that are 
constructed. One class is constructed
+    # per AMQP type used as the key in map AmqpPrimitiveTypes.TYPE_MAP.
+    TEST_CASE_CLASSES = []
+
+    # TEST_SUITE is the final suite of tests that will be run and which 
contains all the dynamically created
+    # type classes, each of which contains a test for the combinations of 
client shims
+    TEST_SUITE = unittest.TestSuite()
+    
+    # Remove shims excluded from the command-line
+    if args.exclude_shim is not None:
+        for shim in args.exclude_shim:
+            SHIM_MAP.pop(shim)          
+    # Create test classes dynamically
+    for at in sorted(AmqpPrimitiveTypes.get_type_list()):
+        if args.exclude_type is None or at not in args.exclude_type:
+            test_case_class = create_testcase_class(args.broker,
+                                                    at,
+                                                    
AmqpPrimitiveTypes.get_test_value_list(at),
+                                                    product(SHIM_MAP.values(), 
repeat=2))
+            TEST_CASE_CLASSES.append(test_case_class)
+            TEST_SUITE.addTest(unittest.makeSuite(test_case_class))
+
+    # Finally, run all the dynamically created tests
+    unittest.TextTestRunner(verbosity=2).run(TEST_SUITE)


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to