/******************************************************************************* * 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.test.inferencing; import java.io.ByteArrayInputStream; import java.io.InputStream; import junit.framework.AssertionFailedError; import org.codehaus.groovy.eclipse.codeassist.tests.CompletionTestCase; import org.codehaus.groovy.eclipse.editor.GroovyEditor; import org.codehaus.jdt.groovy.model.GroovyCompilationUnit; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.tests.util.GroovyUtils; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.ui.internal.Workbench; import org.grails.ide.eclipse.core.internal.plugins.GrailsCore; import org.grails.ide.eclipse.core.internal.plugins.GrailsElementKind; import org.grails.ide.eclipse.core.model.GrailsVersion; import org.grails.ide.eclipse.editor.groovy.elements.DomainClass; import org.grails.ide.eclipse.test.MockGrailsTestProjectUtils; import org.grails.ide.eclipse.test.util.GrailsTest; import org.grails.ide.eclipse.test.util.GrailsTestUtilActivator; import org.grails.ide.eclipse.ui.internal.importfixes.GrailsProjectVersionFixer; import org.springsource.ide.eclipse.commons.frameworks.core.FrameworkCoreActivator; import org.springsource.ide.eclipse.commons.frameworks.test.util.ACondition; import org.springsource.ide.eclipse.commons.tests.util.StsTestUtil; /** * Tests content assist in grails artifact files * @author Andrew Eisenberg * @since 2.6.1 */ public class GrailsContentAssistTests extends CompletionTestCase { private GrailsVersion grailsVersion; public GrailsContentAssistTests(String name) { super(name); } @Override protected void setUp() throws Exception { GrailsTest.assertEquals(true, true); //touch the GrailsTest class force GTUA to get started. This matters mostly when running // these test in isolation where otherwise GrailsVersions may not yet be configured when test starts. GrailsProjectVersionFixer.testMode(); grailsVersion = GrailsVersion.MOST_RECENT; GrailsTest.ensureDefaultGrailsVersion(grailsVersion); super.setUp(); } @Override protected void tearDown() throws Exception { // be sure to close all editors now try { ((GroovyEditor) Workbench.getInstance().getActiveWorkbenchWindow().getPages()[0].getActiveEditor()).close(false); } catch (Exception e) { e.printStackTrace(); } avoidUIThreadStarvation(); super.tearDown(); } private void avoidUIThreadStarvation() { try { //Suspect that this test suite is completely 'owning' the UI thread. This leads to things that need to run on it // piling up and eventually crashing the JVM with OME exception. new ACondition("Wait for Jobs") { @Override public boolean test() throws Exception { assertJobManagerIdle(); return true; } }.waitFor(3 * 60 * 1000); } catch (Throwable e) { //Log exception but don't fail tests because of it FrameworkCoreActivator.log(e); } } public void testDomainClassFields1() throws Exception { for (String fieldName : DomainClass.staticFields) { checkDomainField1(fieldName); } } private void checkDomainField1(String fieldName) throws Exception { String contents = "class MyDomain {\n\n}"; int offset = contents.indexOf('\n')+1; String expected = contents.replaceFirst("\n\n", "\nstatic " + fieldName + " = null\n"); checkProposalApplication("MyDomain", contents, GrailsElementKind.DOMAIN_CLASS, expected, offset, fieldName, false); } public void testDomainClassFields2() throws Exception { for (String fieldName : DomainClass.staticFields) { checkDomainField2(fieldName); } } private void checkDomainField2(String fieldName) throws Exception { String contents = "class MyDomain {\nst\n}"; int offset = contents.indexOf("\nst")+3; String expected = contents.replaceFirst("\nst\n", "\nstatic " + fieldName + " = null\n"); checkProposalApplication("MyDomain", contents, GrailsElementKind.DOMAIN_CLASS, expected, offset, "static " + fieldName, false); } public void testDomainClassFields3() throws Exception { for (String fieldName : DomainClass.staticFields) { checkDomainField3(fieldName); } } private void checkDomainField3(String fieldName) throws Exception { String completion = "static " + fieldName.substring(0, fieldName.length()- 3); String contents = "class MyDomain {\n" + completion + "\n}"; int offset = contents.indexOf(completion) + completion.length(); String expected = contents.replaceFirst(completion, "static " + fieldName + " = null"); checkProposalApplication("MyDomain", contents, GrailsElementKind.DOMAIN_CLASS, expected, offset, "static " + fieldName, false); } public void testServiceClassDefinition() throws Exception { createGrailsElement(GrailsElementKind.SERVICE_CLASS, "JustSomeService", "class JustSomeService { }"); String completion = "justSomeService"; String contents = "class MyDomain {\n\n}"; int offset = contents.indexOf('\n') + 1; String expected = contents.replaceFirst("\n\n", "\ndef " + completion + "\n"); checkProposalApplication("MyDomain", contents, GrailsElementKind.DOMAIN_CLASS, expected, offset, completion, false); contents = "class MyDomain {\nde\n}"; offset = contents.indexOf("\nde") + "\nde".length(); checkProposalApplication("MyDomain", contents, GrailsElementKind.DOMAIN_CLASS, expected, offset, "def " + completion, false); contents = "class MyDomain {\ndef ju\n}"; offset = contents.indexOf("\ndef ju") + "\ndef ju".length(); checkProposalApplication("MyDomain", contents, GrailsElementKind.DOMAIN_CLASS, expected, offset, "def " + completion, false); } public void testNamedQuery1() throws Exception { // failing on older versions of Groovy/Grails if (GroovyUtils.GROOVY_LEVEL < 18) { return; } String contents = "class MyDomain {\n" + " String name\n" + " static namedQueries = {\n" + " firstQuery { }\n" + " secondQuery { }\n" + " }\n" + " def x = { MyDomain. }\n" + "\n}"; String str = "MyDomain."; String strRegex = "MyDomain\\."; int offset = contents.indexOf(str)+str.length(); String expected = contents.replaceFirst(strRegex, "MyDomain.firstQuery"); checkProposalApplication("MyDomain", contents, GrailsElementKind.DOMAIN_CLASS, expected, offset, "firstQuery", false); expected = contents.replaceFirst(strRegex, "MyDomain.secondQuery"); checkProposalApplication("MyDomain", contents, GrailsElementKind.DOMAIN_CLASS, expected, offset, "secondQuery", false); } public void testNamedQuery2() throws Exception { // failing on older versions of Groovy/Grails if (GroovyUtils.GROOVY_LEVEL < 18) { return; } String contents = "class MyDomain {\n" + " String name\n" + " static namedQueries = {\n" + " firstQuery { }\n" + " secondQuery { }\n" + " }\n" + " def x = { MyDomain.firstQuery. }\n" + "\n}"; String str = "firstQuery."; String strRegex = "firstQuery\\."; int offset = contents.indexOf(str)+str.length(); String expected = contents.replaceFirst(strRegex, "firstQuery.firstQuery"); checkProposalApplication("MyDomain", contents, GrailsElementKind.DOMAIN_CLASS, expected, offset, "firstQuery", false); expected = contents.replaceFirst(strRegex, "firstQuery.secondQuery"); checkProposalApplication("MyDomain", contents, GrailsElementKind.DOMAIN_CLASS, expected, offset, "secondQuery", false); } public void testNamedQuery3() throws Exception { // failing on older versions of Groovy/Grails if (GroovyUtils.GROOVY_LEVEL < 18) { return; } String contents = "class MyDomain {\n" + " String name\n" + " static namedQueries = {\n" + " firstQuery { }\n" + " secondQuery { }\n" + " }\n" + " def x = { MyDomain.firstQuery. }\n" + "\n}"; String str = "firstQuery."; String strRegex = "firstQuery\\."; int offset = contents.indexOf(str)+str.length(); String expected; // seems to be a bit random whether the result has a closure as a last param or not // so try both ways try { expected = contents.replaceFirst(strRegex, "firstQuery.list(parameterTypes) {"); checkProposalApplication("MyDomain", contents, GrailsElementKind.DOMAIN_CLASS, expected, offset, "list", false); } catch (AssertionFailedError e) { expected = contents.replaceFirst(strRegex, "firstQuery.list(parameterTypes)"); checkProposalApplication("MyDomain", contents, GrailsElementKind.DOMAIN_CLASS, expected, offset, "list", false); } // seems to be a bit random whether the result is 'null' or 'this' // so try both ways try { expected = contents.replaceFirst(strRegex, "firstQuery.get(null)"); checkProposalApplication("MyDomain", contents, GrailsElementKind.DOMAIN_CLASS, expected, offset, "get", false); } catch (AssertionFailedError e) { expected = contents.replaceFirst(strRegex, "firstQuery.get(this)"); checkProposalApplication("MyDomain", contents, GrailsElementKind.DOMAIN_CLASS, expected, offset, "get", false); } try { expected = contents.replaceFirst(strRegex, "firstQuery.findAllWhere"); checkProposalApplication("MyDomain", contents, GrailsElementKind.DOMAIN_CLASS, expected, offset, "findAllWhere", false); } catch (AssertionFailedError e) { expected = contents.replaceFirst(strRegex, "firstQuery.findAllWhere(null)"); checkProposalApplication("MyDomain", contents, GrailsElementKind.DOMAIN_CLASS, expected, offset, "findAllWhere", false); } } public void testNamedQuery4() throws Exception { String contents = "class MyDomain {\n" + " String name\n" + " static namedQueries = {\n" + " firstQuery { }\n" + " secondQuery { }\n" + " }\n" + " def x = { MyDomain.firstQuery.findAllByName }\n" + "\n}"; String str = "firstQuery.findAllByName"; int offset = contents.indexOf(str)+str.length(); String expected = contents.replaceFirst(str, "firstQuery.findAllByNameBetween"); checkProposalApplication("MyDomain", contents, GrailsElementKind.DOMAIN_CLASS, expected, offset, "findAllByNameBetween", false); } protected void checkProposalApplication(String name, String contents, GrailsElementKind kind, String expected, int completionOffset, String proposalName, boolean isType) throws Exception { ICompletionProposal[] proposals = createProposalsAtOffset(name, contents, kind, completionOffset); ICompletionProposal firstProposal = findFirstProposal(proposals, proposalName, isType); if (firstProposal == null) { fail("Expected at least one proposal, but found none"); } applyProposalAndCheck(new Document(contents), firstProposal, expected); avoidUIThreadStarvation(); } @Override protected IPath createGenericProject() throws Exception { if (genericProjectExists()) { return env.getProject("Project").getFullPath(); } IPath projectPath = super.createGenericProject(); IProject project = env.getProject(projectPath); MockGrailsTestProjectUtils.mockGrailsProject(project, grailsVersion); fullBuild(projectPath); env.addPackageFragmentRoot(projectPath, "grails-app/services"); //$NON-NLS-1$ env.addPackageFragmentRoot(projectPath, "grails-app/domain"); //$NON-NLS-1$ env.addPackageFragmentRoot(projectPath, "grails-app/controllers"); //$NON-NLS-1$ env.addPackageFragmentRoot(projectPath, "grails-app/taglib"); //$NON-NLS-1$ env.setOutputFolder(projectPath, "bin"); //$NON-NLS-1$ return projectPath; } protected ICompletionProposal[] createProposalsAtOffset(String name, String contents, GrailsElementKind kind, int completionOffset) throws Exception { GroovyCompilationUnit unit = createGrailsElement(kind, name, contents); fullBuild(); unit.becomeWorkingCopy(null); return super.createProposalsAtOffset(unit, completionOffset); } protected GroovyCompilationUnit createGrailsElement(GrailsElementKind kind, String name, String contents) throws Exception { String path = null; switch (kind) { case DOMAIN_CLASS: path = "grails-app/domain"; break; case CONTROLLER_CLASS: path = "grails-app/controllers"; break; case TAGLIB_CLASS: path = "grails-app/taglib"; break; case SERVICE_CLASS: path = "grails-app/services"; break; default: fail("Invalid element kind to be created: " + kind); break; } IPath genericProject = createGenericProject(); IProject project = env.getProject(genericProject); IPath pathToClass = project.getFile(path).getFullPath().append(name + ".groovy"); IFile file = env.getWorkspace().getRoot().getFile(pathToClass); if (file.exists()) { setContents(file, contents); } else { env.addGroovyClass(pathToClass.removeLastSegments(1), "", name, contents); } return (GroovyCompilationUnit) JavaCore.create(file); } protected void setContents(IFile orig, String newContents) throws Exception { InputStream stream = new ByteArrayInputStream(newContents.getBytes()); orig.setContents(stream, true, false, null); } }