You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by rg...@apache.org on 2007/03/20 15:59:08 UTC

svn commit: r520415 - in /incubator/qpid/branches/M2/java/integrationtests: ./ src/main/java/org/apache/qpid/interop/coordinator/ src/main/java/org/apache/qpid/interop/testclient/ src/main/java/org/apache/qpid/interop/testclient/testcases/ src/main/jav...

Author: rgreig
Date: Tue Mar 20 07:59:07 2007
New Revision: 520415

URL: http://svn.apache.org/viewvc?view=rev&rev=520415
Log:
Improvements made to interop tests.

Added:
    incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java
    incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java
    incubator/qpid/branches/M2/java/integrationtests/src/resources/
    incubator/qpid/branches/M2/java/integrationtests/src/resources/org/
    incubator/qpid/branches/M2/java/integrationtests/src/resources/org/apache/
    incubator/qpid/branches/M2/java/integrationtests/src/resources/org/apache/qpid/
    incubator/qpid/branches/M2/java/integrationtests/src/resources/org/apache/qpid/interop/
    incubator/qpid/branches/M2/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties
Modified:
    incubator/qpid/branches/M2/java/integrationtests/pom.xml
    incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java
    incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java
    incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java
    incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java
    incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java
    incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java
    incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java

Modified: incubator/qpid/branches/M2/java/integrationtests/pom.xml
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/integrationtests/pom.xml?view=diff&rev=520415&r1=520414&r2=520415
==============================================================================
--- incubator/qpid/branches/M2/java/integrationtests/pom.xml (original)
+++ incubator/qpid/branches/M2/java/integrationtests/pom.xml Tue Mar 20 07:59:07 2007
@@ -107,6 +107,15 @@
         </plugins>
 
         <resources>
+            <!-- Ensure all resources defined in the resources directory are copied into the build jar. -->
+            <resource>
+                <targetPath></targetPath>
+                <filtering>false</filtering>
+                <directory>src/resources</directory>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+            </resource>
         </resources>
 
     </build>

Modified: incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java?view=diff&rev=520415&r1=520414&r2=520415
==============================================================================
--- incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java (original)
+++ incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/CoordinatingTestCase.java Tue Mar 20 07:59:07 2007
@@ -23,6 +23,9 @@
 import java.util.Collection;
 import java.util.Properties;
 
+import javax.jms.JMSException;
+import javax.jms.Message;
+
 import junit.framework.TestCase;
 
 import org.apache.qpid.util.ConversationHelper;
@@ -55,61 +58,120 @@
  *
  * <p><table id="crc"><caption>CRC Card</caption>
  * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Accept notification of test case participants. <td> {@link InvitingTestDecorator}
  * <tr><td> Coordinate the test sequence amongst participants. <td> {@link ConversationHelper}
