1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2025-09-09 18:29:45 +02:00

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

@ -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;
}
}