From b9c951723fe5081ea2611e7c04ee8d2df6a03205 Mon Sep 17 00:00:00 2001 From: Rupert Smith Date: Mon, 26 Nov 2007 11:38:27 +0000 Subject: Updated test framework to seperate JMS and AMQP specific test cases. Added configurable test decorators so that in vm connection can be injected at run time. git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/branches/M2.1@598233 13f79535-47bb-0310-9956-ffa450edef68 --- .../framework/distributedtesting/TestClient.java | 463 ------------------- .../qpid/perftests/QpidTestThroughputPerf.java | 170 ------- .../qpid/test/testcases/MessageThroughputPerf.java | 172 +++++++ java/pom.xml | 20 +- java/systests/pom.xml | 5 + .../qpid/server/exchange/ImmediateMessageTest.java | 296 ------------ .../qpid/server/exchange/MandatoryMessageTest.java | 308 ------------- .../apache/qpid/test/framework/AMQPPublisher.java | 54 +++ .../apache/qpid/test/framework/CircuitEndBase.java | 11 +- .../qpid/test/framework/ExceptionMonitor.java | 51 ++- .../qpid/test/framework/FrameworkBaseCase.java | 109 +---- .../test/framework/LocalAMQPCircuitFactory.java | 168 +++++++ .../qpid/test/framework/LocalCircuitFactory.java | 315 +++++++++++++ .../framework/MessagingTestConfigProperties.java | 268 +++++++++-- .../test/framework/NotApplicableAssertion.java | 112 +++++ .../org/apache/qpid/test/framework/Publisher.java | 40 +- .../org/apache/qpid/test/framework/Receiver.java | 48 +- .../org/apache/qpid/test/framework/TestUtils.java | 8 +- .../clocksynch/ClockSynchFailureException.java | 4 +- .../framework/clocksynch/ClockSynchronizer.java | 2 +- .../DistributedPublisherImpl.java | 35 +- .../DistributedReceiverImpl.java | 47 +- .../distributedcircuit/TestClientCircuitEnd.java | 17 +- .../framework/distributedtesting/Coordinator.java | 60 +-- .../distributedtesting/InteropTestDecorator.java | 2 +- .../framework/distributedtesting/TestClient.java | 497 +++++++++++++++++++++ .../localcircuit/LocalAMQPPublisherImpl.java | 133 ++++++ .../framework/localcircuit/LocalCircuitImpl.java | 181 +------- .../framework/localcircuit/LocalPublisherImpl.java | 60 ++- .../framework/localcircuit/LocalReceiverImpl.java | 54 ++- .../test/framework/qpid/AMQPFeatureDecorator.java | 83 ++++ .../test/framework/qpid/InVMBrokerDecorator.java | 92 ++++ .../framework/sequencers/BaseCircuitFactory.java | 9 +- .../test/framework/sequencers/CircuitFactory.java | 8 +- .../framework/sequencers/FanOutCircuitFactory.java | 4 +- .../sequencers/InteropCircuitFactory.java | 12 +- .../qpid/test/testcases/ImmediateMessageTest.java | 307 +++++++++++++ .../qpid/test/testcases/MandatoryMessageTest.java | 321 +++++++++++++ .../apache/qpid/test/testcases/RollbackTest.java | 132 ++++++ .../org/apache/qpid/util/ConversationFactory.java | 2 +- 40 files changed, 3018 insertions(+), 1662 deletions(-) delete mode 100644 java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java delete mode 100644 java/perftests/src/main/java/org/apache/qpid/perftests/QpidTestThroughputPerf.java create mode 100644 java/perftests/src/main/java/org/apache/qpid/test/testcases/MessageThroughputPerf.java delete mode 100644 java/systests/src/main/java/org/apache/qpid/server/exchange/ImmediateMessageTest.java delete mode 100644 java/systests/src/main/java/org/apache/qpid/server/exchange/MandatoryMessageTest.java create mode 100644 java/systests/src/main/java/org/apache/qpid/test/framework/AMQPPublisher.java create mode 100644 java/systests/src/main/java/org/apache/qpid/test/framework/LocalAMQPCircuitFactory.java create mode 100644 java/systests/src/main/java/org/apache/qpid/test/framework/LocalCircuitFactory.java create mode 100644 java/systests/src/main/java/org/apache/qpid/test/framework/NotApplicableAssertion.java create mode 100644 java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java create mode 100644 java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalAMQPPublisherImpl.java create mode 100644 java/systests/src/main/java/org/apache/qpid/test/framework/qpid/AMQPFeatureDecorator.java create mode 100644 java/systests/src/main/java/org/apache/qpid/test/framework/qpid/InVMBrokerDecorator.java create mode 100644 java/systests/src/main/java/org/apache/qpid/test/testcases/ImmediateMessageTest.java create mode 100644 java/systests/src/main/java/org/apache/qpid/test/testcases/MandatoryMessageTest.java create mode 100644 java/systests/src/main/java/org/apache/qpid/test/testcases/RollbackTest.java (limited to 'java') diff --git a/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java b/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java deleted file mode 100644 index 8f0ef193ef..0000000000 --- a/java/integrationtests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java +++ /dev/null @@ -1,463 +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. - * - */ -package org.apache.qpid.test.framework.distributedtesting; - -import org.apache.log4j.Logger; -import org.apache.log4j.NDC; - -import org.apache.qpid.interop.clienttestcases.*; -import org.apache.qpid.sustained.SustainedClientTestCase; -import org.apache.qpid.test.framework.MessagingTestConfigProperties; -import org.apache.qpid.test.framework.TestUtils; -import org.apache.qpid.test.framework.clocksynch.ClockSynchThread; -import org.apache.qpid.test.framework.clocksynch.ClockSynchronizer; -import org.apache.qpid.test.framework.clocksynch.UDPClockSynchronizer; -import org.apache.qpid.test.framework.distributedcircuit.TestClientCircuitEnd; - -import uk.co.thebadgerset.junit.extensions.SleepThrottle; -import uk.co.thebadgerset.junit.extensions.Throttle; -import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; -import uk.co.thebadgerset.junit.extensions.util.TestContextProperties; - -import javax.jms.*; - -import java.util.*; - -/** - * Implements a test client as described in the interop testing spec - * (http://cwiki.apache.org/confluence/display/qpid/Interop+Testing+Specification). A test client is an agent that - * reacts to control message sequences send by the test {@link Coordinator}. - * - *

- *
Messages Handled by SustainedTestClient
Message Action - *
Invite(compulsory) Reply with Enlist. - *
Invite(test case) Reply with Enlist if test case available. - *
AssignRole(test case) Reply with Accept Role if matches an enlisted test. Keep test parameters. - *
Start Send test messages defined by test parameters. Send report on messages sent. - *
Status Request Send report on messages received. - *
Terminate Terminate the test client. - *
ClockSynch Synch clock against the supplied UDP address. - *
- * - *

- *
CRC Card
Responsibilities Collaborations - *
Handle all incoming control messages. {@link TestClientControlledTest} - *
Configure and look up test cases by name. {@link TestClientControlledTest} - *
- */ -public class TestClient implements MessageListener -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(TestClient.class); - - /** Used for reporting to the console. */ - private static final Logger console = Logger.getLogger("CONSOLE"); - - /** Holds the default identifying name of the test client. */ - public static final String CLIENT_NAME = "java"; - - /** Holds the URL of the broker to run the tests on. */ - public static String brokerUrl; - - /** Holds the virtual host to run the tests on. If null, then the default virtual host is used. */ - public static String virtualHost; - - /** - * Holds the test context properties that provides the default test parameters, plus command line overrides. - * This is initialized with the default test parameters, to which command line overrides may be applied. - */ - public static ParsedProperties testContextProperties = - TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); - - /** Holds all the test cases loaded from the classpath. */ - Map testCases = new HashMap(); - - /** Holds the test case currently being run by this client. */ - protected TestClientControlledTest currentTestCase; - - /** Holds the connection to the broker that the test is being coordinated on. */ - protected Connection connection; - - /** Holds the message producer to hold the test coordination over. */ - protected MessageProducer producer; - - /** Holds the JMS controlSession for the test coordination. */ - protected Session session; - - /** Holds the name of this client, with a default value. */ - protected String clientName = CLIENT_NAME; - - /** This flag indicates that the test client should attempt to join the currently running test case on start up. */ - protected boolean join; - - /** Holds the clock synchronizer for the test node. */ - ClockSynchThread clockSynchThread; - - /** - * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified client - * identifying name. - * - * @param brokerUrl The url of the broker to connect to. - * @param virtualHost The virtual host to conect to. - * @param clientName The client name to use. - * @param join Flag to indicate that this client should attempt to join running tests. - */ - public TestClient(String brokerUrl, String virtualHost, String clientName, boolean join) - { - log.debug("public TestClient(String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost - + ", String clientName = " + clientName + ", boolean join = " + join + "): called"); - - // Retain the connection parameters. - this.brokerUrl = brokerUrl; - this.virtualHost = virtualHost; - this.clientName = clientName; - this.join = join; - } - - /** - * The entry point for the interop test coordinator. This client accepts the following command line arguments: - * - *

- *
-b The broker URL. Optional. - *
-h The virtual host. Optional. - *
-n The test client name. Optional. - *
name=value Trailing argument define name/value pairs. Added to system properties. Optional. - *
- * - * @param args The command line arguments. - */ - public static void main(String[] args) - { - log.debug("public static void main(String[] args = " + Arrays.toString(args) + "): called"); - console.info("Qpid Distributed Test Client."); - - // Override the default broker url to be localhost:5672. - testContextProperties.setProperty(MessagingTestConfigProperties.BROKER_PROPNAME, "tcp://localhost:5672"); - - // Use the command line parser to evaluate the command line with standard handling behaviour (print errors - // and usage then exist if there are errors). - // Any options and trailing name=value pairs are also injected into the test context properties object, - // to override any defaults that may have been set up. - ParsedProperties options = - new ParsedProperties(uk.co.thebadgerset.junit.extensions.util.CommandLineParser.processCommandLine(args, - new uk.co.thebadgerset.junit.extensions.util.CommandLineParser( - new String[][] - { - { "b", "The broker URL.", "broker", "false" }, - { "h", "The virtual host to use.", "virtual host", "false" }, - { "o", "The name of the directory to output test timings to.", "dir", "false" }, - { "n", "The name of the test client.", "name", "false" }, - { "j", "Join this test client to running test.", "false" } - }), testContextProperties)); - - // Extract the command line options. - String brokerUrl = options.getProperty("b"); - String virtualHost = options.getProperty("h"); - String clientName = options.getProperty("n"); - clientName = (clientName == null) ? CLIENT_NAME : clientName; - boolean join = options.getPropertyAsBoolean("j"); - - // To distinguish logging output set up an NDC on the client name. - NDC.push(clientName); - - // Create a test client and start it running. - TestClient client = new TestClient(brokerUrl, virtualHost, clientName, join); - - // Use a class path scanner to find all the interop test case implementations. - // Hard code the test classes till the classpath scanner is fixed. - Collection> testCaseClasses = - new ArrayList>(); - // ClasspathScanner.getMatches(TestClientControlledTest.class, "^TestCase.*", true); - Collections.addAll(testCaseClasses, TestCase1DummyRun.class, TestCase2BasicP2P.class, TestCase3BasicPubSub.class, - TestCase4P2PMessageSize.class, TestCase5PubSubMessageSize.class, SustainedClientTestCase.class, - TestClientCircuitEnd.class); - - try - { - client.start(testCaseClasses); - } - catch (Exception e) - { - log.error("The test client was unable to start.", e); - console.info(e.getMessage()); - System.exit(1); - } - } - - /** - * Starts the interop test client running. This causes it to start listening for incoming test invites. - * - * @param testCaseClasses The classes of the available test cases. The test case names from these are used to - * matchin incoming test invites against. - * - * @throws JMSException Any underlying JMSExceptions are allowed to fall through. - */ - protected void start(Collection> testCaseClasses) throws JMSException - { - log.debug("protected void start(Collection> testCaseClasses = " - + testCaseClasses + "): called"); - - // Create all the test case implementations and index them by the test names. - for (Class nextClass : testCaseClasses) - { - try - { - TestClientControlledTest testCase = nextClass.newInstance(); - testCases.put(testCase.getName(), testCase); - } - catch (InstantiationException e) - { - log.warn("Could not instantiate test case class: " + nextClass.getName(), e); - // Ignored. - } - catch (IllegalAccessException e) - { - log.warn("Could not instantiate test case class due to illegal access: " + nextClass.getName(), e); - // Ignored. - } - } - - // Open a connection to communicate with the coordinator on. - connection = TestUtils.createConnection(testContextProperties); - session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - // Set this up to listen for control messages. - Topic privateControlTopic = session.createTopic("iop.control." + clientName); - MessageConsumer consumer = session.createConsumer(privateControlTopic); - consumer.setMessageListener(this); - - Topic controlTopic = session.createTopic("iop.control"); - MessageConsumer consumer2 = session.createConsumer(controlTopic); - consumer2.setMessageListener(this); - - // Create a producer to send replies with. - producer = session.createProducer(null); - - // If the join flag was set, then broadcast a join message to notify the coordinator that a new test client - // is available to join the current test case, if it supports it. This message may be ignored, or it may result - // in this test client receiving a test invite. - if (join) - { - Message joinMessage = session.createMessage(); - - joinMessage.setStringProperty("CONTROL_TYPE", "JOIN"); - joinMessage.setStringProperty("CLIENT_NAME", clientName); - joinMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); - producer.send(controlTopic, joinMessage); - } - - // Start listening for incoming control messages. - connection.start(); - } - - /** - * Handles all incoming control messages. - * - * @param message The incoming message. - */ - public void onMessage(Message message) - { - NDC.push(clientName); - log.debug("public void onMessage(Message message = " + message + "): called"); - - try - { - String controlType = message.getStringProperty("CONTROL_TYPE"); - String testName = message.getStringProperty("TEST_NAME"); - - log.debug("Received control of type '" + controlType + "' for the test '" + testName + "'"); - - // Check if the message is a test invite. - if ("INVITE".equals(controlType)) - { - // Flag used to indicate that an enlist should be sent. Only enlist to compulsory invites or invites - // for which test cases exist. - boolean enlist = false; - - if (testName != null) - { - log.debug("Got an invite to test: " + testName); - - // Check if the requested test case is available. - TestClientControlledTest testCase = testCases.get(testName); - - if (testCase != null) - { - log.debug("Found implementing class for test '" + testName + "', enlisting for it."); - - // Check if the test case will accept the invitation. - enlist = testCase.acceptInvite(message); - - log.debug("The test case " - + (enlist ? " accepted the invite, enlisting for it." - : " did not accept the invite, not enlisting.")); - - // Make the requested test case the current test case. - currentTestCase = testCase; - } - else - { - log.debug("Received an invite to the test '" + testName + "' but this test is not known."); - } - } - else - { - log.debug("Got a compulsory invite, enlisting for it."); - - enlist = true; - } - - if (enlist) - { - // Reply with the client name in an Enlist message. - Message enlistMessage = session.createMessage(); - enlistMessage.setStringProperty("CONTROL_TYPE", "ENLIST"); - enlistMessage.setStringProperty("CLIENT_NAME", clientName); - enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); - enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - log.debug("Sending enlist message '" + enlistMessage + "' to " + message.getJMSReplyTo()); - - producer.send(message.getJMSReplyTo(), enlistMessage); - } - else - { - // Reply with the client name in an Decline message. - Message enlistMessage = session.createMessage(); - enlistMessage.setStringProperty("CONTROL_TYPE", "DECLINE"); - enlistMessage.setStringProperty("CLIENT_NAME", clientName); - enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); - enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - log.debug("Sending decline message '" + enlistMessage + "' to " + message.getJMSReplyTo()); - - producer.send(message.getJMSReplyTo(), enlistMessage); - } - } - else if ("ASSIGN_ROLE".equals(controlType)) - { - // Assign the role to the current test case. - String roleName = message.getStringProperty("ROLE"); - - log.debug("Got a role assignment to role: " + roleName); - - TestClientControlledTest.Roles role = Enum.valueOf(TestClientControlledTest.Roles.class, roleName); - - currentTestCase.assignRole(role, message); - - // Reply by accepting the role in an Accept Role message. - Message acceptRoleMessage = session.createMessage(); - acceptRoleMessage.setStringProperty("CLIENT_NAME", clientName); - acceptRoleMessage.setStringProperty("CONTROL_TYPE", "ACCEPT_ROLE"); - acceptRoleMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - log.debug("Sending accept role message '" + acceptRoleMessage + "' to " + message.getJMSReplyTo()); - - producer.send(message.getJMSReplyTo(), acceptRoleMessage); - } - else if ("START".equals(controlType) || "STATUS_REQUEST".equals(controlType)) - { - if ("START".equals(controlType)) - { - log.debug("Got a start notification."); - - // Extract the number of test messages to send from the start notification. - int numMessages; - - try - { - numMessages = message.getIntProperty("MESSAGE_COUNT"); - } - catch (NumberFormatException e) - { - // If the number of messages is not specified, use the default of one. - numMessages = 1; - } - - // Start the current test case. - currentTestCase.start(numMessages); - } - else - { - log.debug("Got a status request."); - } - - // Generate the report from the test case and reply with it as a Report message. - Message reportMessage = currentTestCase.getReport(session); - reportMessage.setStringProperty("CLIENT_NAME", clientName); - reportMessage.setStringProperty("CONTROL_TYPE", "REPORT"); - reportMessage.setJMSCorrelationID(message.getJMSCorrelationID()); - - log.debug("Sending report message '" + reportMessage + "' to " + message.getJMSReplyTo()); - - producer.send(message.getJMSReplyTo(), reportMessage); - } - else if ("TERMINATE".equals(controlType)) - { - console.info("Received termination instruction from coordinator."); - - // Is a cleaner shutdown needed? - connection.close(); - System.exit(0); - } - else if ("CLOCK_SYNCH".equals(controlType)) - { - log.debug("Received clock synch command."); - String address = message.getStringProperty("ADDRESS"); - - log.debug("address = " + address); - - // Re-create (if necessary) and start the clock synch thread to synch the clock every ten seconds. - if (clockSynchThread != null) - { - clockSynchThread.terminate(); - } - - SleepThrottle throttle = new SleepThrottle(); - throttle.setRate(0.1f); - - clockSynchThread = new ClockSynchThread(new UDPClockSynchronizer(address), throttle); - clockSynchThread.start(); - } - else - { - // Log a warning about this but otherwise ignore it. - log.warn("Got an unknown control message, controlType = " + controlType + ", message = " + message); - } - } - catch (JMSException e) - { - // Log a warning about this, but otherwise ignore it. - log.warn("Got JMSException whilst handling message: " + message, e); - } - // Log any runtimes that fall through this message handler. These are fatal errors for the test client. - catch (RuntimeException e) - { - log.error("The test client message handler got an unhandled exception: ", e); - console.info("The message handler got an unhandled exception, terminating the test client."); - System.exit(1); - } - finally - { - NDC.pop(); - } - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/perftests/QpidTestThroughputPerf.java b/java/perftests/src/main/java/org/apache/qpid/perftests/QpidTestThroughputPerf.java deleted file mode 100644 index 760d1c84a4..0000000000 --- a/java/perftests/src/main/java/org/apache/qpid/perftests/QpidTestThroughputPerf.java +++ /dev/null @@ -1,170 +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. - * - */ -package org.apache.qpid.perftests; - -import junit.framework.Test; -import junit.framework.TestSuite; - -import org.apache.log4j.Logger; - -import org.apache.qpid.test.framework.Assertion; -import org.apache.qpid.test.framework.Circuit; -import org.apache.qpid.test.framework.FrameworkBaseCase; -import org.apache.qpid.test.framework.MessagingTestConfigProperties; -import org.apache.qpid.test.framework.sequencers.CircuitFactory; - -import uk.co.thebadgerset.junit.extensions.TestThreadAware; -import uk.co.thebadgerset.junit.extensions.TimingController; -import uk.co.thebadgerset.junit.extensions.TimingControllerAware; -import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; -import uk.co.thebadgerset.junit.extensions.util.TestContextProperties; - -import java.util.LinkedList; - -/** - * QpidTestThroughputPerf runs a test over a {@link Circuit} controlled by the test parameters. It logs timings of - * the time required to receive samples consisting of batches of messages. As the time samples is taken over a reasonable - * sized message batch, it measures message throughput. - * - *

- *
CRC Card
Responsibilities Collaborations - *
- *
- * - * @todo Check that all of the messages were sent. Check that the receiving end got the same number of messages as - * the publishing end. - */ -public class QpidTestThroughputPerf extends FrameworkBaseCase implements TimingControllerAware, TestThreadAware -{ - /** Used for debugging. */ - private static final Logger log = Logger.getLogger(QpidTestThroughputPerf.class); - - /** Holds the timing controller, used to log test timings from self-timed tests. */ - private TimingController timingController; - - /** Thread local to hold the per-thread test setup fields. */ - ThreadLocal threadSetup = new ThreadLocal(); - - /** - * Creates a new test case with the specified name. - * - * @param name The test case name. - */ - public QpidTestThroughputPerf(String name) - { - super(name); - } - - /** - * Performs the a basic P2P test case. - * - * @param numMessages The number of messages to send in the test. - */ - public void testThroughput(int numMessages) - { - log.debug("public void testThroughput(): called"); - - PerThreadSetup setup = threadSetup.get(); - assertNoFailures(setup.testCircuit.test(numMessages, new LinkedList())); - } - - /** - * Should provide a translation from the junit method name of a test to its test case name as known to the test - * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test - * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case - * name "TC2_BasicP2P". - * - * @param methodName The name of the JUnit test method. - * - * @return The name of the corresponding interop test case. - */ - public String getTestCaseNameForTestMethod(String methodName) - { - log.debug("public String getTestCaseNameForTestMethod(String methodName = " + methodName + "): called"); - - return "DEFAULT_CIRCUIT_TEST"; - } - - /** - * Used by test runners that can supply a {@link uk.co.thebadgerset.junit.extensions.TimingController} to set the - * controller on an aware test. - * - * @param controller The timing controller. - */ - public void setTimingController(TimingController controller) - { - timingController = controller; - } - - /** - * Performs test fixture creation on a per thread basis. This will only be called once for each test thread. - */ - public void threadSetUp() - { - // Get the test parameters, any overrides on the command line will have been applied. - ParsedProperties testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); - - // Customize the test parameters. - testProps.setProperty("TEST_NAME", "DEFAULT_CIRCUIT_TEST"); - testProps.setProperty(MessagingTestConfigProperties.SEND_DESTINATION_NAME_ROOT_PROPNAME, "testqueue"); - - // Get the test circuit factory to create test circuits and run the standard test procedure through. - CircuitFactory circuitFactory = getCircuitFactory(); - - // Create the test circuit. This projects the circuit onto the available test nodes and connects it up. - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - // Store the test configuration for the thread. - PerThreadSetup setup = new PerThreadSetup(); - setup.testCircuit = testCircuit; - threadSetup.set(setup); - } - - /** - * Called when a test thread is destroyed. - */ - public void threadTearDown() - { } - - /** - * Holds the per-thread test configurations. - */ - protected static class PerThreadSetup - { - /** Holds the test circuit to run tests on. */ - Circuit testCircuit; - } - - /** - * Compiles all the tests in this class into a suite. - * - * @return The test suite. - */ - public static Test suite() - { - // Build a new test suite - TestSuite suite = new TestSuite("Qpid Throughput Performance Tests"); - - suite.addTest(new QpidTestThroughputPerf("testThroughput")); - - return suite; - } -} diff --git a/java/perftests/src/main/java/org/apache/qpid/test/testcases/MessageThroughputPerf.java b/java/perftests/src/main/java/org/apache/qpid/test/testcases/MessageThroughputPerf.java new file mode 100644 index 0000000000..887479c032 --- /dev/null +++ b/java/perftests/src/main/java/org/apache/qpid/test/testcases/MessageThroughputPerf.java @@ -0,0 +1,172 @@ +/* + * + * 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. + * + */ +package org.apache.qpid.test.testcases; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.Assertion; +import org.apache.qpid.test.framework.Circuit; +import org.apache.qpid.test.framework.FrameworkBaseCase; +import org.apache.qpid.test.framework.MessagingTestConfigProperties; +import org.apache.qpid.test.framework.sequencers.CircuitFactory; + +import uk.co.thebadgerset.junit.extensions.TestThreadAware; +import uk.co.thebadgerset.junit.extensions.TimingController; +import uk.co.thebadgerset.junit.extensions.TimingControllerAware; +import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; +import uk.co.thebadgerset.junit.extensions.util.TestContextProperties; + +import java.util.LinkedList; + +/** + * MessageThroughputPerf runs a test over a {@link Circuit} controlled by the test parameters. It logs timings of + * the time required to receive samples consisting of batches of messages. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
+ *
+ * + * @todo Check that all of the messages were sent. Check that the receiving end got the same number of messages as + * the publishing end. + * + * @todo Set this up to run with zero sized tests. Size zero means send forever. Continuous sending to be interrupted + * by completion of the test duration, or shutdown hook when the user presses Ctrl-C. + */ +public class MessageThroughputPerf extends FrameworkBaseCase implements TimingControllerAware, TestThreadAware +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(MessageThroughputPerf.class); + + /** Holds the timing controller, used to log test timings from self-timed tests. */ + private TimingController timingController; + + /** Thread local to hold the per-thread test setup fields. */ + ThreadLocal threadSetup = new ThreadLocal(); + + /** + * Creates a new test case with the specified name. + * + * @param name The test case name. + */ + public MessageThroughputPerf(String name) + { + super(name); + } + + /** + * Performs the a basic P2P test case. + * + * @param numMessages The number of messages to send in the test. + */ + public void testThroughput(int numMessages) + { + log.debug("public void testThroughput(): called"); + + PerThreadSetup setup = threadSetup.get(); + assertNoFailures(setup.testCircuit.test(numMessages, new LinkedList())); + } + + /** + * Should provide a translation from the junit method name of a test to its test case name as known to the test + * clients that will run the test. The purpose of this is to convert the JUnit method name into the correct test + * case name to place into the test invite. For example the method "testP2P" might map onto the interop test case + * name "TC2_BasicP2P". + * + * @param methodName The name of the JUnit test method. + * + * @return The name of the corresponding interop test case. + */ + public String getTestCaseNameForTestMethod(String methodName) + { + log.debug("public String getTestCaseNameForTestMethod(String methodName = " + methodName + "): called"); + + return "DEFAULT_CIRCUIT_TEST"; + } + + /** + * Used by test runners that can supply a {@link uk.co.thebadgerset.junit.extensions.TimingController} to set the + * controller on an aware test. + * + * @param controller The timing controller. + */ + public void setTimingController(TimingController controller) + { + timingController = controller; + } + + /** + * Performs test fixture creation on a per thread basis. This will only be called once for each test thread. + */ + public void threadSetUp() + { + // Get the test parameters, any overrides on the command line will have been applied. + ParsedProperties testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); + + // Customize the test parameters. + testProps.setProperty("TEST_NAME", "DEFAULT_CIRCUIT_TEST"); + testProps.setProperty(MessagingTestConfigProperties.SEND_DESTINATION_NAME_ROOT_PROPNAME, "testqueue"); + + // Get the test circuit factory to create test circuits and run the standard test procedure through. + CircuitFactory circuitFactory = getCircuitFactory(); + + // Create the test circuit. This projects the circuit onto the available test nodes and connects it up. + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Store the test configuration for the thread. + PerThreadSetup setup = new PerThreadSetup(); + setup.testCircuit = testCircuit; + threadSetup.set(setup); + } + + /** + * Called when a test thread is destroyed. + */ + public void threadTearDown() + { } + + /** + * Holds the per-thread test configurations. + */ + protected static class PerThreadSetup + { + /** Holds the test circuit to run tests on. */ + Circuit testCircuit; + } + + /** + * Compiles all the tests in this class into a suite. + * + * @return The test suite. + */ + public static Test suite() + { + // Build a new test suite + TestSuite suite = new TestSuite("Qpid Throughput Performance Tests"); + + suite.addTest(new MessageThroughputPerf("testThroughput")); + + return suite; + } +} diff --git a/java/pom.xml b/java/pom.xml index 1abe0c6836..b80dca00d8 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -398,7 +398,7 @@ under the License. uk.co.thebadgerset junit-toolkit-maven-plugin - 0.6.1 + 0.7-SNAPSHOT @@ -546,7 +546,7 @@ under the License. uk.co.thebadgerset junit-toolkit - 0.6.1 + 0.7-SNAPSHOT compile @@ -690,6 +690,14 @@ under the License. false + + + junittoolkit.snapshots + http://junit-toolkit.svn.sourceforge.net/svnroot/junit-toolkit/snapshots/ + + true + + @@ -711,6 +719,14 @@ under the License. + + junittoolkit.plugin.snapshots + http://junit-toolkit.svn.sourceforge.net/svnroot/junit-toolkit/snapshots/ + + true + + + diff --git a/java/systests/pom.xml b/java/systests/pom.xml index 4b3dc40517..8f963d9b91 100644 --- a/java/systests/pom.xml +++ b/java/systests/pom.xml @@ -82,6 +82,11 @@ **/*Test.class + + **/testcases/ImmediateMessageTest.class + **/testcases/MandatoryMessageTest.class + **/testcases/RollbackTest.class + diff --git a/java/systests/src/main/java/org/apache/qpid/server/exchange/ImmediateMessageTest.java b/java/systests/src/main/java/org/apache/qpid/server/exchange/ImmediateMessageTest.java deleted file mode 100644 index 9b5c9c3d00..0000000000 --- a/java/systests/src/main/java/org/apache/qpid/server/exchange/ImmediateMessageTest.java +++ /dev/null @@ -1,296 +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. - * - */ -package org.apache.qpid.server.exchange; - -import org.apache.qpid.test.framework.Circuit; -import org.apache.qpid.test.framework.FrameworkBaseCase; -import org.apache.qpid.test.framework.MessagingTestConfigProperties; -import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*; -import org.apache.qpid.test.framework.sequencers.CircuitFactory; - -import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; -import uk.co.thebadgerset.junit.extensions.util.TestContextProperties; - -/** - * ImmediateMessageTest tests for the desired behaviour of immediate messages. Immediate messages are a non-JMS - * feature. A message may be marked with an immediate delivery flag, which means that a consumer must be connected - * to receive the message, through a valid route, when it is sent, or when its transaction is committed in the case - * of transactional messaging. If this is not the case, the broker should return the message with a NO_CONSUMERS code. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Check that an immediate message is sent succesfully not using transactions when a consumer is connected. - *
Check that an immediate message is committed succesfully in a transaction when a consumer is connected. - *
Check that an immediate message results in no consumers code, not using transactions, when a consumer is - * disconnected. - *
Check that an immediate message results in no consumers code, in a transaction, when a consumer is - * disconnected. - *
Check that an immediate message results in no route code, not using transactions, when no outgoing route is - * connected. - *
Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is - * connected. - *
Check that an immediate message is sent succesfully not using transactions when a consumer is connected. - *
Check that an immediate message is committed succesfully in a transaction when a consumer is connected. - *
Check that an immediate message results in no consumers code, not using transactions, when a consumer is - * disconnected. - *
Check that an immediate message results in no consumers code, in a transaction, when a consumer is - * disconnected. - *
Check that an immediate message results in no route code, not using transactions, when no outgoing route is - * connected. - *
Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is - * connected. - *
- * - * @todo All of these test cases will be generated by a test generator that thoroughly tests all combinations of test - * circuits. - */ -public class ImmediateMessageTest extends FrameworkBaseCase -{ - /** Used to read the tests configurable properties through. */ - ParsedProperties testProps; - - /** - * Creates a new test case with the specified name. - * - * @param name The test case name. - */ - public ImmediateMessageTest(String name) - { - super(name); - } - - /** Check that an immediate message is sent succesfully not using transactions when a consumer is connected. */ - public void test_QPID_517_ImmediateOkNoTxP2P() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Run the default test sequence over the test circuit checking for no errors. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion()))); - } - - /** Check that an immediate message is committed succesfully in a transaction when a consumer is connected. */ - public void test_QPID_517_ImmediateOkTxP2P() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Send one message with no errors. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion()))); - } - - /** Check that an immediate message results in no consumers code, not using transactions, when a consumer is disconnected. */ - public void test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxP2P() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Disconnect the consumer. - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); - - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - // Send one message and get a linked no consumers exception. - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noConsumersAssertion()))); - } - - /** Check that an immediate message results in no consumers code, in a transaction, when a consumer is disconnected. */ - public void test_QPID_517_ImmediateFailsConsumerDisconnectedTxP2P() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Disconnect the consumer. - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); - - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - // Send one message and get a linked no consumers exception. - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noConsumersAssertion()))); - } - - /** Check that an immediate message results in no route code, not using transactions, when no outgoing route is connected. */ - public void test_QPID_517_ImmediateFailsNoRouteNoTxP2P() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to - // collect its messages). - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); - - // Send one message and get a linked no route exception. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noRouteAssertion()))); - } - - /** Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is connected. */ - public void test_QPID_517_ImmediateFailsNoRouteTxP2P() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to - // collect its messages). - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); - - // Send one message and get a linked no route exception. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noRouteAssertion()))); - } - - /** Check that an immediate message is sent succesfully not using transactions when a consumer is connected. */ - public void test_QPID_517_ImmediateOkNoTxPubSub() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Send one message with no errors. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion()))); - } - - /** Check that an immediate message is committed succesfully in a transaction when a consumer is connected. */ - public void test_QPID_517_ImmediateOkTxPubSub() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Send one message with no errors. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion()))); - } - - /** Check that an immediate message results in no consumers code, not using transactions, when a consumer is disconnected. */ - public void test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxPubSub() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Use durable subscriptions, so that the route remains open with no subscribers. - testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true); - - // Disconnect the consumer. - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); - - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - // Send one message and get a linked no consumers exception. - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noConsumersAssertion()))); - } - - /** Check that an immediate message results in no consumers code, in a transaction, when a consumer is disconnected. */ - public void test_QPID_517_ImmediateFailsConsumerDisconnectedTxPubSub() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Use durable subscriptions, so that the route remains open with no subscribers. - testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true); - - // Disconnect the consumer. - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); - - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - // Send one message and get a linked no consumers exception. - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noConsumersAssertion()))); - } - - /** Check that an immediate message results in no route code, not using transactions, when no outgoing route is connected. */ - public void test_QPID_517_ImmediateFailsNoRouteNoTxPubSub() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to - // collect its messages). - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); - - // Send one message and get a linked no route exception. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noRouteAssertion()))); - } - - /** Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is connected. */ - public void test_QPID_517_ImmediateFailsNoRouteTxPubSub() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to - // collect its messages). - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); - - // Send one message and get a linked no route exception. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noRouteAssertion()))); - } - - protected void setUp() throws Exception - { - super.setUp(); - - testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); - - /** All these tests should have the immediate flag on. */ - testProps.setProperty(IMMEDIATE_PROPNAME, true); - testProps.setProperty(MANDATORY_PROPNAME, false); - - /** Bind the receivers consumer by default. */ - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, true); - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, true); - } -} diff --git a/java/systests/src/main/java/org/apache/qpid/server/exchange/MandatoryMessageTest.java b/java/systests/src/main/java/org/apache/qpid/server/exchange/MandatoryMessageTest.java deleted file mode 100644 index df99d044d2..0000000000 --- a/java/systests/src/main/java/org/apache/qpid/server/exchange/MandatoryMessageTest.java +++ /dev/null @@ -1,308 +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. - * - */ -package org.apache.qpid.server.exchange; - -import org.apache.qpid.test.framework.Circuit; -import org.apache.qpid.test.framework.FrameworkBaseCase; -import org.apache.qpid.test.framework.MessagingTestConfigProperties; -import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*; -import org.apache.qpid.test.framework.sequencers.CircuitFactory; - -import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; -import uk.co.thebadgerset.junit.extensions.util.TestContextProperties; - -/** - * MandatoryMessageTest tests for the desired behaviour of mandatory messages. Mandatory messages are a non-JMS - * feature. A message may be marked with a mandatory delivery flag, which means that a valid route for the message - * must exist, when it is sent, or when its transaction is committed in the case of transactional messaging. If this - * is not the case, the broker should return the message with a NO_CONSUMERS code. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. - *
Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. - *
Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected - * but the route exists. - *
Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but - * the route exists. - *
Check that an mandatory message results in no route code, not using transactions, when no consumer is - * connected. - *
Check that an mandatory message results in no route code, upon transaction commit, when a consumer is - * connected. - *
Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. - *
Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. - *
Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected - * but the route exists. - *
Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but - * the route exists. - *
Check that an mandatory message results in no route code, not using transactions, when no consumer is - * connected. - *
Check that an mandatory message results in no route code, upon transaction commit, when a consumer is - * connected. - *
- * - * @todo All of these test cases will be generated by a test generator that thoroughly tests all combinations of test - * circuits. - */ -public class MandatoryMessageTest extends FrameworkBaseCase -{ - /** Used to read the tests configurable properties through. */ - ParsedProperties testProps; - - /** - * Creates a new test case with the specified name. - * - * @param name The test case name. - */ - public MandatoryMessageTest(String name) - { - super(name); - } - - /** Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. */ - public void test_QPID_508_MandatoryOkNoTxP2P() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Run the default test sequence over the test circuit checking for no errors. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion()))); - } - - /** Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. */ - public void test_QPID_508_MandatoryOkTxP2P() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Run the default test sequence over the test circuit checking for no errors. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion()))); - } - - /** - * Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected but - * the route exists. - */ - public void test_QPID_517_MandatoryOkConsumerDisconnectedNoTxP2P() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Disconnect the consumer. - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); - - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - // Send one message with no errors. - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion()))); - } - - /** - * Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but - * the route exists. - */ - public void test_QPID_517_MandatoryOkConsumerDisconnectedTxP2P() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Disconnect the consumer. - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); - - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - // Send one message with no errors. - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion()))); - } - - /** Check that an mandatory message results in no route code, not using transactions, when no consumer is connected. */ - public void test_QPID_508_MandatoryFailsNoRouteNoTxP2P() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to - // collect its messages). - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); - - // Send one message and get a linked no route exception. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noRouteAssertion()))); - } - - /** Check that an mandatory message results in no route code, upon transaction commit, when a consumer is connected. */ - public void test_QPID_508_MandatoryFailsNoRouteTxP2P() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, false); - - // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to - // collect its messages). - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); - - // Send one message and get a linked no route exception. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noRouteAssertion()))); - } - - /** Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. */ - public void test_QPID_508_MandatoryOkNoTxPubSub() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Run the default test sequence over the test circuit checking for no errors. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion()))); - } - - /** Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. */ - public void test_QPID_508_MandatoryOkTxPubSub() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Run the default test sequence over the test circuit checking for no errors. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion()))); - } - - /** - * Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected but - * the route exists. - */ - public void test_QPID_517_MandatoryOkConsumerDisconnectedNoTxPubSub() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Use durable subscriptions, so that the route remains open with no subscribers. - testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true); - - // Disconnect the consumer. - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); - - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - // Send one message with no errors. - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion()))); - } - - /** - * Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but - * the route exists. - */ - public void test_QPID_517_MandatoryOkConsumerDisconnectedTxPubSub() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Use durable subscriptions, so that the route remains open with no subscribers. - testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true); - - // Disconnect the consumer. - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); - - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - // Send one message with no errors. - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion()))); - } - - /** Check that an mandatory message results in no route code, not using transactions, when no consumer is connected. */ - public void test_QPID_508_MandatoryFailsNoRouteNoTxPubSub() - { - // Ensure transactional sessions are off. - testProps.setProperty(TRANSACTED_PROPNAME, false); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to - // collect its messages). - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); - - // Send one message and get a linked no route exception. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noRouteAssertion()))); - } - - /** Check that an mandatory message results in no route code, upon transaction commit, when a consumer is connected. */ - public void test_QPID_508_MandatoryFailsNoRouteTxPubSub() - { - // Ensure transactional sessions are on. - testProps.setProperty(TRANSACTED_PROPNAME, true); - testProps.setProperty(PUBSUB_PROPNAME, true); - - // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to - // collect its messages). - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); - - // Send one message and get a linked no route exception. - CircuitFactory circuitFactory = getCircuitFactory(); - Circuit testCircuit = circuitFactory.createCircuit(testProps); - - assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noRouteAssertion()))); - } - - protected void setUp() throws Exception - { - super.setUp(); - - testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); - - /** All these tests should have the mandatory flag on. */ - testProps.setProperty(IMMEDIATE_PROPNAME, false); - testProps.setProperty(MANDATORY_PROPNAME, true); - - /** Bind the receivers consumer by default. */ - testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, true); - testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, true); - } -} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/AMQPPublisher.java b/java/systests/src/main/java/org/apache/qpid/test/framework/AMQPPublisher.java new file mode 100644 index 0000000000..a8f79ad59d --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/AMQPPublisher.java @@ -0,0 +1,54 @@ +/* + * + * 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. + * + */ +package org.apache.qpid.test.framework; + +import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; + +/** + * An AMQPPublisher represents the status of the publishing side of a test circuit that exposes AMQP specific features. + * Its provides additional assertions not available through the plain JMS {@link Publisher} interface. + * + *