+ * <tr><td> Supply test properties
  * </table>
  */
 public abstract class CoordinatingTestCase extends TestCase
 {
+    /** Holds the contact details for the sending test client. */
+    TestClientDetails sender;
+
+    /** Holds the contact details for the receving test client. */
+    TestClientDetails receiver;
+
+    ConversationHelper conversation;
+
     /**
+     * Creates a new coordinating test case with the specified name.
      *
-     * @param sender         The contact details of the sending client in the test.
-     * @param receiver       The contact details of the sending client in the test.
-     * @param allClients     The list of all possible test clients that may accept the invitation.
-     * @param testProperties The test case definition.
+     * @param name The test case name.
+     */
+    public CoordinatingTestCase(String name)
+    {
+        super(name);
+    }
+
+    /**
+     * 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)
+    {
+        this.sender = sender;
+    }
+
+    /**
+     * 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)
+    {
+        this.receiver = receiver;
+    }
+
+    /**
+     * Supplies the sending test client.
+     *
+     * @return The sending test client.
+     */
+    public TestClientDetails getSender()
+    {
+        return sender;
+    }
+
+    /**
+     * Supplies the receiving test client.
+     *
+     * @return The receiving test client.
      */
-    public void TestCase(TestClientDetails sender, TestClientDetails receiver, Collection<TestClientDetails> allClients,
-                         Properties testProperties)
-    { }
+    public TestClientDetails getReceiver()
+    {
+        return receiver;
+    }
 
     /**
      * Holds a test coordinating conversation with the test clients. This is the basic implementation of the inner
      * loop of Use Case 5. It consists of assign the test roles, begining the test and gathering the test reports
      * from the participants.
      *
-     * @param sender                  The contact details of the sending client in the test.
-     * @param receiver                The contact details of the receiving client in the test.
-     * @param allParticipatingClients The list of all clients accepted the invitation.
-     * @param testProperties          The test case definition.
+     * @param testProperties The test case definition.
      *
      * @return The test results from the senders and receivers.
+     *
+     * @throws JMSException All underlying JMSExceptions are allowed to fall through.
      */
-    protected Object[] sequenceTest(TestClientDetails sender, TestClientDetails receiver, Properties testProperties)
+    protected Object[] sequenceTest(Properties testProperties) throws JMSException
     {
-        // Check if the sender and recevier did not accept the invite to this test.
-        {
-            // Automatically fail this combination of sender and receiver.
-        }
-
         // Assign the sender role to the sending test client.
+        Message assignSender = conversation.getSession().createMessage();
+        assignSender.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE");
+        assignSender.setStringProperty("ROLE", "SENDER");
+
+        conversation.send(assignSender);
 
         // Assign the receiver role the receiving client.
+        Message assignReceiver = conversation.getSession().createMessage();
+        assignReceiver.setStringProperty("CONTROL_TYPE", "ASSIGN_ROLE");
+        assignReceiver.setStringProperty("ROLE", "RECEIVER");
+
+        conversation.send(assignReceiver);
 
         // Wait for the senders and receivers to confirm their roles.
+        conversation.receive();
+        conversation.receive();
 
         // Start the test.
+        Message start = conversation.getSession().createMessage();
+        start.setStringProperty("CONTROL_TYPE", "START");
+
+        conversation.send(start);
 
         // Wait for the test sender to return its report.
+        Message senderReport = conversation.receive();
+
+        // Ask the receiver for its report.
+        Message statusRequest = conversation.getSession().createMessage();
+        statusRequest.setStringProperty("CONTROL_TYPE", "STATUS_REQUEST");
 
-        // As the receiver for its report.
+        conversation.send(statusRequest);
 
         // Wait for the receiver to send its report.
+        Message receiverReport = conversation.receive();
 
-        return null;
+        return new Message[] { senderReport, receiverReport };
     }
-
-    /*protected void setUp()
-    { }*/
-
-    /*protected void tearDown()
-    { }*/
 }

Modified: incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java?view=diff&rev=520415&r1=520414&r2=520415
==============================================================================
--- incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java (original)
+++ incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/Coordinator.java Tue Mar 20 07:59:07 2007
@@ -20,14 +20,26 @@
  */
 package org.apache.qpid.interop.coordinator;
 
-import java.util.Properties;
+import java.util.*;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import javax.jms.*;
 
 import junit.framework.Test;
 import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+import org.apache.log4j.Logger;
 
+import org.apache.qpid.interop.testclient.InteropClientTestCase;
+import org.apache.qpid.interop.testclient.TestClient;
+import org.apache.qpid.util.ClasspathScanner;
 import org.apache.qpid.util.CommandLineParser;
+import org.apache.qpid.util.ConversationHelper;
+import org.apache.qpid.util.PrettyPrintingUtils;
 
 import uk.co.thebadgerset.junit.extensions.TestRunnerImprovedErrorHandling;
