PingTestPerf.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.ping;
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.requestreply.PingPongProducer;
030 
031 import org.apache.qpid.junit.extensions.AsymptoticTestCase;
032 import org.apache.qpid.junit.extensions.TestThreadAware;
033 import org.apache.qpid.junit.extensions.util.ParsedProperties;
034 import org.apache.qpid.junit.extensions.util.TestContextProperties;
035 
036 import javax.jms.*;
037 
038 /**
039  * PingTestPerf is a ping test, that has been written with the intention of being scaled up to run many times
040  * simultaneously to simluate many clients/producers/connections.
041  *
042  <p/>A single run of the test using the default JUnit test runner will result in the sending and timing of a single
043  * full round trip ping. This test may be scaled up using a suitable JUnit test runner.
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/threads that may be run,
047  * except if the connection is lost in which case an attempt to re-establish the setup is made.
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 a message on the original queue and waits for a response on the
051  * 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 PingTestPerf extends AsymptoticTestCase implements TestThreadAware
060 {
061     private static Logger _logger = Logger.getLogger(PingTestPerf.class);
062 
063     /** Thread local to hold the per-thread test setup fields. */
064     ThreadLocal<PerThreadSetup> threadSetup = new ThreadLocal<PerThreadSetup>();
065 
066     /** Holds a property reader to extract the test parameters from. */
067     protected ParsedProperties testParameters =
068         TestContextProperties.getInstance(PingPongProducer.defaults /*System.getProperties()*/);
069 
070     public PingTestPerf(String name)
071     {
072         super(name);
073 
074         _logger.debug("testParameters = " + testParameters);
075     }
076 
077     /**
078      * Compile all the tests into a test suite.
079      @return The test method testPingOk.
080      */
081     public static Test suite()
082     {
083         // Build a new test suite
084         TestSuite suite = new TestSuite("Ping Performance Tests");
085 
086         // Run performance tests in read committed mode.
087         suite.addTest(new PingTestPerf("testPingOk"));
088 
089         return suite;
090     }
091 
092     public void testPingOk(int numPingsthrows Exception
093     {
094         if (numPings == 0)
095         {
096             Assert.fail("Number of pings requested was zero.");
097         }
098 
099         // Get the per thread test setup to run the test through.
100         PerThreadSetup perThreadSetup = threadSetup.get();
101 
102         if (perThreadSetup == null)
103         {
104             Assert.fail("Could not get per thread test setup, it was null.");
105         }
106 
107         // Generate a sample message. This message is already time stamped and has its reply-to destination set.
108         Message msg =
109             perThreadSetup._pingClient.getTestMessage(perThreadSetup._pingClient.getReplyDestinations().get(0),
110                 testParameters.getPropertyAsInteger(PingPongProducer.MESSAGE_SIZE_PROPNAME),
111                 testParameters.getPropertyAsBoolean(PingPongProducer.PERSISTENT_MODE_PROPNAME));
112 
113         // start the test
114         long timeout = Long.parseLong(testParameters.getProperty(PingPongProducer.TIMEOUT_PROPNAME));
115         int numReplies = perThreadSetup._pingClient.pingAndWaitForReply(msg, numPings, timeout, null);
116 
117         // Fail the test if the timeout was exceeded.
118         if (numReplies != perThreadSetup._pingClient.getExpectedNumPings(numPings))
119         {
120             Assert.fail("The ping timed out after " + timeout + " ms. Messages Sent = " + numPings + ", MessagesReceived = "
121                 + numReplies);
122         }
123     }
124 
125     /**
126      * Performs test fixture creation on a per thread basis. This will only be called once for each test thread.
127      */
128     public void threadSetUp()
129     {
130         _logger.debug("public void threadSetUp(): called");
131 
132         try
133         {
134             PerThreadSetup perThreadSetup = new PerThreadSetup();
135 
136             // This is synchronized because there is a race condition, which causes one connection to sleep if
137             // all threads try to create connection concurrently.
138             synchronized (this)
139             {
140                 // Establish a client to ping a Destination and listen the reply back from same Destination
141                 perThreadSetup._pingClient = new PingClient(testParameters);
142                 perThreadSetup._pingClient.establishConnection(true, true);
143             }
144             // Start the client connection
145             perThreadSetup._pingClient.start();
146 
147             // Attach the per-thread set to the thread.
148             threadSetup.set(perThreadSetup);
149         }
150         catch (Exception e)
151         {
152             _logger.warn("There was an exception during per thread setup.", e);
153         }
154     }
155 
156     /**
157      * Performs test fixture clean
158      */
159     public void threadTearDown()
160     {
161         _logger.debug("public void threadTearDown(): called");
162 
163         try
164         {
165             // Get the per thread test fixture.
166             PerThreadSetup perThreadSetup = threadSetup.get();
167 
168             // Close the pingers so that it cleans up its connection cleanly.
169             synchronized (this)
170             {
171                 if ((perThreadSetup != null&& (perThreadSetup._pingClient != null))
172                 {
173                     perThreadSetup._pingClient.close();
174                 }
175             }
176         }
177         catch (JMSException e)
178         {
179             _logger.warn("There was an exception during per thread tear down.");
180         }
181         finally
182         {
183             // Ensure the per thread fixture is reclaimed.
184             threadSetup.remove();
185         }
186     }
187 
188     protected static class PerThreadSetup
189     {
190         /**
191          * Holds the test ping client.
192          */
193         protected PingClient _pingClient;
194         protected String _correlationId;
195     }
196 }