2015-03-18 09:52:33 +01:00
|
|
|
/**
|
|
|
|
*
|
2017-03-09 21:35:29 +01:00
|
|
|
* Copyright 2015-2017 Florian Schmaus
|
2015-03-18 09:52:33 +01:00
|
|
|
*
|
|
|
|
* 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;
|
2016-06-29 08:08:13 +02:00
|
|
|
import java.util.Locale;
|
2015-03-18 09:52:33 +01:00
|
|
|
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 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.XMPPException;
|
2017-07-28 11:59:11 +02:00
|
|
|
import org.jivesoftware.smack.debugger.ConsoleDebugger;
|
2015-03-18 09:52:33 +01:00
|
|
|
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;
|
2017-06-14 17:12:43 +02:00
|
|
|
|
2017-07-28 11:59:11 +02:00
|
|
|
import org.jivesoftware.smackx.debugger.EnhancedDebugger;
|
|
|
|
import org.jivesoftware.smackx.debugger.EnhancedDebuggerWindow;
|
2015-03-18 09:52:33 +01:00
|
|
|
import org.jivesoftware.smackx.iqregister.AccountManager;
|
2017-06-14 17:12:43 +02:00
|
|
|
|
|
|
|
import org.igniterealtime.smack.inttest.IntTestUtil.UsernameAndPassword;
|
2015-03-18 09:52:33 +01:00
|
|
|
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;
|
|
|
|
|
|
|
|
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 {
|
2017-06-02 12:26:37 +02:00
|
|
|
Configuration config = Configuration.newConfiguration(args);
|
2015-03-18 09:52:33 +01:00
|
|
|
|
|
|
|
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());
|
|
|
|
}
|
2016-12-19 14:35:09 +01:00
|
|
|
final int successfulTests = testRunResult.successfulTests.size();
|
|
|
|
final int availableTests = testRunResult.getNumberOfAvailableTests();
|
|
|
|
final int possibleTests = testRunResult.getNumberOfPossibleTests();
|
2015-03-18 09:52:33 +01:00
|
|
|
LOGGER.info("SmackIntegrationTestFramework[" + testRunResult.testRunId + ']' + ": Finished ["
|
2016-12-19 14:35:09 +01:00
|
|
|
+ successfulTests + '/' + possibleTests + "] (of " + availableTests + " available tests)");
|
2017-07-28 11:59:11 +02:00
|
|
|
|
|
|
|
int exitStatus;
|
2015-03-18 09:52:33 +01:00
|
|
|
if (!testRunResult.failedIntegrationTests.isEmpty()) {
|
2016-12-19 14:35:09 +01:00
|
|
|
final int failedTests = testRunResult.failedIntegrationTests.size();
|
|
|
|
LOGGER.warning("The following " + failedTests + " tests failed!");
|
2015-03-18 09:52:33 +01:00
|
|
|
for (FailedTest failedTest : testRunResult.failedIntegrationTests) {
|
|
|
|
final Method method = failedTest.testMethod;
|
|
|
|
final String className = method.getDeclaringClass().getName();
|
|
|
|
final String methodName = method.getName();
|
2015-06-09 17:24:17 +02:00
|
|
|
final Throwable cause = failedTest.failureReason;
|
2015-03-18 09:52:33 +01:00
|
|
|
LOGGER.severe(className + CLASS_METHOD_SEP + methodName + " failed: " + cause);
|
|
|
|
}
|
2017-07-28 11:59:11 +02:00
|
|
|
exitStatus = 2;
|
2016-12-19 14:35:09 +01:00
|
|
|
} else {
|
|
|
|
LOGGER.info("All possible Smack Integration Tests completed successfully. \\o/");
|
2017-07-28 11:59:11 +02:00
|
|
|
exitStatus = 0;
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
2017-07-28 11:59:11 +02:00
|
|
|
|
|
|
|
switch (config.debugger) {
|
|
|
|
case enhanced:
|
|
|
|
EnhancedDebuggerWindow.getInstance().waitUntilClosed();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
System.exit(exitStatus);
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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");
|
2017-07-28 11:59:11 +02:00
|
|
|
if (config.debugger != Configuration.Debugger.none) {
|
2015-03-18 09:52:33 +01:00
|
|
|
// 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) {
|
2017-01-12 20:57:19 +01:00
|
|
|
SmackConfiguration.setDefaultReplyTimeout(config.replyTimeout);
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
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()]);
|
|
|
|
}
|
2015-04-24 21:29:50 +02:00
|
|
|
Reflections reflections = new Reflections(testPackages, new SubTypesScanner(),
|
2015-03-18 09:52:33 +01:00
|
|
|
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);
|
2016-07-20 20:57:04 +02:00
|
|
|
disconnectAndMaybeDelete(environment.conThree);
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return testRunResult;
|
|
|
|
}
|
|
|
|
|
2015-09-13 18:12:33 +02:00
|
|
|
@SuppressWarnings({"unchecked", "Finally"})
|
2015-03-18 09:52:33 +01:00
|
|
|
private void runTests(Set<Class<? extends AbstractSmackIntTest>> classes)
|
2016-07-20 20:57:04 +02:00
|
|
|
throws NoResponseException, InterruptedException {
|
2015-03-18 09:52:33 +01:00
|
|
|
for (Class<? extends AbstractSmackIntTest> testClass : classes) {
|
|
|
|
final String testClassName = testClass.getName();
|
|
|
|
|
2016-07-20 20:57:04 +02:00
|
|
|
if (config.enabledTests != null && !isInSet(testClass, config.enabledTests)) {
|
2015-03-18 09:52:33 +01:00
|
|
|
LOGGER.info("Skipping test class " + testClassName + " because it is not enabled");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-07-20 20:57:04 +02:00
|
|
|
if (isInSet(testClass, config.disabledTests)) {
|
2015-03-18 09:52:33 +01:00
|
|
|
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();
|
2016-07-20 20:57:04 +02:00
|
|
|
if (config.enabledTests != null && !(config.enabledTests.contains(methodName)
|
|
|
|
|| isInSet(testClass, config.enabledTests))) {
|
2015-03-18 09:52:33 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-12-19 14:35:09 +01:00
|
|
|
final int detectedTestMethodsCount = smackIntegrationTestMethods.size();
|
|
|
|
testRunResult.numberOfAvailableTests.addAndGet(detectedTestMethodsCount);
|
|
|
|
testRunResult.numberOfPossibleTests.addAndGet(detectedTestMethodsCount);
|
2015-03-18 09:52:33 +01:00
|
|
|
|
|
|
|
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());
|
2016-12-19 14:35:09 +01:00
|
|
|
testRunResult.numberOfPossibleTests.addAndGet(-detectedTestMethodsCount);
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
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(
|
2016-12-19 18:10:04 +01:00
|
|
|
SmackIntegrationTestEnvironment.class);
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
catch (NoSuchMethodException | SecurityException e) {
|
|
|
|
LOGGER.log(Level.WARNING,
|
|
|
|
"Smack Integration Test class could not get constructed (public Con)structor(SmackIntegrationTestEnvironment) missing?)",
|
|
|
|
e);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2016-12-19 18:10:04 +01:00
|
|
|
test = cons.newInstance(environment);
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
catch (InvocationTargetException e) {
|
|
|
|
Throwable cause = e.getCause();
|
|
|
|
if (cause instanceof TestNotPossibleException) {
|
|
|
|
testRunResult.impossibleTestClasses.put(testClass, cause.getMessage());
|
2016-12-19 14:35:09 +01:00
|
|
|
testRunResult.numberOfPossibleTests.addAndGet(-detectedTestMethodsCount);
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
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
|
2016-12-23 13:30:30 +01:00
|
|
|
));
|
2015-03-18 09:52:33 +01:00
|
|
|
|
|
|
|
// 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()) {
|
2015-04-04 17:38:00 +02:00
|
|
|
throw new IllegalArgumentException("@BeforeClass methods with wrong signature found");
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (beforeClassMethods.size() == 1) {
|
|
|
|
Method beforeClassMethod = beforeClassMethods.iterator().next();
|
2016-12-23 13:27:51 +01:00
|
|
|
LOGGER.info("Executing @BeforeClass method of " + testClass);
|
2015-03-18 09:52:33 +01:00
|
|
|
try {
|
2016-12-23 13:30:30 +01:00
|
|
|
beforeClassMethod.invoke(test);
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
catch (InvocationTargetException | IllegalAccessException e) {
|
2016-12-23 13:26:57 +01:00
|
|
|
LOGGER.log(Level.SEVERE, "Exception executing @BeforeClass method", e);
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
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() + '.'
|
2015-06-10 08:44:47 +02:00
|
|
|
+ testMethod.getName() + " (" + testType + "): ";
|
2015-03-18 09:52:33 +01:00
|
|
|
// 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;
|
|
|
|
}
|
2015-06-09 17:24:17 +02:00
|
|
|
Throwable nonFatalFailureReason;
|
|
|
|
// junit assert's throw an AssertionError if they fail, those should not be
|
|
|
|
// thrown up, as it would be done by throwFatalException()
|
|
|
|
if (cause instanceof AssertionError) {
|
|
|
|
nonFatalFailureReason = cause;
|
|
|
|
} else {
|
|
|
|
nonFatalFailureReason = throwFatalException(cause);
|
|
|
|
}
|
2015-03-18 09:52:33 +01:00
|
|
|
// An integration test failed
|
|
|
|
testRunResult.failedIntegrationTests.add(new FailedTest(testMethod, testStart, testEnd, null,
|
2015-06-09 17:24:17 +02:00
|
|
|
nonFatalFailureReason));
|
2015-03-18 09:52:33 +01:00
|
|
|
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
|
2016-12-23 13:30:30 +01:00
|
|
|
));
|
2015-03-18 09:52:33 +01:00
|
|
|
|
|
|
|
// See if there are any methods that have the @AfterClassAnnotation but a wrong signature
|
2015-04-04 09:32:57 +02:00
|
|
|
Set<Method> allAfterClassMethods = getAllMethods(testClass, withAnnotation(AfterClass.class));
|
2015-03-18 09:52:33 +01:00
|
|
|
allAfterClassMethods.removeAll(afterClassMethods);
|
|
|
|
if (!allAfterClassMethods.isEmpty()) {
|
2015-04-04 17:38:00 +02:00
|
|
|
throw new IllegalArgumentException("@AfterClass methods with wrong signature found");
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (afterClassMethods.size() == 1) {
|
|
|
|
Method afterClassMethod = afterClassMethods.iterator().next();
|
2016-12-23 13:27:51 +01:00
|
|
|
LOGGER.info("Executing @AfterClass method of " + testClass);
|
2015-03-18 09:52:33 +01:00
|
|
|
try {
|
2016-12-23 13:30:30 +01:00
|
|
|
afterClassMethod.invoke(test);
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-24 21:26:07 +02:00
|
|
|
private void invokeLowLevel(Method testMethod, AbstractSmackIntTest test) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InterruptedException {
|
2015-03-18 09:52:33 +01:00
|
|
|
// We have checked before that every parameter, if any, is of type XMPPTCPConnection
|
|
|
|
final int numberOfConnections = testMethod.getParameterTypes().length;
|
|
|
|
XMPPTCPConnection[] connections = null;
|
|
|
|
try {
|
2016-11-27 21:14:44 +01:00
|
|
|
if (numberOfConnections > 0 && !config.isAccountRegistrationPossible()) {
|
2015-03-18 09:52:33 +01:00
|
|
|
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) {
|
2016-12-19 18:10:04 +01:00
|
|
|
connections[i] = getConnectedConnection(environment, i);
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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) {
|
2016-11-27 21:14:44 +01:00
|
|
|
IntTestUtil.disconnectAndMaybeDelete(connections[i], config);
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-24 21:26:07 +02:00
|
|
|
protected void disconnectAndMaybeDelete(XMPPTCPConnection connection) throws InterruptedException {
|
2016-11-27 21:14:44 +01:00
|
|
|
IntTestUtil.disconnectAndMaybeDelete(connection, config);
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
protected SmackIntegrationTestEnvironment prepareEnvironment() throws SmackException,
|
|
|
|
IOException, XMPPException, InterruptedException, KeyManagementException,
|
|
|
|
NoSuchAlgorithmException {
|
|
|
|
XMPPTCPConnection conOne = null;
|
|
|
|
XMPPTCPConnection conTwo = null;
|
2016-07-20 20:57:04 +02:00
|
|
|
XMPPTCPConnection conThree = null;
|
2015-03-18 09:52:33 +01:00
|
|
|
try {
|
|
|
|
conOne = getConnectedConnectionFor(AccountNum.One);
|
|
|
|
conTwo = getConnectedConnectionFor(AccountNum.Two);
|
2016-07-20 20:57:04 +02:00
|
|
|
conThree = getConnectedConnectionFor(AccountNum.Three);
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
catch (Exception e) {
|
2016-07-20 20:57:04 +02:00
|
|
|
// TODO Reverse the order, i.e. conThree should be disconnected first.
|
2015-03-18 09:52:33 +01:00
|
|
|
if (conOne != null) {
|
|
|
|
conOne.disconnect();
|
|
|
|
}
|
|
|
|
if (conTwo != null) {
|
|
|
|
conTwo.disconnect();
|
|
|
|
}
|
2016-07-20 20:57:04 +02:00
|
|
|
if (conThree != null) {
|
|
|
|
conThree.disconnect();
|
|
|
|
}
|
2015-03-18 09:52:33 +01:00
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
|
2016-07-20 20:57:04 +02:00
|
|
|
return new SmackIntegrationTestEnvironment(conOne, conTwo, conThree, testRunResult.testRunId, config);
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
enum AccountNum {
|
|
|
|
One,
|
|
|
|
Two,
|
2016-07-20 20:57:04 +02:00
|
|
|
Three,
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2016-07-20 20:57:04 +02:00
|
|
|
case Three:
|
|
|
|
accountUsername = config.accountThreeUsername;
|
|
|
|
accountPassword = config.accountThreePassword;
|
|
|
|
middlefix = "three";
|
|
|
|
break;
|
2015-03-18 09:52:33 +01:00
|
|
|
default:
|
|
|
|
throw new IllegalStateException();
|
|
|
|
}
|
|
|
|
if (StringUtils.isNullOrEmpty(accountUsername)) {
|
2017-05-23 16:45:04 +02:00
|
|
|
accountUsername = USERNAME_PREFIX + '-' + middlefix + '-' + testRunResult.testRunId;
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
if (StringUtils.isNullOrEmpty(accountPassword)) {
|
2016-01-06 14:56:49 +01:00
|
|
|
accountPassword = StringUtils.insecureRandomString(16);
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
2018-05-09 18:10:11 +02:00
|
|
|
|
|
|
|
Builder builder = getConnectionConfigurationBuilder(config);
|
|
|
|
builder.setUsernameAndPassword(accountUsername, accountPassword)
|
|
|
|
.setResource(middlefix + '-' + testRunResult.testRunId);
|
|
|
|
|
2015-03-18 09:52:33 +01:00
|
|
|
XMPPTCPConnection connection = new XMPPTCPConnection(builder.build());
|
|
|
|
connection.connect();
|
2016-11-27 21:14:44 +01:00
|
|
|
if (config.isAccountRegistrationPossible()) {
|
2016-12-19 14:13:53 +01:00
|
|
|
UsernameAndPassword uap = IntTestUtil.registerAccount(connection, accountUsername, accountPassword, config);
|
2015-03-18 09:52:33 +01:00
|
|
|
|
|
|
|
// 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();
|
|
|
|
|
2016-12-19 14:13:53 +01:00
|
|
|
connection.login(uap.username, uap.password);
|
|
|
|
} else {
|
|
|
|
connection.login();
|
|
|
|
}
|
2015-03-18 09:52:33 +01:00
|
|
|
|
|
|
|
return connection;
|
|
|
|
}
|
|
|
|
|
2018-05-09 18:10:11 +02:00
|
|
|
static XMPPTCPConnectionConfiguration.Builder getConnectionConfigurationBuilder(Configuration config) {
|
2015-03-18 09:52:33 +01:00
|
|
|
XMPPTCPConnectionConfiguration.Builder builder = XMPPTCPConnectionConfiguration.builder();
|
2017-03-09 21:35:29 +01:00
|
|
|
if (config.tlsContext != null) {
|
|
|
|
builder.setCustomSSLContext(config.tlsContext);
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
builder.setSecurityMode(config.securityMode);
|
2015-06-08 14:39:05 +02:00
|
|
|
builder.setXmppDomain(config.service);
|
2017-07-28 11:59:11 +02:00
|
|
|
|
|
|
|
switch (config.debugger) {
|
|
|
|
case enhanced:
|
|
|
|
builder.setDebuggerFactory(EnhancedDebugger.Factory.INSTANCE);
|
|
|
|
break;
|
|
|
|
case console:
|
|
|
|
builder.setDebuggerFactory(ConsoleDebugger.Factory.INSTANCE);
|
|
|
|
break;
|
|
|
|
case none:
|
|
|
|
// Nothing to do :).
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-05-09 18:10:11 +02:00
|
|
|
return builder;
|
|
|
|
}
|
|
|
|
|
|
|
|
static XMPPTCPConnection getConnectedConnection(SmackIntegrationTestEnvironment environment, int connectionId)
|
|
|
|
throws KeyManagementException, NoSuchAlgorithmException, InterruptedException,
|
|
|
|
SmackException, IOException, XMPPException {
|
|
|
|
Configuration config = environment.configuration;
|
|
|
|
XMPPTCPConnectionConfiguration.Builder builder = getConnectionConfigurationBuilder(config);
|
|
|
|
|
2015-03-18 09:52:33 +01:00
|
|
|
XMPPTCPConnection connection = new XMPPTCPConnection(builder.build());
|
|
|
|
connection.connect();
|
2016-12-19 18:10:04 +01:00
|
|
|
UsernameAndPassword uap = IntTestUtil.registerAccount(connection, environment, connectionId);
|
2015-03-18 09:52:33 +01:00
|
|
|
connection.login(uap.username, uap.password);
|
|
|
|
return connection;
|
|
|
|
}
|
|
|
|
|
2015-06-09 21:43:26 +02:00
|
|
|
private static Exception throwFatalException(Throwable e) throws Error, NoResponseException,
|
2015-03-18 09:52:33 +01:00
|
|
|
InterruptedException {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-07-20 20:57:04 +02:00
|
|
|
private static boolean isInSet(Class<?> clz, Set<String> classes) {
|
|
|
|
if (classes == null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
final String className = clz.getName();
|
|
|
|
final String unqualifiedClassName = clz.getSimpleName();
|
|
|
|
return (classes.contains(className) || classes.contains(unqualifiedClassName));
|
|
|
|
}
|
|
|
|
|
2015-04-06 10:45:12 +02:00
|
|
|
public static final class TestRunResult {
|
2016-06-29 08:08:13 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A short String of lowercase characters and numbers used to identify a integration test
|
|
|
|
* run. We use lowercase characters because this string will eventually be part of the
|
|
|
|
* localpart of the used JIDs (and the localpart is case insensitive).
|
|
|
|
*/
|
|
|
|
public final String testRunId = StringUtils.insecureRandomString(5).toLowerCase(Locale.US);
|
|
|
|
|
2015-03-18 09:52:33 +01:00
|
|
|
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<>();
|
2016-12-19 14:35:09 +01:00
|
|
|
private final AtomicInteger numberOfAvailableTests = new AtomicInteger();
|
|
|
|
private final AtomicInteger numberOfPossibleTests = new AtomicInteger();
|
2015-03-18 09:52:33 +01:00
|
|
|
|
|
|
|
private TestRunResult() {
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getTestRunId() {
|
|
|
|
return testRunId;
|
|
|
|
}
|
|
|
|
|
2016-12-19 14:35:09 +01:00
|
|
|
public int getNumberOfAvailableTests() {
|
|
|
|
return numberOfAvailableTests.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getNumberOfPossibleTests() {
|
|
|
|
return numberOfPossibleTests.get();
|
2015-03-18 09:52:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|