blob: 98ad30b88ef40e4c29ab74e3c07c65d8ce0ebde4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.camel.itest.springboot.command;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
import org.apache.camel.CamelContext;
import org.apache.camel.itest.springboot.Command;
import org.apache.camel.itest.springboot.ITestConfig;
import org.junit.Assert;
import org.junit.runner.Description;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.RegexPatternTypeFilter;
import org.springframework.stereotype.Component;
/**
* A command that executes all unit tests contained in the module.
*/
@Component("unittest")
public class UnitTestCommand extends AbstractTestCommand implements Command {
Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private CamelContext context;
@Override
public UnitTestResult executeTest(final ITestConfig config, String component) throws Exception {
logger.info("Spring-Boot test configuration {}", config);
Pattern pattern = Pattern.compile(config.getUnitTestInclusionPattern());
logger.info("Scaning the classpath for test classes");
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new RegexPatternTypeFilter(pattern));
Set<BeanDefinition> defs = scanner.findCandidateComponents(config.getUnitTestBasePackage());
List<String> testClasses = new LinkedList<>();
for (BeanDefinition bd : defs) {
testClasses.add(bd.getBeanClassName());
}
if (config.getUnitTestExclusionPattern() != null) {
Pattern exclusionPattern = Pattern.compile(config.getUnitTestExclusionPattern());
for (Iterator<String> it = testClasses.iterator(); it.hasNext();) {
String cn = it.next();
if (exclusionPattern.matcher(cn).matches()) {
logger.warn("Excluding Test Class: {}", cn);
it.remove();
}
}
}
final List<Class<?>> classes = new ArrayList<>();
for (String cn : testClasses) {
try {
Class<?> clazz = Class.forName(cn);
if (isAdmissible(clazz)) {
logger.info("Found admissible test class: {}", cn);
classes.add(clazz);
}
} catch (Throwable t) {
logger.warn("Test class {} has thrown an exception during initialization", cn);
logger.debug("Exception for test cass " + cn + " is:", t);
}
}
logger.info("Run JUnit tests on {} test classes", classes.size());
JUnitCore runner = new JUnitCore();
runner.addListener(new RunListener() {
@Override
public void testStarted(Description description) throws Exception {
disableJmx(config.getJmxDisabledNames());
}
});
Result result = runner.run(classes.toArray(new Class[]{}));
logger.info(config.getModuleName() + " unit tests. "
+ "Success: " + result.wasSuccessful() + " - Test Run: " + result.getRunCount() + " - Failures: " + result.getFailureCount()
+ " - Ignored Tests: " + result.getIgnoreCount());
for (Failure f : result.getFailures()) {
logger.warn("Failed test description: {}", f.getDescription());
logger.warn("Message: {}", f.getMessage());
if (f.getException() != null) {
logger.warn("Exception thrown from test", f.getException());
}
}
if (!result.wasSuccessful()) {
Assert.fail("Some unit tests failed (" + result.getFailureCount() + "/" + result.getRunCount() + "), check the logs for more details");
}
if (result.getRunCount() == 0 && config.getUnitTestsExpectedNumber() == null) {
Assert.fail("No tests have been found");
}
Integer expectedTests = config.getUnitTestsExpectedNumber();
if (expectedTests != null && expectedTests != result.getRunCount()) {
Assert.fail("Wrong number of tests: expected " + expectedTests + " found " + result.getRunCount());
}
return new UnitTestResult(result);
}
private void disableJmx(Set<String> disabledJmx) throws Exception {
logger.info("Disabling JMX names: {}", disabledJmx);
for (MBeanServer server : getMBeanServers()) {
for (String jmxName : disabledJmx) {
logger.info("Disabling JMX query {}", jmxName);
ObjectName oName = new ObjectName(jmxName);
Set<ObjectName> names = new HashSet<>(server.queryNames(oName, null));
for (ObjectName name : names) {
logger.info("Disabled JMX name {}", name);
server.unregisterMBean(name);
}
}
}
}
private List<MBeanServer> getMBeanServers() {
List<MBeanServer> servers = MBeanServerFactory.findMBeanServer(null);
if (servers == null) {
servers = Collections.emptyList();
}
return servers;
}
private boolean isAdmissible(Class<?> testClass) {
if (testClass.getPackage().getName().startsWith("org.apache.camel.itest.springboot")) {
// no tests from the integration test suite
return false;
}
URL location = testClass.getResource("/" + testClass.getName().replace(".", "/") + ".class");
if (location != null) {
int firstLevel = location.toString().indexOf("!/");
int lastLevel = location.toString().lastIndexOf("!/");
if (firstLevel >= 0 && lastLevel >= 0 && firstLevel != lastLevel) {
// test class is in a nested jar, skipping
return false;
}
}
return true;
}
}