+ *
CRC Card
Responsibilities + *
Provide assertion that the publishers received a no consumers error code on every message. + *
Provide assertion that the publishers received a no route error code on every message. + *
+ */ +public interface AMQPPublisher extends Publisher +{ + /** + * Provides an assertion that the publisher got a no consumers exception on every message. + * + * @param testProps The test configuration properties. + * + * @return An assertion that the publisher got a no consumers exception on every message. + */ + Assertion noConsumersAssertion(ParsedProperties testProps); + + /** + * Provides an assertion that the publisher got a no rout exception on every message. + * + * @param testProps The test configuration properties. + * + * @return An assertion that the publisher got a no rout exception on every message. + */ + Assertion noRouteAssertion(ParsedProperties testProps); +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/CircuitEndBase.java b/java/systests/src/main/java/org/apache/qpid/test/framework/CircuitEndBase.java index 251b216c45..b11650021c 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/CircuitEndBase.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/CircuitEndBase.java @@ -51,11 +51,14 @@ public class CircuitEndBase implements CircuitEnd ExceptionMonitor exceptionMonitor; /** - * Creates a circuit end point on the specified producer, consumer and controlSession. + * Creates a circuit end point on the specified producer, consumer and controlSession. Monitors are also configured + * for messages and exceptions received by the circuit end. * - * @param producer The message producer for the circuit end point. - * @param consumer The message consumer for the circuit end point. - * @param session The controlSession for the circuit end point. + * @param producer The message producer for the circuit end point. + * @param consumer The message consumer for the circuit end point. + * @param session The controlSession for the circuit end point. + * @param messageMonitor The monitor to notify of all messages received by the circuit end. + * @param exceptionMonitor The monitor to notify of all exceptions received by the circuit end. */ public CircuitEndBase(MessageProducer producer, MessageConsumer consumer, Session session, MessageMonitor messageMonitor, ExceptionMonitor exceptionMonitor) diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/ExceptionMonitor.java b/java/systests/src/main/java/org/apache/qpid/test/framework/ExceptionMonitor.java index 9f1207202e..e8f5f7c53b 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/ExceptionMonitor.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/ExceptionMonitor.java @@ -36,7 +36,7 @@ import java.util.List; * *

*
CRC Card
Responsibilities Collaborations - *
Record all exceptions received. {@link ExceptionListener} + *
Record all exceptions received. *
*/ public class ExceptionMonitor implements ExceptionListener @@ -45,7 +45,7 @@ public class ExceptionMonitor implements ExceptionListener private final Logger log = Logger.getLogger(ExceptionMonitor.class); /** Holds the received exceptions. */ - List exceptions = new ArrayList(); + List exceptions = new ArrayList(); /** * Receives incoming exceptions. @@ -82,6 +82,8 @@ public class ExceptionMonitor implements ExceptionListener /** * Checks that exactly one exception, with a linked cause of the specified type, has been received. * + * @param aClass The type of the linked cause. + * * @return true if exactly one exception, with a linked cause of the specified type, been received, * false otherwise. */ @@ -89,19 +91,50 @@ public class ExceptionMonitor implements ExceptionListener { if (exceptions.size() == 1) { - JMSException e = exceptions.get(0); - - Exception linkedCause = e.getLinkedException(); + Exception e = exceptions.get(0); - if ((linkedCause != null) && aClass.isInstance(linkedCause)) + if (e instanceof JMSException) { - return true; + JMSException jmse = (JMSException) e; + + Exception linkedCause = jmse.getLinkedException(); + + if ((linkedCause != null) && aClass.isInstance(linkedCause)) + { + return true; + } } } return false; } + /** + * Checks that at least one exception of the the specified type, has been received. + * + * @param exceptionClass The type of the exception. + * + * @return true if at least one exception of the specified type has been received, false otherwise. + */ + public synchronized boolean assertExceptionOfType(Class exceptionClass) + { + // Start by assuming that the exception has no been received. + boolean passed = false; + + // Scan all the exceptions for a match. + for (Exception e : exceptions) + { + if (exceptionClass.isInstance(e)) + { + passed = true; + + break; + } + } + + return passed; + } + /** * Reports the number of exceptions held by this monitor. * @@ -117,7 +150,7 @@ public class ExceptionMonitor implements ExceptionListener */ public synchronized void reset() { - exceptions = new ArrayList(); + exceptions = new ArrayList(); } /** @@ -130,7 +163,7 @@ public class ExceptionMonitor implements ExceptionListener { String result = "ExceptionMonitor: holds " + exceptions.size() + " exceptions.\n\n"; - for (JMSException ex : exceptions) + for (Exception ex : exceptions) { result += getStackTrace(ex) + "\n"; } diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkBaseCase.java b/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkBaseCase.java index 77bb7a5c52..3e80d48eb3 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkBaseCase.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/FrameworkBaseCase.java @@ -20,23 +20,15 @@ */ package org.apache.qpid.test.framework; -import junit.framework.TestCase; - import org.apache.log4j.Logger; import org.apache.log4j.NDC; -import org.apache.qpid.client.transport.TransportConnection; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.test.framework.localcircuit.LocalCircuitImpl; import org.apache.qpid.test.framework.sequencers.CircuitFactory; -import org.apache.qpid.util.ConversationFactory; import uk.co.thebadgerset.junit.extensions.AsymptoticTestCase; -import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; import java.util.ArrayList; import java.util.List; -import java.util.Properties; /** * FrameworkBaseCase provides a starting point for writing test cases against the test framework. Its main purpose is @@ -56,7 +48,7 @@ public class FrameworkBaseCase extends AsymptoticTestCase private static final Logger log = Logger.getLogger(FrameworkBaseCase.class); /** Holds the test sequencer to create and run test circuits with. */ - protected CircuitFactory circuitFactory = new DefaultCircuitFactory(); + protected CircuitFactory circuitFactory = new LocalCircuitFactory(); /** * Creates a new test case with the specified name. @@ -116,7 +108,7 @@ public class FrameworkBaseCase extends AsymptoticTestCase * * @param asserts The list of failed assertions. */ - protected void assertNoFailures(List asserts) + protected static void assertNoFailures(List asserts) { log.debug("protected void assertNoFailures(List asserts = " + asserts + "): called"); @@ -140,7 +132,7 @@ public class FrameworkBaseCase extends AsymptoticTestCase * * @return The error message. */ - protected String assertionsToString(List asserts) + protected static String assertionsToString(List asserts) { String errorMessage = ""; @@ -160,9 +152,6 @@ public class FrameworkBaseCase extends AsymptoticTestCase protected void setUp() throws Exception { NDC.push(getName()); - - // Ensure that the in-vm broker is created. - TransportConnection.createVMBroker(1); } /** @@ -170,16 +159,7 @@ public class FrameworkBaseCase extends AsymptoticTestCase */ protected void tearDown() { - try - { - // Ensure that the in-vm broker is cleaned up so that the next test starts afresh. - TransportConnection.killVMBroker(1); - ApplicationRegistry.remove(1); - } - finally - { - NDC.pop(); - } + NDC.pop(); } /** @@ -196,85 +176,4 @@ public class FrameworkBaseCase extends AsymptoticTestCase { return methodName; } - - /** - * DefaultCircuitFactory is a test sequencer that creates test circuits with publishing and receiving ends rooted - * on the same JVM. - */ - public class DefaultCircuitFactory implements CircuitFactory - { - /** - * Holds a test coordinating conversation with the test clients. This should consist of assigning the test roles, - * begining the test and gathering the test reports from the participants. - * - * @param testCircuit The test circuit. - * @param assertions The list of assertions to apply to the test circuit. - * @param testProperties The test case definition. - */ - public void sequenceTest(Circuit testCircuit, List assertions, Properties testProperties) - { - assertNoFailures(testCircuit.test(1, assertions)); - } - - /** - * Creates a test circuit for the test, configered by the test parameters specified. - * - * @param testProperties The test parameters. - * @return A test circuit. - */ - public Circuit createCircuit(ParsedProperties testProperties) - { - return LocalCircuitImpl.createCircuit(testProperties); - } - - /** - * Sets the sender test client to coordinate the test with. - * - * @param sender The contact details of the sending client in the test. - */ - public void setSender(TestClientDetails sender) - { - throw new RuntimeException("Not implemented."); - } - - /** - * Sets the receiving test client to coordinate the test with. - * - * @param receiver The contact details of the sending client in the test. - */ - public void setReceiver(TestClientDetails receiver) - { - throw new RuntimeException("Not implemented."); - } - - /** - * Supplies the sending test client. - * - * @return The sending test client. - */ - public TestClientDetails getSender() - { - throw new RuntimeException("Not implemented."); - } - - /** - * Supplies the receiving test client. - * - * @return The receiving test client. - */ - public List getReceivers() - { - throw new RuntimeException("Not implemented."); - } - - /** - * Accepts the conversation factory over which to hold the test coordinating conversation. - * - * @param conversationFactory The conversation factory to coordinate the test over. - */ - public void setConversationFactory(ConversationFactory conversationFactory) - { - throw new RuntimeException("Not implemented."); - } - } } diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/LocalAMQPCircuitFactory.java b/java/systests/src/main/java/org/apache/qpid/test/framework/LocalAMQPCircuitFactory.java new file mode 100644 index 0000000000..dac826fd5e --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/LocalAMQPCircuitFactory.java @@ -0,0 +1,168 @@ +/* + * + * 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. + * + */ +package org.apache.qpid.test.framework; + +import org.apache.log4j.Logger; + +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.test.framework.localcircuit.LocalAMQPPublisherImpl; +import org.apache.qpid.test.framework.localcircuit.LocalPublisherImpl; + +import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; + +import javax.jms.*; + +/** + * LocalAMQPCircuitFactory is a test sequencer that creates test circuits with publishing and receiving ends rooted + * on the same JVM, allowing AMQP/Qpid specific options to be applied to the circuit. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Provide a standard test procedure over a test circuit. + *
Construct test circuits appropriate to a tests context. + *
Construct test circuits the support AMQP specific options. + *
+ */ +public class LocalAMQPCircuitFactory extends LocalCircuitFactory +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(LocalAMQPCircuitFactory.class); + + /** + * Builds a circuit end suitable for the publishing side of a test circuit, from standard test parameters. + * + * @param connection The connection to build the circuit end on. + * @param testProps The test parameters to configure the circuit end construction. + * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique. + * + * @return A circuit end suitable for the publishing side of a test circuit. + * + * @throws javax.jms.JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation. + */ + public CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId) + throws JMSException + { + log.debug( + "public CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = " + + uniqueId + "): called"); + + // Cast the test properties into a typed interface for convenience. + MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); + + Session session = connection.createSession(props.getPublisherTransacted(), props.getAckMode()); + + Destination destination = + props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId) + : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId); + + MessageProducer producer = + props.getPublisherProducerBind() + ? ((props.getImmediate() | props.getMandatory()) + ? ((AMQSession) session).createProducer(destination, props.getMandatory(), props.getImmediate()) + : session.createProducer(destination)) : null; + + MessageConsumer consumer = + props.getPublisherConsumerBind() + ? session.createConsumer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null; + + MessageMonitor messageMonitor = new MessageMonitor(); + + if (consumer != null) + { + consumer.setMessageListener(messageMonitor); + } + + ExceptionMonitor exceptionMonitor = new ExceptionMonitor(); + connection.setExceptionListener(exceptionMonitor); + + if (!props.getPublisherConsumerActive() && (consumer != null)) + { + consumer.close(); + } + + return new CircuitEndBase(producer, consumer, session, messageMonitor, exceptionMonitor); + } + + /** + * Builds a circuit end suitable for the receiving side of a test circuit, from standard test parameters. + * + * @param connection The connection to build the circuit end on. + * @param testProps The test parameters to configure the circuit end construction. + * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique. + * + * @return A circuit end suitable for the receiving side of a test circuit. + * + * @throws JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation. + */ + public CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId) + throws JMSException + { + log.debug( + "public CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = " + + uniqueId + "): called"); + + // Cast the test properties into a typed interface for convenience. + MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); + + Session session = connection.createSession(props.getPublisherTransacted(), props.getAckMode()); + + MessageProducer producer = + props.getReceiverProducerBind() + ? session.createProducer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null; + + Destination destination = + props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId) + : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId); + + MessageConsumer consumer = + props.getReceiverConsumerBind() + ? ((props.getDurableSubscription() && props.getPubsub()) + ? session.createDurableSubscriber((Topic) destination, "testsub") : session.createConsumer(destination)) + : null; + + MessageMonitor messageMonitor = new MessageMonitor(); + + if (consumer != null) + { + consumer.setMessageListener(messageMonitor); + } + + if (!props.getReceiverConsumerActive() && (consumer != null)) + { + consumer.close(); + } + + return new CircuitEndBase(producer, consumer, session, messageMonitor, null); + } + + /** + * Creates a local {@link Publisher} from a {@link CircuitEnd}. The publisher implementation provides AMQP + * specific assertion methods, for testing beyond JMS. + * + * @param publisherEnd The publishing circuit end. + * + * @return A {@link Receiver}. + */ + protected LocalPublisherImpl createPublisherFromCircuitEnd(CircuitEndBase publisherEnd) + { + return new LocalAMQPPublisherImpl(publisherEnd); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/LocalCircuitFactory.java b/java/systests/src/main/java/org/apache/qpid/test/framework/LocalCircuitFactory.java new file mode 100644 index 0000000000..0744522535 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/LocalCircuitFactory.java @@ -0,0 +1,315 @@ +/* + * + * 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. + * + */ +package org.apache.qpid.test.framework; + +import org.apache.log4j.Logger; + +import org.apache.qpid.test.framework.localcircuit.LocalCircuitImpl; +import org.apache.qpid.test.framework.localcircuit.LocalPublisherImpl; +import org.apache.qpid.test.framework.localcircuit.LocalReceiverImpl; +import org.apache.qpid.test.framework.sequencers.CircuitFactory; +import org.apache.qpid.util.ConversationFactory; + +import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; + +import javax.jms.*; + +import java.util.List; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicLong; + +/** + * LocalCircuitFactory is a circuit factory that creates test circuits with publishing and receiving ends rooted + * on the same JVM. The ends of the circuit are presented as {@link Publisher} and {@link Receiver} interfaces, which + * in turn provide methods to apply assertions to the circuit. The creation of the circuit ends, and the presentation + * of the ends as publisher/receiver interfaces, are designed to be overriden, so that circuits and assertions that + * use messaging features not available in JMS can be written. This provides an extension point for writing tests + * against proprietary features of JMS implementations. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Provide a standard test procedure over a test circuit. + *
Construct test circuits appropriate to a tests context. + *
+ */ +public class LocalCircuitFactory implements CircuitFactory +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(LocalCircuitFactory.class); + + /** Used to create unique destination names for each test. */ + protected static AtomicLong uniqueDestsId = new AtomicLong(); + + /** + * Holds a test coordinating conversation with the test clients. This should consist of assigning the test roles, + * begining the test and gathering the test reports from the participants. + * + * @param testCircuit The test circuit. + * @param assertions The list of assertions to apply to the test circuit. + * @param testProperties The test case definition. + */ + public void sequenceTest(Circuit testCircuit, List assertions, Properties testProperties) + { + FrameworkBaseCase.assertNoFailures(testCircuit.test(1, assertions)); + } + + /** + * Creates a test circuit for the test, configered by the test parameters specified. + * + * @param testProperties The test parameters. + * @return A test circuit. + */ + public Circuit createCircuit(ParsedProperties testProperties) + { + Circuit result; + + // Cast the test properties into a typed interface for convenience. + MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProperties); + + // Create a standard publisher/receivers test client pair on a shared connection, individual sessions. + try + { + // Get a unique offset to append to destination names to make them unique to the connection. + long uniqueId = uniqueDestsId.incrementAndGet(); + + // Set up the connection. + Connection connection = TestUtils.createConnection(testProperties); + + // Add the connection exception listener to assert on exception conditions with. + // ExceptionMonitor exceptionMonitor = new ExceptionMonitor(); + // connection.setExceptionListener(exceptionMonitor); + + // Set up the publisher. + CircuitEndBase publisherEnd = createPublisherCircuitEnd(connection, props, uniqueId); + + // Set up the receiver. + CircuitEndBase receiverEnd = createReceiverCircuitEnd(connection, props, uniqueId); + + // Start listening for incoming messages. + connection.start(); + + // Package everything up. + LocalPublisherImpl publisher = createPublisherFromCircuitEnd(publisherEnd); + LocalReceiverImpl receiver = createReceiverFromCircuitEnd(receiverEnd); + + result = new LocalCircuitImpl(testProperties, publisher, receiver, connection, publisher.getExceptionMonitor()); + } + catch (JMSException e) + { + throw new RuntimeException("Could not create publisher/receivers pair due to a JMSException.", e); + } + + return result; + } + + /** + * Creates a local {@link Receiver} from a {@link CircuitEnd}. Sub-classes may override this to provide more + * specialized receivers if necessary. + * + * @param receiverEnd The receiving circuit end. + * + * @return A {@link Receiver}. + */ + protected LocalReceiverImpl createReceiverFromCircuitEnd(CircuitEndBase receiverEnd) + { + return new LocalReceiverImpl(receiverEnd); + } + + /** + * Creates a local {@link Publisher} from a {@link CircuitEnd}. Sub-classes may override this to provide more + * specialized receivers if necessary. + * + * @param publisherEnd The publishing circuit end. + * + * @return A {@link Receiver}. + */ + protected LocalPublisherImpl createPublisherFromCircuitEnd(CircuitEndBase publisherEnd) + { + return new LocalPublisherImpl(publisherEnd); + } + + /** + * Builds a circuit end suitable for the publishing side of a test circuit, from standard test parameters. + * + * @param connection The connection to build the circuit end on. + * @param testProps The test parameters to configure the circuit end construction. + * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique. + * + * @return A circuit end suitable for the publishing side of a test circuit. + * + * @throws JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation. + */ + public CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId) + throws JMSException + { + log.debug( + "public CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = " + + uniqueId + "): called"); + + // Cast the test properties into a typed interface for convenience. + MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); + + // Check that the test properties do not contain AMQP/Qpid specific settings, and fail if they do. + if (props.getImmediate() || props.getMandatory()) + { + throw new RuntimeException( + "Cannot create a pure JMS circuit as the test properties require AMQP specific options."); + } + + Session session = connection.createSession(props.getPublisherTransacted(), props.getAckMode()); + + Destination destination = + props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId) + : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId); + + MessageProducer producer = props.getPublisherProducerBind() ? session.createProducer(destination) : null; + + MessageConsumer consumer = + props.getPublisherConsumerBind() + ? session.createConsumer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null; + + MessageMonitor messageMonitor = new MessageMonitor(); + + if (consumer != null) + { + consumer.setMessageListener(messageMonitor); + } + + ExceptionMonitor exceptionMonitor = new ExceptionMonitor(); + connection.setExceptionListener(exceptionMonitor); + + if (!props.getPublisherConsumerActive() && (consumer != null)) + { + consumer.close(); + } + + return new CircuitEndBase(producer, consumer, session, messageMonitor, exceptionMonitor); + } + + /** + * Builds a circuit end suitable for the receiving side of a test circuit, from standard test parameters. + * + * @param connection The connection to build the circuit end on. + * @param testProps The test parameters to configure the circuit end construction. + * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique. + * + * @return A circuit end suitable for the receiving side of a test circuit. + * + * @throws JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation. + */ + public CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId) + throws JMSException + { + log.debug( + "public CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = " + + uniqueId + "): called"); + + // Cast the test properties into a typed interface for convenience. + MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); + + // Check that the test properties do not contain AMQP/Qpid specific settings, and fail if they do. + if (props.getImmediate() || props.getMandatory()) + { + throw new RuntimeException( + "Cannot create a pure JMS circuit as the test properties require AMQP specific options."); + } + + Session session = connection.createSession(props.getPublisherTransacted(), props.getAckMode()); + + MessageProducer producer = + props.getReceiverProducerBind() + ? session.createProducer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null; + + Destination destination = + props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId) + : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId); + + MessageConsumer consumer = + props.getReceiverConsumerBind() + ? ((props.getDurableSubscription() && props.getPubsub()) + ? session.createDurableSubscriber((Topic) destination, "testsub") : session.createConsumer(destination)) + : null; + + MessageMonitor messageMonitor = new MessageMonitor(); + + if (consumer != null) + { + consumer.setMessageListener(messageMonitor); + } + + if (!props.getReceiverConsumerActive() && (consumer != null)) + { + consumer.close(); + } + + return new CircuitEndBase(producer, consumer, session, messageMonitor, null); + } + + /** + * Sets the sender test client to coordinate the test with. + * + * @param sender The contact details of the sending client in the test. + */ + public void setSender(TestClientDetails sender) + { + throw new RuntimeException("Not implemented."); + } + + /** + * Sets the receiving test client to coordinate the test with. + * + * @param receiver The contact details of the sending client in the test. + */ + public void setReceiver(TestClientDetails receiver) + { + throw new RuntimeException("Not implemented."); + } + + /** + * Supplies the sending test client. + * + * @return The sending test client. + */ + public TestClientDetails getSender() + { + throw new RuntimeException("Not implemented."); + } + + /** + * Supplies the receiving test client. + * + * @return The receiving test client. + */ + public List getReceivers() + { + throw new RuntimeException("Not implemented."); + } + + /** + * Accepts the conversation factory over which to hold the test coordinating conversation. + * + * @param conversationFactory The conversation factory to coordinate the test over. + */ + public void setConversationFactory(ConversationFactory conversationFactory) + { + throw new RuntimeException("Not implemented."); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/MessagingTestConfigProperties.java b/java/systests/src/main/java/org/apache/qpid/test/framework/MessagingTestConfigProperties.java index f6664a78d9..2c0f39d54c 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/MessagingTestConfigProperties.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/MessagingTestConfigProperties.java @@ -20,10 +20,10 @@ */ package org.apache.qpid.test.framework; -import org.apache.qpid.jms.Session; - import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; +import javax.jms.Session; + import java.util.Properties; /** @@ -181,10 +181,16 @@ public class MessagingTestConfigProperties extends ParsedProperties public static final boolean PERSISTENT_MODE_DEFAULT = false; /** Holds the name of the property to get the test transactional mode from. */ - public static final String TRANSACTED_PROPNAME = "transacted"; + public static final String TRANSACTED_PUBLISHER_PROPNAME = "transactedPublisher"; + + /** Holds the transactional mode to use for the test. */ + public static final boolean TRANSACTED_PUBLISHER_DEFAULT = false; + + /** Holds the name of the property to get the test transactional mode from. */ + public static final String TRANSACTED_RECEIVER_PROPNAME = "transactedReceiver"; /** Holds the transactional mode to use for the test. */ - public static final boolean TRANSACTED_DEFAULT = false; + public static final boolean TRANSACTED_RECEIVER_DEFAULT = false; /** Holds the name of the property to set the no local flag from. */ public static final String NO_LOCAL_PROPNAME = "noLocal"; @@ -204,7 +210,7 @@ public class MessagingTestConfigProperties extends ParsedProperties /** Defines the default value of the durable subscriptions flag. */ public static final boolean DURABLE_SUBSCRIPTION_DEFAULT = false; - // ====================== Qpid Options and Flags ================================ + // ====================== Qpid/AMQP Options and Flags ================================ /** Holds the name of the property to set the exclusive flag from. */ public static final String EXCLUSIVE_PROPNAME = "exclusive"; @@ -230,7 +236,7 @@ public class MessagingTestConfigProperties extends ParsedProperties /** Default value for the durable destinations flag. */ public static final boolean DURABLE_DESTS_DEFAULT = false; - /** Holds the name of the proeprty to set the prefetch size from. */ + /** Holds the name of the property to set the prefetch size from. */ public static final String PREFETCH_PROPNAME = "prefetch"; /** Defines the default prefetch size to use when consuming messages. */ @@ -274,6 +280,26 @@ public class MessagingTestConfigProperties extends ParsedProperties /** Defines the default maximum quantity of pending message data to allow producers to hold. */ public static final int MAX_PENDING_DEFAULT = 0; + /** Holds the name of the property to get the publisher rollback flag from. */ + public static final String ROLLBACK_PUBLISHER_PROPNAME = "rollbackPublisher"; + + /** Holds the default publisher roll back setting. */ + public static final boolean ROLLBACK_PUBLISHER_DEFAULT = false; + + /** Holds the name of the property to get the publisher rollback flag from. */ + public static final String ROLLBACK_RECEIVER_PROPNAME = "rollbackReceiver"; + + /** Holds the default publisher roll back setting. */ + public static final boolean ROLLBACK_RECEIVER_DEFAULT = false; + + // ====================== Options that control the bahviour of the test framework. ========================= + + /** Holds the name of the property to get the behavioural mode of not applicable assertions. */ + public static final String NOT_APPLICABLE_ASSERTION_PROPNAME = "notApplicableAssertion"; + + /** Holds the default behavioral mode of not applicable assertions, which is logging them as a warning. */ + public static final String NOT_APPLICABLE_ASSERTION_DEFAULT = "warn"; + /** Holds the name of the property to get the verbose mode proeprty from. */ public static final String VERBOSE_PROPNAME = "verbose"; @@ -286,8 +312,11 @@ public class MessagingTestConfigProperties extends ParsedProperties static { defaults.setPropertyIfNull(INITIAL_CONTEXT_FACTORY_PROPNAME, INITIAL_CONTEXT_FACTORY_DEFAULT); - // defaults.setPropertyIfNull(CONNECTION_PROPNAME, CONNECTION_DEFAULT); - defaults.setPropertyIfNull(MESSAGE_SIZE_PROPNAME, MESSAGE_SIZE_DEAFULT); + defaults.setPropertyIfNull(BROKER_PROPNAME, BROKER_DEFAULT); + defaults.setPropertyIfNull(VIRTUAL_HOST_PROPNAME, VIRTUAL_HOST_DEFAULT); + defaults.setPropertyIfNull(USERNAME_PROPNAME, USERNAME_DEFAULT); + defaults.setPropertyIfNull(PASSWORD_PROPNAME, PASSWORD_DEFAULT); + defaults.setPropertyIfNull(PUBLISHER_PRODUCER_BIND_PROPNAME, PUBLISHER_PRODUCER_BIND_DEFAULT); defaults.setPropertyIfNull(PUBLISHER_CONSUMER_BIND_PROPNAME, PUBLISHER_CONSUMER_BIND_DEFAULT); defaults.setPropertyIfNull(RECEIVER_PRODUCER_BIND_PROPNAME, RECEIVER_PRODUCER_BIND_DEFAULT); @@ -296,28 +325,33 @@ public class MessagingTestConfigProperties extends ParsedProperties defaults.setPropertyIfNull(RECEIVER_CONSUMER_ACTIVE_PROPNAME, RECEIVER_CONSUMER_ACTIVE_DEFAULT); defaults.setPropertyIfNull(SEND_DESTINATION_NAME_ROOT_PROPNAME, SEND_DESTINATION_NAME_ROOT_DEFAULT); defaults.setPropertyIfNull(RECEIVE_DESTINATION_NAME_ROOT_PROPNAME, RECEIVE_DESTINATION_NAME_ROOT_DEFAULT); - defaults.setPropertyIfNull(PERSISTENT_MODE_PROPNAME, PERSISTENT_MODE_DEFAULT); - defaults.setPropertyIfNull(TRANSACTED_PROPNAME, TRANSACTED_DEFAULT); - defaults.setPropertyIfNull(BROKER_PROPNAME, BROKER_DEFAULT); - defaults.setPropertyIfNull(VIRTUAL_HOST_PROPNAME, VIRTUAL_HOST_DEFAULT); - defaults.setPropertyIfNull(RATE_PROPNAME, RATE_DEFAULT); - defaults.setPropertyIfNull(VERBOSE_PROPNAME, VERBOSE_DEFAULT); - defaults.setPropertyIfNull(PUBSUB_PROPNAME, PUBSUB_DEFAULT); - defaults.setPropertyIfNull(USERNAME_PROPNAME, USERNAME_DEFAULT); - defaults.setPropertyIfNull(PASSWORD_PROPNAME, PASSWORD_DEFAULT); - defaults.setPropertyIfNull(SELECTOR_PROPNAME, SELECTOR_DEFAULT); defaults.setPropertyIfNull(DESTINATION_COUNT_PROPNAME, DESTINATION_COUNT_DEFAULT); - defaults.setPropertyIfNull(TIMEOUT_PROPNAME, TIMEOUT_DEFAULT); - defaults.setPropertyIfNull(TX_BATCH_SIZE_PROPNAME, TX_BATCH_SIZE_DEFAULT); - defaults.setPropertyIfNull(DURABLE_DESTS_PROPNAME, DURABLE_DESTS_DEFAULT); + defaults.setPropertyIfNull(PUBSUB_PROPNAME, PUBSUB_DEFAULT); + + defaults.setPropertyIfNull(PERSISTENT_MODE_PROPNAME, PERSISTENT_MODE_DEFAULT); + defaults.setPropertyIfNull(TRANSACTED_PUBLISHER_PROPNAME, TRANSACTED_PUBLISHER_DEFAULT); + defaults.setPropertyIfNull(TRANSACTED_RECEIVER_PROPNAME, TRANSACTED_RECEIVER_DEFAULT); + defaults.setPropertyIfNull(NO_LOCAL_PROPNAME, NO_LOCAL_DEFAULT); defaults.setPropertyIfNull(ACK_MODE_PROPNAME, ACK_MODE_DEFAULT); defaults.setPropertyIfNull(DURABLE_SUBSCRIPTION_PROPNAME, DURABLE_SUBSCRIPTION_DEFAULT); - defaults.setPropertyIfNull(MAX_PENDING_PROPNAME, MAX_PENDING_DEFAULT); - defaults.setPropertyIfNull(PREFETCH_PROPNAME, PREFETCH_DEFAULT); - defaults.setPropertyIfNull(NO_LOCAL_PROPNAME, NO_LOCAL_DEFAULT); + defaults.setPropertyIfNull(EXCLUSIVE_PROPNAME, EXCLUSIVE_DEFAULT); defaults.setPropertyIfNull(IMMEDIATE_PROPNAME, IMMEDIATE_DEFAULT); defaults.setPropertyIfNull(MANDATORY_PROPNAME, MANDATORY_DEFAULT); + defaults.setPropertyIfNull(DURABLE_DESTS_PROPNAME, DURABLE_DESTS_DEFAULT); + defaults.setPropertyIfNull(PREFETCH_PROPNAME, PREFETCH_DEFAULT); + + defaults.setPropertyIfNull(MESSAGE_SIZE_PROPNAME, MESSAGE_SIZE_DEAFULT); + defaults.setPropertyIfNull(RATE_PROPNAME, RATE_DEFAULT); + defaults.setPropertyIfNull(SELECTOR_PROPNAME, SELECTOR_DEFAULT); + defaults.setPropertyIfNull(TIMEOUT_PROPNAME, TIMEOUT_DEFAULT); + defaults.setPropertyIfNull(TX_BATCH_SIZE_PROPNAME, TX_BATCH_SIZE_DEFAULT); + defaults.setPropertyIfNull(MAX_PENDING_PROPNAME, MAX_PENDING_DEFAULT); + defaults.setPropertyIfNull(ROLLBACK_PUBLISHER_PROPNAME, ROLLBACK_PUBLISHER_DEFAULT); + defaults.setPropertyIfNull(ROLLBACK_RECEIVER_PROPNAME, ROLLBACK_RECEIVER_DEFAULT); + + defaults.setPropertyIfNull(NOT_APPLICABLE_ASSERTION_PROPNAME, NOT_APPLICABLE_ASSERTION_DEFAULT); + defaults.setPropertyIfNull(VERBOSE_PROPNAME, VERBOSE_DEFAULT); } /** @@ -338,148 +372,314 @@ public class MessagingTestConfigProperties extends ParsedProperties super(properties); } + /** + * The size of test messages to send. + * + * @return The size of test messages to send. + */ public int getMessageSize() { return getPropertyAsInteger(MESSAGE_SIZE_PROPNAME); } + /** + * Flag to indicate that the publishing producer should be set up to publish to a destination. + * + * @return Flag to indicate that the publishing producer should be set up to publish to a destination. + */ public boolean getPublisherProducerBind() { return getPropertyAsBoolean(PUBLISHER_PRODUCER_BIND_PROPNAME); } + /** + * Flag to indicate that the publishing consumer should be set up to receive from a destination. + * + * @return Flag to indicate that the publishing consumer should be set up to receive from a destination. + */ public boolean getPublisherConsumerBind() { return getPropertyAsBoolean(PUBLISHER_CONSUMER_BIND_PROPNAME); } + /** + * Flag to indicate that the receiving producer should be set up to publish to a destination. + * + * @return Flag to indicate that the receiving producer should be set up to publish to a destination. + */ public boolean getReceiverProducerBind() { return getPropertyAsBoolean(RECEIVER_PRODUCER_BIND_PROPNAME); } + /** + * Flag to indicate that the receiving consumer should be set up to receive from a destination. + * + * @return Flag to indicate that the receiving consumer should be set up to receive from a destination. + */ public boolean getReceiverConsumerBind() { return getPropertyAsBoolean(RECEIVER_CONSUMER_BIND_PROPNAME); } + /** + * Flag to indicate that the publishing consumer should be created and actively listening. + * + * @return Flag to indicate that the publishing consumer should be created. + */ public boolean getPublisherConsumerActive() { return getPropertyAsBoolean(PUBLISHER_CONSUMER_ACTIVE_PROPNAME); } + /** + * Flag to indicate that the receiving consumers should be created and actively listening. + * + * @return Flag to indicate that the receiving consumers should be created and actively listening. + */ public boolean getReceiverConsumerActive() { return getPropertyAsBoolean(RECEIVER_CONSUMER_ACTIVE_PROPNAME); } + /** + * A root to create all test destination names from. + * + * @return A root to create all test destination names from. + */ public String getSendDestinationNameRoot() { return getProperty(SEND_DESTINATION_NAME_ROOT_PROPNAME); } + /** + * A root to create all receiving destination names from. + * + * @return A root to create all receiving destination names from. + */ public String getReceiveDestinationNameRoot() { return getProperty(RECEIVE_DESTINATION_NAME_ROOT_PROPNAME); } + /** + * Flag to indicate that persistent messages should be used. + * + * @return Flag to indicate that persistent messages should be used. + */ public boolean getPersistentMode() { return getPropertyAsBoolean(PERSISTENT_MODE_PROPNAME); } - public boolean getTransacted() + /** + * Flag to indicate that transactional messages should be sent by the publisher. + * + * @return Flag to indicate that transactional messages should be sent by the publisher. + */ + public boolean getPublisherTransacted() { - return getPropertyAsBoolean(TRANSACTED_PROPNAME); + return getPropertyAsBoolean(TRANSACTED_PUBLISHER_PROPNAME); } - public String getBroker() + /** + * Flag to indicate that transactional receives should be used by the receiver. + * + * @return Flag to indicate that transactional receives should be used by the receiver. + */ + public boolean getReceiverTransacted() { - return getProperty(BROKER_PROPNAME); + return getPropertyAsBoolean(TRANSACTED_PUBLISHER_PROPNAME); } + /** + * The name of the virtual host to run all tests over. + * + * @return The name of the virtual host to run all tests over. + */ public String getVirtualHost() { return getProperty(VIRTUAL_HOST_PROPNAME); } + /** + * Limiting rate for each sender in messages per second, or zero for unlimited. + * + * @return Limiting rate for each sender in messages per second, or zero for unlimited. + */ public String getRate() { return getProperty(RATE_PROPNAME); } + /** + * Flag to indicate that test messages should be received publish/subscribe style by all receivers. + * + * @return Flag to indicate that test messages should be received publish/subscribe style by all receivers. + */ public boolean getPubsub() { return getPropertyAsBoolean(PUBSUB_PROPNAME); } + /** + * The username credentials to run tests with. + * + * @return The username credentials to run tests with. + */ public String getUsername() { return getProperty(USERNAME_PROPNAME); } + /** + * The password credentials to run tests with. + * + * @return The password credentials to run tests with. + */ public String getPassword() { return getProperty(PASSWORD_PROPNAME); } - public int getDestinationCount() - { - return getPropertyAsInteger(DESTINATION_COUNT_PROPNAME); - } - + /** + * The timeout duration to fail tests on, should they receive no messages within it. + * + * @return The timeout duration to fail tests on, should they receive no messages within it. + */ public long getTimeout() { return getPropertyAsLong(TIMEOUT_PROPNAME); } + /** + * The number of messages to batch into each transaction in transational tests. + * + * @return The number of messages to batch into each transaction in transational tests. + */ public int getTxBatchSize() { return getPropertyAsInteger(TX_BATCH_SIZE_PROPNAME); } + /** + * Flag to indicate that tests should use durable destinations. + * + * @return Flag to indicate that tests should use durable destinations. + */ public boolean getDurableDests() { return getPropertyAsBoolean(DURABLE_DESTS_PROPNAME); } + /** + * The ack mode for message receivers to use. + * + * @return The ack mode for message receivers to use. + */ public int getAckMode() { return getPropertyAsInteger(ACK_MODE_PROPNAME); } + /** + * Flag to indicate that tests should use durable subscriptions. + * + * @return Flag to indicate that tests should use durable subscriptions. + */ public boolean getDurableSubscription() { return getPropertyAsBoolean(DURABLE_SUBSCRIPTION_PROPNAME); } + /** + * The maximum amount of in-flight data, in bytes, that tests should send at any time. + * + * @return The maximum amount of in-flight data, in bytes, that tests should send at any time. + */ public int getMaxPending() { return getPropertyAsInteger(MAX_PENDING_PROPNAME); } - public int getPrefecth() + /** + * The size of the prefetch queue to use. + * + * @return The size of the prefetch queue to use. + */ + public int getPrefetch() { return getPropertyAsInteger(PREFETCH_PROPNAME); } + /** + * Flag to indicate that subscriptions should be no-local. + * + * @return Flag to indicate that subscriptions should be no-local. + */ public boolean getNoLocal() { return getPropertyAsBoolean(NO_LOCAL_PROPNAME); } + /** + * Flag to indicate that subscriptions should be exclusive. + * + * @return Flag to indicate that subscriptions should be exclusive. + */ public boolean getExclusive() { return getPropertyAsBoolean(EXCLUSIVE_PROPNAME); } + /** + * Flag to indicate that messages must be delivered immediately. + * + * @return Flag to indicate that messages must be delivered immediately. + */ public boolean getImmediate() { return getPropertyAsBoolean(IMMEDIATE_PROPNAME); } + /** + * Flag to indicate that messages must be routable. + * + * @return Flag to indicate that messages must be routable. + */ public boolean getMandatory() { return getPropertyAsBoolean(MANDATORY_PROPNAME); } + + /** + * Gets the value of a flag to indicate that the publisher should rollback all messages sent. + * + * @return A flag to indicate that the publisher should rollback all messages sent. + */ + public boolean getRollbackPublisher() + { + return getPropertyAsBoolean(ROLLBACK_PUBLISHER_PROPNAME); + } + + /** + * Gets the value of a flag to indicate that the receiver should rollback all messages received, then receive them + * again. + * + * @return A flag to indicate that the publisher should rollback all messages received. + */ + public boolean getRollbackReceiver() + { + return getPropertyAsBoolean(ROLLBACK_RECEIVER_PROPNAME); + } + + /** + * Gets the behavioural mode of not applicable assertions. Should be one of 'quiet', 'warn' or 'fail'. + * + * @return The behavioural mode of not applicable assertions. + */ + public String getNotApplicableAssertionMode() + { + return getProperty(NOT_APPLICABLE_ASSERTION_PROPNAME); + } } diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/NotApplicableAssertion.java b/java/systests/src/main/java/org/apache/qpid/test/framework/NotApplicableAssertion.java new file mode 100644 index 0000000000..c9bba54c51 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/NotApplicableAssertion.java @@ -0,0 +1,112 @@ +/* + * + * 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. + * + */ +package org.apache.qpid.test.framework; + +import org.apache.log4j.Logger; + +import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; + +/** + * NotApplicableAssertion is a messaging assertion that can be used when an assertion requested by a test-case is not + * applicable to the testing scenario. For example an assertion may relate to AMQP functionality, but a test case may be + * being run over a non-AMQP JMS implementation, in which case the request to create the assertion may return this + * instead of the proper assertion. The test framework is configurable to quietly drop these assertions, log them + * as warnings to the console, or raise them as test failures. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Quitely pass. + *
Log a warning. + *
Raise a test failure. + *
+ */ +public class NotApplicableAssertion implements Assertion +{ + /** Used for logging to the console. */ + private static final Logger console = Logger.getLogger("CONSOLE." + NotApplicableAssertion.class.getName()); + + /** The possible behavioural modes of this assertion. */ + private enum Mode + { + /** Quietly ignore the assertion by passing. */ + Quiet, + + /** Ignore the assertion by passing but log a warning about it. */ + Warn, + + /** Fail the assertion. */ + Fail; + } + + /** The behavioural mode of the assertion. */ + private Mode mode; + + /** + * Creates an assertion that is driven by the value of the 'notApplicableAssertion' property of the test + * configuration. Its value should match one of 'quiet', 'warn' or 'fail' and if it does not it is automatically + * read as 'fail'. + * + * @param testProperties The test configuration properties. + */ + public NotApplicableAssertion(ParsedProperties testProperties) + { + // Cast the test properties into a typed interface for convenience. + MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProperties); + + String modeName = props.getNotApplicableAssertionMode(); + + if ("quiet".equals(modeName)) + { + mode = Mode.Quiet; + } + else if ("warn".equals(modeName)) + { + mode = Mode.Warn; + } + else + { + mode = Mode.Fail; + } + } + + /** + * Applies the assertion. + * + * @return true if the assertion passes, false if it fails. + */ + public boolean apply() + { + switch (mode) + { + case Quiet: + return true; + + case Warn: + console.warn("Warning: Not applicable assertion being ignored."); + + return true; + + case Fail: + default: + return false; + } + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/Publisher.java b/java/systests/src/main/java/org/apache/qpid/test/framework/Publisher.java index df35579533..418776b5cb 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/Publisher.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/Publisher.java @@ -20,37 +20,55 @@ */ package org.apache.qpid.test.framework; +import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; + /** - * A Publisher is a {@link CircuitEnd} that represents the status of the publishing side of a test circuit. Its main - * purpose is to provide assertions that can be applied to test the behaviour of the publishers. + * A Publisher represents the status of the publishing side of a test circuit. Its main purpose is to provide assertions + * that can be applied to test the behaviour of the publishers. * *

*
CRC Card
Responsibilities *
Provide assertion that the publishers received no exceptions. - *
Provide assertion that the publishers received a no consumers error code on every message. - *
Provide assertion that the publishers received a no route error code on every message. *
+ * + * @todo There are mixtures of AMQP and JMS assertions in this interface. Either keep them here, but quietly (or with a + * warning or error) drop them from test cases where they are not relevant, or push them down into sub-classes. + * I am tempted to go with the dropping/warning/error approach, that would imply that it makes sense to pull + * the assertions back from AMQPPublisher to here. */ public interface Publisher { + // Assertions that are meaningfull to AMQP and to JMS. + /** * Provides an assertion that the publisher encountered no exceptions. * + * @param testProps The test configuration properties. + * * @return An assertion that the publisher encountered no exceptions. */ - public Assertion noExceptionsAssertion(); + public Assertion noExceptionsAssertion(ParsedProperties testProps); + + // Assertions that are meaningfull only to AMQP. /** - * Provides an assertion that the publisher got a no consumers exception on every message. + * Provides an assertion that the AMQP channel was forcibly closed by an error condition. + * + * @param testProps The test configuration properties. * - * @return An assertion that the publisher got a no consumers exception on every message. + * @return An assertion that the AMQP channel was forcibly closed by an error condition. */ - public Assertion noConsumersAssertion(); + public Assertion channelClosedAssertion(ParsedProperties testProps); + + // Assertions that are meaningfull only to Java/JMS. /** - * Provides an assertion that the publisher got a no rout exception on every message. + * Provides an assertion that the publisher got a given exception during the test. + * + * @param testProps The test configuration properties. + * @param exceptionClass The exception class to check for. * - * @return An assertion that the publisher got a no rout exception on every message. + * @return An assertion that the publisher got a given exception during the test. */ - public Assertion noRouteAssertion(); + public Assertion exceptionAssertion(ParsedProperties testProps, Class exceptionClass); } diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/Receiver.java b/java/systests/src/main/java/org/apache/qpid/test/framework/Receiver.java index e01b272d5a..4fd4ffeb48 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/Receiver.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/Receiver.java @@ -20,6 +20,8 @@ */ package org.apache.qpid.test.framework; +import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; + /** * A Receiver is a {@link CircuitEnd} that represents the status of the receiving side of a test circuit. Its main * purpose is to provide assertions that can be applied to check the behaviour of the receivers. @@ -29,20 +31,62 @@ package org.apache.qpid.test.framework; * Provide assertion that the receivers received no exceptions. * Provide assertion that the receivers received all test messages sent to it. * + * + * @todo There are mixtures of AMQP and JMS assertions in this interface. Either keep them here, but quietly (or with a + * warning or error) drop them from test cases where they are not relevant, or push them down into sub-classes. + * I am tempted to go with the dropping/warning/error approach. */ public interface Receiver { + // Assertions that are meaningfull to AMQP and to JMS. + /** * Provides an assertion that the receivers encountered no exceptions. * + * @param testProps The test configuration properties. + * * @return An assertion that the receivers encountered no exceptions. */ - public Assertion noExceptionsAssertion(); + public Assertion noExceptionsAssertion(ParsedProperties testProps); /** * Provides an assertion that the receivers got all messages that were sent to it. * + * @param testProps The test configuration properties. + * * @return An assertion that the receivers got all messages that were sent to it. */ - public Assertion allMessagesAssertion(); + public Assertion allMessagesReceivedAssertion(ParsedProperties testProps); + + /** + * Provides an assertion that the receivers got none of the messages that were sent to it. + * + * @param testProps The test configuration properties. + * + * @return An assertion that the receivers got none of the messages that were sent to it. + */ + public Assertion noMessagesReceivedAssertion(ParsedProperties testProps); + + // Assertions that are meaningfull only to AMQP. + + /** + * Provides an assertion that the AMQP channel was forcibly closed by an error condition. + * + * @param testProps The test configuration properties. + * + * @return An assertion that the AMQP channel was forcibly closed by an error condition. + */ + public Assertion channelClosedAssertion(ParsedProperties testProps); + + // Assertions that are meaningfull only to Java/JMS. + + /** + * Provides an assertion that the receiver got a given exception during the test. + * + * @param testProps The test configuration properties. + * @param exceptionClass The exception class to check for. + * + * @return An assertion that the receiver got a given exception during the test. + */ + public Assertion exceptionAssertion(ParsedProperties testProps, Class exceptionClass); } diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/TestUtils.java b/java/systests/src/main/java/org/apache/qpid/test/framework/TestUtils.java index 569eac425c..6ad6185ece 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/TestUtils.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/TestUtils.java @@ -32,7 +32,6 @@ import javax.naming.InitialContext; import javax.naming.NamingException; import java.util.Map; -import java.util.Properties; /** * TestUtils provides static helper methods that are usefull for writing tests against QPid. @@ -40,7 +39,9 @@ import java.util.Properties; *

*
CRC Card
Responsibilities Collaborations *
Create connections from test properties. {@link MessagingTestConfigProperties} + *
Create test messages. *
Inject a short pause in a test. + *
Serialize properties into a message. *
*/ public class TestUtils @@ -48,7 +49,8 @@ public class TestUtils /** Used for debugging. */ private static Logger log = Logger.getLogger(TestUtils.class); - private static byte[] MESSAGE_DATA_BYTES = + /** Some dummy data to stuff all test messages with. */ + private static final byte[] MESSAGE_DATA_BYTES = "Test Message -- Test Message -- Test Message -- Test Message -- Test Message -- Test Message -- Test Message -- " .getBytes(); @@ -118,7 +120,7 @@ public class TestUtils * * @return A bytes message, of the specified size, filled with dummy data. * - * + * @throws JMSException Any underlying JMSExceptions are allowed to fall through. */ public static Message createTestMessageOfSize(Session session, int size) throws JMSException { diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchFailureException.java b/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchFailureException.java index b04ab0f9b0..86bddaf10d 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchFailureException.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchFailureException.java @@ -21,8 +21,8 @@ package org.apache.qpid.test.framework.clocksynch; /** - * ClockSynchFailureException represents failure of a {@link ClockSynchronizer} to achieve synchronization. This could - * be because a reference signal is not available, or because a desired accurracy cannot be attained, for example. + * ClockSynchFailureException represents failure of a {@link ClockSynchronizer} to achieve synchronization. For example, + * this could be because a reference signal is not available, or because a desired accurracy cannot be attained. * *

*
CRC Card
Responsibilities Collaborations diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchronizer.java b/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchronizer.java index d0fd16d715..610f4b75c3 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchronizer.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/clocksynch/ClockSynchronizer.java @@ -32,7 +32,7 @@ package org.apache.qpid.test.framework.clocksynch; * *

*
CRC Card
Responsibilities Collaborations - *
Trigger a clock synchronziation. + *
Trigger a clock synchronization. *
Compute a clock delta to apply to the local clock. *
Estimate the error in the synchronzation. *
diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedPublisherImpl.java b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedPublisherImpl.java index 9e3054964c..693fd854c7 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedPublisherImpl.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedPublisherImpl.java @@ -23,10 +23,17 @@ package org.apache.qpid.test.framework.distributedcircuit; import org.apache.qpid.test.framework.Assertion; import org.apache.qpid.test.framework.Publisher; +import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; + /** + * DistributedPublisherImpl represents the status of the publishing side of a test circuit. Its main purpose is to + * provide assertions that can be applied to verify the behaviour of a non-local publisher. + * *

*
CRC Card
Responsibilities Collaborations - *
+ *
Provide assertion that the publishers received no exceptions. + *
Provide assertion that the publishers received a no consumers error code on every message. + *
Provide assertion that the publishers received a no route error code on every message. *
*/ public class DistributedPublisherImpl implements Publisher @@ -35,8 +42,9 @@ public class DistributedPublisherImpl implements Publisher * Provides an assertion that the publisher encountered no exceptions. * * @return An assertion that the publisher encountered no exceptions. + * @param testProps */ - public Assertion noExceptionsAssertion() + public Assertion noExceptionsAssertion(ParsedProperties testProps) { throw new RuntimeException("Not implemented."); } @@ -60,4 +68,27 @@ public class DistributedPublisherImpl implements Publisher { throw new RuntimeException("Not implemented."); } + + /** + * Provides an assertion that the AMQP channel was forcibly closed by an error condition. + * + * @param testProps The test configuration properties. + * @return An assertion that the AMQP channel was forcibly closed by an error condition. + */ + public Assertion channelClosedAssertion(ParsedProperties testProps) + { + throw new RuntimeException("Not implemented."); + } + + /** + * Provides an assertion that the publisher got a given exception during the test. + * + * @param testProps The test configuration properties. + * @param exceptionClass The exception class to check for. + * @return An assertion that the publisher got a given exception during the test. + */ + public Assertion exceptionAssertion(ParsedProperties testProps, Class exceptionClass) + { + throw new RuntimeException("Not implemented."); + } } diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedReceiverImpl.java b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedReceiverImpl.java index 4a77906448..14782ee5e2 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedReceiverImpl.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/DistributedReceiverImpl.java @@ -23,10 +23,16 @@ package org.apache.qpid.test.framework.distributedcircuit; import org.apache.qpid.test.framework.Assertion; import org.apache.qpid.test.framework.Receiver; +import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; + /** + * DistributedReceiverImpl represents the status of the receiving side of a test circuit. Its main purpose is to + * provide assertions that can be applied to verify the behaviour of a non-local receiver. + * *

*
CRC Card
Responsibilities Collaborations - *
+ *
Provide assertion that the receivers received no exceptions. + *
Provide assertion that the receivers received all test messages sent to it. *
*/ public class DistributedReceiverImpl implements Receiver @@ -35,8 +41,9 @@ public class DistributedReceiverImpl implements Receiver * Provides an assertion that the receivers encountered no exceptions. * * @return An assertion that the receivers encountered no exceptions. + * @param testProps */ - public Assertion noExceptionsAssertion() + public Assertion noExceptionsAssertion(ParsedProperties testProps) { throw new RuntimeException("Not implemented."); } @@ -45,8 +52,42 @@ public class DistributedReceiverImpl implements Receiver * Provides an assertion that the receivers got all messages that were sent to it. * * @return An assertion that the receivers got all messages that were sent to it. + * @param testProps + */ + public Assertion allMessagesReceivedAssertion(ParsedProperties testProps) + { + throw new RuntimeException("Not implemented."); + } + + /** + * Provides an assertion that the receivers got none of the messages that were sent to it. + * + * @return An assertion that the receivers got none of the messages that were sent to it. + * @param testProps + */ + public Assertion noMessagesReceivedAssertion(ParsedProperties testProps) + { + throw new RuntimeException("Not implemented."); + } + + /** + * Provides an assertion that the AMQP channel was forcibly closed by an error condition. + * + * @param testProps The test configuration properties. + * @return An assertion that the AMQP channel was forcibly closed by an error condition. + */ + public Assertion channelClosedAssertion(ParsedProperties testProps) + { + throw new RuntimeException("Not implemented."); + } + + /** + * Provides an assertion that the receiver got a given exception during the test. + * + * @param testProps + *@param exceptionClass The exception class to check for. @return An assertion that the receiver got a given exception during the test. */ - public Assertion allMessagesAssertion() + public Assertion exceptionAssertion(ParsedProperties testProps, Class exceptionClass) { throw new RuntimeException("Not implemented."); } diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/TestClientCircuitEnd.java b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/TestClientCircuitEnd.java index 621d16ec9b..845b0a39f6 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/TestClientCircuitEnd.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedcircuit/TestClientCircuitEnd.java @@ -24,7 +24,6 @@ import org.apache.log4j.Logger; import org.apache.qpid.test.framework.*; import org.apache.qpid.test.framework.distributedtesting.TestClientControlledTest; -import org.apache.qpid.test.framework.localcircuit.LocalCircuitImpl; import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; import uk.co.thebadgerset.junit.extensions.util.TestContextProperties; @@ -48,7 +47,15 @@ import javax.jms.*; * *

*
CRC Card
Responsibilities Collaborations - *
+ *
Provide a message producer for sending messages. + * {@link CircuitEnd}, {@link LocalCircuitFactory}, {@link TestUtils} + *
Provide a message consumer for receiving messages. + * {@link CircuitEnd}, {@link LocalCircuitFactory}, {@link TestUtils} + *
Supply the name of the test case that this implements. + *
Accept/Reject invites based on test parameters. {@link MessagingTestConfigProperties} + *
Adapt to assigned roles. {@link TestClientControlledTest.Roles} + *
Perform test case actions. {@link MessageMonitor} + *
Generate test reports. {@link MessageMonitor} *
*/ public class TestClientCircuitEnd implements CircuitEnd, TestClientControlledTest @@ -145,13 +152,15 @@ public class TestClientCircuitEnd implements CircuitEnd, TestClientControlledTes connection = TestUtils.createConnection(testProps); // Create a circuit end that matches the assigned role and test parameters. + LocalCircuitFactory circuitFactory = new LocalCircuitFactory(); + switch (role) { // Check if the sender role is being assigned, and set up a message producer if so. case SENDER: // Set up the publisher. - circuitEnd = LocalCircuitImpl.createPublisherCircuitEnd(connection, testProps, 0L); + circuitEnd = circuitFactory.createPublisherCircuitEnd(connection, testProps, 0L); // Create a custom message monitor that will be updated on every message sent. messageMonitor = new MessageMonitor(); @@ -162,7 +171,7 @@ public class TestClientCircuitEnd implements CircuitEnd, TestClientControlledTes case RECEIVER: // Set up the receiver. - circuitEnd = LocalCircuitImpl.createReceiverCircuitEnd(connection, testProps, 0L); + circuitEnd = circuitFactory.createReceiverCircuitEnd(connection, testProps, 0L); // Use the message monitor from the consumer for stats. messageMonitor = getMessageMonitor(); diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/Coordinator.java b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/Coordinator.java index 90a44e3b6b..1be2f338f7 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/Coordinator.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/Coordinator.java @@ -33,7 +33,6 @@ import org.apache.qpid.test.framework.TestClientDetails; import org.apache.qpid.test.framework.TestUtils; import org.apache.qpid.test.framework.clocksynch.UDPClockReference; import org.apache.qpid.util.ConversationFactory; -import org.apache.qpid.util.PrettyPrintingUtils; import uk.co.thebadgerset.junit.extensions.TKTestRunner; import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; @@ -56,7 +55,7 @@ import java.util.concurrent.LinkedBlockingQueue; *

*
CRC Card
Responsibilities Collaborations *
Find out what test clients are available. {@link ConversationFactory} - *
Decorate available tests to run all available clients. {@link DistributedTestDecorator} + *
Decorate available tests to run on all available clients. {@link DistributedTestDecorator} *
Attach XML test result logger. *
Terminate the interop testing framework. *
@@ -64,8 +63,6 @@ import java.util.concurrent.LinkedBlockingQueue; * @todo Should accumulate failures over all tests, and return with success or fail code based on all results. May need * to write a special TestResult to do this properly. At the moment only the last one used will be tested for * errors, as the start method creates a fresh one for each test case run. - * - * @todo Remove hard coding of test cases and put on command line instead. */ public class Coordinator extends TKTestRunner { @@ -119,27 +116,29 @@ public class Coordinator extends TKTestRunner /** * Creates an interop test coordinator on the specified broker and virtual host. * - * @param repetitions The number of times to repeat the test, or test batch size. - * @param duration The length of time to run the tests for. -1 means no duration has been set. - * @param threads The concurrency levels to ramp up to. - * @param delay A delay in milliseconds between test runs. - * @param params The sets of 'size' parameters to pass to test. - * @param testCaseName The name of the test case to run. - * @param reportDir The directory to output the test results to. - * @param runName The name of the test run; used to name the output file. - * @param verbose Whether to print comments during test run. - * @param brokerUrl The URL of the broker to connect to. - * @param virtualHost The virtual host to run all tests on. Optional, may be null. - * @param engine The distributed test engine type to run the tests with. - * @param terminate true if test client nodes should be terminated at the end of the tests. - * @param csv true if the CSV results listener should be attached. - * @param xml true if the XML results listener should be attached. + * @param repetitions The number of times to repeat the test, or test batch size. + * @param duration The length of time to run the tests for. -1 means no duration has been set. + * @param threads The concurrency levels to ramp up to. + * @param delay A delay in milliseconds between test runs. + * @param params The sets of 'size' parameters to pass to test. + * @param testCaseName The name of the test case to run. + * @param reportDir The directory to output the test results to. + * @param runName The name of the test run; used to name the output file. + * @param verbose Whether to print comments during test run. + * @param brokerUrl The URL of the broker to connect to. + * @param virtualHost The virtual host to run all tests on. Optional, may be null. + * @param engine The distributed test engine type to run the tests with. + * @param terminate true if test client nodes should be terminated at the end of the tests. + * @param csv true if the CSV results listener should be attached. + * @param xml true if the XML results listener should be attached. + * @param decoratorFactories List of factories for user specified decorators. */ public Coordinator(Integer repetitions, Long duration, int[] threads, int delay, int[] params, String testCaseName, String reportDir, String runName, boolean verbose, String brokerUrl, String virtualHost, TestEngine engine, - boolean terminate, boolean csv, boolean xml) + boolean terminate, boolean csv, boolean xml, List decoratorFactories) { - super(repetitions, duration, threads, delay, params, testCaseName, reportDir, runName, csv, xml, verbose); + super(repetitions, duration, threads, delay, params, testCaseName, reportDir, runName, csv, xml, verbose, + decoratorFactories); log.debug("public Coordinator(Integer repetitions = " + repetitions + " , Long duration = " + duration + ", int[] threads = " + Arrays.toString(threads) + ", int delay = " + delay + ", int[] params = " @@ -221,7 +220,11 @@ public class Coordinator extends TKTestRunner }, { "s", "The size parameter to run tests with.", "size", "false", MathUtils.SEQUENCE_REGEXP }, { "v", "Verbose mode.", null, "false" }, - { "n", "A name for this test run, used to name the output file.", "name", "true" } + { "n", "A name for this test run, used to name the output file.", "name", "true" }, + { + "X:decorators", "A list of additional test decorators to wrap the tests in.", + "\"class.name[:class.name]*\"", "false" + } }), testContextProperties)); // Extract the command line options. @@ -234,13 +237,13 @@ public class Coordinator extends TKTestRunner boolean terminate = options.getPropertyAsBoolean("t"); boolean csvResults = options.getPropertyAsBoolean("-csv"); boolean xmlResults = options.getPropertyAsBoolean("-xml"); - String threadsString = options.getProperty("c"); Integer repetitions = options.getPropertyAsInteger("r"); String durationString = options.getProperty("d"); String paramsString = options.getProperty("s"); boolean verbose = options.getPropertyAsBoolean("v"); String testRunName = options.getProperty("n"); + String decorators = options.getProperty("X:decorators"); int[] threads = (threadsString == null) ? null : MathUtils.parseSequence(threadsString); int[] params = (paramsString == null) ? null : MathUtils.parseSequence(paramsString); @@ -253,6 +256,9 @@ public class Coordinator extends TKTestRunner Collection> testCaseClasses = new ArrayList>(); + // Create a list of test decorator factories for use specified decorators to be applied. + List decoratorFactories = parseDecorators(decorators); + // Scan for available test cases using a classpath scanner. // ClasspathScanner.getMatches(DistributedTestCase.class, "^Test.*", true); @@ -306,7 +312,7 @@ public class Coordinator extends TKTestRunner // Create a coordinator and begin its test procedure. Coordinator coordinator = new Coordinator(repetitions, duration, threads, 0, params, null, reportDir, testRunName, verbose, brokerUrl, - virtualHost, engine, terminate, csvResults, xmlResults); + virtualHost, engine, terminate, csvResults, xmlResults, decoratorFactories); TestResult testResult = coordinator.start(testClassNames); @@ -339,8 +345,7 @@ public class Coordinator extends TKTestRunner */ public TestResult start(String[] testClassNames) throws Exception { - log.debug("public TestResult start(String[] testClassNames = " + PrettyPrintingUtils.printArray(testClassNames) - + ": called"); + log.debug("public TestResult start(String[] testClassNames = " + Arrays.toString(testClassNames) + ": called"); // Connect to the broker. connection = TestUtils.createConnection(TestContextProperties.getInstance()); @@ -494,6 +499,9 @@ public class Coordinator extends TKTestRunner log.debug("Wrapped with a WrappedSuiteTestDecorator."); } + // Apply any optional user specified decorators. + targetTest = applyOptionalUserDecorators(targetTest); + // Wrap the tests in a suitable distributed test decorator, to perform the invite/test cycle. targetTest = newTestDecorator(targetTest, enlistedClients, conversationFactory, connection); diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropTestDecorator.java b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropTestDecorator.java index 918f0f9177..00285ab37e 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropTestDecorator.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/InteropTestDecorator.java @@ -50,7 +50,7 @@ import java.util.*; *

Broadcast test invitations and collect enlists. {@link org.apache.qpid.util.ConversationFactory}. *
Output test failures for clients unwilling to run the test case. {@link Coordinator} *
Execute distributed test cases. {@link FrameworkBaseCase} - *
Fail non participating pairings. {@link OptOutTestCase} + *
Fail non-participating pairings. {@link OptOutTestCase} *
*/ public class InteropTestDecorator extends DistributedTestDecorator diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java new file mode 100644 index 0000000000..b011e52ca7 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/distributedtesting/TestClient.java @@ -0,0 +1,497 @@ +/* + * + * 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. + * + */ +package org.apache.qpid.test.framework.distributedtesting; + +import org.apache.log4j.Logger; +import org.apache.log4j.NDC; + +import org.apache.qpid.test.framework.MessagingTestConfigProperties; +import org.apache.qpid.test.framework.TestUtils; +import org.apache.qpid.test.framework.clocksynch.ClockSynchThread; +import org.apache.qpid.test.framework.clocksynch.UDPClockSynchronizer; +import org.apache.qpid.util.ReflectionUtils; +import org.apache.qpid.util.ReflectionUtilsException; + +import uk.co.thebadgerset.junit.extensions.SleepThrottle; +import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; +import uk.co.thebadgerset.junit.extensions.util.TestContextProperties; + +import javax.jms.*; + +import java.util.*; + +/** + * Implements a test client as described in the interop testing spec + * (http://cwiki.apache.org/confluence/display/qpid/Interop+Testing+Specification). A test client is an agent that + * reacts to control message sequences send by the test {@link Coordinator}. + * + *

+ *
Messages Handled by TestClient
Message Action + *
Invite(compulsory) Reply with Enlist. + *
Invite(test case) Reply with Enlist if test case available. + *
AssignRole(test case) Reply with Accept Role if matches an enlisted test. Keep test parameters. + *
Start Send test messages defined by test parameters. Send report on messages sent. + *
Status Request Send report on messages received. + *
Terminate Terminate the test client. + *
ClockSynch Synch clock against the supplied UDP address. + *
+ * + *

+ *
CRC Card
Responsibilities Collaborations + *
Handle all incoming control messages. {@link TestClientControlledTest} + *
Configure and look up test cases by name. {@link TestClientControlledTest} + *
+ */ +public class TestClient implements MessageListener +{ + /** Used for debugging. */ + private static final Logger log = Logger.getLogger(TestClient.class); + + /** Used for reporting to the console. */ + private static final Logger console = Logger.getLogger("CONSOLE"); + + /** Holds the default identifying name of the test client. */ + public static final String CLIENT_NAME = "java"; + + /** Holds the URL of the broker to run the tests on. */ + public static String brokerUrl; + + /** Holds the virtual host to run the tests on. If null, then the default virtual host is used. */ + public static String virtualHost; + + /** + * Holds the test context properties that provides the default test parameters, plus command line overrides. + * This is initialized with the default test parameters, to which command line overrides may be applied. + */ + public static ParsedProperties testContextProperties = + TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); + + /** Holds all the test cases loaded from the classpath. */ + Map testCases = new HashMap(); + + /** Holds the test case currently being run by this client. */ + protected TestClientControlledTest currentTestCase; + + /** Holds the connection to the broker that the test is being coordinated on. */ + protected Connection connection; + + /** Holds the message producer to hold the test coordination over. */ + protected MessageProducer producer; + + /** Holds the JMS controlSession for the test coordination. */ + protected Session session; + + /** Holds the name of this client, with a default value. */ + protected String clientName = CLIENT_NAME; + + /** This flag indicates that the test client should attempt to join the currently running test case on start up. */ + protected boolean join; + + /** Holds the clock synchronizer for the test node. */ + ClockSynchThread clockSynchThread; + + /** + * Creates a new interop test client, listenting to the specified broker and virtual host, with the specified client + * identifying name. + * + * @param pBrokerUrl The url of the broker to connect to. + * @param pVirtualHost The virtual host to conect to. + * @param clientName The client name to use. + * @param join Flag to indicate that this client should attempt to join running tests. + */ + public TestClient(String pBrokerUrl, String pVirtualHost, String clientName, boolean join) + { + log.debug("public TestClient(String pBrokerUrl = " + pBrokerUrl + ", String pVirtualHost = " + pVirtualHost + + ", String clientName = " + clientName + ", boolean join = " + join + "): called"); + + // Retain the connection parameters. + brokerUrl = pBrokerUrl; + virtualHost = pVirtualHost; + this.clientName = clientName; + this.join = join; + } + + /** + * The entry point for the interop test coordinator. This client accepts the following command line arguments: + * + *

+ *
-b The broker URL. Optional. + *
-h The virtual host. Optional. + *
-n The test client name. Optional. + *
name=value Trailing argument define name/value pairs. Added to system properties. Optional. + *
+ * + * @param args The command line arguments. + */ + public static void main(String[] args) + { + log.debug("public static void main(String[] args = " + Arrays.toString(args) + "): called"); + console.info("Qpid Distributed Test Client."); + + // Override the default broker url to be localhost:5672. + testContextProperties.setProperty(MessagingTestConfigProperties.BROKER_PROPNAME, "tcp://localhost:5672"); + + // Use the command line parser to evaluate the command line with standard handling behaviour (print errors + // and usage then exist if there are errors). + // Any options and trailing name=value pairs are also injected into the test context properties object, + // to override any defaults that may have been set up. + ParsedProperties options = + new ParsedProperties(uk.co.thebadgerset.junit.extensions.util.CommandLineParser.processCommandLine(args, + new uk.co.thebadgerset.junit.extensions.util.CommandLineParser( + new String[][] + { + { "b", "The broker URL.", "broker", "false" }, + { "h", "The virtual host to use.", "virtual host", "false" }, + { "o", "The name of the directory to output test timings to.", "dir", "false" }, + { "n", "The name of the test client.", "name", "false" }, + { "j", "Join this test client to running test.", "false" } + }), testContextProperties)); + + // Extract the command line options. + String brokerUrl = options.getProperty("b"); + String virtualHost = options.getProperty("h"); + String clientName = options.getProperty("n"); + clientName = (clientName == null) ? CLIENT_NAME : clientName; + boolean join = options.getPropertyAsBoolean("j"); + + // To distinguish logging output set up an NDC on the client name. + NDC.push(clientName); + + // Create a test client and start it running. + TestClient client = new TestClient(brokerUrl, virtualHost, clientName, join); + + // Use a class path scanner to find all the interop test case implementations. + // Hard code the test classes till the classpath scanner is fixed. + Collection> testCaseClasses = + new ArrayList>(); + // ClasspathScanner.getMatches(TestClientControlledTest.class, "^TestCase.*", true); + testCaseClasses.addAll(loadTestCases("org.apache.qpid.interop.clienttestcases.TestCase1DummyRun", + "org.apache.qpid.interop.clienttestcases.TestCase2BasicP2P", + "org.apache.qpid.interop.clienttestcases.TestCase3BasicPubSub", + "org.apache.qpid.interop.clienttestcases.TestCase4P2PMessageSize", + "org.apache.qpid.interop.clienttestcases.TestCase5PubSubMessageSize", + "org.apache.qpid.test.framework.distributedcircuit.TestClientCircuitEnd")); + + try + { + client.start(testCaseClasses); + } + catch (Exception e) + { + log.error("The test client was unable to start.", e); + console.info(e.getMessage()); + System.exit(1); + } + } + + /** + * Parses a list of class names, and loads them if they are available on the class path. + * + * @param classNames The names of the classes to load. + * + * @return A list of the loaded test case classes. + */ + public static List> loadTestCases(String... classNames) + { + List> testCases = + new LinkedList>(); + + for (String className : classNames) + { + try + { + Class cls = ReflectionUtils.forName(className); + testCases.add((Class) cls); + } + catch (ReflectionUtilsException e) + { + // Ignore, class could not be found, so test not available. + console.warn("Requested class " + className + " cannot be found, ignoring it."); + } + catch (ClassCastException e) + { + // Ignore, class was not of correct type to be a test case. + console.warn("Requested class " + className + " is not an instance of TestClientControlledTest."); + } + } + + return testCases; + } + + /** + * Starts the interop test client running. This causes it to start listening for incoming test invites. + * + * @param testCaseClasses The classes of the available test cases. The test case names from these are used to + * matchin incoming test invites against. + * + * @throws JMSException Any underlying JMSExceptions are allowed to fall through. + */ + protected void start(Collection> testCaseClasses) throws JMSException + { + log.debug("protected void start(Collection> testCaseClasses = " + + testCaseClasses + "): called"); + + // Create all the test case implementations and index them by the test names. + for (Class nextClass : testCaseClasses) + { + try + { + TestClientControlledTest testCase = nextClass.newInstance(); + testCases.put(testCase.getName(), testCase); + } + catch (InstantiationException e) + { + log.warn("Could not instantiate test case class: " + nextClass.getName(), e); + // Ignored. + } + catch (IllegalAccessException e) + { + log.warn("Could not instantiate test case class due to illegal access: " + nextClass.getName(), e); + // Ignored. + } + } + + // Open a connection to communicate with the coordinator on. + connection = TestUtils.createConnection(testContextProperties); + session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + // Set this up to listen for control messages. + Topic privateControlTopic = session.createTopic("iop.control." + clientName); + MessageConsumer consumer = session.createConsumer(privateControlTopic); + consumer.setMessageListener(this); + + Topic controlTopic = session.createTopic("iop.control"); + MessageConsumer consumer2 = session.createConsumer(controlTopic); + consumer2.setMessageListener(this); + + // Create a producer to send replies with. + producer = session.createProducer(null); + + // If the join flag was set, then broadcast a join message to notify the coordinator that a new test client + // is available to join the current test case, if it supports it. This message may be ignored, or it may result + // in this test client receiving a test invite. + if (join) + { + Message joinMessage = session.createMessage(); + + joinMessage.setStringProperty("CONTROL_TYPE", "JOIN"); + joinMessage.setStringProperty("CLIENT_NAME", clientName); + joinMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); + producer.send(controlTopic, joinMessage); + } + + // Start listening for incoming control messages. + connection.start(); + } + + /** + * Handles all incoming control messages. + * + * @param message The incoming message. + */ + public void onMessage(Message message) + { + NDC.push(clientName); + log.debug("public void onMessage(Message message = " + message + "): called"); + + try + { + String controlType = message.getStringProperty("CONTROL_TYPE"); + String testName = message.getStringProperty("TEST_NAME"); + + log.debug("Received control of type '" + controlType + "' for the test '" + testName + "'"); + + // Check if the message is a test invite. + if ("INVITE".equals(controlType)) + { + // Flag used to indicate that an enlist should be sent. Only enlist to compulsory invites or invites + // for which test cases exist. + boolean enlist = false; + + if (testName != null) + { + log.debug("Got an invite to test: " + testName); + + // Check if the requested test case is available. + TestClientControlledTest testCase = testCases.get(testName); + + if (testCase != null) + { + log.debug("Found implementing class for test '" + testName + "', enlisting for it."); + + // Check if the test case will accept the invitation. + enlist = testCase.acceptInvite(message); + + log.debug("The test case " + + (enlist ? " accepted the invite, enlisting for it." + : " did not accept the invite, not enlisting.")); + + // Make the requested test case the current test case. + currentTestCase = testCase; + } + else + { + log.debug("Received an invite to the test '" + testName + "' but this test is not known."); + } + } + else + { + log.debug("Got a compulsory invite, enlisting for it."); + + enlist = true; + } + + if (enlist) + { + // Reply with the client name in an Enlist message. + Message enlistMessage = session.createMessage(); + enlistMessage.setStringProperty("CONTROL_TYPE", "ENLIST"); + enlistMessage.setStringProperty("CLIENT_NAME", clientName); + enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); + enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + log.debug("Sending enlist message '" + enlistMessage + "' to " + message.getJMSReplyTo()); + + producer.send(message.getJMSReplyTo(), enlistMessage); + } + else + { + // Reply with the client name in an Decline message. + Message enlistMessage = session.createMessage(); + enlistMessage.setStringProperty("CONTROL_TYPE", "DECLINE"); + enlistMessage.setStringProperty("CLIENT_NAME", clientName); + enlistMessage.setStringProperty("CLIENT_PRIVATE_CONTROL_KEY", "iop.control." + clientName); + enlistMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + log.debug("Sending decline message '" + enlistMessage + "' to " + message.getJMSReplyTo()); + + producer.send(message.getJMSReplyTo(), enlistMessage); + } + } + else if ("ASSIGN_ROLE".equals(controlType)) + { + // Assign the role to the current test case. + String roleName = message.getStringProperty("ROLE"); + + log.debug("Got a role assignment to role: " + roleName); + + TestClientControlledTest.Roles role = Enum.valueOf(TestClientControlledTest.Roles.class, roleName); + + currentTestCase.assignRole(role, message); + + // Reply by accepting the role in an Accept Role message. + Message acceptRoleMessage = session.createMessage(); + acceptRoleMessage.setStringProperty("CLIENT_NAME", clientName); + acceptRoleMessage.setStringProperty("CONTROL_TYPE", "ACCEPT_ROLE"); + acceptRoleMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + log.debug("Sending accept role message '" + acceptRoleMessage + "' to " + message.getJMSReplyTo()); + + producer.send(message.getJMSReplyTo(), acceptRoleMessage); + } + else if ("START".equals(controlType) || "STATUS_REQUEST".equals(controlType)) + { + if ("START".equals(controlType)) + { + log.debug("Got a start notification."); + + // Extract the number of test messages to send from the start notification. + int numMessages; + + try + { + numMessages = message.getIntProperty("MESSAGE_COUNT"); + } + catch (NumberFormatException e) + { + // If the number of messages is not specified, use the default of one. + numMessages = 1; + } + + // Start the current test case. + currentTestCase.start(numMessages); + } + else + { + log.debug("Got a status request."); + } + + // Generate the report from the test case and reply with it as a Report message. + Message reportMessage = currentTestCase.getReport(session); + reportMessage.setStringProperty("CLIENT_NAME", clientName); + reportMessage.setStringProperty("CONTROL_TYPE", "REPORT"); + reportMessage.setJMSCorrelationID(message.getJMSCorrelationID()); + + log.debug("Sending report message '" + reportMessage + "' to " + message.getJMSReplyTo()); + + producer.send(message.getJMSReplyTo(), reportMessage); + } + else if ("TERMINATE".equals(controlType)) + { + console.info("Received termination instruction from coordinator."); + + // Is a cleaner shutdown needed? + connection.close(); + System.exit(0); + } + else if ("CLOCK_SYNCH".equals(controlType)) + { + log.debug("Received clock synch command."); + String address = message.getStringProperty("ADDRESS"); + + log.debug("address = " + address); + + // Re-create (if necessary) and start the clock synch thread to synch the clock every ten seconds. + if (clockSynchThread != null) + { + clockSynchThread.terminate(); + } + + SleepThrottle throttle = new SleepThrottle(); + throttle.setRate(0.1f); + + clockSynchThread = new ClockSynchThread(new UDPClockSynchronizer(address), throttle); + clockSynchThread.start(); + } + else + { + // Log a warning about this but otherwise ignore it. + log.warn("Got an unknown control message, controlType = " + controlType + ", message = " + message); + } + } + catch (JMSException e) + { + // Log a warning about this, but otherwise ignore it. + log.warn("Got JMSException whilst handling message: " + message, e); + } + // Log any runtimes that fall through this message handler. These are fatal errors for the test client. + catch (RuntimeException e) + { + log.error("The test client message handler got an unhandled exception: ", e); + console.info("The message handler got an unhandled exception, terminating the test client."); + System.exit(1); + } + finally + { + NDC.pop(); + } + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalAMQPPublisherImpl.java b/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalAMQPPublisherImpl.java new file mode 100644 index 0000000000..44a807ed6d --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalAMQPPublisherImpl.java @@ -0,0 +1,133 @@ +/* + * + * 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. + * + */ +package org.apache.qpid.test.framework.localcircuit; + +import org.apache.qpid.client.AMQNoConsumersException; +import org.apache.qpid.client.AMQNoRouteException; +import org.apache.qpid.test.framework.*; + +import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; + +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Session; + +/** + * LocalAMQPPublisherImpl is an extension of {@link LocalPublisherImpl} that adds AMQP specific features. Specifically + * extra assertions for AMQP features not available through generic JMS. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
+ *
+ */ +public class LocalAMQPPublisherImpl extends LocalPublisherImpl implements AMQPPublisher +{ + /** + * Creates a circuit end point on the specified producer, consumer and controlSession. Monitors are also configured + * for messages and exceptions received by the circuit end. + * + * @param producer The message producer for the circuit end point. + * @param consumer The message consumer for the circuit end point. + * @param session The controlSession for the circuit end point. + * @param messageMonitor The monitor to notify of all messages received by the circuit end. + * @param exceptionMonitor The monitor to notify of all exceptions received by the circuit end. + */ + public LocalAMQPPublisherImpl(MessageProducer producer, MessageConsumer consumer, Session session, + MessageMonitor messageMonitor, ExceptionMonitor exceptionMonitor) + { + super(producer, consumer, session, messageMonitor, exceptionMonitor); + } + + /** + * Creates a circuit end point from the producer, consumer and controlSession in a circuit end base implementation. + * + * @param end The circuit end base implementation to take producers and consumers from. + */ + public LocalAMQPPublisherImpl(CircuitEndBase end) + { + super(end); + } + + /** + * Provides an assertion that the publisher got a no consumers exception on every message. + * + * @param testProps The test configuration properties. + * + * @return An assertion that the publisher got a no consumers exception on every message. + */ + public Assertion noConsumersAssertion(ParsedProperties testProps) + { + return new AssertionBase() + { + public boolean apply() + { + boolean passed = true; + ExceptionMonitor connectionExceptionMonitor = circuit.getConnectionExceptionMonitor(); + + if (!connectionExceptionMonitor.assertOneJMSExceptionWithLinkedCause(AMQNoConsumersException.class)) + { + passed = false; + + addError("Was expecting linked exception type " + AMQNoConsumersException.class.getName() + + " on the connection.\n"); + addError((connectionExceptionMonitor.size() > 0) + ? ("Actually got the following exceptions on the connection, " + connectionExceptionMonitor) + : "Got no exceptions on the connection."); + } + + return passed; + } + }; + } + + /** + * Provides an assertion that the publisher got a no rout exception on every message. + * + * @param testProps The test configuration properties. + * + * @return An assertion that the publisher got a no rout exception on every message. + */ + public Assertion noRouteAssertion(ParsedProperties testProps) + { + return new AssertionBase() + { + public boolean apply() + { + boolean passed = true; + ExceptionMonitor connectionExceptionMonitor = circuit.getConnectionExceptionMonitor(); + + if (!connectionExceptionMonitor.assertOneJMSExceptionWithLinkedCause(AMQNoRouteException.class)) + { + passed = false; + + addError("Was expecting linked exception type " + AMQNoRouteException.class.getName() + + " on the connection.\n"); + addError((connectionExceptionMonitor.size() > 0) + ? ("Actually got the following exceptions on the connection, " + connectionExceptionMonitor) + : "Got no exceptions on the connection."); + } + + return passed; + } + }; + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalCircuitImpl.java b/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalCircuitImpl.java index 010669a7c2..9c6220d97f 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalCircuitImpl.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalCircuitImpl.java @@ -22,7 +22,6 @@ package org.apache.qpid.test.framework.localcircuit; import org.apache.log4j.Logger; -import org.apache.qpid.client.AMQSession; import org.apache.qpid.test.framework.*; import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; @@ -31,7 +30,6 @@ import javax.jms.*; import java.util.LinkedList; import java.util.List; -import java.util.concurrent.atomic.AtomicLong; /** * LocalCircuitImpl provides an implementation of the test circuit. This is a local only circuit implementation that @@ -47,7 +45,7 @@ import java.util.concurrent.atomic.AtomicLong; * Apply assertions against the circuits state. {@link Assertion} * Send test messages over the circuit. * Perform the default test procedure on the circuit. - * Provide access to connection and controlSession exception monitors {@link ExceptionMonitor} + * Provide access to connection and controlSession exception monitors. {@link ExceptionMonitor} * */ public class LocalCircuitImpl implements Circuit @@ -55,9 +53,6 @@ public class LocalCircuitImpl implements Circuit /** Used for debugging. */ private static final Logger log = Logger.getLogger(LocalCircuitImpl.class); - /** Used to create unique destination names for each test. */ - private static AtomicLong uniqueDestsId = new AtomicLong(); - /** Holds the test configuration for the circuit. */ private ParsedProperties testProps; @@ -86,7 +81,7 @@ public class LocalCircuitImpl implements Circuit * @param connection The connection. * @param connectionExceptionMonitor The connection exception monitor. */ - protected LocalCircuitImpl(ParsedProperties testProps, LocalPublisherImpl publisher, LocalReceiverImpl receiver, + public LocalCircuitImpl(ParsedProperties testProps, LocalPublisherImpl publisher, LocalReceiverImpl receiver, Connection connection, ExceptionMonitor connectionExceptionMonitor) { this.testProps = testProps; @@ -101,159 +96,6 @@ public class LocalCircuitImpl implements Circuit receiver.setCircuit(this); } - /** - * Creates a local test circuit from the specified test parameters. - * - * @param testProps The test parameters. - * - * @return A connected and ready to start, test circuit. - */ - public static Circuit createCircuit(ParsedProperties testProps) - { - // Create a standard publisher/receivers test client pair on a shared connection, individual sessions. - try - { - // Cast the test properties into a typed interface for convenience. - MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); - - // Get a unique offset to append to destination names to make them unique to the connection. - long uniqueId = uniqueDestsId.incrementAndGet(); - - // Set up the connection. - Connection connection = TestUtils.createConnection(testProps); - - // Add the connection exception listener to assert on exception conditions with. - // ExceptionMonitor exceptionMonitor = new ExceptionMonitor(); - // connection.setExceptionListener(exceptionMonitor); - - // Set up the publisher. - CircuitEndBase publisherEnd = createPublisherCircuitEnd(connection, props, uniqueId); - - // Set up the receiver. - CircuitEndBase receiverEnd = createReceiverCircuitEnd(connection, props, uniqueId); - - // Start listening for incoming messages. - connection.start(); - - // Package everything up. - LocalPublisherImpl publisher = new LocalPublisherImpl(publisherEnd); - LocalReceiverImpl receiver = new LocalReceiverImpl(receiverEnd); - - return new LocalCircuitImpl(testProps, publisher, receiver, connection, publisher.getExceptionMonitor()); - } - catch (JMSException e) - { - throw new RuntimeException("Could not create publisher/receivers pair due to a JMSException.", e); - } - } - - /** - * Builds a circuit end suitable for the publishing side of a test circuit, from standard test parameters. - * - * @param connection The connection to build the circuit end on. - * @param testProps The test parameters to configure the circuit end construction. - * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique. - * - * @return A circuit end suitable for the publishing side of a test circuit. - * - * @throws JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation. - */ - public static CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId) - throws JMSException - { - log.debug( - "public static CircuitEndBase createPublisherCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = " - + uniqueId + "): called"); - - // Cast the test properties into a typed interface for convenience. - MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); - - Session session = connection.createSession(props.getTransacted(), props.getAckMode()); - - Destination destination = - props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId) - : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId); - - MessageProducer producer = - props.getPublisherProducerBind() - ? ((props.getImmediate() | props.getMandatory()) - ? ((AMQSession) session).createProducer(destination, props.getMandatory(), props.getImmediate()) - : session.createProducer(destination)) : null; - - MessageConsumer consumer = - props.getPublisherConsumerBind() - ? session.createConsumer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null; - - MessageMonitor messageMonitor = new MessageMonitor(); - - if (consumer != null) - { - consumer.setMessageListener(messageMonitor); - } - - ExceptionMonitor exceptionMonitor = new ExceptionMonitor(); - connection.setExceptionListener(exceptionMonitor); - - if (!props.getPublisherConsumerActive() && (consumer != null)) - { - consumer.close(); - } - - return new CircuitEndBase(producer, consumer, session, messageMonitor, exceptionMonitor); - } - - /** - * Builds a circuit end suitable for the receiving side of a test circuit, from standard test parameters. - * - * @param connection The connection to build the circuit end on. - * @param testProps The test parameters to configure the circuit end construction. - * @param uniqueId A unique number to being numbering destinations from, to make this circuit unique. - * - * @return A circuit end suitable for the receiving side of a test circuit. - * - * @throws JMSException Any underlying JMSExceptions are allowed to fall through and fail the creation. - */ - public static CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId) - throws JMSException - { - log.debug( - "public static CircuitEndBase createReceiverCircuitEnd(Connection connection, ParsedProperties testProps, long uniqueId = " - + uniqueId + "): called"); - - // Cast the test properties into a typed interface for convenience. - MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); - - Session session = connection.createSession(props.getTransacted(), props.getAckMode()); - - MessageProducer producer = - props.getReceiverProducerBind() - ? session.createProducer(session.createQueue(props.getReceiveDestinationNameRoot() + "_" + uniqueId)) : null; - - Destination destination = - props.getPubsub() ? session.createTopic(props.getSendDestinationNameRoot() + "_" + uniqueId) - : session.createQueue(props.getSendDestinationNameRoot() + "_" + uniqueId); - - MessageConsumer consumer = - props.getReceiverConsumerBind() - ? ((props.getDurableSubscription() && props.getPubsub()) - ? session.createDurableSubscriber((Topic) destination, "testsub") : session.createConsumer(destination)) - : null; - - MessageMonitor messageMonitor = new MessageMonitor(); - - if (consumer != null) - { - consumer.setMessageListener(messageMonitor); - } - - if (!props.getReceiverConsumerActive() && (consumer != null)) - { - consumer.close(); - } - - return new CircuitEndBase(producer, consumer, session, messageMonitor, null); - } - /** * Gets the interface on the publishing end of the circuit. * @@ -351,16 +193,24 @@ public class LocalCircuitImpl implements Circuit */ protected void send() { - boolean transactional = testProps.getPropertyAsBoolean(MessagingTestConfigProperties.TRANSACTED_PROPNAME); + // Cast the test properties into a typed interface for convenience. + MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); - // Send an immediate message through the publisher and ensure that it results in a JMSException. + boolean transactional = props.getPublisherTransacted(); + boolean rollback = props.getRollbackPublisher(); + + // Send a message through the publisher and log any exceptions raised. try { CircuitEnd end = getLocalPublisherCircuitEnd(); end.send(createTestMessage(end)); - if (transactional) + if (rollback) + { + end.getSession().rollback(); + } + else if (transactional) { end.getSession().commit(); } @@ -427,7 +277,10 @@ public class LocalCircuitImpl implements Circuit */ private Message createTestMessage(CircuitEnd client) throws JMSException { - return client.getSession().createTextMessage("Hello"); + // Cast the test properties into a typed interface for convenience. + MessagingTestConfigProperties props = new MessagingTestConfigProperties(testProps); + + return TestUtils.createTestMessageOfSize(client.getSession(), props.getMessageSize()); } /** diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalPublisherImpl.java b/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalPublisherImpl.java index 93a137dffa..bd15839e41 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalPublisherImpl.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalPublisherImpl.java @@ -20,10 +20,10 @@ */ package org.apache.qpid.test.framework.localcircuit; -import org.apache.qpid.client.AMQNoConsumersException; -import org.apache.qpid.client.AMQNoRouteException; import org.apache.qpid.test.framework.*; +import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; + import javax.jms.MessageConsumer; import javax.jms.MessageProducer; import javax.jms.Session; @@ -31,7 +31,7 @@ import javax.jms.Session; /** * Provides an implementation of the {@link Publisher} interface and wraps a single message producer and consumer on * a single controlSession, as a {@link CircuitEnd}. A local publisher also acts as a circuit end, because for a locally - * located circuit the assertions may be applied directly, there does not need to be any inter process messaging + * located circuit the assertions may be applied directly, there does not need to be any inter-process messaging * between the publisher and its single circuit end, in order to ascertain its status. * *

@@ -46,14 +46,17 @@ import javax.jms.Session; public class LocalPublisherImpl extends CircuitEndBase implements Publisher { /** Holds a reference to the containing circuit. */ - private LocalCircuitImpl circuit; + protected LocalCircuitImpl circuit; /** - * Creates a circuit end point on the specified producer, consumer and controlSession. + * Creates a circuit end point on the specified producer, consumer and controlSession. Monitors are also configured + * for messages and exceptions received by the circuit end. * * @param producer The message producer for the circuit end point. * @param consumer The message consumer for the circuit end point. * @param session The controlSession for the circuit end point. + * @param messageMonitor The monitor to notify of all messages received by the circuit end. + * @param exceptionMonitor The monitor to notify of all exceptions received by the circuit end. */ public LocalPublisherImpl(MessageProducer producer, MessageConsumer consumer, Session session, MessageMonitor messageMonitor, ExceptionMonitor exceptionMonitor) @@ -74,9 +77,11 @@ public class LocalPublisherImpl extends CircuitEndBase implements Publisher /** * Provides an assertion that the publisher encountered no exceptions. * + * @param testProps + * * @return An assertion that the publisher encountered no exceptions. */ - public Assertion noExceptionsAssertion() + public Assertion noExceptionsAssertion(ParsedProperties testProps) { return new AssertionBase() { @@ -109,41 +114,26 @@ public class LocalPublisherImpl extends CircuitEndBase implements Publisher } /** - * Provides an assertion that the publisher got a no consumers exception on every message. + * Provides an assertion that the AMQP channel was forcibly closed by an error condition. + * + * @param testProps The test configuration properties. * - * @return An assertion that the publisher got a no consumers exception on every message. + * @return An assertion that the AMQP channel was forcibly closed by an error condition. */ - public Assertion noConsumersAssertion() + public Assertion channelClosedAssertion(ParsedProperties testProps) { - return new AssertionBase() - { - public boolean apply() - { - boolean passed = true; - ExceptionMonitor connectionExceptionMonitor = circuit.getConnectionExceptionMonitor(); - - if (!connectionExceptionMonitor.assertOneJMSExceptionWithLinkedCause(AMQNoConsumersException.class)) - { - passed = false; - - addError("Was expecting linked exception type " + AMQNoConsumersException.class.getName() - + " on the connection.\n"); - addError((connectionExceptionMonitor.size() > 0) - ? ("Actually got the following exceptions on the connection, " + connectionExceptionMonitor) - : "Got no exceptions on the connection."); - } - - return passed; - } - }; + return new NotApplicableAssertion(testProps); } /** - * Provides an assertion that the publisher got a no rout exception on every message. + * Provides an assertion that the publisher got a given exception during the test. + * + * @param testProps The test configuration properties. + * @param exceptionClass The exception class to check for. * - * @return An assertion that the publisher got a no rout exception on every message. + * @return An assertion that the publisher got a given exception during the test. */ - public Assertion noRouteAssertion() + public Assertion exceptionAssertion(ParsedProperties testProps, final Class exceptionClass) { return new AssertionBase() { @@ -152,11 +142,11 @@ public class LocalPublisherImpl extends CircuitEndBase implements Publisher boolean passed = true; ExceptionMonitor connectionExceptionMonitor = circuit.getConnectionExceptionMonitor(); - if (!connectionExceptionMonitor.assertOneJMSExceptionWithLinkedCause(AMQNoRouteException.class)) + if (!connectionExceptionMonitor.assertExceptionOfType(exceptionClass)) { passed = false; - addError("Was expecting linked exception type " + AMQNoRouteException.class.getName() + addError("Was expecting linked exception type " + exceptionClass.getName() + " on the connection.\n"); addError((connectionExceptionMonitor.size() > 0) ? ("Actually got the following exceptions on the connection, " + connectionExceptionMonitor) diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalReceiverImpl.java b/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalReceiverImpl.java index 1b5acf92de..9a86c00a1b 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalReceiverImpl.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/localcircuit/LocalReceiverImpl.java @@ -22,6 +22,8 @@ package org.apache.qpid.test.framework.localcircuit; import org.apache.qpid.test.framework.*; +import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; + import javax.jms.MessageConsumer; import javax.jms.MessageProducer; import javax.jms.Session; @@ -46,11 +48,14 @@ public class LocalReceiverImpl extends CircuitEndBase implements Receiver private LocalCircuitImpl circuit; /** - * Creates a circuit end point on the specified producer, consumer and controlSession. + * Creates a circuit end point on the specified producer, consumer and controlSession. Monitors are also configured + * for messages and exceptions received by the circuit end. * * @param producer The message producer for the circuit end point. * @param consumer The message consumer for the circuit end point. * @param session The controlSession for the circuit end point. + * @param messageMonitor The monitor to notify of all messages received by the circuit end. + * @param exceptionMonitor The monitor to notify of all exceptions received by the circuit end. */ public LocalReceiverImpl(MessageProducer producer, MessageConsumer consumer, Session session, MessageMonitor messageMonitor, ExceptionMonitor exceptionMonitor) @@ -71,21 +76,60 @@ public class LocalReceiverImpl extends CircuitEndBase implements Receiver /** * Provides an assertion that the receivers encountered no exceptions. * + * @param testProps The test configuration properties. + * * @return An assertion that the receivers encountered no exceptions. */ - public Assertion noExceptionsAssertion() + public Assertion noExceptionsAssertion(ParsedProperties testProps) + { + return new NotApplicableAssertion(testProps); + } + + /** + * Provides an assertion that the AMQP channel was forcibly closed by an error condition. + * + * @param testProps The test configuration properties. + * + * @return An assertion that the AMQP channel was forcibly closed by an error condition. + */ + public Assertion channelClosedAssertion(ParsedProperties testProps) { - return null; + return new NotApplicableAssertion(testProps); } /** * Provides an assertion that the receivers got all messages that were sent to it. * + * @param testProps The test configuration properties. + * * @return An assertion that the receivers got all messages that were sent to it. */ - public Assertion allMessagesAssertion() + public Assertion allMessagesReceivedAssertion(ParsedProperties testProps) + { + return new NotApplicableAssertion(testProps); + } + + /** + * Provides an assertion that the receivers got none of the messages that were sent to it. + * + * @param testProps The test configuration properties. + * + * @return An assertion that the receivers got none of the messages that were sent to it. + */ + public Assertion noMessagesReceivedAssertion(ParsedProperties testProps) + { + return new NotApplicableAssertion(testProps); + } + + /** + * Provides an assertion that the receiver got a given exception during the test. + * + * @param testProps The test configuration properties. + * @param exceptionClass The exception class to check for. @return An assertion that the receiver got a given exception during the test. + */ + public Assertion exceptionAssertion(ParsedProperties testProps, Class exceptionClass) { - return null; + return new NotApplicableAssertion(testProps); } /** diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/AMQPFeatureDecorator.java b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/AMQPFeatureDecorator.java new file mode 100644 index 0000000000..cb487a315b --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/AMQPFeatureDecorator.java @@ -0,0 +1,83 @@ +/* + * + * 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. + * + */ +package org.apache.qpid.test.framework.qpid; + +import junit.framework.Test; +import junit.framework.TestResult; + +import org.apache.qpid.test.framework.FrameworkBaseCase; +import org.apache.qpid.test.framework.LocalAMQPCircuitFactory; + +import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; + +/** + * AMQPFeatureDecorator applies decorations to {@link FrameworkBaseCase} tests, so that they may use Qpid/AMQP specific + * features, not available through JMS. For example, the immediate and mandatory flags. This decorator replaces the + * standard test circuit factory on the base class with one that allows these features to be used. + * + *

CRC Card
+ *
CRC Card
Responsibilities Collaborations + *
Substitute the circuit factory with an AMQP/Qpid specific one. + *
+ * + * @todo This wrapper substitutes in a LocalAMQPCircuitFactory, which is fine for local tests. For distributed tests + * the Fanout or Interop factories are substituted in by their decorators instead. These actually use + * distributed circuit static create methods to build the circuits, which should actually be changed to a factory, + * so that static methods do not need to be used. The distributed circuit creater delegates the circuit + * construction to remote test nodes. This decorator should not be used with distributed tests, or should be made + * aware of them, in which case it might ensure that an AMQP feature (implied already by other properties) flag + * is passed out to the remote test nodes, and provide a mechansim for them to decorate their circuit creation + * with AMQP features too. Add factory substituion/decoration mechansim for test clients, here or in a seperate + * class. + */ +public class AMQPFeatureDecorator extends WrappedSuiteTestDecorator +{ + /** The test suite to run. */ + private Test test; + + /** + * Creates a wrapped test test decorator from another one. + * + * @param test The test test. + */ + public AMQPFeatureDecorator(WrappedSuiteTestDecorator test) + { + super(test); + this.test = test; + } + + /** + * Runs the tests with a LocalAMQPCircuitFactory. + * + * @param testResult The the results object to monitor the test results with. + */ + public void run(TestResult testResult) + { + for (Test test : getAllUnderlyingTests()) + { + FrameworkBaseCase frameworkTest = (FrameworkBaseCase) test; + frameworkTest.setCircuitFactory(new LocalAMQPCircuitFactory()); + } + + // Run the test. + test.run(testResult); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/InVMBrokerDecorator.java b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/InVMBrokerDecorator.java new file mode 100644 index 0000000000..8b21ec1144 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/qpid/InVMBrokerDecorator.java @@ -0,0 +1,92 @@ +/* + * + * 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. + * + */ +package org.apache.qpid.test.framework.qpid; + +import junit.framework.Test; +import junit.framework.TestResult; +import junit.framework.TestSuite; + +import org.apache.qpid.client.transport.TransportConnection; +import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.test.framework.FrameworkBaseCase; + +import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator; + +/** + * InVMBrokerDecorator is a test decorator, that is activated when running tests against an in-vm broker only. Its + * purpose is to automatically create, and close and delete an in-vm broker, during the set-up and tear-down of + * each test case. This decorator may only be used in conjunction with tests that extend {@link FrameworkBaseCase}. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Create/Destroy an in-vm broker on every test run. + *
+ * + * @todo May need to add a more fine grained injection point for the in-vm broker management, as this may act at the + * suite level, rather than the individual test level. + */ +public class InVMBrokerDecorator extends WrappedSuiteTestDecorator +{ + /** The test suite to run. */ + private Test test; + + /** + * Creates a wrapped test suite decorator from another one. + * + * @param test The test suite. + */ + public InVMBrokerDecorator(WrappedSuiteTestDecorator test) + { + super(test); + this.test = test; + } + + /** + * Runs the tests with in-vm broker management. + * + * @param testResult The the results object to monitor the test results with. + */ + public void run(TestResult testResult) + { + /*for (Test test : getAllUnderlyingTests()) + { + FrameworkBaseCase frameworkTest = (FrameworkBaseCase) test; + }*/ + + // Ensure that the in-vm broker is created. + try + { + TransportConnection.createVMBroker(1); + } + catch (AMQVMBrokerCreationException e) + { + throw new RuntimeException("In-VM broker creation failed: " + e.getMessage(), e); + } + + // Run the test. + test.run(testResult); + + // Ensure that the in-vm broker is cleaned up so that the next test starts afresh. + TransportConnection.killVMBroker(1); + ApplicationRegistry.remove(1); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/BaseCircuitFactory.java b/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/BaseCircuitFactory.java index 555071760e..d323563582 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/BaseCircuitFactory.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/BaseCircuitFactory.java @@ -31,13 +31,20 @@ import java.util.List; import java.util.Properties; /** + * BaseCircuitFactory provides some functionality common to all {@link CircuitFactory}s, such as the details of + * all {@link org.apache.qpid.test.framework.distributedtesting.TestClient}s that make up the end-points of + * the circuits that the factory creates, and an active {@link ConversationFactory} that can be used to generate + * control conversations with those circuit end-points. + * *

*
CRC Card
Responsibilities Collaborations - *
+ *
Hold the details of the sending and receiving end-points to create circuits from. + *
Provide a conversation factory to create control conversations with the end-points. *
*/ public abstract class BaseCircuitFactory implements CircuitFactory { + /** Used for debugging. */ private final Logger log = Logger.getLogger(BaseCircuitFactory.class); diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/CircuitFactory.java b/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/CircuitFactory.java index 82caa20d79..ade96427ac 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/CircuitFactory.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/CircuitFactory.java @@ -35,7 +35,7 @@ import java.util.Map; import java.util.Properties; /** - * A TestCaseSequence is responsibile for creating test circuits appropriate to the context that a test case is + * A CircuitFactory is responsibile for creating test circuits appropriate to the context that a test case is * running in, and providing an implementation of a standard test procedure over a test circuit. * *

@@ -43,12 +43,6 @@ import java.util.Properties; *
CRC Card
Provide a standard test procedure over a test circuit. *
Construct test circuits appropriate to a tests context. *
- * - * @todo The sequence test method is deprecated, in favour of using test circuits instead. This interface might be - * better renamed to somethign like CircuitFactory, also the split between this interface and - * DistributedTestSequencer could be removed and DistributedTestCase functionality merged into FrameworkBaseCase. - * This is so that any test case written on top of the framework base case can be distributed, without having - * to extend a different base test class. */ public interface CircuitFactory { diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/FanOutCircuitFactory.java b/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/FanOutCircuitFactory.java index 673b47df03..ff4ee741ad 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/FanOutCircuitFactory.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/FanOutCircuitFactory.java @@ -46,7 +46,7 @@ import java.util.Properties; * *

*
CRC Card
Responsibilities Collaborations - *
+ *
Create distributed circuits from one to many test nodes, for fanout style testing. *
* * @todo Adapt this to be an n*m topology circuit factory. Need to add circuit topology definitions to the test @@ -57,7 +57,7 @@ import java.util.Properties; * * @todo The createCircuit methods on this and InteropCircuitFactory are going to be identical. This is because the * partitioning into senders and receivers is already done by the test decorators. Either eliminate these factories - * as unnesesary, or move the partitioning functionaility into the factories, in which case the test decorators + * as unnesesary, or move the partitioning functionality into the factories, in which case the test decorators * can probably be merged or eliminated. There is confusion over the placement of responsibilities between the * factories and the test decorators... although the test decorators may well do more than just circuit creation * in the future. For example, there may have to be a special decorator for test repetition that does one circuit diff --git a/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/InteropCircuitFactory.java b/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/InteropCircuitFactory.java index 35bcf78b72..4c56a68d8f 100644 --- a/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/InteropCircuitFactory.java +++ b/java/systests/src/main/java/org/apache/qpid/test/framework/sequencers/InteropCircuitFactory.java @@ -41,10 +41,20 @@ import java.util.List; import java.util.Properties; /** + * InteropCircuitFactory is a circuit factory that creates distributed test circuits. Given a set of participating + * test client nodes, it assigns one node to the SENDER role and one the RECEIVER role. + * *

*
CRC Card
Responsibilities Collaborations - *
+ *
Create distributed circuits from pairs of test nodes, for interop style testing. *
+ * + * @todo The partitioning of a set of nodes into sender and receiver roles is actually done by the interop test + * decorator. See the todo comment in FanOutCircuitFactory about merging the factories with the decorators, or + * more carefully dividing up responsibilities between them. + * + * @todo The squenceTest code is deprecated, but currently still used by the interop tests. It will be removed once it + * have been fully replaced by the default test procedure. */ public class InteropCircuitFactory extends BaseCircuitFactory { diff --git a/java/systests/src/main/java/org/apache/qpid/test/testcases/ImmediateMessageTest.java b/java/systests/src/main/java/org/apache/qpid/test/testcases/ImmediateMessageTest.java new file mode 100644 index 0000000000..e82a7927b6 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/test/testcases/ImmediateMessageTest.java @@ -0,0 +1,307 @@ +/* + * + * 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. + * + */ +package org.apache.qpid.test.testcases; + +import org.apache.qpid.test.framework.AMQPPublisher; +import org.apache.qpid.test.framework.Circuit; +import org.apache.qpid.test.framework.FrameworkBaseCase; +import org.apache.qpid.test.framework.MessagingTestConfigProperties; +import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*; +import org.apache.qpid.test.framework.sequencers.CircuitFactory; + +import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; +import uk.co.thebadgerset.junit.extensions.util.TestContextProperties; + +/** + * ImmediateMessageTest tests for the desired behaviour of immediate messages. Immediate messages are a non-JMS + * feature. A message may be marked with an immediate delivery flag, which means that a consumer must be connected + * to receive the message, through a valid route, when it is sent, or when its transaction is committed in the case + * of transactional messaging. If this is not the case, the broker should return the message with a NO_CONSUMERS code. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Check that an immediate message is sent succesfully not using transactions when a consumer is connected. + *
Check that an immediate message is committed succesfully in a transaction when a consumer is connected. + *
Check that an immediate message results in no consumers code, not using transactions, when a consumer is + * disconnected. + *
Check that an immediate message results in no consumers code, in a transaction, when a consumer is + * disconnected. + *
Check that an immediate message results in no route code, not using transactions, when no outgoing route is + * connected. + *
Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is + * connected. + *
Check that an immediate message is sent succesfully not using transactions when a consumer is connected. + *
Check that an immediate message is committed succesfully in a transaction when a consumer is connected. + *
Check that an immediate message results in no consumers code, not using transactions, when a consumer is + * disconnected. + *
Check that an immediate message results in no consumers code, in a transaction, when a consumer is + * disconnected. + *
Check that an immediate message results in no route code, not using transactions, when no outgoing route is + * connected. + *
Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is + * connected. + *
+ * + * @todo All of these test cases will be generated by a test generator that thoroughly tests all combinations of test + * circuits. + */ +public class ImmediateMessageTest extends FrameworkBaseCase +{ + /** Used to read the tests configurable properties through. */ + ParsedProperties testProps; + + /** + * Creates a new test case with the specified name. + * + * @param name The test case name. + */ + public ImmediateMessageTest(String name) + { + super(name); + } + + /** Check that an immediate message is sent succesfully not using transactions when a consumer is connected. */ + public void test_QPID_517_ImmediateOkNoTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion(testProps)))); + } + + /** Check that an immediate message is committed succesfully in a transaction when a consumer is connected. */ + public void test_QPID_517_ImmediateOkTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Send one message with no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().noExceptionsAssertion(testProps)))); + } + + /** Check that an immediate message results in no consumers code, not using transactions, when a consumer is disconnected. */ + public void test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message and get a linked no consumers exception. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noConsumersAssertion(testProps)))); + } + + /** Check that an immediate message results in no consumers code, in a transaction, when a consumer is disconnected. */ + public void test_QPID_517_ImmediateFailsConsumerDisconnectedTxP2P() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message and get a linked no consumers exception. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noConsumersAssertion(testProps)))); + } + + /** Check that an immediate message results in no route code, not using transactions, when no outgoing route is connected. */ + public void test_QPID_517_ImmediateFailsNoRouteNoTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + /** Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is connected. */ + public void test_QPID_517_ImmediateFailsNoRouteTxP2P() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + /** Check that an immediate message is sent succesfully not using transactions when a consumer is connected. */ + public void test_QPID_517_ImmediateOkNoTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Send one message with no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** Check that an immediate message is committed succesfully in a transaction when a consumer is connected. */ + public void test_QPID_517_ImmediateOkTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Send one message with no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** Check that an immediate message results in no consumers code, not using transactions, when a consumer is disconnected. */ + public void test_QPID_517_ImmediateFailsConsumerDisconnectedNoTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Use durable subscriptions, so that the route remains open with no subscribers. + testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message and get a linked no consumers exception. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noConsumersAssertion(testProps)))); + } + + /** Check that an immediate message results in no consumers code, in a transaction, when a consumer is disconnected. */ + public void test_QPID_517_ImmediateFailsConsumerDisconnectedTxPubSub() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Use durable subscriptions, so that the route remains open with no subscribers. + testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message and get a linked no consumers exception. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noConsumersAssertion(testProps)))); + } + + /** Check that an immediate message results in no route code, not using transactions, when no outgoing route is connected. */ + public void test_QPID_517_ImmediateFailsNoRouteNoTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + /** Check that an immediate message results in no route code, upon transaction commit, when no outgoing route is connected. */ + public void test_QPID_517_ImmediateFailsNoRouteTxPubSub() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + protected void setUp() throws Exception + { + super.setUp(); + + testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); + + /** All these tests should have the immediate flag on. */ + testProps.setProperty(IMMEDIATE_PROPNAME, true); + testProps.setProperty(MANDATORY_PROPNAME, false); + + /** Bind the receivers consumer by default. */ + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, true); + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, true); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/testcases/MandatoryMessageTest.java b/java/systests/src/main/java/org/apache/qpid/test/testcases/MandatoryMessageTest.java new file mode 100644 index 0000000000..7391bf23d2 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/test/testcases/MandatoryMessageTest.java @@ -0,0 +1,321 @@ +/* + * + * 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. + * + */ +package org.apache.qpid.test.testcases; + +import org.apache.qpid.test.framework.AMQPPublisher; +import org.apache.qpid.test.framework.Circuit; +import org.apache.qpid.test.framework.FrameworkBaseCase; +import org.apache.qpid.test.framework.MessagingTestConfigProperties; +import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*; +import org.apache.qpid.test.framework.sequencers.CircuitFactory; + +import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; +import uk.co.thebadgerset.junit.extensions.util.TestContextProperties; + +/** + * MandatoryMessageTest tests for the desired behaviour of mandatory messages. Mandatory messages are a non-JMS + * feature. A message may be marked with a mandatory delivery flag, which means that a valid route for the message + * must exist, when it is sent, or when its transaction is committed in the case of transactional messaging. If this + * is not the case, the broker should return the message with a NO_CONSUMERS code. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. + *
Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. + *
Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected + * but the route exists. + *
Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but + * the route exists. + *
Check that an mandatory message results in no route code, not using transactions, when no consumer is + * connected. + *
Check that an mandatory message results in no route code, upon transaction commit, when a consumer is + * connected. + *
Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. + *
Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. + *
Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected + * but the route exists. + *
Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but + * the route exists. + *
Check that an mandatory message results in no route code, not using transactions, when no consumer is + * connected. + *
Check that an mandatory message results in no route code, upon transaction commit, when a consumer is + * connected. + *
+ * + * @todo All of these test cases will be generated by a test generator that thoroughly tests all combinations of test + * circuits. + */ +public class MandatoryMessageTest extends FrameworkBaseCase +{ + /** Used to read the tests configurable properties through. */ + ParsedProperties testProps; + + /** + * Creates a new test case with the specified name. + * + * @param name The test case name. + */ + public MandatoryMessageTest(String name) + { + super(name); + } + + /** Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. */ + public void test_QPID_508_MandatoryOkNoTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. */ + public void test_QPID_508_MandatoryOkTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** + * Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected but + * the route exists. + */ + public void test_QPID_517_MandatoryOkConsumerDisconnectedNoTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message with no errors. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** + * Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but + * the route exists. + */ + public void test_QPID_517_MandatoryOkConsumerDisconnectedTxP2P() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message with no errors. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** Check that an mandatory message results in no route code, not using transactions, when no consumer is connected. */ + public void test_QPID_508_MandatoryFailsNoRouteNoTxP2P() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + /** Check that an mandatory message results in no route code, upon transaction commit, when a consumer is connected. */ + public void test_QPID_508_MandatoryFailsNoRouteTxP2P() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, false); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + /** Check that an mandatory message is sent succesfully not using transactions when a consumer is connected. */ + public void test_QPID_508_MandatoryOkNoTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** Check that an mandatory message is committed succesfully in a transaction when a consumer is connected. */ + public void test_QPID_508_MandatoryOkTxPubSub() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** + * Check that a mandatory message is sent succesfully, not using transactions, when a consumer is disconnected but + * the route exists. + */ + public void test_QPID_517_MandatoryOkConsumerDisconnectedNoTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Use durable subscriptions, so that the route remains open with no subscribers. + testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message with no errors. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** + * Check that a mandatory message is sent succesfully, in a transaction, when a consumer is disconnected but + * the route exists. + */ + public void test_QPID_517_MandatoryOkConsumerDisconnectedTxPubSub() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Use durable subscriptions, so that the route remains open with no subscribers. + testProps.setProperty(DURABLE_SUBSCRIPTION_PROPNAME, true); + + // Disconnect the consumer. + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, false); + + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + // Send one message with no errors. + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noExceptionsAssertion(testProps)))); + } + + /** Check that an mandatory message results in no route code, not using transactions, when no consumer is connected. */ + public void test_QPID_508_MandatoryFailsNoRouteNoTxPubSub() + { + // Ensure transactional sessions are off. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + /** Check that an mandatory message results in no route code, upon transaction commit, when a consumer is connected. */ + public void test_QPID_508_MandatoryFailsNoRouteTxPubSub() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(PUBSUB_PROPNAME, true); + + // Set up the messaging topology so that only the publishers producer is bound (do not set up the receivers to + // collect its messages). + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, false); + + // Send one message and get a linked no route exception. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(((AMQPPublisher) testCircuit.getPublisher()).noRouteAssertion(testProps)))); + } + + protected void setUp() throws Exception + { + super.setUp(); + + testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); + + /** All these tests should have the mandatory flag on. */ + testProps.setProperty(IMMEDIATE_PROPNAME, false); + testProps.setProperty(MANDATORY_PROPNAME, true); + + /** Bind the receivers consumer by default. */ + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, true); + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, true); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/test/testcases/RollbackTest.java b/java/systests/src/main/java/org/apache/qpid/test/testcases/RollbackTest.java new file mode 100644 index 0000000000..163a6a67f6 --- /dev/null +++ b/java/systests/src/main/java/org/apache/qpid/test/testcases/RollbackTest.java @@ -0,0 +1,132 @@ +/* + * + * 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. + * + */ +package org.apache.qpid.test.testcases; + +import org.apache.qpid.test.framework.Circuit; +import org.apache.qpid.test.framework.FrameworkBaseCase; +import org.apache.qpid.test.framework.MessagingTestConfigProperties; +import static org.apache.qpid.test.framework.MessagingTestConfigProperties.*; +import org.apache.qpid.test.framework.sequencers.CircuitFactory; + +import uk.co.thebadgerset.junit.extensions.util.ParsedProperties; +import uk.co.thebadgerset.junit.extensions.util.TestContextProperties; + +/** + * RollbackTest tests the rollback ability of transactional messaging. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Check messages sent but rolled back are never received. + *
Check messages received but rolled back are redelivered on subsequent receives. + *
Attempting to rollback outside of a transaction results in an IllegalStateException. + *
+ */ +public class RollbackTest extends FrameworkBaseCase +{ + /** Used to read the tests configurable properties through. */ + ParsedProperties testProps; + + /** + * Creates a new test case with the specified name. + * + * @param name The test case name. + */ + public RollbackTest(String name) + { + super(name); + } + + /** Check messages sent but rolled back are never received. */ + public void testRolledbackMessageNotDelivered() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, true); + testProps.setProperty(ROLLBACK_PUBLISHER_PROPNAME, true); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(testCircuit.getPublisher().noExceptionsAssertion(testProps), + testCircuit.getReceiver().noMessagesReceivedAssertion(testProps)))); + } + + /** Check messages received but rolled back are redelivered on subsequent receives. */ + public void testRolledbackMessagesSubsequentlyReceived() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_RECEIVER_PROPNAME, true); + testProps.setProperty(ROLLBACK_RECEIVER_PROPNAME, true); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, + assertionList(testCircuit.getPublisher().noExceptionsAssertion(testProps), + testCircuit.getReceiver().allMessagesReceivedAssertion(testProps)))); + } + + /** Attempting to rollback outside of a transaction results in an IllegalStateException. */ + public void testRollbackUnavailableOutsideTransactionPublisher() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_PUBLISHER_PROPNAME, false); + testProps.setProperty(ROLLBACK_PUBLISHER_PROPNAME, true); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getPublisher().channelClosedAssertion(testProps)))); + } + + /** Attempting to rollback outside of a transaction results in an IllegalStateException. */ + public void testRollbackUnavailableOutsideTransactionReceiver() + { + // Ensure transactional sessions are on. + testProps.setProperty(TRANSACTED_RECEIVER_PROPNAME, false); + testProps.setProperty(ROLLBACK_RECEIVER_PROPNAME, true); + + // Run the default test sequence over the test circuit checking for no errors. + CircuitFactory circuitFactory = getCircuitFactory(); + Circuit testCircuit = circuitFactory.createCircuit(testProps); + + assertNoFailures(testCircuit.test(1, assertionList(testCircuit.getReceiver().channelClosedAssertion(testProps)))); + } + + /** + * Sets up all tests to have an active outward route and consumer by default. + * + * @throws Exception Any exceptions are allowed to fall through and fail the test. + */ + protected void setUp() throws Exception + { + super.setUp(); + + testProps = TestContextProperties.getInstance(MessagingTestConfigProperties.defaults); + + /** Bind the receivers consumer by default. */ + testProps.setProperty(RECEIVER_CONSUMER_BIND_PROPNAME, true); + testProps.setProperty(RECEIVER_CONSUMER_ACTIVE_PROPNAME, true); + } +} diff --git a/java/systests/src/main/java/org/apache/qpid/util/ConversationFactory.java b/java/systests/src/main/java/org/apache/qpid/util/ConversationFactory.java index a325e7025e..d1ba96dc71 100644 --- a/java/systests/src/main/java/org/apache/qpid/util/ConversationFactory.java +++ b/java/systests/src/main/java/org/apache/qpid/util/ConversationFactory.java @@ -92,7 +92,7 @@ import java.util.concurrent.atomic.AtomicLong; * *

*
CRC Card
Responsibilities Collaborations - *
Associate messages to an ongoing conversation using correlation ids. + *
Associate messages to an ongoing conversation using correlation ids. *
Auto manage sessions for conversations. *
Store messages not in a conversation in dead letter box. *
-- cgit v1.2.1