/******************************************************************************* * Copyright (c) 2012 VMWare, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VMWare, Inc. - initial API and implementation *******************************************************************************/ package org.grails.ide.eclipse.maven.test; import static org.grails.ide.eclipse.commands.GrailsCommandFactory.createDomainClass; import java.io.IOException; import java.util.Arrays; import java.util.List; import org.apache.maven.model.Dependency; import org.codehaus.jdt.groovy.model.GroovyNature; import org.eclipse.core.resources.ICommand; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationType; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.debug.ui.RefreshTab; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaModelMarker; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.m2e.actions.MavenLaunchConstants; import org.eclipse.m2e.core.MavenPlugin; import org.eclipse.m2e.core.internal.IMavenConstants; import org.eclipse.m2e.core.project.IMavenProjectFacade; import org.eclipse.m2e.core.project.IMavenProjectRegistry; import org.eclipse.m2e.core.project.ResolverConfiguration; import org.eclipse.m2e.core.ui.internal.editing.PomEdits; import org.eclipse.m2e.core.ui.internal.editing.PomEdits.Operation; import org.eclipse.m2e.core.ui.internal.editing.PomEdits.OperationTuple; import org.eclipse.m2e.core.ui.internal.editing.PomHelper; import org.eclipse.m2e.jdt.IClasspathManager; import org.eclipse.m2e.tests.common.AbstractLifecycleMappingTest; import org.grails.ide.eclipse.commands.GrailsCommand; import org.grails.ide.eclipse.core.internal.GrailsNature; import org.grails.ide.eclipse.core.internal.GrailsResourceUtil; import org.grails.ide.eclipse.core.launch.SynchLaunch.ILaunchResult; import org.grails.ide.eclipse.core.model.GrailsVersion; import org.grails.ide.eclipse.test.util.GrailsTest; import org.grails.ide.eclipse.ui.internal.importfixes.GrailsProjectVersionFixer; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * * @author Andrew Eisenberg * @created Sep 11, 2012 */ @SuppressWarnings("nls") public class GrailsMavenTests extends AbstractLifecycleMappingTest { private final static String MAVEN_BUILDER_ID = "org.eclipse.m2e.core.maven2Builder"; private final static String FACET_BUILDER_ID = "org.eclipse.wst.common.project.facet.core.builder"; private final static Dependency FEEDS_DEPENDENCY = createDependency("org.grails.plugins", "feeds", "1.5", "zip"); private static Dependency createDependency(String group, String artifact, String version, String type) { Dependency d = new Dependency(); d.setArtifactId(artifact); d.setGroupId(group); d.setVersion(version); d.setType(type); return d; } // all entries on the classpath...commented nes are those we are not interested in private static String[] EXPECTED_CLASSPATH_ENTRIES = { "/ggts-maven-test/src/main/java", "/ggts-maven-test/grails-app/conf", "/ggts-maven-test/grails-app/controllers", "/ggts-maven-test/grails-app/domain", "/ggts-maven-test/grails-app/services", "/ggts-maven-test/grails-app/taglib", "/ggts-maven-test/grails-app/utils", "/ggts-maven-test/src/groovy", "/ggts-maven-test/src/java", "/ggts-maven-test/src/main/resources", "/ggts-maven-test/src/test/java", "/ggts-maven-test/test/unit", "/ggts-maven-test/test/integration", // "org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6", "org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER", "/ggts-maven-test/.link_to_grails_plugins/database-migration-1.1/grails-app/conf", "/ggts-maven-test/.link_to_grails_plugins/database-migration-1.1/grails-app/controllers", "/ggts-maven-test/.link_to_grails_plugins/database-migration-1.1/grails-app/views", "/ggts-maven-test/.link_to_grails_plugins/database-migration-1.1/src/groovy", "/ggts-maven-test/.link_to_grails_plugins/hibernate-2.1.1/grails-app/i18n", "/ggts-maven-test/.link_to_grails_plugins/jquery-1.7.1/grails-app/conf", "/ggts-maven-test/.link_to_grails_plugins/jquery-1.7.1/grails-app/i18n", "/ggts-maven-test/.link_to_grails_plugins/jquery-1.7.1/grails-app/services", "/ggts-maven-test/.link_to_grails_plugins/jquery-1.7.1/grails-app/taglib", "/ggts-maven-test/.link_to_grails_plugins/jquery-1.7.1/src/groovy", "/ggts-maven-test/.link_to_grails_plugins/resources-1.1.6/grails-app/conf", "/ggts-maven-test/.link_to_grails_plugins/resources-1.1.6/grails-app/i18n", "/ggts-maven-test/.link_to_grails_plugins/resources-1.1.6/grails-app/resourceMappers", "/ggts-maven-test/.link_to_grails_plugins/resources-1.1.6/grails-app/taglib", "/ggts-maven-test/.link_to_grails_plugins/resources-1.1.6/src/groovy", "/ggts-maven-test/.link_to_grails_plugins/resources-1.1.6/src/java", "org.grails.ide.eclipse.core.CLASSPATH_CONTAINER", "GROOVY_DSL_SUPPORT" }; private final static String[] FEEDS_PATHS = { "/ggts-maven-test/.link_to_grails_plugins/feeds-1.5/grails-app/i18n", "/ggts-maven-test/.link_to_grails_plugins/feeds-1.5/grails-app/taglib", "/ggts-maven-test/.link_to_grails_plugins/feeds-1.5/src/groovy", }; @Override protected void setUp() throws Exception { GrailsProjectVersionFixer.testMode(); GrailsTest.ensureDefaultGrailsVersion(GrailsVersion.V_2_1_1); GrailsTest.waitForGrailsIntall(); super.setUp(); } public void testGrailsApp() throws Exception { ResolverConfiguration configuration = new ResolverConfiguration(); IProject project = importProject("testProjects/ggts-maven-test/pom.xml", configuration); System.out.println("Project imported ggts-maven-test"); project.build(IncrementalProjectBuilder.FULL_BUILD, monitor); System.out.println("Project built ggts-maven-test"); assertNaturesAndBuilders(project); assertClasspath(project, false); System.out.println("First round of assertions complete ggts-maven-test"); // add a dependency IFile pomFile = project.getFile("pom.xml"); addDependency(FEEDS_DEPENDENCY, pomFile); MavenPlugin.getProjectConfigurationManager().updateProjectConfiguration(project, monitor); System.out.println("Dependency added ggts-maven-test"); project.build(IncrementalProjectBuilder.FULL_BUILD, monitor); System.out.println("Project rebuilt ggts-maven-test"); assertClasspath(project, true); System.out.println("Second round of assertions ggts-maven-test"); // remove dependency removeDependency(FEEDS_DEPENDENCY, pomFile); System.out.println("Dependency removed ggts-maven-test"); cleanProject(project); System.out.println("Project cleaned ggts-maven-test"); assertClasspath(project, false); System.out.println("Third round of assertions ggts-maven-test"); // issue a grails command GrailsCommand cmd = createDomainClass(project, "gTunes.Song"); ILaunchResult result = cmd.synchExec(); System.out.println(result.getOutput()); System.out.println("Grails command complete ggts-maven-test"); GrailsTest.assertRegexp("Created.*Song", result.getOutput()); project.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); assertTrue("Expected domain class to exist", project.getFile("/grails-app/domain/gTunes/Song.groovy").exists()); System.out.println("Domain class exists ggts-maven-test"); project.build(IncrementalProjectBuilder.FULL_BUILD, monitor); System.out.println("Project rebuilt ggts-maven-test"); assertClasspath(project, false); System.out.println("Final round of assertions ggts-maven-test"); } private void cleanProject(IProject project) throws CoreException, InterruptedException { // simulate a cleam ILaunchConfiguration configuration = createLaunchConfiguration(project, "clean"); DebugEventWaiter waiter = new DebugEventWaiter(DebugEvent.TERMINATE); configuration.launch("run", monitor, false); waiter.waitForEvent(); project.build(IncrementalProjectBuilder.CLEAN_BUILD, monitor); project.refreshLocal(IResource.DEPTH_INFINITE, monitor); waitForJobsToComplete(monitor); MavenPlugin.getProjectConfigurationManager().updateProjectConfiguration(project, monitor); project.build(IncrementalProjectBuilder.FULL_BUILD, monitor); } private void assertNaturesAndBuilders(IProject project) throws CoreException { assertTrue("Expected Grails nature", project.hasNature(GrailsNature.NATURE_ID)); assertFalse("Expected no Old Grails nature", project.hasNature(GrailsNature.OLD_NATURE_ID)); assertTrue("Expected Groovy nature", project.hasNature(GroovyNature.GROOVY_NATURE)); assertTrue("Expected Java nature", project.hasNature(JavaCore.NATURE_ID)); // check builders ICommand[] buildSpec = project.getDescription().getBuildSpec(); boolean javaBuilder = false; boolean m2eBuilder = false; boolean facetBuilder = false; for (ICommand command : buildSpec) { if (command.getBuilderName().equals(JavaCore.BUILDER_ID)) { javaBuilder = true; } else if (command.getBuilderName().equals(MAVEN_BUILDER_ID)) { m2eBuilder = true; } else if (command.getBuilderName().equals(FACET_BUILDER_ID)) { facetBuilder = true; } } assertTrue("Expected Java builder", javaBuilder); assertTrue("Expected m2e builder", m2eBuilder); assertTrue("Expected facet builder", facetBuilder); assertEquals("Expected exactly 3 builders, but found:\n" + Arrays.toString(buildSpec), 3, buildSpec.length); } private void assertClasspath(IProject project, boolean feedsExists) throws CoreException { assertNull(getProblems(project)); IClasspathEntry[] entries = JavaCore.create(project).getRawClasspath(); for (String pathName : EXPECTED_CLASSPATH_ENTRIES) { assertClasspathEntry(pathName, entries); } for (String pathName : FEEDS_PATHS) { if (feedsExists) { assertClasspathEntry(pathName, entries); } else { assertNoClasspathEntry(pathName, entries); } } assertClasspathAttributes(entries); } /** * If the classpath entry has a grails source folder attribute, then it must also have a maven.pomderived attribite * @param entries */ private void assertClasspathAttributes(IClasspathEntry[] entries) { for (IClasspathEntry entry : entries) { if (GrailsResourceUtil.isGrailsClasspathEntry(entry) && !GrailsResourceUtil.hasClasspathAttribute(entry, IClasspathManager.POMDERIVED_ATTRIBUTE)) { fail("Classpath entry is grails source folder, but was not added by m2e: " + entry); } } } private void assertClasspathEntry(String pathName, IClasspathEntry[] entries) { boolean found = false; for (IClasspathEntry entry : entries) { if (entry.getPath().toPortableString().equals(pathName)) { found = true; break; } } if (!found) { fail("Expected to have " + pathName + " as a classpath entry, but didn't find it. Found:\n" + Arrays.toString(entries)); } } private void assertNoClasspathEntry(String pathName, IClasspathEntry[] entries) { boolean found = false; for (IClasspathEntry entry : entries) { if (entry.getPath().toPortableString().equals(pathName)) { found = true; break; } } if (found) { fail("Expected not to have " + pathName + " as a classpath entry, but found it. Found:\n" + Arrays.toString(entries)); } } private String getProblems(IProject project) throws CoreException { StringBuilder sb = new StringBuilder(); sb.append(createErrorString(project, IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER)); sb.append(createErrorString(project, IJavaModelMarker.BUILDPATH_PROBLEM_MARKER)); sb.append(createErrorString(project, IMavenConstants.MARKER_BUILD_ID)); return sb.length() > 0 ? "Problems:\n" + sb.toString() : null; } private String createErrorString(IProject project, String markerName) throws CoreException { IMarker[] markers = project.findMarkers(markerName, true, IResource.DEPTH_INFINITE); StringBuilder sb = new StringBuilder(); for (int i = 0; i < markers.length; i++) { if (((Integer) markers[i].getAttribute(IMarker.SEVERITY)).intValue() == IMarker.SEVERITY_ERROR) { sb.append(" "); sb.append(markers[i].getResource().getName()).append(" : "); sb.append(markers[i].getAttribute(IMarker.LOCATION)).append(" : "); sb.append(markers[i].getAttribute(IMarker.MESSAGE)).append("\n"); } } return sb.toString(); } // borrowed from org.eclipse.m2e.core.ui.internal.actions.AddDependencyAction private void addDependency(final Dependency dependency, IFile file) throws IOException, CoreException { PomEdits.performOnDOMDocument(new OperationTuple(file, new Operation() { public void process(Document document) { Element depsEl = PomEdits.getChild( document.getDocumentElement(), PomEdits.DEPENDENCIES); Element dep = PomEdits.findChild( depsEl, PomEdits.DEPENDENCY, PomEdits.childEquals(PomEdits.GROUP_ID, dependency.getGroupId()), PomEdits.childEquals(PomEdits.ARTIFACT_ID, dependency.getArtifactId())); if (dep == null) { dep = PomHelper.createDependency(depsEl, dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion()); } else { // only set version if already exists if (dependency.getVersion() != null) { PomEdits.setText( PomEdits.getChild(dep, PomEdits.VERSION), dependency.getVersion()); } } if (dependency.getType() != null // && !"jar".equals(dependency.getType()) // //$NON-NLS-1$ && !"null".equals(dependency.getType())) { // guard against MNGECLIPSE-622 //$NON-NLS-1$ PomEdits.setText(PomEdits.getChild(dep, PomEdits.TYPE), dependency.getType()); } if (dependency.getClassifier() != null) { PomEdits.setText( PomEdits.getChild(dep, PomEdits.CLASSIFIER), dependency.getClassifier()); } if (dependency.getScope() != null && !"compile".equals(dependency.getScope())) { //$NON-NLS-1$ PomEdits.setText(PomEdits.getChild(dep, PomEdits.SCOPE), dependency.getScope()); } } })); } private void removeDependency(final Dependency dependency, IFile file) throws IOException, CoreException { PomEdits.performOnDOMDocument(new OperationTuple(file, new Operation() { public void process(Document document) { Element depsEl = PomEdits.getChild( document.getDocumentElement(), PomEdits.DEPENDENCIES); Element dep = PomEdits.findChild( depsEl, PomEdits.DEPENDENCY, PomEdits.childEquals(PomEdits.GROUP_ID, dependency.getGroupId()), PomEdits.childEquals(PomEdits.ARTIFACT_ID, dependency.getArtifactId())); if (dep != null) { PomEdits.removeChild(depsEl, dep); } } })); } // copied from org.grails.ide.eclipse.maven.test.GrailsMavenTests private ILaunchConfiguration createLaunchConfiguration(IContainer basedir, String goal) throws CoreException { try { ILaunchManager launchManager = DebugPlugin.getDefault() .getLaunchManager(); ILaunchConfigurationType launchConfigurationType = launchManager .getLaunchConfigurationType(MavenLaunchConstants.LAUNCH_CONFIGURATION_TYPE_ID); ILaunchConfigurationWorkingCopy workingCopy = launchConfigurationType .newInstance(null, "Test launch"); workingCopy.setAttribute(MavenLaunchConstants.ATTR_POM_DIR, basedir .getLocation().toOSString()); workingCopy.setAttribute(MavenLaunchConstants.ATTR_GOALS, goal); workingCopy.setAttribute(IDebugUIConstants.ATTR_PRIVATE, true); workingCopy.setAttribute(RefreshTab.ATTR_REFRESH_SCOPE, "${project}"); //$NON-NLS-1$ workingCopy.setAttribute(RefreshTab.ATTR_REFRESH_RECURSIVE, true); workingCopy.setAttribute(IDebugUIConstants.ATTR_LAUNCH_IN_BACKGROUND, false); setProjectConfiguration(workingCopy, basedir); IPath path = getJREContainerPath(basedir); if (path != null) { workingCopy .setAttribute( IJavaLaunchConfigurationConstants.ATTR_JRE_CONTAINER_PATH, path.toPortableString()); } return workingCopy; } catch (CoreException ex) { throw ex; } } // copied from org.grails.ide.eclipse.maven.test.GrailsMavenTests private void setProjectConfiguration( ILaunchConfigurationWorkingCopy workingCopy, IContainer basedir) { IMavenProjectRegistry projectManager = MavenPlugin .getMavenProjectRegistry(); IFile pomFile = basedir .getFile(new Path(IMavenConstants.POM_FILE_NAME)); IMavenProjectFacade projectFacade = projectManager.create(pomFile, false, new NullProgressMonitor()); if (projectFacade != null) { ResolverConfiguration configuration = projectFacade .getResolverConfiguration(); String selectedProfiles = configuration.getSelectedProfiles(); if (selectedProfiles != null && selectedProfiles.length() > 0) { workingCopy.setAttribute(MavenLaunchConstants.ATTR_PROFILES, selectedProfiles); } } } // copied from org.grails.ide.eclipse.maven.test.GrailsMavenTests private IPath getJREContainerPath(IContainer basedir) throws CoreException { IProject project = basedir.getProject(); if (project != null && project.hasNature(JavaCore.NATURE_ID)) { IJavaProject javaProject = JavaCore.create(project); IClasspathEntry[] entries = javaProject.getRawClasspath(); for (int i = 0; i < entries.length; i++) { IClasspathEntry entry = entries[i]; if (JavaRuntime.JRE_CONTAINER .equals(entry.getPath().segment(0))) { return entry.getPath(); } } } return null; } }