/******************************************************************************* | |
* Copyright (c) 2012, 2021 IBM Corporation and others. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v2.0 | |
* which accompanies this distribution, and is available at | |
* https://www.eclipse.org/legal/epl-2.0/ | |
* | |
* Contributors: | |
* IBM Corporation - initial API and implementation | |
* Red Hat, Inc. - refactoring | |
*******************************************************************************/ | |
package org.eclipse.wst.jsdt.ui.tests.utils; | |
import java.util.HashMap; | |
import java.util.Iterator; | |
import java.util.Map; | |
import org.eclipse.core.resources.IFile; | |
import org.eclipse.core.resources.IProject; | |
import org.eclipse.core.resources.IResource; | |
import org.eclipse.core.resources.IWorkspaceRoot; | |
import org.eclipse.core.resources.ResourcesPlugin; | |
import org.eclipse.core.runtime.IPath; | |
import org.eclipse.core.runtime.NullProgressMonitor; | |
import org.eclipse.core.runtime.Path; | |
import org.eclipse.jface.text.IDocument; | |
import org.eclipse.ui.IEditorPart; | |
import org.eclipse.ui.IWorkbenchPage; | |
import org.eclipse.ui.IWorkbenchWindow; | |
import org.eclipse.ui.PlatformUI; | |
import org.eclipse.ui.ide.IDE; | |
import org.eclipse.ui.texteditor.AbstractTextEditor; | |
import org.eclipse.ui.texteditor.ITextEditor; | |
import org.eclipse.wst.jsdt.internal.core.JavaModelManager; | |
import org.eclipse.wst.jsdt.internal.core.search.indexing.IndexManager; | |
import org.eclipse.wst.jsdt.internal.ui.javaeditor.JavaEditor; | |
import junit.framework.Assert; | |
/** | |
* <p>Sets up a test project.</p> | |
* | |
* @see org.eclipse.wst.jsdt.ui.tests.utils | |
* @see org.eclipse.wst.jsdt.web.ui.tests.internal | |
*/ | |
@SuppressWarnings({"nls", "restriction"}) | |
public class TestProjectSetup { | |
/** preference for ignoring WTP UI */ | |
private static final String WTP_AUTOTEST_NONINTERACTIVE = "wtp.autotest.noninteractive"; | |
/** The location of the testing files */ | |
protected static final String TESTING_RESOURCES_DIR = "testresources"; | |
/** previous value for hiding WTP UI */ | |
private String fPreviousWTPAutoTestNonInteractivePropValue = null; | |
/** Name of the project the tests use */ | |
private final String fProjectName; | |
/** The project that the tests use */ | |
private IProject fProject; | |
/** The root directory to start with when looking for files */ | |
private final String fRootDirectory; | |
/** | |
* <p> | |
* <code>true</code> if should delete project on tear down, | |
* <code>false</code> to leave it for other tests to use. | |
* </p> | |
*/ | |
private final boolean fDeleteOnTearDown; | |
/** | |
* Used to keep track of the already open editors so that the tests don't go through | |
* the trouble of opening the same editors over and over again | |
*/ | |
private Map fFileToEditorMap = new HashMap<IFile, JavaEditor>(); | |
/** | |
* <p> | |
* Path to the library file to import into this test project. | |
* </p> | |
* | |
* @see #fLibraryFilesDestinationPath | |
*/ | |
private final String fLibraryFilePath; | |
/** | |
* <p> | |
* Path relative to the test project that the given {@link #fLibraryFilePath} should be | |
* imported into, if one is given. Or <code>null</code> if the given | |
* {@link #fLibraryFilePath} should be imported into the test project root | |
* </p> | |
* | |
* @see #fLibraryFilePath | |
*/ | |
private final String fLibraryFilesDestinationPath; | |
/** | |
* <p> | |
* <b>NOTE:</b> will not delete the project on tear down so other tests can use it. | |
* </p> | |
* | |
* @param test | |
* do setup for the given test | |
* @param projectName | |
* name of the project to set up | |
* @param rootDirectory | |
* path to the root directory to look for all files under, or <code>null</code> if | |
* look directly under project root | |
*/ | |
public TestProjectSetup(String projectName, String rootDirectory) { | |
this(projectName, rootDirectory, false, null, null); | |
} | |
/** | |
* @param test | |
* do setup for the given test | |
* @param projectName | |
* name of the project to set up | |
* @param rootDirectory | |
* path to the root directory to look for all files under, or <code>null</code> if | |
* look directly under project root | |
* @param deleteOnTearDown | |
* <code>true</code> if should delete project on tear down, <code>false</code> to | |
* leave it for other tests to use. | |
*/ | |
public TestProjectSetup(String projectName, String rootDirectory, boolean deleteOnTearDown) { | |
this(projectName, rootDirectory, deleteOnTearDown, null, null); | |
} | |
/** | |
* @param test | |
* do setup for the given test | |
* @param projectName | |
* name of the project to set up | |
* @param rootDirectory | |
* path to the root directory to look for all files under, or <code>null</code> if | |
* look directly under project root | |
* @param deleteOnTearDown | |
* <code>true</code> if should delete project on tear down, <code>false</code> to | |
* leave it for other tests to use. | |
* @param libraryFilePath | |
* Path to the library file to import into this test project, relative to TESTING_RESOURCES_DIR | |
* @param libraryFilesDestinationPath | |
* Path relative to the test project that the given {@link #fLibraryFilePath} | |
* should be | |
* imported into, if one is given. Or <code>null</code> if the given | |
* {@link #fLibraryFilePath} should be imported into the test project root | |
*/ | |
public TestProjectSetup(String projectName, String rootDirectory, boolean deleteOnTearDown, | |
String libraryFilePath, String libraryFilesDestinationPath) { | |
this.fProjectName = projectName; | |
this.fRootDirectory = rootDirectory; | |
this.fDeleteOnTearDown = deleteOnTearDown; | |
this.fLibraryFilePath = libraryFilePath; | |
this.fLibraryFilesDestinationPath = libraryFilesDestinationPath; | |
} | |
/** | |
* @return {@link IProject} that was setup | |
*/ | |
public IProject getProject() { | |
return this.fProject; | |
} | |
/** | |
* <p> | |
* Given a <code>file</code> get an editor for it. If an editor has already been retrieved for | |
* the given <code>file</code> then return the same already open editor. | |
* </p> | |
* | |
* <p> | |
* When opening the editor it will also standardized the line endings to <code>\n</code> | |
* </p> | |
* | |
* @param file | |
* open and return an editor for this | |
* @return <code>StructuredTextEditor</code> opened from the given <code>file</code> | |
*/ | |
public JavaEditor getEditor(IFile file) { | |
JavaEditor editor = (JavaEditor) fFileToEditorMap.get(file); | |
if (editor == null) { | |
try { | |
IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); | |
IWorkbenchPage page = workbenchWindow.getActivePage(); | |
// IEditorPart editorPart = IDE.openEditor(page, file, true, true); | |
IEditorPart editorPart = IDE.openEditor(page, file, "org.eclipse.wst.jsdt.ui.CompilationUnitEditor", true); | |
if (editorPart instanceof JavaEditor) { | |
editor = (JavaEditor) editorPart; | |
} else { | |
} | |
if(editor != null) { | |
standardizeLineEndings(editor); | |
fFileToEditorMap.put(file, editor); | |
} else { | |
Assert.fail("Could not open editor for " + file); | |
} | |
} catch(Exception e) { | |
Assert.fail("Could not open editor for " + file + " exception: " + e.getMessage()); | |
} | |
} | |
if (!(editor instanceof JavaEditor)) { | |
Assert.fail("Unable to open JS editor for: " + file.getFullPath() + " (got " + editor.getClass() + ")"); | |
} | |
return editor; | |
} | |
/** | |
* <p> | |
* Given a file path in the test project attempts to get an <code>IFile</code> for it, if the | |
* file doesn't exist the test fails. | |
* </p> | |
* | |
* @param path | |
* the name of the file to get | |
* | |
* @return the {@link IFile} associated with the given file path | |
*/ | |
public IFile getFile(String path) { | |
IFile file = null; | |
if (this.fRootDirectory != null) { | |
file = this.fProject.getFile(this.fRootDirectory + IPath.SEPARATOR + path); | |
} else { | |
file = this.fProject.getFile(path); | |
} | |
Assert.assertTrue("Test file " + file + " can not be found", file.exists()); | |
return file; | |
} | |
/** | |
* <p> | |
* Edits the file given by the fileName. The number of characters indicated by length are | |
* replaced by the given text beginning at the character located at the given line number and | |
* the line character offset. | |
* </p> | |
* | |
* @param fileName | |
* @param lineNum | |
* @param lineRelativeCharOffset | |
* @param length | |
* @param text | |
* @throws Exception | |
*/ | |
public void editFile(String fileName, int lineNum, int lineRelativeCharOffset, int length, String text) | |
throws Exception { | |
IFile file = this.getFile(fileName); | |
JavaEditor editor = this.getEditor(file); | |
IDocument doc = editor.getDocumentProvider().getDocument(editor.getEditorInput()); | |
int offset = doc.getLineOffset(lineNum) + lineRelativeCharOffset; | |
doc.replace(offset, length, text); | |
waitForIndexManager(); | |
} | |
/** | |
* <p> | |
* This is run Once while the test is being setUp for all the tests. | |
* </p> | |
* <p> | |
* Designed to be overridden by content assist test suite Implementers to do additional test | |
* setup. | |
* </p> | |
* | |
*/ | |
public void additionalSetUp() throws Exception { | |
// default do nothing | |
} | |
/** | |
* <p> | |
* This is run once before all of the tests | |
* </p> | |
* | |
* @see junit.extensions.TestSetup#setUp() | |
*/ | |
public void setUp() throws Exception { | |
// setup properties | |
String noninteractive = System.getProperty(WTP_AUTOTEST_NONINTERACTIVE); | |
if(noninteractive != null) { | |
fPreviousWTPAutoTestNonInteractivePropValue = noninteractive; | |
} else { | |
fPreviousWTPAutoTestNonInteractivePropValue = "false"; | |
} | |
System.setProperty(WTP_AUTOTEST_NONINTERACTIVE, "true"); | |
// get project | |
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); | |
fProject = root.getProject(this.fProjectName); | |
// setup project if it is not yet setup | |
if ((fProject == null) || !fProject.exists()) { | |
fProject = BundleResourceUtil.createSimpleProject(this.fProjectName, null, null); | |
} | |
BundleResourceUtil.copyBundleEntriesIntoWorkspace(TESTING_RESOURCES_DIR + IPath.SEPARATOR + this.fProjectName, | |
IPath.SEPARATOR + this.fProjectName); | |
// import library file if one is specified | |
if(this.fLibraryFilePath != null) { | |
//calculate destination path | |
String libraryFilesDestinationPath = ""; | |
if(this.fLibraryFilesDestinationPath == null) { | |
libraryFilesDestinationPath = IPath.SEPARATOR + this.fProjectName; | |
}else{ | |
libraryFilesDestinationPath = IPath.SEPARATOR + this.fLibraryFilesDestinationPath; | |
} | |
//copy the library file contents to the destination | |
BundleResourceUtil.copyBundleEntryIntoWorkspace(TESTING_RESOURCES_DIR + IPath.SEPARATOR + this.fLibraryFilePath, | |
libraryFilesDestinationPath); | |
} | |
// run any additional test setup | |
this.additionalSetUp(); | |
fProject.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); | |
// give the workspace a second to settle before running tests | |
Thread.sleep(1000); | |
waitForIndexManager(); | |
} | |
/** | |
* Imports the contents of a zip file into the project. | |
* | |
* @param fLibraryFilesZipPath | |
* name of the zip file to import. | |
* @param fLibraryFilesDestinationPath | |
* location to import files relative to project. | |
* @throws Exception | |
*/ | |
public void importZip(String fLibraryFilesZipPath, String fLibraryFilesDestinationPath) throws Exception { | |
if(fLibraryFilesZipPath != null) { | |
//calculate destination path | |
IPath libraryFilesDestinationPath = null; | |
if(fLibraryFilesDestinationPath != null) { | |
libraryFilesDestinationPath = new Path(IPath.SEPARATOR + this.fProjectName + IPath.SEPARATOR + fLibraryFilesDestinationPath); | |
} else { | |
libraryFilesDestinationPath = new Path(IPath.SEPARATOR + this.fProjectName); | |
} | |
libraryFilesDestinationPath.addTrailingSeparator(); | |
//copy the library zip contents to the destination | |
BundleResourceUtil.copyBundleZippedEntriesIntoWorkspace(TESTING_RESOURCES_DIR + IPath.SEPARATOR + fLibraryFilesZipPath, | |
libraryFilesDestinationPath); | |
} | |
// give the workspace a second to settle before continuing tests | |
Thread.sleep(1000); | |
waitForIndexManager(); | |
} | |
/** | |
* <p> | |
* This is run once after all of the tests have been run | |
* </p> | |
* | |
* @see junit.extensions.TestSetup#tearDown() | |
*/ | |
public void tearDown() throws Exception { | |
// close out the editors | |
@SuppressWarnings("restriction") | |
Iterator<JavaEditor> iter = fFileToEditorMap.values().iterator(); | |
while(iter.hasNext()) { | |
AbstractTextEditor editor = iter.next(); | |
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().closeEditor(editor, false); | |
} | |
this.fFileToEditorMap.clear(); | |
//delete the project | |
if(this.fDeleteOnTearDown) { | |
this.fProject.close(new NullProgressMonitor()); | |
this.fProject.delete(true, new NullProgressMonitor()); | |
} | |
// restore properties | |
if(fPreviousWTPAutoTestNonInteractivePropValue != null) { | |
System.setProperty(WTP_AUTOTEST_NONINTERACTIVE, fPreviousWTPAutoTestNonInteractivePropValue); | |
} | |
} | |
/** | |
* <p> | |
* Line endings can be an issue when running tests on different OSs. This function standardizes | |
* the line endings to use <code>\n</code> | |
* </p> | |
* | |
* <p> | |
* It will get the text from the given editor, change the line endings, and then save the editor | |
* </p> | |
* | |
* @param editor | |
* standardize the line endings of the text presented in this | |
* editor. | |
*/ | |
private static void standardizeLineEndings(ITextEditor editor) { | |
IDocument doc = editor.getDocumentProvider().getDocument(editor.getEditorInput()); | |
String contents = doc.get(); | |
contents = StringUtils.replace(contents, "\r\n", "\n"); | |
contents = StringUtils.replace(contents, "\r", "\n"); | |
doc.set(contents); | |
} | |
/** | |
* <p> | |
* Wait for the index manager with a time out of 10 seconds. | |
* </p> | |
*/ | |
private static void waitForIndexManager() { | |
waitForIndexManager(10000); | |
} | |
/** | |
* <p> | |
* Wait for the index manager for the given max time. | |
* </p> | |
* | |
* @param max | |
* maximum amount of time to wait for the index manager | |
*/ | |
@SuppressWarnings("restriction") | |
private static void waitForIndexManager(long max) { | |
// Wait for the end of indexing | |
IndexManager indexManager = JavaModelManager.getJavaModelManager().getIndexManager(); | |
long maxWaits = max / 10; | |
while ((indexManager.awaitingJobsCount() > 0) && (maxWaits-- > 0)) { | |
try { | |
Thread.sleep(10); | |
} | |
catch (InterruptedException e) { | |
return; | |
} | |
} | |
} | |
/** | |
* | |
* @return Project's root directory. | |
*/ | |
public String getRootDirectory(){ | |
return fRootDirectory; | |
} | |
} |