package com.sap.furcas.ide.projectwizard.test; import static org.junit.Assert.fail; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Platform; import org.junit.Test; import org.osgi.framework.Bundle; import com.sap.furcas.ide.projectwizard.util.CodeGenerationException; import com.sap.furcas.ide.projectwizard.util.ProjectInfo; import com.sap.furcas.ide.projectwizard.util.SourceCodeFactory; import com.sap.furcas.ide.projectwizard.wizards.FurcasWizard; import com.sun.tools.javac.Main; /** * This class includes the test method <code>GeneratedClassesTest</code> and provides methods to generate the classes * (ParserFactory, Activator, Editor, ...) as generated by the projectwizard and tries to build them making sure that * their dependencies are still up to date and working. It fails if one of the classes can't compile. * * @author Frederik Petersen (D054528) * @author Axel Uhl (D043530) * */ public class GeneratedClassesTest { private static final String BIN_DIR_NAME = "bin"; private final ArrayList<String> nonWorkspacePlugins = new ArrayList<String>(); private final ArrayList<String> workspacePlugins = new ArrayList<String>(); /** * Maps plain bundle names (e.g., "org.eclipse.emf") to a {@link File} location in the workspace in case * a (sub)directory by the single name given by the key exists in the workspace that contains a "bin" folder. * If <code>null</code>, the workspace hasn't been analyzed yet. */ private Map<String, File> workspacePluginCandidates; /** * This test method calls the other methods in this class to generate, compile and clean the java classes. */ @Test public void compileGeneratedClasses() throws Exception { ProjectInfo pi = new ProjectInfo(); configureProjectInfo(pi); FurcasWizard wizard = new FurcasWizard(); wizard.structuredProcess(pi, new NullProgressMonitor()); if (wizard.isHadError()) { fail("Wizard has ERRORs. See console output for details."); } IWorkspace workspace = ResourcesPlugin.getWorkspace(); IProject project = workspace.getRoot().getProject(pi.getProjectName()); try { compileClasses(project); } finally { project.delete(true, new NullProgressMonitor()); } } private void compileClasses(IProject project) throws IOException { ByteArrayOutputStream errByteStream = new ByteArrayOutputStream(); PrintStream systemErrOld = redirectSystemErrTo(errByteStream); String requiredBundles = getRequiredBundles(); List<File> filesToCompile = findJavaSources(Platform.getLocation().append(project.getFullPath()).toOSString()); try { int success = Main.compile(composeCompilationOptions(filesToCompile, requiredBundles)); if (success != 0) { fail("Parser compilation failed with code '" + success + "'. Messages: \n" + errByteStream.toString()+"\nClasspath was: "+requiredBundles); } } finally { restoreOldSystemErr(systemErrOld); cleanGenerationFolder(filesToCompile); } } private List<File> findJavaSources(String folderWithFilesToCompile) { File dir = new File(folderWithFilesToCompile); List<File> files = new ArrayList<File>(); return findJavaSourcesRecursively(dir, files); } private List<File> findJavaSourcesRecursively(File dir, List<File> files) { for (File file : dir.listFiles()) { if (file.getAbsolutePath().endsWith(".java")) { files.add(file); } else if (file.isDirectory()) { findJavaSourcesRecursively(file, files); } } return files; } private String[] composeCompilationOptions(List<File> filesToCompile, String requiredBundles) { String[] compilationOptions = new String[filesToCompile.size() + 2]; for (int i=0; i<filesToCompile.size(); i++) { compilationOptions[i] = filesToCompile.get(i).getPath(); } compilationOptions[compilationOptions.length-2] = "-cp"; compilationOptions[compilationOptions.length-1] = requiredBundles; return compilationOptions; } /** * This plugin uses variables <code>nonWorkspacePlugins</code> and <code>workspacePlugins</code> to build a String containing * the classpath for compiling the java classes generated by the wizard. * * @return The classpath for the compilation process. */ private String getRequiredBundles() throws IOException, IllegalArgumentException, SecurityException { getManifestPluginEntries(); String eclipsePath; if (System.getProperty("eclipse.location")!=null) { eclipsePath = System.getProperty("eclipse.location"); } else { eclipsePath = System.getProperty("target.location"); } StringBuffer requiredBundles = new StringBuffer("./lib/org.eclipse.swt.gtk.linux.x86_64_3.6.1.v3655c.jar" + File.pathSeparator + "./lib/static"); for (String plugin : workspacePlugins) { requiredBundles.append(File.pathSeparator + getWorkspacePluginCandidates().get(plugin) + File.separator + BIN_DIR_NAME); } // The non-workspace bundles are tricky to find, particularly when executing in a Maven // environment. There, the bundles of the executing JUnit plugin test shell are obtained // from the Maven repository under .m2/repository/... and may have slightly different versions // as compared to the Eclipse instance installed under ${eclipse.location}. Therefore, the // lookup should first check ${target.location} before ${eclipse.location}. // Furthermore, the bundle JAR files may be located in some nested directory structure, such // as .m2/repository/p2/osgi/bundle/org.eclipse.ui/3.6.2.M20110203-1100 for the bundle // org.eclipse.ui-3.6.2.M20110203-1100.jar. The bundle location can be determined by // Bundle.getLocation() and gives a hint at the physical location of the JAR file. Trailing // slashes have to be removed. Object[] bundles = nonWorkspacePlugins.toArray(); for (int i = 0; i < bundles.length; i++) { String bundlePath = null; Bundle bundle = Platform.getBundle((String) bundles[i]); if (bundle == null) { System.err.println("Unable to find bundle "+bundles[i]); } else { try { System.out.println("Trying to find bundle "+bundle); Object bundleData = bundle.getClass().getMethod("getBundleData").invoke(bundle); System.out.println(" got bundle data "+bundleData); Object bundleFile = bundleData.getClass().getMethod("getBundleFile").invoke(bundleData); System.out.println(" got bundle file "+bundleFile); File baseFile = (File) bundleFile.getClass().getMethod("getBaseFile").invoke(bundleFile); System.out.println(" got base file "+baseFile); bundlePath = baseFile.getCanonicalPath(); } catch (Exception e) { e.printStackTrace(); // it didn't work; try something else } if (bundlePath == null) { File bundleJar = findBundleJar(bundle); if (bundleJar != null) { bundlePath = bundleJar.getCanonicalPath(); } else { String bundleJarName = bundle.toString().split(" ")[0] + ".jar"; if (eclipsePath.contains("/")) { if (eclipsePath.endsWith("/")) { bundlePath = eclipsePath + "plugins/" + bundleJarName; } else { bundlePath = eclipsePath + "/plugins/" + bundleJarName; } } else { if (eclipsePath.endsWith("\\")) { bundlePath = eclipsePath + "plugins\\" + bundleJarName; } else { bundlePath = eclipsePath + "\\plugins\\" + bundleJarName; } } } } File f = new File(bundlePath); try { System.out.print("Canonical bundle path: "+f.getCanonicalPath()); System.out.println(f.exists() ? " EXISTS" : " DOES NOT EXIST"); } catch (IOException e) { e.printStackTrace(); } requiredBundles.append(File.pathSeparator + bundlePath); } } return requiredBundles.toString(); } private File findBundleJar(Bundle bundle) { File searchStartDir = new File(System.getProperty("target.location")); return findRecursively(searchStartDir, bundle.toString()); } private File findRecursively(File d, String string) { if (d.exists() && d.isDirectory()) { if (Arrays.asList(d.list()).contains(string)) { return new File(d, string); } else { for (File entry : d.listFiles()) { File result = findRecursively(entry, string); if (result != null) { return result; } } } } return null; } /** * Gets rid of all generated .java and .class files */ private void cleanGenerationFolder(List<File> filesToCompile) { for (File source : filesToCompile) { source.delete(); File compiled = new File(source.getPath().replace(".java", ".class")); if (compiled.exists()) { compiled.delete(); } } } /** * Gives default values to the ProjectInfo instance. * * @param pi */ public static void configureProjectInfo(ProjectInfo pi) { pi.setLoadMetamodel(false); pi.setClassName("ExampleClass"); pi.setFileExtension("dsl"); pi.setLanguageName("Mydsl"); pi.setModelPath("/my.dsl.metamodel/model/Mydsl.ecore"); pi.setNsURI("http://www.example.com/mydsl"); pi.setProjectName("my.dsl"); pi.setAutoResolve(false); } private static PrintStream redirectSystemErrTo(ByteArrayOutputStream errByteStream) { PrintStream originalSystemErr = System.err; System.setErr(new PrintStream(errByteStream)); return originalSystemErr; } private static void restoreOldSystemErr(PrintStream systemErr) { System.setErr(systemErr); } /** * Generates the contents of the manifest as generated by the wizard and reads the entries * into the variables: <code>nonWorkspacePlugins</code> and <code>workspacePlugins</code>. */ private void getManifestPluginEntries() { String[] lines = null; SourceCodeFactory codeFactory = new SourceCodeFactory(); ProjectInfo pi = new ProjectInfo(); configureProjectInfo(pi); try { String contents = codeFactory.createManifest(pi,""); lines = contents.split("\n"); } catch (CodeGenerationException e) { e.printStackTrace(); } for (String i : lines) { if (i.contains(";")) { i = i.split(";")[0]; } if (i.startsWith("Require") || i.startsWith(" ")) { String modifier = i.replaceAll("Require-Bundle: ", "").trim(); modifier = modifier.replace(",", ""); if (isBundleInWorkspace(modifier)) { workspacePlugins.add(modifier); } else { nonWorkspacePlugins.add(modifier); } } } } private boolean isBundleInWorkspace(String bundleName) { File bundleDir = getWorkspacePluginCandidates().get(bundleName); return bundleDir != null; } private Map<String, File> getWorkspacePluginCandidates() { if (workspacePluginCandidates == null) { workspacePluginCandidates = determineWorkspacePluginCandidates(); } return workspacePluginCandidates; } private Map<String, File> determineWorkspacePluginCandidates() { Map<String, File> result = new HashMap<String, File>(); File workspaceRoot = new File("../.."); // assuming we're running in DSLEngineering/*wizard.test assert(workspaceRoot.exists() && workspaceRoot.isDirectory()); findRecursively(workspaceRoot, result); return result; } private boolean containsBin(File bundleDir) { assert(bundleDir.exists() && bundleDir.isDirectory()); for (File f : bundleDir.listFiles()) { if (f.getName().equals(BIN_DIR_NAME)) { return true; } } return false; } private void findRecursively(File d, Map<String, File> result) { if (d.exists() && d.isDirectory()) { if (containsBin(d)) { result.put(d.getName(), d); } else { for (File entry : d.listFiles()) { findRecursively(entry, result); } } } } }