You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by kp...@apache.org on 2016/10/07 16:50:41 UTC
[2/5] qpid-interop-test git commit: QPIDIT-41: Reorganized dir
structure and tidied up the test code. Copied the old jms_messages_test to a
new jms_hdrs_props_test and simplified the jms_messages_test to include only
message body tests. Simplified parame
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
deleted file mode 100755
index 56cb203..0000000
--- a/src/python/qpid-interop-test/jms_messages_test.py
+++ /dev/null
@@ -1,511 +0,0 @@
-#!/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 sys import stdout
-
-import broker_properties
-import shims
-from proton import symbol
-from 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(shims.THREAD_TIMEOUT)
- receiver.join_or_kill(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' % 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) == 4:
- return_jms_message_type, return_test_values, return_msg_hdrs, return_msg_props = 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))
- 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(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_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 = {shims.ProtonCppShim.NAME: shims.ProtonCppShim(PROTON_CPP_SENDER_SHIM, PROTON_CPP_RECEIVER_SHIM),
- shims.ProtonPythonShim.NAME: shims.ProtonPythonShim(PROTON_PYTHON_SENDER_SHIM, PROTON_PYTHON_RECEIVER_SHIM),
- shims.QpidJmsShim.NAME: 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 = broker_properties.getBrokerProperties(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
- 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
deleted file mode 100644
index 5e94d2d..0000000
--- a/src/python/qpid-interop-test/shims.py
+++ /dev/null
@@ -1,229 +0,0 @@
-"""
-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: # AMQP type test return
- self.return_obj = loads(str_tvl[1])
- elif len(str_tvl) == 4: # JMS test return
- self.return_obj = (str_tvl[0], loads(str_tvl[1]), loads(str_tvl[2]), loads(str_tvl[3]))
- 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
deleted file mode 100644
index b571ac8..0000000
--- a/src/python/qpid-interop-test/test_type_map.py
+++ /dev/null
@@ -1,85 +0,0 @@
-"""
-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):
- res = {}
- for d in dict_args:
- res.update(d)
- return res
http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/514bac75/src/python/qpid_interop_test/.gitignore
----------------------------------------------------------------------
diff --git a/src/python/qpid_interop_test/.gitignore b/src/python/qpid_interop_test/.gitignore
new file mode 100644
index 0000000..835fca3
--- /dev/null
+++ b/src/python/qpid_interop_test/.gitignore
@@ -0,0 +1,4 @@
+/interop_test_errors.pyc
+/shim_utils.pyc
+/broker_properties.pyc
+/test_type_map.pyc
http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/514bac75/src/python/qpid_interop_test/__init__.py
----------------------------------------------------------------------
diff --git a/src/python/qpid_interop_test/__init__.py b/src/python/qpid_interop_test/__init__.py
new file mode 100644
index 0000000..70204e4
--- /dev/null
+++ b/src/python/qpid_interop_test/__init__.py
@@ -0,0 +1,23 @@
+#
+# 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 broker_properties
+import interop_test_errors
+import shims
+import test_type_map
http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/514bac75/src/python/qpid_interop_test/amqp_types_test.py
----------------------------------------------------------------------
diff --git a/src/python/qpid_interop_test/amqp_types_test.py b/src/python/qpid_interop_test/amqp_types_test.py
new file mode 100755
index 0000000..bde21f3
--- /dev/null
+++ b/src/python/qpid_interop_test/amqp_types_test.py
@@ -0,0 +1,443 @@
+#!/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 sys
+import unittest
+
+from itertools import product
+from json import dumps
+from os import getenv, path
+from time import mktime, time
+from uuid import UUID, uuid4
+
+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)
+
+
+class AmqpPrimitiveTypes(TestTypeMap):
+ """
+ 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',
+ '0x1',
+ '0xff',
+ '0x100',
+ '0x7fffffffffffffff',
+ '0x8000000000000000',
+ '0xffffffffffffffff'],
+ 'byte': ['-0x80',
+ '-0x1',
+ '0x0',
+ '0x7f'],
+ 'short': ['-0x8000',
+ '-0x1',
+ '0x0',
+ '0x7fff'],
+ 'int': ['-0x80000000',
+ '-0x1',
+ '0x0',
+ '0x7fffffff'],
+ 'long': ['-0x8000000000000000',
+ '-0x81',
+ '-0x80',
+ '-0x1',
+ '0x0',
+ '0x7f',
+ '0x80',
+ '0x7fffffffffffffff'],
+ # float and double: Because of difficulty with rounding of floating point numbers, we use the binary
+ # representation instead which should be exact when comparing sent and received values.
+ '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
+ '0xffc00000'], # -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 negative normalized number
+ '0x7fefffffffffffff', # Largest positive normalized number
+ '0xffefffffffffffff', # Largest negative normalized number
+ '0x7ff0000000000000', # +Infinity
+ '0xfff0000000000000', # -Infinity
+ '0x7ff8000000000000', # +NaN
+ '0xfff8000000000000'], # -NaN
+ # decimal32, decimal64, decimal128:
+ # Until more formal support for decimal32, decimal64 and decimal128 are included in Python, we use
+ # a hex format for basic tests, and treat the data as a binary blob.
+ 'decimal32': ['0x00000000',
+ '0x40490fdb',
+ '0xc02df854',
+ '0xff7fffff'],
+ 'decimal64': ['0x0000000000000000',
+ '0x400921fb54442eea',
+ '0xc005bf0a8b145fcf',
+ '0xffefffffffffffff'],
+ 'decimal128': ['0x00000000000000000000000000000000',
+ '0xff0102030405060708090a0b0c0d0e0f'],
+ 'char': [u'a',
+ u'Z',
+ u'0x1',
+ u'0x7f',
+ u'0x16b5', # Rune 'G'
+ u'0x10ffff'],
+ # timestamp: Must be in milliseconds since the Unix epoch
+ 'timestamp': ['0x0',
+ '0x%x' % int(mktime((2000, 1, 1, 0, 0, 0, 5, 1, 0))*1000),
+ '0x%x' % int(time()*1000)
+ ],
+ 'uuid': [str(UUID(int=0x0)),
+ str(UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')),
+ str(uuid4())],
+ 'binary': [bytes(),
+ bytes(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
+ ],
+ # 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 dog 0123456789.' * 100
+ ],
+ 'symbol': ['',
+ 'myDomain.123',
+ 'domain.0123456789.' * 100],
+ 'list': [[],
+ ['ubyte:1', 'int:-2', 'float:3.14'],
+ ['string:a', 'string:b', 'string:c'],
+ ['ulong:12345',
+ 'timestamp:%d' % (time()*1000),
+ 'short:-2500',
+ 'uuid:%s' % uuid4(),
+ 'symbol:a.b.c',
+ 'none:',
+ 'decimal64:0x400921fb54442eea'
+ ],
+ [[],
+ 'none',
+ ['ubyte:1', 'ubyte:2', 'ubyte:3'],
+ 'boolean:True',
+ 'boolean:False',
+ {'string:hello': 'long:1234', 'string:goodbye': 'boolean:True'}
+ ],
+ [[], [[], [[], [], []], []], []],
+ ['short:0',
+ 'short:1',
+ 'short:2',
+ 'short:3',
+ 'short:4',
+ 'short:5',
+ 'short:6',
+ 'short:7',
+ 'short:8',
+ 'short:9'] * 10
+ ],
+ 'map': [
+ # Enpty map
+ {},
+ # Map with string keys
+ {'string:one': 'ubyte:1',
+ 'string:two': 'ushort:2'},
+ # Map with other AMQP simple types as keys
+ {'none:': 'string:None',
+ 'string:None': 'none:',
+ 'string:One': 'long:-1234567890',
+ 'short:2': 'int:2',
+ 'boolean:True': 'string:True',
+ 'string:False': 'boolean:False',
+ #['string:AAA', 'ushort:5951']: 'string:list value',
+ #{'byte:-55': 'ubyte:200',
+ # 'boolean:True': 'string:Hello, world!'}: 'symbol:map.value',
+ #'string:list': [],
+ 'string:map': {'char:A': 'int:1',
+ 'char:B': 'int:2'}},
+ ],
+ # TODO: Support all AMQP types in array (including keys)
+ #'array': [[],
+ # [1, 2, 3],
+ # ['Hello', 'world'],
+ # [[1, 2, 3],
+ # ['a', 'b', 'c'],
+ # [2.3, 3.4, 4,5],
+ # [True, False, True, True]]
+ # ]
+ }
+
+ # This section contains tests that should be skipped because of know issues that would cause the test to fail.
+ # As the issues are resolved, these should be removed.
+ BROKER_SKIP = {'null': {'ActiveMQ': 'Null type not sent in Proton Python binding: PROTON-1091',
+ 'qpid-cpp': 'Null type not sent in Proton Python binding: PROTON-1091',},
+ 'decimal32': {'ActiveMQ': 'decimal32 and decimal64 sent byte reversed: PROTON-1160',
+ 'qpid-cpp': 'decimal32 not supported on qpid-cpp broker: QPIDIT-5, QPID-6328',
+ 'apache-activemq-artemis': 'decimal32 and decimal64 sent byte reversed: PROTON-1160',
+ 'qpid-dispatch-router': 'decimal32 and decimal64 sent byte reversed: PROTON-1160'},
+ 'decimal64': {'ActiveMQ': 'decimal32 and decimal64 sent byte reversed: PROTON-1160',
+ 'qpid-cpp': 'decimal64 not supported on qpid-cpp broker: QPIDIT-6, QPID-6328',
+ 'apache-activemq-artemis': 'decimal32 and decimal64 sent byte reversed: PROTON-1160',
+ 'qpid-dispatch-router': 'decimal32 and decimal64 sent byte reversed: PROTON-1160'},
+ 'decimal128': {'qpid-cpp': 'decimal128 not supported on qpid-cpp broker: QPIDIT-3, QPID-6328',},
+ 'char': {'qpid-cpp': 'char not supported on qpid-cpp broker: QPIDIT-4, QPID-6328',
+ 'apache-activemq-artemis': 'char types > 16 bits truncated on Artemis: ENTMQ-1685'},
+ 'float': {'apache-activemq-artemis': '-NaN is stripped of its sign: ENTMQ-1686'},
+ 'double': {'apache-activemq-artemis': '-NaN is stripped of its sign: ENTMQ-1686'},
+ }
+
+
+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:
+ # TODO: When Artemis can support it (in the next release), revert the queue name back to 'qpid-interop...'
+ # Currently, Artemis only supports auto-create queues for JMS, and the queue name must be prefixed by
+ # 'jms.queue.'
+ #queue_name = 'qpid-interop.simple_type_tests.%s.%s.%s' % (amqp_type, send_shim.NAME, receive_shim.NAME)
+ queue_name = 'jms.queue.qpid-interop.simple_type_tests.%s.%s.%s' % \
+ (amqp_type, send_shim.NAME, receive_shim.NAME)
+
+ # Start the receive shim first (for queueless brokers/dispatch)
+ receiver = receive_shim.create_receiver(broker_addr, queue_name, amqp_type,
+ str(len(test_value_list)))
+ receiver.start()
+
+ # Start the send shim
+ sender = send_shim.create_sender(broker_addr, queue_name, amqp_type,
+ dumps(test_value_list))
+ 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('Sender error: %s' % str(send_obj))
+
+ # Process return string from receiver
+ receive_obj = receiver.get_return_object()
+ if isinstance(receive_obj, tuple):
+ if len(receive_obj) == 2:
+ return_amqp_type, return_test_value_list = receive_obj
+ self.assertEqual(return_amqp_type, amqp_type,
+ msg='AMQP type error:\n\n sent:%s\n\n received:%s' % \
+ (amqp_type, return_amqp_type))
+ self.assertEqual(return_test_value_list, test_value_list, msg='\n sent:%s\nreceived:%s' % \
+ (test_value_list, return_test_value_list))
+ 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, amqp_type, 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, send_shim, receive_shim):
+ """Function which creates a new test method in class cls"""
+
+ @unittest.skipIf(types.skip_test(amqp_type, broker_name),
+ types.skip_test_message(amqp_type, broker_name))
+ 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->%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': types.get_test_values(amqp_type)}
+ new_class = type(class_name, (AmqpTypeTestCase,), class_dict)
+ for send_shim, receive_shim in shim_product:
+ add_test_method(new_class, send_shim, receive_shim)
+ return new_class
+
+
+# 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.
+PROTON_CPP_RECEIVER_SHIM = path.join(QPID_INTEROP_TEST_HOME, 'shims', 'qpid-proton-cpp', 'build', 'amqp_types_test',
+ 'Receiver')
+PROTON_CPP_SENDER_SHIM = path.join(QPID_INTEROP_TEST_HOME, 'shims', 'qpid-proton-cpp', 'build', 'amqp_types_test',
+ 'Sender')
+PROTON_PYTHON_RECEIVER_SHIM = path.join(QPID_INTEROP_TEST_HOME, 'shims', 'qpid-proton-python', 'src', 'amqp_types_test',
+ 'Receiver.py')
+PROTON_PYTHON_SENDER_SHIM = path.join(QPID_INTEROP_TEST_HOME, 'shims', 'qpid-proton-python', 'src', 'amqp_types_test',
+ 'Sender.py')
+
+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),
+ }
+
+
+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 AMQP simple 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(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' % 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 = AmqpPrimitiveTypes()
+
+ # 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(TYPES.get_type_list()):
+ if ARGS.exclude_type is None or at not in ARGS.exclude_type:
+ test_case_class = create_testcase_class(BROKER,
+ TYPES,
+ ARGS.broker,
+ 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
+ RES = unittest.TextTestRunner(verbosity=2).run(TEST_SUITE)
+ if not RES.wasSuccessful():
+ sys.exit(1) # Errors or failures present
http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/514bac75/src/python/qpid_interop_test/broker_properties.py
----------------------------------------------------------------------
diff --git a/src/python/qpid_interop_test/broker_properties.py b/src/python/qpid_interop_test/broker_properties.py
new file mode 100644
index 0000000..01a090f
--- /dev/null
+++ b/src/python/qpid_interop_test/broker_properties.py
@@ -0,0 +1,55 @@
+"""
+Module containing a small client which connects to the broker and
+gets the broker connection properties so as to identify the broker.
+"""
+
+#
+# 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 proton.handlers import MessagingHandler
+from proton.reactor import Container
+
+class Client(MessagingHandler):
+ """
+ Client to connect to broker and collect connection properties, used to identify the test broker
+ """
+ def __init__(self, url):
+ super(Client, self).__init__()
+ self.url = url
+ self.remote_properties = None
+
+ def on_connection_remote_open(self, event):
+ """Callback for remote connection open"""
+ self.remote_properties = event.connection.remote_properties
+ event.connection.close()
+
+ def on_start(self, event):
+ """Event loop start"""
+ event.container.connect(url=self.url)
+
+ def get_connection_properties(self):
+ """Return the connection properties"""
+ return self.remote_properties
+
+
+def get_broker_properties(broker_url):
+ """Start client, then return its connection properties"""
+ msg_handler = Client(broker_url)
+ Container(msg_handler).run()
+ return msg_handler.get_connection_properties()
http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/514bac75/src/python/qpid_interop_test/interop_test_errors.py
----------------------------------------------------------------------
diff --git a/src/python/qpid_interop_test/interop_test_errors.py b/src/python/qpid_interop_test/interop_test_errors.py
new file mode 100644
index 0000000..6be8959
--- /dev/null
+++ b/src/python/qpid_interop_test/interop_test_errors.py
@@ -0,0 +1,29 @@
+"""
+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 InteropTestError(StandardError):
+ """
+ Generic simple error class for use in interop tests
+ """
+ def __init__(self, error_message):
+ super(InteropTestError, self).__init__(error_message)
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org