/*******************************************************************************
* 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.refactoring.test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.codehaus.jdt.groovy.model.GroovyNature;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.TypeNameRequestor;
import org.eclipse.jdt.core.tests.compiler.regression.Requestor;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.CheckConditionsOperation;
import org.eclipse.ltk.core.refactoring.CreateChangeOperation;
import org.eclipse.ltk.core.refactoring.IUndoManager;
import org.eclipse.ltk.core.refactoring.PerformChangeOperation;
import org.eclipse.ltk.core.refactoring.Refactoring;
import org.eclipse.ltk.core.refactoring.RefactoringCore;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.grails.ide.eclipse.commands.test.AbstractCommandTest;
import org.grails.ide.eclipse.core.GrailsCoreActivator;
import org.grails.ide.eclipse.core.internal.GrailsNature;
import org.grails.ide.eclipse.core.internal.plugins.GrailsCore;
import org.grails.ide.eclipse.core.model.GrailsVersion;
import org.springsource.ide.eclipse.commons.frameworks.test.util.ACondition;
import org.springsource.ide.eclipse.commons.tests.util.StsTestUtil;
/**
* @author Kris De Volder
* @since 2.7
*/
public abstract class GrailsRefactoringTest extends AbstractCommandTest {
public static String getContents(IFile file) throws IOException, CoreException {
return getContents(file.getContents());
}
public static String getContents(InputStream in) throws IOException {
BufferedReader br= new BufferedReader(new InputStreamReader(in));
StringBuffer sb= new StringBuffer(300);
try {
int read= 0;
while ((read= br.read()) != -1)
sb.append((char) read);
} finally {
br.close();
}
return sb.toString();
}
/**
* Line-based version of junit.framework.Assert.assertEquals(String, String)
* without considering line delimiters.
* @param expected the expected value
* @param actual the actual value
*/
public static void assertEqualLines(String expected, String actual) {
assertEquals("", lineStrip(expected.trim()), lineStrip(actual.trim()));
}
// /**
// * Line-based version of junit.framework.Assert.assertEquals(String, String, String)
// * without considering line delimiters.
// * @param message the message
// * @param expected the expected value
// * @param actual the actual value
// */
// public static void assertEqualLines(String message, String expected, String actual) {
//// String[] expectedLines= Strings.convertIntoLines(expected);
//// String[] actualLines= Strings.convertIntoLines(actual);
////
//// String expected2= (expectedLines == null ? null : Strings.concatenate(expectedLines, "\n"));
//// String actual2= (actualLines == null ? null : Strings.concatenate(actualLines, "\n"));
// assertEquals(message, expected., actual2);
// }
/**
* Remove trailing white space from all lines in 'content'.
* Also removes lines that only contain whitespace.
**/
private static String lineStrip(String content) {
Pattern trailingWhiteSpace = Pattern.compile("(\\s)+$", Pattern.MULTILINE);
Matcher matcher = trailingWhiteSpace.matcher(content);
return matcher.replaceAll("");
}
protected Change undo = null; //Will be set by 'performRefactoring' if an undo is available.
/**
* Checks a bunch of stuff about the imported test project.
*
* @throws Throwable
*/
protected void checkImportedProject() throws Exception {
//Check project config, like classpath related stuff.
// The config may not be right initially... but should eventually become correct as a background
// refresh dependency job should get scheduled.
// ACondition
new ACondition() {
@Override
public boolean test() throws Exception {
assertJobManagerIdle(); //Wait for jobs... like Refresh dependencies to all complete.
System.out.println("Checking project config...");
IJavaProject javaProject = JavaCore.create(project);
assertDefaultOutputFolder(javaProject);
assertTrue(project.hasNature(JavaCore.NATURE_ID)); // Should have Java Nature at this point
assertTrue(GroovyNature.hasGroovyNature(project)); // Should also have Groovy nature
assertTrue(GrailsNature.isGrailsAppProject(project)); // Should look like a Grails app to grails tooling
///////////////////////////////////
// Check resolved classpath stuff
IClasspathEntry[] classPath = javaProject.getResolvedClasspath(false);
// A whole bunch of libraries should be there, check for just a few of those
assertClassPathEntry(IClasspathEntry.CPE_LIBRARY, "/jsse.jar", classPath);
assertClassPathEntry(IClasspathEntry.CPE_LIBRARY, "grails-core", classPath);
assertClassPathEntry(IClasspathEntry.CPE_LIBRARY, "grails-bootstrap", classPath);
assertClassPathEntry(IClasspathEntry.CPE_LIBRARY, "groovy-all", classPath);
// assertClassPathEntry(IClasspathEntry.CPE_LIBRARY, "servlet-api", classPath);
// System.out.println(">>>Resolved classpath");
// for (IClasspathEntry entry : classPath) {
// System.out.println(kindString(entry.getEntryKind())+": "+entry.getPath());
// }
// System.out.println("<<<Resolved classpath");
///////////////////////////////////
// Check raw classpath stuff
classPath = javaProject.getRawClasspath();
// for (IClasspathEntry classpathEntry : classPath) {
// System.out.println(kindString(classpathEntry.getEntryKind())+": "+classpathEntry.getPath());
// }
//The usual source folders:
assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "src/java", classPath);
assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "src/groovy", classPath);
assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "grails-app/conf", classPath);
assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "grails-app/controllers", classPath);
assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "grails-app/domain", classPath);
assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "grails-app/services", classPath);
assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "grails-app/taglib", classPath);
assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "test/integration", classPath);
assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "test/unit", classPath);
//The classpath containers for Java and Grails
assertClassPathEntry(IClasspathEntry.CPE_CONTAINER, "org.eclipse.jdt.launching.JRE_CONTAINER", classPath);
assertClassPathEntry(IClasspathEntry.CPE_CONTAINER, "org.grails.ide.eclipse.core.CLASSPATH_CONTAINER", classPath);
//Installed plugins source folders
assertDefaultPluginSourceFolders(project);
System.out.println("Checking project config => OK!");
return true;
}
}.waitFor(4*60000);
}
protected IType getType(String fqName) throws JavaModelException {
IJavaProject javaProject = JavaCore.create(project);
IType target = javaProject.findType(fqName);
assertNotNull("Couldn't find type: "+fqName, target);
return target;
}
@Override
protected void setUp() throws Exception {
super.setUp();
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
}
protected void importZippedProject(String projectName) throws Exception {
final URL zipFileURL = getProjectZip(projectName, GrailsVersion.getDefault());
importProject(zipFileURL, projectName);
}
protected void assertFile(String path, String contents) throws IOException,
CoreException {
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(path));
assertEqualLines(contents, getContents(file));
}
public void assertFile(String path) {
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(path));
assertTrue(file.exists());
}
public void assertFileDeleted(String path) {
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(path));
assertFalse("File exists: "+file, file.exists());
}
public static void performDummySearch(IJavaElement element) throws Exception{
new SearchEngine().searchAllTypeNames(
null,
SearchPattern.R_EXACT_MATCH,
"XXXXXXXXX".toCharArray(), // make sure we search a concrete name. This is faster according to Kent
SearchPattern.R_EXACT_MATCH,
IJavaSearchConstants.CLASS,
SearchEngine.createJavaSearchScope(new IJavaElement[]{element}),
new Requestor(),
IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH,
null);
}
private static class Requestor extends TypeNameRequestor {
}
public static abstract class RefactoringJob extends Job {
public RefactoringJob(String name) {
super(name);
}
public RefactoringStatus refactoringStatus;
protected abstract RefactoringStatus perform(IProgressMonitor mon) throws Throwable;
@Override
final protected IStatus run(IProgressMonitor mon) {
try {
this.refactoringStatus = perform(mon);
} catch (Throwable e) {
return new Status(IStatus.ERROR, GrailsCoreActivator.PLUGIN_ID, "Problem performing refactoring", e);
}
if (this.refactoringStatus==null) {
return new Status(IStatus.ERROR, GrailsCoreActivator.PLUGIN_ID, "Refactoring status was not set");
} else {
if (!this.refactoringStatus.isOK()) {
GrailsCoreActivator.log(refactoringStatus.toString());
return new Status(IStatus.ERROR, GrailsCoreActivator.PLUGIN_ID, refactoringStatus.toString());
}
}
return Status.OK_STATUS;
}
}
protected final RefactoringStatus performRefactoring(final Refactoring ref, final boolean providesUndo,
final boolean performOnFail) throws Exception {
performDummySearch(); //Why?
RefactoringJob job = new RefactoringJob("Perform Refactoring") {
@Override
protected RefactoringStatus perform(IProgressMonitor monitor) throws Throwable {
IUndoManager undoManager= getUndoManager();
final CreateChangeOperation create= new CreateChangeOperation(
new CheckConditionsOperation(ref, CheckConditionsOperation.ALL_CONDITIONS),
RefactoringStatus.FATAL);
final PerformChangeOperation perform= new PerformChangeOperation(create);
perform.setUndoManager(undoManager, ref.getName());
IWorkspace workspace= ResourcesPlugin.getWorkspace();
executePerformOperation(perform, workspace);
RefactoringStatus status = create.getConditionCheckingStatus();
if (!status.hasError() && !performOnFail)
return status;
assertTrue("Change wasn't executed", perform.changeExecuted());
undo = perform.getUndoChange();
if (providesUndo) {
assertNotNull("Undo doesn't exist", undo);
assertTrue("Undo manager is empty", undoManager.anythingToUndo());
} else {
assertNull("Undo manager contains undo but shouldn't", undo);
}
return status;
}
};
waitForWorkspaceJob(job);
assertTrue(job.getResult().isOK());
return job.refactoringStatus;
}
private void performDummySearch() throws Exception {
performDummySearch(JavaCore.create(project));
}
protected void undoLastRefactoring() throws CoreException {
assertNotNull("No undo found", undo);
undo.perform(new NullProgressMonitor());
}
protected IUndoManager getUndoManager() {
IUndoManager undoManager= RefactoringCore.getUndoManager();
undoManager.flush();
return undoManager;
}
protected void executePerformOperation(final PerformChangeOperation perform, IWorkspace workspace)
throws CoreException {
workspace.run(perform, new NullProgressMonitor());
}
protected void deleteResource(String path) throws CoreException {
IFile rsrc = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(path));
rsrc.delete(true, new NullProgressMonitor());
}
}