Add Integration Test Framework

and resurrect a few integration tests.
This commit is contained in:
Florian Schmaus 2015-03-18 09:52:33 +01:00
parent 4e6fbe7293
commit b8f046706b
34 changed files with 2333 additions and 100 deletions

View File

@ -36,6 +36,7 @@ allprojects {
// build, causing unnecessary rebuilds.
builtDate = (new java.text.SimpleDateFormat("yyyy-MM-dd")).format(new Date())
oneLineDesc = 'An Open Source XMPP (Jabber) client library'
javadocAllProjects = subprojects - project(':smack-integration-test')
// A dirty hack used for Gradle's jacoco plugin, since is not
// hable to handle the case when a (sub)project has no unit
// tests. :-(
@ -65,6 +66,7 @@ allprojects {
].collect{ project(it) }
androidBootClasspath = getAndroidRuntimeJar()
androidJavadocOffline = getAndroidJavadocOffline()
junitVersion = '4.11'
}
group = 'org.igniterealtime.smack'
sourceCompatibility = 1.7
@ -147,7 +149,7 @@ gradle.taskGraph.whenReady { taskGraph ->
}
task javadocAll(type: Javadoc) {
source subprojects.collect {project ->
source javadocAllProjects.collect {project ->
project.sourceSets.main.allJava }
destinationDir = javadocAllDir
// Might need a classpath
@ -317,6 +319,9 @@ subprojects {
}
}
// No need to ever clirr smack-integration-test
project(':smack-integration-test').clirr.enabled = false
subprojects*.jar {
manifest {
from sharedManifest
@ -350,6 +355,10 @@ task clirrRootReport(type: org.kordamp.gradle.clirr.ClirrReportTask) {
reports = files((subprojects.findAll { it.clirr.enabled == true }).tasks.clirr.xmlReport)
}
task integrationTest {
dependsOn project(':smack-integration-test').tasks.run
}
def getGitCommit() {
def dotGit = new File("$projectDir/.git")
if (!dotGit.isDirectory()) return 'non-git build'

View File

@ -0,0 +1,142 @@
Smack's Integration Test Framework
==================================
Introduction
------------
Smack's Integration Test Framwork is used ot run a set of tests against a real XMPP service.
The framework discovers on startup the available tests by reflection.
Quickstart
----------
You can run the framework against an XMPP service with
```bash
$ gradle integrationTest -Dsinttest.service=my.xmppservice.org
```
Configuration
-------------
The framework is configured with a standard Java properties file.
This file simply contains key/value pairs, which are separated by an equals sign ("=").
The most important configuration value is the `service` value, it's also the only required setting.
The file properties can be overridden with Java system properties.
The name of a system property that is used by the framework needs to be prefixed with `sinttest.` (*S*mack *Int*egration *Test* Framework).
For example the `service` property becomes `sinttest.service`.
### Minimal example **properties** file
```bash
service=example.org
```
### Another example **properties** file
```bash
service=example.org
serviceTlsPin=serviceTlsPin=CERTSHA256:2F:92:C9:4D:30:58:E1:05:21:9A:57:59:5F:6E:25:9A:0F:BF:FF:64:1A:C3:4B:EC:06:7D:4A:6F:0A:D5:21:85
debug=true
```
### Framework properties
| Name | |
|----------------------|-------------------------------------------|
| service | XMPP service to run the tests on |
| serviceTlsPin | TLS Pin (used by java-pinning) |
| securityMode | Either 'required' or disabled' |
| replyTimeout | In milliseconds |
| accountOneUsername | Username of the first XMPP account |
| accountOnePassword | Password of the first XMPP account |
| accountTwoUsername | Username of the second XMPP account |
| accountTwoPassword | Password of the second XMPP account |
| debug | 'true' to enable debug output |
| enabledTests | List of enabled tests |
| disabledTests | List of disabled tests |
| testPackages | List of packages with tests |
Overview of the components
--------------------------
Package `org.igniterealtime.smack.inttest`
### `SmackIntegrationTestFramework`
Contains `public static void main` method, i.e. the entry point for the framework.
Here the available integration tests are discovered by means of reflections, the configured is read and a `IntegrationTestEnvironment` instance created, include the XMPPConnections.
### `AbstractSmackIntegrationTest`
The base class that integration tests need to subclass.
### `AbstractSmackLowLevelIntegrationTest`
Allows low level integration test, i.e. ever test method will have it's on exclusive XMPPTCPConnection instances.
### `IntegrationTestEnvironment`
The environment, e.g. the `XMPPConnections` provided to the integration tests by the framework. Note that for convenience `AbstractSmackIntegrationTest` contains some of those as protected members.
### `SmackIntegrationTest`
An annotation that needs to be added to all methods that represent a single integration test.
Annotated integration test methods must not take any arguments (i.e. their parameter count is 0), and should return void as it's not evaluated in any way.
The methods are supposed to throw an exception if their integration test fails.
### `TestNotPossibleException`
Can be thrown by test methods or constructors to signal that their test it no possible, e.g. because the service does not support the required feature.
Running the integration tests
-----------------------------
Smack's Gradle build system is configured with a special task called `integrationTest`, which means you can run the tests simply with
```bash
$ gradle integrationTest
```
If one of `accountOneUsername`, `accountOnePassword`, `accountTwoUsername` or `accountTwoPassword` is not configured, then the framework will automatically create the accounts on the service (if account registration is supported and enabled).
If the accounts got created, then they will also be deleted at the end of the test.
Implementing Integration Tests
------------------------------
Create a new class which extends `AbstractSmackIntegrationTest`.
Every non-static method, including the constructor, of this class will have two XMPPConnections available to perform the integration tests with: `conOne` and `conTwo`.
You can use the constructor to check if the XMPP service does provide the required XMPP feature.
If it does not, simply throw a `TestNotPossibleException`.
Test methods must be `public`, take zero arguments i.e. declare no parameters and be annoated with `@SmackIntegrationTest`.
If the test method is not able to perform a test then it should throw a `TestNotPossibleException`.
### Rules for integration tests
Tests should not leave any traces on the service if they are finished, i.e. the service state at the end of the test must be equal to the state of the beginning.
It must be possible to run the tests in parallel.
### Why are there two mechanisms to signal that the test is not possible?
Because the XMPP service may provide a component that is required to perform a certain integration test, but that component may not support all features.
For example, the XMPP service may provides a PubSub (XEP-60) component, but this component may not support all features of XEP-60.
### Low-Level Integration Tests
Classes that implement low-level integration tests need to sublcass `AbstractSmackLowLevelIntegrationTest`.
The test methods can declare as many parameters as they need to, but every parameter must be of type `XMPPTCPConnection`.
The framework will automatically create, register and login the connections.
After the test is finished, the connections will be unregistered with the XMPP service and terminated.
Running your own integration tests
----------------------------------
The framework can be used to run your own tests.
Simply set the `testPackages` property to a comma separated list of package names where the framework should look for integration tests.
Example:
```bash
$ gradle integrationTest -Dsinttest.service=my.xmppserivce.org -Dsinttest.testPackages=org.mypackage,org.otherpackage
```

View File

@ -21,4 +21,5 @@ include 'smack-core',
'smack-bosh',
'smack-android',
'smack-android-extensions',
'smack-java7'
'smack-java7',
'smack-integration-test'

View File

@ -11,7 +11,7 @@ dependencies {
compile "org.jxmpp:jxmpp-core:$jxmppVersion"
compile "org.jxmpp:jxmpp-jid:$jxmppVersion"
testCompile "org.jxmpp:jxmpp-jid:$jxmppVersion:tests"
testCompile 'junit:junit:4.11'
testCompile "junit:junit:$junitVersion"
testCompile 'xmlunit:xmlunit:1.5'
testCompile 'org.powermock:powermock-module-junit4:1.5.5'
testCompile 'org.powermock:powermock-api-mockito:1.5.5'

View File

@ -31,35 +31,6 @@ public class LoginTest extends SmackTestCase {
super(arg0);
}
/**
* Check that the server is returning the correct error when trying to login using an invalid
* (i.e. non-existent) user.
*/
public void testInvalidLogin() {
try {
XMPPTCPConnection connection = createConnection();
connection.connect();
try {
// Login with an invalid user
connection.login("invaliduser" , "invalidpass");
connection.disconnect();
fail("Invalid user was able to log into the server");
}
catch (XMPPException e) {
if (e.getXMPPError() != null) {
assertEquals("Incorrect error code while login with an invalid user", 401,
e.getXMPPError().getCode());
}
}
// Wait here while trying tests with exodus
//Thread.sleep(300);
}
catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
}
/**
* Check that the server handles anonymous users correctly.
*/

View File

@ -50,6 +50,7 @@ import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.SmackException.ConnectionException;
import org.jivesoftware.smack.SmackException.ResourceBindingNotOfferedException;
import org.jivesoftware.smack.SmackException.SecurityRequiredException;
import org.jivesoftware.smack.XMPPException.StreamErrorException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.compress.packet.Compress;
import org.jivesoftware.smack.compression.XMPPInputOutputStream;
@ -68,6 +69,7 @@ import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Session;
import org.jivesoftware.smack.packet.StartTls;
import org.jivesoftware.smack.packet.PlainStreamElement;
import org.jivesoftware.smack.packet.StreamError;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.parsing.ParsingExceptionCallback;
import org.jivesoftware.smack.parsing.UnparsablePacket;
@ -1195,7 +1197,19 @@ public abstract class AbstractXMPPConnection implements XMPPConnection {
}
protected void callConnectionClosedOnErrorListener(Exception e) {
LOGGER.log(Level.WARNING, "Connection closed with error", e);
boolean logWarning = true;
if (e instanceof StreamErrorException) {
StreamErrorException see = (StreamErrorException) e;
if (see.getStreamError().getCondition() == StreamError.Condition.not_authorized
&& wasAuthenticated) {
logWarning = false;
LOGGER.log(Level.FINE,
"Connection closed with not-authorized stream error after it was already authenticated. The account was likely deleted/unregistered on the server");
}
}
if (logWarning) {
LOGGER.log(Level.WARNING, "Connection closed with error", e);
}
for (ConnectionListener listener : connectionListeners) {
try {
listener.connectionClosedOnError(e);

View File

@ -16,6 +16,9 @@
*/
package org.jivesoftware.smack.util;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Async {
/**
@ -50,4 +53,30 @@ public class Async {
thread.setDaemon(true);
return thread;
}
/**
* Like {@link Runnable}, but allows the <code>runOrThrow()</code> method to throw an exception.
* <p>
* If the exception is an instance of {@link RuntimeException}, then it will be re-thrown, otherwise <b>it will be
* simply logged.</b>
*/
public static abstract class ThrowingRunnable implements Runnable {
public static final Logger LOGGER = Logger.getLogger(ThrowingRunnable.class.getName());
@Override
public final void run() {
try {
runOrThrow();
}
catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
LOGGER.log(Level.WARNING, "Catched Exception", e);
}
}
public abstract void runOrThrow() throws Exception;
}
}

View File

@ -24,4 +24,8 @@ public class Objects {
}
return obj;
}
public static <T> T requireNonNull(T obj) {
return requireNonNull(obj, null);
}
}

View File

@ -1,67 +0,0 @@
/**
*
* Copyright 2003-2007 Jive Software.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx;
import org.jivesoftware.smack.PacketCollector;
import org.jivesoftware.smack.filter.PacketIDFilter;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.test.SmackTestCase;
import org.jivesoftware.smackx.packet.Version;
/**
* Test case to ensure that Smack is able to get and parse correctly iq:version packets.
*
* @author Gaston Dombiak
*/
public class VersionTest extends SmackTestCase {
public VersionTest(String arg0) {
super(arg0);
}
/**
* Get the version of the server and make sure that all the required data is present
*
* Note: This test expects the server to answer an iq:version packet.
*/
public void testGetServerVersion() {
Version version = new Version();
version.setType(IQ.Type.get);
version.setTo(getServiceName());
// Create a packet collector to listen for a response.
PacketCollector collector = getConnection(0).createPacketCollector(new PacketIDFilter(version.getStanzaId()));
getConnection(0).sendStanza(version);
// Wait up to 5 seconds for a result.
IQ result = (IQ)collector.nextResult(5000);
// Close the collector
collector.cancel();
assertNotNull("No result from the server", result);
assertEquals("Incorrect result type", IQ.Type.result, result.getType());
assertNotNull("No name specified in the result", ((Version)result).getName());
assertNotNull("No version specified in the result", ((Version)result).getVersion());
}
protected int getMaxConnections() {
return 1;
}
}

View File

@ -0,0 +1,21 @@
apply plugin: 'application'
description = """\
Smack integration tests."""
mainClassName = 'org.igniterealtime.smack.inttest.SmackIntegrationTestFramework'
dependencies {
compile project(':smack-java7')
compile project(':smack-tcp')
compile project(':smack-extensions')
compile 'org.reflections:reflections:0.9.9-RC1'
compile 'eu.geekplace.javapinning:java-pinning-jar:1.0.1'
compile "junit:junit:$junitVersion"
testCompile "org.jxmpp:jxmpp-jid:$jxmppVersion:tests"
}
run {
// Pass all system properties down to the "application" run
systemProperties System.getProperties()
}

View File

@ -0,0 +1,25 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.igniterealtime.smack.inttest;
import java.util.logging.Logger;
public abstract class AbstractSmackIntTest {
protected static final Logger LOGGER = Logger.getLogger(AbstractSmackIntTest.class.getName());
}

View File

@ -0,0 +1,52 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.igniterealtime.smack.inttest;
import org.jivesoftware.smack.XMPPConnection;
public abstract class AbstractSmackIntegrationTest extends AbstractSmackIntTest {
/**
* The first connection.
*/
protected final XMPPConnection conOne;
/**
* The second connection.
*/
protected final XMPPConnection conTwo;
/**
* An alias for the first connection {@link #conOne}.
*/
protected final XMPPConnection connection;
protected final long defaultTimeout;
protected final String testRunId;
public AbstractSmackIntegrationTest(SmackIntegrationTestEnvironment environment) {
this.connection = this.conOne = environment.conOne;
this.conTwo = environment.conTwo;
if (environment.configuration.replyTimeout > 0) {
this.defaultTimeout = environment.configuration.replyTimeout;
} else {
this.defaultTimeout = 2 * 60 * 1000;
}
this.testRunId = environment.testRunId;
}
}

View File

@ -0,0 +1,70 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.igniterealtime.smack.inttest;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.SSLContext;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jxmpp.jid.DomainBareJid;
import eu.geekplace.javapinning.JavaPinning;
public abstract class AbstractSmackLowLevelIntegrationTest extends AbstractSmackIntTest {
/**
* The configuration
*/
protected final Configuration configuration;
protected final String testRunId;
protected final DomainBareJid service;
public AbstractSmackLowLevelIntegrationTest(Configuration configuration, String testRunId) {
this.configuration = configuration;
this.testRunId = testRunId;
this.service = configuration.service;
}
public final XMPPTCPConnectionConfiguration.Builder getConnectionConfiguration() throws KeyManagementException, NoSuchAlgorithmException {
XMPPTCPConnectionConfiguration.Builder builder = XMPPTCPConnectionConfiguration.builder();
if (configuration.serviceTlsPin != null) {
SSLContext sc = JavaPinning.forPin(configuration.serviceTlsPin);
builder.setCustomSSLContext(sc);
}
builder.setSecurityMode(configuration.securityMode);
builder.setServiceName(service);
return builder;
}
protected void performCheck(ConnectionCallback callback) throws Exception {
XMPPTCPConnection connection = SmackIntegrationTestFramework.getConnectedConnection(configuration);
try {
callback.connectionCallback(connection);
} finally {
connection.disconnect();
}
}
public interface ConnectionCallback {
public void connectionCallback(XMPPTCPConnection connection) throws Exception;
}
}

View File

@ -0,0 +1,291 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.igniterealtime.smack.inttest;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.util.StringUtils;
import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
public class Configuration {
public final DomainBareJid service;
public final String serviceTlsPin;
public final SecurityMode securityMode;
public final int replyTimeout;
public final boolean registerAccounts;
public final String accountOneUsername;
public final String accountOnePassword;
public final String accountTwoUsername;
public final String accountTwoPassword;
public final boolean debug;
public final Set<String> enabledTests;
public final Set<String> disabledTests;
public final Set<String> testPackages;
private Configuration(DomainBareJid service, String serviceTlsPin, SecurityMode securityMode, int replyTimeout,
boolean debug, String accountOneUsername, String accountOnePassword, String accountTwoUsername,
String accountTwoPassword, Set<String> enabledTests, Set<String> disabledTests,
Set<String> testPackages) {
this.service = service;
this.serviceTlsPin = serviceTlsPin;
this.securityMode = securityMode;
this.replyTimeout = replyTimeout;
this.debug = debug;
if (StringUtils.isNullOrEmpty(accountOneUsername) || StringUtils.isNullOrEmpty(accountOnePassword)
|| StringUtils.isNullOrEmpty(accountTwoUsername)
|| StringUtils.isNullOrEmpty(accountTwoPassword)) {
registerAccounts = true;
}
else {
registerAccounts = false;
}
this.accountOneUsername = accountOneUsername;
this.accountOnePassword = accountOnePassword;
this.accountTwoUsername = accountTwoUsername;
this.accountTwoPassword = accountTwoPassword;
this.enabledTests = enabledTests;
this.disabledTests = disabledTests;
this.testPackages = testPackages;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private DomainBareJid service;
private String serviceTlsPin;
private SecurityMode securityMode;
private int replyTimeout;
private String accountOneUsername;
private String accountOnePassword;
private String accountTwoUsername;
private String accountTwoPassword;
private boolean debug;
private Set<String> enabledTests;
private Set<String> disabledTests;
private Set<String> testPackages;
private Builder() {
}
public Builder setService(String service) throws XmppStringprepException {
return setService(JidCreate.domainBareFrom(service));
}
public Builder setService(DomainBareJid service) {
this.service = service;
return this;
}
public Builder addEnabledTest(Class<? extends AbstractSmackIntTest> enabledTest) {
if (enabledTests == null) {
enabledTests = new HashSet<>();
}
enabledTests.add(enabledTest.getName());
// Also add the package of the test as test package
return addTestPackage(enabledTest.getPackage().getName());
}
public Builder addTestPackage(String testPackage) {
if (testPackages == null) {
testPackages = new HashSet<>();
}
testPackages.add(testPackage);
return this;
}
public Builder setUsernamesAndPassword(String accountOneUsername, String accountOnePassword,
String accountTwoUsername, String accountTwoPassword) {
this.accountOneUsername = accountOneUsername;
this.accountOnePassword = accountOnePassword;
this.accountTwoUsername = accountTwoUsername;
this.accountTwoPassword = accountTwoPassword;
return this;
}
public Builder setServiceTlsPin(String tlsPin) {
this.serviceTlsPin = tlsPin;
return this;
}
public Builder setSecurityMode(String securityModeString) {
if (securityModeString != null) {
securityMode = SecurityMode.valueOf(securityModeString);
}
else {
securityMode = SecurityMode.required;
}
return this;
}
public Builder setReplyTimeout(String timeout) {
if (timeout != null) {
replyTimeout = Integer.valueOf(timeout);
}
return this;
}
public Builder setDebug(String debugString) {
if (debugString != null) {
debug = Boolean.valueOf(debugString);
}
return this;
}
public Builder setEnabledTests(String enabledTestsString) {
enabledTests = getTestSetFrom(enabledTestsString);
return this;
}
public Builder setDisabledTests(String disabledTestsString) {
disabledTests = getTestSetFrom(disabledTestsString);
return this;
}
public Builder setTestPackages(String testPackagesString) {
if (testPackagesString != null) {
String[] testPackagesArray = testPackagesString.split(",");
testPackages = new HashSet<>(testPackagesArray.length);
for (String s : testPackagesArray) {
testPackages.add(s.trim());
}
}
return this;
}
public Configuration build() {
return new Configuration(service, serviceTlsPin, securityMode, replyTimeout, debug, accountOneUsername,
accountOnePassword, accountTwoUsername, accountTwoPassword, enabledTests, disabledTests,
testPackages);
}
}
private static final String SINTTEST = "sinttest.";
public static Configuration newConfiguration() throws IOException {
File propertiesFile = findPropertiesFile();
Properties properties = new Properties();
try (FileInputStream in = new FileInputStream(propertiesFile)) {
properties.load(in);
}
// Properties set via the system override the file properties
Properties systemProperties = System.getProperties();
for (Entry<Object, Object> entry : systemProperties.entrySet()) {
String key = (String) entry.getKey();
if (!key.startsWith(SINTTEST)) {
continue;
}
key = key.substring(SINTTEST.length());
String value = (String) entry.getValue();
properties.put(key, value);
}
Builder builder = builder();
builder.setService(properties.getProperty("service"));
builder.setServiceTlsPin(properties.getProperty("serviceTlsPin"));
builder.setSecurityMode(properties.getProperty("securityMode"));
builder.setReplyTimeout(properties.getProperty("replyTimeout", "60000"));
String accountOneUsername = properties.getProperty("accountOneUsername");
String accountOnePassword = properties.getProperty("accountOnePassword");
String accountTwoUsername = properties.getProperty("accountTwoUsername");
String accountTwoPassword = properties.getProperty("accountTwoPassword");
builder.setUsernamesAndPassword(accountOneUsername, accountOnePassword, accountTwoUsername, accountTwoPassword);
builder.setDebug(properties.getProperty("debug"));
builder.setEnabledTests(properties.getProperty("enabledTests"));
builder.setDisabledTests(properties.getProperty("disabledTests"));
builder.setTestPackages(properties.getProperty("testPackages"));
return builder.build();
}
private static File findPropertiesFile() throws IOException {
List<String> possibleLocations = new LinkedList<>();
possibleLocations.add("properties");
String userHome = System.getProperty("user.home");
if (userHome != null) {
possibleLocations.add(userHome + "/.config/smack-integration-test/properties");
}
for (String possibleLocation : possibleLocations) {
File res = new File(possibleLocation);
if (res.isFile())
return res;
}
throw new IOException("Could not find properties file");
}
private static Set<String> getTestSetFrom(String string) {
if (string == null) {
return null;
}
String[] stringArray = string.split(",");
Set<String> res = new HashSet<>(stringArray.length);
for (String s : stringArray) {
res.add(getFullTestStringFrom(s));
}
return res;
}
private static String getFullTestStringFrom(String string) {
string = string.trim();
if (string.startsWith("smackx.") || string.startsWith("smack.")) {
string = "org.jivesoftware." + string;
}
return string;
}
}

View File

@ -0,0 +1,30 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.igniterealtime.smack.inttest;
import java.lang.reflect.Method;
import java.util.List;
public class FailedTest extends TestResult {
public final Exception failureReason;
public FailedTest(Method testMethod, long startTime, long endTime, List<String> logMessages, Exception failureReason) {
super(testMethod, startTime, endTime, logMessages);
this.failureReason = failureReason;
}
}

View File

@ -0,0 +1,67 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.igniterealtime.smack.inttest;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.iqregister.AccountManager;
public class IntTestUtil {
public static UsernameAndPassword registerAccount(XMPPConnection connection)
throws NoResponseException, XMPPErrorException, NotConnectedException,
InterruptedException {
return registerAccount(connection, StringUtils.randomString(12),
StringUtils.randomString(12));
}
public static UsernameAndPassword registerAccount(XMPPConnection connection, String username,
String password) throws NoResponseException, XMPPErrorException,
NotConnectedException, InterruptedException {
AccountManager accountManager = AccountManager.getInstance(connection);
if (!accountManager.supportsAccountCreation()) {
throw new UnsupportedOperationException("Account creation/registation is not supported");
}
Set<String> requiredAttributes = accountManager.getAccountAttributes();
if (requiredAttributes.size() > 4) {
throw new IllegalStateException("Unkown required attributes");
}
Map<String, String> additionalAttributes = new HashMap<>();
additionalAttributes.put("name", "Smack Integration Test");
additionalAttributes.put("email", "flow@igniterealtime.org");
accountManager.createAccount(username, password, additionalAttributes);
return new UsernameAndPassword(username, password);
}
public static class UsernameAndPassword {
public final String username;
public final String password;
private UsernameAndPassword(String username, String password) {
this.username = username;
this.password = password;
}
}
}

View File

@ -0,0 +1,28 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.igniterealtime.smack.inttest;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SmackIntegrationTest {
}

View File

@ -0,0 +1,38 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.igniterealtime.smack.inttest;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
public class SmackIntegrationTestEnvironment {
public final XMPPTCPConnection conOne;
public final XMPPTCPConnection conTwo;
public final String testRunId;
public final Configuration configuration;
SmackIntegrationTestEnvironment(XMPPTCPConnection conOne, XMPPTCPConnection conTwo, String testRunId,
Configuration configuration) {
this.conOne = conOne;
this.conTwo = conTwo;
this.testRunId = testRunId;
this.configuration = configuration;
}
}

View File

@ -0,0 +1,633 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.igniterealtime.smack.inttest;
import static org.reflections.ReflectionUtils.getAllMethods;
import static org.reflections.ReflectionUtils.withAnnotation;
import static org.reflections.ReflectionUtils.withModifier;
import static org.reflections.ReflectionUtils.withParametersCount;
import static org.reflections.ReflectionUtils.withReturnType;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.SSLContext;
import org.igniterealtime.smack.inttest.IntTestUtil.UsernameAndPassword;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration.Builder;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.iqregister.AccountManager;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.reflections.Reflections;
import org.reflections.scanners.MethodAnnotationsScanner;
import org.reflections.scanners.MethodParameterScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import eu.geekplace.javapinning.JavaPinning;
public class SmackIntegrationTestFramework {
private static final Logger LOGGER = Logger.getLogger(SmackIntegrationTestFramework.class.getName());
private static final char CLASS_METHOD_SEP = '#';
protected final Configuration config;
protected TestRunResult testRunResult;
private SmackIntegrationTestEnvironment environment;
public enum TestType {
Normal,
LowLevel,
}
public static void main(String[] args) throws IOException, KeyManagementException,
NoSuchAlgorithmException, SmackException, XMPPException, InterruptedException {
Configuration config = Configuration.newConfiguration();
SmackIntegrationTestFramework sinttest = new SmackIntegrationTestFramework(config);
TestRunResult testRunResult = sinttest.run();
for (Entry<Class<? extends AbstractSmackIntTest>, String> entry : testRunResult.impossibleTestClasses.entrySet()) {
LOGGER.info("Could not run " + entry.getKey().getName() + " because: "
+ entry.getValue());
}
for (TestNotPossible testNotPossible : testRunResult.impossibleTestMethods) {
LOGGER.info("Could not run " + testNotPossible.testMethod.getName() + " because: "
+ testNotPossible.testNotPossibleException.getMessage());
}
LOGGER.info("SmackIntegrationTestFramework[" + testRunResult.testRunId + ']' + ": Finished ["
+ testRunResult.successfulTests.size() + '/' + testRunResult.numberOfTests + ']');
if (!testRunResult.failedIntegrationTests.isEmpty()) {
for (FailedTest failedTest : testRunResult.failedIntegrationTests) {
final Method method = failedTest.testMethod;
final String className = method.getDeclaringClass().getName();
final String methodName = method.getName();
final Exception cause = failedTest.failureReason;
LOGGER.severe(className + CLASS_METHOD_SEP + methodName + " failed: " + cause);
}
System.exit(2);
}
System.exit(0);
}
public SmackIntegrationTestFramework(Configuration configuration) {
this.config = configuration;
}
public synchronized TestRunResult run() throws KeyManagementException, NoSuchAlgorithmException, SmackException,
IOException, XMPPException, InterruptedException {
testRunResult = new TestRunResult();
LOGGER.info("SmackIntegrationTestFramework [" + testRunResult.testRunId + ']' + ": Starting");
if (config.debug) {
// JUL Debugger will not print any information until configured to print log messages of
// level FINE
// TODO configure JUL for log?
SmackConfiguration.addDisabledSmackClass("org.jivesoftware.smack.debugger.JulDebugger");
SmackConfiguration.DEBUG = true;
}
if (config.replyTimeout > 0) {
SmackConfiguration.setDefaultPacketReplyTimeout(config.replyTimeout);
}
if (config.securityMode != SecurityMode.required) {
AccountManager.sensitiveOperationOverInsecureConnectionDefault(true);
}
// TODO print effective configuration
String[] testPackages;
if (config.testPackages == null) {
testPackages = new String[] { "org.jivesoftware.smackx", "org.jivesoftware.smack" };
}
else {
testPackages = config.testPackages.toArray(new String[config.testPackages.size()]);
}
Reflections reflections = new Reflections((Object[]) testPackages, new SubTypesScanner(),
new TypeAnnotationsScanner(), new MethodAnnotationsScanner(), new MethodParameterScanner());
Set<Class<? extends AbstractSmackIntegrationTest>> inttestClasses = reflections.getSubTypesOf(AbstractSmackIntegrationTest.class);
Set<Class<? extends AbstractSmackLowLevelIntegrationTest>> lowLevelInttestClasses = reflections.getSubTypesOf(AbstractSmackLowLevelIntegrationTest.class);
Set<Class<? extends AbstractSmackIntTest>> classes = new HashSet<>(inttestClasses.size()
+ lowLevelInttestClasses.size());
classes.addAll(inttestClasses);
classes.addAll(lowLevelInttestClasses);
if (classes.isEmpty()) {
throw new IllegalStateException("No test classes found");
}
LOGGER.info("SmackIntegrationTestFramework [" + testRunResult.testRunId
+ "]: Finished scanning for tests, preparing environment");
environment = prepareEnvironment();
try {
runTests(classes);
}
finally {
// Ensure that the accounts are deleted and disconnected before we continue
disconnectAndMaybeDelete(environment.conOne);
disconnectAndMaybeDelete(environment.conTwo);
}
return testRunResult;
}
@SuppressWarnings("unchecked")
private void runTests(Set<Class<? extends AbstractSmackIntTest>> classes)
throws NoResponseException, NotConnectedException, InterruptedException {
for (Class<? extends AbstractSmackIntTest> testClass : classes) {
final String testClassName = testClass.getName();
if (config.enabledTests != null && !config.enabledTests.contains(testClassName)) {
LOGGER.info("Skipping test class " + testClassName + " because it is not enabled");
continue;
}
if (config.disabledTests != null && config.disabledTests.contains(testClassName)) {
LOGGER.info("Skipping test class " + testClassName + " because it is disalbed");
continue;
}
TestType testType;
if (AbstractSmackLowLevelIntegrationTest.class.isAssignableFrom(testClass)) {
testType = TestType.LowLevel;
} else if (AbstractSmackIntegrationTest.class.isAssignableFrom(testClass)) {
testType = TestType.Normal;
} else {
throw new AssertionError();
}
List<Method> smackIntegrationTestMethods = new LinkedList<>();
for (Method method : testClass.getMethods()) {
if (!method.isAnnotationPresent(SmackIntegrationTest.class)) {
continue;
}
Class<?> retClass = method.getReturnType();
if (!(retClass.equals(Void.TYPE))) {
LOGGER.warning("SmackIntegrationTest annotation on method that does not return void");
continue;
}
final Class<?>[] parameterTypes = method.getParameterTypes();
switch (testType) {
case Normal:
if (method.getParameterTypes().length > 0) {
LOGGER.warning("SmackIntegrationTest annotaton on method that takes arguments ");
continue;
}
break;
case LowLevel:
for (Class<?> parameterType : parameterTypes) {
if (!parameterType.isAssignableFrom(XMPPTCPConnection.class)) {
LOGGER.warning("SmackIntegrationTest low-level test method declares parameter that is not of type XMPPTCPConnection");
}
}
break;
}
smackIntegrationTestMethods.add(method);
}
if (smackIntegrationTestMethods.isEmpty()) {
LOGGER.warning("No integration test methods found");
continue;
}
Iterator<Method> it = smackIntegrationTestMethods.iterator();
while (it.hasNext()) {
final Method method = it.next();
final String methodName = method.getName();
final String className = method.getDeclaringClass().getName();
if (config.enabledTests != null && !config.enabledTests.contains(methodName)
&& !config.enabledTests.contains(className)) {
LOGGER.fine("Skipping test method " + methodName + " because it is not enabled");
it.remove();
continue;
}
if (config.disabledTests != null && config.disabledTests.contains(methodName)) {
LOGGER.info("Skipping test method " + methodName + " because it is disabled");
it.remove();
continue;
}
}
if (smackIntegrationTestMethods.isEmpty()) {
LOGGER.info("All tests in " + testClassName + " are disabled");
continue;
}
testRunResult.numberOfTests.addAndGet(smackIntegrationTestMethods.size());
AbstractSmackIntTest test;
switch (testType) {
case Normal: {
Constructor<? extends AbstractSmackIntegrationTest> cons;
try {
cons = ((Class<? extends AbstractSmackIntegrationTest>) testClass).getConstructor(SmackIntegrationTestEnvironment.class);
}
catch (NoSuchMethodException | SecurityException e) {
LOGGER.log(Level.WARNING,
"Smack Integration Test class could not get constructed (public Con)structor(SmackIntegrationTestEnvironment) missing?)",
e);
continue;
}
try {
test = cons.newInstance(environment);
}
catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof TestNotPossibleException) {
testRunResult.impossibleTestClasses.put(testClass, cause.getMessage());
}
else {
throwFatalException(cause);
LOGGER.log(Level.WARNING, "Could not construct test class", e);
}
continue;
}
catch (InstantiationException | IllegalAccessException | IllegalArgumentException e) {
LOGGER.log(Level.WARNING, "todo", e);
continue;
}
} break;
case LowLevel: {
Constructor<? extends AbstractSmackLowLevelIntegrationTest> cons;
try {
cons = ((Class<? extends AbstractSmackLowLevelIntegrationTest>) testClass).getConstructor(
Configuration.class, String.class);
}
catch (NoSuchMethodException | SecurityException e) {
LOGGER.log(Level.WARNING,
"Smack Integration Test class could not get constructed (public Con)structor(SmackIntegrationTestEnvironment) missing?)",
e);
continue;
}
try {
test = cons.newInstance(config, testRunResult.testRunId);
}
catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof TestNotPossibleException) {
testRunResult.impossibleTestClasses.put(testClass, cause.getMessage());
}
else {
throwFatalException(cause);
LOGGER.log(Level.WARNING, "Could not construct test class", e);
}
continue;
}
catch (InstantiationException | IllegalAccessException | IllegalArgumentException e) {
LOGGER.log(Level.WARNING, "todo", e);
continue;
}
} break;
default:
throw new AssertionError();
}
try {
// Run the @BeforeClass methods (if any)
Set<Method> beforeClassMethods = getAllMethods(testClass,
withAnnotation(BeforeClass.class), withReturnType(Void.TYPE),
withParametersCount(0), withModifier(Modifier.PUBLIC
| Modifier.STATIC));
// See if there are any methods that have the @BeforeClassAnnotation but a wrong signature
Set<Method> allBeforeClassMethods = getAllMethods(testClass, withAnnotation(BeforeClass.class));
allBeforeClassMethods.removeAll(beforeClassMethods);
if (!allBeforeClassMethods.isEmpty()) {
LOGGER.warning("@BeforeClass methods with wrong signature found");
}
if (beforeClassMethods.size() == 1) {
Method beforeClassMethod = beforeClassMethods.iterator().next();
try {
beforeClassMethod.invoke(null);
}
catch (InvocationTargetException | IllegalAccessException e) {
LOGGER.log(Level.SEVERE, "Exception executing @AfterClass method", e);
}
catch (IllegalArgumentException e) {
throw new AssertionError(e);
}
}
else if (beforeClassMethods.size() > 1) {
throw new IllegalArgumentException("Only one @BeforeClass method allowed");
}
for (Method testMethod : smackIntegrationTestMethods) {
final String testPrefix = testClass.getSimpleName() + '.'
+ testMethod.getName() + ": ";
// Invoke all test methods on the test instance
LOGGER.info(testPrefix + "Start");
long testStart = System.currentTimeMillis();
try {
switch (testType) {
case Normal:
testMethod.invoke(test);
break;
case LowLevel:
invokeLowLevel(testMethod, test);
break;
}
LOGGER.info(testPrefix + "Success");
long testEnd = System.currentTimeMillis();
testRunResult.successfulTests.add(new SuccessfulTest(testMethod, testStart, testEnd, null));
}
catch (InvocationTargetException e) {
long testEnd = System.currentTimeMillis();
Throwable cause = e.getCause();
if (cause instanceof TestNotPossibleException) {
LOGGER.info(testPrefix + "Not possible");
testRunResult.impossibleTestMethods.add(new TestNotPossible(testMethod, testStart, testEnd,
null, (TestNotPossibleException) cause));
continue;
}
Exception nonFatalException = throwFatalException(cause);
// An integration test failed
testRunResult.failedIntegrationTests.add(new FailedTest(testMethod, testStart, testEnd, null,
nonFatalException));
LOGGER.log(Level.SEVERE, testPrefix + "Failed", e);
}
catch (IllegalArgumentException | IllegalAccessException e) {
throw new AssertionError(e);
}
}
}
finally {
// Run the @AfterClass method (if any)
Set<Method> afterClassMethods = getAllMethods(testClass,
withAnnotation(AfterClass.class), withReturnType(Void.TYPE),
withParametersCount(0), withModifier(Modifier.PUBLIC
| Modifier.STATIC));
// See if there are any methods that have the @AfterClassAnnotation but a wrong signature
Set<Method> allAfterClassMethods = getAllMethods(testClass, withAnnotation(BeforeClass.class));
allAfterClassMethods.removeAll(afterClassMethods);
if (!allAfterClassMethods.isEmpty()) {
LOGGER.warning("@AfterClass methods with wrong signature found");
}
if (afterClassMethods.size() == 1) {
Method afterClassMethod = afterClassMethods.iterator().next();
try {
afterClassMethod.invoke(null);
}
catch (InvocationTargetException | IllegalAccessException e) {
LOGGER.log(Level.SEVERE, "Exception executing @AfterClass method", e);
}
catch (IllegalArgumentException e) {
throw new AssertionError(e);
}
}
else if (afterClassMethods.size() > 1) {
throw new IllegalArgumentException("Only one @AfterClass method allowed");
}
}
}
}
private void invokeLowLevel(Method testMethod, AbstractSmackIntTest test) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
// We have checked before that every parameter, if any, is of type XMPPTCPConnection
final int numberOfConnections = testMethod.getParameterTypes().length;
XMPPTCPConnection[] connections = null;
try {
if (numberOfConnections > 0 && !config.registerAccounts) {
throw new TestNotPossibleException(
"Must create accounts for this test, but it's not enabled");
}
connections = new XMPPTCPConnection[numberOfConnections];
for (int i = 0; i < numberOfConnections; ++i) {
connections[i] = getConnectedConnection(config);
}
}
catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
// Behave like this was an InvocationTargetException
throw new InvocationTargetException(e);
}
try {
testMethod.invoke(test, (Object[]) connections);
}
finally {
for (int i = 0; i < numberOfConnections; ++i) {
try {
AccountManager.getInstance(connections[i]).deleteAccount();
LOGGER.info("Successfully deleted account for connection ("
+ connections[i].getConnectionCounter() + ')');
}
catch (NoResponseException | XMPPErrorException | NotConnectedException
| InterruptedException e) {
LOGGER.log(Level.SEVERE, "Could not delete account", e);
}
connections[i].disconnect();
}
}
}
protected void disconnectAndMaybeDelete(XMPPTCPConnection connection)
throws NoResponseException, XMPPErrorException, NotConnectedException,
InterruptedException {
if (config.registerAccounts) {
AccountManager am = AccountManager.getInstance(connection);
am.deleteAccount();
}
connection.disconnect();
}
protected SmackIntegrationTestEnvironment prepareEnvironment() throws SmackException,
IOException, XMPPException, InterruptedException, KeyManagementException,
NoSuchAlgorithmException {
XMPPTCPConnection conOne = null;
XMPPTCPConnection conTwo = null;
try {
conOne = getConnectedConnectionFor(AccountNum.One);
conTwo = getConnectedConnectionFor(AccountNum.Two);
}
catch (Exception e) {
if (conOne != null) {
conOne.disconnect();
}
if (conTwo != null) {
conTwo.disconnect();
}
throw e;
}
return new SmackIntegrationTestEnvironment(conOne, conTwo, testRunResult.testRunId, config);
}
enum AccountNum {
One,
Two,
}
private static final String USERNAME_PREFIX = "smack-inttest";
private XMPPTCPConnection getConnectedConnectionFor(AccountNum accountNum)
throws SmackException, IOException, XMPPException, InterruptedException,
KeyManagementException, NoSuchAlgorithmException {
String middlefix;
String accountUsername;
String accountPassword;
switch (accountNum) {
case One:
accountUsername = config.accountOneUsername;
accountPassword = config.accountOnePassword;
middlefix = "one";
break;
case Two:
accountUsername = config.accountTwoUsername;
accountPassword = config.accountTwoPassword;
middlefix = "two";
break;
default:
throw new IllegalStateException();
}
if (StringUtils.isNullOrEmpty(accountUsername)) {
accountUsername = USERNAME_PREFIX + '-' + middlefix + '-' +testRunResult.testRunId;
}
if (StringUtils.isNullOrEmpty(accountPassword)) {
accountPassword = StringUtils.randomString(16);
}
// @formatter:off
Builder builder = XMPPTCPConnectionConfiguration.builder()
.setServiceName(config.service)
.setUsernameAndPassword(accountUsername, accountPassword)
.setResource(middlefix + '-' + testRunResult.testRunId)
.setSecurityMode(config.securityMode);
// @formatter:on
if (StringUtils.isNotEmpty(config.serviceTlsPin)) {
SSLContext sc = JavaPinning.forPin(config.serviceTlsPin);
builder.setCustomSSLContext(sc);
}
XMPPTCPConnection connection = new XMPPTCPConnection(builder.build());
connection.connect();
if (config.registerAccounts) {
IntTestUtil.registerAccount(connection, accountUsername, accountPassword);
// TODO is this still required?
// Some servers, e.g. Openfire, do not support a login right after the account was
// created, so disconnect and re-connection the connection first.
connection.disconnect();
connection.connect();
}
connection.login();
return connection;
}
static XMPPTCPConnection getConnectedConnection(Configuration config)
throws KeyManagementException, NoSuchAlgorithmException, InterruptedException,
SmackException, IOException, XMPPException {
XMPPTCPConnectionConfiguration.Builder builder = XMPPTCPConnectionConfiguration.builder();
if (config.serviceTlsPin != null) {
SSLContext sc = JavaPinning.forPin(config.serviceTlsPin);
builder.setCustomSSLContext(sc);
}
builder.setSecurityMode(config.securityMode);
builder.setServiceName(config.service);
XMPPTCPConnection connection = new XMPPTCPConnection(builder.build());
connection.connect();
UsernameAndPassword uap = IntTestUtil.registerAccount(connection);
connection.login(uap.username, uap.password);
return connection;
}
private static Exception throwFatalException(Throwable e) throws Error, NotConnectedException, NoResponseException,
InterruptedException {
if (e instanceof NotConnectedException) {
throw (NotConnectedException) e;
}
if (e instanceof NoResponseException) {
throw (NoResponseException) e;
}
if (e instanceof InterruptedException) {
throw (InterruptedException) e;
}
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
if (e instanceof Error) {
throw (Error) e;
}
return (Exception) e;
}
public static class TestRunResult {
public final String testRunId = StringUtils.randomString(5);
private final List<SuccessfulTest> successfulTests = Collections.synchronizedList(new LinkedList<SuccessfulTest>());
private final List<FailedTest> failedIntegrationTests = Collections.synchronizedList(new LinkedList<FailedTest>());
private final List<TestNotPossible> impossibleTestMethods = Collections.synchronizedList(new LinkedList<TestNotPossible>());
private final Map<Class<? extends AbstractSmackIntTest>, String> impossibleTestClasses = new HashMap<>();
private final AtomicInteger numberOfTests = new AtomicInteger();
private TestRunResult() {
}
public String getTestRunId() {
return testRunId;
}
public int getNumberOfTests() {
return numberOfTests.get();
}
public List<SuccessfulTest> getSuccessfulTests() {
return Collections.unmodifiableList(successfulTests);
}
public List<FailedTest> getFailedTests() {
return Collections.unmodifiableList(failedIntegrationTests);
}
public List<TestNotPossible> getNotPossibleTests() {
return Collections.unmodifiableList(impossibleTestMethods);
}
public Map<Class<? extends AbstractSmackIntTest>, String> getImpossibleTestClasses() {
return Collections.unmodifiableMap(impossibleTestClasses);
}
}
}

