PingPongTestPerf.java
001 /*
002  *
003  * Licensed to the Apache Software Foundation (ASF) under one
004  * or more contributor license agreements.  See the NOTICE file
005  * distributed with this work for additional information
006  * regarding copyright ownership.  The ASF licenses this file
007  * to you under the Apache License, Version 2.0 (the
008  * "License"); you may not use this file except in compliance
009  * with the License.  You may obtain a copy of the License at
010  *
011  *   http://www.apache.org/licenses/LICENSE-2.0
012  *
013  * Unless required by applicable law or agreed to in writing,
014  * software distributed under the License is distributed on an
015  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016  * KIND, either express or implied.  See the License for the
017  * specific language governing permissions and limitations
018  * under the License.
019  *
020  */
021 package org.apache.qpid.requestreply;
022 
023 import junit.framework.Assert;
024 import junit.framework.Test;
025 import junit.framework.TestSuite;
026 
027 import org.apache.log4j.Logger;
028 
029 import org.apache.qpid.junit.extensions.AsymptoticTestCase;
030 import org.apache.qpid.junit.extensions.util.ParsedProperties;
031 import org.apache.qpid.junit.extensions.util.TestContextProperties;
032 
033 import javax.jms.*;
034 
035 /**
036  * PingPongTestPerf is a full round trip ping test, that has been written with the intention of being scaled up to run
037  * many times simultaneously to simluate many clients/producer/connections. A full round trip ping sends a message from
038  * a producer to a conumer, then the consumer replies to the message on a temporary queue.
039  *
040  <p/>A single run of the test using the default JUnit test runner will result in the sending and timing of the number
041  * of pings specified by the test size and time how long it takes for all of these to complete. This test may be scaled
042  * up using a suitable JUnit test runner. See {@link org.apache.qpid.junit.extensions.TKTestRunner} for more
043  * information on how to do this.
044  *
045  <p/>The setup/teardown cycle establishes a connection to a broker and sets up a queue to send ping messages to and a
046  * temporary queue for replies. This setup is only established once for all the test repeats, but each test threads
047  * gets its own connection/producer/consumer, this is only re-established if the connection is lost.
048  *
049  <p/>The test cycle is: Connects to a queue, creates a temporary queue, creates messages containing a property that
050  * is the name of the temporary queue, fires off many messages on the original queue and waits for them all to come
051  * back on the temporary queue.
052  *
053  <p/>Configurable test properties: message size, transacted or not, persistent or not. Broker connection details.
054  *
055  <p><table id="crc"><caption>CRC Card</caption>
056  <tr><th> Responsibilities <th> Collaborations
057  </table>
058  */
059 public class PingPongTestPerf extends AsymptoticTestCase
060 {
061     private static Logger _logger = Logger.getLogger(PingPongTestPerf.class);
062 
063     /** Thread local to hold the per-thread test setup fields. */
064     ThreadLocal<PerThreadSetup> threadSetup = new ThreadLocal<PerThreadSetup>();
065 
066     // Set up a property reader to extract the test parameters from. Once ContextualProperties is available in
067     // the project dependencies, use it to get property overrides for configurable tests and to notify the test runner
068     // of the test parameters to log with the results. It also providers some basic type parsing convenience methods.
069     // private Properties testParameters = System.getProperties();
070     private ParsedProperties testParameters =
071         TestContextProperties.getInstance(PingPongProducer.defaults /*System.getProperties()*/);
072 
073     public PingPongTestPerf(String name)
074     {
075         super(name);
076 
077         _logger.debug(testParameters);
078 
079         // Sets up the test parameters with defaults.
080         /*testParameters.setPropertyIfNull(PingPongProducer.TX_BATCH_SIZE_PROPNAME,
081             Integer.toString(PingPongProducer.TX_BATCH_SIZE_DEFAULT));
082         testParameters.setPropertyIfNull(PingPongProducer.MESSAGE_SIZE_PROPNAME,
083             Integer.toString(PingPongProducer.MESSAGE_SIZE_DEAFULT));
084         testParameters.setPropertyIfNull(PingPongProducer.PING_QUEUE_NAME_PROPNAME,
085             PingPongProducer.PING_QUEUE_NAME_DEFAULT);
086         testParameters.setPropertyIfNull(PingPongProducer.PERSISTENT_MODE_PROPNAME,
087             Boolean.toString(PingPongProducer.PERSISTENT_MODE_DEFAULT));
088         testParameters.setPropertyIfNull(PingPongProducer.TRANSACTED_PROPNAME,
089             Boolean.toString(PingPongProducer.TRANSACTED_DEFAULT));
090         testParameters.setPropertyIfNull(PingPongProducer.BROKER_PROPNAME, PingPongProducer.BROKER_DEFAULT);
091         testParameters.setPropertyIfNull(PingPongProducer.USERNAME_PROPNAME, PingPongProducer.USERNAME_DEFAULT);
092         testParameters.setPropertyIfNull(PingPongProducer.PASSWORD_PROPNAME, PingPongProducer.PASSWORD_DEFAULT);
093         testParameters.setPropertyIfNull(PingPongProducer.VIRTUAL_HOST_PROPNAME, PingPongProducer.VIRTUAL_HOST_DEFAULT);
094         testParameters.setPropertyIfNull(PingPongProducer.VERBOSE_PROPNAME,
095             Boolean.toString(PingPongProducer.VERBOSE_DEFAULT));
096         testParameters.setPropertyIfNull(PingPongProducer.RATE_PROPNAME, Integer.toString(PingPongProducer.RATE_DEFAULT));
097         testParameters.setPropertyIfNull(PingPongProducer.PUBSUB_PROPNAME,
098             Boolean.toString(PingPongProducer.PUBSUB_DEFAULT));
099         testParameters.setPropertyIfNull(PingPongProducer.TX_BATCH_SIZE_PROPNAME,
100             Integer.toString(PingPongProducer.TX_BATCH_SIZE_DEFAULT));
101         testParameters.setPropertyIfNull(PingPongProducer.TIMEOUT_PROPNAME, Long.toString(PingPongProducer.TIMEOUT_DEFAULT));
102         testParameters.setPropertyIfNull(PingPongProducer.DESTINATION_COUNT_PROPNAME,
103             Integer.toString(PingPongProducer.DESTINATION_COUNT_DEFAULT));
104         testParameters.setPropertyIfNull(PingPongProducer.FAIL_AFTER_COMMIT_PROPNAME,
105             PingPongProducer.FAIL_AFTER_COMMIT_DEFAULT);
106         testParameters.setPropertyIfNull(PingPongProducer.FAIL_BEFORE_COMMIT_PROPNAME,
107             PingPongProducer.FAIL_BEFORE_COMMIT_DEFAULT);
108         testParameters.setPropertyIfNull(PingPongProducer.FAIL_AFTER_SEND_PROPNAME,
109             PingPongProducer.FAIL_AFTER_SEND_DEFAULT);
110         testParameters.setPropertyIfNull(PingPongProducer.FAIL_BEFORE_SEND_PROPNAME,
111             PingPongProducer.FAIL_BEFORE_SEND_DEFAULT);
112         testParameters.setPropertyIfNull(PingPongProducer.FAIL_ONCE_PROPNAME, PingPongProducer.FAIL_ONCE_DEFAULT);
113         testParameters.setPropertyIfNull(PingPongProducer.UNIQUE_DESTS_PROPNAME,
114             Boolean.toString(PingPongProducer.UNIQUE_DESTS_DEFAULT));
115         testParameters.setPropertyIfNull(PingPongProducer.ACK_MODE_PROPNAME,
116             Integer.toString(PingPongProducer.ACK_MODE_DEFAULT));
117         testParameters.setPropertyIfNull(PingPongProducer.PAUSE_AFTER_BATCH_PROPNAME,
118             PingPongProducer.PAUSE_AFTER_BATCH_DEFAULT);*/
119     }
120 
121     /**
122      * Compile all the tests into a test suite.
123      */
124     public static Test suite()
125     {
126         // Build a new test suite
127         TestSuite suite = new TestSuite("Ping-Pong Performance Tests");
128 
129         // Run performance tests in read committed mode.
130         suite.addTest(new PingPongTestPerf("testPingPongOk"));
131 
132         return suite;
133     }
134 
135     private static void setSystemPropertyIfNull(String propName, String propValue)
136     {
137         if (System.getProperty(propName== null)
138         {
139             System.setProperty(propName, propValue);
140         }
141     }
142 
143     public void testPingPongOk(int numPingsthrows Exception
144     {
145         // Get the per thread test setup to run the test through.
146         PerThreadSetup perThreadSetup = threadSetup.get();
147 
148         // Generate a sample message. This message is already time stamped and has its reply-to destination set.
149         Message msg =
150             perThreadSetup._testPingProducer.getTestMessage(perThreadSetup._testPingProducer.getReplyDestinations().get(0),
151                 testParameters.getPropertyAsInteger(PingPongProducer.MESSAGE_SIZE_PROPNAME),
152                 testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME));
153 
154         // Send the message and wait for a reply.
155         int numReplies =
156             perThreadSetup._testPingProducer.pingAndWaitForReply(msg, numPings, PingPongProducer.TIMEOUT_DEFAULT, null);
157 
158         // Fail the test if the timeout was exceeded.
159         if (numReplies != numPings)
160         {
161             Assert.fail("The ping timed out, got " + numReplies + " out of " + numPings);
162         }
163     }
164 
165     /**
166      * Performs test fixture creation on a per thread basis. This will only be called once for each test thread.
167      */
168     public void threadSetUp()
169     {
170         try
171         {
172             PerThreadSetup perThreadSetup = new PerThreadSetup();
173 
174             // Extract the test set up paramaeters.
175             String brokerDetails = testParameters.getProperty(PingPongProducer.BROKER_PROPNAME);
176             String username = testParameters.getProperty(PingPongProducer.USERNAME_PROPNAME);
177             String password = testParameters.getProperty(PingPongProducer.PASSWORD_PROPNAME);
178             String virtualPath = testParameters.getProperty(PingPongProducer.VIRTUAL_HOST_PROPNAME);
179             String destinationName = testParameters.getProperty(PingPongProducer.PING_QUEUE_NAME_PROPNAME);
180             boolean persistent = testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME);
181             boolean transacted = testParameters.getPropertyAsBoolean(PingPongProducer.TRANSACTED_PROPNAME);
182             String selector = testParameters.getProperty(PingPongProducer.SELECTOR_PROPNAME);
183             boolean verbose = testParameters.getPropertyAsBoolean(PingPongProducer.VERBOSE_PROPNAME);
184             boolean pubsub = testParameters.getPropertyAsBoolean(PingPongProducer.PUBSUB_PROPNAME);
185 
186             synchronized (this)
187             {
188                 // Establish a bounce back client on the ping queue to bounce back the pings.
189                 perThreadSetup._testPingBouncer =
190                     new PingPongBouncer(brokerDetails, username, password, virtualPath, destinationName, persistent,
191                         transacted, selector, verbose, pubsub);
192 
193                 // Start the connections for client and producer running.
194                 perThreadSetup._testPingBouncer.getConnection().start();
195 
196                 // Establish a ping-pong client on the ping queue to send the pings and receive replies with.
197                 perThreadSetup._testPingProducer = new PingPongProducer(testParameters);
198                 perThreadSetup._testPingProducer.establishConnection(true, true);
199                 perThreadSetup._testPingProducer.start();
200             }
201 
202             // Attach the per-thread set to the thread.
203             threadSetup.set(perThreadSetup);
204         }
205         catch (Exception e)
206         {
207             _logger.warn("There was an exception during per thread setup.", e);
208         }
209     }
210 
211     /**
212      * Performs test fixture clean
213      */
214     public void threadTearDown()
215     {
216         _logger.debug("public void threadTearDown(): called");
217 
218         try
219         {
220             // Get the per thread test fixture.
221             PerThreadSetup perThreadSetup = threadSetup.get();
222 
223             // Close the pingers so that it cleans up its connection cleanly.
224             synchronized (this)
225             {
226                 perThreadSetup._testPingProducer.close();
227                 // perThreadSetup._testPingBouncer.close();
228             }
229 
230             // Ensure the per thread fixture is reclaimed.
231             threadSetup.remove();
232         }
233         catch (JMSException e)
234         {
235             _logger.warn("There was an exception during per thread tear down.");
236         }
237     }
238 
239     protected static class PerThreadSetup
240     {
241         /**
242          * Holds the test ping-pong producer.
243          */
244         private PingPongProducer _testPingProducer;
245 
246         /**
247          * Holds the test ping client.
248          */
249         private PingPongBouncer _testPingBouncer;
250     }
251 }