2004-07-12 15:31:29 +02:00
|
|
|
/**
|
|
|
|
* $RCSfile$
|
|
|
|
* $Revision$
|
2006-01-16 18:34:56 +01:00
|
|
|
* $Date: $
|
2004-07-12 15:31:29 +02:00
|
|
|
*
|
2006-01-16 18:34:56 +01:00
|
|
|
* Copyright 2003-2005 Jive Software.
|
2004-07-12 15:31:29 +02:00
|
|
|
*
|
2006-01-16 18:34:56 +01:00
|
|
|
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
2004-07-12 15:31:29 +02:00
|
|
|
*
|
2006-01-16 18:34:56 +01:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2004-07-12 15:31:29 +02:00
|
|
|
*
|
2006-01-16 18:34:56 +01:00
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
2004-07-12 15:31:29 +02:00
|
|
|
*/
|
|
|
|
package org.jivesoftware.smack.test;
|
|
|
|
|
|
|
|
import java.io.InputStream;
|
|
|
|
import java.net.URL;
|
|
|
|
import java.util.Enumeration;
|
|
|
|
|
|
|
|
import javax.net.SocketFactory;
|
|
|
|
|
|
|
|
import org.jivesoftware.smack.XMPPConnection;
|
2005-04-14 04:08:56 +02:00
|
|
|
import org.jivesoftware.smack.XMPPException;
|
2006-01-17 21:05:31 +01:00
|
|
|
import org.jivesoftware.smack.ConnectionConfiguration;
|
2004-07-12 15:31:29 +02:00
|
|
|
import org.xmlpull.v1.*;
|
2005-03-23 03:36:19 +01:00
|
|
|
import org.xmlpull.mxp1.MXParser;
|
2004-07-12 15:31:29 +02:00
|
|
|
|
|
|
|
import junit.framework.TestCase;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Base class for all the test cases which provides a pre-configured execution context. This
|
|
|
|
* means that any test case that subclassifies this base class will have access to a pool of
|
|
|
|
* connections and to the user of each connection. The maximum number of connections in the pool
|
|
|
|
* can be controlled by the message {@link #getMaxConnections()} which every subclass must
|
|
|
|
* implement.<p>
|
|
|
|
*
|
|
|
|
* This base class defines a default execution context (i.e. host, port, chat domain and muc
|
|
|
|
* domain) which can be found in the file "config/test-case.xml". However, each subclass could
|
|
|
|
* redefine the default configuration by providing its own configuration file (if desired). The
|
|
|
|
* name of the configuration file must be of the form <test class name>.xml (e.g. RosterTest.xml).
|
|
|
|
* The file must be placed in the folder "config". This folder is where the default configuration
|
|
|
|
* file is being held.
|
|
|
|
*
|
|
|
|
* @author Gaston Dombiak
|
|
|
|
*/
|
|
|
|
public abstract class SmackTestCase extends TestCase {
|
|
|
|
|
|
|
|
private String host = "localhost";
|
2005-08-27 04:21:24 +02:00
|
|
|
private String serviceName = "localhost";
|
2004-07-12 15:31:29 +02:00
|
|
|
private int port = 5222;
|
|
|
|
|
|
|
|
private String chatDomain = "chat.localhost";
|
|
|
|
private String mucDomain = "conference.localhost";
|
|
|
|
|
|
|
|
private XMPPConnection[] connections = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor for SmackTestCase.
|
|
|
|
* @param arg0
|
|
|
|
*/
|
|
|
|
public SmackTestCase(String arg0) {
|
|
|
|
super(arg0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the maximum number of connections to initialize for this test case. All the
|
|
|
|
* initialized connections will be connected to the server using a new test account for
|
|
|
|
* each conection.
|
|
|
|
*
|
|
|
|
* @return the maximum number of connections to initialize for this test case.
|
|
|
|
*/
|
|
|
|
protected abstract int getMaxConnections();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a SocketFactory that will be used to create the socket to the XMPP server. By
|
|
|
|
* default no SocketFactory is used but subclasses my want to redefine this method.<p>
|
|
|
|
*
|
|
|
|
* A custom SocketFactory allows fine-grained control of the actual connection to the XMPP
|
|
|
|
* server. A typical use for a custom SocketFactory is when connecting through a SOCKS proxy.
|
|
|
|
*
|
|
|
|
* @return a SocketFactory that will be used to create the socket to the XMPP server.
|
|
|
|
*/
|
|
|
|
protected SocketFactory getSocketFactory() {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the XMPPConnection located at the requested position. Each test case holds a
|
|
|
|
* pool of connections which is initialized while setting up the test case. The maximum
|
|
|
|
* number of connections is controlled by the message {@link #getMaxConnections()} which
|
|
|
|
* every subclass must implement.<p>
|
|
|
|
*
|
|
|
|
* If the requested position is greater than the connections size then an
|
|
|
|
* IllegalArgumentException will be thrown.
|
|
|
|
*
|
|
|
|
* @param index the position in the pool of the connection to look for.
|
|
|
|
* @return the XMPPConnection located at the requested position.
|
|
|
|
*/
|
|
|
|
protected XMPPConnection getConnection(int index) {
|
|
|
|
if (index > getMaxConnections()) {
|
|
|
|
throw new IllegalArgumentException("Index out of bounds");
|
|
|
|
}
|
|
|
|
return connections[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the name of the user (e.g. johndoe) that is using the connection
|
|
|
|
* located at the requested position.
|
|
|
|
*
|
|
|
|
* @param index the position in the pool of the connection to look for.
|
|
|
|
* @return the user of the user (e.g. johndoe).
|
|
|
|
*/
|
|
|
|
protected String getUsername(int index) {
|
|
|
|
if (index > getMaxConnections()) {
|
|
|
|
throw new IllegalArgumentException("Index out of bounds");
|
|
|
|
}
|
|
|
|
return "user" + index;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the bare XMPP address of the user (e.g. johndoe@jabber.org) that is
|
|
|
|
* using the connection located at the requested position.
|
|
|
|
*
|
|
|
|
* @param index the position in the pool of the connection to look for.
|
|
|
|
* @return the bare XMPP address of the user (e.g. johndoe@jabber.org).
|
|
|
|
*/
|
|
|
|
protected String getBareJID(int index) {
|
2005-08-27 04:21:24 +02:00
|
|
|
return getUsername(index) + "@" + getConnection(index).getServiceName();
|
2004-07-12 15:31:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the full XMPP address of the user (e.g. johndoe@jabber.org/Smack) that is
|
|
|
|
* using the connection located at the requested position.
|
|
|
|
*
|
|
|
|
* @param index the position in the pool of the connection to look for.
|
|
|
|
* @return the full XMPP address of the user (e.g. johndoe@jabber.org/Smack).
|
|
|
|
*/
|
|
|
|
protected String getFullJID(int index) {
|
|
|
|
return getBareJID(index) + "/Smack";
|
|
|
|
}
|
|
|
|
|
|
|
|
protected String getHost() {
|
|
|
|
return host;
|
|
|
|
}
|
|
|
|
|
2004-11-06 15:57:43 +01:00
|
|
|
protected int getPort() {
|
|
|
|
return port;
|
|
|
|
}
|
|
|
|
|
2005-08-27 04:21:24 +02:00
|
|
|
protected String getServiceName() {
|
|
|
|
return serviceName;
|
|
|
|
}
|
|
|
|
|
2004-07-12 15:31:29 +02:00
|
|
|
/**
|
|
|
|
* Returns the default groupchat service domain.
|
|
|
|
*
|
|
|
|
* @return the default groupchat service domain.
|
|
|
|
*/
|
|
|
|
protected String getChatDomain() {
|
|
|
|
return chatDomain;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the default MUC service domain.
|
|
|
|
*
|
|
|
|
* @return the default MUC service domain.
|
|
|
|
*/
|
|
|
|
protected String getMUCDomain() {
|
|
|
|
return mucDomain;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void setUp() throws Exception {
|
|
|
|
super.setUp();
|
|
|
|
init();
|
2004-11-06 15:57:43 +01:00
|
|
|
if (getMaxConnections() < 1) {
|
|
|
|
return;
|
|
|
|
}
|
2004-07-12 15:31:29 +02:00
|
|
|
connections = new XMPPConnection[getMaxConnections()];
|
|
|
|
try {
|
|
|
|
// Connect to the server
|
|
|
|
for (int i = 0; i < getMaxConnections(); i++) {
|
2006-01-17 21:05:31 +01:00
|
|
|
// Create the configuration for this new connection
|
|
|
|
ConnectionConfiguration config = new ConnectionConfiguration(host, port);
|
|
|
|
config.setTLSEnabled(true);
|
|
|
|
config.setCompressionEnabled(Boolean.getBoolean("test.compressionEnabled"));
|
|
|
|
config.setSASLAuthenticationEnabled(true);
|
2004-07-12 15:31:29 +02:00
|
|
|
if (getSocketFactory() == null) {
|
2006-01-17 21:05:31 +01:00
|
|
|
connections[i] = new XMPPConnection(config);
|
2004-07-12 15:31:29 +02:00
|
|
|
}
|
|
|
|
else {
|
2006-01-17 21:05:31 +01:00
|
|
|
connections[i] = new XMPPConnection(config, getSocketFactory());
|
2004-07-12 15:31:29 +02:00
|
|
|
}
|
|
|
|
}
|
2005-03-30 04:27:34 +02:00
|
|
|
// Use the host name that the server reports. This is a good idea in most
|
|
|
|
// cases, but could fail if the user set a hostname in their XMPP server
|
|
|
|
// that will not resolve as a network connection.
|
|
|
|
host = connections[0].getHost();
|
2005-08-27 04:21:24 +02:00
|
|
|
serviceName = connections[0].getServiceName();
|
2004-07-12 15:31:29 +02:00
|
|
|
// Create the test accounts
|
|
|
|
if (!getConnection(0).getAccountManager().supportsAccountCreation())
|
|
|
|
fail("Server does not support account creation");
|
|
|
|
|
|
|
|
for (int i = 0; i < getMaxConnections(); i++) {
|
|
|
|
// Create the test account
|
2005-04-14 04:08:56 +02:00
|
|
|
try {
|
|
|
|
getConnection(i).getAccountManager().createAccount("user" + i, "user" + i);
|
|
|
|
} catch (XMPPException e) {
|
|
|
|
// Do nothing if the accout already exists
|
2006-01-17 21:05:31 +01:00
|
|
|
if (e.getXMPPError() == null || e.getXMPPError().getCode() != 409) {
|
2005-04-14 04:08:56 +02:00
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
2004-07-12 15:31:29 +02:00
|
|
|
// Login with the new test account
|
|
|
|
getConnection(i).login("user" + i, "user" + i);
|
|
|
|
}
|
2005-01-12 04:02:53 +01:00
|
|
|
// Let the server process the available presences
|
|
|
|
Thread.sleep(150);
|
2004-07-12 15:31:29 +02:00
|
|
|
}
|
|
|
|
catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
fail(e.getMessage());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void tearDown() throws Exception {
|
|
|
|
super.tearDown();
|
|
|
|
|
|
|
|
for (int i = 0; i < getMaxConnections(); i++) {
|
|
|
|
// Delete the created account for the test
|
|
|
|
getConnection(i).getAccountManager().deleteAccount();
|
|
|
|
// Close the connection
|
|
|
|
getConnection(i).close();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initializes the context of the test case. We will first try to load the configuration from
|
|
|
|
* a file whose name is conformed by the test case class name plus an .xml extension
|
|
|
|
* (e.g RosterTest.xml). If no file was found under that name then we will try to load the
|
|
|
|
* default configuration for all the test cases from the file "config/test-case.xml".
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
private void init() {
|
|
|
|
try {
|
|
|
|
boolean found = false;
|
|
|
|
// Try to load the configutation from an XML file specific for this test case
|
2004-11-11 04:07:44 +01:00
|
|
|
Enumeration resources =
|
2004-07-12 15:31:29 +02:00
|
|
|
ClassLoader.getSystemClassLoader().getResources(getConfigurationFilename());
|
2004-11-11 04:07:44 +01:00
|
|
|
while (resources.hasMoreElements()) {
|
|
|
|
found = parseURL((URL) resources.nextElement());
|
2004-07-12 15:31:29 +02:00
|
|
|
}
|
|
|
|
// If none was found then try to load the configuration from the default configuration
|
|
|
|
// file (i.e. "config/test-case.xml")
|
|
|
|
if (!found) {
|
2004-11-11 04:07:44 +01:00
|
|
|
resources = ClassLoader.getSystemClassLoader().getResources("config/test-case.xml");
|
|
|
|
while (resources.hasMoreElements()) {
|
|
|
|
found = parseURL((URL) resources.nextElement());
|
2004-07-12 15:31:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found) {
|
|
|
|
System.err.println("File config/test-case.xml not found. Using default config.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception e) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if the given URL was found and parsed without problems. The file provided
|
|
|
|
* by the URL must contain information useful for the test case configuration, such us,
|
|
|
|
* host and port of the server.
|
|
|
|
*
|
|
|
|
* @param url the url of the file to parse.
|
|
|
|
* @return true if the given URL was found and parsed without problems.
|
|
|
|
*/
|
|
|
|
private boolean parseURL(URL url) {
|
|
|
|
boolean parsedOK = false;
|
|
|
|
InputStream systemStream = null;
|
|
|
|
try {
|
|
|
|
systemStream = url.openStream();
|
2005-03-23 03:36:19 +01:00
|
|
|
XmlPullParser parser = new MXParser();
|
|
|
|
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
|
2004-07-12 15:31:29 +02:00
|
|
|
parser.setInput(systemStream, "UTF-8");
|
|
|
|
int eventType = parser.getEventType();
|
|
|
|
do {
|
|
|
|
if (eventType == XmlPullParser.START_TAG) {
|
|
|
|
if (parser.getName().equals("host")) {
|
|
|
|
host = parser.nextText();
|
|
|
|
}
|
|
|
|
else if (parser.getName().equals("port")) {
|
|
|
|
port = parseIntProperty(parser, port);
|
2005-08-27 04:21:24 +02:00
|
|
|
}
|
|
|
|
else if (parser.getName().equals("serviceName")) {
|
|
|
|
serviceName = parser.nextText();
|
2004-07-12 15:31:29 +02:00
|
|
|
}
|
|
|
|
else if (parser.getName().equals("chat")) {
|
|
|
|
chatDomain = parser.nextText();
|
|
|
|
}
|
|
|
|
else if (parser.getName().equals("muc")) {
|
|
|
|
mucDomain = parser.nextText();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
eventType = parser.next();
|
|
|
|
}
|
|
|
|
while (eventType != XmlPullParser.END_DOCUMENT);
|
|
|
|
parsedOK = true;
|
|
|
|
}
|
|
|
|
catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
finally {
|
|
|
|
try {
|
|
|
|
systemStream.close();
|
|
|
|
}
|
|
|
|
catch (Exception e) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return parsedOK;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static int parseIntProperty(XmlPullParser parser, int defaultValue) throws Exception {
|
|
|
|
try {
|
|
|
|
return Integer.parseInt(parser.nextText());
|
|
|
|
}
|
|
|
|
catch (NumberFormatException nfe) {
|
|
|
|
nfe.printStackTrace();
|
|
|
|
return defaultValue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the name of the configuration file related to <b>this</b> test case. By default all
|
|
|
|
* the test cases will use the same configuration file. However, it's possible to override the
|
|
|
|
* default configuration by providing a file of the form <test case class name>.xml
|
|
|
|
* (e.g. RosterTest.xml).
|
|
|
|
*
|
|
|
|
* @return the name of the configuration file related to this test case.
|
|
|
|
*/
|
|
|
|
private String getConfigurationFilename() {
|
|
|
|
String fullClassName = this.getClass().getName();
|
|
|
|
int firstChar = fullClassName.lastIndexOf('.') + 1;
|
|
|
|
return "config/" + fullClassName.substring(firstChar) + ".xml";
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|