View File

@ -0,0 +1,28 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.igniterealtime.smack.inttest;
import java.lang.reflect.Method;
import java.util.List;
public class SuccessfulTest extends TestResult {
public SuccessfulTest(Method testMethod, long startTime, long endTime, List<String> logMessages) {
super(testMethod, startTime, endTime, logMessages);
}
}

View File

@ -0,0 +1,31 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.igniterealtime.smack.inttest;
import java.lang.reflect.Method;
import java.util.List;
public class TestNotPossible extends TestResult {
public final TestNotPossibleException testNotPossibleException;
public TestNotPossible(Method testMethod, long startTime, long endTime, List<String> logMessages,
TestNotPossibleException testNotPossibleException) {
super(testMethod, startTime, endTime, logMessages);
this.testNotPossibleException = testNotPossibleException;
}
}

View File

@ -0,0 +1,29 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.igniterealtime.smack.inttest;
public class TestNotPossibleException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public TestNotPossibleException(String reason) {
super(reason);
}
}

View File

@ -0,0 +1,38 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.igniterealtime.smack.inttest;
import java.lang.reflect.Method;
import java.util.List;
public abstract class TestResult {
public final Method testMethod;
public final long startTime;
public final long endTime;
public final long duration;
public final List<String> logMessages;
public TestResult(Method testMethod, long startTime, long endTime, List<String> logMessages) {
this.testMethod = testMethod;
assert (endTime > startTime);
this.startTime = startTime;
this.endTime = endTime;
this.duration = endTime - startTime;
this.logMessages = logMessages;
}
}

