/* * Copyright 2013 Red Hat, Inc. and/or its affiliates. * * Licensed under the Eclipse Public License version 1.0, available at * http://www.eclipse.org/legal/epl-v10.html */ package org.jboss.forge.arquillian; import static org.jboss.forge.arquillian.commandcompleter.TestFrameworkCompleter.OPTION_TEST_FRAMEWORK; import static org.jboss.forge.arquillian.commandcompleter.ContainerCommandCompleter.*; import org.apache.maven.model.Profile; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.Velocity; import org.jboss.forge.arquillian.commandcompleter.ContainerCommandCompleter; import org.jboss.forge.arquillian.commandcompleter.ProfileCommandCompleter; import org.jboss.forge.arquillian.commandcompleter.TestFrameworkCompleter; import org.jboss.forge.arquillian.container.Configuration; import org.jboss.forge.arquillian.container.Container; import org.jboss.forge.arquillian.container.ContainerDirectoryParser; import org.jboss.forge.arquillian.container.ContainerType; import org.jboss.forge.arquillian.testframework.ProvidesFacetForQualifier; import org.jboss.forge.arquillian.testframework.TestFrameworkFacet; import org.jboss.forge.arquillian.testframework.TestFrameworkFacetInstaller; import org.jboss.forge.maven.MavenCoreFacet; import org.jboss.forge.parser.JavaParser; import org.jboss.forge.parser.java.JavaClass; import org.jboss.forge.parser.java.JavaSource; import org.jboss.forge.parser.xml.Node; import org.jboss.forge.parser.xml.XMLParser; import org.jboss.forge.project.Project; import org.jboss.forge.project.dependencies.Dependency; import org.jboss.forge.project.dependencies.DependencyBuilder; import org.jboss.forge.project.dependencies.ScopeType; import org.jboss.forge.project.facets.DependencyFacet; import org.jboss.forge.project.facets.JavaExecutionFacet; import org.jboss.forge.project.facets.JavaSourceFacet; import org.jboss.forge.project.facets.ResourceFacet; import org.jboss.forge.project.facets.events.InstallFacets; import org.jboss.forge.resources.FileResource; import org.jboss.forge.resources.java.JavaResource; import org.jboss.forge.shell.PromptType; import org.jboss.forge.shell.Shell; import org.jboss.forge.shell.events.PickupResource; import org.jboss.forge.shell.plugins.*; import javax.enterprise.event.Event; import javax.enterprise.inject.Any; import javax.enterprise.inject.Instance; import javax.enterprise.inject.spi.BeanManager; import javax.inject.Inject; import java.io.FileNotFoundException; import java.io.IOException; import java.io.StringWriter; import java.util.List; import java.util.Properties; @Alias("arquillian") @RequiresProject @RequiresFacet({ DependencyFacet.class, JavaSourceFacet.class }) @Help("This plugin will help you setting up Arquillian tests.") public class ArquillianPlugin implements Plugin { static { Properties properties = new Properties(); properties.setProperty("resource.loader", "class"); properties.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); Velocity.init(properties); } public static final String ARQ_CORE_VERSION_PROP_NAME = "version.arquillian_core"; public static final String ARQ_CORE_VERSION_PROP = "${" + ARQ_CORE_VERSION_PROP_NAME + "}"; private String arquillianVersion; private DependencyFacet dependencyFacet; @Inject private Project project; @Inject private BeanManager beanManager; @Inject private Event<PickupResource> pickup; @Inject @Current private JavaResource resource; @Inject private Shell shell; @Inject private ContainerInstaller containerInstaller; @Inject private ContainerDirectoryParser containerDirectoryParser; @Inject @Any private Event<ContainerInstallEvent> installEvent; @Inject private Event<InstallFacets> installFacetsEvent; @Inject @Any private Instance<TestFrameworkFacetInstaller> testFrameworkFacetInstallers; @SetupCommand public void installContainer( @Option(name = OPTION_CONTAINER_NAME, required = true, completer = ContainerCommandCompleter.class) String containerName, @Option(name = OPTION_CONTAINER_TYPE, required = false) ContainerType containerType, @Option(name = OPTION_TEST_FRAMEWORK, required = false, completer = TestFrameworkCompleter.class, defaultValue = "junit") String selectedTestFramework) { String containerId = Container.idForDisplayName(containerName); this.dependencyFacet = project.getFacet(DependencyFacet.class); installArquillianBom(); installTestFramework(selectedTestFramework); installContainer(containerId); configureSelectedContainer(containerId); } private void installContainer(String containerId) { List<Container> containers; try { containers = containerDirectoryParser.getContainers(); } catch (IOException e) { throw new RuntimeException(e); } boolean foundContainer = false; for (Container container : containers) { if (container.getId().equals(containerId)) { containerInstaller.installContainer(container); installEvent.fire(new ContainerInstallEvent(container)); foundContainer = true; break; } } if (!foundContainer) { throw new RuntimeException("Container not recognized"); } } private void configureSelectedContainer(String containerId) { ResourceFacet resources = project.getFacet(ResourceFacet.class); FileResource<?> resource = (FileResource<?>) resources.getTestResourceFolder().getChild("arquillian.xml"); Node arquillianConfig = null; if (!resource.exists()) { arquillianConfig = createNewArquillianConfig(); } else { arquillianConfig = XMLParser.parse(resource.getResourceInputStream()); } // Make sure a container config exists for this container (otherwise activating it will fail) Node containerConfig = arquillianConfig.getSingle("container@qualifier=" + containerId); if (containerConfig == null) { arquillianConfig.createChild("container@qualifier=" + containerId); resource.setContents(XMLParser.toXMLString(arquillianConfig)); } } @Command(value = "configure-container") public void configureContainer(@Option(name = "profile", required = true, completer = ProfileCommandCompleter.class) String profileId) { // loop, user presses ctrl-c to exit while (true) { Profile profile = getProfile(profileId); Container container; try { container = getContainer(profile); } catch (IOException e) { throw new RuntimeException(e); } // TODO: show current values in options list Configuration configuration = shell.promptChoiceTyped( "Which property do you want to set? (default values shown)\n(Press Enter to return to shell)", container.getConfigurations(), null); if (configuration == null) { break; } ResourceFacet resources = project.getFacet(ResourceFacet.class); FileResource<?> resource = (FileResource<?>) resources.getTestResourceFolder().getChild("arquillian.xml"); Node xml = null; if (!resource.exists()) { xml = createNewArquillianConfig(); } else { xml = XMLParser.parse(resource.getResourceInputStream()); } // TODO show current value String value = shell.prompt("What value do you want to assign to the " + configuration.getName() + " property?"); addPropertyToArquillianConfig(xml, container.getId(), configuration.getName(), value); resource.setContents(XMLParser.toXMLString(xml)); } } private Container getContainer(Profile profile) throws IOException { String profileId = profile.getId().replaceFirst("^arq-", "arquillian-"); for (Container container : containerDirectoryParser.getContainers()) { if (container.getProfileId().equals(profileId)) { return container; } } throw new RuntimeException("Container not found for profile " + profile); } private Profile getProfile(String profile) { MavenCoreFacet mavenCoreFacet = project.getFacet(MavenCoreFacet.class); List<Profile> profileList = mavenCoreFacet.getPOM().getProfiles(); for (Profile p : profileList) { if (p.getId().equals(profile)) { return p; } } throw new RuntimeException("Profile " + profile + " could not be found"); } private Node createNewArquillianConfig() { return XMLParser .parse("<arquillian xmlns=\"http://jboss.org/schema/arquillian\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" + " xsi:schemaLocation=\"http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd\"></arquillian>"); } private void addPropertyToArquillianConfig(Node xml, String container, String key, String value) { xml.getOrCreate("container@qualifier=" + container) .getOrCreate("configuration") .getOrCreate("property@name=" + key) .text(value); } @Command(value = "create-test", help = "Create a new test class with a default @Deployment method") public void createTest( @Option(name = "class", required = true, type = PromptType.JAVA_CLASS) JavaResource classUnderTest, @Option(name = "enableJPA", required = false, flagOnly = true) boolean enableJPA, final PipeOut out) throws FileNotFoundException { final TestFrameworkFacet testFrameworkFacet = project.getFacet(TestFrameworkFacet.class); final JavaSourceFacet java = project.getFacet(JavaSourceFacet.class); final JavaSource<?> javaSource = classUnderTest.getJavaSource(); final VelocityContext context = initializeVelocityContext(enableJPA, javaSource); final StringWriter writer = new StringWriter(); Velocity.mergeTemplate(testFrameworkFacet.getTemplateName(), "UTF-8", context, writer); final JavaClass testClass = JavaParser.parse(JavaClass.class, writer.toString()); java.saveTestJavaSource(testClass); pickup.fire(new PickupResource(java.getTestJavaResource(testClass))); } private VelocityContext initializeVelocityContext(boolean enableJPA, JavaSource<?> javaSource) { VelocityContext context = new VelocityContext(); context.put("package", javaSource.getPackage()); context.put("ClassToTest", javaSource.getName()); context.put("classToTest", javaSource.getName().toLowerCase()); context.put("packageImport", javaSource.getPackage()); context.put("enableJPA", enableJPA); return context; } /** * This command exports an Archive generated by a @Deployment method to disk. Because the project's classpath is not * in the classpath of Forge, the @Deployment method can't be called directly.The plugin works in the following * steps: 1 - Generate a new class to the src/test/java folder 2 - Compile the user's classes using mvn test-compile * 3 - Run the generated class using mvn exec:java (so that the project's classes are on the classpath) 4 - Delete * the generated class */ @Command(value = "export", help = "Export a @Deployment configuration to a zip file on disk.") @RequiresResource(JavaResource.class) public void exportDeployment(@Option(name = "keepExporter", flagOnly = true) boolean keepExporter, PipeOut out) { final JavaSourceFacet javaSourceFacet = project.getFacet(JavaSourceFacet.class); try { JavaResource testJavaResource = javaSourceFacet.getTestJavaResource("forge/arquillian/DeploymentExporter.java"); if (!testJavaResource.exists()) { generateExporterClass(javaSourceFacet); } runExporterClass(out); if (!keepExporter) { testJavaResource.delete(); } } catch (Exception ex) { throw new RuntimeException("Error while calling generated DeploymentExporter ", ex); } } private void runExporterClass(PipeOut out) throws IOException { JavaExecutionFacet facet = project.getFacet(JavaExecutionFacet.class); facet.executeProjectClass("forge.arquillian.DeploymentExporter", resource.getJavaSource().getQualifiedName()); } private void generateExporterClass(JavaSourceFacet java) throws FileNotFoundException { VelocityContext context = new VelocityContext(); StringWriter writer = new StringWriter(); Velocity.mergeTemplate("DeploymentExporter.vtl", "UTF-8", context, writer); JavaClass deploymentExporter = JavaParser.parse(JavaClass.class, writer.toString()); java.saveTestJavaSource(deploymentExporter); java.saveTestJavaSource(deploymentExporter); } private void installArquillianBom() { DependencyBuilder arquillianBom = DependencyBuilder.create().setGroupId("org.jboss.arquillian") .setArtifactId("arquillian-bom").setPackagingType("pom").setScopeType(ScopeType.IMPORT); arquillianVersion = dependencyFacet.getProperty(ARQ_CORE_VERSION_PROP_NAME); if (arquillianVersion == null) { List<Dependency> dependencies = dependencyFacet.resolveAvailableVersions(arquillianBom); Dependency dependency = shell.promptChoiceTyped("Which version of Arquillian do you want to install?", dependencies, DependencyUtil.getLatestNonSnapshotVersion(dependencies)); arquillianVersion = dependency.getVersion(); dependencyFacet.setProperty(ARQ_CORE_VERSION_PROP_NAME, arquillianVersion); } // need to set version after resolve is done, else nothing will resolve. if (!dependencyFacet.hasDirectManagedDependency(arquillianBom)) { arquillianBom.setVersion(ARQ_CORE_VERSION_PROP); dependencyFacet.addDirectManagedDependency(arquillianBom); } } private void installTestFramework(String selectedTestFramework) { resolveTestFrameworkInstaller(selectedTestFramework).install(); } private TestFrameworkFacetInstaller resolveTestFrameworkInstaller(String testFramework) { try { return testFrameworkFacetInstallers.select(new ProvidesFacetForQualifier(testFramework)).get(); } catch (Exception e) { throw new RuntimeException("Unable to resolve provider for selected test framework [" + testFramework + "]", e); } } }