+import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator;
 
 /**
  * <p/>Implements the coordinator client described in the interop testing specification
@@ -36,20 +48,27 @@
  *
  * <p><table id="crc"><caption>CRC Card</caption>
  * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Find out what test clients are available.
- * <tr><td> Decorate available tests to run all available clients.
+ * <tr><td> Find out what test clients are available. <td> {@link ConversationHelper}
+ * <tr><td> Decorate available tests to run all available clients. <td> {@link InvitingTestDecorator}
  * <tr><td> Attach XML test result logger.
  * <tr><td> Terminate the interop testing framework.
  * </table>
  */
 public class Coordinator extends TestRunnerImprovedErrorHandling
 {
+    private static final Logger log = Logger.getLogger(Coordinator.class);
+
+    public static final String DEFAULT_CONNECTION_PROPS_RESOURCE = "org/apache/qpid/interop/connection.properties";
+
     /** Holds the URL of the broker to coordinate the tests on. */
     String brokerUrl;
 
     /** Holds the virtual host to coordinate the tests on. If <tt>null</tt>, then the default virtual host is used. */
     String virtualHost;
 
+    /** Holds the list of all clients that enlisted, when the compulsory invite was issued. */
+    Set<TestClientDetails> enlistedClients = new HashSet<TestClientDetails>();
+
     /**
      * Creates an interop test coordinator on the specified broker and virtual host.
      *
@@ -58,6 +77,8 @@
      */
     Coordinator(String brokerUrl, String virtualHost)
     {
+        log.debug("Coordinator(String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost + "): called");
+
         // Retain the connection parameters.
         this.brokerUrl = brokerUrl;
         this.virtualHost = virtualHost;
@@ -76,42 +97,51 @@
      */
     public static void main(String[] args)
     {
-        // Use the command line parser to evaluate the command line.
-        CommandLineParser commandLine =
-            new CommandLineParser(new String[][]
-                                  {
-                                      { "b", "The broker URL.", "broker", "true" },
-                                      { "h", "The virtual host to use.", "virtual host", "false" }
-                                  });
-
-        // Capture the command line arguments or display errors and correct usage and then exit.
-        Properties options = null;
-
         try
         {
-            options = commandLine.parseCommandLine(args);
-        }
-        catch (IllegalArgumentException e)
-        {
-            System.out.println(commandLine.getErrors());
-            System.out.println(commandLine.getUsage());
-            System.exit(1);
-        }
+            // Use the command line parser to evaluate the command line.
+            CommandLineParser commandLine =
+                new CommandLineParser(new String[][]
+                                      {
+                                          { "b", "The broker URL.", "broker", "false" },
+                                          { "h", "The virtual host to use.", "virtual host", "false" }
+                                      });
+
+            // Capture the command line arguments or display errors and correct usage and then exit.
+            Properties options = null;
+
+            try
+            {
+                options = commandLine.parseCommandLine(args);
+            }
+            catch (IllegalArgumentException e)
+            {
+                System.out.println(commandLine.getErrors());
+                System.out.println(commandLine.getUsage());
+                System.exit(1);
+            }
 
-        // Extract the command line options.
-        String brokerUrl = options.getProperty("b");
-        String virtualHost = options.getProperty("h");
-
-        // Add all the trailing command line options (name=value pairs) to system properties. Tests may pick up
-        // overridden values from there.
-        commandLine.addCommandLineToSysProperties();
+            // Extract the command line options.
+            String brokerUrl = options.getProperty("b");
+            String virtualHost = options.getProperty("h");
+
+            // Add all the trailing command line options (name=value pairs) to system properties. Tests may pick up
+            // overridden values from there.
+            commandLine.addCommandLineToSysProperties();
+
+            // Scan for available test cases using a classpath scanner.
+            Collection<Class<? extends CoordinatingTestCase>> testCaseClasses =
+                ClasspathScanner.getMatches(CoordinatingTestCase.class, "^Test.*", true);
 
-        // Scan for available test cases using a classpath scanner.
-        String[] testClassNames = null;
+            int i = 0;
+            String[] testClassNames = new String[testCaseClasses.size()];
 
-        // Create a coordinator and begin its test procedure.
-        try
-        {
+            for (Class testClass : testCaseClasses)
+            {
+                testClassNames[i++] = testClass.getName();
+            }
+
+            // Create a coordinator and begin its test procedure.
             Coordinator coordinator = new Coordinator(brokerUrl, virtualHost);
             TestResult testResult = coordinator.start(testClassNames);
 
@@ -127,31 +157,92 @@
         catch (Exception e)
         {
             System.err.println(e.getMessage());
+            log.error("Top level handler caught execption.", e);
             System.exit(EXCEPTION_EXIT);
         }
     }
 
+    /**
+     * Starts all of the test classes to be run by this coordinator running.
+     *
+     * @param testClassNames An array of all the coordinating test case implementations.
+     *
+     * @return A JUnit TestResult to run the tests with.
+     *
+     * @throws Exception Any underlying exceptions are allowed to fall through, and fail the test process.
+     */
     public TestResult start(String[] testClassNames) throws Exception
     {
+        log.debug("public TestResult start(String[] testClassNames = " + PrettyPrintingUtils.printArray(testClassNames)
+                  + "): called");
+
         // Connect to the broker.
+        Connection connection = TestClient.createConnection(DEFAULT_CONNECTION_PROPS_RESOURCE, brokerUrl, virtualHost);
+        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+        Destination controlTopic = session.createTopic("iop.control");
+        Destination responseQueue = session.createQueue("coordinator");
+
+        ConversationHelper conversation =
+            new ConversationHelper(connection, controlTopic, responseQueue, LinkedBlockingQueue.class);
 
         // Broadcast the compulsory invitation to find out what clients are available to test.
+        Message invite = session.createMessage();
+        invite.setStringProperty("CONTROL_TYPE", "INVITE");
+        invite.setJMSReplyTo(responseQueue);
+
+        conversation.send(invite);
 
         // Wait for a short time, to give test clients an opportunity to reply to the invitation.
+        Collection<Message> enlists = conversation.receiveAll(0, 10000);
 
-        // Retain the list of all available clients.
+        enlistedClients = extractEnlists(enlists);
 
         // Run all of the tests in the suite using JUnit.
         TestResult result = super.start(testClassNames);
 
         // At this point in time, all tests have completed. Broadcast the shutdown message.
+        Message terminate = session.createMessage();
+        terminate.setStringProperty("CONTROL_TYPE", "TERMINATE");
+
+        conversation.send(terminate);
 
         return result;
     }
 
     /**
+     * For a collection of enlist messages, this method pulls out of the client details for the enlisting clients.
+     *
+     * @param enlists The enlist messages.
+     *
+     * @return A set of enlisting clients, extracted from the enlist messages.
+     *
+     * @throws JMSException Any underlying JMSException is allowed to fall through.
+     */
+    public static Set<TestClientDetails> extractEnlists(Collection<Message> enlists) throws JMSException
+    {
+        log.debug("public static Set<TestClientDetails> extractEnlists(Collection<Message> enlists = " + enlists
+                  + "): called");
+
+        Set<TestClientDetails> enlistedClients = new HashSet<TestClientDetails>();
+
+        // Retain the list of all available clients.
+        for (Message enlist : enlists)
+        {
+            TestClientDetails clientDetails = new TestClientDetails();
+            clientDetails.clientName = enlist.getStringProperty("CLIENT_NAME");
+            clientDetails.privateControlKey = enlist.getStringProperty("CLIENT_PRIVATE_CONTROL_KEY");
+
+            enlistedClients.add(clientDetails);
+        }
+
+        return enlistedClients;
+    }
+
+    /**
      * Runs a test or suite of tests, using the super class implemenation. This method wraps the test to be run
-     * in any test decorators needed to add in the configured toolkits enhanced junit functionality.
+     * in any test decorators needed to add in the coordinators ability to invite test clients to participate in
+     * tests.
      *
      * @param test The test to run.
      * @param wait Undocumented. Nothing in the JUnit javadocs to say what this is for.
@@ -160,9 +251,39 @@
      */
     public TestResult doRun(Test test, boolean wait)
     {
-        // Combine together the available test cases and test clients to produce a complete list of test case instances
-        // to run as a JUnit test suite.
+        log.debug("public TestResult doRun(Test \"" + test + "\", boolean " + wait + "): called");
+
+        // Wrap all tests in the test suite with WrappedSuiteTestDecorators. This is quite ugly and a bit baffling,
+        // but the reason it is done is because the JUnit implementation of TestDecorator has some bugs in it.
+        WrappedSuiteTestDecorator targetTest = null;
+
+        if (test instanceof TestSuite)
+        {
+            log.debug("targetTest is a TestSuite");
+
+            TestSuite suite = (TestSuite) test;
+
+            int numTests = suite.countTestCases();
+            log.debug("There are " + numTests + " in the suite.");
+
+            for (int i = 0; i < numTests; i++)
+            {
+                Test nextTest = suite.testAt(i);
+                log.debug("suite.testAt(" + i + ") = " + nextTest);
+
+                if (nextTest instanceof CoordinatingTestCase)
+                {
+                    log.debug("nextTest is a CoordinatingTestCase");
+                }
+            }
+
+            targetTest = new WrappedSuiteTestDecorator(suite);
+            log.debug("Wrapped with a WrappedSuiteTestDecorator.");
+        }
+
+        // Wrap the tests in an inviting test decorator, to perform the invite/test cycle.
+        targetTest = new InvitingTestDecorator(targetTest, enlistedClients);
 
-        return null;
+        return super.doRun(targetTest, wait);
     }
 }

Added: incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java?view=auto&rev=520415
==============================================================================
--- incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java (added)
+++ incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/InvitingTestDecorator.java Tue Mar 20 07:59:07 2007
@@ -0,0 +1,199 @@
+/*
+ *
+ * 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.interop.coordinator;
+
+import java.util.*;
+
+import javax.jms.JMSException;
+import javax.jms.Message;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+import org.apache.log4j.Logger;
+
+import org.apache.qpid.util.ConversationHelper;
+
+import uk.co.thebadgerset.junit.extensions.WrappedSuiteTestDecorator;
+
+/**
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Broadcast test invitations and collect enlists. <td> {@link ConversationHelper}.
+ * <tr><td> Output test failures for clients unwilling to run the test case. <td> {@link Coordinator}
+ * <tr><td> Execute coordinated test cases. <td> {@link CoordinatingTestCase}
+ * </table>
+ */
+public class InvitingTestDecorator extends WrappedSuiteTestDecorator
+{
+    private static final Logger log = Logger.getLogger(InvitingTestDecorator.class);
+
+    Set<TestClientDetails> allClients;
+    ConversationHelper conversation;
+
+    WrappedSuiteTestDecorator testSuite;
+
+    /**
+     * Creates a wrappred suite test decorator from a test suite.
+     *
+     * @param suite      The test suite.
+     * @param allClients The list of all clients that responded to the compulsory invite.
+     */
+    /*public InvitingTestDecorator(TestSuite suite, Collection<TestClientDetails> allClients, ConversationHelper conversation)
+    {
+        super(suite);
+    }*/
+
+    /**
+     * Creates a wrapped suite test decorator from another one.
+     *
+     * @param suite The test suite.
+     * @param allClients The list of all clients that responded to the compulsory invite.
+     */
+    public InvitingTestDecorator(WrappedSuiteTestDecorator suite, Set<TestClientDetails> allClients)
+    {
+        super(suite);
+
+        log.debug("public InvitingTestDecorator(WrappedSuiteTestDecorator suite, Set<TestClientDetails> allClients = "
+                  + allClients + "): called");
+    }
+
+    /**
+     * Broadcasts a test invitation and accetps enlisting from participating clients. The wrapped test case is
+     * then repeated for every combination of test clients (provided the wrapped test case extends
+     * {@link CoordinatingTestCase}.
+     *
+     * <p/>Any JMSExceptions during the invite/enlist conversation will be allowed to fall through as runtime exceptions,
+     * resulting in the non-completion of the test run.
+     *
+     * @todo Better error recovery for failure of the invite/enlist conversation could be added.
+     *
+     * @param testResult The the results object to monitor the test results with.
+     */
+    public void run(TestResult testResult)
+    {
+        log.debug("public void run(TestResult testResult): called");
+
+        Collection<Test> tests = testSuite.getAllUnderlyingTests();
+
+        for (Test test : tests)
+        {
+            CoordinatingTestCase coordTest = (CoordinatingTestCase) test;
+
+            // Broadcast the invitation to find out what clients are available to test.
+            Set<TestClientDetails> enlists = null;
+            try
+            {
+                Message invite = conversation.getSession().createMessage();
+                invite.setStringProperty("CONTROL_TYPE", "INVITE");
+                invite.setStringProperty("TEST_NAME", coordTest.getName());
+
+                conversation.send(invite);
+
+                // Wait for a short time, to give test clients an opportunity to reply to the invitation.
+                Collection<Message> replies = conversation.receiveAll(allClients.size(), 10000);
+                enlists = Coordinator.extractEnlists(replies);
+            }
+            catch (JMSException e)
+            {
+                throw new RuntimeException("There was a JMSException during the invite/enlist conversation.", e);
+            }
+
+            // Compare the list of willing clients to the list of all available.
+            Set<TestClientDetails> optOuts = new HashSet<TestClientDetails>(allClients);
+            optOuts.removeAll(enlists);
+
+            // Output test failures for clients that will not particpate in the test.
+            Set<List<TestClientDetails>> failPairs = allPairs(optOuts, allClients);
+
+            for (List<TestClientDetails> failPair : failPairs)
+            {
+                CoordinatingTestCase failTest = new OptOutTestCase("");
+                failTest.setSender(failPair.get(0));
+                failTest.setReceiver(failPair.get(1));
+
+                failTest.run(testResult);
+            }
+
+            // Loop over all combinations of clients, willing to run the test.
+            Set<List<TestClientDetails>> enlistedPairs = allPairs(enlists, enlists);
+
+            for (List<TestClientDetails> enlistedPair : enlistedPairs)
+            {
+                // Set the sending and receiving client details on the test case.
+                coordTest.setSender(enlistedPair.get(0));
+                coordTest.setReceiver(enlistedPair.get(1));
+
+                // Execute the test case.
+                coordTest.run(testResult);
+            }
+        }
+    }
+
+    /**
+     * Produces all pairs of combinations of elements from two sets. The ordering of the elements in the pair is
+     * important, that is the pair <l, r> is distinct from <r, l>; both pairs are generated. For any element, i, in
+     * both the left and right sets, the reflexive pair <i, i> is not generated.
+     *
+     * @param left  The left set.
+     * @param right The right set.
+     *
+     * @return All pairs formed from the permutations of all elements of the left and right sets.
+     */
+    private <E> Set<List<E>> allPairs(Set<E> left, Set<E> right)
+    {
+        log.debug("private <E> Set<List<E>> allPairs(Set<E> left = " + left + ", Set<E> right = " + right + "): called");
+
+        Set<List<E>> results = new HashSet<List<E>>();
+
+        // Form all pairs from left to right.
+        // Form all pairs from right to left.
+        for (E le : left)
+        {
+            for (E re : right)
+            {
+                if (!le.equals(re))
+                {
+                    results.add(new Pair<E>(le, re));
+                    results.add(new Pair<E>(re, le));
+                }
+            }
+        }
+
+        log.debug("results = " + results);
+
+        return results;
+    }
+
+    /**
+     * A simple implementation of a pair, using a list.
+     */
+    private class Pair<T> extends ArrayList<T>
+    {
+        public Pair(T first, T second)
+        {
+            super();
+            super.add(first);
+            super.add(second);
+        }
+    }
+}

Added: incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java?view=auto&rev=520415
==============================================================================
--- incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java (added)
+++ incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/OptOutTestCase.java Tue Mar 20 07:59:07 2007
@@ -0,0 +1,52 @@
+/*
+ *
+ * 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.interop.coordinator;
+
+import junit.framework.Assert;
+
+/**
+ * An OptOutTestCase is a test case that automatically fails. It is used when a list of test clients has been generated
+ * from a compulsory invite, but only some of those clients have responded to a specific test case invite. The clients
+ * that did not respond, are automatically given a fail for the test.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Fail the test with a suitable reason.
+ * </table>
+ */
+public class OptOutTestCase extends CoordinatingTestCase
+{
+    /**
+     * Creates a new coordinating test case with the specified name.
+     *
+     * @param name The test case name.
+     */
+    public OptOutTestCase(String name)
+    {
+        super(name);
+    }
+
+    /** Generates an appropriate test failure assertion. */
+    public void testOptOut()
+    {
+        Assert.fail("One of " + getSender() + " and " + getReceiver() + " opted out of the test.");
+    }
+}

Modified: incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java?view=diff&rev=520415&r1=520414&r2=520415
==============================================================================
--- incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java (original)
+++ incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/coordinator/TestClientDetails.java Tue Mar 20 07:59:07 2007
@@ -30,8 +30,58 @@
     /** The test clients name. */
     public String clientName;
 
-    /* The test clients unqiue sequence number. Not currently used. */
+    /* The test clients unique sequence number. Not currently used. */
 
     /** The routing key of the test clients control topic. */
     public String privateControlKey;
+
+    /**
+     * Two TestClientDetails are considered to be equal, iff they have the same client name.
+     *
+     * @param o The object to compare to.
+     *
+     * @return <tt>If the object to compare to is a TestClientDetails equal to this one, <tt>false</tt> otherwise.
+     */
+    public boolean equals(Object o)
+    {
+        if (this == o)
+        {
+            return true;
+        }
+
+        if (!(o instanceof TestClientDetails))
+        {
+            return false;
+        }
+
+        final TestClientDetails testClientDetails = (TestClientDetails) o;
+
+        if ((clientName != null) ? (!clientName.equals(testClientDetails.clientName))
+                                 : (testClientDetails.clientName != null))
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Computes a hash code compatible with the equals method; based on the client name alone.
+     *
+     * @return A hash code for this.
+     */
+    public int hashCode()
+    {
+        return ((clientName != null) ? clientName.hashCode() : 0);
+    }
+
+    /**
+     * Outputs the client name and address details. Mostly used for debugging purposes.
+     *
+     * @return The client name and address.
+     */
+    public String toString()
+    {
+        return "clientName = " + clientName + ", privateControlKey = " + privateControlKey;
+    }
 }

Modified: incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java?view=diff&rev=520415&r1=520414&r2=520415
==============================================================================
--- incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java (original)
+++ incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/TestClient.java Tue Mar 20 07:59:07 2007
@@ -75,13 +75,16 @@
     public static final String CONNECTION_PROPERTY = "connectionfactory.broker";
     public static final String CONNECTION_NAME = "broker";
     public static final String CLIENT_NAME = "java";
-    public static final String DEFAULT_CONNECTION_PROPS_RESOURCE = "org/apache/qpid/interop/client/connection.properties";
+    public static final String DEFAULT_CONNECTION_PROPS_RESOURCE = "org/apache/qpid/interop/connection.properties";
 
     private MessageProducer producer;
     private Session session;
 
     public TestClient(String brokerUrl, String virtualHost)
     {
+        log.debug("public TestClient(String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost
+                  + "): called");
+
         // Retain the connection parameters.
         this.brokerUrl = brokerUrl;
         this.virtualHost = virtualHost;
@@ -104,7 +107,7 @@
         CommandLineParser commandLine =
             new CommandLineParser(new String[][]
                                   {
-                                      { "b", "The broker URL.", "broker", "true" },
+                                      { "b", "The broker URL.", "broker", "false" },
                                       { "h", "The virtual host to use.", "virtual host", "false" }
                                   });
 
@@ -146,6 +149,8 @@
 
     private void start() throws JMSException
     {
+        log.debug("private void start(): called");
+
         // Use a class path scanner to find all the interop test case implementations.
         Collection<Class<? extends InteropClientTestCase>> testCaseClasses =
             ClasspathScanner.getMatches(InteropClientTestCase.class, "^TestCase.*", true);
@@ -201,17 +206,23 @@
      *
      * @return A JMS conneciton.
      */
-    private static Connection createConnection(String connectionPropsResource, String brokerUrl, String virtualHost)
+    public static Connection createConnection(String connectionPropsResource, String brokerUrl, String virtualHost)
     {
+        log.debug("public static Connection createConnection(String connectionPropsResource = " + connectionPropsResource
+                  + ", String brokerUrl = " + brokerUrl + ", String virtualHost = " + virtualHost + "): called");
+
         try
         {
             Properties connectionProps =
                 PropertiesUtils.getProperties(TestClient.class.getClassLoader().getResourceAsStream(
                                                   connectionPropsResource));
 
-            String connectionString =
-                "amqp://guest:guest/" + ((virtualHost != null) ? virtualHost : "") + "?brokerlist='" + brokerUrl + "'";
-            connectionProps.setProperty(CONNECTION_PROPERTY, connectionString);
+            if (brokerUrl != null)
+            {
+                String connectionString =
+                    "amqp://guest:guest/" + ((virtualHost != null) ? virtualHost : "") + "?brokerlist='" + brokerUrl + "'";
+                connectionProps.setProperty(CONNECTION_PROPERTY, connectionString);
+            }
 
             Context ctx = new InitialContext(connectionProps);
 
@@ -241,6 +252,8 @@
      */
     public void onMessage(Message message)
     {
+        log.debug("public void onMessage(Message message = " + message + "): called");
+
         try
         {
             String controlType = message.getStringProperty("CONTROL_TYPE");

Modified: incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java?view=diff&rev=520415&r1=520414&r2=520415
==============================================================================
--- incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java (original)
+++ incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/interop/testclient/testcases/TestCase1DummyRun.java Tue Mar 20 07:59:07 2007
@@ -24,6 +24,8 @@
 import javax.jms.Message;
 import javax.jms.Session;
 
+import org.apache.log4j.Logger;
+
 import org.apache.qpid.interop.testclient.InteropClientTestCase;
 
 /**
@@ -41,29 +43,41 @@
  */
 public class TestCase1DummyRun implements InteropClientTestCase
 {
+    private static final Logger log = Logger.getLogger(TestCase1DummyRun.class);
+
     public String getName()
     {
+        log.debug("public String getName(): called");
+
         return "TC1_DummyRun";
     }
 
     public boolean acceptInvite(Message inviteMessage) throws JMSException
     {
+        log.debug("public boolean acceptInvite(Message inviteMessage): called");
+
         // Test parameters don't matter, accept all invites.
         return true;
     }
 
     public void assignRole(Roles role, Message assignRoleMessage) throws JMSException
     {
+        log.debug("public void assignRole(Roles role, Message assignRoleMessage): called");
+
         // Do nothing, both roles are the same.
     }
 
     public void start()
     {
+        log.debug("public void start(): called");
+
         // Do nothing.
     }
 
     public Message getReport(Session session) throws JMSException
     {
+        log.debug("public Message getReport(Session session): called");
+
         // Generate a dummy report, the coordinator expects a report but doesn't care what it is.
         return session.createTextMessage("Dummy Run, Ok.");
     }

Modified: incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java?view=diff&rev=520415&r1=520414&r2=520415
==============================================================================
--- incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java (original)
+++ incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/util/ClasspathScanner.java Tue Mar 20 07:59:07 2007
@@ -63,12 +63,17 @@
     public static <T> Collection<Class<? extends T>> getMatches(Class<T> matchingClass, String matchingRegexp,
                                                                 boolean beanOnly)
     {
+        log.debug("public static <T> Collection<Class<? extends T>> getMatches(Class<T> matchingClass = " + matchingClass
+                  + ", String matchingRegexp = " + matchingRegexp + ", boolean beanOnly = " + beanOnly + "): called");
+
         // Build a compiled regular expression from the pattern to match.
         Pattern matchPattern = Pattern.compile(matchingRegexp);
 
         String classPath = System.getProperty("java.class.path");
         Map<String, Class<? extends T>> result = new HashMap<String, Class<? extends T>>();
 
+        log.debug("classPath = " + classPath);
+
         // Find matching classes starting from all roots in the classpath.
         for (String path : splitClassPath(classPath))
         {
@@ -92,15 +97,19 @@
     private static <T> void gatherFiles(File classRoot, String classFileName, Map<String, Class<? extends T>> result,
                                         Pattern matchPattern, Class<? extends T> matchClass)
     {
+        log.debug("private static <T> void gatherFiles(File classRoot = " + classRoot + ", String classFileName = "
+                  + classFileName + ", Map<String, Class<? extends T>> result, Pattern matchPattern = " + matchPattern
+                  + ", Class<? extends T> matchClass = " + matchClass + "): called");
+
         File thisRoot = new File(classRoot, classFileName);
 
         // If the current location is a file, check if it is a matching class.
         if (thisRoot.isFile())
         {
             // Check that the file has a matching name.
-            if (matchesName(classFileName, matchPattern))
+            if (matchesName(thisRoot.getName(), matchPattern))
             {
-                String className = classNameFromFile(classFileName);
+                String className = classNameFromFile(thisRoot.getName());
 
                 // Check that the class has matching type.
                 try
@@ -206,6 +215,8 @@
      */
     private static String classNameFromFile(String classFileName)
     {
+        log.debug("private static String classNameFromFile(String classFileName = " + classFileName + "): called");
+
         // Remove the .class ending.
         String s = classFileName.substring(0, classFileName.length() - ".class".length());
 

Modified: incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java?view=diff&rev=520415&r1=520414&r2=520415
==============================================================================
--- incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java (original)
+++ incubator/qpid/branches/M2/java/integrationtests/src/main/java/org/apache/qpid/util/ConversationHelper.java Tue Mar 20 07:59:07 2007
@@ -21,7 +21,6 @@
 package org.apache.qpid.util;
 
 import java.util.*;
-import java.util.Queue;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.atomic.AtomicLong;
@@ -109,7 +108,7 @@
     private MessageProducer producer;
     private MessageConsumer consumer;
 
-    Class<? extends BlockingQueue<Message>> queueClass;
+    Class<? extends BlockingQueue> queueClass;
 
     BlockingQueue<Message> deadLetterBox = new LinkedBlockingQueue<Message>();
 
@@ -140,7 +139,7 @@
      * @throws JMSException All undelying JMSExceptions are allowed to fall through.
      */
     public ConversationHelper(Connection connection, Destination sendDestination, Destination receiveDestination,
-                              Class<? extends BlockingQueue<Message>> queueClass) throws JMSException
+                              Class<? extends BlockingQueue> queueClass) throws JMSException
     {
         session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
         producer = session.createProducer(sendDestination);
@@ -180,7 +179,7 @@
     {
         if (!idsToQueues.containsKey(conversationId))
         {
-            idsToQueues.put(conversationId, ReflectionUtils.<BlockingQueue<Message>>newInstance(queueClass));
+            idsToQueues.put(conversationId, ReflectionUtils.<BlockingQueue>newInstance(queueClass));
         }
     }
 
@@ -210,6 +209,28 @@
     }
 
     /**
+     * Gets many messages in an ongoing conversation. If a limit is specified, then once that many messages are
+     * received they will be returned. If a timeout is specified, then all messages up to the limit, received within
+     * that timespan will be returned.
+     *
+     * @param num     The number of messages to receive, or all if this is less than 1.
+     * @param timeout The timeout in milliseconds to receive the messages in, or forever if this is less than 1.
+     *
+     * @return All messages received within the count limit and the timeout.
+     */
+    public Collection<Message> receiveAll(int num, long timeout)
+    {
+        Collection<Message> result = new ArrayList<Message>();
+
+        for (int i = 0; i < num; i++)
+        {
+            result.add(receive());
+        }
+
+        return result;
+    }
+
+    /**
      * Completes the conversation. Any open transactions are committed. Any correlation id's pertaining to the
      * conversation are no longer valid, and any incoming messages using them will go to the dead letter box.
      */
@@ -234,7 +255,7 @@
      */
     public Collection<Message> emptyDeadLetterBox()
     {
-        Collection<Message> result = new LinkedList<Message>();
+        Collection<Message> result = new ArrayList<Message>();
         deadLetterBox.drainTo(result);
 
         return result;
@@ -279,5 +300,10 @@
     {
         /** Holds the correlation id for the current threads conversation. */
         long conversationId;
+    }
+
+    public Session getSession()
+    {
+        return session;
     }
 }

Added: incubator/qpid/branches/M2/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties
URL: http://svn.apache.org/viewvc/incubator/qpid/branches/M2/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties?view=auto&rev=520415
==============================================================================
--- incubator/qpid/branches/M2/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties (added)
+++ incubator/qpid/branches/M2/java/integrationtests/src/resources/org/apache/qpid/interop/connection.properties Tue Mar 20 07:59:07 2007
@@ -0,0 +1,2 @@
+java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextFactory
+connectionfactory.broker = amqp://guest:guest@clientid/?brokerlist='tcp://localhost:5672'