package org.rubypeople.rdt.core.tests; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.URL; import junit.framework.TestCase; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IStorage; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceDescription; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.IWorkspaceRunnable; 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.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.jobs.Job; import org.rubypeople.rdt.core.ILoadpathEntry; import org.rubypeople.rdt.core.IRubyProject; import org.rubypeople.rdt.core.IRubyScript; import org.rubypeople.rdt.core.ISourceFolder; import org.rubypeople.rdt.core.ISourceFolderRoot; import org.rubypeople.rdt.core.RubyCore; import org.rubypeople.rdt.core.RubyModelException; import org.rubypeople.rdt.internal.core.LoadpathEntry; import org.rubypeople.rdt.internal.core.util.CharOperation; import org.rubypeople.rdt.internal.core.util.Util; public abstract class AbstractRubyModelTest extends TestCase { protected IRubyProject currentProject; protected String endChar = ","; public AbstractRubyModelTest(String name) { super(name); } @Override protected void setUp() throws Exception { // TODO Make it so this stuff is only run once per suite, not before every method super.setUp(); // ensure autobuilding is turned off IWorkspaceDescription description = getWorkspace().getDescription(); if (description.isAutoBuilding()) { description.setAutoBuilding(false); getWorkspace().setDescription(description); } } protected IRubyProject setUpRubyProject(final String projectName) throws CoreException, IOException { this.currentProject = setUpRubyProject(projectName, "1.8.4"); return this.currentProject; } protected String getPluginDirectoryPath() { try { URL platformURL = Platform.getBundle("org.rubypeople.rdt.core.tests").getEntry("/"); return new File(FileLocator.toFileURL(platformURL).getFile()).getAbsolutePath(); } catch (IOException e) { e.printStackTrace(); } return null; } public String getSourceWorkspacePath() { return getPluginDirectoryPath() + java.io.File.separator + "workspace"; } /** * Returns the IWorkspace this test suite is running on. */ public IWorkspace getWorkspace() { return ResourcesPlugin.getWorkspace(); } public IWorkspaceRoot getWorkspaceRoot() { return getWorkspace().getRoot(); } /** * Copy file from src (path to the original file) to dest (path to the destination file). */ public void copy(File src, File dest) throws IOException { // read source bytes byte[] srcBytes = this.read(src); if (convertToIndependantLineDelimiter(src)) { String contents = new String(srcBytes); contents = org.rubypeople.rdt.core.tests.util.Util.convertToIndependantLineDelimiter(contents); srcBytes = contents.getBytes(); } // write bytes to dest FileOutputStream out = new FileOutputStream(dest); out.write(srcBytes); out.close(); } public byte[] read(java.io.File file) throws java.io.IOException { int fileLength; byte[] fileBytes = new byte[fileLength = (int) file.length()]; java.io.FileInputStream stream = new java.io.FileInputStream(file); int bytesRead = 0; int lastReadSize = 0; while ((lastReadSize != -1) && (bytesRead != fileLength)) { lastReadSize = stream.read(fileBytes, bytesRead, fileLength - bytesRead); bytesRead += lastReadSize; } stream.close(); return fileBytes; } public boolean convertToIndependantLineDelimiter(File file) { return file.getName().endsWith(".rb") || file.getName().endsWith(".rbw"); } /** * Copy the given source directory (and all its contents) to the given target directory. */ protected void copyDirectory(File source, File target) throws IOException { if (!target.exists()) { target.mkdirs(); } File[] files = source.listFiles(); if (files == null) return; for (int i = 0; i < files.length; i++) { File sourceChild = files[i]; String name = sourceChild.getName(); if (name.equals("CVS")) continue; File targetChild = new File(target, name); if (sourceChild.isDirectory()) { copyDirectory(sourceChild, targetChild); } else { copy(sourceChild, targetChild); } } } protected IRubyProject setUpRubyProject(final String projectName, String compliance) throws CoreException, IOException { // copy files in project from source workspace to target workspace String sourceWorkspacePath = getSourceWorkspacePath(); String targetWorkspacePath = getWorkspaceRoot().getLocation().toFile().getCanonicalPath(); copyDirectory(new File(sourceWorkspacePath, projectName), new File(targetWorkspacePath, projectName)); // ensure variables are set // setUpJCLClasspathVariables(compliance); // create project final IProject project = getWorkspaceRoot().getProject(projectName); IWorkspaceRunnable populate = new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { project.create(null); project.open(null); } }; getWorkspace().run(populate, null); IRubyProject rubyProject = RubyCore.create(project); return rubyProject; } /** * Batch deletion of projects */ protected void deleteProjects(final String[] projectNames) throws CoreException { ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { if (projectNames != null){ for (int i = 0, max = projectNames.length; i < max; i++){ if (projectNames[i] != null) deleteProject(projectNames[i]); } } } }, null); } protected void deleteProject(String projectName) throws CoreException { IProject project = this.getProject(projectName); if (project.exists() && !project.isOpen()) { // force opening so that project can be deleted without logging (see bug 23629) project.open(null); } deleteResource(project); } /** * Delete this resource. */ public void deleteResource(IResource resource) throws CoreException { int retryCount = 0; // wait 1 minute at most while (++retryCount <= 60) { if (!org.rubypeople.rdt.core.tests.util.Util.delete(resource)) { System.gc(); } } } protected IProject getProject(String project) { return getWorkspaceRoot().getProject(project); } /** * Wait for autobuild notification to occur */ public static void waitForAutoBuild() { boolean wasInterrupted = false; do { try { Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_BUILD, null); wasInterrupted = false; } catch (OperationCanceledException e) { e.printStackTrace(); } catch (InterruptedException e) { wasInterrupted = true; } } while (wasInterrupted); } protected void assertResourcesEqual(String message, String expected, Object[] resources) { sortResources(resources); StringBuffer buffer = new StringBuffer(); for (int i = 0, length = resources.length; i < length; i++) { if (resources[i] instanceof IResource) { buffer.append(((IResource) resources[i]).getFullPath().toString()); } else if (resources[i] instanceof IStorage) { buffer.append(((IStorage) resources[i]).getFullPath().toString()); } else if (resources[i] == null) { buffer.append("<null>"); } if (i != length-1)buffer.append("\n"); } if (!expected.equals(buffer.toString())) { System.out.print(org.rubypeople.rdt.core.tests.util.Util.displayString(buffer.toString(), 2)); System.out.println(this.endChar); } assertEquals( message, expected, buffer.toString() ); } protected void sortResources(Object[] resources) { Util.Comparer comparer = new Util.Comparer() { public int compare(Object a, Object b) { IResource resourceA = (IResource)a; IResource resourceB = (IResource)b; return resourceA.getFullPath().toString().compareTo(resourceB.getFullPath().toString()); } }; Util.sort(resources, comparer); } protected IFile getFile(String path) { return getWorkspaceRoot().getFile(new Path(path)); } protected IRubyProject createRubyProject(String projectName) throws CoreException { return this.createRubyProject(projectName, new String[] {""}, new String[] {"RUBY_LIB", "org.rubypeople.rdt.launching.RUBY_CONTAINER"}); } /* * Creates a Java project with the given source folders. * Add those on the project's loadpath. */ protected IRubyProject createRubyProject(String projectName, String[] sourceFolders) throws CoreException { return this.createRubyProject( projectName, sourceFolders, null/*no lib*/, null/*no inclusion pattern*/, null/*no exclusion pattern*/, null/*no project*/, null/*no inclusion pattern*/, null/*no exclusion pattern*/, null/*no exported project*/, null/*no inclusion pattern*/, null/*no exclusion pattern*/ ); } protected IRubyProject createRubyProject(String projectName, String[] sourceFolders, String[] libraries) throws CoreException { return this.createRubyProject( projectName, sourceFolders, libraries, null/*no inclusion pattern*/, null/*no exclusion pattern*/, null/*no project*/, null/*no inclusion pattern*/, null/*no exclusion pattern*/, null/*no exported project*/, null/*no inclusion pattern*/, null/*no exclusion pattern*/ ); } protected IRubyProject createRubyProject(String projectName, String[] sourceFolders, String[] libraries, String[] projects, boolean[] exportedProject) throws CoreException { return this.createRubyProject( projectName, sourceFolders, libraries, null/*no inclusion pattern*/, null/*no exclusion pattern*/, projects, null/*no inclusion pattern*/, null/*no exclusion pattern*/, exportedProject, null/*no inclusion pattern*/, null/*no exclusion pattern*/ ); } protected IRubyProject createRubyProject(String projectName, String[] sourceFolders, String[] libraries, String[] projects) throws CoreException { return createRubyProject( projectName, sourceFolders, libraries, null/*no inclusion pattern*/, null/*no exclusion pattern*/, projects, null/*no inclusion pattern*/, null/*no exclusion pattern*/, null/*no exported project*/, null/*no inclusion pattern*/, null/*no exclusion pattern*/ ); } protected IRubyProject createRubyProject(final String projectName, final String[] sourceFolders, final String[] libraries, final String[] projects, final boolean[] exportedProjects, final String[][] inclusionPatterns, final String[][] exclusionPatterns) throws CoreException { return this.createRubyProject( projectName, sourceFolders, libraries, null/*no inclusion pattern*/, null/*no exclusion pattern*/, projects, null/*no inclusion pattern*/, null/*no exclusion pattern*/, exportedProjects, inclusionPatterns, exclusionPatterns ); } protected IRubyProject createRubyProject( final String projectName, final String[] sourceFolders, final String[] libraries, final String[][] librariesInclusionPatterns, final String[][] librariesExclusionPatterns, final String[] projects, final String[][] projectsInclusionPatterns, final String[][] projectsExclusionPatterns, final boolean[] exportedProjects, final String[][] inclusionPatterns, final String[][] exclusionPatterns) throws CoreException { final IRubyProject[] result = new IRubyProject[1]; IWorkspaceRunnable create = new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { // create project createProject(projectName); // set ruby nature addRubyNature(projectName); // create loadpath entries IProject project = getWorkspaceRoot().getProject(projectName); IPath projectPath = project.getFullPath(); int sourceLength = sourceFolders == null ? 0 : sourceFolders.length; int libLength = libraries == null ? 0 : libraries.length; int projectLength = projects == null ? 0 : projects.length; ILoadpathEntry[] entries = new ILoadpathEntry[sourceLength+libLength+projectLength]; for (int i= 0; i < sourceLength; i++) { IPath sourcePath = new Path(sourceFolders[i]); int segmentCount = sourcePath.segmentCount(); if (segmentCount > 0) { // create folder and its parents IContainer container = project; for (int j = 0; j < segmentCount; j++) { IFolder folder = container.getFolder(new Path(sourcePath.segment(j))); if (!folder.exists()) { folder.create(true, true, null); } container = folder; } } // inclusion patterns IPath[] inclusionPaths; if (inclusionPatterns == null) { inclusionPaths = new IPath[0]; } else { String[] patterns = inclusionPatterns[i]; int length = patterns.length; inclusionPaths = new IPath[length]; for (int j = 0; j < length; j++) { String inclusionPattern = patterns[j]; inclusionPaths[j] = new Path(inclusionPattern); } } // exclusion patterns IPath[] exclusionPaths; if (exclusionPatterns == null) { exclusionPaths = new IPath[0]; } else { String[] patterns = exclusionPatterns[i]; int length = patterns.length; exclusionPaths = new IPath[length]; for (int j = 0; j < length; j++) { String exclusionPattern = patterns[j]; exclusionPaths[j] = new Path(exclusionPattern); } } // create source entry entries[i] = RubyCore.newSourceEntry( projectPath.append(sourcePath), inclusionPaths, exclusionPaths, LoadpathEntry.NO_EXTRA_ATTRIBUTES); } for (int i= 0; i < libLength; i++) { String lib = libraries[i]; // if (lib.startsWith("JCL")) { // try { // // ensure JCL variables are set // setUpJCLClasspathVariables(compliance); // } catch (IOException e) { // e.printStackTrace(); // } // } // // accessible files IPath[] accessibleFiles; if (librariesInclusionPatterns == null) { accessibleFiles = new IPath[0]; } else { String[] patterns = librariesInclusionPatterns[i]; int length = patterns.length; accessibleFiles = new IPath[length]; for (int j = 0; j < length; j++) { String inclusionPattern = patterns[j]; accessibleFiles[j] = new Path(inclusionPattern); } } // non accessible files IPath[] nonAccessibleFiles; if (librariesExclusionPatterns == null) { nonAccessibleFiles = new IPath[0]; } else { String[] patterns = librariesExclusionPatterns[i]; int length = patterns.length; nonAccessibleFiles = new IPath[length]; for (int j = 0; j < length; j++) { String exclusionPattern = patterns[j]; nonAccessibleFiles[j] = new Path(exclusionPattern); } } if (lib.indexOf(File.separatorChar) == -1 && lib.charAt(0) != '/' && lib.equals(lib.toUpperCase())) { // all upper case is a var char[][] vars = CharOperation.splitOn(',', lib.toCharArray()); entries[sourceLength+i] = RubyCore.newVariableEntry(new Path(new String(vars[0])), false); } else if (lib.startsWith("org.rubypeople.rdt")) { // container entries[sourceLength+i] = RubyCore.newContainerEntry(new Path(lib), false); } else { IPath libPath = new Path(lib); if (!libPath.isAbsolute() && libPath.segmentCount() > 0 && libPath.getFileExtension() == null) { project.getFolder(libPath).create(true, true, null); libPath = projectPath.append(libPath); } entries[sourceLength+i] = RubyCore.newLibraryEntry(libPath, false); } } for (int i= 0; i < projectLength; i++) { boolean isExported = exportedProjects != null && exportedProjects.length > i && exportedProjects[i]; // accessible files IPath[] accessibleFiles; if (projectsInclusionPatterns == null) { accessibleFiles = new IPath[0]; } else { String[] patterns = projectsInclusionPatterns[i]; int length = patterns.length; accessibleFiles = new IPath[length]; for (int j = 0; j < length; j++) { String inclusionPattern = patterns[j]; accessibleFiles[j] = new Path(inclusionPattern); } } // non accessible files IPath[] nonAccessibleFiles; if (projectsExclusionPatterns == null) { nonAccessibleFiles = new IPath[0]; } else { String[] patterns = projectsExclusionPatterns[i]; int length = patterns.length; nonAccessibleFiles = new IPath[length]; for (int j = 0; j < length; j++) { String exclusionPattern = patterns[j]; nonAccessibleFiles[j] = new Path(exclusionPattern); } } entries[sourceLength+libLength+i] = RubyCore.newProjectEntry(new Path(projects[i]), isExported); } // set classpath and output location IRubyProject javaProject = RubyCore.create(project); javaProject.setRawLoadpath(entries, null, null); result[0] = javaProject; } }; getWorkspace().run(create, null); return result[0]; } /* * Create simple project. */ protected IProject createProject(final String projectName) throws CoreException { final IProject project = getProject(projectName); IWorkspaceRunnable create = new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { project.create(null); project.open(null); } }; getWorkspace().run(create, null); return project; } protected void addRubyNature(String projectName) throws CoreException { IProject project = getWorkspaceRoot().getProject(projectName); IProjectDescription description = project.getDescription(); description.setNatureIds(new String[] {RubyCore.NATURE_ID}); project.setDescription(description, null); } /** * Returns the specified ruby script in the given project, root, and * source folder or <code>null</code> if it does not exist. */ public IRubyScript getRubyScript(String projectName, String rootPath, String packageName, String cuName) throws RubyModelException { ISourceFolder pkg= getSourceFolder(projectName, rootPath, packageName); if (pkg == null) { return null; } return pkg.getRubyScript(cuName); } /** * Returns the specified package fragment in the given project and root, or * <code>null</code> if it does not exist. * The rootPath must be specified as a project relative path. The empty * path refers to the default package fragment. */ public ISourceFolder getSourceFolder(String projectName, String rootPath, String packageName) throws RubyModelException { ISourceFolderRoot root= getSourceFolderRoot(projectName, rootPath); if (root == null) { return null; } return root.getSourceFolder(packageName); } /** * Returns the specified package fragment root in the given project, or * <code>null</code> if it does not exist. * If relative, the rootPath must be specified as a project relative path. * The empty path refers to the package fragment root that is the project * folder iteslf. * If absolute, the rootPath refers to either an external jar, or a resource * internal to the workspace */ public ISourceFolderRoot getSourceFolderRoot( String projectName, String rootPath) throws RubyModelException { IRubyProject project = getRubyProject(projectName); if (project == null) { return null; } IPath path = new Path(rootPath); if (path.isAbsolute()) { IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); IResource resource = workspaceRoot.findMember(path); ISourceFolderRoot root; if (resource == null) { // external jar root = project.getSourceFolderRoot(rootPath); } else { // resource in the workspace root = project.getSourceFolderRoot(resource); } return root; } else { ISourceFolderRoot[] roots = project.getSourceFolderRoots(); if (roots == null || roots.length == 0) { return null; } for (int i = 0; i < roots.length; i++) { ISourceFolderRoot root = roots[i]; if (!root.isExternal() && root.getUnderlyingResource().getProjectRelativePath().equals(path)) { return root; } } } return null; } /** * Returns the Ruby Project with the given name in this test * suite's model. This is a convenience method. */ public IRubyProject getRubyProject(String name) { IProject project = getProject(name); return RubyCore.create(project); } /* * Asserts that the given actual source (usually coming from a file content) is equal to the expected one. * Note that 'expected' is assumed to have the '\n' line separator. * The line separators in 'actual' are converted to '\n' before the comparison. */ protected void assertSourceEquals(String message, String expected, String actual) { if (actual == null) { assertEquals(message, expected, null); return; } actual = org.rubypeople.rdt.core.tests.util.Util.convertToIndependantLineDelimiter(actual); if (!actual.equals(expected)) { System.out.print(org.rubypeople.rdt.core.tests.util.Util.displayString(actual.toString(), 2)); System.out.println(this.endChar); } assertEquals(message, expected, actual); } protected IFolder createFolder(IPath path) throws CoreException { final IFolder folder = getWorkspaceRoot().getFolder(path); getWorkspace().run(new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { IContainer parent = folder.getParent(); if (parent instanceof IFolder && !parent.exists()) { createFolder(parent.getFullPath()); } folder.create(true, true, null); } }, null); return folder; } protected IRubyScript getRubyScript(String path) { return RubyCore.create(getFile(path)); } public static void waitUntilIndexesReady() { // TODO Find some way to wait until the indexes are ready from SymbolIndex/build process } public void deleteFile(File file) { int retryCount = 0; while (++retryCount <= 60) { // wait 1 minute at most if (org.rubypeople.rdt.core.tests.util.Util.delete(file)) { break; } } } protected void deleteFolder(IPath folderPath) throws CoreException { deleteResource(getFolder(folderPath)); } protected IFolder getFolder(IPath path) { return getWorkspaceRoot().getFolder(path); } }