blob: add23408f097d8044c8630959085d89f480860ac [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2011 Obeo.
* 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:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.docs.intent.client.ui.test.util;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceDescription;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.ILogListener;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.mylyn.docs.intent.client.ui.IntentEditorActivator;
import org.eclipse.mylyn.docs.intent.client.ui.editor.IntentEditor;
import org.eclipse.mylyn.docs.intent.client.ui.ide.builder.IntentNature;
import org.eclipse.mylyn.docs.intent.client.ui.ide.builder.ToggleNatureAction;
import org.eclipse.mylyn.docs.intent.client.ui.preferences.IntentPreferenceConstants;
import org.eclipse.mylyn.docs.intent.client.ui.utils.IntentEditorOpener;
import org.eclipse.mylyn.docs.intent.collab.common.query.IntentDocumentQuery;
import org.eclipse.mylyn.docs.intent.collab.common.repository.IntentRepositoryInitializer;
import org.eclipse.mylyn.docs.intent.collab.common.repository.IntentRepositoryManager;
import org.eclipse.mylyn.docs.intent.collab.handlers.adapters.RepositoryAdapter;
import org.eclipse.mylyn.docs.intent.collab.repository.Repository;
import org.eclipse.mylyn.docs.intent.collab.repository.RepositoryConnectionException;
import org.eclipse.mylyn.docs.intent.core.document.IntentChapter;
import org.eclipse.mylyn.docs.intent.core.document.IntentDocument;
import org.eclipse.mylyn.docs.intent.core.document.IntentSection;
import org.eclipse.mylyn.docs.intent.core.document.IntentStructuredElement;
import org.eclipse.mylyn.docs.intent.parser.IntentParser;
import org.eclipse.mylyn.docs.intent.parser.modelingunit.ParseException;
import org.eclipse.mylyn.docs.intent.parser.test.utils.FileToStringConverter;
import org.eclipse.ui.PlatformUI;
/**
* An abstract test class providing API for managing an Intent IDE projects and editors.
*
* @author <a href="mailto:alex.lagarde@obeo.fr">Alex Lagarde</a>
*/
public abstract class AbstractIntentUITest extends TestCase implements ILogListener {
public static final String INTENT_NEW_PROJECT_WIZARD_ID = "org.eclipse.mylyn.docs.intent.client.ui.ide.wizards.NewIntentProjectWizard";
protected static final String INTENT_EMPTY_DOC_PATH = "data/unit/documents/empty.intent";
private static final int WAITING_DELAY_MILLIS = 500;
protected IProject intentProject;
protected Repository repository;
protected RepositoryAdapter repositoryAdapter;
protected RepositoryListenerForTests repositoryListener;
private IntentDocument intentDocument;
private List<IntentEditor> openedEditors;
/**
* {@inheritDoc}
*
* @see junit.framework.TestCase#setUp()
*/
@Override
protected void setUp() throws Exception {
initClassLoader();
// Step 1 : printing testclass name (make hudson debugs easier)
for (int i = 0; i < getClass().getName().length() - 1; i++) {
System.out.print("=");
}
System.out.println("=");
System.out.println(getClass().getName());
super.setUp();
// Step 2 : deactivating the automatic build of the workspace (if needed)
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IWorkspaceDescription description = workspace.getDescription();
if (description.isAutoBuilding()) {
description.setAutoBuilding(false);
workspace.setDescription(description);
}
// Step 3 : clean workspace, close welcome page
WorkspaceUtils.cleanWorkspace();
WorkspaceUtils.closeWelcomePage();
waitForAllOperationsInUIThread();
IntentEditorActivator.getDefault().getLog().addLogListener(this);
openedEditors = new ArrayList<IntentEditor>();
}
/**
* Forces OSGI to load ui.ide plugin first. Otherwise, IntentRepositoryManager.INSTANCE.getRepository is
* called twice at the same time (?). Happens only in the test suite.
*/
private void initClassLoader() {
assertNotNull(IntentNature.class);
}
private void traceHeapSize() {
long maxHeapSize = Runtime.getRuntime().maxMemory();
long allocatedHeapSize = Runtime.getRuntime().totalMemory();
long usedHeap = allocatedHeapSize - Runtime.getRuntime().freeMemory();
System.out.println(" Heap size : " + usedHeap + "/" + allocatedHeapSize + "("
+ Math.ceil(usedHeap * 100 / allocatedHeapSize) + "% - "
+ Math.ceil(usedHeap * 100 / maxHeapSize) + "% of max heap size)");
}
/**
* {@inheritDoc}
*
* @see junit.framework.TestCase#tearDown()
*/
@Override
protected void tearDown() throws Exception {
waitForAllOperationsInUIThread();
// Step 1: close editors
for (IntentEditor editor : openedEditors) {
editor.close(false);
}
// Step 2: clean workspace
if (intentProject != null) {
IntentRepositoryManager.INSTANCE.deleteRepository(intentProject.getName());
waitForAllOperationsInUIThread();
intentProject.delete(true, true, new NullProgressMonitor());
}
IntentEditorActivator.getDefault().getLog().removeLogListener(this);
WorkspaceUtils.cleanWorkspace();
// Step 3: unregister repository listener and deactivate advance loggin
if (repositoryListener != null) {
Platform.removeLogListener(repositoryListener);
}
IEclipsePreferences node = InstanceScope.INSTANCE.getNode(IntentEditorActivator.getDefault()
.getBundle().getSymbolicName());
node.putBoolean(IntentPreferenceConstants.ACTIVATE_ADVANCE_LOGGING, false);
// Step 4: setting all fields to null (to avoid memory leaks)
setAllFieldsToNull();
super.tearDown();
}
/**
* Creates and register an new {@link org.eclipse.mylyn.docs.intent.collab.handlers.RepositoryClient } in
* charge of detecting that events happened on the repository.
*/
protected final void registerRepositoryListener() {
this.repositoryListener = new RepositoryListenerForTests();
Platform.addLogListener(repositoryListener);
}
/**
* Creates a new empty Intent project.
*
* @param projectName
* the intent project name
*/
protected final void setUpIntentProject(final String projectName) {
setUpIntentProject(projectName, INTENT_EMPTY_DOC_PATH, false);
}
/**
* Creates a new Intent project using the intent document located at the given path.
*
* @param projectName
* the intent project name
* @param intentDocumentPath
* the path of the intent document to use (relative to the
* org.eclipse.mylyn.docs.intent.client.ui.test project).
*/
protected final void setUpIntentProject(final String projectName, String intentDocumentPath) {
setUpIntentProject(projectName, intentDocumentPath, false);
}
/**
* Creates a new Intent project using the intent document located at the given path.
*
* @param projectName
* the intent project name
* @param intentDocumentPath
* the path of the intent document to use (relative to the
* org.eclipse.mylyn.docs.intent.client.ui.test project)
* @param listenForRepository
* indicates whether a repository listener should be registered. If you want to determine if a
* client has done a job (by calling {@link AbstractIntentUITest#waitForIndexer() for example}
* ), this must be true.
*/
protected void setUpIntentProject(final String projectName, String intentDocumentPath,
boolean listenForRepository) {
try {
// Step 1: getting the content of the intent document located at the given path.
File file = new File(intentDocumentPath);
final String intentDocumentContent = FileToStringConverter.getFileAsString(file);
// Step 2: creating the intent project
doCreateIntentProject(projectName, intentDocumentContent);
while (repositoryAdapter == null) {
Thread.sleep(10);
}
// Step 3: registering the repository listener
registerRepositoryListener();
repositoryListener.clearPreviousEntries();
// Step 4: additional setup operations
additionalSetUpOperations();
} catch (CoreException e) {
AssertionFailedError error = new AssertionFailedError("Failed to create Intent project");
error.setStackTrace(e.getStackTrace());
throw error;
} catch (IOException e) {
AssertionFailedError error = new AssertionFailedError(
"Failed to get content of intent document '" + intentDocumentPath + "'");
error.setStackTrace(e.getStackTrace());
throw error;
} catch (InterruptedException e) {
AssertionFailedError error = new AssertionFailedError("Failed to create Intent project");
error.setStackTrace(e.getStackTrace());
throw error;
}
}
/**
* Creates the intent project with the given name, with the given content.
*
* @param projectName
* the name of the project to create
* @param intentDocumentContent
* the content to associate to the created intent document
* @throws CoreException
* if the project cannot be created properly
*/
protected void doCreateIntentProject(final String projectName, final String intentDocumentContent)
throws CoreException {
IWorkspaceRunnable create = new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) throws CoreException {
IProject project = WorkspaceUtils.createProject(projectName, monitor);
ToggleNatureAction.toggleNature(project);
IntentRepositoryInitializer.initializeContent(projectName, intentDocumentContent);
// Step 3 : initializing all useful informations
intentProject = project;
setUpRepository(project);
}
};
ResourcesPlugin.getWorkspace().run(create, null);
}
/**
* Set up the repository for the given project.
*
* @param project
* the project
*/
protected final void setUpRepository(IProject project) {
assertNotNull(project);
try {
repository = IntentRepositoryManager.INSTANCE.getRepository(project.getName());
assertNotNull(repository);
repositoryAdapter = repository.createRepositoryAdapter();
} catch (RepositoryConnectionException e) {
AssertionFailedError error = new AssertionFailedError(
"Cannot connect to the created IntentRepository");
error.setStackTrace(e.getStackTrace());
throw error;
} catch (CoreException e) {
AssertionFailedError error = new AssertionFailedError(
"Cannot retrieve the correct IntentRepository type");
error.setStackTrace(e.getStackTrace());
throw error;
}
// wait for initialization completed
waitForAllOperationsInUIThread();
}
// /**
// * Loads the {@link IntentStructuredElement} located at the given path. If it contains an
// IntentDocument,
// * also updates the intentDocument field.
// *
// * @param intentDocumentModelPath
// * the path of the Intent document model (from
// * org.eclipse.mylyn.docs.intent.client.ui.test/data)
// * @return the loaded {@link IntentStructuredElement}
// */
// protected IntentStructuredElement loadIntentDocumentFromTests(String intentDocumentModelPath) {
// ResourceSet rs = new ResourceSetImpl();
// URI documentURI = URI.createURI("platform:/plugin/org.eclipse.mylyn.docs.intent.client.ui.test/data/"
// + intentDocumentModelPath);
// Resource documentResource = rs.getResource(documentURI, true);
// if (documentResource != null && documentResource.getContents().iterator().hasNext()
// && documentResource.getContents().iterator().next() instanceof IntentStructuredElement) {
// if (documentResource.getContents().iterator().next() instanceof IntentDocument) {
// intentDocument = (IntentDocument)documentResource.getContents().iterator().next();
// }
// return (IntentStructuredElement)documentResource.getContents().iterator().next();
// }
// throw new AssertionFailedError("Could not load Intent model at " + intentDocumentModelPath);
// }
/**
* Parses the {@link IntentStructuredElement} located at the given path. If it contains an IntentDocument,
* also updates the intentDocument field.
*
* @param intentDocumentModelPath
* the path of the Intent document model (from
* org.eclipse.mylyn.docs.intent.client.ui.test/data)
* @return the loaded {@link IntentStructuredElement}
*/
protected IntentDocument parseIntentDocumentFromTests(String intentDocumentModelPath) {
EObject res = null;
try {
res = new IntentParser().parse(FileToStringConverter.getFileAsString(new File(
intentDocumentModelPath)));
} catch (ParseException e) {
fail(e.getMessage());
} catch (IOException e) {
fail(e.getMessage());
}
if (res instanceof IntentDocument) {
intentDocument = (IntentDocument)res;
}
return intentDocument;
}
/**
* Returns the intentDocument associated to the current Intent project.
*
* @return the intentDocument associated to the current Intent project
*/
protected final IntentDocument getIntentDocument() {
if (intentDocument == null) {
intentDocument = new IntentDocumentQuery(repositoryAdapter).getOrCreateIntentDocument();
}
return intentDocument;
}
/**
* Return the chapter at the given number.
*
* @param number
* the number of the chapter
* @return the chapter
*/
protected final IntentChapter getIntentChapter(int number) {
return getIntentDocument().getChapters().get(number - 1);
}
/**
* Return the section at the given number.
*
* @param number
* the number of the section
* @return the section
*/
protected final IntentSection getIntentSection(int... number) {
IntentSection section = getIntentChapter(number[0]).getSubSections().get(number[1] - 1);
if (number.length > 2) {
for (int i = 2; i < number.length; i++) {
section = section.getSubSections().get(number[i] - 1);
}
}
return section;
}
/**
* Opens an editor on the Document contained in the intent project.
*
* @return the opened editor
*/
protected final IntentEditor openIntentEditor() {
return openIntentEditor(getIntentDocument());
}
/**
* Opens an editor on the given {@link IntentStructuredElement}.
*
* @param element
* the {@link IntentStructuredElement} to open an editor on
* @return the opened editor
*/
protected final IntentEditor openIntentEditor(IntentStructuredElement element) {
IntentEditorOpener.openIntentEditor(repository, element, true, null, true);
waitForAllOperationsInUIThread();
IntentEditor editor = IntentEditorOpener.getAlreadyOpenedEditor(element);
assertNotNull(editor);
openedEditors.add(editor);
return editor;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.core.runtime.ILogListener#logging(org.eclipse.core.runtime.IStatus, java.lang.String)
*/
public void logging(IStatus status, String plugin) {
if (status.getSeverity() == IStatus.ERROR) {
fail(status.getMessage());
}
}
/**
* Wait until the end of all asynchronous operations launched in the UI Thread.
*/
protected static void waitForAllOperationsInUIThread() {
while (PlatformUI.getWorkbench().getDisplay().readAndDispatch()) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// Nothing to do
}
}
}
/**
* Wait for synchronizer to complete work.
*/
protected final void waitForSynchronizer() {
waitForSynchronizer(true);
}
/**
* Wait for repository to complete work.
*/
protected final void waitForIndexer() {
waitForIndexer(true);
}
/**
* Wait for compiler to complete work.
*/
protected final void waitForCompiler() {
waitForCompiler(true);
}
/**
* Wait for the project explorer refresher to complete work.
*/
protected final void waitForProjectExplorerRefresher() {
waitForProjectExplorerRefresher(true);
}
/**
* Ensures that the synchronizer has been launched or not, according to the given boolean.
*
* @param synchronizerShouldBeNotified
* indicates whether the synchronizer should be notified or not
*/
protected final void waitForSynchronizer(boolean synchronizerShouldBeNotified) {
waitForAllOperationsInUIThread();
assertNotNull(
"Cannot wait for synchronizer : you need to initialize a repository listener by calling the registerRepositoryListener() method",
repositoryListener);
if (synchronizerShouldBeNotified) {
assertTrue("Time out : synchronizer should have handle changes but did not",
repositoryListener.waitForModificationOn("Synchronizer"));
} else {
assertFalse("Synchonizer should not have been notifed",
repositoryListener.waitForModificationOn("Synchronizer"));
}
try {
Thread.sleep(WAITING_DELAY_MILLIS);
} catch (InterruptedException e) {
// fail silently
}
waitForAllOperationsInUIThread();
}
/**
* Ensures that the indexer has been launched or not, according to the given boolean.
*
* @param indexerShouldBeNotified
* indicates whether the indexer should be notified or not
*/
protected final void waitForIndexer(boolean indexerShouldBeNotified) {
waitForAllOperationsInUIThread();
assertNotNull(
"Cannot wait for Indexer : you need to initialize a repository listener by calling the registerRepositoryListener() method",
repositoryListener);
if (indexerShouldBeNotified) {
assertTrue("Time out : indexer should have handle changes but did not",
repositoryListener.waitForModificationOn("Indexer"));
} else {
assertFalse("Indexer should not have been notifed",
repositoryListener.waitForModificationOn("Indexer"));
}
waitForAllOperationsInUIThread();
}
/**
* Ensures that the project explorer refreshed has been launched or not, according to the given boolean.
*
* @param refresherShouldBeNotified
* indicates whether the indexer should be notified or not
*/
protected final void waitForProjectExplorerRefresher(boolean refresherShouldBeNotified) {
waitForAllOperationsInUIThread();
assertNotNull(
"Cannot wait for Project Explorer Refresher : you need to initialize a repository listener by calling the registerRepositoryListener() method",
repositoryListener);
if (refresherShouldBeNotified) {
assertTrue("Time out : Project Explorer Refresher should have handle changes but did not",
repositoryListener.waitForModificationOn("Project Explorer Refresher"));
} else {
assertFalse("Project Explorer Refresher should not have been notifed",
repositoryListener.waitForModificationOn("Project Explorer Refresher"));
}
waitForAllOperationsInUIThread();
}
/**
* Ensures that the compiler has been launched or not, according to the given boolean.
*
* @param compilerShouldBeNotified
* indicates whether the compiler should be notified or not
*/
protected final void waitForCompiler(boolean compilerShouldBeNotified) {
waitForAllOperationsInUIThread();
assertNotNull(
"Cannot wait for compiler : you need to initialize a repository listener by calling the registerRepositoryListener() method",
repositoryListener);
if (compilerShouldBeNotified) {
assertTrue("Time out : compiler should have handle changes but did not",
repositoryListener.waitForModificationOn("Compiler"));
} else {
assertFalse("Compiler should not have been notifed",
repositoryListener.waitForModificationOn("Compiler"));
}
try {
Thread.sleep(WAITING_DELAY_MILLIS);
} catch (InterruptedException e) {
// fail silently
}
waitForAllOperationsInUIThread();
}
/**
* Additional operations performed at the end of set-up.
*/
protected void additionalSetUpOperations() {
// waiting for synchronizer to pass
waitForSynchronizer();
}
/**
* Sets all fields of the current test case to null (to avoid memory leaks).
*/
private void setAllFieldsToNull() {
// For all fields defined in the current test and all its superclasses
for (Class<?> clazz = this.getClass(); clazz != TestCase.class; clazz = clazz.getSuperclass()) {
for (Field field : clazz.getDeclaredFields()) {
boolean isReference = !field.getType().isPrimitive();
try {
field.setAccessible(true);
boolean isSet = field.get(this) != null;
// We do not clean non set references
if (isReference && isSet) {
boolean isFinal = Modifier.isFinal(field.getModifiers());
// We do not clean final fields
if (!isFinal) {
// Setting the field to null
field.set(this, null);
}
}
} catch (IllegalArgumentException e) {
// Do nothing
} catch (IllegalAccessException e) {
// Do nothing
}
}
}
}
}