/******************************************************************************* * 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.commands.test; import static org.grails.ide.eclipse.commands.GrailsCommandFactory.createDomainClass; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.net.URL; import java.util.Properties; import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.grails.ide.eclipse.commands.GrailsCommand; import org.grails.ide.eclipse.commands.GrailsCommandUtils; import org.grails.ide.eclipse.commands.GroovyCompilerVersionCheck; import org.grails.ide.eclipse.core.GrailsCoreActivator; import org.grails.ide.eclipse.core.internal.classpath.GrailsClasspathUtils; import org.grails.ide.eclipse.core.internal.classpath.GrailsPluginUtil; import org.grails.ide.eclipse.core.internal.plugins.GrailsCore; import org.grails.ide.eclipse.core.launch.GrailsLaunchConfigurationDelegate; import org.grails.ide.eclipse.core.launch.SynchLaunch.ILaunchResult; import org.grails.ide.eclipse.core.model.GrailsVersion; import org.grails.ide.eclipse.editor.gsp.tags.PerProjectTagProvider; import org.grails.ide.eclipse.runtime.shared.DependencyData; import org.grails.ide.eclipse.runtime.shared.DependencyFileFormat; import org.grails.ide.eclipse.runtime.shared.SharedLaunchConstants; import org.grails.ide.eclipse.test.GrailsTestsActivator; import org.grails.ide.eclipse.test.util.GrailsTest; import org.springsource.ide.eclipse.commons.frameworks.test.util.ACondition; import org.springsource.ide.eclipse.commons.tests.util.StsTestUtil; /** * Tests for the GrailsCommand class. * * @author Kris De Volder * @author Nieraj Singh * @author Andrew Eisenberg * @created 2010-08-04 */ public class GrailsCommandTest extends AbstractCommandTest { @Override protected void setUp() throws Exception { super.setUp(); GroovyCompilerVersionCheck.testMode(); ensureDefaultGrailsVersion(GrailsVersion.MOST_RECENT); // GrailsCoreActivator.getDefault().setKeepGrailsRunning(true); } /** * Relates to https://issuetracker.springsource.com/browse/STS-1874. Example code from * https://issuetracker.springsource.com/browse/STS-1880. */ public void testAgentBasedReloading() throws Exception { GrailsVersion version = GrailsVersion.MOST_RECENT; if (version.compareTo(GrailsVersion.V_2_0_0)>=0 && !GrailsTestsActivator.isJointGrailsTest()) { ensureDefaultGrailsVersion(version); final String projectName = TEST_PROJECT_NAME; project = ensureProject(projectName); createResource(project, "grails-app/controllers/ReloadableController.groovy", "class ReloadableController\n" + "{\n" + " def index = { render \"hello world\" }\n" + "}"); StsTestUtil.assertNoErrors(project); // Forces build and checks for compile errors in project. final int port = StsTestUtil.findFreeSocketPort(); ILaunchConfigurationWorkingCopy launchConf = (ILaunchConfigurationWorkingCopy) GrailsLaunchConfigurationDelegate.getLaunchConfiguration(project, "-Dserver.port="+port+" run-app", false); ILaunch launch = launchConf.launch(ILaunchManager.RUN_MODE, new NullProgressMonitor()); dumpOutput(launch); final URL url = new URL("http://localhost:"+port+"/"+projectName+"/reloadable/index"); try { new ACondition("hello world") { public boolean test() throws Exception { String page = getPageContent(url); assertEquals("hello world\n", page); return true; } }.waitFor(5*60*1000); createResource(project, "grails-app/controllers/ReloadableController.groovy", "class ReloadableController\n" + "{\n" + " def index = { render \"goodbye world\" }\n" + "}"); new ACondition("goodbye world") { public boolean test() throws Exception { String page = getPageContent(url); assertEquals("goodbye world\n", page); return true; } }.waitFor(30000); // updating contents with SpringLoaded should be faster? } finally { launch.terminate(); } } else { System.out.println("Skipping this test"); } } public void testInPlacePluginDependency() throws Exception { IProject plugin = ensureProject(this.getClass().getSimpleName()+"-"+"plug-in", true); IProject nonPlugin = ensureProject(this.getClass().getSimpleName()+"-"+"NonPlugin"); IJavaProject jNonPlugin = JavaCore.create(nonPlugin); // at first, should not have a dependency and should not be on classpath assertFalse("Dependency should not exist", GrailsPluginUtil.dependencyExists(nonPlugin, plugin)); assertFalse("Should not be on classpath", isOnClasspath(nonPlugin, plugin)); GrailsPluginUtil.addPluginDependency(nonPlugin, plugin); // dependency should exist, but not on classpath until refresh // dependencies is run assertTrue("Dependency should exist", GrailsPluginUtil.dependencyExists(nonPlugin, plugin)); assertFalse("Should not be on classpath", isOnClasspath(nonPlugin, plugin)); GrailsCommandUtils.refreshDependencies(jNonPlugin, false); assertTrue("Should be on classpath", isOnClasspath(nonPlugin, plugin)); assertTrue("Dependency should exist", GrailsPluginUtil.dependencyExists(nonPlugin, plugin)); // now remove dependency GrailsPluginUtil.removePluginDependency(nonPlugin, plugin); // dependency should not exist, but still on classpath until refresh // dependencies is run assertFalse("Dependency should not exist", GrailsPluginUtil.dependencyExists(nonPlugin, plugin)); assertTrue("Should be on classpath", isOnClasspath(nonPlugin, plugin)); GrailsCommandUtils.refreshDependencies(jNonPlugin, false); assertFalse("Should not be on classpath", isOnClasspath(nonPlugin, plugin)); assertFalse("Dependency should not exist", GrailsPluginUtil.dependencyExists(nonPlugin, plugin)); // try one more time for good show GrailsPluginUtil.addPluginDependency(nonPlugin, plugin); // dependency should exist, but not on classpath until refresh // dependencies is run assertTrue("Dependency should exist", GrailsPluginUtil.dependencyExists(nonPlugin, plugin)); assertFalse("Should not be on classpath", isOnClasspath(nonPlugin, plugin)); GrailsCommandUtils.refreshDependencies(jNonPlugin, false); assertTrue("Should be on classpath", isOnClasspath(nonPlugin, plugin)); } /** * Execute a command that runs "inside" a Grails project. */ public void testCreateDomainClass() throws Exception { IProject proj = ensureProject(TEST_PROJECT_NAME); GrailsCommand cmd = createDomainClass(proj, "gTunes.Song"); ILaunchResult result = cmd.synchExec(); System.out.println(result.getOutput()); GrailsTest.assertRegexp("Created.*Song", result.getOutput()); proj.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); assertResourceExists(TEST_PROJECT_NAME+"/grails-app/domain/gTunes/Song.groovy"); // assertResourceExists(TEST_PROJECT_NAME+"/test/unit/gTunes/SongTests.groovy"); } public void testBogusCommand() throws Exception { IProject proj = ensureProject(TEST_PROJECT_NAME); GrailsCommand cmd = GrailsCommand.forTest(proj,"create-domain") .addArgument("gTunes.Album"); try { cmd.synchExec(); fail("Should have an exception, but didn't have one"); } catch (CoreException e) { if (e.getStatus() instanceof MultiStatus) { MultiStatus m = (MultiStatus) e.getStatus(); if (m.getChildren().length == 2) { assertContains("Script 'CreateDomain' not found", m.getChildren()[0].getMessage()); return; } } fail("Incorrect exception thrown. Status:\n" + e.getMessage()); } } /** * Test the hook-up of DependencyExtractingBuildListener as a * "BuildListener" to a grails process. */ public void testBuildListener() throws Exception { ensureDefaultGrailsVersion(GrailsVersion.MOST_RECENT); IProject proj = ensureProject(TEST_PROJECT_NAME); GrailsCommand cmd = GrailsCommand.forTest(proj, "compile"); File tmpFile = File.createTempFile("testListener", ".log"); if (tmpFile.exists()) { tmpFile.delete(); } assertFalse(tmpFile.exists()); // Typical way to pass info to build listener in external process is by // setting system properties cmd.setSystemProperty(SharedLaunchConstants.DEPENDENCY_FILE_NAME_PROP, tmpFile.toString()); cmd.attachBuildListener(SharedLaunchConstants.DependencyExtractingBuildListener_CLASS); cmd.synchExec(); checkDependencyFile(proj, tmpFile); } /** * Repeat the above check, but instead of using the "nuts and bolts" just * use enableRefreshDependencyFile(). This should do the same thing (so the * generated dependency file should pass the same checks). */ public void testBuildListener2() throws Exception { ensureDefaultGrailsVersion(GrailsVersion.MOST_RECENT); IProject proj = ensureProject(TEST_PROJECT_NAME); GrailsCommand cmd = GrailsCommand.forTest(proj, "compile"); cmd.enableRefreshDependencyFile(); File tmpFile = GrailsClasspathUtils.getDependencyDescriptor(proj); if (tmpFile.exists()) { tmpFile.delete(); } assertFalse(tmpFile.exists()); cmd.synchExec(); checkDependencyFile(proj, tmpFile); } /** * Do a few simple checks to see if the contents of a generated dependency * file looks ok. These checks are by no means comprehensive, but it is * better than nothing. * * @throws IOException */ protected void checkDependencyFile(IProject project, File file) throws IOException { // /////////////////////////////////////// // Check the generated file... assertTrue(file.exists()); DependencyData depData = DependencyFileFormat.read(file); String dotGrailsFolder = new File(GrailsCoreActivator.getDefault().getUserHome() + "/" + ".grails/" + grailsVersion()).getCanonicalPath(); // Check plugins directory points where it should String pluginsDirectory = depData.getPluginsDirectory(); if (GrailsVersion.V_2_3_.compareTo(GrailsVersion.MOST_RECENT) <=0) { //Grails 2.3 has moved the plugin area into the project area. It's no longer inside // the .grails folder. assertEquals(project.getLocation()+"/target/work/plugins", pluginsDirectory); } else { assertEquals(dotGrailsFolder + "/projects/"+TEST_PROJECT_NAME+"/plugins", pluginsDirectory); } Set<String> sources = depData.getSources(); for (String string : sources) { System.out.println(string); } String[] expectedPlugins = new String[] { "tomcat", "hibernate" }; // Checkinf for these makes the tests unstable? No real way to know what is expected here // It varies depending on plugins and grails version. // for (String pluginName : expectedPlugins) { // for (String javaGroovy : new String[] { "java", "groovy" }) { // String expect = pluginsDirectory + "/" + pluginName + "-" // + grailsVersion() + "/src/" + javaGroovy; // assertTrue("Missing source entry: " + expect, // sources.contains(expect)); // } // } Set<String> pluginsXmls = depData.getPluginDescriptors(); for (String string : pluginsXmls) { System.out.println(string); } for (String pluginName : expectedPlugins) { assertPluginXml(pluginName, pluginsXmls); } // TODO: KDV: (depend) add some more checks of the contents of the file // (i.e. check for a few basic jar dependencies that should always be // there.) } private void assertPluginXml(String pluginName, Set<String> pluginsXmls) { for (String string : pluginsXmls) { if (string.contains(pluginName) && string.endsWith("plugin.xml")) { return; //OK, found a entry for that plugin } } fail("No plugin.xml file found for plugin "+pluginName); } /** * Test to see if after changing application.properties and doing refresh * dependencies, the project source folders look correct. * * @throws Exception */ public void testEditedApplicationProperties() throws Exception { // application.properties no longer supported in 2.3 and greater if (GrailsVersion.V_2_3_.compareTo(GrailsVersion.MOST_RECENT) <= 0) { return; } IProject proj = ensureProject(TEST_PROJECT_NAME); IFile eclipsePropFile = proj.getFile("application.properties"); File propFile = eclipsePropFile.getLocation().toFile(); // Modify the props file add two plugins Properties props = new Properties(); props.load(new FileInputStream(propFile)); // Need to close this reader? props.put("plugins.feeds", "1.5"); props.put("plugins.spring-security-core", "1.2.7.3"); props.store(new FileOutputStream(propFile), "#Grails metadata file"); eclipsePropFile.refreshLocal(IResource.DEPTH_ZERO, new NullProgressMonitor()); refreshDependencies(proj); // Check that the plugins linked source folders are now there. assertPluginSourceFolder(proj, "feeds-1.5", "src", "groovy"); assertPluginSourceFolder(proj, "spring-security-core-1.2.7.3", "src", "groovy"); // ///////////////////////////////////////////////////////////// // Now modify the version of the plugins and try this again props.put("plugins.feeds", "1.6"); props.put("plugins.spring-security-core", "1.2.7.2"); props.store(new FileOutputStream(propFile), "#Grails metadata file"); eclipsePropFile.refreshLocal(IResource.DEPTH_ZERO, new NullProgressMonitor()); // Refresh dependencies // GrailsClient.DEBUG_PROCESS = true; refreshDependencies(proj); // GrailsClient.DEBUG_PROCESS = false; // Check that the linked source folders of the replaced version are no // longer there. assertAbsentPluginSourceFolder(proj, "feeds-1.5", "src", "groovy"); assertAbsentPluginSourceFolder(proj, "spring-security-core-1.2.7.3", "src", "groovy"); // Check that the linked source folders of the new versions are there. assertPluginSourceFolder(proj, "feeds-1.6", "src", "groovy"); assertPluginSourceFolder(proj, "spring-security-core-1.2.7.2", "src", "groovy"); // ///////////////////////////////////////////////////////////// // Now remove the plugins and try this again props.remove("plugins.feeds"); props.remove("plugins.spring-security-core"); props.store(new FileOutputStream(propFile), "#Grails metadata file"); eclipsePropFile.refreshLocal(IResource.DEPTH_ZERO, new NullProgressMonitor()); // Refresh dependencies refreshDependencies(proj); // Check that the linked source folders of the replaced version are no // longer there. assertAbsentPluginSourceFolder(proj, "feeds-1.5", "src", "groovy"); assertAbsentPluginSourceFolder(proj, "spring-security-core-1.2.7.3", "src", "groovy"); // Check that the linked source folders of the new versions are also no // longer there. assertAbsentPluginSourceFolder(proj, "feeds-1.6", "src", "groovy"); assertAbsentPluginSourceFolder(proj, "spring-security-core-1.2.7.2", "src", "groovy"); } public void testEditedBuildConfig() throws Exception { // only test in 2.3 and greater if (GrailsVersion.V_2_3_.compareTo(GrailsVersion.MOST_RECENT) > 0) { return; } ensureDefaultGrailsVersion(GrailsVersion.MOST_RECENT); final IProject proj = ensureProject(TEST_PROJECT_NAME); IFile buildConfig = proj.getFile("grails-app/conf/BuildConfig.groovy"); String origContents = GrailsTest.getContents(buildConfig); // System.out.println("=== BuildConfig.groovy ===="); // System.out.println(origContents); // System.out.println("=== BuildConfig.groovy ===="); // Modify the props file add two plugins // A bit of a hack, but this is a simple way to add plugins to the build String newContents = origContents.replace("plugins {\n", "plugins {" + "\n\t\tcompile \":feeds:1.6\"\n"); GrailsTest.setContents(buildConfig, newContents); refreshDependencies(proj); // Check that the plugins linked source folders are now there. new ACondition("installed feeds-1.6") { @Override public boolean test() throws Exception { assertPluginSourceFolder(proj, "feeds-1.6", "src", "groovy"); return true; } }.waitFor(60000); // ///////////////////////////////////////////////////////////// // Now remove the plugins and try this again // props.remove("plugins.feeds"); // props.remove("plugins.spring-security-core"); GrailsTest.setContents(buildConfig, origContents); // Refresh dependencies refreshDependencies(proj); // Check that the linked source folders of the replaced version are no // longer there. new ACondition("removed feeds") { @Override public boolean test() throws Exception { // Check that the linked source folders of the plugin are no // longer there. assertAbsentPluginSourceFolder(proj, "feeds-1.6", "src", "groovy"); return true; } }.waitFor(10000); } private void refreshDependencies(IProject proj) throws CoreException { try { GrailsCommandUtils.refreshDependencies(JavaCore.create(proj), true); } catch (Exception e) { // as of Grails 2.3, we really should only need to run refresh dependencies once // but below that, we need to do it twice if (GrailsVersion.V_2_3_.compareTo(GrailsVersion.MOST_RECENT) > 0) { try { GrailsCommandUtils.refreshDependencies(JavaCore.create(proj), true); } catch (Exception e2) { throw new RuntimeException(e2); } } else { throw new RuntimeException(e); } } } public void testTagLibsFromPlugin() throws Exception { IProject proj = ensureProject(TEST_PROJECT_NAME); ensureDefaultGrailsVersion(GrailsVersion.getGrailsVersion(proj)); if (GrailsVersion.V_2_3_.compareTo(GrailsVersion.MOST_RECENT) > 0) { // below 2.3 install-plugin command works GrailsCommand cmd = GrailsCommand.forTest(proj, "install-plugin") .addArgument("feeds") .addArgument("1.6"); cmd.synchExec(); } else { // after 2.3, must edit build config IFile buildConfig = proj.getFile("grails-app/conf/BuildConfig.groovy"); String origContents = GrailsTest.getContents(buildConfig); // Modify the props file add two plugins // A bit of a hack, but this is a simple way to add plugins to the build String newContents = origContents.replace("plugins {\n", "plugins {\n\t\tcompile \":feeds:1.6\"\n"); GrailsTest.setContents(buildConfig, newContents); } refreshDependencies(proj); assertPluginSourceFolder(proj, "feeds-1.6", "src", "groovy"); // now also check to see that the tag is available PerProjectTagProvider provider = GrailsCore.get().connect(proj, PerProjectTagProvider.class); assertNotNull("feeds:meta tag not installed", provider.getDocumentForTagName("feed:meta")); } public void testOutputLimit() throws Exception { IProject proj = ensureProject(TEST_PROJECT_NAME); ensureDefaultGrailsVersion(GrailsVersion.getGrailsVersion(proj)); GrailsCommand cmd = GrailsCommand.forTest("help"); ILaunchResult result = cmd.synchExec(); // String allOutput = result.getOutput(); int orgLimit = GrailsCoreActivator.getDefault().getGrailsCommandOutputLimit(); try { GrailsCoreActivator.getDefault().setGrailsCommandOutputLimit(100); result = cmd.synchExec(); assertEquals(100, result.getOutput().length()); } finally { GrailsCoreActivator.getDefault().setGrailsCommandOutputLimit(orgLimit); } } // /** // * Test grails command to build "exploded" war file inside the target // directory (rather than a ".war" archive). To do this // * we need to somehow set additional properties in the grails build // settings that we do not ordinarily have access to) // * from the command line (only can be set in the BuildSettings.groovy // config file). So the this test implicitly checks // * the mechanism hacked-up here to set properties in BuildSettings) // */ // public void testExplodedWar() throws Exception { // IProject proj = ensureProject(TEST_PROJECT_NAME); // GrailsCommand cmd = new GrailsCommand(proj, "dev war"); // cmd. // } /** * What's the default grails version that we expect new projects to be * created with. */ private String grailsVersion() { return GrailsCoreActivator.getDefault().getInstallManager() .getDefaultGrailsInstall().getVersionString(); } }