blob: 809bb1dd2f1891aa2c8e127456d4dd4d9e84a3e1 [file] [log] [blame]
/*
*
* 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 junit.framework.Test;
import junit.framework.TestResult;
import org.apache.log4j.Logger;
import org.apache.qpid.junit.extensions.WrappedSuiteTestDecorator;
import org.apache.qpid.test.framework.DropInTest;
import org.apache.qpid.test.framework.FrameworkBaseCase;
import org.apache.qpid.test.framework.TestClientDetails;
import org.apache.qpid.test.framework.sequencers.CircuitFactory;
import org.apache.qpid.test.framework.sequencers.FanOutCircuitFactory;
import org.apache.qpid.test.utils.ConversationFactory;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import java.util.Iterator;
import java.util.Set;
/**
* FanOutTestDecorator is an {@link DistributedTestDecorator} that runs one test client in the sender role, and the remainder
* in the receivers role. It also has the capability to listen for new test cases joining the test beyond the initial start
* point. This feature can be usefull when experimenting with adding more load, in the form of more test clients, to assess
* its impact on a running test.
*
* <p><table id="crc"><caption>CRC Card</caption>
* <tr><th> Responsibilities <th> Collaborations
* <tr><td> Execute coordinated test cases. <td> {@link FrameworkBaseCase}
* <tr><td> Accept test clients joining a running test.
* </table>
*/
public class FanOutTestDecorator extends DistributedTestDecorator implements MessageListener
{
/** Used for debugging. */
private static final Logger log = Logger.getLogger(FanOutTestDecorator.class);
/** Holds the currently running test case. */
private FrameworkBaseCase currentTest = null;
/**
* Creates a wrapped suite test decorator from another one.
*
* @param suite The test suite.
* @param availableClients The list of all clients that responded to the compulsory invite.
* @param controlConversation The conversation helper for the control level, test coordination conversation.
* @param controlConnection The connection that the coordination messages are sent over.
*/
public FanOutTestDecorator(WrappedSuiteTestDecorator suite, Set<TestClientDetails> availableClients,
ConversationFactory controlConversation, Connection controlConnection)
{
super(suite, availableClients, controlConversation, controlConnection);
log.debug("public DistributedTestDecorator(WrappedSuiteTestDecorator suite, Set<TestClientDetails> allClients = "
+ availableClients + ", ConversationHelper controlConversation = " + controlConversation + "): called");
testSuite = suite;
allClients = availableClients;
conversationFactory = controlConversation;
connection = controlConnection;
// Sign available clients up to the test.
for (Test test : getAllUnderlyingTests())
{
FrameworkBaseCase coordTest = (FrameworkBaseCase) test;
// Get all of the clients able to participate in the test.
Set<TestClientDetails> enlists = signupClients(coordTest);
// Check that there were some clients available.
if (enlists.size() == 0)
{
throw new RuntimeException("No clients to test with");
}
// Create a distributed test circuit factory for the test.
CircuitFactory circuitFactory = getTestSequencer();
// Set up the first client in the sender role, and the remainder in the receivers role.
Iterator<TestClientDetails> clients = enlists.iterator();
circuitFactory.setSender(clients.next());
while (clients.hasNext())
{
// Set the sending and receiving client details on the test case.
circuitFactory.setReceiver(clients.next());
}
// Pass down the connection to hold the coordinating conversation over.
circuitFactory.setConversationFactory(conversationFactory);
// If the current test case is a drop-in test, set it up as the currently running test for late joiners to
// add in to. Otherwise the current test field is set to null, to indicate that late joiners are not allowed.
currentTest = (coordTest instanceof DropInTest) ? coordTest : null;
// Execute the test case.
coordTest.setCircuitFactory(circuitFactory);
}
}
/**
* Broadcasts a test invitation and accepts enlists from participating clients. The wrapped test cases are run
* with one test client in the sender role, and the remaining test clients in the receiving role.
*
* <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.
*
* @param testResult The the results object to monitor the test results with.
*
* @todo Better error recovery for failure of the invite/enlist conversation could be added.
*/
public void run(TestResult testResult)
{
log.debug("public void run(TestResult testResult): called");
// Listen for late joiners on the control topic.
try
{
conversationFactory.getSession().createConsumer(controlTopic).setMessageListener(this);
}
catch (JMSException e)
{
throw new RuntimeException("Unable to set up the message listener on the control topic.", e);
}
// Run all of the test cases in the test suite.
/*for (Test test : getAllUnderlyingTests())
{
FrameworkBaseCase coordTest = (FrameworkBaseCase) test;
// Get all of the clients able to participate in the test.
Set<TestClientDetails> enlists = signupClients(coordTest);
// Check that there were some clients available.
if (enlists.size() == 0)
{
throw new RuntimeException("No clients to test with");
}
// Create a distributed test circuit factory for the test.
CircuitFactory circuitFactory = getTestSequencer();
// Set up the first client in the sender role, and the remainder in the receivers role.
Iterator<TestClientDetails> clients = enlists.iterator();
circuitFactory.setSender(clients.next());
while (clients.hasNext())
{
// Set the sending and receiving client details on the test case.
circuitFactory.setReceiver(clients.next());
}
// Pass down the connection to hold the coordinating conversation over.
circuitFactory.setConversationFactory(conversationFactory);
// If the current test case is a drop-in test, set it up as the currently running test for late joiners to
// add in to. Otherwise the current test field is set to null, to indicate that late joiners are not allowed.
currentTest = (coordTest instanceof DropInTest) ? coordTest : null;
// Execute the test case.
coordTest.setCircuitFactory(circuitFactory);
}*/
// Run all of the test cases in the test suite.
for (Test test : getAllUnderlyingTests())
{
FrameworkBaseCase coordTest = (FrameworkBaseCase) test;
coordTest.run(testResult);
currentTest = null;
}
}
/**
* Should provide the distributed test sequencer to pass to {@link org.apache.qpid.test.framework.FrameworkBaseCase}
* tests.
*
* @return A distributed test sequencer.
*/
public CircuitFactory getTestSequencer()
{
return new FanOutCircuitFactory();
}
/**
* Listens to incoming messages on the control topic. If the messages are 'join' messages, signalling a new
* test client wishing to join the current test, then the new client will be added to the current test in the
* receivers role.
*
* @param message The incoming control message.
*/
public void onMessage(Message message)
{
try
{
// Check if the message is from a test client attempting to join a running test, and join it to the current
// test case if so.
if (message.getStringProperty("CONTROL_TYPE").equals("JOIN") && (currentTest != null))
{
((DropInTest) currentTest).lateJoin(message);
}
}
// There is not a lot can be done with this error, so it is deliberately ignored.
catch (JMSException e)
{
log.debug("Unable to process message:" + message);
}
}
/**
* Prints a string summarizing this test decorator, mainly for debugging purposes.
*
* @return String representation for debugging purposes.
*/
public String toString()
{
return "FanOutTestDecorator: [ testSuite = " + testSuite + " ]";
}
}