http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/514bac75/shims/qpid-proton-python/src/jms_hdrs_props_test/Receiver.py ---------------------------------------------------------------------- diff --git a/shims/qpid-proton-python/src/jms_hdrs_props_test/Receiver.py b/shims/qpid-proton-python/src/jms_hdrs_props_test/Receiver.py new file mode 100755 index 0000000..5740c16 --- /dev/null +++ b/shims/qpid-proton-python/src/jms_hdrs_props_test/Receiver.py @@ -0,0 +1,357 @@ +#!/usr/bin/env python + +""" +JMS message headers and properties test receiver shim for qpid-interop-test +""" + +# +# 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 dumps, loads +from struct import pack, unpack +from subprocess import check_output +import sys +from traceback import format_exc + +from qpid_interop_test.interop_test_errors import InteropTestError +from proton import byte, symbol +from proton.handlers import MessagingHandler +from proton.reactor import Container + +# These values must tie in with the Qpid-JMS client values found in +# org.apache.qpid.jms.provider.amqp.message.AmqpMessageSupport +QPID_JMS_TYPE_ANNOTATION_NAME = symbol(u'x-opt-jms-msg-type') + +class JmsReceiverShim(MessagingHandler): + """ + Receiver shim: This shim receives JMS messages sent by the Sender shim and prints the contents of the received + messages onto the terminal in JSON format for retrieval by the test harness. The JMS messages type and, where + applicable, body values, as well as the combinations of JMS headers and properties which may be attached to + the message are received on the command-line in JSON format when this program is launched. + """ + def __init__(self, url, jms_msg_type, test_parameters_list): + super(JmsReceiverShim, self).__init__() + self.url = url + self.jms_msg_type = jms_msg_type + self.expteced_msg_map = test_parameters_list[0] + self.flag_map = test_parameters_list[1] + self.subtype_itr = iter(sorted(self.expteced_msg_map.keys())) + self.expected = self._get_tot_num_messages() + self.received = 0 + self.received_value_map = {} + self.current_subtype = None + self.current_subtype_msg_list = None + self.jms_header_map = {} + self.jms_property_map = {} + + def get_received_value_map(self): + """"Return the collected message values received""" + return self.received_value_map + + def get_jms_header_map(self): + """Return the collected message headers received""" + return self.jms_header_map + + def get_jms_property_map(self): + """Return the collected message properties received""" + return self.jms_property_map + + def on_start(self, event): + """Event callback for when the client starts""" + event.container.create_receiver(self.url) + + def on_message(self, event): + """Event callback when a message is received by the client""" + if event.message.id and event.message.id < self.received: + return # ignore duplicate message + if self.expected == 0 or self.received < self.expected: + if self.current_subtype is None: + self.current_subtype = self.subtype_itr.next() + self.current_subtype_msg_list = [] + self.current_subtype_msg_list.append(self._handle_message(event.message)) + self._process_jms_headers(event.message) + self._process_jms_properties(event.message) + if len(self.current_subtype_msg_list) >= self.expteced_msg_map[self.current_subtype]: + self.received_value_map[self.current_subtype] = self.current_subtype_msg_list + self.current_subtype = None + self.current_subtype_msg_list = [] + self.received += 1 + if self.received == self.expected: + event.receiver.close() + event.connection.close() + + def on_connection_error(self, event): + print 'JmsReceiverShim.on_connection_error' + + def on_session_error(self, event): + print 'JmsReceiverShim.on_session_error' + + def on_link_error(self, event): + print 'JmsReceiverShim.on_link_error' + + def _handle_message(self, message): + """Handles the analysis of a received message""" + if self.jms_msg_type == 'JMS_MESSAGE_TYPE': + return self._receive_jms_message(message) + if self.jms_msg_type == 'JMS_BYTESMESSAGE_TYPE': + return self._receive_jms_bytesmessage(message) + if self.jms_msg_type == 'JMS_MAPMESSAGE_TYPE': + return self._recieve_jms_mapmessage(message) + if self.jms_msg_type == 'JMS_OBJECTMESSAGE_TYPE': + return self._recieve_jms_objectmessage(message) + if self.jms_msg_type == 'JMS_STREAMMESSAGE_TYPE': + return self._receive_jms_streammessage(message) + if self.jms_msg_type == 'JMS_TEXTMESSAGE_TYPE': + return self._receive_jms_textmessage(message) + print 'jms-receive: Unsupported JMS message type "%s"' % self.jms_msg_type + return None + + def _get_tot_num_messages(self): + """"Counts up the total number of messages which should be received from the expected message map""" + total = 0 + for key in self.expteced_msg_map: + total += int(self.expteced_msg_map[key]) + return total + + def _receive_jms_message(self, message): + """"Receives a JMS message (without a body)""" + assert self.jms_msg_type == 'JMS_MESSAGE_TYPE' + assert message.annotations[QPID_JMS_TYPE_ANNOTATION_NAME] == byte(0) + if message.body is not None: + raise InteropTestError('_receive_jms_message: Invalid body for type JMS_MESSAGE_TYPE: %s' % + str(message.body)) + return None + + def _receive_jms_bytesmessage(self, message): + """"Receives a JMS bytes message""" + assert self.jms_msg_type == 'JMS_BYTESMESSAGE_TYPE' + assert message.annotations[QPID_JMS_TYPE_ANNOTATION_NAME] == byte(3) + if self.current_subtype == 'boolean': + if message.body == b'\x00': + return 'False' + if message.body == b'\x01': + return 'True' + raise InteropTestError('_receive_jms_bytesmessage: Invalid encoding for subtype boolean: %s' % + str(message.body)) + if self.current_subtype == 'byte': + return hex(unpack('b', message.body)[0]) + if self.current_subtype == 'bytes': + return str(message.body) + if self.current_subtype == 'char': + if len(message.body) == 2: # format 'a' or '\xNN' + return str(message.body[1]) # strip leading '\x00' char + raise InteropTestError('Unexpected strring length for type char: %d' % len(message.body)) + if self.current_subtype == 'double': + return '0x%016x' % unpack('!Q', message.body)[0] + if self.current_subtype == 'float': + return '0x%08x' % unpack('!L', message.body)[0] + if self.current_subtype == 'int': + return hex(unpack('!i', message.body)[0]) + if self.current_subtype == 'long': + return hex(unpack('!q', message.body)[0]) + if self.current_subtype == 'short': + return hex(unpack('!h', message.body)[0]) + if self.current_subtype == 'string': + # NOTE: first 2 bytes are string length, must be present + if len(message.body) >= 2: + str_len = unpack('!H', message.body[:2])[0] + str_body = str(message.body[2:]) + if len(str_body) != str_len: + raise InteropTestError('String length mismatch: size=%d, but len(\'%s\')=%d' % + (str_len, str_body, len(str_body))) + return str_body + else: + raise InteropTestError('Malformed string binary: len(\'%s\')=%d' % + (repr(message.body), len(message.body))) + raise InteropTestError('JMS message type %s: Unknown or unsupported subtype \'%s\'' % + (self.jms_msg_type, self.current_subtype)) + + def _recieve_jms_mapmessage(self, message): + """"Receives a JMS map message""" + assert self.jms_msg_type == 'JMS_MAPMESSAGE_TYPE' + assert message.annotations[QPID_JMS_TYPE_ANNOTATION_NAME] == byte(2) + key, value = message.body.items()[0] + assert key[:-3] == self.current_subtype + if self.current_subtype == 'boolean': + return str(value) + if self.current_subtype == 'byte': + return hex(value) + if self.current_subtype == 'bytes': + return str(value) + if self.current_subtype == 'char': + return str(value) + if self.current_subtype == 'double': + return '0x%016x' % unpack('!Q', pack('!d', value))[0] + if self.current_subtype == 'float': + return '0x%08x' % unpack('!L', pack('!f', value))[0] + if self.current_subtype == 'int': + return hex(value) + if self.current_subtype == 'long': + return hex(int(value)) + if self.current_subtype == 'short': + return hex(value) + if self.current_subtype == 'string': + return str(value) + raise InteropTestError('JMS message type %s: Unknown or unsupported subtype \'%s\'' % + (self.jms_msg_type, self.current_subtype)) + + def _recieve_jms_objectmessage(self, message): + """"Receives a JMS Object message""" + assert self.jms_msg_type == 'JMS_OBJECTMESSAGE_TYPE' + assert message.annotations[QPID_JMS_TYPE_ANNOTATION_NAME] == byte(1) + return self._get_java_obj(message.body) + + def _get_java_obj(self, java_obj_bytes): + """ + Take bytes from serialized Java object and construct a Java object, then return its toString() value. The + work of 'translating' the bytes to a Java object and obtaining its class and value is done in a Java + utility org.apache.qpid.interop_test.obj_util.BytesToJavaObj located in jar JavaObjUtils.jar. + java_obj_bytes: hex string representation of bytes from Java object (eg 'aced00057372...') + returns: string containing Java class value as returned by the toString() method + """ + java_obj_bytes_str = ''.join(["%02x" % ord(x) for x in java_obj_bytes]).strip() + out_str = check_output(['java', + '-cp', + 'target/JavaObjUtils.jar', + 'org.apache.qpid.interop_test.obj_util.BytesToJavaObj', + java_obj_bytes_str]) + out_str_list = out_str.split('\n')[:-1] # remove trailing \n + if len(out_str_list) > 1: + raise InteropTestError('Unexpected return from JavaObjUtils: %s' % out_str) + colon_index = out_str_list[0].index(':') + if colon_index < 0: + raise InteropTestError('Unexpected format from JavaObjUtils: %s' % out_str) + java_class_name = out_str_list[0][:colon_index] + java_class_value_str = out_str_list[0][colon_index+1:] + if java_class_name != self.current_subtype: + raise InteropTestError('Unexpected class name from JavaObjUtils: expected %s, recieved %s' % + (self.current_subtype, java_class_name)) + return java_class_value_str + + def _receive_jms_streammessage(self, message): + """Receives a JMS stream message""" + assert self.jms_msg_type == 'JMS_STREAMMESSAGE_TYPE' + assert message.annotations[QPID_JMS_TYPE_ANNOTATION_NAME] == byte(4) + # Every message is a list with one item [value] + assert len(message.body) == 1 + value = message.body[0] + if self.current_subtype == 'boolean': + return str(value) + if self.current_subtype == 'byte': + return hex(value) + if self.current_subtype == 'bytes': + return str(value) + if self.current_subtype == 'char': + return str(value) + if self.current_subtype == 'double': + return '0x%016x' % unpack('!Q', pack('!d', value))[0] + if self.current_subtype == 'float': + return '0x%08x' % unpack('!L', pack('!f', value))[0] + if self.current_subtype == 'int': + return hex(value) + if self.current_subtype == 'long': + return hex(int(value)) + if self.current_subtype == 'short': + return hex(value) + if self.current_subtype == 'string': + return str(value) + raise InteropTestError('JmsRecieverShim._receive_jms_streammessage(): ' + + 'JMS message type %s: Unknown or unsupported subtype \'%s\'' % + (self.jms_msg_type, self.current_subtype)) + + def _receive_jms_textmessage(self, message): + """"Receives a JMS text message""" + assert self.jms_msg_type == 'JMS_TEXTMESSAGE_TYPE' + assert message.annotations[QPID_JMS_TYPE_ANNOTATION_NAME] == byte(5) + return message.body + + def _process_jms_headers(self, message): + """"Checks the supplied message for three JMS headers: message type, correlation-id and reply-to""" + # JMS message type header + message_type_header = message._get_subject() + if message_type_header is not None: + self.jms_header_map['JMS_TYPE_HEADER'] = {'string': message_type_header} + + # JMS correlation ID + correlation_id = message._get_correlation_id() + if correlation_id is not None: + if 'JMS_CORRELATIONID_AS_BYTES' in self.flag_map and self.flag_map['JMS_CORRELATIONID_AS_BYTES']: + self.jms_header_map['JMS_CORRELATIONID_HEADER'] = {'bytes': correlation_id} + else: + self.jms_header_map['JMS_CORRELATIONID_HEADER'] = {'string': correlation_id} + + # JMS reply-to + reply_to = message._get_reply_to() + if reply_to is not None: + if 'JMS_REPLYTO_AS_TOPIC' in self.flag_map and self.flag_map['JMS_REPLYTO_AS_TOPIC']: + # Some brokers prepend 'queue://' and 'topic://' to reply_to addresses, strip these when present + if len(reply_to) > 8 and reply_to[0:8] == 'topic://': + reply_to = reply_to[8:] + self.jms_header_map['JMS_REPLYTO_HEADER'] = {'topic': reply_to} + else: + if len(reply_to) > 8 and reply_to[0:8] == 'queue://': + reply_to = reply_to[8:] + self.jms_header_map['JMS_REPLYTO_HEADER'] = {'queue': reply_to} + + def _process_jms_properties(self, message): + """"Checks the supplied message for JMS message properties and decodes them""" + if message.properties is not None: + for jms_property_name in message.properties: + underscore_index = jms_property_name.find('_') + if underscore_index >= 0: # Ignore any other properties without '_' + jms_property_type = jms_property_name[0:underscore_index] + value = message.properties[jms_property_name] + if jms_property_type == 'boolean': + self.jms_property_map[jms_property_name] = {'boolean': str(value)} + elif jms_property_type == 'byte': + self.jms_property_map[jms_property_name] = {'byte': hex(value)} + elif jms_property_type == 'double': + self.jms_property_map[jms_property_name] = {'double': '0x%016x' % + unpack('!Q', pack('!d', value))[0]} + elif jms_property_type == 'float': + self.jms_property_map[jms_property_name] = {'float': '0x%08x' % + unpack('!L', pack('!f', value))[0]} + elif jms_property_type == 'int': + self.jms_property_map[jms_property_name] = {'int': hex(value)} + elif jms_property_type == 'long': + self.jms_property_map[jms_property_name] = {'long': hex(int(value))} + elif jms_property_type == 'short': + self.jms_property_map[jms_property_name] = {'short': hex(value)} + elif jms_property_type == 'string': + self.jms_property_map[jms_property_name] = {'string': str(value)} + else: + pass # Ignore any other properties, brokers can add them and we don't know what they may be + + +# --- main --- +# Args: 1: Broker address (ip-addr:port) +# 2: Queue name +# 3: JMS message type +# 4: JSON Test parameters containing 2 maps: [testValuesMap, flagMap] +#print '#### sys.argv=%s' % sys.argv +try: + RECEIVER = JmsReceiverShim('%s/%s' % (sys.argv[1], sys.argv[2]), sys.argv[3], loads(sys.argv[4])) + Container(RECEIVER).run() + print sys.argv[3] + print dumps([RECEIVER.get_received_value_map(), RECEIVER.get_jms_header_map(), RECEIVER.get_jms_property_map()]) +except KeyboardInterrupt: + pass +except Exception as exc: + print 'jms-receiver-shim EXCEPTION:', exc + print format_exc()
http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/514bac75/shims/qpid-proton-python/src/jms_hdrs_props_test/Sender.py ---------------------------------------------------------------------- diff --git a/shims/qpid-proton-python/src/jms_hdrs_props_test/Sender.py b/shims/qpid-proton-python/src/jms_hdrs_props_test/Sender.py new file mode 100755 index 0000000..89abf25 --- /dev/null +++ b/shims/qpid-proton-python/src/jms_hdrs_props_test/Sender.py @@ -0,0 +1,388 @@ +#!/usr/bin/env python + +""" +JMS message headers and properties test sender shim for qpid-interop-test +""" + +# +# 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 struct import pack, unpack +from subprocess import check_output +import sys +from traceback import format_exc + +from proton import byte, char, float32, int32, Message, short, symbol +from proton.handlers import MessagingHandler +from proton.reactor import Container +from qpid_interop_test.interop_test_errors import InteropTestError +from qpid_interop_test.test_type_map import TestTypeMap + +# These values must tie in with the Qpid-JMS client values found in +# org.apache.qpid.jms.provider.amqp.message.AmqpMessageSupport +QPID_JMS_TYPE_ANNOTATION_NAME = symbol(u'x-opt-jms-msg-type') +QPID_JMS_TYPE_ANNOTATIONS = { + 'JMS_MESSAGE_TYPE': byte(0), + 'JMS_BYTESMESSAGE_TYPE': byte(3), + 'JMS_MAPMESSAGE_TYPE': byte(2), + 'JMS_OBJECTMESSAGE_TYPE': byte(1), + 'JMS_STREAMMESSAGE_TYPE': byte(4), + 'JMS_TEXTMESSAGE_TYPE': byte(5) + } +def create_annotation(jms_msg_type): + """Function which creates a message annotation for JMS message type as used by the Qpid JMS client""" + return {QPID_JMS_TYPE_ANNOTATION_NAME: QPID_JMS_TYPE_ANNOTATIONS[jms_msg_type]} + +class JmsSenderShim(MessagingHandler): + """ + This shim sends JMS messages of a particular JMS message type according to the test parameters list. This list + contains three maps: + 0: The test value map, which contains test value types as keys, and lists of values of that type; + 1. The test headers map, which contains the JMS headers as keys and a submap conatining types and values; + 2. The test proprties map, which contains the name of the properties as keys, and a submap containing types + and values + This shim takes the combinations of the above map and creates test cases, each of which sends a single message + with (or without) JMS headers and properties. + """ + def __init__(self, broker_ip_addr, queue_name, jms_msg_type, test_parameters_list): + super(JmsSenderShim, self).__init__() + self.broker_ip_addr = broker_ip_addr + self.queue_name = queue_name + self.jms_msg_type = jms_msg_type + self.test_value_map = test_parameters_list[0] + self.test_headers_map = test_parameters_list[1] + self.test_properties_map = test_parameters_list[2] + self.sent = 0 + self.confirmed = 0 + self.total = self._get_total_num_msgs() + + def on_start(self, event): + """Event callback for when the client starts""" + event.container.create_sender('%s/%s' % (self.broker_ip_addr, self.queue_name)) + + def on_sendable(self, event): + """Event callback for when send credit is received, allowing the sending of messages""" + if self.sent == 0: + # These types expect a test_values Python string representation of a map: '{type:[val, val, val], ...}' + for sub_type in sorted(self.test_value_map.keys()): + if self._send_test_values(event, sub_type, self.test_value_map[sub_type]): + return + + def on_connection_error(self, event): + print 'JmsSenderShim.on_connection_error' + + def on_session_error(self, event): + print 'JmsSenderShim.on_session_error' + + def on_link_error(self, event): + print 'JmsSenderShim.on_link_error' + + def on_accepted(self, event): + """Event callback for when a sent message is accepted by the broker""" + self.confirmed += 1 + if self.confirmed == self.total: + event.connection.close() + + def on_disconnected(self, event): + """Event callback for when the broker disconnects with the client""" + self.sent = self.confirmed + + def _get_total_num_msgs(self): + """ + Calculates the total number of messages to be sent based on the message parameters received on the command-line + """ + total = 0 + for key in self.test_value_map.keys(): + total += len(self.test_value_map[key]) + return total + + def _send_test_values(self, event, test_value_type, test_values): + """Method which loops through recieved parameters and sends the corresponding messages""" + value_num = 0 + for test_value in test_values: + if event.sender.credit: + hdr_kwargs, hdr_annotations = self._get_jms_message_header_kwargs() + message = self._create_message(test_value_type, test_value, value_num, hdr_kwargs, hdr_annotations) + # TODO: set message to address + if message is not None: + #self._add_jms_message_headers(message) + self._add_jms_message_properties(message) + event.sender.send(message) + self.sent += 1 + value_num += 1 + else: + event.connection.close() + return True + return False + + # TODO: Change this to return a list of messages. That way each test can return more than one message + def _create_message(self, test_value_type, test_value, value_num, hdr_kwargs, hdr_annotations): + """Create a single message of the appropriate JMS message type""" + if self.jms_msg_type == 'JMS_MESSAGE_TYPE': + return self._create_jms_message(test_value_type, test_value, hdr_kwargs, hdr_annotations) + elif self.jms_msg_type == 'JMS_BYTESMESSAGE_TYPE': + return self._create_jms_bytesmessage(test_value_type, test_value, hdr_kwargs, hdr_annotations) + elif self.jms_msg_type == 'JMS_MAPMESSAGE_TYPE': + return self._create_jms_mapmessage(test_value_type, test_value, "%s%03d" % (test_value_type, value_num), + hdr_kwargs, hdr_annotations) + elif self.jms_msg_type == 'JMS_OBJECTMESSAGE_TYPE': + return self._create_jms_objectmessage('%s:%s' % (test_value_type, test_value), hdr_kwargs, hdr_annotations) + elif self.jms_msg_type == 'JMS_STREAMMESSAGE_TYPE': + return self._create_jms_streammessage(test_value_type, test_value, hdr_kwargs, hdr_annotations) + elif self.jms_msg_type == 'JMS_TEXTMESSAGE_TYPE': + return self._create_jms_textmessage(test_value, hdr_kwargs, hdr_annotations) + else: + print 'jms-send: Unsupported JMS message type "%s"' % self.jms_msg_type + return None + + def _create_jms_message(self, test_value_type, test_value, hdr_kwargs, hdr_annotations): + """Create a JMS message type (without message body)""" + if test_value_type != 'none': + raise InteropTestError('JmsSenderShim._create_jms_message: Unknown or unsupported subtype "%s"' % + test_value_type) + if test_value is not None: + raise InteropTestError('JmsSenderShim._create_jms_message: Invalid value "%s" for subtype "%s"' % + (test_value, test_value_type)) + return Message(id=(self.sent+1), + content_type='application/octet-stream', + annotations=TestTypeMap.merge_dicts(create_annotation('JMS_MESSAGE_TYPE'), + hdr_annotations), + **hdr_kwargs) + + def _create_jms_bytesmessage(self, test_value_type, test_value, hdr_kwargs, hdr_annotations): + """Create a JMS bytes message""" + # NOTE: test_value contains all unicode strings u'...' as returned by json + body_bytes = None + if test_value_type == 'boolean': + body_bytes = b'\x01' if test_value == 'True' else b'\x00' + elif test_value_type == 'byte': + body_bytes = pack('b', int(test_value, 16)) + elif test_value_type == 'bytes': + body_bytes = str(test_value) # remove unicode + elif test_value_type == 'char': + # JMS expects two-byte chars, ASCII chars can be prefixed with '\x00' + body_bytes = '\x00' + str(test_value) # remove unicode + elif test_value_type == 'double' or test_value_type == 'float': + body_bytes = test_value[2:].decode('hex') + elif test_value_type == 'int': + body_bytes = pack('!i', int(test_value, 16)) + elif test_value_type == 'long': + body_bytes = pack('!q', long(test_value, 16)) + elif test_value_type == 'short': + body_bytes = pack('!h', short(test_value, 16)) + elif test_value_type == 'string': + # NOTE: First two bytes must be string length + test_value_str = str(test_value) # remove unicode + body_bytes = pack('!H', len(test_value_str)) + test_value_str + else: + raise InteropTestError('JmsSenderShim._create_jms_bytesmessage: Unknown or unsupported subtype "%s"' % + test_value_type) + return Message(id=(self.sent+1), + body=body_bytes, + inferred=True, + content_type='application/octet-stream', + annotations=TestTypeMap.merge_dicts(create_annotation('JMS_BYTESMESSAGE_TYPE'), + hdr_annotations), + **hdr_kwargs) + + def _create_jms_mapmessage(self, test_value_type, test_value, name, hdr_kwargs, hdr_annotations): + """Create a JMS map message""" + if test_value_type == 'boolean': + value = test_value == 'True' + elif test_value_type == 'byte': + value = byte(int(test_value, 16)) + elif test_value_type == 'bytes': + value = str(test_value) # remove unicode + elif test_value_type == 'char': + value = char(test_value) + elif test_value_type == 'double': + value = unpack('!d', test_value[2:].decode('hex'))[0] + elif test_value_type == 'float': + value = float32(unpack('!f', test_value[2:].decode('hex'))[0]) + elif test_value_type == 'int': + value = int32(int(test_value, 16)) + elif test_value_type == 'long': + value = long(test_value, 16) + elif test_value_type == 'short': + value = short(int(test_value, 16)) + elif test_value_type == 'string': + value = test_value + else: + raise InteropTestError('JmsSenderShim._create_jms_mapmessage: Unknown or unsupported subtype "%s"' % + test_value_type) + return Message(id=(self.sent+1), + body={name: value}, + inferred=False, + annotations=TestTypeMap.merge_dicts(create_annotation('JMS_MAPMESSAGE_TYPE'), + hdr_annotations), + **hdr_kwargs) + + def _create_jms_objectmessage(self, test_value, hdr_kwargs, hdr_annotations): + """Create a JMS object message""" + java_binary = self._s_get_java_obj_binary(test_value) + return Message(id=(self.sent+1), + body=java_binary, + inferred=True, + content_type='application/x-java-serialized-object', + annotations=TestTypeMap.merge_dicts(create_annotation('JMS_MAPMESSAGE_TYPE'), + hdr_annotations), + **hdr_kwargs) + + @staticmethod + def _s_get_java_obj_binary(java_class_str): + """Call external utility to create Java object and stringify it, returning the string representation""" + out_str = check_output(['java', + '-cp', + 'target/JavaObjUtils.jar', + 'org.apache.qpid.interop_test.obj_util.JavaObjToBytes', + java_class_str]) + out_str_list = out_str.split('\n')[:-1] # remove trailing \n + if out_str_list[0] != java_class_str: + raise InteropTestError('JmsSenderShim._s_get_java_obj_binary(): Call to JavaObjToBytes failed\n%s' % + out_str) + return out_str_list[1].decode('hex') + + def _create_jms_streammessage(self, test_value_type, test_value, hdr_kwargs, hdr_annotations): + """Create a JMS stream message""" + if test_value_type == 'boolean': + body_list = [test_value == 'True'] + elif test_value_type == 'byte': + body_list = [byte(int(test_value, 16))] + elif test_value_type == 'bytes': + body_list = [str(test_value)] + elif test_value_type == 'char': + body_list = [char(test_value)] + elif test_value_type == 'double': + body_list = [unpack('!d', test_value[2:].decode('hex'))[0]] + elif test_value_type == 'float': + body_list = [float32(unpack('!f', test_value[2:].decode('hex'))[0])] + elif test_value_type == 'int': + body_list = [int32(int(test_value, 16))] + elif test_value_type == 'long': + body_list = [long(test_value, 16)] + elif test_value_type == 'short': + body_list = [short(int(test_value, 16))] + elif test_value_type == 'string': + body_list = [test_value] + else: + raise InteropTestError('JmsSenderShim._create_jms_streammessage: Unknown or unsupported subtype "%s"' % + test_value_type) + return Message(id=(self.sent+1), + body=body_list, + inferred=True, + annotations=TestTypeMap.merge_dicts(create_annotation('JMS_STREAMMESSAGE_TYPE'), + hdr_annotations), + **hdr_kwargs) + + def _create_jms_textmessage(self, test_value_text, hdr_kwargs, hdr_annotations): + """Create a JMS text message""" + return Message(id=(self.sent+1), + body=unicode(test_value_text), + annotations=TestTypeMap.merge_dicts(create_annotation('JMS_TEXTMESSAGE_TYPE'), + hdr_annotations), + **hdr_kwargs) + + def _get_jms_message_header_kwargs(self): + hdr_kwargs = {} + hdr_annotations = {} + for jms_header in self.test_headers_map.iterkeys(): + value_map = self.test_headers_map[jms_header] + value_type = value_map.keys()[0] # There is only ever one value in map + value = value_map[value_type] + if jms_header == 'JMS_TYPE_HEADER': + if value_type == 'string': + hdr_kwargs['subject'] = value + else: + raise InteropTestError('JmsSenderShim._get_jms_message_header_kwargs(): ' + + 'JMS_TYPE_HEADER requires value type "string", type "%s" found' % + value_type) + elif jms_header == 'JMS_CORRELATIONID_HEADER': + if value_type == 'string': + hdr_kwargs['correlation_id'] = value + elif value_type == 'bytes': + hdr_kwargs['correlation_id'] = str(value) + else: + raise InteropTestError('JmsSenderShim._get_jms_message_header_kwargs(): ' + + 'JMS_CORRELATIONID_HEADER requires value type "string" or "bytes", ' + + 'type "%s" found' % value_type) + hdr_annotations[symbol(u'x-opt-app-correlation-id')] = True + elif jms_header == 'JMS_REPLYTO_HEADER': + if value_type == 'queue': + hdr_kwargs['reply_to'] = value + hdr_annotations[symbol(u'x-opt-jms-reply-to')] = byte(0) + elif value_type == 'topic': + hdr_kwargs['reply_to'] = value + hdr_annotations[symbol(u'x-opt-jms-reply-to')] = byte(1) + elif value_type == 'temp_queue' or value_type == 'temp_topic': + raise InteropTestError('JmsSenderShim._get_jms_message_header_kwargs(): ' + + 'JMS_REPLYTO_HEADER type "temp_queue" or "temp_topic" not handled') + else: + raise InteropTestError('JmsSenderShim._get_jms_message_header_kwargs(): ' + + 'JMS_REPLYTO_HEADER requires value type "queue" or "topic", ' + + 'type "%s" found' % value_type) + else: + raise InteropTestError('JmsSenderShim._add_jms_message_headers(): Invalid JMS message header "%s"' % + jms_header) + return (hdr_kwargs, hdr_annotations) + + def _add_jms_message_properties(self, message): + """Adds message properties to the supplied message from self.test_properties_map""" + for property_name in self.test_properties_map.iterkeys(): + value_map = self.test_properties_map[property_name] + value_type = value_map.keys()[0] # There is only ever one value in map + value = value_map[value_type] + if message.properties is None: + message.properties = {} + if value_type == 'boolean': + message.properties[property_name] = value == 'True' + elif value_type == 'byte': + message.properties[property_name] = byte(int(value, 16)) + elif value_type == 'double': + message.properties[property_name] = unpack('!d', value[2:].decode('hex'))[0] + elif value_type == 'float': + message.properties[property_name] = float32(unpack('!f', value[2:].decode('hex'))[0]) + elif value_type == 'int': + message.properties[property_name] = int(value, 16) + elif value_type == 'long': + message.properties[property_name] = long(value, 16) + elif value_type == 'short': + message.properties[property_name] = short(int(value, 16)) + elif value_type == 'string': + message.properties[property_name] = value + else: + raise InteropTestError('JmsSenderShim._add_jms_message_properties: ' + + 'Unknown or unhandled message property type ?%s"' % value_type) + + + +# --- main --- +# Args: 1: Broker address (ip-addr:port) +# 2: Queue name +# 3: JMS message type +# 4: JSON Test parameters containing 3 maps: [testValueMap, testHeadersMap, testPropertiesMap] +#print '#### sys.argv=%s' % sys.argv +#print '>>> test_values=%s' % loads(sys.argv[4]) +try: + Container(JmsSenderShim(sys.argv[1], sys.argv[2], sys.argv[3], loads(sys.argv[4]))).run() +except KeyboardInterrupt: + pass +except Exception as exc: + print 'jms-sender-shim EXCEPTION:', exc + print format_exc() http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/514bac75/shims/qpid-proton-python/src/jms_messages_test/Receiver.py ---------------------------------------------------------------------- diff --git a/shims/qpid-proton-python/src/jms_messages_test/Receiver.py b/shims/qpid-proton-python/src/jms_messages_test/Receiver.py index 9140db1..eff67c1 100755 --- a/shims/qpid-proton-python/src/jms_messages_test/Receiver.py +++ b/shims/qpid-proton-python/src/jms_messages_test/Receiver.py @@ -23,16 +23,17 @@ JMS receiver shim for qpid-interop-test # under the License. # -import sys -from interop_test_errors import InteropTestError from json import dumps, loads -from proton import byte, symbol -from proton.handlers import MessagingHandler -from proton.reactor import Container from struct import pack, unpack from subprocess import check_output +import sys from traceback import format_exc +from proton import byte, symbol +from proton.handlers import MessagingHandler +from proton.reactor import Container +from qpid_interop_test.interop_test_errors import InteropTestError + # These values must tie in with the Qpid-JMS client values found in # org.apache.qpid.jms.provider.amqp.message.AmqpMessageSupport QPID_JMS_TYPE_ANNOTATION_NAME = symbol(u'x-opt-jms-msg-type') @@ -48,29 +49,18 @@ class JmsReceiverShim(MessagingHandler): super(JmsReceiverShim, self).__init__() self.url = url self.jms_msg_type = jms_msg_type - self.expteced_msg_map = test_parameters_list[0] - self.flag_map = test_parameters_list[1] + self.expteced_msg_map = test_parameters_list self.subtype_itr = iter(sorted(self.expteced_msg_map.keys())) self.expected = self._get_tot_num_messages() self.received = 0 self.received_value_map = {} self.current_subtype = None self.current_subtype_msg_list = None - self.jms_header_map = {} - self.jms_property_map = {} def get_received_value_map(self): """"Return the collected message values received""" return self.received_value_map - def get_jms_header_map(self): - """Return the collected message headers received""" - return self.jms_header_map - - def get_jms_property_map(self): - """Return the collected message properties received""" - return self.jms_property_map - def on_start(self, event): """Event callback for when the client starts""" event.container.create_receiver(self.url) @@ -84,8 +74,6 @@ class JmsReceiverShim(MessagingHandler): self.current_subtype = self.subtype_itr.next() self.current_subtype_msg_list = [] self.current_subtype_msg_list.append(self._handle_message(event.message)) - self._process_jms_headers(event.message) - self._process_jms_properties(event.message) if len(self.current_subtype_msg_list) >= self.expteced_msg_map[self.current_subtype]: self.received_value_map[self.current_subtype] = self.current_subtype_msg_list self.current_subtype = None @@ -280,62 +268,6 @@ class JmsReceiverShim(MessagingHandler): assert message.annotations[QPID_JMS_TYPE_ANNOTATION_NAME] == byte(5) return message.body - def _process_jms_headers(self, message): - """"Checks the supplied message for three JMS headers: message type, correlation-id and reply-to""" - # JMS message type header - message_type_header = message._get_subject() - if message_type_header is not None: - self.jms_header_map['JMS_TYPE_HEADER'] = {'string': message_type_header} - - # JMS correlation ID - correlation_id = message._get_correlation_id() - if correlation_id is not None: - if 'JMS_CORRELATIONID_AS_BYTES' in self.flag_map and self.flag_map['JMS_CORRELATIONID_AS_BYTES']: - self.jms_header_map['JMS_CORRELATIONID_HEADER'] = {'bytes': correlation_id} - else: - self.jms_header_map['JMS_CORRELATIONID_HEADER'] = {'string': correlation_id} - - # JMS reply-to - reply_to = message._get_reply_to() - if reply_to is not None: - if 'JMS_REPLYTO_AS_TOPIC' in self.flag_map and self.flag_map['JMS_REPLYTO_AS_TOPIC']: - # Some brokers prepend 'queue://' and 'topic://' to reply_to addresses, strip these when present - if len(reply_to) > 8 and reply_to[0:8] == 'topic://': - reply_to = reply_to[8:] - self.jms_header_map['JMS_REPLYTO_HEADER'] = {'topic': reply_to} - else: - if len(reply_to) > 8 and reply_to[0:8] == 'queue://': - reply_to = reply_to[8:] - self.jms_header_map['JMS_REPLYTO_HEADER'] = {'queue': reply_to} - - def _process_jms_properties(self, message): - """"Checks the supplied message for JMS message properties and decodes them""" - if message.properties is not None: - for jms_property_name in message.properties: - underscore_index = jms_property_name.find('_') - if underscore_index >= 0: # Ignore any other properties without '_' - jms_property_type = jms_property_name[0:underscore_index] - value = message.properties[jms_property_name] - if jms_property_type == 'boolean': - self.jms_property_map[jms_property_name] = {'boolean': str(value)} - elif jms_property_type == 'byte': - self.jms_property_map[jms_property_name] = {'byte': hex(value)} - elif jms_property_type == 'double': - self.jms_property_map[jms_property_name] = {'double': '0x%016x' % - unpack('!Q', pack('!d', value))[0]} - elif jms_property_type == 'float': - self.jms_property_map[jms_property_name] = {'float': '0x%08x' % - unpack('!L', pack('!f', value))[0]} - elif jms_property_type == 'int': - self.jms_property_map[jms_property_name] = {'int': hex(value)} - elif jms_property_type == 'long': - self.jms_property_map[jms_property_name] = {'long': hex(int(value))} - elif jms_property_type == 'short': - self.jms_property_map[jms_property_name] = {'short': hex(value)} - elif jms_property_type == 'string': - self.jms_property_map[jms_property_name] = {'string': str(value)} - else: - pass # Ignore any other properties, brokers can add them and we don't know what they may be # --- main --- @@ -349,8 +281,6 @@ try: Container(RECEIVER).run() print sys.argv[3] print dumps(RECEIVER.get_received_value_map()) - print dumps(RECEIVER.get_jms_header_map()) - print dumps(RECEIVER.get_jms_property_map()) except KeyboardInterrupt: pass except Exception as exc: http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/514bac75/shims/qpid-proton-python/src/jms_messages_test/Sender.py ---------------------------------------------------------------------- diff --git a/shims/qpid-proton-python/src/jms_messages_test/Sender.py b/shims/qpid-proton-python/src/jms_messages_test/Sender.py index 5a1108a..33a6acd 100755 --- a/shims/qpid-proton-python/src/jms_messages_test/Sender.py +++ b/shims/qpid-proton-python/src/jms_messages_test/Sender.py @@ -23,16 +23,17 @@ JMS sender shim for qpid-interop-test # under the License. # -import sys from json import loads -from proton import byte, char, float32, int32, Message, short, symbol -from proton.handlers import MessagingHandler -from proton.reactor import Container -from interop_test_errors import InteropTestError from subprocess import check_output from struct import pack, unpack +import sys from traceback import format_exc +from proton import byte, char, float32, int32, Message, short, symbol +from proton.handlers import MessagingHandler +from proton.reactor import Container +from qpid_interop_test.interop_test_errors import InteropTestError + # These values must tie in with the Qpid-JMS client values found in # org.apache.qpid.jms.provider.amqp.message.AmqpMessageSupport QPID_JMS_TYPE_ANNOTATION_NAME = symbol(u'x-opt-jms-msg-type') @@ -64,9 +65,7 @@ class JmsSenderShim(MessagingHandler): self.broker_ip_addr = broker_ip_addr self.queue_name = queue_name self.jms_msg_type = jms_msg_type - self.test_value_map = test_parameters_list[0] - self.test_headers_map = test_parameters_list[1] - self.test_properties_map = test_parameters_list[2] + self.test_value_map = test_parameters_list self.sent = 0 self.confirmed = 0 self.total = self._get_total_num_msgs() @@ -119,8 +118,6 @@ class JmsSenderShim(MessagingHandler): message = self._create_message(test_value_type, test_value, value_num) # TODO: set message to address if message is not None: - self._add_jms_message_headers(message) - self._add_jms_message_properties(message) event.sender.send(message) self.sent += 1 value_num += 1 @@ -283,94 +280,6 @@ class JmsSenderShim(MessagingHandler): body=unicode(test_value_text), annotations=create_annotation('JMS_TEXTMESSAGE_TYPE')) - def _add_jms_message_headers(self, message): - """Add JMS headers to the supplied message from self.test_headers_map""" - for jms_header in self.test_headers_map.iterkeys(): - value_map = self.test_headers_map[jms_header] - value_type = value_map.keys()[0] # There is only ever one value in map - value = value_map[value_type] - if jms_header == 'JMS_TYPE_HEADER': - if value_type == 'string': - self._s_set_jms_type_header(message, value) - else: - raise InteropTestError('JmsSenderShim._add_jms_message_headers(): ' + - 'JMS_TYPE_HEADER requires value type "string", type "%s" found' % - value_type) - elif jms_header == 'JMS_CORRELATIONID_HEADER': - if value_type == 'string': - self._s_set_jms_correlation_id(message, value) - elif value_type == 'bytes': - self._s_set_jms_correlation_id(message, str(value)) - else: - raise InteropTestError('JmsSenderShim._add_jms_message_headers(): ' + - 'JMS_CORRELATIONID_HEADER requires value type "string" or "bytes", ' + - 'type "%s" found' % value_type) - elif jms_header == 'JMS_REPLYTO_HEADER': - if value_type == 'queue' or value_type == 'topic': - self._s_set_jms_reply_to(message, value_type, value) - elif value_type == 'temp_queue' or value_type == 'temp_topic': - raise InteropTestError('JmsSenderShim._add_jms_message_headers(): ' + - 'JMS_REPLYTO_HEADER type "temp_queue" or "temp_topic" not handled') - else: - raise InteropTestError('JmsSenderShim._add_jms_message_headers(): ' + - 'JMS_REPLYTO_HEADER requires value type "queue" or "topic", ' + - 'type "%s" found' % value_type) - else: - raise InteropTestError('JmsSenderShim._add_jms_message_headers(): Invalid JMS message header "%s"' % - jms_header) - - - @staticmethod - def _s_set_jms_type_header(message, message_type): - """Adds a JMS message type header""" - message._set_subject(message_type) - - @staticmethod - def _s_set_jms_correlation_id(message, correlation_id): - """Adds a JMS correlation id header""" - message._set_correlation_id(correlation_id) - message.annotations[symbol(u'x-opt-app-correlation-id')] = True - - @staticmethod - def _s_set_jms_reply_to(message, jms_destination_type_str, destination): - """Adds a JMS reply-to header""" - if jms_destination_type_str == 'queue': - message._set_reply_to(destination) - message.annotations[symbol(u'x-opt-jms-reply-to')] = byte(0) - elif jms_destination_type_str == 'topic': - message._set_reply_to(destination) - message.annotations[symbol(u'x-opt-jms-reply-to')] = byte(1) - else: - raise InteropTestError('JmsSenderShim._s_set_jms_reply_to(): ' + - 'Invalid value for jms_destination_type_str "%s"' % jms_destination_type_str) - - def _add_jms_message_properties(self, message): - """Adds message properties to the supplied message from self.test_properties_map""" - for property_name in self.test_properties_map.iterkeys(): - value_map = self.test_properties_map[property_name] - value_type = value_map.keys()[0] # There is only ever one value in map - value = value_map[value_type] - if message.properties is None: - message.properties = {} - if value_type == 'boolean': - message.properties[property_name] = value == 'True' - elif value_type == 'byte': - message.properties[property_name] = byte(int(value, 16)) - elif value_type == 'double': - message.properties[property_name] = unpack('!d', value[2:].decode('hex'))[0] - elif value_type == 'float': - message.properties[property_name] = float32(unpack('!f', value[2:].decode('hex'))[0]) - elif value_type == 'int': - message.properties[property_name] = int(value, 16) - elif value_type == 'long': - message.properties[property_name] = long(value, 16) - elif value_type == 'short': - message.properties[property_name] = short(int(value, 16)) - elif value_type == 'string': - message.properties[property_name] = value - else: - raise InteropTestError('JmsSenderShim._add_jms_message_properties: ' + - 'Unknown or unhandled message property type ?%s"' % value_type) 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 deleted file mode 100644 index 835fca3..0000000 --- a/src/python/qpid-interop-test/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/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 deleted file mode 100644 index a94c993..0000000 --- a/src/python/qpid-interop-test/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -# -# 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 -import types -import jms - 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 deleted file mode 100755 index 30d0577..0000000 --- a/src/python/qpid-interop-test/amqp_types_test.py +++ /dev/null @@ -1,435 +0,0 @@ -#!/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 sys import stdout -from time import mktime, time -from uuid import UUID, uuid4 - -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) - - -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 are sent byte reversed: PROTON-1160', - 'qpid-cpp': 'decimal32 not supported on qpid-cpp broker: QPIDIT-5, QPID-6328', - 'apache-activemq-artemis': 'decimal32 and decimal64 are sent byte reversed: PROTON-1160', - 'qpid-dispatch-router': 'decimal32 and decimal64 are sent byte reversed: PROTON-1160'}, - 'decimal64': {'ActiveMQ': 'decimal32 and decimal64 are sent byte reversed: PROTON-1160', - 'qpid-cpp': 'decimal64 not supported on qpid-cpp broker: QPIDIT-6, QPID-6328', - 'apache-activemq-artemis': 'decimal32 and decimal64 are sent byte reversed: PROTON-1160', - 'qpid-dispatch-router': 'decimal32 and decimal64 are 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(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('Sender error: %s' % str(send_obj)) - - # Process return string from receiver - receive_obj = receiver.get_return_object() - if isinstance(receive_obj, list): - self.assertEqual(receive_obj, test_value_list, msg='\n sent:%s\nreceived:%s' % \ - (test_value_list, receive_obj)) - else: - self.fail('Receiver error: %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 = {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), - } - - -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 = 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 = 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 deleted file mode 100644 index 08cc9cc..0000000 --- a/src/python/qpid-interop-test/broker_properties.py +++ /dev/null @@ -1,54 +0,0 @@ -""" -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): - 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 getBrokerProperties(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 deleted file mode 100644 index 6be8959..0000000 --- a/src/python/qpid-interop-test/interop_test_errors.py +++ /dev/null @@ -1,29 +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 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: [email protected] For additional commands, e-mail: [email protected]
