Repository: qpid-interop-test Updated Branches: refs/heads/master 514bac751 -> 424d23636
QPIDIT-42: JMS headers/properties test finalized. Project: http://git-wip-us.apache.org/repos/asf/qpid-interop-test/repo Commit: http://git-wip-us.apache.org/repos/asf/qpid-interop-test/commit/424d2363 Tree: http://git-wip-us.apache.org/repos/asf/qpid-interop-test/tree/424d2363 Diff: http://git-wip-us.apache.org/repos/asf/qpid-interop-test/diff/424d2363 Branch: refs/heads/master Commit: 424d23636b8ba4e4e02b27585c5214b9c1bf937f Parents: 514bac7 Author: Kim van der Riet <kp...@apache.org> Authored: Fri Oct 14 16:14:53 2016 -0400 Committer: Kim van der Riet <kp...@apache.org> Committed: Fri Oct 14 16:14:53 2016 -0400 ---------------------------------------------------------------------- .../jms_hdrs_props_test/Receiver.java | 46 ++- .../qpid-proton-cpp/src/qpidit/QpidItErrors.cpp | 9 + .../qpid-proton-cpp/src/qpidit/QpidItErrors.hpp | 7 + .../src/qpidit/jms_hdrs_props_test/Receiver.cpp | 54 ++- .../src/qpidit/jms_hdrs_props_test/Receiver.hpp | 2 + .../src/qpidit/jms_hdrs_props_test/Sender.cpp | 2 +- .../src/jms_hdrs_props_test/Receiver.py | 54 ++- .../qpid_interop_test/jms_hdrs_props_test.py | 346 ++++++++++++++----- src/python/qpid_interop_test/shims.py | 10 +- 9 files changed, 412 insertions(+), 118 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/424d2363/shims/qpid-jms/src/main/java/org/apache/qpid/qpid_interop_test/jms_hdrs_props_test/Receiver.java ---------------------------------------------------------------------- diff --git a/shims/qpid-jms/src/main/java/org/apache/qpid/qpid_interop_test/jms_hdrs_props_test/Receiver.java b/shims/qpid-jms/src/main/java/org/apache/qpid/qpid_interop_test/jms_hdrs_props_test/Receiver.java index 27b5e7e..8dc083a 100644 --- a/shims/qpid-jms/src/main/java/org/apache/qpid/qpid_interop_test/jms_hdrs_props_test/Receiver.java +++ b/shims/qpid-jms/src/main/java/org/apache/qpid/qpid_interop_test/jms_hdrs_props_test/Receiver.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.StringReader; import java.io.StringWriter; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -29,6 +30,7 @@ import java.util.List; import javax.jms.BytesMessage; import javax.jms.Connection; import javax.jms.ConnectionFactory; +import javax.jms.DeliveryMode; import javax.jms.Destination; import javax.jms.ExceptionListener; import javax.jms.JMSException; @@ -369,6 +371,43 @@ public class Receiver { } else { addMessageHeaderDestination("JMS_REPLYTO_HEADER", JMS_DESTINATION_TYPE.JMS_QUEUE, message.getJMSReplyTo()); } + if (flagMap.containsKey("JMS_CLIENT_CHECKS") && flagMap.getBoolean("JMS_CLIENT_CHECKS")) { + // Get and check message headers which are set by a JMS-compient sender + // See: https://docs.oracle.com/cd/E19798-01/821-1841/bnces/index.html + // 1. Destination + Destination destination = message.getJMSDestination(); + if (destination.toString().compareTo(_queue.toString()) != 0) { + throw new Exception("JMS_DESTINATION header invalid: found \"" + destination.toString() + + "\"; expected \"" + _queue.toString() + "\""); + } + // 2. Delivery Mode (persistence) + int deliveryMode = message.getJMSDeliveryMode(); + if (deliveryMode != DeliveryMode.NON_PERSISTENT && deliveryMode != DeliveryMode.PERSISTENT) { + throw new Exception("JMS_DELIVERY_MODE header invalid: " + deliveryMode); + } + // 3. Expiration + long expiration = message.getJMSExpiration(); + if (expiration != 0) { + throw new Exception("JMS_EXPIRATION header is non-zero"); + } + // 4. Message ID + String message_id = message.getJMSMessageID(); + // TODO: Find a check for this + // 5. Message priority + int message_priority = message.getJMSPriority(); + if (message_priority != 4) { // Default JMS message priority + throw new Exception("JMS_PRIORITY header is not default (4): found " + message_priority); + } + // 6. Message timestamp + long timeStamp = message.getJMSTimestamp(); + long currentTime = System.currentTimeMillis(); + if (currentTime - timeStamp > 60 * 1000) { // More than 1 minute old + SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss.S z"); + throw new Exception("JMS_TIMESTAMP header contains suspicious value: found " + timeStamp + + " (" + df.format(timeStamp) + ") is not within 1 minute of " + currentTime + + " (" + df.format(currentTime) + ")"); + } + } } protected void addMessageHeaderString(String headerName, String value) { @@ -409,9 +448,10 @@ public class Receiver { while (propertyNames.hasMoreElements()) { JsonObjectBuilder valueMap = Json.createObjectBuilder(); String propertyName = propertyNames.nextElement(); - int underscoreIndex = propertyName.indexOf('_'); - if (underscoreIndex >= 0) { - String propType = propertyName.substring(0, underscoreIndex); + int underscoreIndex1 = propertyName.indexOf('_'); + int underscoreIndex2 = propertyName.indexOf('_', underscoreIndex1 + 1); + if (underscoreIndex1 == 4 && underscoreIndex2 > 5) { + String propType = propertyName.substring(underscoreIndex1 + 1, underscoreIndex2); switch (propType) { case "boolean": valueMap.add(propType, message.getBooleanProperty(propertyName) ? "True" : "False"); http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/424d2363/shims/qpid-proton-cpp/src/qpidit/QpidItErrors.cpp ---------------------------------------------------------------------- diff --git a/shims/qpid-proton-cpp/src/qpidit/QpidItErrors.cpp b/shims/qpid-proton-cpp/src/qpidit/QpidItErrors.cpp index 9d7b821..ed8bc09 100644 --- a/shims/qpid-proton-cpp/src/qpidit/QpidItErrors.cpp +++ b/shims/qpid-proton-cpp/src/qpidit/QpidItErrors.cpp @@ -163,6 +163,15 @@ namespace qpidit PopenError::~PopenError() throw() {} + // --- UnexpectedJMSMessageHeader --- + + UnexpectedJMSMessageHeader::UnexpectedJMSMessageHeader(const std::string& jmsMessageHeader, const std::string& errorDescription) : + std::runtime_error(MSG("Unexpected JMS message header: " << jmsMessageHeader << ": " << errorDescription)) + {} + + UnexpectedJMSMessageHeader::~UnexpectedJMSMessageHeader() throw() {} + + // --- UnknownAmqpTypeError --- UnknownAmqpTypeError::UnknownAmqpTypeError(const std::string& amqpType) : http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/424d2363/shims/qpid-proton-cpp/src/qpidit/QpidItErrors.hpp ---------------------------------------------------------------------- diff --git a/shims/qpid-proton-cpp/src/qpidit/QpidItErrors.hpp b/shims/qpid-proton-cpp/src/qpidit/QpidItErrors.hpp index 45b0c22..1779cce 100644 --- a/shims/qpid-proton-cpp/src/qpidit/QpidItErrors.hpp +++ b/shims/qpid-proton-cpp/src/qpidit/QpidItErrors.hpp @@ -133,6 +133,13 @@ namespace qpidit virtual ~PopenError() throw(); }; + class UnexpectedJMSMessageHeader: public std::runtime_error + { + public: + UnexpectedJMSMessageHeader(const std::string& jmsMessageHeader, const std::string& errorDescription); + virtual ~UnexpectedJMSMessageHeader() throw(); + }; + class UnknownAmqpTypeError: public std::runtime_error { public: http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/424d2363/shims/qpid-proton-cpp/src/qpidit/jms_hdrs_props_test/Receiver.cpp ---------------------------------------------------------------------- diff --git a/shims/qpid-proton-cpp/src/qpidit/jms_hdrs_props_test/Receiver.cpp b/shims/qpid-proton-cpp/src/qpidit/jms_hdrs_props_test/Receiver.cpp index 19a40ed..69c217b 100644 --- a/shims/qpid-proton-cpp/src/qpidit/jms_hdrs_props_test/Receiver.cpp +++ b/shims/qpid-proton-cpp/src/qpidit/jms_hdrs_props_test/Receiver.cpp @@ -34,10 +34,12 @@ namespace qpidit namespace jms_hdrs_props_test { Receiver::Receiver(const std::string& brokerUrl, + const std::string& queueName, const std::string& jmsMessageType, const Json::Value& testNumberMap, const Json::Value& flagMap): _brokerUrl(brokerUrl), + _queueName(queueName), _jmsMessageType(jmsMessageType), _testNumberMap(testNumberMap), _flagMap(flagMap), @@ -66,7 +68,9 @@ namespace qpidit } void Receiver::on_container_start(proton::container &c) { - c.open_receiver(_brokerUrl); + std::ostringstream oss; + oss << _brokerUrl << "/" << _queueName; + c.open_receiver(oss.str()); } void Receiver::on_message(proton::delivery &d, proton::message &m) { @@ -309,6 +313,49 @@ namespace qpidit addMessageHeaderDestination("JMS_REPLYTO_HEADER", qpidit::JMS_QUEUE, reply_to); } } + + if (_flagMap.isMember("JMS_CLIENT_CHECKS") && _flagMap["JMS_CLIENT_CHECKS"].asBool()) { + // Get and check message headers which are set by a JMS-compient sender + // See: https://docs.oracle.com/cd/E19798-01/821-1841/bnces/index.html + // 1. Destination + const std::string destination = msg.to(); + if (destination.compare(_queueName) != 0) { + std::ostringstream oss; + oss << "Invalid header: found \"" << destination << "\"; expected \"" << _queueName << "\""; + throw qpidit::UnexpectedJMSMessageHeader("JMS_DESTINATION", oss.str()); + } + // 2. Delivery Mode (persistence) + if (msg.durable()) { + throw qpidit::UnexpectedJMSMessageHeader("JMS_DELIVERY_MODE", "Expected NON_PERSISTENT, found PERSISTENT"); + } + // 3. Expiration + const time_t expiryTime = msg.expiry_time().milliseconds(); + if (expiryTime != 0) { + std::ostringstream oss; + oss << "Expected expiration time 0, found " << expiryTime << " (" << std::asctime(std::localtime(&expiryTime)) << ")"; + throw qpidit::UnexpectedJMSMessageHeader("JMS_EXPIRATION", oss.str()); + } + // 4. Message ID + proton::message_id mid = msg.id(); + // TODO: Find a check for this + // 5. Message priority + int msgPriority = msg.priority(); + if (msgPriority != 4) { // Default JMS message priority + std::ostringstream oss; + oss << "Expected default priority (4), found priority " << msgPriority; + throw qpidit::UnexpectedJMSMessageHeader("JMS_PRIORITY", oss.str()); + } + // 6. Message timestamp + const time_t creationTime = msg.creation_time().milliseconds(); + const time_t currentTime = proton::timestamp::now().milliseconds(); + if (currentTime - creationTime > 60 * 1000) { // More than 1 minute old + std::ostringstream oss; + oss << "Header contains suspicious value: found " << creationTime << " ("; + oss << std::asctime(std::localtime(&creationTime)) << ") is not within 1 minute of now "; + oss << currentTime << " (" << std::asctime(std::localtime(¤tTime)) << ")"; + throw qpidit::UnexpectedJMSMessageHeader("JMS_TIMESTAMP", oss.str()); + } + } } void Receiver::addMessageHeaderString(const char* headerName, const std::string& value) { @@ -370,9 +417,6 @@ int main(int argc, char** argv) { throw qpidit::ArgumentError("Incorrect number of arguments"); } - std::ostringstream oss; - oss << argv[1] << "/" << argv[2]; - try { Json::Value testParams; Json::Reader jsonReader; @@ -380,7 +424,7 @@ int main(int argc, char** argv) { throw qpidit::JsonParserError(jsonReader); } - qpidit::jms_hdrs_props_test::Receiver receiver(oss.str(), argv[3], testParams[0], testParams[1]); + qpidit::jms_hdrs_props_test::Receiver receiver(argv[1], argv[2], argv[3], testParams[0], testParams[1]); proton::default_container(receiver).run(); Json::FastWriter fw; http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/424d2363/shims/qpid-proton-cpp/src/qpidit/jms_hdrs_props_test/Receiver.hpp ---------------------------------------------------------------------- diff --git a/shims/qpid-proton-cpp/src/qpidit/jms_hdrs_props_test/Receiver.hpp b/shims/qpid-proton-cpp/src/qpidit/jms_hdrs_props_test/Receiver.hpp index 36db58d..b5240c8 100644 --- a/shims/qpid-proton-cpp/src/qpidit/jms_hdrs_props_test/Receiver.hpp +++ b/shims/qpid-proton-cpp/src/qpidit/jms_hdrs_props_test/Receiver.hpp @@ -38,6 +38,7 @@ namespace qpidit { protected: const std::string _brokerUrl; + const std::string _queueName; const std::string _jmsMessageType; const Json::Value _testNumberMap; const Json::Value _flagMap; @@ -51,6 +52,7 @@ namespace qpidit Json::Value _receivedPropertiesMap; public: Receiver(const std::string& brokerUrl, + const std::string& queueName, const std::string& jmsMessageType, const Json::Value& testNumberMap, const Json::Value& flagMap); http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/424d2363/shims/qpid-proton-cpp/src/qpidit/jms_hdrs_props_test/Sender.cpp ---------------------------------------------------------------------- diff --git a/shims/qpid-proton-cpp/src/qpidit/jms_hdrs_props_test/Sender.cpp b/shims/qpid-proton-cpp/src/qpidit/jms_hdrs_props_test/Sender.cpp index e3ff0e6..c34dfc2 100644 --- a/shims/qpid-proton-cpp/src/qpidit/jms_hdrs_props_test/Sender.cpp +++ b/shims/qpid-proton-cpp/src/qpidit/jms_hdrs_props_test/Sender.cpp @@ -446,6 +446,6 @@ int main(int argc, char** argv) { qpidit::jms_hdrs_props_test::Sender sender(oss.str(), argv[3], testParams); proton::default_container(sender).run(); } catch (const std::exception& e) { - std::cout << "JmsSender error: " << e.what() << std::endl; + std::cout << "Sender error: " << e.what() << std::endl; } } http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/424d2363/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 index 5740c16..5c43474 100755 --- a/shims/qpid-proton-python/src/jms_hdrs_props_test/Receiver.py +++ b/shims/qpid-proton-python/src/jms_hdrs_props_test/Receiver.py @@ -27,6 +27,7 @@ from json import dumps, loads from struct import pack, unpack from subprocess import check_output import sys +from time import strftime, time from traceback import format_exc from qpid_interop_test.interop_test_errors import InteropTestError @@ -45,9 +46,10 @@ class JmsReceiverShim(MessagingHandler): 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): + def __init__(self, url, queue, jms_msg_type, test_parameters_list): super(JmsReceiverShim, self).__init__() self.url = url + self.queue = queue self.jms_msg_type = jms_msg_type self.expteced_msg_map = test_parameters_list[0] self.flag_map = test_parameters_list[1] @@ -74,7 +76,7 @@ class JmsReceiverShim(MessagingHandler): def on_start(self, event): """Event callback for when the client starts""" - event.container.create_receiver(self.url) + event.container.create_receiver('%s/%s' % (self.url, self.queue)) def on_message(self, event): """Event callback when a message is received by the client""" @@ -284,12 +286,12 @@ class JmsReceiverShim(MessagingHandler): 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() + message_type_header = message.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() + correlation_id = message.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} @@ -297,7 +299,7 @@ class JmsReceiverShim(MessagingHandler): self.jms_header_map['JMS_CORRELATIONID_HEADER'] = {'string': correlation_id} # JMS reply-to - reply_to = message._get_reply_to() + reply_to = message.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 @@ -309,13 +311,47 @@ class JmsReceiverShim(MessagingHandler): reply_to = reply_to[8:] self.jms_header_map['JMS_REPLYTO_HEADER'] = {'queue': reply_to} + # JMS client checks + if 'JMS_CLIENT_CHECKS' in self.flag_map and self.flag_map['JMS_CLIENT_CHECKS']: + # Get and check message headers which are set by a JMS-compient sender + # See: https://docs.oracle.com/cd/E19798-01/821-1841/bnces/index.html + # 1. Destination + destination = message.address + if destination != self.queue: + raise InteropTestError('JMS_DESTINATION header invalid: found "' + destination + + '"; expected "' + self.queue + '"') + # 2. Delivery Mode (persistence) + if message.durable: + raise InteropTestError('JMS_DELIVERY_MODE header invalid: expected NON_PERSISTENT; found PERSISTENT') + # 3. Expiration + expiration = message.expiry_time + if expiration != 0: + raise InteropTestError('JMS_EXPIRATION header is non-zero') + # 4. Message ID + message_id = message.id + if (len(message_id) == 0): + raise InteropTestError('JMS_MESSAGEID header is empty (zero-length)') + # TODO: Find a check for this + # 5. Message priority + if message.priority != 4: + raise InteropTestError('JMS_PRIORITY header is not default (4): found %d' % message.priority) + # 6. Message timestamp + time_stamp = message.creation_time + current_time = time() + if current_time - time_stamp > 60 * 1000: # More than 1 minute old + raise InteropTestError('JMS_TIMESTAMP header contains suspicious value: ' + \ + 'found %d (%s) is not within 1 minute of now %d (%s)' % + (time_stamp, strftime('%m/%d/%Y %H:%M:%S %Z', time_stamp), + current_time, strftime('%m/%d/%Y %H:%M:%S %Z', current_time))) + 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] + underscore_index_1 = jms_property_name.find('_') + underscore_index_2 = jms_property_name.find('_', underscore_index_1+1) + if underscore_index_1 == 4 and underscore_index_2 > 5: # Ignore any other properties without '_' + jms_property_type = jms_property_name[underscore_index_1+1:underscore_index_2] value = message.properties[jms_property_name] if jms_property_type == 'boolean': self.jms_property_map[jms_property_name] = {'boolean': str(value)} @@ -346,7 +382,7 @@ class JmsReceiverShim(MessagingHandler): # 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])) + RECEIVER = JmsReceiverShim(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()]) http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/424d2363/src/python/qpid_interop_test/jms_hdrs_props_test.py ---------------------------------------------------------------------- diff --git a/src/python/qpid_interop_test/jms_hdrs_props_test.py b/src/python/qpid_interop_test/jms_hdrs_props_test.py index 134f465..92a40d1 100755 --- a/src/python/qpid_interop_test/jms_hdrs_props_test.py +++ b/src/python/qpid_interop_test/jms_hdrs_props_test.py @@ -27,7 +27,7 @@ import argparse import sys import unittest -from itertools import product +from itertools import combinations, product from json import dumps from os import getenv, path @@ -131,21 +131,6 @@ class JmsMessageTypes(TestTypeMap): # 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', @@ -242,18 +227,18 @@ class JmsMessageTypes(TestTypeMap): BROKER_SKIP = {} -class JmsMessageTypeTestCase(unittest.TestCase): +class JmsMessageHdrsPropsTestCase(unittest.TestCase): """ - Abstract base class for JMS message type test cases + Abstract base class for JMS message headers and properties test cases """ - def run_test(self, broker_addr, jms_message_type, test_values, msg_hdrs, msg_props, send_shim, receive_shim): + def run_test(self, broker_addr, queue_name_fragment, 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) + queue_name = 'jms.queue.qpid-interop.jms_message_hdrs_props_tests.%s' % queue_name_fragment # First create a map containing the numbers of expected mesasges for each JMS message type num_test_values_map = {} @@ -263,10 +248,14 @@ class JmsMessageTypeTestCase(unittest.TestCase): # 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 + if msg_hdrs is not None: + if 'JMS_CORRELATIONID_HEADER' in msg_hdrs and 'bytes' in msg_hdrs['JMS_CORRELATIONID_HEADER']: + flags_map['JMS_CORRELATIONID_AS_BYTES'] = True + if msg_props is not None: + if 'JMS_REPLYTO_HEADER' in msg_hdrs and 'topic' in msg_hdrs['JMS_REPLYTO_HEADER']: + flags_map['JMS_REPLYTO_AS_TOPIC'] = True + if send_shim.JMS_CLIENT: + flags_map['JMS_CLIENT_CHECKS'] = True # Start the receiver shim receiver = receive_shim.create_receiver(broker_addr, queue_name, jms_message_type, dumps([num_test_values_map, flags_map])) @@ -297,7 +286,7 @@ class JmsMessageTypeTestCase(unittest.TestCase): if isinstance(receive_obj, tuple): if len(receive_obj) == 2: return_jms_message_type, return_list = receive_obj - if (len(return_list) == 3): + if len(return_list) == 3: return_test_values = return_list[0] return_msg_hdrs = return_list[1] return_msg_props = return_list[2] @@ -320,23 +309,47 @@ class JmsMessageTypeTestCase(unittest.TestCase): self.fail(str(receive_obj)) -def create_testcase_class(broker_name, types, broker_addr, jms_message_type, shim_product): +def create_testcases(): + # --- Message headers on JMS Message --- + + # Part A: Single message header on each message + test_case_class_a = create_part_a_testcase_class() + TEST_SUITE.addTest(unittest.makeSuite(test_case_class_a)) + + # Part B: Combination of message headers, using first value in each value list + test_case_class_b = create_part_b_testcase_class() + TEST_SUITE.addTest(unittest.makeSuite(test_case_class_b)) + + # TODO: Add part C and D (properties) when C++ cleint can handle them + + # Part C: Single message property on each message + #test_case_class_c = create_part_c_testcase_class() + #TEST_SUITE.addTest(unittest.makeSuite(test_case_class_c)) + + # Part D: All headers and all properties on one of each type of JMS message + #for jms_message_type in sorted(TYPES.TYPE_MAP.keys()): + # test_case_class_d = create_part_d_testcase_class(jms_message_type) + # TEST_SUITE.addTest(unittest.makeSuite(test_case_class_d)) + + +def create_part_a_testcase_class(): """ - 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 + Class factory function which creates new subclasses to JmsMessageTypeTestCase. Creates a test case class for + a single JMS message type containing a single JMS header, one for each possible header """ def __repr__(self): """Print the class name""" return self.__class__.__name__ - def add_test_method(cls, hdrs, props, send_shim, receive_shim): + def add_test_method(cls, queue_name_fragment, 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)) + @unittest.skipIf(TYPES.skip_test(jms_message_type, BROKER), + TYPES.skip_test_message(jms_message_type, BROKER)) def inner_test_method(self): self.run_test(self.broker_addr, + queue_name_fragment, self.jms_message_type, self.test_values, hdrs[1], @@ -344,69 +357,222 @@ def create_testcase_class(broker_name, types, broker_addr, jms_message_type, shi 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) + inner_test_method.__name__ = 'test.A.%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' + jms_message_type = TYPES.TYPE_MAP.keys()[0] + class_name = 'PartA_SingleJmsHeader_TestCase' class_dict = {'__name__': class_name, '__repr__': __repr__, - '__doc__': 'Test case for JMS message type \'%s\'' % jms_message_type, + '__doc__': 'Test case for JMS message type \'%s\' containing a single ' % jms_message_type + + 'JMS header, one for each possible header', '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 + 'broker_addr': ARGS.broker, + 'test_values': TYPES.get_test_values(jms_message_type)} + new_class = type(class_name, (JmsMessageHdrsPropsTestCase,), class_dict) + + for send_shim, receive_shim in product(SHIM_MAP.values(), repeat=2): + for msg_header in TYPES.HEADERS_MAP.iterkeys(): + for header_type, header_val_list in TYPES.HEADERS_MAP[msg_header].iteritems(): + header_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) + header_val_cnt += 1 + method_subname = '%s.%s-%02d' % (msg_header, header_type, header_val_cnt) add_test_method(new_class, - (test_name, {msg_header: {header_type: header_val}}), + method_subname, + (method_subname, {msg_header: {header_type: header_val}}), ('', {}), send_shim, receive_shim) + return new_class + + +def create_part_b_testcase_class(): + """ + Class factory function which creates new subclasses to JmsMessageTypeTestCase. Creates a test case class for + a single JMS message type containing a combination of JMS headers + """ + + def __repr__(self): + """Print the class name""" + return self.__class__.__name__ + + def add_test_method(cls, queue_name_fragment, 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), + TYPES.skip_test_message(jms_message_type, BROKER)) + def inner_test_method(self): + self.run_test(self.broker_addr, + queue_name_fragment, + self.jms_message_type, + self.test_values, + hdrs[1], + props[1], + send_shim, + receive_shim) + + inner_test_method.__name__ = 'test.B.%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) + + jms_message_type = TYPES.TYPE_MAP.keys()[0] + class_name = 'PartB_JmsHeaderCombination_TestCase' + class_dict = {'__name__': class_name, + '__repr__': __repr__, + '__doc__': 'Test case for JMS message type \'%s\' containing a combination ' % jms_message_type + + 'of possible JMS headers', + 'jms_message_type': jms_message_type, + 'broker_addr': ARGS.broker, + 'test_values': TYPES.get_test_values(jms_message_type)} + new_class = type(class_name, (JmsMessageHdrsPropsTestCase,), class_dict) + + for send_shim, receive_shim in product(SHIM_MAP.values(), repeat=2): + jms_hdrs_combos = [] + for jms_hdrs_combo_index in range(0, len(TYPES.HEADERS_MAP.keys())+1): + for jms_hdrs_combo in combinations(TYPES.HEADERS_MAP.iterkeys(), jms_hdrs_combo_index): + jms_hdr_list = [] + for jms_header in jms_hdrs_combo: + data_type_list = [] + for data_type in TYPES.HEADERS_MAP[jms_header].keys(): + data_type_list.append((jms_header, data_type)) + jms_hdr_list.append(data_type_list) + for combo in product(*jms_hdr_list): + if len(combo) > 1: # ignore empty and single combos (already tested in Part A) + method_subname = '' + header_map = {} + for combo_item in combo: + if len(method_subname) > 0: + method_subname += '+' + method_subname += '%s:%s' % combo_item + header_type_map = TYPES.HEADERS_MAP[combo_item[0]] + header_val_list = header_type_map[combo_item[1]] + header_map[combo_item[0]] = {combo_item[1]: header_val_list[0]} + add_test_method(new_class, + method_subname, + (method_subname, header_map), + ('', {}), + send_shim, + receive_shim) + return new_class + + +def create_part_c_testcase_class(): + """ + Class factory function which creates new subclasses to JmsMessageTypeTestCase. Creates a test case class for + a single JMS message type containing a single JMS property + """ + + def __repr__(self): + """Print the class name""" + return self.__class__.__name__ + + def add_test_method(cls, queue_name_fragment, 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), + TYPES.skip_test_message(jms_message_type, BROKER)) + def inner_test_method(self): + self.run_test(self.broker_addr, + queue_name_fragment, + self.jms_message_type, + self.test_values, + hdrs[1], + props[1], + send_shim, + receive_shim) + + inner_test_method.__name__ = 'test.C.%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) + + jms_message_type = TYPES.TYPE_MAP.keys()[0] + class_name = 'PartC_SingleJmsProperty_TestCase' + class_dict = {'__name__': class_name, + '__repr__': __repr__, + '__doc__': 'Test case for JMS message type \'%s\' containing a single ' % jms_message_type + + 'JMS property', + 'jms_message_type': jms_message_type, + 'broker_addr': ARGS.broker, + 'test_values': TYPES.get_test_values(jms_message_type)} + new_class = type(class_name, (JmsMessageHdrsPropsTestCase,), class_dict) + + for send_shim, receive_shim in product(SHIM_MAP.values(), repeat=2): + 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 + prop_name = 'prop_%s_%02d' % (prop_type, prop_val_cnt) + add_test_method(new_class, + prop_name, + ('', {}), + (prop_name, {prop_name: {prop_type: prop_val}}), + send_shim, + receive_shim) + return new_class + + +def create_part_d_testcase_class(jms_message_type): + """ + Class factory function which creates new subclasses to JmsMessageTypeTestCase. Creates a test case class for + all message headers and properties on each type of JMS message + """ - # 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) + def __repr__(self): + """Print the class name""" + return self.__class__.__name__ + def add_test_method(cls, queue_name_fragment, 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), + TYPES.skip_test_message(jms_message_type, BROKER)) + def inner_test_method(self): + self.run_test(self.broker_addr, + queue_name_fragment, + jms_message_type, + self.test_values, + hdrs[1], + props[1], + send_shim, + receive_shim) + + inner_test_method.__name__ = 'test.D.%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 = 'PartD_AllJmsHeaders_AllJmsProperties_TestCase' + class_dict = {'__name__': class_name, + '__repr__': __repr__, + '__doc__': 'Test case for JMS message type \'%s\' containing a single ' % jms_message_type + + 'JMS property', + 'jms_message_type': jms_message_type, + 'broker_addr': ARGS.broker, + 'test_values': TYPES.get_test_values(jms_message_type)} + new_class = type(class_name, (JmsMessageHdrsPropsTestCase,), class_dict) + + 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} + + 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['prop_%s_%02d' % (prop_type, prop_val_cnt)] = {prop_type: prop_val} + + for send_shim, receive_shim in product(SHIM_MAP.values(), repeat=2): + add_test_method(new_class, + 'HDRS+PROPS', + ('hdrs', all_hdrs), + ('pros', all_props), + send_shim, + receive_shim) return new_class @@ -491,10 +657,6 @@ if __name__ == '__main__': 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() @@ -504,15 +666,7 @@ if __name__ == '__main__': 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)) + create_testcases() # Finally, run all the dynamically created tests RES = unittest.TextTestRunner(verbosity=2).run(TEST_SUITE) http://git-wip-us.apache.org/repos/asf/qpid-interop-test/blob/424d2363/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 index f6d019e..56d6d93 100644 --- a/src/python/qpid_interop_test/shims.py +++ b/src/python/qpid_interop_test/shims.py @@ -129,12 +129,12 @@ class Receiver(ShimWorkerThread): else: #print '<<<', stdoutdata # DEBUG - useful to see text received from shim str_tvl = stdoutdata.split('\n')[0:-1] # remove trailing \n - #if len(str_tvl) == 1: - # self.return_obj = output if len(str_tvl) == 2: - self.return_obj = (str_tvl[0], loads(str_tvl[1])) + try: + self.return_obj = (str_tvl[0], loads(str_tvl[1])) + except ValueError: + self.return_obj = stdoutdata 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 @@ -142,6 +142,7 @@ class Receiver(ShimWorkerThread): class Shim(object): """Abstract shim class, parent of all shims.""" NAME = None + JMS_CLIENT = False # Enables certain JMS-specific message checks def __init__(self, sender_shim, receiver_shim): self.sender_shim = sender_shim self.receiver_shim = receiver_shim @@ -178,6 +179,7 @@ class ProtonCppShim(Shim): class QpidJmsShim(Shim): """Shim for qpid-jms JMS client""" NAME = 'QpidJms' + JMS_CLIENT = True # Installed versions # TODO: Automate this - it gets out of date quickly --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org For additional commands, e-mail: commits-h...@qpid.apache.org