Repository: qpid-interop-test
Updated Branches:
  refs/heads/master 83b89fe40 -> 514bac751


http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/514bac75/src/python/qpid_interop_test/jms_hdrs_props_test.py
----------------------------------------------------------------------
diff --git a/src/python/qpid_interop_test/jms_hdrs_props_test.py 
b/src/python/qpid_interop_test/jms_hdrs_props_test.py
new file mode 100755
index 0000000..134f465
--- /dev/null
+++ b/src/python/qpid_interop_test/jms_hdrs_props_test.py
@@ -0,0 +1,520 @@
+#!/usr/bin/env python
+
+"""
+Module to test JMS headers and properties
+"""
+
+#
+# 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 sys
+import unittest
+
+from itertools import product
+from json import dumps
+from os import getenv, path
+
+from proton import symbol
+import qpid_interop_test.broker_properties
+import qpid_interop_test.shims
+from qpid_interop_test.test_type_map import TestTypeMap
+
+
+# TODO: propose a sensible default when installation details are worked out
+QPID_INTEROP_TEST_HOME = getenv('QPID_INTEROP_TEST_HOME')
+if QPID_INTEROP_TEST_HOME is None:
+    print 'ERROR: Environment variable QPID_INTEROP_TEST_HOME is not set'
+    sys.exit(1)
+MAVEN_REPO_PATH = getenv('MAVEN_REPO_PATH', path.join(getenv('HOME'), '.m2', 
'repository'))
+
+class JmsMessageTypes(TestTypeMap):
+    """
+    Class which contains all the described JMS message types and the test 
values to be used in testing.
+    """
+
+    COMMON_SUBMAP = {
+        'boolean': ['True',
+                    'False'],
+        'byte': ['-0x80',
+                 '-0x1',
+                 '0x0',
+                 '0x7f'],
+        '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
+                   '0x8010000000000000', # Largest negative denormalized number
+                   '0x7fefffffffffffff', # Largest positive normalized number
+                   '0xffefffffffffffff', # Largest negative normalized number
+                   '0x7ff0000000000000', # +Infinity
+                   '0xfff0000000000000', # -Infinity
+                   '0x7ff8000000000000'], # +NaN
+        '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  # PROTON-1149 - fails on RHEL7
+                  #'0xff800000', # -Infinity # PROTON-1149 - fails on RHEL7
+                  '0x7fc00000'], # +NaN
+        'int': ['-0x80000000',
+                '-0x81',
+                '-0x80',
+                '-0x1',
+                '0x0',
+                '0x7f',
+                '0x80',
+                '0x7fffffff'],
+        'long': ['-0x8000000000000000',
+                 '-0x81',
+                 '-0x80',
+                 '-0x1',
+                 '0x0',
+                 '0x7f',
+                 '0x80',
+                 '0x7fffffffffffffff'],
+        'short': ['-0x8000',
+                  '-0x1',
+                  '0x0',
+                  '0x7fff'],
+        'string': ['',
+                   'Hello, world',
+                   '"Hello, world"',
+                   "Charlie's \"peach\"",
+                   'Charlie\'s "peach"',
+                   'The quick brown fox jumped over the lazy dog 0123456789.'# 
* 100]
+                  ]
+        }
+
+    TYPE_ADDITIONAL_SUBMAP = {
+        'bytes': [b'',
+                  b'12345',
+                  b'Hello, world',
+                  b'\\x01\\x02\\x03\\x04\\x05abcde\\x80\\x81\\xfe\\xff',
+                  b'The quick brown fox jumped over the lazy dog 0123456789.' 
#* 100],
+                 ],
+        'char': ['a',
+                 'Z',
+                 '\x01',
+                 '\x7f'],
+        }
+
+    # The TYPE_SUBMAP defines test values for JMS message types that allow 
typed message content. Note that the
+    # types defined here are understood to be *Java* types and the stringified 
values are to be interpreted
+    # as the appropriate Java type by the send shim.
+    TYPE_SUBMAP = TestTypeMap.merge_dicts(COMMON_SUBMAP, 
TYPE_ADDITIONAL_SUBMAP)
+
+    # Defines JMS headers that should be set by the send or publish API call 
of the client
+    HEADERS_PUBLISH_LIST = [
+        'JMS_DESTINATION',
+        'JMS_DELIVERY_MODE',
+        'JMS_EXPIRATION',
+        'JMS_PRIORITY',
+        'JMS_MESSAGEID',
+        'JMS_TIMESTAMP',
+        ]
+
+    # Defines JMS headers that are modified by the broker when he message is 
consumed
+    HEADERS_BROKER_LIST = [
+        'JMS_REDELIVERED',
+        ]
+
+    # JMS headers that can be set by the client prior to send / publish, and 
that should be preserved byt he broker
+    HEADERS_MAP = {
+        'JMS_CORRELATIONID_HEADER': {'string': ['Hello, world',
+                                                '"Hello, world"',
+                                                "Charlie's \"peach\"",
+                                                'Charlie\'s "peach"',
+                                                'The quick brown fox jumped 
over the lazy dog 0123456789.' * 10,
+                                                #'', # TODO: Re-enable when 
PROTON-1288 is fixed
+                                               ],
+                                     'bytes': [b'12345\\x006789',
+                                               b'Hello, world',
+                                               b'"Hello, world"',
+                                               
b'\\x01\\x02\\x03\\x04\\x05abcde\\x80\\x81\\xfe\\xff',
+                                               b'The quick brown fox jumped 
over the lazy dog 0123456789.' * 10,
+                                               #b'', # TODO: Re-enable when 
PROTON-1288 is fixed
+                                              ],
+                                    },
+        'JMS_REPLYTO_HEADER': {'queue': ['q_aaa', 'q_bbb'],
+                               'topic': ['t_aaa', 't_bbb'],
+                              },
+        'JMS_TYPE_HEADER': {'string': ['Hello, world',
+                                       '"Hello, world"',
+                                       "Charlie's \"peach\"",
+                                       'Charlie\'s "peach"',
+                                       'The quick brown fox jumped over the 
lazy dog 0123456789.' * 10,
+                                       #'', # TODO: Re-enable when PROTON-1288 
is fixed
+                                      ],
+                           },
+        }
+
+    PROPERTIES_MAP = COMMON_SUBMAP # disabled until PROTON-1284 is fixed
+
+    TYPE_MAP = {
+        'JMS_MESSAGE_TYPE': {'none': [None]},
+        'JMS_BYTESMESSAGE_TYPE': TYPE_SUBMAP,
+        'JMS_MAPMESSAGE_TYPE': TYPE_SUBMAP,
+        'JMS_STREAMMESSAGE_TYPE': TYPE_SUBMAP,
+        'JMS_TEXTMESSAGE_TYPE': {'text': ['',
+                                          'Hello, world',
+                                          '"Hello, world"',
+                                          "Charlie's \"peach\"",
+                                          'Charlie\'s "peach"',
+                                          'The quick brown fox jumped over the 
lazy dog 0123456789.' * 10
+                                         ]
+                                },
+        # TODO: Add Object messages when other (non-JMS clients) can generate 
Java class strings used in this message
+        # type
+        #'JMS_OBJECTMESSAGE_TYPE': {
+        #    'java.lang.Boolean': ['true',
+        #                          'false'],
+        #    'java.lang.Byte': ['-128',
+        #                       '0',
+        #                       '127'],
+        #    'java.lang.Character': [u'a',
+        #                            u'Z'],
+        #    'java.lang.Double': ['0.0',
+        #                         '3.141592654',
+        #                         '-2.71828182846'],
+        #    'java.lang.Float': ['0.0',
+        #                        '3.14159',
+        #                        '-2.71828'],
+        #    'java.lang.Integer': ['-2147483648',
+        #                          '-129',
+        #                          '-128',
+        #                          '-1',
+        #                          '0',
+        #                          '127',
+        #                          '128',
+        #                          '2147483647'],
+        #    'java.lang.Long' : ['-9223372036854775808',
+        #                        '-129',
+        #                        '-128',
+        #                        '-1',
+        #                        '0',
+        #                        '127',
+        #                        '128',
+        #                        '9223372036854775807'],
+        #    'java.lang.Short': ['-32768',
+        #                        '-129',
+        #                        '-128',
+        #                        '-1',
+        #                        '0',
+        #                        '127',
+        #                        '128',
+        #                        '32767'],
+        #    'java.lang.String': [u'',
+        #                         u'Hello, world',
+        #                         u'"Hello, world"',
+        #                         u"Charlie's \"peach\"",
+        #                         u'Charlie\'s "peach"']
+        #    },
+        }
+
+    BROKER_SKIP = {}
+
+
+class JmsMessageTypeTestCase(unittest.TestCase):
+    """
+    Abstract base class for JMS message type test cases
+    """
+
+    def run_test(self, broker_addr, jms_message_type, test_values, msg_hdrs, 
msg_props, 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.
+        """
+        queue_name = 'jms.queue.qpid-interop.jms_message_type_tests.%s.%s.%s' 
% (jms_message_type, send_shim.NAME,
+                                                                               
  receive_shim.NAME)
+
+        # First create a map containing the numbers of expected mesasges for 
each JMS message type
+        num_test_values_map = {}
+        if len(test_values) > 0:
+            for index in test_values.keys():
+                num_test_values_map[index] = len(test_values[index])
+        # Create a map of flags which indicate to the receiver the details of 
some of the messages so that it can
+        # be correctly handled (as these require some prior knowledge)
+        flags_map = {}
+        if 'JMS_CORRELATIONID_HEADER' in msg_hdrs and 'bytes' in 
msg_hdrs['JMS_CORRELATIONID_HEADER']:
+            flags_map['JMS_CORRELATIONID_AS_BYTES'] = True
+        if 'JMS_REPLYTO_HEADER' in msg_hdrs and 'topic' in 
msg_hdrs['JMS_REPLYTO_HEADER']:
+            flags_map['JMS_REPLYTO_AS_TOPIC'] = True
+        # Start the receiver shim
+        receiver = receive_shim.create_receiver(broker_addr, queue_name, 
jms_message_type,
+                                                dumps([num_test_values_map, 
flags_map]))
+        receiver.start()
+
+        # Start the send shim
+        sender = send_shim.create_sender(broker_addr, queue_name, 
jms_message_type,
+                                         dumps([test_values, msg_hdrs, 
msg_props]))
+        sender.start()
+
+        # Wait for both shims to finish
+        sender.join_or_kill(qpid_interop_test.shims.THREAD_TIMEOUT)
+        receiver.join_or_kill(qpid_interop_test.shims.THREAD_TIMEOUT)
+
+        # Process return string from sender
+        send_obj = sender.get_return_object()
+        if send_obj is not None:
+            if isinstance(send_obj, str) and len(send_obj) > 0:
+                self.fail('Send shim \'%s\':\n%s' % (send_shim.NAME, send_obj))
+            else:
+                self.fail('Send shim \'%s\':\n%s' % (send_shim.NAME, 
str(send_obj)))
+
+        # Process return string from receiver
+        receive_obj = receiver.get_return_object()
+        if receive_obj is None:
+            self.fail('JmsReceiver shim returned None')
+        else:
+            if isinstance(receive_obj, tuple):
+                if len(receive_obj) == 2:
+                    return_jms_message_type, return_list = receive_obj
+                    if (len(return_list) == 3):
+                        return_test_values = return_list[0]
+                        return_msg_hdrs = return_list[1]
+                        return_msg_props = return_list[2]
+                        self.assertEqual(return_jms_message_type, 
jms_message_type,
+                                         msg='JMS message type error:\n\n    
sent:%s\n\n    received:%s' % \
+                                         (jms_message_type, 
return_jms_message_type))
+                        self.assertEqual(return_test_values, test_values,
+                                         msg='JMS message body error:\n\n    
sent:%s\n\n    received:%s' % \
+                                         (test_values, return_test_values))
+                        self.assertEqual(return_msg_hdrs, msg_hdrs,
+                                         msg='JMS message headers error:\n\n   
 sent:%s\n\n    received:%s' % \
+                                         (msg_hdrs, return_msg_hdrs))
+                        self.assertEqual(return_msg_props, msg_props,
+                                         msg='JMS message properties 
error:\n\n    sent:%s\n\n    received:%s' % \
+                                         (msg_props, return_msg_props))
+                    else:
+                        self.fail('Return value list needs 3 items, found %d 
items: %s' % (len(return_list),
+                                                                               
            str(return_list)))
+            else:
+                self.fail(str(receive_obj))
+
+
+def create_testcase_class(broker_name, types, broker_addr, jms_message_type, 
shim_product):
+    """
+    Class factory function which creates new subclasses to 
JmsMessageTypeTestCase. Each call creates a single new
+    test case named and based on the parameters supplied to the method
+    """
+
+    def __repr__(self):
+        """Print the class name"""
+        return self.__class__.__name__
+
+    def add_test_method(cls, hdrs, props, send_shim, receive_shim):
+        """Function which creates a new test method in class cls"""
+
+        @unittest.skipIf(types.skip_test(jms_message_type, broker_name),
+                         types.skip_test_message(jms_message_type, 
broker_name))
+        def inner_test_method(self):
+            self.run_test(self.broker_addr,
+                          self.jms_message_type,
+                          self.test_values,
+                          hdrs[1],
+                          props[1],
+                          send_shim,
+                          receive_shim)
+
+        inner_test_method.__name__ = 'test_%s%s%s_%s->%s' % 
(jms_message_type[4:-5], hdrs[0], props[0], send_shim.NAME,
+                                                             receive_shim.NAME)
+        setattr(cls, inner_test_method.__name__, inner_test_method)
+
+    class_name = jms_message_type[4:-5].title() + 'TestCase'
+    class_dict = {'__name__': class_name,
+                  '__repr__': __repr__,
+                  '__doc__': 'Test case for JMS message type \'%s\'' % 
jms_message_type,
+                  'jms_message_type': jms_message_type,
+                  'broker_addr': broker_addr,
+                  'test_values': types.get_test_values(jms_message_type)} # 
tuple (tot_size, {...}
+    new_class = type(class_name, (JmsMessageTypeTestCase,), class_dict)
+    for send_shim, receive_shim in shim_product:
+        # Message without any headers or properties
+        add_test_method(new_class, ('', {}), ('', {}), send_shim, receive_shim)
+
+        # Iterate through message headers, add one test per header value, no 
combinations
+        # Structure: {HEADER_NAME_1; {header_type_1: [val_1_1, val_1_2, 
val_1_3, ...],
+        #                             header_type_2: [val_2_1, val_2_2, 
val_2_3, ...],
+        #                             ...
+        #                            },
+        #             ...
+        #            }
+        for msg_header, header_type_dict in types.HEADERS_MAP.iteritems():
+            for header_type, header_val_list in header_type_dict.iteritems():
+                hdr_val_cnt = 0
+                for header_val in header_val_list:
+                    hdr_val_cnt += 1
+                    test_name = '_hdr.%s.%s.%02d' % (msg_header[4:-7], 
header_type, hdr_val_cnt)
+                    add_test_method(new_class,
+                                    (test_name, {msg_header: {header_type: 
header_val}}),
+                                    ('', {}),
+                                    send_shim,
+                                    receive_shim)
+
+        # One message with all the headers together using type[0] and val[0]
+        all_hdrs = {}
+        for msg_header in types.HEADERS_MAP.iterkeys():
+            header_type_dict = types.HEADERS_MAP[msg_header]
+            header_type, header_val_list = header_type_dict.iteritems().next()
+            header_val = header_val_list[0]
+            all_hdrs[msg_header] = {header_type: header_val}
+        add_test_method(new_class, ('_hdrs', all_hdrs), ('', {}), send_shim, 
receive_shim)
+
+        # Properties tests disabled until PROTON-1284 fixed
+        ## Iterate through properties
+        ## Structure: {prop_type_1: [val_1_1, val_1_2, ...],
+        ##             prop_type_2: [val_2_1, val_2_2, ...],
+        ##             ...
+        ##            }
+        #all_props = {}
+        #for prop_type, prop_val_list in types.PROPERTIES_MAP.iteritems():
+        #    prop_val_cnt = 0
+        #    for prop_val in prop_val_list:
+        #        prop_val_cnt += 1
+        #        all_props['%s_%02d' % (prop_type, prop_val_cnt)] = 
{prop_type: prop_val}
+
+        ## One message with all properties together
+        #add_test_method(new_class, ('', {}), ('_props', all_props), 
send_shim, receive_shim)
+
+        ## One message with all headers and all properties together
+        #add_test_method(new_class, ('_hdrs', all_hdrs), ('_props', 
all_props), send_shim, receive_shim)
+
+    return new_class
+
+
+PROTON_CPP_RECEIVER_SHIM = path.join(QPID_INTEROP_TEST_HOME, 'shims', 
'qpid-proton-cpp', 'build', 'jms_hdrs_props_test',
+                                     'Receiver')
+PROTON_CPP_SENDER_SHIM = path.join(QPID_INTEROP_TEST_HOME, 'shims', 
'qpid-proton-cpp', 'build', 'jms_hdrs_props_test',
+                                   'Sender')
+PROTON_PYTHON_RECEIVER_SHIM = path.join(QPID_INTEROP_TEST_HOME, 'shims', 
'qpid-proton-python', 'src',
+                                        'jms_hdrs_props_test', 'Receiver.py')
+PROTON_PYTHON_SENDER_SHIM = path.join(QPID_INTEROP_TEST_HOME, 'shims', 
'qpid-proton-python', 'src',
+                                      'jms_hdrs_props_test', 'Sender.py')
+QIT_JMS_CLASSPATH_FILE = path.join(QPID_INTEROP_TEST_HOME, 'shims', 
'qpid-jms', 'cp.txt')
+with open(QIT_JMS_CLASSPATH_FILE, 'r') as classpath_file:
+    QIT_JMS_CLASSPATH = classpath_file.read()
+QPID_JMS_RECEIVER_SHIM = 
'org.apache.qpid.interop_test.jms_hdrs_props_test.Receiver'
+QPID_JMS_SENDER_SHIM = 
'org.apache.qpid.interop_test.jms_hdrs_props_test.Sender'
+
+# 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 = {qpid_interop_test.shims.ProtonCppShim.NAME: \
+                qpid_interop_test.shims.ProtonCppShim(PROTON_CPP_SENDER_SHIM, 
PROTON_CPP_RECEIVER_SHIM),
+            qpid_interop_test.shims.ProtonPythonShim.NAME: \
+                
qpid_interop_test.shims.ProtonPythonShim(PROTON_PYTHON_SENDER_SHIM, 
PROTON_PYTHON_RECEIVER_SHIM),
+            qpid_interop_test.shims.QpidJmsShim.NAME: \
+                qpid_interop_test.shims.QpidJmsShim(QIT_JMS_CLASSPATH, 
QPID_JMS_SENDER_SHIM, QPID_JMS_RECEIVER_SHIM),
+           }
+
+# TODO: Complete the test options to give fine control over running tests
+class TestOptions(object):
+    """
+    Class controlling command-line arguments used to control the test.
+    """
+    def __init__(self,):
+        parser = argparse.ArgumentParser(description='Qpid-interop AMQP client 
interoparability test suite '
+                                         'for JMS message types')
+        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(JmsMessageTypes.TYPE_MAP.keys()))
+        parser.add_argument('--exclude-type', action='append', 
metavar='JMS-MESSAGE-TYPE',
+                            help='Name of JMS message type to exclude. 
Supported types:\n%s' %
+                            sorted(JmsMessageTypes.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' % sorted(SHIM_MAP.keys()))
+        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 # debug
+
+    # Connect to broker to find broker type
+    CONNECTION_PROPS = 
qpid_interop_test.broker_properties.get_broker_properties(ARGS.broker)
+    if CONNECTION_PROPS is None:
+        print 'WARNING: Unable to get connection properties - unknown broker'
+        BROKER = 'unknown'
+    else:
+        BROKER = CONNECTION_PROPS[symbol(u'product')] if symbol(u'product') in 
CONNECTION_PROPS \
+                 else '<product not found>'
+        BROKER_VERSION = CONNECTION_PROPS[symbol(u'version')] if 
symbol(u'version') in CONNECTION_PROPS \
+                         else '<version not found>'
+        BROKER_PLATFORM = CONNECTION_PROPS[symbol(u'platform')] if 
symbol(u'platform') in CONNECTION_PROPS \
+                          else '<platform not found>'
+        print 'Test Broker: %s v.%s on %s' % (BROKER, BROKER_VERSION, 
BROKER_PLATFORM)
+        print
+        sys.stdout.flush()
+
+    TYPES = JmsMessageTypes()
+
+    # 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 JmsMessageTypes.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 jmt in sorted(TYPES.get_type_list()):
+        if ARGS.exclude_type is None or jmt not in ARGS.exclude_type:
+            test_case_class = create_testcase_class(BROKER,
+                                                    TYPES,
+                                                    ARGS.broker,
+                                                    jmt,
+                                                    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
+    RES = unittest.TextTestRunner(verbosity=2).run(TEST_SUITE)
+    if not RES.wasSuccessful():
+        sys.exit(1)

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/514bac75/src/python/qpid_interop_test/jms_messages_test.py
----------------------------------------------------------------------
diff --git a/src/python/qpid_interop_test/jms_messages_test.py 
b/src/python/qpid_interop_test/jms_messages_test.py
new file mode 100755
index 0000000..8de22b2
--- /dev/null
+++ b/src/python/qpid_interop_test/jms_messages_test.py
@@ -0,0 +1,402 @@
+#!/usr/bin/env python
+
+"""
+Module to test JMS message 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 sys
+import unittest
+
+from itertools import product
+from json import dumps
+from os import getenv, path
+
+from proton import symbol
+import qpid_interop_test.broker_properties
+import qpid_interop_test.shims
+from qpid_interop_test.test_type_map import TestTypeMap
+
+
+# TODO: propose a sensible default when installation details are worked out
+QPID_INTEROP_TEST_HOME = getenv('QPID_INTEROP_TEST_HOME')
+if QPID_INTEROP_TEST_HOME is None:
+    print 'ERROR: Environment variable QPID_INTEROP_TEST_HOME is not set'
+    sys.exit(1)
+MAVEN_REPO_PATH = getenv('MAVEN_REPO_PATH', path.join(getenv('HOME'), '.m2', 
'repository'))
+
+class JmsMessageTypes(TestTypeMap):
+    """
+    Class which contains all the described JMS message types and the test 
values to be used in testing.
+    """
+
+    COMMON_SUBMAP = {
+        'boolean': ['True',
+                    'False'],
+        'byte': ['-0x80',
+                 '-0x1',
+                 '0x0',
+                 '0x7f'],
+        '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
+                   '0x8010000000000000', # Largest negative denormalized number
+                   '0x7fefffffffffffff', # Largest positive normalized number
+                   '0xffefffffffffffff', # Largest negative normalized number
+                   '0x7ff0000000000000', # +Infinity
+                   '0xfff0000000000000', # -Infinity
+                   '0x7ff8000000000000'], # +NaN
+        '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  # PROTON-1149 - fails on RHEL7
+                  #'0xff800000', # -Infinity # PROTON-1149 - fails on RHEL7
+                  '0x7fc00000'], # +NaN
+        'int': ['-0x80000000',
+                '-0x81',
+                '-0x80',
+                '-0x1',
+                '0x0',
+                '0x7f',
+                '0x80',
+                '0x7fffffff'],
+        'long': ['-0x8000000000000000',
+                 '-0x81',
+                 '-0x80',
+                 '-0x1',
+                 '0x0',
+                 '0x7f',
+                 '0x80',
+                 '0x7fffffffffffffff'],
+        'short': ['-0x8000',
+                  '-0x1',
+                  '0x0',
+                  '0x7fff'],
+        'string': ['',
+                   'Hello, world',
+                   '"Hello, world"',
+                   "Charlie's \"peach\"",
+                   'Charlie\'s "peach"',
+                   'The quick brown fox jumped over the lazy dog 0123456789.'# 
* 100]
+                  ]
+        }
+
+    TYPE_ADDITIONAL_SUBMAP = {
+        'bytes': [b'',
+                  b'12345',
+                  b'Hello, world',
+                  b'\\x01\\x02\\x03\\x04\\x05abcde\\x80\\x81\\xfe\\xff',
+                  b'The quick brown fox jumped over the lazy dog 0123456789.' 
#* 100],
+                 ],
+        'char': ['a',
+                 'Z',
+                 '\x01',
+                 '\x7f'],
+        }
+
+    # The TYPE_SUBMAP defines test values for JMS message types that allow 
typed message content. Note that the
+    # types defined here are understood to be *Java* types and the stringified 
values are to be interpreted
+    # as the appropriate Java type by the send shim.
+    TYPE_SUBMAP = TestTypeMap.merge_dicts(COMMON_SUBMAP, 
TYPE_ADDITIONAL_SUBMAP)
+
+    TYPE_MAP = {
+        'JMS_MESSAGE_TYPE': {'none': [None]},
+        'JMS_BYTESMESSAGE_TYPE': TYPE_SUBMAP,
+        'JMS_MAPMESSAGE_TYPE': TYPE_SUBMAP,
+        'JMS_STREAMMESSAGE_TYPE': TYPE_SUBMAP,
+        'JMS_TEXTMESSAGE_TYPE': {'text': ['',
+                                          'Hello, world',
+                                          '"Hello, world"',
+                                          "Charlie's \"peach\"",
+                                          'Charlie\'s "peach"',
+                                          'The quick brown fox jumped over the 
lazy dog 0123456789.' * 10
+                                         ]
+                                },
+        # TODO: Add Object messages when other (non-JMS clients) can generate 
Java class strings used in this message
+        # type
+        #'JMS_OBJECTMESSAGE_TYPE': {
+        #    'java.lang.Boolean': ['true',
+        #                          'false'],
+        #    'java.lang.Byte': ['-128',
+        #                       '0',
+        #                       '127'],
+        #    'java.lang.Character': [u'a',
+        #                            u'Z'],
+        #    'java.lang.Double': ['0.0',
+        #                         '3.141592654',
+        #                         '-2.71828182846'],
+        #    'java.lang.Float': ['0.0',
+        #                        '3.14159',
+        #                        '-2.71828'],
+        #    'java.lang.Integer': ['-2147483648',
+        #                          '-129',
+        #                          '-128',
+        #                          '-1',
+        #                          '0',
+        #                          '127',
+        #                          '128',
+        #                          '2147483647'],
+        #    'java.lang.Long' : ['-9223372036854775808',
+        #                        '-129',
+        #                        '-128',
+        #                        '-1',
+        #                        '0',
+        #                        '127',
+        #                        '128',
+        #                        '9223372036854775807'],
+        #    'java.lang.Short': ['-32768',
+        #                        '-129',
+        #                        '-128',
+        #                        '-1',
+        #                        '0',
+        #                        '127',
+        #                        '128',
+        #                        '32767'],
+        #    'java.lang.String': [u'',
+        #                         u'Hello, world',
+        #                         u'"Hello, world"',
+        #                         u"Charlie's \"peach\"",
+        #                         u'Charlie\'s "peach"']
+        #    },
+        }
+
+    BROKER_SKIP = {}
+
+
+class JmsMessageTypeTestCase(unittest.TestCase):
+    """
+    Abstract base class for JMS message type test cases
+    """
+
+    def run_test(self, broker_addr, jms_message_type, test_values, 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.
+        """
+        queue_name = 'jms.queue.qpid-interop.jms_message_type_tests.%s.%s.%s' 
% (jms_message_type, send_shim.NAME,
+                                                                               
  receive_shim.NAME)
+
+        # First create a map containing the numbers of expected mesasges for 
each JMS message type
+        num_test_values_map = {}
+        if len(test_values) > 0:
+            for index in test_values.keys():
+                num_test_values_map[index] = len(test_values[index])
+        # Start the receiver shim
+        receiver = receive_shim.create_receiver(broker_addr, queue_name, 
jms_message_type, dumps(num_test_values_map))
+        receiver.start()
+
+        # Start the send shim
+        sender = send_shim.create_sender(broker_addr, queue_name, 
jms_message_type, dumps(test_values))
+        sender.start()
+
+        # Wait for both shims to finish
+        sender.join_or_kill(qpid_interop_test.shims.THREAD_TIMEOUT)
+        receiver.join_or_kill(qpid_interop_test.shims.THREAD_TIMEOUT)
+
+        # Process return string from sender
+        send_obj = sender.get_return_object()
+        if send_obj is not None:
+            if isinstance(send_obj, str) and len(send_obj) > 0:
+                self.fail('Send shim \'%s\':\n%s' % (send_shim.NAME, send_obj))
+            else:
+                self.fail('Send shim \'%s\':\n%s' % (send_shim.NAME, 
str(send_obj)))
+
+        # Process return string from receiver
+        receive_obj = receiver.get_return_object()
+        if receive_obj is None:
+            self.fail('JmsReceiver shim returned None')
+        else:
+            if isinstance(receive_obj, tuple):
+                if len(receive_obj) == 2:
+                    return_jms_message_type, return_test_values = receive_obj
+                    self.assertEqual(return_jms_message_type, jms_message_type,
+                                     msg='JMS message type error:\n\n    
sent:%s\n\n    received:%s' % \
+                                     (jms_message_type, 
return_jms_message_type))
+                    self.assertEqual(return_test_values, test_values,
+                                     msg='JMS message body error:\n\n    
sent:%s\n\n    received:%s' % \
+                                     (test_values, return_test_values))
+                else:
+                    self.fail('Received incorrect tuple format: %s' % 
str(receive_obj))
+            else:
+                self.fail('Received non-tuple: %s' % str(receive_obj))
+
+
+def create_testcase_class(broker_name, types, broker_addr, jms_message_type, 
shim_product):
+    """
+    Class factory function which creates new subclasses to 
JmsMessageTypeTestCase. Each call creates a single new
+    test case named and based on the parameters supplied to the method
+    """
+
+    def __repr__(self):
+        """Print the class name"""
+        return self.__class__.__name__
+
+    def add_test_method(cls, send_shim, receive_shim):
+        """Function which creates a new test method in class cls"""
+
+        @unittest.skipIf(types.skip_test(jms_message_type, broker_name),
+                         types.skip_test_message(jms_message_type, 
broker_name))
+        def inner_test_method(self):
+            self.run_test(self.broker_addr,
+                          self.jms_message_type,
+                          self.test_values,
+                          send_shim,
+                          receive_shim)
+
+        inner_test_method.__name__ = 'test_%s_%s->%s' % 
(jms_message_type[4:-5], send_shim.NAME, receive_shim.NAME)
+        setattr(cls, inner_test_method.__name__, inner_test_method)
+
+    class_name = jms_message_type[4:-5].title() + 'TestCase'
+    class_dict = {'__name__': class_name,
+                  '__repr__': __repr__,
+                  '__doc__': 'Test case for JMS message type \'%s\'' % 
jms_message_type,
+                  'jms_message_type': jms_message_type,
+                  'broker_addr': broker_addr,
+                  'test_values': types.get_test_values(jms_message_type)} # 
tuple (tot_size, {...}
+    new_class = type(class_name, (JmsMessageTypeTestCase,), class_dict)
+    for send_shim, receive_shim in shim_product:
+        add_test_method(new_class, send_shim, receive_shim)
+
+    return new_class
+
+
+PROTON_CPP_RECEIVER_SHIM = path.join(QPID_INTEROP_TEST_HOME, 'shims', 
'qpid-proton-cpp', 'build', 'jms_messages_test',
+                                     'Receiver')
+PROTON_CPP_SENDER_SHIM = path.join(QPID_INTEROP_TEST_HOME, 'shims', 
'qpid-proton-cpp', 'build', 'jms_messages_test',
+                                   'Sender')
+PROTON_PYTHON_RECEIVER_SHIM = path.join(QPID_INTEROP_TEST_HOME, 'shims', 
'qpid-proton-python', 'src',
+                                        'jms_messages_test', 'Receiver.py')
+PROTON_PYTHON_SENDER_SHIM = path.join(QPID_INTEROP_TEST_HOME, 'shims', 
'qpid-proton-python', 'src',
+                                      'jms_messages_test', 'Sender.py')
+QIT_JMS_CLASSPATH_FILE = path.join(QPID_INTEROP_TEST_HOME, 'shims', 
'qpid-jms', 'cp.txt')
+with open(QIT_JMS_CLASSPATH_FILE, 'r') as classpath_file:
+    QIT_JMS_CLASSPATH = classpath_file.read()
+QPID_JMS_RECEIVER_SHIM = 
'org.apache.qpid.interop_test.jms_messages_test.Receiver'
+QPID_JMS_SENDER_SHIM = 'org.apache.qpid.interop_test.jms_messages_test.Sender'
+
+# 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 = {qpid_interop_test.shims.ProtonCppShim.NAME: \
+                qpid_interop_test.shims.ProtonCppShim(PROTON_CPP_SENDER_SHIM, 
PROTON_CPP_RECEIVER_SHIM),
+            qpid_interop_test.shims.ProtonPythonShim.NAME: \
+                
qpid_interop_test.shims.ProtonPythonShim(PROTON_PYTHON_SENDER_SHIM, 
PROTON_PYTHON_RECEIVER_SHIM),
+            qpid_interop_test.shims.QpidJmsShim.NAME: \
+                qpid_interop_test.shims.QpidJmsShim(QIT_JMS_CLASSPATH, 
QPID_JMS_SENDER_SHIM, QPID_JMS_RECEIVER_SHIM),
+           }
+
+# TODO: Complete the test options to give fine control over running tests
+class TestOptions(object):
+    """
+    Class controlling command-line arguments used to control the test.
+    """
+    def __init__(self,):
+        parser = argparse.ArgumentParser(description='Qpid-interop AMQP client 
interoparability test suite '
+                                         'for JMS message types')
+        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(JmsMessageTypes.TYPE_MAP.keys()))
+        parser.add_argument('--exclude-type', action='append', 
metavar='JMS-MESSAGE-TYPE',
+                            help='Name of JMS message type to exclude. 
Supported types:\n%s' %
+                            sorted(JmsMessageTypes.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' % sorted(SHIM_MAP.keys()))
+        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 # debug
+
+    # Connect to broker to find broker type
+    CONNECTION_PROPS = 
qpid_interop_test.broker_properties.get_broker_properties(ARGS.broker)
+    if CONNECTION_PROPS is None:
+        print 'WARNING: Unable to get connection properties - unknown broker'
+        BROKER = 'unknown'
+    else:
+        BROKER = CONNECTION_PROPS[symbol(u'product')] if symbol(u'product') in 
CONNECTION_PROPS \
+                 else '<product not found>'
+        BROKER_VERSION = CONNECTION_PROPS[symbol(u'version')] if 
symbol(u'version') in CONNECTION_PROPS \
+                         else '<version not found>'
+        BROKER_PLATFORM = CONNECTION_PROPS[symbol(u'platform')] if 
symbol(u'platform') in CONNECTION_PROPS \
+                          else '<platform not found>'
+        print 'Test Broker: %s v.%s on %s' % (BROKER, BROKER_VERSION, 
BROKER_PLATFORM)
+        print
+        sys.stdout.flush()
+
+    TYPES = JmsMessageTypes()
+
+    # 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 JmsMessageTypes.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 jmt in sorted(TYPES.get_type_list()):
+        if ARGS.exclude_type is None or jmt not in ARGS.exclude_type:
+            test_case_class = create_testcase_class(BROKER,
+                                                    TYPES,
+                                                    ARGS.broker,
+                                                    jmt,
+                                                    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
+    RES = unittest.TextTestRunner(verbosity=2).run(TEST_SUITE)
+    if not RES.wasSuccessful():
+        sys.exit(1)

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/514bac75/src/python/qpid_interop_test/shims.py
----------------------------------------------------------------------
diff --git a/src/python/qpid_interop_test/shims.py 
b/src/python/qpid_interop_test/shims.py
new file mode 100644
index 0000000..f6d019e
--- /dev/null
+++ b/src/python/qpid_interop_test/shims.py
@@ -0,0 +1,227 @@
+"""
+Module containing worker thread classes and shims
+"""
+#
+# 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.
+#
+
+from json import loads
+from os import getenv, getpgid, killpg, path, setsid
+from signal import SIGKILL, SIGTERM
+from subprocess import Popen, PIPE, CalledProcessError
+from sys import stdout
+from threading import Thread
+from time import sleep
+
+
+THREAD_TIMEOUT = 10.0 # seconds to complete before join is forced
+
+
+class ShimWorkerThread(Thread):
+    """Parent class for shim worker threads and return a string once the 
thread has ended"""
+    def __init__(self, thread_name):
+        super(ShimWorkerThread, self).__init__(name=thread_name)
+        self.arg_list = []
+        self.return_obj = None
+        self.proc = None
+
+    def get_return_object(self):
+        """Get the return object from the completed thread"""
+        return self.return_obj
+
+    def join_or_kill(self, timeout):
+        """
+        Wait for thread to join after timeout (seconds). If still alive, it is 
then terminated, then if still alive,
+        killed
+        """
+        self.join(timeout)
+        if self.is_alive():
+            if self.proc is not None:
+                if self._terminate_pg_loop():
+                    if self._kill_pg_loop():
+                        print '\n  ERROR: Thread %s (pid=%d) alive after kill' 
% (self.name, self.proc.pid)
+                    else:
+                        print 'Killed'
+                        stdout.flush()
+                else:
+                    print 'Terminated'
+                    stdout.flush()
+            else:
+                print 'ERROR: shims.join_or_kill(): Process joined and is 
alive, yet proc is None.'
+
+    def _terminate_pg_loop(self, num_attempts=2, wait_time=2):
+        cnt = 0
+        while cnt < num_attempts and self.is_alive():
+            cnt += 1
+            print '\n  Thread %s (pid=%d) alive after timeout, terminating 
(try #%d)...' % (self.name, self.proc.pid,
+                                                                               
             cnt),
+            stdout.flush()
+            killpg(getpgid(self.proc.pid), SIGTERM)
+            sleep(wait_time)
+        return self.is_alive()
+
+    def _kill_pg_loop(self, num_attempts=2, wait_time=5):
+        cnt = 0
+        while cnt < num_attempts and self.is_alive():
+            cnt += 1
+            print '\n  Thread %s (pid=%d) alive after terminate, killing (try 
#%d)...' % (self.name, self.proc.pid,
+                                                                               
           cnt),
+            stdout.flush()
+            killpg(getpgid(self.proc.pid), SIGKILL)
+            sleep(wait_time)
+        return self.is_alive()
+
+
+class Sender(ShimWorkerThread):
+    """Sender class for multi-threaded send"""
+    def __init__(self, use_shell_flag, send_shim_args, broker_addr, 
queue_name, msg_type, json_test_str):
+        super(Sender, self).__init__('sender_thread_%s' % queue_name)
+        if send_shim_args is None:
+            print 'ERROR: Sender: send_shim_args == None'
+        self.use_shell_flag = use_shell_flag
+        self.arg_list.extend(send_shim_args)
+        self.arg_list.extend([broker_addr, queue_name, msg_type, 
json_test_str])
+
+    def run(self):
+        """Thread starts here"""
+        try:
+            #print '\n>>>', self.arg_list # DEBUG - useful to see command-line 
sent to shim
+            self.proc = Popen(self.arg_list, stdout=PIPE, stderr=PIPE, 
shell=self.use_shell_flag, preexec_fn=setsid)
+            (stdoutdata, stderrdata) = self.proc.communicate()
+            if len(stdoutdata) > 0 or len(stderrdata) > 0:
+                self.return_obj = (stdoutdata, stderrdata)
+        except CalledProcessError as exc:
+            self.return_obj = str(exc) + '\n\nOutput:\n' + exc.output
+
+
+class Receiver(ShimWorkerThread):
+    """Receiver class for multi-threaded receive"""
+    def __init__(self, receive_shim_args, broker_addr, queue_name, msg_type, 
json_test_str):
+        super(Receiver, self).__init__('receiver_thread_%s' % queue_name)
+        if receive_shim_args is None:
+            print 'ERROR: Receiver: receive_shim_args == None'
+        self.arg_list.extend(receive_shim_args)
+        self.arg_list.extend([broker_addr, queue_name, msg_type, 
json_test_str])
+
+    def run(self):
+        """Thread starts here"""
+        try:
+            #print '\n>>>', self.arg_list # DEBUG - useful to see command-line 
sent to shim
+            self.proc = Popen(self.arg_list, stdout=PIPE, stderr=PIPE, 
preexec_fn=setsid)
+            (stdoutdata, stderrdata) = self.proc.communicate()
+            if len(stderrdata) > 0:
+                self.return_obj = (stdoutdata, stderrdata)
+            else:
+                #print '<<<', stdoutdata # DEBUG - useful to see text received 
from shim
+                str_tvl = stdoutdata.split('\n')[0:-1] # remove trailing \n
+                #if len(str_tvl) == 1:
+                #    self.return_obj = output
+                if len(str_tvl) == 2:
+                    self.return_obj = (str_tvl[0], loads(str_tvl[1]))
+                else: # Make a single line of all the bits and return that
+                    #self.return_obj = loads("".join(str_tvl[1:]))
+                    self.return_obj = stdoutdata
+        except CalledProcessError as exc:
+            self.return_obj = str(exc) + '\n\n' + exc.output
+
+class Shim(object):
+    """Abstract shim class, parent of all shims."""
+    NAME = None
+    def __init__(self, sender_shim, receiver_shim):
+        self.sender_shim = sender_shim
+        self.receiver_shim = receiver_shim
+        self.send_params = None
+        self.receive_params = None
+        self.use_shell_flag = False
+
+    def create_sender(self, broker_addr, queue_name, msg_type, json_test_str):
+        """Create a new sender instance"""
+        return Sender(self.use_shell_flag, self.send_params, broker_addr, 
queue_name, msg_type, json_test_str)
+
+    def create_receiver(self, broker_addr, queue_name, msg_type, 
json_test_str):
+        """Create a new receiver instance"""
+        return Receiver(self.receive_params, broker_addr, queue_name, 
msg_type, json_test_str)
+
+class ProtonPythonShim(Shim):
+    """Shim for qpid-proton Python client"""
+    NAME = 'ProtonPython'
+    def __init__(self, sender_shim, receiver_shim):
+        super(ProtonPythonShim, self).__init__(sender_shim, receiver_shim)
+        self.send_params = [self.sender_shim]
+        self.receive_params = [self.receiver_shim]
+
+
+class ProtonCppShim(Shim):
+    """Shim for qpid-proton C++ client"""
+    NAME = 'ProtonCpp'
+    def __init__(self, sender_shim, receiver_shim):
+        super(ProtonCppShim, self).__init__(sender_shim, receiver_shim)
+        self.send_params = [self.sender_shim]
+        self.receive_params = [self.receiver_shim]
+
+
+class QpidJmsShim(Shim):
+    """Shim for qpid-jms JMS client"""
+    NAME = 'QpidJms'
+
+    # Installed versions
+    # TODO: Automate this - it gets out of date quickly
+    # Maven works out all the deps, should use that
+    QPID_JMS_SHIM_VER = '0.1.0-SNAPSHOT'
+    QPID_JMS_VER = '0.20.0-SNAPSHOT'
+    QPID_PROTON_J_VER = '0.15.0-SNAPSHOT'
+    JMS_API_VER = '1.1.1'
+    LOGGER_API_VER = '1.7.21'
+    LOGGER_IMPL_VER = '1.7.21'
+    NETTY_VER = '4.0.40.Final'
+
+    # Classpath components
+    #QPID_INTEROP_TEST_SHIM_JAR = path.join(QPID_INTEROP_TEST_HOME, 'shims', 
'qpid-jms', 'target', 'qpid-jms-shim.jar')
+    MAVEN_REPO_PATH = path.join(getenv('HOME'), '.m2', 'repository')
+    JMS_API_JAR = path.join(MAVEN_REPO_PATH, 'org', 'apache', 'geronimo', 
'specs', 'geronimo-jms_1.1_spec', JMS_API_VER,
+                            'geronimo-jms_1.1_spec-%s.jar' % JMS_API_VER)
+    JMS_IMPL_JAR = path.join(MAVEN_REPO_PATH, 'org', 'apache', 'qpid', 
'qpid-jms-client', QPID_JMS_VER,
+                             'qpid-jms-client-%s.jar' % QPID_JMS_VER)
+    LOGGER_API_JAR = path.join(MAVEN_REPO_PATH, 'org', 'slf4j', 'slf4j-api', 
LOGGER_API_VER,
+                               'slf4j-api-%s.jar' % LOGGER_API_VER)
+    LOGGER_IMPL_JAR = path.join(MAVEN_REPO_PATH, 'org', 'slf4j', 'slf4j-nop', 
LOGGER_IMPL_VER,
+                                'slf4j-nop-%s.jar' % LOGGER_IMPL_VER)
+    PROTON_J_JAR = path.join(MAVEN_REPO_PATH, 'org', 'apache', 'qpid', 
'proton-j', QPID_PROTON_J_VER,
+                             'proton-j-%s.jar' % QPID_PROTON_J_VER)
+    NETTY_JAR = path.join(MAVEN_REPO_PATH, 'io', 'netty', 'netty-all', 
NETTY_VER, 'netty-all-%s.jar' % NETTY_VER)
+    QPID_JMS_SHIM_JAR = path.join(MAVEN_REPO_PATH, 'org', 'apache', 'qpid', 
'qpid-interop-test-jms-shim',
+                                  QPID_JMS_SHIM_VER, 
'qpid-interop-test-jms-shim-%s.jar' % QPID_JMS_SHIM_VER)
+
+    JAVA_HOME = getenv('JAVA_HOME', '/usr/bin') # Default only works in Linux
+    JAVA_EXEC = path.join(JAVA_HOME, 'java')
+
+    def __init__(self, dependency_class_path, sender_shim, receiver_shim):
+        super(QpidJmsShim, self).__init__(sender_shim, receiver_shim)
+        self.dependency_class_path = dependency_class_path
+        self.send_params = [self.JAVA_EXEC, '-cp', self.get_java_class_path(), 
self.sender_shim]
+        self.receive_params = [self.JAVA_EXEC, '-cp', 
self.get_java_class_path(), self.receiver_shim]
+
+    def get_java_class_path(self):
+        """Method to construct and return the Java class path necessary to run 
the shim"""
+        return ':'.join([self.QPID_JMS_SHIM_JAR, self.dependency_class_path])
+    #                     self.JMS_API_JAR,
+    #                     self.JMS_IMPL_JAR,
+    #                     self.LOGGER_API_JAR,
+    #                     self.LOGGER_IMPL_JAR,
+    #                     self.PROTON_J_JAR,
+    #                     self.NETTY_JAR])

http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/514bac75/src/python/qpid_interop_test/test_type_map.py
----------------------------------------------------------------------
diff --git a/src/python/qpid_interop_test/test_type_map.py 
b/src/python/qpid_interop_test/test_type_map.py
new file mode 100644
index 0000000..0fd4552
--- /dev/null
+++ b/src/python/qpid_interop_test/test_type_map.py
@@ -0,0 +1,86 @@
+"""
+Module containing Error classes for interop testing
+"""
+#
+# 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 TestTypeMap(object):
+    """
+    Class which contains all the described types and the test values to be 
used in testing against those types.
+    """
+
+    # TYPE_MAP: Map containing all described types as the indecies, and a list 
of values to be used in testing
+    # that type as a list of values.
+    #
+    # Format: {'type_1' : [val_1_1, val_1_2, ...],
+    #          'type_2' : [val_2_1, val_2_2, ...],
+    #          ...
+    #         }
+    TYPE_MAP = {}
+
+    # BROKER_SKIP: For know broker issues where a type would cause a test to 
fail or hang,
+    # entries in BROKER_SKIP will cause the test to be skipped with a message.
+    # This is a map containing AMQP types as a key, and a list of brokers for 
which this
+    # type should be skipped.
+    # Format: {'jms_msg_type_1' : {'broker_1' : 'skip msg for broker_1',
+    #                              'broker_2' : 'skip msg for broker_2',
+    #                               ...
+    #                             },
+    #          'jms_msg_type_2' : {'broker_1' : 'skip msg for broker_1',
+    #                              'broker_2' : 'skip msg for broker_2',
+    #                              ...
+    #                             },
+    #          ...
+    #         }
+    # where broker_1, broker_2, ... are broker product names as defined by the
+    # connection property string it returns.
+    BROKER_SKIP = {}
+
+    def __init__(self):
+        pass
+
+    def get_type_list(self):
+        """Return a list of types which this test suite supports"""
+        return self.TYPE_MAP.keys()
+
+    def get_test_values(self, test_type):
+        """Return test values to use when testing the supplied type."""
+        if test_type not in self.TYPE_MAP.keys():
+            return None
+        return self.TYPE_MAP[test_type]
+
+    def skip_test_message(self, test_type, broker_name):
+        """Return the message to use if a test is skipped"""
+        if test_type in self.BROKER_SKIP.keys():
+            if broker_name in self.BROKER_SKIP[test_type]:
+                return str(self.BROKER_SKIP[test_type][broker_name])
+        return None
+
+    def skip_test(self, test_type, broker_name):
+        """Return boolean True if test should be skipped"""
+        return test_type in self.BROKER_SKIP.keys() and \
+            broker_name in self.BROKER_SKIP[test_type]
+
+    @staticmethod
+    def merge_dicts(*dict_args):
+        """Static method to merge two or more dictionaries"""
+        res = {}
+        for this_dict in dict_args:
+            res.update(this_dict)
+        return res


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

Reply via email to