package org.springframework.roo.addon.test;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentContext;
import org.springframework.roo.addon.test.providers.TestCreatorProvider;
import org.springframework.roo.classpath.TypeLocationService;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.project.Dependency;
import org.springframework.roo.project.DependencyScope;
import org.springframework.roo.project.DependencyType;
import org.springframework.roo.project.Plugin;
import org.springframework.roo.project.ProjectOperations;
import org.springframework.roo.project.maven.Pom;
import org.springframework.roo.support.logging.HandlerUtils;
import org.springframework.roo.support.util.XmlUtils;
import org.w3c.dom.Element;
/**
* Provides convenience methods that can be used to create unit and integration tests.
*
* @author Ben Alex
* @author Sergio Clares
* @since 1.0
*/
@Component
@Service
public class TestOperationsImpl implements TestOperations {
protected final static Logger LOGGER = HandlerUtils.getLogger(TestCommands.class);
private static final Dependency JUNIT_DEPENDENCY = new Dependency("junit", "junit", null,
DependencyType.JAR, DependencyScope.TEST);
private static final Dependency ASSERTJ_CORE_DEPENDENCY = new Dependency("org.assertj",
"assertj-core", null, DependencyType.JAR, DependencyScope.TEST);
private static final Dependency SPRING_TEST_DEPENDENCY = new Dependency("org.springframework",
"spring-test", null, DependencyType.JAR, DependencyScope.TEST);
private static final Plugin MAVEN_SUREFIRE_PLUGIN = new Plugin("org.apache.maven.plugins",
"maven-surefire-plugin", null);
private static final Dependency SPRING_BOOT_TEST_DEPENDENCY = new Dependency(
"org.springframework.boot", "spring-boot-test", null, DependencyType.JAR,
DependencyScope.TEST);
private BundleContext context;
@Reference
private TypeLocationService typeLocationService;
@Reference
private ProjectOperations projectOperations;
// TestCreatorProvider implementations
private List<TestCreatorProvider> testCreators = new ArrayList<TestCreatorProvider>();
protected void activate(final ComponentContext cContext) {
this.context = cContext.getBundleContext();
}
protected void deactivate(final ComponentContext context) {
this.context = null;
}
@Override
public void createUnitTest(JavaType type) {
// Check if specified type exists in the project
String physicalTypeIdentifier = typeLocationService.getPhysicalTypeIdentifier(type);
if (physicalTypeIdentifier == null) {
throw new IllegalArgumentException(String.format(
"The class '%s' doesn't exists in the project. Please, specify an existing class", type));
}
// Adding unit test dependencies
List<TestCreatorProvider> validTestCreators = getValidTestCreatorsForType(type);
if (!validTestCreators.isEmpty()) {
addUnitTestDependencies(type.getModule());
}
// Creating tests
if (validTestCreators.isEmpty()) {
throw new IllegalArgumentException(
"Unable to find a valid test creator for this type of class. "
+ "Please, select another type of class to generate the test, such as an entity.");
} else {
for (TestCreatorProvider creator : validTestCreators) {
creator.createUnitTest(type);
}
}
}
@Override
public void createIntegrationTest(JavaType type, Pom module) {
// Check if specified type exists in the project
String physicalTypeIdentifier = typeLocationService.getPhysicalTypeIdentifier(type);
if (physicalTypeIdentifier == null) {
throw new IllegalArgumentException(String.format(
"The class '%s' doesn't exists in the project. Please, specify an existing class", type));
}
// Adding integration test dependencies
List<TestCreatorProvider> validTestCreators = getValidTestCreatorsForType(type);
if (!validTestCreators.isEmpty()) {
addIntegrationTestDependencies(module.getModuleName());
}
// Creating tests
if (validTestCreators.isEmpty()) {
throw new IllegalArgumentException(
"Unable to find a valid test creator for this type of class. "
+ "Please, select another type of class to generate the test, such a repository.");
} else {
for (TestCreatorProvider creator : validTestCreators) {
creator.createIntegrationTest(type, module);
}
}
}
/**
* Add needed dependencies and plugins to run created integration tests.
*
* @param module {@link String} the module name where add dependencies.
*/
private void addIntegrationTestDependencies(String moduleName) {
// Add dependencies if needed
projectOperations.addDependency(moduleName, JUNIT_DEPENDENCY);
projectOperations.addDependency(moduleName, ASSERTJ_CORE_DEPENDENCY);
projectOperations.addDependency(moduleName, SPRING_TEST_DEPENDENCY);
projectOperations.addDependency(moduleName, SPRING_BOOT_TEST_DEPENDENCY);
// Add plugin maven-failsafe-plugin
Pom module = projectOperations.getPomFromModuleName(moduleName);
// Stop if the plugin is already installed
for (final Plugin plugin : module.getBuildPlugins()) {
if (plugin.getArtifactId().equals("maven-failsafe-plugin")) {
return;
}
}
final Element configuration = XmlUtils.getConfiguration(getClass());
final Element plugin = XmlUtils.findFirstElement("/configuration/plugin", configuration);
// Now install the plugin itself
if (plugin != null) {
projectOperations.addBuildPlugin(moduleName, new Plugin(plugin));
}
}
/**
* Add needed dependencies and plugins to run created unit tests.
*
* @param module {@link String} the module name where add dependencies.
*/
private void addUnitTestDependencies(String module) {
// Add dependencies if needed
projectOperations.addDependency(module, JUNIT_DEPENDENCY);
projectOperations.addDependency(module, ASSERTJ_CORE_DEPENDENCY);
projectOperations.addDependency(module, SPRING_TEST_DEPENDENCY);
// Add plugins if needed
projectOperations.addBuildPlugin(module, MAVEN_SUREFIRE_PLUGIN);
}
/**
* Gets all the valid implementations of TestCreatorProvider for a JavaType.
*
* @param type the JavaType to get the valid implementations.
* @return a `List` with the {@link TestCreatorProvider} valid
* implementations. Never `null`.
*/
private List<TestCreatorProvider> getValidTestCreatorsForType(JavaType type) {
// Get all Services implement TestCreatorProvider interface
if (this.testCreators.isEmpty()) {
try {
ServiceReference<?>[] references =
this.context.getAllServiceReferences(TestCreatorProvider.class.getName(), null);
for (ServiceReference<?> ref : references) {
TestCreatorProvider testCreatorProvider =
(TestCreatorProvider) this.context.getService(ref);
this.testCreators.add(testCreatorProvider);
}
} catch (InvalidSyntaxException e) {
LOGGER.warning("Cannot load TestCreatorProvider on TestOperationsImpl.");
return null;
}
}
List<TestCreatorProvider> validTestCreators = new ArrayList<TestCreatorProvider>();
for (TestCreatorProvider provider : this.testCreators) {
if (provider.isValid(type)) {
validTestCreators.add(provider);
}
}
return validTestCreators;
}
}