View File

@ -0,0 +1,61 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.igniterealtime.smack.inttest.util;
import java.util.concurrent.TimeoutException;
import org.jivesoftware.smack.util.Objects;
public class ResultSyncPoint<R, E extends Exception> {
private R result;
private E exception;
public R waitForResult(long timeout) throws E, InterruptedException, TimeoutException {
synchronized(this) {
if (result != null) {
return result;
}
if (exception != null) {
throw exception;
}
wait(timeout);
}
if (result != null) {
return result;
}
if (exception != null) {
throw exception;
}
throw new TimeoutException("Timeout expired");
}
public void signal(R result) {
synchronized(this) {
this.result = Objects.requireNonNull(result);
notifyAll();
}
}
public void signal(E exception) {
synchronized(this) {
this.exception = Objects.requireNonNull(exception);
notifyAll();
}
}
}

View File

@ -0,0 +1,71 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import org.igniterealtime.smack.inttest.AbstractSmackLowLevelIntegrationTest;
import org.igniterealtime.smack.inttest.Configuration;
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
import org.jivesoftware.smack.sasl.SASLError;
import org.jivesoftware.smack.sasl.SASLErrorException;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jivesoftware.smack.util.StringUtils;
public class LoginIntegrationTest extends AbstractSmackLowLevelIntegrationTest {
public LoginIntegrationTest(Configuration configuration, String testRunId) {
super(configuration, testRunId);
}
/**
* Check that the server is returning the correct error when trying to login using an invalid
* (i.e. non-existent) user.
*
* @throws InterruptedException
* @throws XMPPException
* @throws IOException
* @throws SmackException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
@SmackIntegrationTest
public void testInvalidLogin() throws SmackException, IOException, XMPPException,
InterruptedException, KeyManagementException, NoSuchAlgorithmException {
final String nonExistentUserString = StringUtils.randomString(24);
XMPPTCPConnectionConfiguration conf = getConnectionConfiguration().setUsernameAndPassword(
nonExistentUserString, "invalidPassword").build();
XMPPTCPConnection connection = new XMPPTCPConnection(conf);
connection.connect();
try {
connection.login();
fail("Exception expected");
}
catch (SASLErrorException e) {
assertEquals(SASLError.not_authorized, e.getSASLFailure().getSASLError());
}
}
}

View File

@ -0,0 +1,82 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import org.igniterealtime.smack.inttest.AbstractSmackLowLevelIntegrationTest;
import org.igniterealtime.smack.inttest.Configuration;
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
import org.igniterealtime.smack.inttest.TestNotPossibleException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.junit.AfterClass;
import org.junit.BeforeClass;
public class StreamManagementTest extends AbstractSmackLowLevelIntegrationTest {
public StreamManagementTest(Configuration configuration, String testRunId)
throws Exception {
super(configuration, testRunId);
performCheck(new ConnectionCallback() {
@Override
public void connectionCallback(XMPPTCPConnection connection) throws Exception {
if (!connection.isSmAvailable()) {
throw new TestNotPossibleException("XEP-198: Stream Mangement not supported by service");
}
}
});
}
@BeforeClass
public static void before() {
// TODO remove this once stream mangement is enabled per default
XMPPTCPConnection.setUseStreamManagementDefault(true);
}
@AfterClass
public static void after() {
XMPPTCPConnection.setUseStreamManagementDefault(false);
}
@SmackIntegrationTest
public void testStreamManagement(XMPPTCPConnection conOne, XMPPTCPConnection conTwo) throws InterruptedException, KeyManagementException,
NoSuchAlgorithmException, SmackException, IOException, XMPPException,
TestNotPossibleException {
send("Hi, what's up?", conOne, conTwo);
conOne.instantShutdown();
send("Hi, what's up? I've been just instantly shutdown", conOne, conTwo);
// Reconnect with xep198
conOne.connect();
send("Hi, what's up? I've been just resumed", conOne, conTwo);
// TODO check that all messages where received
}
private static void send(String messageString, XMPPConnection from, XMPPConnection to)
throws NotConnectedException, InterruptedException {
Message message = new Message(to.getUser());
message.setBody(messageString);
from.sendStanza(message);
}
}

View File

@ -0,0 +1,112 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.filetransfer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
import org.igniterealtime.smack.inttest.util.ResultSyncPoint;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.util.StringUtils;
public class FileTransferIntegrationTest extends AbstractSmackIntegrationTest {
private static final int MAX_FT_DURATION = 360;
private final FileTransferManager ftManagerOne;
private final FileTransferManager ftManagerTwo;
public FileTransferIntegrationTest(SmackIntegrationTestEnvironment environment) {
super(environment);
ftManagerOne = FileTransferManager.getInstanceFor(conOne);
ftManagerTwo = FileTransferManager.getInstanceFor(conTwo);
}
private static final byte[] dataToSend = StringUtils.randomString(1024 * 4 * 5).getBytes();
@SmackIntegrationTest
public void fileTransferTest() throws Exception {
genericfileTransferTest();
}
@SmackIntegrationTest
public void ibbFileTransferTest() throws Exception {
FileTransferNegotiator.IBB_ONLY = true;
genericfileTransferTest();
FileTransferNegotiator.IBB_ONLY = false;
}
private void genericfileTransferTest() throws Exception {
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
final FileTransferListener receiveListener = new FileTransferListener() {
@Override
public void fileTransferRequest(FileTransferRequest request) {
byte[] dataReceived = null;
IncomingFileTransfer ift = request.accept();
try {
InputStream is = ift.recieveFile();
ByteArrayOutputStream os = new ByteArrayOutputStream();
int nRead;
byte[] buf = new byte[1024];
while ((nRead = is.read(buf, 0, buf.length)) != -1) {
os.write(buf, 0, nRead);
}
os.flush();
dataReceived = os.toByteArray();
if (Arrays.equals(dataToSend, dataReceived)) {
resultSyncPoint.signal("Received data matches send data. \\o/");
}
else {
resultSyncPoint.signal(new Exception("Received data does not match"));
}
}
catch (SmackException | IOException | XMPPErrorException | InterruptedException e) {
resultSyncPoint.signal(e);
}
}
};
ftManagerTwo.addFileTransferListener(receiveListener);
OutgoingFileTransfer oft = ftManagerOne.createOutgoingFileTransfer(conTwo.getUser());
oft.sendStream(new ByteArrayInputStream(dataToSend), "hello.txt", dataToSend.length, "A greeting");
int duration = 0;
while (!oft.isDone()) {
switch (oft.getStatus()) {
case error:
throw new Exception("Filetransfer error: " + oft.getError());
default:
LOGGER.info("Filetransfer status: " + oft.getStatus() + ". Progress: " + oft.getProgress());
break;
}
Thread.sleep(1000);
if (++duration > MAX_FT_DURATION) {
throw new Exception("Max duration reached");
}
}
resultSyncPoint.waitForResult(MAX_FT_DURATION * 1000);
ftManagerTwo.removeFileTransferListener(receiveListener);
}
}

View File

@ -0,0 +1,51 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.iqversion;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
import org.igniterealtime.smack.inttest.TestNotPossibleException;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smackx.iqversion.packet.Version;
public class VersionIntegrationTest extends AbstractSmackIntegrationTest {
public VersionIntegrationTest(SmackIntegrationTestEnvironment environment) {
super(environment);
}
@SmackIntegrationTest
public void testVersion() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, TestNotPossibleException {
// TODO put into @BeforeClass method
VersionManager.setAutoAppendSmackVersion(false);
VersionManager versionManagerOne = VersionManager.getInstanceFor(conOne);
VersionManager versionManagerTwo = VersionManager.getInstanceFor(conTwo);
final String versionName = "Smack Integration Test " + testRunId;
versionManagerTwo.setVersion(versionName, "1.0");
assertTrue (versionManagerOne.isSupported(conTwo.getUser()));
Version version = versionManagerOne.getVersion(conTwo.getUser());
assertEquals(versionName, version.getName());
}
}

View File

@ -0,0 +1,97 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.muc;
import java.util.List;
import java.util.concurrent.TimeoutException;
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
import org.igniterealtime.smack.inttest.TestNotPossibleException;
import org.igniterealtime.smack.inttest.util.ResultSyncPoint;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.xdata.Form;
import org.jivesoftware.smackx.xdata.packet.DataForm;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.jid.parts.Localpart;
import org.jxmpp.jid.parts.Resourcepart;
public class MultiUserChatIntegrationTest extends AbstractSmackIntegrationTest {
private final String randomString = StringUtils.randomString(6);
private final MultiUserChatManager mucManagerOne;
private final MultiUserChatManager mucManagerTwo;
private final DomainBareJid mucService;
public MultiUserChatIntegrationTest(SmackIntegrationTestEnvironment environment)
throws NoResponseException, XMPPErrorException, NotConnectedException,
InterruptedException, TestNotPossibleException {
super(environment);
mucManagerOne = MultiUserChatManager.getInstanceFor(conOne);
mucManagerTwo = MultiUserChatManager.getInstanceFor(conTwo);
List<DomainBareJid> services = mucManagerOne.getServiceNames();
if (services.isEmpty()) {
throw new TestNotPossibleException("No MUC (XEP-45) service found");
}
else {
mucService = services.get(0);
}
}
@SmackIntegrationTest
public void mucTest() throws TimeoutException, Exception {
BareJid mucAddress = JidCreate.bareFrom(Localpart.from("smack-inttest-" + randomString), mucService.getDomain());
MultiUserChat mucAsSeenByOne = mucManagerOne.getMultiUserChat(mucAddress);
MultiUserChat mucAsSeenByTwo = mucManagerTwo.getMultiUserChat(mucAddress);
final String mucMessage = "Smack Integration Test MUC Test Message " + randomString;
final ResultSyncPoint<String, Exception> resultSyncPoint = new ResultSyncPoint<>();
mucAsSeenByTwo.addMessageListener(new MessageListener() {
@Override
public void processMessage(Message message) {
String body = message.getBody();
if (mucMessage.equals(body)) {
resultSyncPoint.signal(body);
}
}
});
boolean newlyCreated = mucAsSeenByOne.createOrJoin(Resourcepart.from("one-" + randomString));
if (newlyCreated) {
mucAsSeenByOne.sendConfigurationForm(new Form(DataForm.Type.submit));
}
mucAsSeenByTwo.join(Resourcepart.from("two-" + randomString));
mucAsSeenByOne.sendMessage(mucMessage);
resultSyncPoint.waitForResult(defaultTimeout);
mucAsSeenByOne.leave();
mucAsSeenByTwo.leave();
}
}

View File

@ -0,0 +1,38 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.ping;
import static org.junit.Assert.assertTrue;
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
import org.jivesoftware.smack.SmackException.NotConnectedException;
public class PingIntegrationTest extends AbstractSmackIntegrationTest {
public PingIntegrationTest(SmackIntegrationTestEnvironment environment) {
super(environment);
}
@SmackIntegrationTest
public void pingServer() throws NotConnectedException, InterruptedException {
PingManager pingManager = PingManager.getInstanceFor(connection);
assertTrue(pingManager.pingMyServer());
}
}

View File

@ -0,0 +1,36 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.igniterealtime.smack.inttest;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
public class DummySmackIntegrationTestFramework extends SmackIntegrationTestFramework {
public DummySmackIntegrationTestFramework(Configuration configuration) {
super(configuration);
}
@Override
protected SmackIntegrationTestEnvironment prepareEnvironment() {
return new SmackIntegrationTestEnvironment(null, null, testRunResult.getTestRunId(), config);
}
@Override
protected void disconnectAndMaybeDelete(XMPPTCPConnection connection) {
// This method is a no-op in DummySmackIntegrationTestFramework
}
}

View File

@ -0,0 +1,33 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.igniterealtime.smack.inttest;
import org.jxmpp.jid.JidTestUtil;
public class SmackIntegrationTestUnitTestUtil {
public static DummySmackIntegrationTestFramework getFrameworkForUnitTest(Class<? extends AbstractSmackIntTest> unitTest) {
// @formatter:off
Configuration configuration = Configuration.builder()
.setService(JidTestUtil.DOMAIN_BARE_JID_1)
.setUsernamesAndPassword("dummy1", "dummy1pass", "dummy2", "dummy2pass")
.addEnabledTest(unitTest).build();
// @formatter:on
return new DummySmackIntegrationTestFramework(configuration);
}
}

View File

@ -0,0 +1,98 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.igniterealtime.smack.inttest.unittest;
import static org.igniterealtime.smack.inttest.SmackIntegrationTestUnitTestUtil.getFrameworkForUnitTest;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
import org.igniterealtime.smack.inttest.DummySmackIntegrationTestFramework;
import org.igniterealtime.smack.inttest.FailedTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
import org.igniterealtime.smack.inttest.SmackIntegrationTestFramework.TestRunResult;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.packet.XMPPError;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
public class SmackIntegrationTestFrameworkUnitTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test
public void throwsRuntimeExceptionsTest() throws KeyManagementException, NoSuchAlgorithmException, SmackException,
IOException, XMPPException, InterruptedException {
expectedException.expect(RuntimeException.class);
expectedException.expectMessage(ThrowsRuntimeExceptionDummyTest.RUNTIME_EXCEPTION_MESSAGE);
DummySmackIntegrationTestFramework sinttest = getFrameworkForUnitTest(ThrowsRuntimeExceptionDummyTest.class);
sinttest.run();
}
public static class ThrowsRuntimeExceptionDummyTest extends AbstractSmackIntegrationTest {
public ThrowsRuntimeExceptionDummyTest(SmackIntegrationTestEnvironment environment) {
super(environment);
}
public static final String RUNTIME_EXCEPTION_MESSAGE = "Dummy RuntimeException";
@SmackIntegrationTest
public void throwRuntimeExceptionTest() {
throw new RuntimeException(RUNTIME_EXCEPTION_MESSAGE);
}
}
@Test
public void logsNonFatalExceptionTest() throws KeyManagementException, NoSuchAlgorithmException, SmackException,
IOException, XMPPException, InterruptedException {
DummySmackIntegrationTestFramework sinttest = getFrameworkForUnitTest(ThrowsNonFatalExceptionDummyTest.class);
TestRunResult testRunResult = sinttest.run();
List<FailedTest> failedTests = testRunResult.getFailedTests();
assertEquals(1, failedTests.size());
FailedTest failedTest = failedTests.get(0);
assertTrue(failedTest.failureReason instanceof XMPPErrorException);
XMPPErrorException ex = (XMPPErrorException) failedTest.failureReason;
assertEquals(XMPPError.Condition.bad_request, ex.getXMPPError().getCondition());
assertEquals(ThrowsNonFatalExceptionDummyTest.DESCRIPTIVE_TEXT, ex.getXMPPError().getDescriptiveText());
}
public static class ThrowsNonFatalExceptionDummyTest extends AbstractSmackIntegrationTest {
public static final String DESCRIPTIVE_TEXT = "I'm not fatal";
public ThrowsNonFatalExceptionDummyTest(SmackIntegrationTestEnvironment environment) {
super(environment);
}
@SmackIntegrationTest
public void throwRuntimeExceptionTest() throws XMPPErrorException {
throw new XMPPException.XMPPErrorException(
XMPPError.from(XMPPError.Condition.bad_request, DESCRIPTIVE_TEXT));
}
}
}

View File

@ -0,0 +1,70 @@
/**
*
* Copyright 2015 Florian Schmaus
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.igniterealtime.smack.inttest.util;
import static org.junit.Assert.assertEquals;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeoutException;
import org.jivesoftware.smack.util.Async;
import org.junit.Test;
public class ResultSyncPointTest {
@Test
public void testResultSyncPoint() throws InterruptedException, TimeoutException, Exception {
final String result = "Hip Hip Hurrary!!111!";
final CyclicBarrier barrier = new CyclicBarrier(2);
final ResultSyncPoint<String, Exception> rsp = new ResultSyncPoint<>();
Async.go(new Async.ThrowingRunnable() {
@Override
public void runOrThrow() throws InterruptedException, BrokenBarrierException {
barrier.await();
rsp.signal(result);
}
});
barrier.await();
String receivedResult = rsp.waitForResult(60 * 1000);
assertEquals(result, receivedResult);
}
@Test(expected=TestException.class)
public void exceptionTestResultSyncPoint() throws InterruptedException, TimeoutException, Exception {
final CyclicBarrier barrier = new CyclicBarrier(2);
final ResultSyncPoint<String, TestException> rsp = new ResultSyncPoint<>();
Async.go(new Async.ThrowingRunnable() {
@Override
public void runOrThrow() throws InterruptedException, BrokenBarrierException {
barrier.await();
rsp.signal(new TestException());
}
});
barrier.await();
rsp.waitForResult(60 * 1000);
}
private static class TestException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
}
}