aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAllan Stockdill-Mander2013-07-17 09:56:16 (EDT)
committerIan Craggs2013-07-17 10:48:19 (EDT)
commitfb552b92c4dae8ec767c6786c373b50af2e7d4ed (patch)
treeb2c5dcd7ba61d89ab5e80e1e92fd124b0fb6f973
parent19db263133dacb51ed9d3ede6fbdc85817989450 (diff)
downloadorg.eclipse.paho.mqtt.java-fb552b92c4dae8ec767c6786c373b50af2e7d4ed.zip
org.eclipse.paho.mqtt.java-fb552b92c4dae8ec767c6786c373b50af2e7d4ed.tar.gz
org.eclipse.paho.mqtt.java-fb552b92c4dae8ec767c6786c373b50af2e7d4ed.tar.bz2
Bug #405722 - adding Java client tests
Signed-off-by: Allan Stockdill-Mander <asm@uk.ibm.com>
-rw-r--r--org.eclipse.paho.client.mqttv3/pom.xml27
-rw-r--r--org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/BasicTest.java387
-rw-r--r--org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/SendReceiveTest.java500
-rw-r--r--org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/client/MqttAsyncClientPaho.java67
-rw-r--r--org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/client/MqttClientFactoryPaho.java99
-rw-r--r--org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/client/MqttClientPaho.java67
-rw-r--r--org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/properties/TestProperties.java395
-rw-r--r--org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/utilities/MqttV3Receiver.java447
-rw-r--r--org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/utilities/StringUtilities.java226
-rw-r--r--org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/utilities/Utility.java106
-rw-r--r--org.eclipse.paho.client.mqttv3/src/test/resources/logging.properties34
-rw-r--r--org.eclipse.paho.client.mqttv3/src/test/resources/test.properties10
12 files changed, 2365 insertions, 0 deletions
diff --git a/org.eclipse.paho.client.mqttv3/pom.xml b/org.eclipse.paho.client.mqttv3/pom.xml
index 90ab48e..fedc51d 100644
--- a/org.eclipse.paho.client.mqttv3/pom.xml
+++ b/org.eclipse.paho.client.mqttv3/pom.xml
@@ -36,6 +36,15 @@
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.3</version>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.2.1</version>
<executions>
@@ -60,6 +69,16 @@
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.15</version>
+ <configuration>
+ <systemPropertyVariables>
+ <SERVER_URI>${test.server_uri}</SERVER_URI>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
<!-- <plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -83,4 +102,12 @@
</plugin>-->
</plugins>
</build>
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.11</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
</project>
diff --git a/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/BasicTest.java b/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/BasicTest.java
new file mode 100644
index 0000000..071354c
--- /dev/null
+++ b/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/BasicTest.java
@@ -0,0 +1,387 @@
+/* Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ *******************************************************************************/
+
+package org.eclipse.paho.client.mqttv3.test;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.eclipse.paho.client.mqttv3.IMqttClient;
+import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
+import org.eclipse.paho.client.mqttv3.MqttCallback;
+import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
+import org.eclipse.paho.client.mqttv3.MqttException;
+import org.eclipse.paho.client.mqttv3.MqttMessage;
+import org.eclipse.paho.client.mqttv3.MqttTopic;
+import org.eclipse.paho.client.mqttv3.test.client.MqttClientFactoryPaho;
+import org.eclipse.paho.client.mqttv3.test.log.LoggingUtilities;
+import org.eclipse.paho.client.mqttv3.test.properties.TestProperties;
+import org.eclipse.paho.client.mqttv3.test.utilities.Utility;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+/**
+ * Tests providing a basic general coverage for the MQTT client API
+ */
+
+public class BasicTest {
+
+ static final Class<?> cclass = BasicTest.class;
+ private static final String className = cclass.getName();
+ private static final Logger log = Logger.getLogger(className);
+
+ private static URI serverURI;
+ private static MqttClientFactoryPaho clientFactory;
+
+ /**
+ * @throws Exception
+ */
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+
+ try {
+ String methodName = Utility.getMethodName();
+ LoggingUtilities.banner(log, cclass, methodName);
+
+ serverURI = TestProperties.getServerURI();
+ clientFactory = new MqttClientFactoryPaho();
+ clientFactory.open();
+ }
+ catch (Exception exception) {
+ log.log(Level.SEVERE, "caught exception:", exception);
+ throw exception;
+ }
+ }
+
+ /**
+ * @throws Exception
+ */
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ String methodName = Utility.getMethodName();
+ LoggingUtilities.banner(log, cclass, methodName);
+
+ try {
+ if (clientFactory != null) {
+ clientFactory.close();
+ clientFactory.disconnect();
+ }
+ }
+ catch (Exception exception) {
+ log.log(Level.SEVERE, "caught exception:", exception);
+ }
+ }
+
+ /**
+ * @throws Exception
+ */
+ @Test
+ public void testConnect() throws Exception {
+ String methodName = Utility.getMethodName();
+ LoggingUtilities.banner(log, cclass, methodName);
+
+ IMqttClient client = null;
+ try {
+ String clientId = methodName;
+ client = clientFactory.createMqttClient(serverURI, clientId);
+
+ log.info("Connecting...(serverURI:" + serverURI + ", ClientId:" + clientId);
+ client.connect();
+
+ String clientId2 = client.getClientId();
+ log.info("clientId = " + clientId2);
+
+ boolean isConnected = client.isConnected();
+ log.info("isConnected = " + isConnected);
+
+ String id = client.getServerURI();
+ log.info("ServerURI = " + id);
+
+ log.info("Disconnecting...");
+ client.disconnect();
+
+ log.info("Re-Connecting...");
+ client.connect();
+
+ log.info("Disconnecting...");
+ client.disconnect();
+ }
+ catch (MqttException exception) {
+ log.log(Level.SEVERE, "caught exception:", exception);
+ Assert.fail("Unexpected exception: " + exception);
+ }
+ finally {
+ if (client != null) {
+ log.info("Close...");
+ client.close();
+ }
+ }
+ }
+
+ /**
+ * @throws Exception
+ */
+ @Test
+ public void testHAConnect() throws Exception {
+ String methodName = Utility.getMethodName();
+ LoggingUtilities.banner(log, cclass, methodName);
+
+ // Some old clients do not support the new HA interface on the connect call
+ if (clientFactory.isHighAvalabilitySupported() == false) {
+ return;
+ }
+
+ IMqttClient client = null;
+ try {
+ try {
+ String clientId = methodName;
+
+ // If a client does not support the URI list in the connect options, then this test should fail.
+ // We ensure this happens by using a junk URI when creating the client.
+ URI junk = new URI("tcp://junk:123");
+ client = clientFactory.createMqttClient(junk, clientId);
+
+ // The first URI has a good protocol, but has a garbage hostname.
+ // This ensures that a connect is attempted to the the second URI in the list
+ String[] urls = new String[]{"tcp://junk", serverURI.toString()};
+
+ MqttConnectOptions options = new MqttConnectOptions();
+ options.setServerURIs(urls);
+
+ log.info("Connecting...");
+ client.connect(options);
+
+ log.info("Disconnecting...");
+ client.disconnect();
+ }
+ catch (Exception e) {
+ // logger.info(e.getClass().getName() + ": " + e.getMessage());
+ e.printStackTrace();
+ throw e;
+ }
+ }
+ finally {
+ if (client != null) {
+ log.info("Close...");
+ client.close();
+ }
+ }
+ }
+
+ /**
+ * @throws Exception
+ */
+ @Test
+ public void testPubSub() throws Exception {
+ String methodName = Utility.getMethodName();
+ LoggingUtilities.banner(log, cclass, methodName);
+
+ IMqttClient client = null;
+ try {
+ String topicStr = "topic" + "_02";
+ String clientId = methodName;
+ client = clientFactory.createMqttClient(serverURI, clientId);
+
+ log.info("Assigning callback...");
+ MessageListener listener = new MessageListener();
+ client.setCallback(listener);
+
+ log.info("Connecting...(serverURI:" + serverURI + ", ClientId:" + clientId);
+ client.connect();
+
+ log.info("Subscribing to..." + topicStr);
+ client.subscribe(topicStr);
+
+ log.info("Publishing to..." + topicStr);
+ MqttTopic topic = client.getTopic(topicStr);
+ MqttMessage message = new MqttMessage("foo".getBytes());
+ topic.publish(message);
+
+ log.info("Checking msg");
+ MqttMessage msg = listener.getNextMessage();
+ Assert.assertNotNull(msg);
+ Assert.assertEquals("foo", msg.toString());
+
+ log.info("getTopic name");
+ String topicName = topic.getName();
+ log.info("topicName = " + topicName);
+ Assert.assertEquals(topicName, topicStr);
+
+ log.info("Disconnecting...");
+ client.disconnect();
+ }
+ finally {
+ if (client != null) {
+ log.info("Close...");
+ client.close();
+ }
+ }
+ }
+
+ /**
+ * @throws Exception
+ */
+ @Test
+ public void testMsgProperties() throws Exception {
+ String methodName = Utility.getMethodName();
+ LoggingUtilities.banner(log, cclass, methodName);
+
+ log.info("Check defaults for empty message");
+ MqttMessage msg = new MqttMessage();
+ Assert.assertTrue(msg.getQos() == 1);
+ Assert.assertTrue(msg.isDuplicate() == false);
+ Assert.assertTrue(msg.isRetained() == false);
+ Assert.assertNotNull(msg.getPayload());
+ Assert.assertTrue(msg.getPayload().length == 0);
+ Assert.assertEquals(msg.toString(), "");
+
+ log.info("Check defaults for message with payload");
+ msg = new MqttMessage("foo".getBytes());
+ Assert.assertTrue(msg.getQos() == 1);
+ Assert.assertTrue(msg.isDuplicate() == false);
+ Assert.assertTrue(msg.isRetained() == false);
+ Assert.assertTrue(msg.getPayload().length == 3);
+ Assert.assertEquals(msg.toString(), "foo");
+
+ log.info("Check qos");
+ msg.setQos(0);
+ Assert.assertTrue(msg.getQos() == 0);
+ msg.setQos(1);
+ Assert.assertTrue(msg.getQos() == 1);
+ msg.setQos(2);
+ Assert.assertTrue(msg.getQos() == 2);
+
+ boolean thrown = false;
+ try {
+ msg.setQos(-1);
+ }
+ catch (IllegalArgumentException iae) {
+ thrown = true;
+ }
+ Assert.assertTrue(thrown);
+ thrown = false;
+ try {
+ msg.setQos(3);
+ }
+ catch (IllegalArgumentException iae) {
+ thrown = true;
+ }
+ Assert.assertTrue(thrown);
+ thrown = false;
+
+ log.info("Check payload");
+ msg.setPayload("foobar".getBytes());
+ Assert.assertTrue(msg.getPayload().length == 6);
+ Assert.assertEquals(msg.toString(), "foobar");
+
+ msg.clearPayload();
+ Assert.assertNotNull(msg.getPayload());
+ Assert.assertTrue(msg.getPayload().length == 0);
+ Assert.assertEquals(msg.toString(), "");
+
+ log.info("Check retained");
+ msg.setRetained(true);
+ Assert.assertTrue(msg.isRetained() == true);
+ msg.setRetained(false);
+ Assert.assertTrue(msg.isRetained() == false);
+ }
+
+ /**
+ * @throws Exception
+ */
+ @Test
+ public void testConnOptDefaults() throws Exception {
+ String methodName = Utility.getMethodName();
+ LoggingUtilities.banner(log, cclass, methodName);
+
+ log.info("Check MqttConnectOptions defaults");
+ MqttConnectOptions connOpts = new MqttConnectOptions();
+ Assert.assertEquals(new Integer(connOpts.getKeepAliveInterval()), new Integer(60));
+ Assert.assertNull(connOpts.getPassword());
+ Assert.assertNull(connOpts.getUserName());
+ Assert.assertNull(connOpts.getSocketFactory());
+ Assert.assertTrue(connOpts.isCleanSession());
+ Assert.assertNull(connOpts.getWillDestination());
+ Assert.assertNull(connOpts.getWillMessage());
+ Assert.assertNull(connOpts.getSSLProperties());
+ }
+
+ // -------------------------------------------------------------
+ // Helper methods/classes
+ // -------------------------------------------------------------
+
+ static final Class<MessageListener> cclass2 = MessageListener.class;
+ static final String classSimpleName2 = cclass2.getSimpleName();
+ static final String classCanonicalName2 = cclass2.getCanonicalName();
+ static final Logger logger2 = Logger.getLogger(classCanonicalName2);
+
+ /**
+ *
+ */
+ class MessageListener implements MqttCallback {
+
+ ArrayList<MqttMessage> messages;
+
+ public MessageListener() {
+ messages = new ArrayList<MqttMessage>();
+ }
+
+ public MqttMessage getNextMessage() {
+ synchronized (messages) {
+ if (messages.size() == 0) {
+ try {
+ messages.wait(1000);
+ }
+ catch (InterruptedException e) {
+ // empty
+ }
+ }
+
+ if (messages.size() == 0) {
+ return null;
+ }
+ return messages.remove(0);
+ }
+ }
+
+ public void connectionLost(Throwable cause) {
+ logger2.info("connection lost: " + cause.getMessage());
+ }
+
+ /**
+ * @param token
+ */
+ public void deliveryComplete(IMqttDeliveryToken token) {
+ logger2.info("delivery complete");
+ }
+
+ /**
+ * @param topic
+ * @param message
+ * @throws Exception
+ */
+ public void messageArrived(String topic, MqttMessage message) throws Exception {
+ logger2.info("message arrived: " + new String(message.getPayload()) + "'");
+
+ synchronized (messages) {
+ messages.add(message);
+ messages.notifyAll();
+ }
+ }
+ }
+}
diff --git a/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/SendReceiveTest.java b/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/SendReceiveTest.java
new file mode 100644
index 0000000..c8c1cf0
--- /dev/null
+++ b/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/SendReceiveTest.java
@@ -0,0 +1,500 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ *******************************************************************************/
+
+package org.eclipse.paho.client.mqttv3.test;
+
+import java.net.URI;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.eclipse.paho.client.mqttv3.IMqttClient;
+import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
+import org.eclipse.paho.client.mqttv3.MqttTopic;
+import org.eclipse.paho.client.mqttv3.test.client.MqttClientFactoryPaho;
+import org.eclipse.paho.client.mqttv3.test.log.LoggingUtilities;
+import org.eclipse.paho.client.mqttv3.test.properties.TestProperties;
+import org.eclipse.paho.client.mqttv3.test.utilities.MqttV3Receiver;
+import org.eclipse.paho.client.mqttv3.test.utilities.Utility;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * This test expects an MQTT Server to be listening on the port
+ * given by the SERVER_URI property (which is 1883 by default)
+ */
+public class SendReceiveTest {
+
+ static final Class<?> cclass = SendReceiveTest.class;
+ private static final String className = cclass.getName();
+ private static final Logger log = Logger.getLogger(className);
+
+ private static URI serverURI;
+ private static MqttClientFactoryPaho clientFactory;
+
+ /**
+ * @throws Exception
+ */
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+
+ try {
+ String methodName = Utility.getMethodName();
+ LoggingUtilities.banner(log, cclass, methodName);
+
+ serverURI = TestProperties.getServerURI();
+ clientFactory = new MqttClientFactoryPaho();
+ clientFactory.open();
+ }
+ catch (Exception exception) {
+ log.log(Level.SEVERE, "caught exception:", exception);
+ throw exception;
+ }
+ }
+
+ /**
+ * @throws Exception
+ */
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ String methodName = Utility.getMethodName();
+ LoggingUtilities.banner(log, cclass, methodName);
+
+ try {
+ if (clientFactory != null) {
+ clientFactory.close();
+ clientFactory.disconnect();
+ }
+ }
+ catch (Exception exception) {
+ log.log(Level.SEVERE, "caught exception:", exception);
+ }
+ }
+
+ /**
+ * Tests that a client can be constructed and that it can connect to and disconnect from the
+ * service
+ * @throws Exception
+ */
+ @Test
+ public void testConnect() throws Exception {
+ final String methodName = Utility.getMethodName();
+ LoggingUtilities.banner(log, cclass, methodName);
+ log.entering(className, methodName);
+
+ IMqttClient mqttClient = null;
+ try {
+ mqttClient = clientFactory.createMqttClient(serverURI, methodName);
+ log.info("Connecting...(serverURI:" + serverURI + ", ClientId:" + methodName);
+ mqttClient.connect();
+ log.info("Disconnecting...");
+ mqttClient.disconnect();
+ log.info("Connecting...(serverURI:" + serverURI + ", ClientId:" + methodName);
+ mqttClient.connect();
+ log.info("Disconnecting...");
+ mqttClient.disconnect();
+ }
+ catch (Exception exception) {
+ log.log(Level.SEVERE, "caught exception:", exception);
+ Assert.fail("Failed:" + methodName + " exception=" + exception);
+ }
+ finally {
+ if (mqttClient != null) {
+ log.info("Close...");
+ mqttClient.close();
+ }
+ }
+
+ log.exiting(className, methodName);
+ }
+
+ /**
+ * Test connection using a remote host name for the local host.
+ * @throws Exception
+ */
+ @Test
+ public void testRemoteConnect() throws Exception {
+ final String methodName = Utility.getMethodName();
+ LoggingUtilities.banner(log, cclass, methodName);
+ log.entering(className, methodName);
+
+ IMqttClient mqttClient = null;
+ try {
+ mqttClient = clientFactory.createMqttClient(serverURI, methodName);
+ log.info("Connecting...(serverURI:" + serverURI + ", ClientId:" + methodName);
+ mqttClient.connect();
+ log.info("Disconnecting...");
+ mqttClient.disconnect();
+
+ MqttV3Receiver mqttV3Receiver = new MqttV3Receiver(mqttClient, LoggingUtilities.getPrintStream());
+ log.info("Assigning callback...");
+ mqttClient.setCallback(mqttV3Receiver);
+ MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
+ mqttConnectOptions.setCleanSession(false);
+ log.info("Connecting...(serverURI:" + serverURI + ", ClientId:" + methodName + ", cleanSession: false");
+ mqttClient.connect(mqttConnectOptions);
+
+ String[] topicNames = new String[]{methodName + "/Topic"};
+ int[] topicQos = {0};
+ log.info("Subscribing to..." + topicNames[0]);
+ mqttClient.subscribe(topicNames, topicQos);
+
+ byte[] payload = ("Message payload " + className + "." + methodName).getBytes();
+ MqttTopic mqttTopic = mqttClient.getTopic(topicNames[0]);
+ log.info("Publishing to..." + topicNames[0]);
+ mqttTopic.publish(payload, 1, false);
+ boolean ok = mqttV3Receiver.validateReceipt(topicNames[0], 0, payload);
+ if (!ok) {
+ Assert.fail("Receive failed");
+ }
+ log.info("Disconnecting...");
+ mqttClient.disconnect();
+ }
+ catch (Exception exception) {
+ log.log(Level.SEVERE, "caught exception:", exception);
+ Assert.fail("Failed:" + methodName + " exception=" + exception);
+ }
+ finally {
+ if (mqttClient != null) {
+ log.info("Close...");
+ mqttClient.close();
+ }
+ }
+
+ log.exiting(className, methodName);
+ }
+
+ /**
+ * Test client pubSub using largish messages
+ */
+ @Test
+ public void testLargeMessage() {
+ final String methodName = Utility.getMethodName();
+ LoggingUtilities.banner(log, cclass, methodName);
+ log.entering(className, methodName);
+
+ IMqttClient mqttClient = null;
+ try {
+ mqttClient = clientFactory.createMqttClient(serverURI, methodName);
+ MqttV3Receiver mqttV3Receiver = new MqttV3Receiver(mqttClient, LoggingUtilities.getPrintStream());
+ log.info("Assigning callback...");
+ mqttClient.setCallback(mqttV3Receiver);
+ log.info("Connecting...(serverURI:" + serverURI + ", ClientId:" + methodName);
+ mqttClient.connect();
+
+ int largeSize = 10000;
+ String[] topicNames = new String[]{methodName + "/Topic"};
+ int[] topicQos = {0};
+ byte[] message = new byte[largeSize];
+
+ java.util.Arrays.fill(message, (byte) 's');
+
+ log.info("Subscribing to..." + topicNames[0]);
+ mqttClient.subscribe(topicNames, topicQos);
+ log.info("Unsubscribing from..." + topicNames[0]);
+ mqttClient.unsubscribe(topicNames);
+ log.info("Subscribing to..." + topicNames[0]);
+ mqttClient.subscribe(topicNames, topicQos);
+ MqttTopic mqttTopic = mqttClient.getTopic(topicNames[0]);
+ log.info("Publishing to..." + topicNames[0]);
+ mqttTopic.publish(message, 0, false);
+
+ boolean ok = mqttV3Receiver.validateReceipt(topicNames[0], 0, message);
+ if (!ok) {
+ Assert.fail("Receive failed");
+ }
+ }
+ catch (Exception exception) {
+ log.log(Level.SEVERE, "caught exception:", exception);
+ Assert.fail("Failed to instantiate:" + methodName + " exception=" + exception);
+ }
+ finally {
+ try {
+ log.info("Disconnecting...");
+ mqttClient.disconnect();
+ log.info("Close...");
+ mqttClient.close();
+ }
+ catch (Exception exception) {
+ log.log(Level.SEVERE, "caught exception:", exception);
+ }
+ }
+
+ log.exiting(className, methodName);
+ }
+
+ /**
+ * Test that QOS values are preserved between MQTT publishers and subscribers.
+ */
+ @Test
+ public void testQoSPreserved() {
+ final String methodName = Utility.getMethodName();
+ LoggingUtilities.banner(log, cclass, methodName);
+ log.entering(className, methodName);
+
+ IMqttClient mqttClient = null;
+ try {
+ mqttClient = clientFactory.createMqttClient(serverURI, methodName);
+ MqttV3Receiver mqttV3Receiver = new MqttV3Receiver(mqttClient, LoggingUtilities.getPrintStream());
+ log.info("Assigning callback...");
+ mqttClient.setCallback(mqttV3Receiver);
+ log.info("Connecting...(serverURI:" + serverURI + ", ClientId:" + methodName);
+ mqttClient.connect();
+
+ String[] topicNames = new String[]{methodName + "/Topic0", methodName + "/Topic1", methodName + "/Topic2"};
+ int[] topicQos = {0, 1, 2};
+ for (int i = 0; i < topicNames.length; i++) {
+ log.info("Subscribing to..." + topicNames[i] + " at Qos " + topicQos[i]);
+ }
+ mqttClient.subscribe(topicNames, topicQos);
+
+ for (int i = 0; i < topicNames.length; i++) {
+ byte[] message = ("Message payload " + className + "." + methodName + " " + topicNames[i]).getBytes();
+ MqttTopic mqttTopic = mqttClient.getTopic(topicNames[i]);
+ for (int iQos = 0; iQos < 3; iQos++) {
+ log.info("Publishing to..." + topicNames[i] + " at Qos " + iQos);
+ mqttTopic.publish(message, iQos, false);
+ boolean ok = mqttV3Receiver.validateReceipt(topicNames[i], Math.min(iQos, topicQos[i]), message);
+ if (!ok) {
+ Assert.fail("Receive failed sub Qos=" + topicQos[i] + " PublishQos=" + iQos);
+ }
+ }
+ }
+ }
+ catch (Exception exception) {
+ log.log(Level.SEVERE, "caught exception:", exception);
+ Assert.fail("Failed:" + methodName + " exception=" + exception);
+ }
+ finally {
+ try {
+ log.info("Disconnecting...");
+ mqttClient.disconnect();
+ log.info("Close...");
+ mqttClient.close();
+ }
+ catch (Exception exception) {
+ log.log(Level.SEVERE, "caught exception:", exception);
+ }
+ }
+
+ log.exiting(className, methodName);
+ }
+
+ /**
+ * Multiple publishers and subscribers.
+ * @throws Exception
+ */
+ @Test
+ public void testMultipleClients() throws Exception {
+ final String methodName = Utility.getMethodName();
+ LoggingUtilities.banner(log, cclass, methodName);
+ log.entering(className, methodName);
+
+ IMqttClient[] mqttPublisher = new IMqttClient[2];
+ IMqttClient[] mqttSubscriber = new IMqttClient[10];
+ try {
+ String[] topicNames = new String[]{methodName + "/Topic"};
+ int[] topicQos = {0};
+
+ MqttTopic[] mqttTopic = new MqttTopic[mqttPublisher.length];
+ for (int i = 0; i < mqttPublisher.length; i++) {
+ mqttPublisher[i] = clientFactory.createMqttClient(serverURI, "MultiPub" + i);
+ log.info("Connecting...(serverURI:" + serverURI + ", ClientId: MultiPub" + i);
+ mqttPublisher[i].connect();
+ mqttTopic[i] = mqttPublisher[i].getTopic(topicNames[0]);
+
+ } // for...
+
+ MqttV3Receiver[] mqttV3Receiver = new MqttV3Receiver[mqttSubscriber.length];
+ for (int i = 0; i < mqttSubscriber.length; i++) {
+ mqttSubscriber[i] = clientFactory.createMqttClient(serverURI, "MultiSubscriber" + i);
+ mqttV3Receiver[i] = new MqttV3Receiver(mqttSubscriber[i], LoggingUtilities.getPrintStream());
+ log.info("Assigning callback...");
+ mqttSubscriber[i].setCallback(mqttV3Receiver[i]);
+ log.info("Connecting...(serverURI:" + serverURI + ", ClientId: MultiSubscriber" + i);
+ mqttSubscriber[i].connect();
+ log.info("Subcribing to..." + topicNames[0]);
+ mqttSubscriber[i].subscribe(topicNames, topicQos);
+ } // for...
+
+ for (int iMessage = 0; iMessage < 10; iMessage++) {
+ byte[] payload = ("Message " + iMessage).getBytes();
+ for (int i = 0; i < mqttPublisher.length; i++) {
+ log.info("Publishing to..." + topicNames[0]);
+ mqttTopic[i].publish(payload, 0, false);
+ }
+
+ for (int i = 0; i < mqttSubscriber.length; i++) {
+ for (int ii = 0; ii < mqttPublisher.length; ii++) {
+ boolean ok = mqttV3Receiver[i].validateReceipt(topicNames[0], 0, payload);
+ if (!ok) {
+ Assert.fail("Receive failed");
+ }
+ } // for publishers...
+ } // for subscribers...
+ } // for messages...
+
+ }
+ catch (Exception exception) {
+ log.log(Level.SEVERE, "caught exception:", exception);
+ throw exception;
+ }
+ finally {
+ try {
+ for (int i = 0; i < mqttPublisher.length; i++) {
+ log.info("Disconnecting...MultiPub" + i);
+ mqttPublisher[i].disconnect();
+ log.info("Close...");
+ mqttPublisher[i].close();
+ }
+ for (int i = 0; i < mqttSubscriber.length; i++) {
+ log.info("Disconnecting...MultiSubscriber" + i);
+ mqttSubscriber[i].disconnect();
+ log.info("Close...");
+ mqttSubscriber[i].close();
+ }
+
+ Thread.sleep(5000);
+ }
+ catch (Exception exception) {
+ log.log(Level.SEVERE, "caught exception:", exception);
+ }
+ }
+
+ log.exiting(className, methodName);
+ }
+
+ /**
+ * Test the behaviour of the cleanStart flag, used to clean up before re-connecting.
+ * @throws Exception
+ */
+ @Test
+ public void testCleanStart() throws Exception {
+ final String methodName = Utility.getMethodName();
+ LoggingUtilities.banner(log, cclass, methodName);
+ log.entering(className, methodName);
+
+ IMqttClient mqttClient = null;
+ try {
+ mqttClient = clientFactory.createMqttClient(serverURI, methodName);
+ MqttV3Receiver mqttV3Receiver = new MqttV3Receiver(mqttClient, LoggingUtilities.getPrintStream());
+ log.info("Assigning callback...");
+ mqttClient.setCallback(mqttV3Receiver);
+ MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
+ // Clean start: true - The broker cleans up all client state, including subscriptions, when the client is disconnected.
+ // Clean start: false - The broker remembers all client state, including subscriptions, when the client is disconnected.
+ // Matching publications will get queued in the broker whilst the client is disconnected.
+ // For Mqtt V3 cleanSession=false, implies new subscriptions are durable.
+ mqttConnectOptions.setCleanSession(false);
+ log.info("Connecting...(serverURI:" + serverURI + ", ClientId:" + methodName + ", cleanSession: false");
+ mqttClient.connect(mqttConnectOptions);
+
+ String[] topicNames = new String[]{methodName + "/Topic"};
+ int[] topicQos = {0};
+ log.info("Subscribing to..." + topicNames[0]);
+ mqttClient.subscribe(topicNames, topicQos);
+
+ byte[] payload = ("Message payload " + className + "." + methodName + " First").getBytes();
+ MqttTopic mqttTopic = mqttClient.getTopic(topicNames[0]);
+ log.info("Publishing to..." + topicNames[0]);
+ mqttTopic.publish(payload, 1, false);
+ boolean ok = mqttV3Receiver.validateReceipt(topicNames[0], 0, payload);
+ if (!ok) {
+ Assert.fail("Receive failed");
+ }
+
+ // Disconnect and reconnect to make sure the subscription and all queued messages are cleared.
+ log.info("Disconnecting...");
+ mqttClient.disconnect();
+ log.info("Close");
+ mqttClient.close();
+
+ // Send a message from another client, to our durable subscription.
+ mqttClient = clientFactory.createMqttClient(serverURI, methodName + "Other");
+ mqttV3Receiver = new MqttV3Receiver(mqttClient, LoggingUtilities.getPrintStream());
+ log.info("Assigning callback...");
+ mqttClient.setCallback(mqttV3Receiver);
+ mqttConnectOptions = new MqttConnectOptions();
+ mqttConnectOptions.setCleanSession(true);
+ log.info("Connecting...(serverURI:" + serverURI + ", ClientId:" + methodName + "Other, cleanSession: true");
+ mqttClient.connect(mqttConnectOptions);
+ // Receive the publication so that we can be sure the first client has also received it.
+ // Otherwise the first client may reconnect with its clean session before the message has arrived.
+ log.info("Subscribing to..." + topicNames[0]);
+ mqttClient.subscribe(topicNames, topicQos);
+ payload = ("Message payload " + className + "." + methodName + " Other client").getBytes();
+ mqttTopic = mqttClient.getTopic(topicNames[0]);
+ log.info("Publishing to..." + topicNames[0]);
+ mqttTopic.publish(payload, 1, false);
+ ok = mqttV3Receiver.validateReceipt(topicNames[0], 0, payload);
+ if (!ok) {
+ Assert.fail("Receive failed");
+ }
+ log.info("Disconnecting...");
+ mqttClient.disconnect();
+ log.info("Close...");
+ mqttClient.close();
+
+ // Reconnect and check we have no messages.
+ mqttClient = clientFactory.createMqttClient(serverURI, methodName);
+ mqttV3Receiver = new MqttV3Receiver(mqttClient, LoggingUtilities.getPrintStream());
+ log.info("Assigning callback...");
+ mqttClient.setCallback(mqttV3Receiver);
+ mqttConnectOptions = new MqttConnectOptions();
+ mqttConnectOptions.setCleanSession(true);
+ log.info("Connecting...(serverURI:" + serverURI + ", ClientId:" + methodName + ", cleanSession: true");
+ mqttClient.connect(mqttConnectOptions);
+ MqttV3Receiver.ReceivedMessage receivedMessage = mqttV3Receiver.receiveNext(100);
+ if (receivedMessage != null) {
+ Assert.fail("Receive messaqe:" + new String(receivedMessage.message.getPayload()));
+ }
+
+ // Also check that subscription is cancelled.
+ payload = ("Message payload " + className + "." + methodName + " Cancelled Subscription").getBytes();
+ mqttTopic = mqttClient.getTopic(topicNames[0]);
+ log.info("Publishing to..." + topicNames[0]);
+ mqttTopic.publish(payload, 1, false);
+
+ receivedMessage = mqttV3Receiver.receiveNext(100);
+ if (receivedMessage != null) {
+ log.info("Message I shouldn't have: " + new String(receivedMessage.message.getPayload()));
+ Assert.fail("Receive messaqe:" + new String(receivedMessage.message.getPayload()));
+ }
+ }
+ catch (Exception exception) {
+ log.log(Level.SEVERE, "caught exception:", exception);
+ throw exception;
+ }
+ finally {
+ try {
+ log.info("Disconnecting...");
+ mqttClient.disconnect();
+ }
+ catch (Exception exception) {
+ // do nothing
+ }
+
+ try {
+ log.info("Close...");
+ mqttClient.close();
+ }
+ catch (Exception exception) {
+ // do nothing
+ }
+ }
+
+ log.exiting(className, methodName);
+ }
+}
diff --git a/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/client/MqttAsyncClientPaho.java b/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/client/MqttAsyncClientPaho.java
new file mode 100644
index 0000000..762b36a
--- /dev/null
+++ b/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/client/MqttAsyncClientPaho.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ *******************************************************************************/
+
+package org.eclipse.paho.client.mqttv3.test.client;
+
+import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
+import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
+import org.eclipse.paho.client.mqttv3.MqttException;
+
+
+/**
+ *
+ */
+public class MqttAsyncClientPaho extends MqttAsyncClient {
+
+ /**
+ * @param serverURI
+ * @param clientId
+ * @throws MqttException
+ */
+ public MqttAsyncClientPaho(String serverURI, String clientId) throws MqttException {
+ super(serverURI, clientId);
+ }
+
+ /**
+ * @param serverURI
+ * @param clientId
+ * @param persistence
+ * @throws MqttException
+ */
+ public MqttAsyncClientPaho(String serverURI, String clientId, MqttClientPersistence persistence) throws MqttException {
+ super(serverURI, clientId, persistence);
+ }
+
+ /**
+ * @throws Exception
+ */
+ public void startTrace() throws Exception {
+ // not implemented
+ }
+
+ /**
+ * @throws Exception
+ */
+ public void stopTrace() throws Exception {
+ // not implemented
+ }
+
+ /**
+ * @return trace buffer
+ * @throws Exception
+ */
+ public String getTraceLog() throws Exception {
+ return null;
+ }
+}
diff --git a/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/client/MqttClientFactoryPaho.java b/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/client/MqttClientFactoryPaho.java
new file mode 100644
index 0000000..47adff8
--- /dev/null
+++ b/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/client/MqttClientFactoryPaho.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ *******************************************************************************/
+
+package org.eclipse.paho.client.mqttv3.test.client;
+
+import java.net.URI;
+import org.eclipse.paho.client.mqttv3.IMqttClient;
+import org.eclipse.paho.client.mqttv3.IMqttAsyncClient;
+import org.eclipse.paho.client.mqttv3.MqttException;
+import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
+
+
+/**
+ *
+ */
+public class MqttClientFactoryPaho {
+
+ /**
+ * @param serverURI
+ * @param clientId
+ * @return MqttClient
+ * @throws Exception
+ */
+ public IMqttClient createMqttClient(URI serverURI, String clientId) throws Exception {
+ return new MqttClientPaho(serverURI.toString(), clientId);
+ }
+
+ /**
+ * @param serverURI
+ * @param clientId
+ * @param persistence
+ * @return MqttClient
+ * @throws Exception
+ */
+ public IMqttClient createMqttClient(URI serverURI, String clientId, MqttClientPersistence persistence) throws Exception {
+ return new MqttClientPaho(serverURI.toString(), clientId, persistence);
+ }
+
+ /**
+ * @param serverURI
+ * @param clientId
+ * @return client
+ * @throws Exception
+ */
+ public IMqttAsyncClient createMqttAsyncClient(URI serverURI, String clientId) throws Exception {
+ return new MqttAsyncClientPaho(serverURI.toString(), clientId);
+ }
+
+ /**
+ * @param serverURI
+ * @param clientId
+ * @param persistence
+ * @return client
+ * @throws Exception
+ */
+ public IMqttAsyncClient createMqttAsyncClient(URI serverURI, String clientId, MqttClientPersistence persistence) throws Exception {
+ return new MqttAsyncClientPaho(serverURI.toString(), clientId, persistence);
+ }
+
+ /**
+ *
+ */
+ public void open() {
+ // empty
+ }
+
+ /**
+ *
+ */
+ public void close() {
+ // empty
+ }
+
+ /**
+ *
+ */
+ public void disconnect() {
+ // empty
+ }
+
+ /**
+ * @return flag indicating if this client supports High Availability
+ */
+ public boolean isHighAvalabilitySupported() {
+ return true;
+ }
+
+}
diff --git a/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/client/MqttClientPaho.java b/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/client/MqttClientPaho.java
new file mode 100644
index 0000000..527bdaf
--- /dev/null
+++ b/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/client/MqttClientPaho.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ *******************************************************************************/
+
+package org.eclipse.paho.client.mqttv3.test.client;
+
+import org.eclipse.paho.client.mqttv3.MqttClient;
+import org.eclipse.paho.client.mqttv3.MqttClientPersistence;
+import org.eclipse.paho.client.mqttv3.MqttException;
+
+
+/**
+ *
+ */
+public class MqttClientPaho extends MqttClient {
+
+ /**
+ * @param serverURI
+ * @param clientId
+ * @throws MqttException
+ */
+ public MqttClientPaho(String serverURI, String clientId) throws MqttException {
+ super(serverURI, clientId);
+ }
+
+ /**
+ * @param serverURI
+ * @param clientId
+ * @param persistence
+ * @throws MqttException
+ */
+ public MqttClientPaho(String serverURI, String clientId, MqttClientPersistence persistence) throws MqttException {
+ super(serverURI, clientId, persistence);
+ }
+
+ /**
+ * @throws Exception
+ */
+ public void startTrace() throws Exception {
+ // not implemented
+ }
+
+ /**
+ * @throws Exception
+ */
+ public void stopTrace() throws Exception {
+ // not implemented
+ }
+
+ /**
+ * @return trace buffer
+ * @throws Exception
+ */
+ public String getTraceLog() throws Exception {
+ return null;
+ }
+}
diff --git a/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/properties/TestProperties.java b/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/properties/TestProperties.java
new file mode 100644
index 0000000..97cac46
--- /dev/null
+++ b/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/properties/TestProperties.java
@@ -0,0 +1,395 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ *******************************************************************************/
+
+package org.eclipse.paho.client.mqttv3.test.properties;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.eclipse.paho.client.mqttv3.test.client.MqttClientFactoryPaho;
+import org.eclipse.paho.client.mqttv3.test.utilities.Utility;
+import org.junit.Assert;
+
+/**
+ * Contains the classes and utilities used to configure the MQTT testcases.
+ *
+ * <p>
+ * The way in which a test is run is controlled by properties which are typically supplied in property file.
+ * A {@link org.eclipse.paho.client.mqttv3.test.properties.TestProperties TestProperties} class provides default values and getter methods
+ * to supported properties. When the properties have been loaded, the framework logs the non-default values
+ *
+ * <p>
+ * Test properties are initialised by loading a property file as follows:
+ * <ul>
+ * <li>Get filename from system property
+ * <ul>
+ * <li>Get filename from system property <code>TEST_PROPERTY_FILENAME</code> with a default value of <code>test.properties</code>
+ * <li>Load properties using this filename as a file on the filesystem
+ * </ul>
+ *
+ * <li>Else use the default filename
+ * <ul>
+ * <li>Use the default filename <code>test.properties</code>
+ * <li>Load properties using this filename as a resource in the same package as the TestProperties class
+ * </ul>
+ * </ul>
+ *
+ * <p>
+ * A property loaded from a file is overridden by a system property of the same name.
+ *
+ * <p>
+ * A property file loaded as a resource may be located in any eclipse project on the runtime classpath.
+ * <ul>
+ * <li>Note: If you intend to run a testcase to run against a server but the the property file is in a server eclipse project,
+ * remember to set the "project" setting on the eclipse run configuration to make eclipse add the server eclipse project
+ * to the runtime classpath, otherwise the testcase will load the wrong properties
+ * </ul>
+ *
+ */
+public class TestProperties {
+
+ static private final Class<?> cclass = TestProperties.class;
+ static private final String className = cclass.getName();
+ static private final Logger log = Logger.getLogger(className);
+
+ /**
+ * The URI of the test MQTT Server.
+ * <p>
+ * The default value is <code>tcp://&lt;localhost&gt;:1883</code>> where <code>&lt;localhost&gt;</code> is expressed as a IPv4 dotted decimal value
+ */
+ static public final String KEY_SERVER_URI = "SERVER_URI";
+
+ /**
+ * The working directory usd by the framework.
+ * <p>
+ * The default value is system property <code>java.io.tmpdir</code>
+ */
+ static public final String KEY_WORKING_DIR = "WORKING_DIR";
+
+ /**
+ * The class name of the client factory the tests are to be run against.
+ *
+ * <p>
+ * The following client factories have been defined
+ * <ul>
+ * <li>{@link org.eclipse.paho.client.mqttv3.test.client.pahoJava.MqttClientFactoryPahoJava PahaJava} (This is the default)
+ * </ul
+ */
+ static public final String KEY_CLIENT_TYPE = "CLIENT_TYPE";
+
+ static private Map<String, String> defaults = new HashMap<String, String>();
+
+ private static TestProperties singleton;
+ private Properties properties = new Properties();
+
+ static {
+ String temporaryDirectoryName = System.getProperty("java.io.tmpdir");
+
+ String localhost = "localhost";
+ try {
+ localhost = InetAddress.getLocalHost().getHostAddress();
+ }
+ catch (UnknownHostException e) {
+ // empty
+ }
+ String defaultServerURI = "tcp://" + localhost + ":1883";
+
+ putDefault(KEY_WORKING_DIR, temporaryDirectoryName);
+ putDefault(KEY_SERVER_URI, defaultServerURI);
+ putDefault(KEY_CLIENT_TYPE, MqttClientFactoryPaho.class.getName());
+
+ // Make sure all the property classes we know about get initialised
+ List<String> list = new ArrayList<String>();
+ list.add("org.eclipse.paho.client.mqttv3.test.properties.ClientTestProperties");
+ list.add("org.eclipse.paho.client.mqttv3.test.properties.MqTestProperties");
+ list.add("org.eclipse.paho.client.mqttv3.test.properties.ImsTestProperties");
+
+ for (String name : list) {
+ try {
+ Class.forName(name);
+ }
+ catch (ClassNotFoundException exception) {
+ log.finest("Property class '" + name + "' not found");
+ }
+ }
+ }
+
+ /**
+ * @param key
+ * @param defaultValue
+ */
+ public static void putDefault(String key, String defaultValue) {
+ defaults.put(key, defaultValue);
+ }
+
+ /**
+ * @return TestProperties
+ */
+ public static TestProperties getInstance() {
+ if (singleton == null) {
+ singleton = new TestProperties();
+ }
+ return singleton;
+ }
+
+ /**
+ * Reads properties from a properties file in the same path as this class
+ * - first look for the property file on the filesystem
+ */
+ public TestProperties() {
+
+ InputStream stream = null;
+ try {
+ String filename = System.getProperty("TEST_PROPERTY_FILENAME", "test.properties");
+ stream = getPropertyFileAsStream(filename);
+
+ if (stream == null) {
+ filename = "test.properties";
+ stream = cclass.getClassLoader().getResourceAsStream(filename);
+ }
+
+ // Read the properties from the property file
+ if (stream != null) {
+ log.info("Loading properties from: '" + filename + "'");
+ properties.load(stream);
+ }
+ }
+ catch (Exception e) {
+ log.log(Level.SEVERE, "caught exception:", e);
+ }
+ finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ }
+ catch (IOException e) {
+ log.log(Level.SEVERE, "caught exception:", e);
+ }
+ }
+ }
+
+ // Override the default property values from SystemProperties
+ for (String key : defaults.keySet()) {
+ String systemValue = System.getProperty(key);
+ if (systemValue != null) {
+ properties.put(key, systemValue);
+ }
+ }
+
+ for (Object object : properties.keySet()) {
+ if (object instanceof String) {
+ String key = (String) object;
+
+ // Override the property values from SystemProperties
+ String systemValue = System.getProperty(key);
+ if (systemValue != null) {
+ properties.put(key, systemValue);
+ }
+
+ String defaultValue = defaults.get(key);
+ String value = getProperty(key);
+
+ // Output the non-default properties
+ boolean isSame = false;
+
+ if (defaultValue == null) {
+ if (value == null) {
+ isSame = true;
+ }
+ }
+ else if (value != null) {
+ isSame = defaultValue.equals(value);
+ }
+
+ if (systemValue != null) {
+ log.info(" System property: " + key + " = " + getProperty(key));
+ }
+ else if (isSame == false) {
+ log.info(" " + key + " = " + getProperty(key));
+ }
+ }
+ }
+ }
+
+ /**
+ * @param filename
+ * @return stream
+ * @throws IOException
+ */
+ private InputStream getPropertyFileAsStream(String filename) throws IOException {
+ InputStream stream = null;
+ try {
+ stream = new FileInputStream(filename);
+ }
+ catch (Exception exception) {
+ log.finest("Property file: '" + filename + "' not found");
+ }
+
+ return stream;
+ }
+
+ /**
+ * This is equivalent to class.getResourceAsStream but allows us to report the URL location
+ *
+ * @param filename
+ * @return stream
+ * @throws IOException
+ */
+ private InputStream getPropertyResourceAsStream(String filename) throws IOException {
+
+ InputStream stream = null;
+ URL url = TestProperties.class.getResource(filename);
+
+ if (url == null) {
+ log.info("Property resource: '" + filename + "' not found");
+ }
+ else {
+ log.info("Property resource: '" + filename + "' found at '" + url + "'");
+ stream = url.openStream();
+
+ if (stream == null) {
+ log.info("Could not open stream to Property resource: '" + filename + "'");
+ }
+ }
+
+ return stream;
+ }
+
+ /**
+ * @param key
+ * @return value
+ */
+ public String getProperty(String key) {
+ String value = properties.getProperty(key);
+
+ if (value == null) {
+ value = defaults.get(key);
+ }
+
+ return value;
+ }
+
+ /**
+ * @param key
+ * @return value
+ */
+ public boolean getBooleanProperty(String key) {
+ String value = getProperty(key);
+ return Boolean.parseBoolean(value);
+ }
+
+ /**
+ * @param key
+ * @return value
+ */
+ public int getIntProperty(String key) {
+ String value = getProperty(key);
+ return Integer.parseInt(value);
+ }
+
+ /**
+ * @return working directory
+ */
+ public static File getTemporaryDirectory() {
+ String pathname = getInstance().getProperty(KEY_WORKING_DIR);
+ return new File(pathname);
+ }
+
+ /**
+ * @return The server URI which may be set in the constructor of an MqttClient
+ * @throws URISyntaxException
+ */
+ public static URI getServerURI() throws URISyntaxException {
+ String methodName = Utility.getMethodName();
+ log.entering(className, methodName);
+
+ String string = getInstance().getProperty(KEY_SERVER_URI);
+ URI uri = new URI(string);
+
+ log.exiting(className, methodName, string);
+ return uri;
+ }
+
+ /**
+ * Returns a list of URIs which may set in the MQTTConnectOptions for an HA testcase
+ *
+ * @return value
+ * @throws URISyntaxException
+ */
+ public static List<URI> getServerURIs() throws URISyntaxException {
+ TestProperties testProperties = getInstance();
+
+ List<URI> list = new ArrayList<URI>();
+ int index = 0;
+ String uri = testProperties.getProperty(KEY_SERVER_URI + "." + index);
+ while (uri != null) {
+ list.add(new URI(uri));
+ index++;
+ uri = testProperties.getProperty(KEY_SERVER_URI + "." + index);
+ }
+
+ return list;
+ }
+
+ /**
+ * Returns an array list or URIs which may be used by an HA testcase
+ *
+ * @return value
+ * @throws URISyntaxException
+ */
+ public static List<String> getServerURIsAsListOfStrings() throws URISyntaxException {
+ List<URI> list1 = getServerURIs();
+
+ List<String> list2 = new ArrayList<String>();
+
+ for (int i = 0; i < list1.size(); i++) {
+ list2.add(list1.get(i).toString());
+ }
+
+ return list2;
+ }
+
+ /**
+ * Returns an array list or URIs which may be used by an HA testcase
+ *
+ * @return value
+ * @throws URISyntaxException
+ */
+ public static String[] getServerURIsAsStringArray() throws URISyntaxException {
+ List<URI> list = getServerURIs();
+
+ String[] array = new String[list.size()];
+
+ for (int i = 0; i < list.size(); i++) {
+ array[i] = list.get(i).toString();
+ }
+
+ return array;
+ }
+
+}
diff --git a/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/utilities/MqttV3Receiver.java b/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/utilities/MqttV3Receiver.java
new file mode 100644
index 0000000..8f28c03
--- /dev/null
+++ b/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/utilities/MqttV3Receiver.java
@@ -0,0 +1,447 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ *******************************************************************************/
+
+package org.eclipse.paho.client.mqttv3.test.utilities;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Logger;
+
+import org.eclipse.paho.client.mqttv3.IMqttAsyncClient;
+import org.eclipse.paho.client.mqttv3.IMqttClient;
+import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
+import org.eclipse.paho.client.mqttv3.MqttCallback;
+import org.eclipse.paho.client.mqttv3.MqttException;
+import org.eclipse.paho.client.mqttv3.MqttMessage;
+
+/**
+ * Listen for in bound messages and connection loss.
+ */
+public class MqttV3Receiver implements MqttCallback {
+
+ static final String className = MqttV3Receiver.class.getName();
+ static final Logger log = Logger.getLogger(className);
+
+ final static String TRACE_GROUP = "Test";
+
+ private final PrintStream reportStream;
+ private boolean reportConnectionLoss = true;
+ private boolean connected = false;
+ private String clientId;
+
+ /**
+ * For the in bound message.
+ */
+ public class ReceivedMessage {
+
+ /** */
+ public String topic;
+ /** */
+ public MqttMessage message;
+
+ ReceivedMessage(String topic, MqttMessage message) {
+ this.topic = topic;
+ this.message = message;
+ }
+ }
+
+ List<ReceivedMessage> receivedMessages = new ArrayList<ReceivedMessage>();
+
+ /**
+ * @param mqttClient
+ * @param reportStream
+ */
+ public MqttV3Receiver(IMqttClient mqttClient, PrintStream reportStream) {
+ String methodName = Utility.getMethodName();
+ log.entering(className, methodName);
+
+ this.reportStream = reportStream;
+ connected = true;
+
+ clientId = mqttClient.getClientId();
+
+ log.exiting(className, methodName);
+ }
+
+ /**
+ * @param mqttClient
+ * @param reportStream
+ */
+ public MqttV3Receiver(IMqttAsyncClient mqttClient, PrintStream reportStream) {
+ String methodName = Utility.getMethodName();
+ log.entering(className, methodName);
+
+ this.reportStream = reportStream;
+ connected = true;
+
+ clientId = mqttClient.getClientId();
+
+ log.exiting(className, methodName);
+ }
+
+ /**
+ * @return flag
+ */
+ public final boolean isReportConnectionLoss() {
+ return reportConnectionLoss;
+ }
+
+ /**
+ * @param reportConnectionLoss
+ */
+ public final void setReportConnectionLoss(boolean reportConnectionLoss) {
+ this.reportConnectionLoss = reportConnectionLoss;
+ }
+
+ /**
+ * @param waitMilliseconds
+ * @return message
+ * @throws InterruptedException
+ */
+ public synchronized ReceivedMessage receiveNext(long waitMilliseconds) throws InterruptedException {
+ final String methodName = "receiveNext";
+ log.entering(className, methodName);
+
+ ReceivedMessage receivedMessage = null;
+ if (receivedMessages.isEmpty()) {
+ wait(waitMilliseconds);
+ }
+ if (!receivedMessages.isEmpty()) {
+ receivedMessage = receivedMessages.remove(0);
+ }
+
+ log.exiting(className, methodName);
+ return receivedMessage;
+ }
+
+ /**
+ * @param sendTopic
+ * @param expectedQos
+ * @param sentBytes
+ * @return flag
+ * @throws MqttException
+ * @throws InterruptedException
+ */
+ public boolean validateReceipt(String sendTopic, int expectedQos, byte[] sentBytes) throws MqttException, InterruptedException {
+ final String methodName = "validateReceipt";
+ log.entering(className, methodName, new Object[]{sendTopic, expectedQos, sentBytes});
+
+ long waitMilliseconds = 10000;
+ ReceivedMessage receivedMessage = receiveNext(waitMilliseconds);
+ if (receivedMessage == null) {
+ report(" No message received in waitMilliseconds=" + waitMilliseconds);
+ log.exiting(className, methodName, "Return false: " + receivedMessage);
+ return false;
+ }
+
+ if (!sendTopic.equals(receivedMessage.topic)) {
+ report(" Received invalid topic sent=" + sendTopic + " received topic=" + receivedMessage.topic);
+ log.exiting(className, methodName, "Return false: " + receivedMessage);
+ return false;
+ }
+
+ if (!java.util.Arrays.equals(sentBytes,
+ receivedMessage.message.getPayload())) {
+ report("Received invalid payload="
+ + receivedMessage.message.getPayload() + "\n" + "Sent:"
+ + new String(sentBytes) + "\n" + "Received:"
+ + new String(receivedMessage.message.getPayload()));
+ log.exiting(className, methodName, "Return false: " + receivedMessage);
+ return false;
+ }
+
+ if (expectedQos != receivedMessage.message.getQos()) {
+ report("expectedQos=" + expectedQos + " != Received Qos="
+ + receivedMessage.message.getQos());
+ log.exiting(className, methodName, "Return false: " + receivedMessage);
+ return false;
+ }
+
+ log.exiting(className, methodName, new Object[]{"true"});
+ return true;
+ }
+
+ /**
+ * Validate receipt of a batch of messages sent to a topic by a number of
+ * publishers The message payloads are expected to have the format<b>
+ * "Batch Message payload :<batch>:<publisher>:<messageNumber>:<any additional payload>"
+ *
+ * We want to detect excess messages, so we don't just handle a certain
+ * number. Instead we wait for a timeout period, and exit if no message is
+ * received in that period.<b> The timeout period can make this test long
+ * running, so we attempt to dynamically adjust, allowing 10 seconds for the
+ * first message and then averaging the time taken to receive messages and
+ * applying some fudge factors.
+ *
+ * @param sendTopics
+ * @param expectedQosList
+ * @param nPublishers
+ * @param expectedBatchNumber
+ * @param sentBytes
+ * @param expectOrdered
+ * @return flag
+ * @throws MqttException
+ * @throws InterruptedException
+ */
+ public boolean validateReceipt(List<String> sendTopics, List<Integer> expectedQosList,
+ int expectedBatchNumber, int nPublishers, List<byte[]> sentBytes,
+ boolean expectOrdered) throws MqttException, InterruptedException {
+ final String methodName = "validateReceipt";
+ log.entering(className, methodName, new Object[]{
+ sendTopics, expectedQosList, sentBytes});
+
+ int expectedMessageNumbers[] = new int[nPublishers];
+ for (int i = 0; i < nPublishers; i++) {
+ expectedMessageNumbers[i] = 0;
+ }
+ long waitMilliseconds = 10000;
+
+ // track time taken to receive messages
+ long totWait = 0;
+ int messageNo = 0;
+ while (true) {
+ long startWait = System.currentTimeMillis();
+ ReceivedMessage receivedMessage = receiveNext(waitMilliseconds);
+ if (receivedMessage == null) {
+ break;
+ }
+ messageNo++;
+ totWait += (System.currentTimeMillis() - startWait);
+
+ // Calculate new wait time based on experience, but not allowing it
+ // to get too small
+ waitMilliseconds = Math.max(totWait / messageNo, 500);
+
+ byte[] payload = receivedMessage.message.getPayload();
+ String payloadString = new String(payload);
+ if (!payloadString.startsWith("Batch Message payload :")) {
+ report("Received invalid payload\n" + "Received:"
+ + payloadString);
+ report("Payload did not start with {"
+ + "Batch Message payload :" + "}");
+ log.exiting(className, methodName, "Return false: " + receivedMessage);
+ return false;
+ }
+
+ String[] payloadParts = payloadString.split(":");
+ if (payloadParts.length != 5) {
+ report("Received invalid payload\n" + "Received:"
+ + payloadString);
+ report("Payload was not of expected format");
+ log.finer("Return false: " + receivedMessage);
+ return false;
+ }
+
+ try {
+ int batchNumber = Integer.parseInt(payloadParts[1]);
+ if (batchNumber != expectedBatchNumber) {
+ report("Received invalid payload\n" + "Received:"
+ + payloadString);
+ report("batchnumber" + batchNumber
+ + " was not the expected value "
+ + expectedBatchNumber);
+ log.exiting(className, methodName, "Return false: " + receivedMessage);
+ return false;
+ }
+ }
+ catch (NumberFormatException e) {
+ report("Received invalid payload\n" + "Received:"
+ + payloadString);
+ report("batchnumber was not a numeric value");
+ log.exiting(className, methodName, "Return false: " + receivedMessage);
+ return false;
+ }
+
+ int publisher = -1;
+ try {
+ publisher = Integer.parseInt(payloadParts[2]);
+ if ((publisher < 0) || (publisher >= nPublishers)) {
+ report("Received invalid payload\n" + "Received:"
+ + payloadString);
+ report("publisher " + publisher
+ + " was not in the range 0 - " + (nPublishers - 1));
+ log.exiting(className, methodName, "Return false: " + receivedMessage);
+ return false;
+ }
+ }
+ catch (NumberFormatException e) {
+ report("Received invalid payload\n" + "Received:"
+ + payloadString);
+ report("publisher was not a numeric value");
+ log.exiting(className, methodName, "Return false: " + receivedMessage);
+ return false;
+ }
+
+ if (expectOrdered) {
+ try {
+ int messageNumber = Integer.parseInt(payloadParts[3]);
+ if (messageNumber == expectedMessageNumbers[publisher]) {
+ expectedMessageNumbers[publisher] += 1;
+ }
+ else {
+ report("Received invalid payload\n" + "Received:"
+ + payloadString);
+ report("messageNumber "
+ + messageNumber
+ + " was received out of sequence - expected value was "
+ + expectedMessageNumbers[publisher]);
+ log.exiting(className, methodName, "Return false: " + receivedMessage);
+ return false;
+ }
+ }
+ catch (NumberFormatException e) {
+ report("Received invalid payload\n" + "Received:"
+ + payloadString);
+ report("messageNumber was not a numeric value");
+ log.exiting(className, methodName, "Return false: " + receivedMessage);
+ return false;
+ }
+ }
+
+ int location;
+ for (location = 0; location < sentBytes.size(); location++) {
+ if (Arrays.equals(payload, sentBytes.get(location))) {
+ break;
+ }
+ }
+
+ String sendTopic = null;
+ int expectedQos = -1;
+ if (location < sentBytes.size()) {
+ sentBytes.remove(location);
+ sendTopic = sendTopics.remove(location);
+ expectedQos = expectedQosList.remove(location);
+ }
+ else {
+ report("Received invalid payload\n" + "Received:"
+ + payloadString);
+ for (byte[] expectedPayload : sentBytes) {
+ report("\texpected message :" + new String(expectedPayload));
+ }
+ log.exiting(className, methodName, "Return false: " + receivedMessage);
+ return false;
+ }
+
+ if (!sendTopic.equals(receivedMessage.topic)) {
+ report(" Received invalid topic sent=" + sendTopic
+ + " received topic=" + receivedMessage.topic);
+ log.exiting(className, methodName, "Return false: " + receivedMessage);
+ return false;
+ }
+
+ if (expectedQos != receivedMessage.message.getQos()) {
+ report("expectedQos=" + expectedQos + " != Received Qos="
+ + receivedMessage.message.getQos());
+ log.exiting(className, methodName, "Return false: " + receivedMessage);
+ return false;
+ }
+
+ }
+
+ if (!sentBytes.isEmpty()) {
+ for (byte[] missedPayload : sentBytes) {
+ report("Did not receive message \n" + new String(missedPayload));
+ }
+ log.exiting(className, methodName, "Return false");
+ return false;
+ }
+
+ log.exiting(className, methodName,
+ new Object[]{"return true"});
+ return true;
+ }
+
+ /**
+ * @param waitMilliseconds
+ * @return flag
+ * @throws InterruptedException
+ */
+ public synchronized boolean waitForConnectionLost(long waitMilliseconds)
+ throws InterruptedException {
+ final String methodName = "waitForConnectionLost";
+ log.entering(className, methodName, new Object[]{
+ waitMilliseconds, connected});
+
+ if (connected) {
+ wait(waitMilliseconds);
+ }
+
+ log.exiting(className, methodName,
+ new Object[]{connected});
+ return connected;
+ }
+
+ /**
+ * @param cause
+ */
+ public void connectionLost(Throwable cause) {
+ final String methodName = "connectionLost";
+ log.entering(className, methodName, new Object[]{cause,
+ connected});
+
+ if (reportConnectionLoss) {
+ report("ConnectionLost: clientId=" + clientId + " cause=" + cause);
+ }
+
+ synchronized (this) {
+ connected = false;
+ notifyAll();
+ }
+
+ log.exiting(className, methodName);
+ }
+
+ /**
+ * @param arg0
+ */
+ public void deliveryComplete(IMqttDeliveryToken arg0) {
+ // Auto-generated method stub
+ }
+
+ /**
+ * @param arg0
+ * @param arg1
+ */
+ public void deliveryFailed(IMqttDeliveryToken arg0, MqttException arg1) {
+ // Auto-generated method stub
+ }
+
+ /**
+ * @param topic
+ * @param message
+ * @throws Exception
+ */
+ public synchronized void messageArrived(String topic, MqttMessage message) throws Exception {
+ final String methodName = "messageArrived";
+ log.entering(className, methodName, new Object[]{topic,
+ message});
+
+ // logger.fine(methodName + ": '" + new String(message.getPayload()) + "'");
+
+ receivedMessages.add(new ReceivedMessage(topic, message));
+ notify();
+
+ log.exiting(className, methodName);
+ }
+
+ /**
+ * @param text
+ */
+ public void report(String text) {
+ StackTraceElement[] stack = (new Throwable()).getStackTrace();
+ reportStream.println(stack[1].getClassName() + ":" + stack[1].getLineNumber() + " " + text);
+ }
+}
diff --git a/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/utilities/StringUtilities.java b/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/utilities/StringUtilities.java
new file mode 100644
index 0000000..8d13b2d
--- /dev/null
+++ b/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/utilities/StringUtilities.java
@@ -0,0 +1,226 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ *******************************************************************************/
+
+package org.eclipse.paho.client.mqttv3.test.utilities;
+
+import java.text.MessageFormat;
+import java.util.logging.Logger;
+
+/**
+ * Static utility functions
+ */
+public class StringUtilities {
+
+ private static final String className = StringUtilities.class.getName();
+ private final static Logger log = Logger.getLogger(className);
+
+ /** Lookup the line separator once */
+ public static final String NL = System.getProperty("line.separator");
+
+ /**
+ * @param bytes
+ * @return string
+ */
+ public static String localizedByteCount(long bytes) {
+ MessageFormat form = new MessageFormat("{0,number,integer}");
+ Object[] args = {bytes};
+ return form.format(args);
+ }
+
+ /**
+ * @param bytes
+ * @param si
+ * @return string
+ */
+ public static String humanReadableByteCount(long bytes, boolean si) {
+ int unit = si ? 1000 : 1024;
+ if (bytes < unit) {
+ return bytes + " B";
+ }
+ int exp = (int) (Math.log(bytes) / Math.log(unit));
+ String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i");
+ return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
+ }
+
+ /**
+ * Helper method to convert a byte[] array (such as a MsgId) to a hex string
+ *
+ * @param array
+ * @return hex string
+ */
+ public static String arrayToHexString(byte[] array) {
+ return arrayToHexString(array, 0, array.length);
+ }
+
+ /**
+ * Helper method to convert a byte[] array (such as a MsgId) to a hex string
+ *
+ * @param array
+ * @param offset
+ * @param limit
+ * @return hex string
+ */
+ public static String arrayToHexString(byte[] array, int offset, int limit) {
+ String retVal;
+ if (array != null) {
+ StringBuffer hexString = new StringBuffer(array.length);
+ int hexVal;
+ char hexChar;
+ int length = Math.min(limit, array.length);
+ for (int i = offset; i < length; i++) {
+ hexVal = (array[i] & 0xF0) >> 4;
+ hexChar = (char) ((hexVal > 9) ? ('A' + (hexVal - 10)) : ('0' + hexVal));
+ hexString.append(hexChar);
+ hexVal = array[i] & 0x0F;
+ hexChar = (char) ((hexVal > 9) ? ('A' + (hexVal - 10)) : ('0' + hexVal));
+ hexString.append(hexChar);
+ }
+ retVal = hexString.toString();
+ }
+ else {
+ retVal = "<null>";
+ }
+ return retVal;
+ }
+
+ /**
+ * @param text
+ * @return a Java string
+ */
+ public static String toJavaString(String text) {
+
+ String string = text;
+ if (string != null) {
+ string = string.replaceAll("\n", "\\\\n");
+ string = string.replaceAll("\r", "\\\\r");
+ string = string.replaceAll("\"", "\\\\\"");
+ }
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("\"");
+ sb.append(string);
+ sb.append("\"");
+
+ return sb.toString();
+ }
+
+ /**
+ * @param object
+ * @return a non-null string based on the given string
+ */
+ public static String safeString(Object object) {
+ return (object == null) ? "<null>" : object.toString();
+ }
+
+ /**
+ * Left justify a string, padding with spaces.
+ *
+ * @param s the string to justify
+ * @param width the field width to justify within
+ *
+ * @return the justified string.
+ */
+ public static String left(String s, int width) {
+ return left(s, width, ' ');
+ }
+
+ /**
+ * Left justify a string.
+ *
+ * @param s the string to justify
+ * @param width the field width to justify within
+ * @param fillChar the character to fill with
+ *
+ * @return the justified string.
+ */
+ public static String left(String s, int width, char fillChar) {
+ if (s.length() >= width) {
+ return s;
+ }
+ StringBuffer sb = new StringBuffer(width);
+ sb.append(s);
+ for (int i = width - s.length(); --i >= 0;) {
+ sb.append(fillChar);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Right justify a string, padding with spaces.
+ *
+ * @param s the string to justify
+ * @param width the field width to justify within
+ *
+ * @return the justified string.
+ */
+ public static String right(String s, int width) {
+ return right(s, width, ' ');
+ }
+
+ /**
+ * Right justify a string.
+ *
+ * @param s the string to justify
+ * @param width the field width to justify within
+ * @param fillChar the character to fill with
+ *
+ * @return the justified string.
+ */
+ public static String right(String s, int width, char fillChar) {
+ if (s.length() >= width) {
+ return s;
+ }
+ StringBuffer sb = new StringBuffer(width);
+ for (int i = width - s.length(); --i >= 0;) {
+ sb.append(fillChar);
+ }
+ sb.append(s);
+ return sb.toString();
+ }
+
+ /**
+ * @param level1
+ * @param level2
+ * @return the higher level
+ */
+ public static String getHigherLevel(String level1, String level2) {
+ int idx;
+ String rlevel = level1;
+
+ idx = 6;
+ if (level1 == null) {
+ rlevel = level2;
+ }
+ else if (level2 == null) {
+ rlevel = level1;
+ }
+ else {
+
+ while (idx < Math.min(level1.length(), level2.length())) {
+ int f = Integer.parseInt(level1.substring(idx, idx + 1));
+ int c = Integer.parseInt(level2.substring(idx, idx + 1));
+ if (f > c) {
+ rlevel = level1;
+ break;
+ }
+ else if (f < c) {
+ rlevel = level2;
+ break;
+ }
+ idx++;
+ }
+ }
+ return rlevel;
+ }
+}
diff --git a/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/utilities/Utility.java b/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/utilities/Utility.java
new file mode 100644
index 0000000..4267e9d
--- /dev/null
+++ b/org.eclipse.paho.client.mqttv3/src/test/java/org/eclipse/paho/client/mqttv3/test/utilities/Utility.java
@@ -0,0 +1,106 @@
+/* Copyright (c) 2009, 2013 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ *******************************************************************************/
+
+package org.eclipse.paho.client.mqttv3.test.utilities;
+
+import java.io.IOException;
+import java.util.logging.FileHandler;
+import java.util.logging.Logger;
+
+import org.eclipse.paho.client.mqttv3.IMqttAsyncClient;
+import org.eclipse.paho.client.mqttv3.IMqttClient;
+import org.eclipse.paho.client.mqttv3.IMqttToken;
+import org.eclipse.paho.client.mqttv3.MqttException;
+
+/**
+ * General purpose test utilities
+ */
+public class Utility {
+
+ static final String className = Utility.class.getName();
+ static final Logger log = Logger.getLogger(className);
+
+ /**
+ * @return the current method name for the caller.
+ */
+ public static String getMethodName() {
+ StackTraceElement[] stack = (new Throwable()).getStackTrace();
+ String methodName = stack[1].getMethodName();
+
+ // Skip over synthetic accessor methods
+ if (methodName.equals("access$0")) {
+ methodName = stack[2].getMethodName();
+ }
+
+ return methodName;
+ }
+
+ /**
+ * @return 'true' if running on Windows
+ */
+ public static boolean isWindows() {
+ String osName = System.getProperty("os.name");
+ if (osName.startsWith("Windows")) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @param client
+ * @throws MqttException
+ */
+ public static void disconnectAndCloseClient(IMqttAsyncClient client) throws MqttException {
+ if (client != null) {
+ if (client.isConnected()) {
+ IMqttToken token = client.disconnect(null, null);
+ token.waitForCompletion();
+ }
+ client.close();
+ }
+ }
+
+ /**
+ * @param client
+ * @throws MqttException
+ */
+ public static void disconnectAndCloseClient(IMqttClient client) throws MqttException {
+ if (client != null) {
+ if (client.isConnected()) {
+ client.disconnect(0);
+ }
+ client.close();
+ }
+ }
+
+ /**
+ * Used to turn trace on dynamically in a test case, eg.
+ * java.util.logging.Logger logger = Logger.getLogger("org.eclipse.paho.client.mqttv3");
+ * logger.addHandler(Utility.getHandler());
+ * logger.setLevel(Level.ALL);
+ */
+ private static java.util.logging.Handler handler = null;
+
+ public synchronized static java.util.logging.Handler getHandler() {
+ try {
+ if (handler == null) {
+ handler = new FileHandler("framework.log", true);
+ handler.setFormatter(new org.eclipse.paho.client.mqttv3.test.log.HumanFormatter());
+ }
+ }
+ catch (IOException exception) {
+ exception.printStackTrace();
+ }
+ return handler;
+ }
+}
diff --git a/org.eclipse.paho.client.mqttv3/src/test/resources/logging.properties b/org.eclipse.paho.client.mqttv3/src/test/resources/logging.properties
new file mode 100644
index 0000000..eebf677
--- /dev/null
+++ b/org.eclipse.paho.client.mqttv3/src/test/resources/logging.properties
@@ -0,0 +1,34 @@
+
+# ***********************************************************************
+# * Configure Logging
+# ***********************************************************************
+
+# The set of handlers to be loaded upon startup.
+handlers=java.util.logging.FileHandler, org.eclipse.paho.client.mqttv3.test.log.ConsoleHandler
+
+# Default global logging level.
+.level=INFO
+
+# Loggers
+# ------------------------------------------
+org.eclipse.paho.client.mqttv3.level=ALL
+org.eclipse.paho.client.mqttv3.test.level=ALL
+com.ibm.level=ALL
+utility.level=ALL
+
+# Handlers
+# -----------------------------------------
+
+# --- ConsoleHandler ---
+org.eclipse.paho.client.mqttv3.test.log.ConsoleHandler.level=INFO
+org.eclipse.paho.client.mqttv3.test.log.ConsoleHandler.formatter=org.eclipse.paho.client.mqttv3.test.log.HumanFormatter
+
+# --- FileHandler ---
+java.util.logging.FileHandler.level=ALL
+java.util.logging.FileHandler.pattern=framework.log
+java.util.logging.FileHandler.formatter=org.eclipse.paho.client.mqttv3.test.log.DetailFormatter
+java.util.logging.FileHandler.append=false
+
+
+
+
diff --git a/org.eclipse.paho.client.mqttv3/src/test/resources/test.properties b/org.eclipse.paho.client.mqttv3/src/test/resources/test.properties
new file mode 100644
index 0000000..b9b180b
--- /dev/null
+++ b/org.eclipse.paho.client.mqttv3/src/test/resources/test.properties
@@ -0,0 +1,10 @@
+# This is the server URI which will be set in the constructor of an MQTT Client
+# The default is "tcp://<localhost>:1883" with <localhost> expressed in IPV4 dotted decimal notation
+SERVER_URI=tcp://m2m.eclipse.org:1883
+
+
+# The list of server URIs which may be set in the MQTT ConnectOptions for an HA testcase.
+# There is no default value
+# URI.0=tcp://localhost:1883
+# URI.1=tcp://localhost:1884
+# URI.2=tcp://localhost:1885