/** * Copyright (C) 2010-2017 Gordon Fraser, Andrea Arcuri and EvoSuite * contributors * * This file is part of EvoSuite. * * EvoSuite is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3.0 of the License, or * (at your option) any later version. * * EvoSuite is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with EvoSuite. If not, see <http://www.gnu.org/licenses/>. */ package org.evosuite.eclipse.popup.actions; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.ArrayList; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceRuleFactory; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jdt.core.IClasspathContainer; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.refactoring.IJavaRefactorings; import org.eclipse.jdt.core.refactoring.descriptors.RenameJavaElementDescriptor; import org.eclipse.jdt.junit.JUnitCore; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.Refactoring; import org.eclipse.ltk.core.refactoring.RefactoringContribution; import org.eclipse.ltk.core.refactoring.RefactoringCore; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.swt.widgets.Shell; import org.evosuite.Properties; import org.evosuite.eclipse.Activator; import org.osgi.framework.Bundle; /** * @author Gordon Fraser * */ public abstract class TestGenerationAction extends AbstractHandler { private static String EVOSUITE_CP = null; protected Shell shell; /* (non-Javadoc) * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction) */ //@Override //public abstract void run(IAction action); @Override public abstract Object execute(ExecutionEvent event) throws ExecutionException; /** * Main action: check if evosuite-tests exists, and add test generation job * * @param target */ public void generateTests(IResource target) { Boolean disabled = System.getProperty("evosuite.disable") != null; // && System.getProperty("evosuite.disable").equals("1") if ( disabled ) { MessageDialog.openInformation(shell, "Sorry!", "The EvoSuite Plugin is disabled :("); return; } System.out.println("EvoSuite Plugin is enabled"); System.out.println("[TestGenerationAction] Generating tests for " + target.toString()); IFolder folder = target.getProject().getFolder("evosuite-tests"); if (!folder.exists()) { // Create evosuite-tests directory and add as source folder try { folder.create(false, true, null); IJavaProject jProject = JavaCore.create(target.getProject()); IPackageFragmentRoot root = jProject.getPackageFragmentRoot(folder); IClasspathEntry[] oldEntries = jProject.getRawClasspath(); IClasspathEntry[] newEntries = new IClasspathEntry[oldEntries.length + 1]; System.arraycopy(oldEntries, 0, newEntries, 0, oldEntries.length); newEntries[oldEntries.length] = JavaCore.newSourceEntry(root.getPath()); jProject.setRawClasspath(newEntries, null); } catch (Throwable t) { t.printStackTrace(); } } addTestJob(target); } /** * Add a new test generation job to the job queue * * @param target */ protected void addTestJob(final IResource target) { IJavaElement element = JavaCore.create(target); if (element == null) { return; } IJavaElement packageElement = element.getParent(); String packageName = packageElement.getElementName(); final String targetClass = (!packageName.isEmpty() ? packageName + "." : "") + target.getName().replace(".java", "").replace(File.separator, "."); System.out.println("* Scheduling new automated job for " + targetClass); final String targetClassWithoutPackage = target.getName().replace(".java", ""); final String suiteClassName = targetClass + Properties.JUNIT_SUFFIX; final String suiteFileName = target.getProject().getLocation() + "/evosuite-tests/" + suiteClassName.replace('.', File.separatorChar) + ".java"; System.out.println("Checking for " + suiteFileName); File suiteFile = new File(suiteFileName); Job job = null; if (suiteFile.exists()) { MessageDialog dialog = new MessageDialog( shell, "Existing test suite found", null, // image "A test suite for class \"" + targetClass + "\" already exists. EvoSuite will overwrite this test suite. Do you really want to proceed?", MessageDialog.QUESTION_WITH_CANCEL, new String[] { "Overwrite", "Extend", "Rename Original", "Cancel" }, 0); int returnCode = dialog.open(); // 0 == overwrite // 1 == extend if(returnCode == 1) { IWorkspaceRoot wroot = target.getWorkspace().getRoot(); IResource suiteResource = wroot.getFileForLocation(new Path(suiteFileName)); job = new TestExtensionJob(shell, suiteResource, targetClass, suiteClassName); } else if (returnCode == 2) { // 2 == Rename renameSuite(target, packageName, targetClassWithoutPackage + Properties.JUNIT_SUFFIX + ".java"); } else if (returnCode > 2) { // Cancel return; } } if(job == null) job = new TestGenerationJob(shell, target, targetClass, suiteClassName); job.setPriority(Job.SHORT); IResourceRuleFactory ruleFactory = ResourcesPlugin.getWorkspace().getRuleFactory(); ISchedulingRule rule = ruleFactory.createRule(target.getProject()); //IFolder folder = proj.getFolder(ResourceUtil.EVOSUITE_FILES); job.setRule(rule); job.setUser(true); job.schedule(); // start as soon as possible } /** * If we move away an old EvoSuite test suite for a class then we rename it * to <name><num>.java. * * @param name * @param cus * @return */ public String getNewName(String name, ICompilationUnit[] cus) { int num = 0; String className = name.replace(".java", ""); String newName = className + num + ".java"; boolean found = true; while (found) { found = false; for (ICompilationUnit cu : cus) { if (cu.getElementName().equals(newName)) { num++; newName = className + num + ".java"; found = true; break; } } } return newName; } /** * If there already exists a test suite for the chosen target class, then * renaming the old test suite is one of the options the user can choose * * @param suite * @param packageName * @param suitePath */ public void renameSuite(final IResource suite, String packageName, String suitePath) { System.out.println("Renaming " + suitePath + " in package " + packageName); IPackageFragment[] packages; try { packages = JavaCore.create(suite.getProject()).getPackageFragments(); System.out.println("Packages found: "+packages.length); for (IPackageFragment f : packages) { if ((f.getKind() == IPackageFragmentRoot.K_SOURCE) && (f.getElementName().equals(packageName))) { for (ICompilationUnit cu : f.getCompilationUnits()) { String cuName = cu.getElementName(); System.out.println("targetPath = " + suitePath); System.out.println("cuName = " + cuName); if (cuName.equals(suitePath)) { RefactoringContribution contribution = RefactoringCore.getRefactoringContribution(IJavaRefactorings.RENAME_COMPILATION_UNIT); RenameJavaElementDescriptor descriptor = (RenameJavaElementDescriptor) contribution.createDescriptor(); descriptor.setProject(cu.getResource().getProject().getName()); String newName = getNewName(cuName, f.getCompilationUnits()); System.out.println("New name for Test Suite: " + newName); descriptor.setNewName(newName); // new name for a Class descriptor.setJavaElement(cu); RefactoringStatus status = new RefactoringStatus(); try { Refactoring refactoring = descriptor.createRefactoring(status); IProgressMonitor monitor = new NullProgressMonitor(); refactoring.checkInitialConditions(monitor); refactoring.checkFinalConditions(monitor); Change change = refactoring.createChange(monitor); change.perform(monitor); } catch (CoreException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } } } } } catch (JavaModelException e1) { e1.printStackTrace(); } } /** * If we generate JUnit tests, we need to make sure that JUnit is on the * classpath of the project, otherwise we will see compile errors * * @param project */ public void fixJUnitClassPath(IJavaProject project) { IPath junitPath = JUnitCore.JUNIT4_CONTAINER_PATH; boolean hasJUnit = false; boolean hasEvoSuite = false; boolean hasOldEvoSuite = false; try { Path containerPath = new Path("org.evosuite.eclipse.classpathContainerInitializer"); IClasspathContainer container = JavaCore.getClasspathContainer(containerPath, project); System.out.println("EvoSuite JAR at: " + container.getPath().toOSString()); IClasspathEntry[] oldEntries = project.getRawClasspath(); ArrayList<IClasspathEntry> newEntries = new ArrayList<IClasspathEntry>(oldEntries.length + 1); IClasspathEntry cpentry = JavaCore.newContainerEntry(junitPath); for (int i = 0; i < oldEntries.length; i++) { IClasspathEntry curr = oldEntries[i]; // Check if JUnit is already in the build path if (curr.getEntryKind() == IClasspathEntry.CPE_CONTAINER) { IPath path = curr.getPath(); if (path.equals(cpentry.getPath())) { hasJUnit = true; } if (path.equals(container.getPath())) { hasEvoSuite = true; } } else if (curr.getEntryKind() == IClasspathEntry.CPE_LIBRARY) { // Check for older EvoSuite entries IPath path = curr.getPath(); if (path.toFile().getName().equals(Activator.EVOSUITE_JAR)) { if (path.equals(container.getPath())) { System.out.println("Is current evosuite!"); hasEvoSuite = true; } else { System.out.println("Is NOT current evosuite!"); hasOldEvoSuite = true; continue; } } if (path.equals(cpentry.getPath())) { hasJUnit = true; } if (path.equals(container.getPath())) { hasEvoSuite = true; } } if (curr != null) { newEntries.add(curr); } } if (hasJUnit && hasEvoSuite && !hasOldEvoSuite) { return; } // add the entry if (!hasJUnit) { newEntries.add(cpentry); } if (!hasEvoSuite && container != null) { for (IClasspathEntry entry : container.getClasspathEntries()) { newEntries.add(entry); } } System.out.println("New classpath: " + newEntries); // newEntries.add(JavaCore.newContainerEntry(EvoSuiteClasspathContainer.ID)); // Convert newEntries to an array IClasspathEntry[] newCPEntries = newEntries.toArray(new IClasspathEntry[newEntries.size()]); project.setRawClasspath(newCPEntries, null); } catch (JavaModelException e) { e.printStackTrace(); } } /** * This is tricky because the evosuite jar in the deployed plugin is * contained in the plugin jar, but for the client process we need it * explicitly on the classpath. Hence, if we see that the evosuite jar is in * another jar, we extract it to a temporary location * * @return Location of the jar file */ public static String getEvoSuiteJar() { if (EVOSUITE_CP != null) return EVOSUITE_CP; Bundle bundle = Platform.getBundle(Activator.EVOSUITE_CORE_BUNDLE); URL url = bundle.getEntry(Activator.EVOSUITE_JAR); try { URL evosuiteLib = FileLocator.resolve(url); System.out.println("Evosuite JAR is at " + evosuiteLib.getPath()); if (evosuiteLib.getPath().startsWith("file")) { System.out.println("Need to extract jar"); EVOSUITE_CP = setupJar(evosuiteLib); } else { System.out.println("Don't need to extract jar"); EVOSUITE_CP = evosuiteLib.getFile(); } } catch (Exception e) { e.printStackTrace(); } return EVOSUITE_CP; } /** * Performs the actual extraction of the jar file to a temporary directory * * @param evosuiteLib * @return * @throws IOException */ protected static String setupJar(URL evosuiteLib) throws IOException { String tmpDir = System.getProperty("java.io.tmpdir"); String jarName = tmpDir + File.separator + Activator.EVOSUITE_JAR; System.out.println("Copying jar file to " + jarName); File tempJar = new File(jarName); writeFile(evosuiteLib.openStream(), new File(jarName)); return tempJar.getPath(); } /** * Helper function to copy files * * @param in * @param dest */ private static void writeFile(InputStream in, File dest) { try { dest.deleteOnExit(); System.out.println("Creating file: " + dest.getPath()); if (!dest.exists()) { OutputStream out = new FileOutputStream(dest); byte[] buf = new byte[1024]; int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } out.close(); } in.close(); } catch (IOException e) { e.printStackTrace(); } } /** * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart) */ // @Override // public void setActivePart(IAction action, IWorkbenchPart targetPart) { // shell = targetPart.getSite().getShell(); // } }