blob: 5ab131539f43949a8f87c2a05acdf46472e3c3a1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.tests.multieditor;
import java.io.IOException;
import java.net.URL;
import junit.framework.TestSuite;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.ILogListener;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.ToolBarContributionItem;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.IActionBars2;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorRegistry;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.internal.WorkbenchPage;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.IContributedContentsView;
import org.eclipse.ui.part.MultiEditor;
import org.eclipse.ui.part.MultiEditorInput;
import org.eclipse.ui.tests.TestPlugin;
import org.eclipse.ui.tests.harness.util.CallHistory;
import org.eclipse.ui.tests.harness.util.UITestCase;
/**
* Test MultiEditor behaviour to highlight some of the broken functionality.
*
* @since 3.1
*/
public class MultiEditorTest extends UITestCase {
private static final String ACTION_TOOLTIP = "MultiEditorActionThing";
private static final String PROJECT_NAME = "TiledEditorProject";
private static final String CONTENT_OUTLINE = "org.eclipse.ui.views.ContentOutline";
private static final String TESTEDITOR_COOLBAR = "org.eclipse.ui.tests.multieditor.actionSet";
private static final String TILED_EDITOR_ID = "org.eclipse.ui.tests.multieditor.TiledEditor";
// tiled editor test files
private static final String DATA_FILES_DIR = "/data/org.eclipse.newMultiEditor/";
private static final String TEST01_TXT = "test01.txt";
private static final String TEST02_TXT = "test02.txt";
private static final String TEST03_ETEST = "test03.etest";
private static final String TEST04_PROPERTIES = "test04.properties";
private static final String BUILD_XML = "build.xml";
//
// call trace for the editor open - setFocus - close test
//
private static String[] gEditorOpenTrace = { "setInitializationData",
"init", "createPartControl", "createInnerPartControl",
"createInnerPartControl", "setFocus", "updateGradient",
"updateGradient", };
private static String[] gEditorFocusTrace = { "setInitializationData",
"init", "createPartControl", "createInnerPartControl",
"createInnerPartControl", "setFocus", "updateGradient",
"updateGradient", "updateGradient", "updateGradient", };
private static String[] gEditorCloseTrace = { "setInitializationData",
"init", "createPartControl", "createInnerPartControl",
"createInnerPartControl", "setFocus", "updateGradient",
"updateGradient", "updateGradient", "updateGradient",
"widgetsDisposed", "dispose" };
public static TestSuite suite() {
return new TestSuite(MultiEditorTest.class);
}
/**
* Can catch a MultiEditor unexpect Exception on init.
*/
private EditorErrorListener fErrorListener;
public MultiEditorTest(String tc) {
super(tc);
}
/**
* Test that the test tiled editor can be opened with a basic
* MultiEditorInput with the same type of files.
*
* Test: Select a couple of files from navigator and use the TiledEditor
* menu to open the editor. It should open with the first selected file on
* top.
*
* @throws Throwable
* on an error
*/
public void testOpenBasicEditor() throws Throwable {
final String[] simpleFiles = { TEST01_TXT, TEST02_TXT };
IWorkbenchWindow window = fWorkbench.getActiveWorkbenchWindow();
IWorkbenchPage page = window.getActivePage();
IProject testProject = findOrCreateProject(PROJECT_NAME);
MultiEditorInput input = generateEditorInput(simpleFiles, testProject);
// validate there are no NullPointerExceptions during editor
// initialization
openAndValidateEditor(page, input);
}
/**
* Test that the public methods in TiledEditor (and MultiEditor) are called
* in the correct order from 3.0 to 3.1.
*
* Test: this test involves opening the tiled editor on 2 files, changing
* the focus from the first file to the second file, and closing the tiled
* editor.
*
* @throws Throwable
*/
public void testOpenTestFile() throws Throwable {
final String[] simpleFiles = { TEST01_TXT, TEST03_ETEST };
IWorkbenchWindow window = fWorkbench.getActiveWorkbenchWindow();
WorkbenchPage page = (WorkbenchPage) window.getActivePage();
IProject testProject = findOrCreateProject(PROJECT_NAME);
MultiEditorInput input = generateEditorInput(simpleFiles, testProject);
// catches the framework NPE
IEditorPart editor = openAndValidateEditor(page, input);
// did we get a multieditor back?
assertTrue(editor instanceof MultiEditor);
MultiEditor multiEditor = (MultiEditor) editor;
chewUpEvents();
// listHistory(((TiledEditor) multiEditor).callHistory);
// check the public API called for opening the TiledEditor
// ((TiledEditor) multiEditor).callHistory.printToConsole();
assertTrue("The editor open trace was incorrect",
((TiledEditor) multiEditor).callHistory
.verifyOrder(gEditorOpenTrace));
// swap focus to the last editor, which is the test editor
// with the test coolbar contribution
IEditorPart[] innerEditors = multiEditor.getInnerEditors();
innerEditors[innerEditors.length - 1].setFocus();
chewUpEvents();
// ((TiledEditor) multiEditor).callHistory.printToConsole();
assertTrue("Editor setFocus trace was incorrect",
((TiledEditor) multiEditor).callHistory
.verifyOrder(gEditorFocusTrace));
page.closeEditor(multiEditor, false);
chewUpEvents();
// ((TiledEditor) multiEditor).callHistory.printToConsole();
assertTrue("Editor close trace was incorrect",
((TiledEditor) multiEditor).callHistory
.verifyOrder(gEditorCloseTrace));
}
/**
* Test that coolbar items in the workbench are updated when focus moves
* through the different inner editors ... this test as written is not 100%
* accurate, as the items are enabled.
*
* Test: Open two files where the first is a text file and the second is of
* type etest. Change focus to the etest file, and the coolbar should update
* with a new action icon and it should be enabled.
*
* @throws Throwable
* on an error
*/
public void testTrackCoolBar() throws Throwable {
final String[] simpleFiles = { TEST01_TXT, TEST02_TXT,
TEST04_PROPERTIES, BUILD_XML, TEST03_ETEST };
IWorkbenchWindow window = fWorkbench.getActiveWorkbenchWindow();
WorkbenchPage page = (WorkbenchPage) window.getActivePage();
IProject testProject = findOrCreateProject(PROJECT_NAME);
MultiEditorInput input = generateEditorInput(simpleFiles, testProject);
// catches the framework NPE
IEditorPart editor = openAndValidateEditor(page, input);
// did we get a multieditor back?
assertTrue(editor instanceof MultiEditor);
MultiEditor multiEditor = (MultiEditor) editor;
chewUpEvents();
// get access to the appropriate coolbar
IContributionItem contribution = findMyCoolBar(page);
// our test editor contribution should not be visible
// but it should be enabled
validateIconState(contribution, ACTION_TOOLTIP, false);
// swap focus to the last editor, which is the test editor
// with the test coolbar contribution
IEditorPart[] innerEditors = multiEditor.getInnerEditors();
innerEditors[innerEditors.length - 1].setFocus();
chewUpEvents();
contribution = findMyCoolBar(page);
assertNotNull("It should be available now", contribution);
// our test editor contribution should now be visible and
// enabled
validateIconState(contribution, ACTION_TOOLTIP, true);
}
/**
* Test that the outline view is updated when focus moves from an editor to
* the ant editor.
*
* Test: Open 2 files where the first is a text file and the second is an
* ant file. Set focus on the ant file, and the outline should be updated to
* reflect the buildfile outline.
*
* @throws Throwable
* on an error
*/
public void testTrackOutline() throws Throwable {
final String[] simpleFiles = { TEST01_TXT, TEST02_TXT,
TEST04_PROPERTIES, BUILD_XML, TEST03_ETEST };
IWorkbenchWindow window = fWorkbench.getActiveWorkbenchWindow();
WorkbenchPage page = (WorkbenchPage) window.getActivePage();
IProject testProject = findOrCreateProject(PROJECT_NAME);
MultiEditorInput input = generateEditorInput(simpleFiles, testProject);
// catches the framework NPE
IEditorPart editor = openAndValidateEditor(page, input);
// did we get a multieditor back?
assertTrue(editor instanceof MultiEditor);
MultiEditor multiEditor = (MultiEditor) editor;
chewUpEvents();
// Swap to the second last editor, which should be the ant
// build editor.
IEditorPart[] innerEditors = multiEditor.getInnerEditors();
innerEditors[innerEditors.length - 2].setFocus();
chewUpEvents();
// get the outline view part
IViewPart outline = window.getActivePage().showView(CONTENT_OUTLINE);
assertNotNull(outline);
// find out who is contributing the outline view.
IContributedContentsView view = (IContributedContentsView) outline
.getAdapter(IContributedContentsView.class);
IWorkbenchPart part = view.getContributingPart();
assertNotNull("The Outline view has not been updated by the editor",
part);
assertTrue("The Outline view is not talking to an editor",
part instanceof IEditorPart);
IEditorPart outlineEditor = (IEditorPart) part;
// the active inner editor (the ant editor) should also
// be the outline editor contributor ... this works in
// 3.0, fails in 3.1
assertEquals("The Outline view is not talking to the correct editor",
multiEditor.getActiveEditor(), outlineEditor);
page.closeEditor(editor, false);
chewUpEvents();
view = (IContributedContentsView) outline
.getAdapter(IContributedContentsView.class);
assertNull(view.getContributingPart());
}
/**
* Return the test editor coolbar.
*
* @param page
* the workbench page
* @return the IContributionItem for the test editor cool bar.
*/
private IContributionItem findMyCoolBar(WorkbenchPage page) {
// listItems(page);
IContributionItem contribution = ((IActionBars2) page.getActionBars())
.getCoolBarManager().find(TESTEDITOR_COOLBAR);
// assertNotNull(contribution);
return contribution;
}
/**
* Validate the state of an icon in the toolbar.
*
* @param contribution
* the high level contribution from the coolbar to look through
* @param tooltip
* the string that matches the action's tooltip
* @param state
* should it be true or false
*/
private void validateIconState(IContributionItem contribution,
String tooltip, boolean state) {
assertTrue("We might not have the contribution or expect it",
contribution != null || !state);
if (contribution == null) {
return;
}
ToolBarManager toolBarManager = (ToolBarManager) ((ToolBarContributionItem) contribution)
.getToolBarManager();
ToolBar bar = toolBarManager.getControl();
assertTrue("It's OK for bar to be null if we expect state to be false",
bar != null || !state);
if (bar == null) {
return;
}
ToolItem[] items = bar.getItems();
for (int i = 0; i < items.length; ++i) {
// System.err.println("Item: " + items[i].getToolTipText());
if (tooltip.equals(items[i].getToolTipText())) {
assertEquals("Invalid icon state for " + tooltip, state,
items[i].getEnabled());
return;
}
}
assertFalse("We haven't found our item", state);
}
/**
* Create the project to work in. If it already exists, just open it.
*
* @param projectName
* the name of the project to create
* @return the newly opened project
* @throws CoreException
*/
private IProject findOrCreateProject(String projectName)
throws CoreException {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IProject testProject = workspace.getRoot().getProject(projectName);
if (!testProject.exists()) {
testProject.create(null);
}
testProject.open(null);
return testProject;
}
/**
* Internal printing method, only used for investigation.
*
* @param page
* the workbench page
*/
private void checkView(WorkbenchPage page) {
IViewPart[] views = page.getViews();
for (int i = 0; i < views.length; ++i) {
System.err.println("view: " + views[i].getViewSite().getId() + "/"
+ views[i].getViewSite().getRegisteredName());
}
}
/**
* Print the call history to console. Only used for investigation.
*
* @param history
* the editor call history object.
*/
private void listHistory(CallHistory history) {
history.printToConsole();
}
/**
* List bits and internals of the coolbar manager contribution items. Only
* used for investigation.
*
* @param page
* the workbench page
*/
private void listItems(WorkbenchPage page) {
checkView(page);
IContributionItem[] items = ((IActionBars2) page.getActionBars())
.getCoolBarManager().getItems();
System.err.println("Length: " + items.length);
for (int i = 0; i < items.length; ++i) {
System.err.println("" + items[i].isEnabled() + ":"
+ items[i].isVisible() + " " + items[i].getId() + "\n\t"
+ items[i].getClass().getName());
if (items[i] instanceof ToolBarContributionItem) {
displayItem(items[i]);
}
}
System.err.println("----");
}
/**
* Display bits of contribution item internals to the console. Only used for
* investigation.
*
* @param contributionItem
* the IContributionItem to display
*/
private void displayItem(IContributionItem contributionItem) {
ToolBarManager toolBarManager = (ToolBarManager) ((ToolBarContributionItem) contributionItem)
.getToolBarManager();
ToolBar bar = toolBarManager.getControl();
if (bar == null) {
System.err.println("\tInfo-items: -1");
} else {
System.err.println("\tInfo-items: " + bar.getItemCount() + "/"
+ bar.getEnabled() + "/" + bar.isEnabled() + "/"
+ bar.isVisible());
ToolItem[] tools = bar.getItems();
for (int t = 0; t < tools.length; ++t) {
System.err.println("\t\titem: " + tools[t].getEnabled() + " "
+ tools[t].getToolTipText());
}
}
}
/**
* After an internal action, see if there are any outstanding SWT events.
*/
private void chewUpEvents() throws InterruptedException {
Thread.sleep(500);
Display display = Display.getCurrent();
while (display.readAndDispatch())
;
}
/**
* Open the test editor. It does basic validation that there is no
* NullPointerException during initialization.
*
* @param page
* the workbench page
* @param input
* the editor input with multiple files
* @return the MultiEditor
* @throws PartInitException
*/
private IEditorPart openAndValidateEditor(IWorkbenchPage page,
MultiEditorInput input) throws PartInitException {
IEditorPart editorPart = null;
try {
setupErrorListener();
editorPart = page
.openEditor(input, MultiEditorTest.TILED_EDITOR_ID);
assertNotNull(editorPart);
// 3.1.0 only
// assertFalse("The editor never actualized",
// editorPart instanceof ErrorEditorPart);
assertTrue("Creation error: " + fErrorListener.fErrorMsg,
fErrorListener.fNoError);
} finally {
removeErrorListener();
}
return editorPart;
}
/**
* Set up to catch any editor initialization exceptions.
*
*/
private void setupErrorListener() {
final ILog log = WorkbenchPlugin.getDefault().getLog();
fErrorListener = new EditorErrorListener();
log.addLogListener(fErrorListener);
}
/**
* Remove the editor error listener.
*/
private void removeErrorListener() {
final ILog log = WorkbenchPlugin.getDefault().getLog();
if (fErrorListener != null) {
log.removeLogListener(fErrorListener);
fErrorListener = null;
}
}
/**
* Create the multi editor input in the given project. Creates the files in
* the project from template files in the classpath if they don't already
* exist.
*
* @param simpleFiles
* the array of filenames to copy over
* @param testProject
* the project to create the files in
* @return the editor input used to open the multieditor
* @throws CoreException
* @throws IOException
*/
private MultiEditorInput generateEditorInput(String[] simpleFiles,
IProject testProject) throws CoreException, IOException {
String[] ids = new String[simpleFiles.length];
IEditorInput[] inputs = new IEditorInput[simpleFiles.length];
IEditorRegistry registry = fWorkbench.getEditorRegistry();
for (int f = 0; f < simpleFiles.length; ++f) {
IFile f1 = testProject.getFile(simpleFiles[f]);
if (!f1.exists()) {
URL file = Platform.asLocalURL(TestPlugin.getDefault()
.getBundle().getEntry(DATA_FILES_DIR + simpleFiles[f]));
f1.create(file.openStream(), true, null);
}
ids[f] = registry.getDefaultEditor(f1.getName()).getId();
inputs[f] = new FileEditorInput(f1);
}
MultiEditorInput input = new MultiEditorInput(ids, inputs);
return input;
}
/**
* Close any editors at the beginner of a test, so the test can be clean.
*/
protected void doSetUp() throws Exception {
super.doSetUp();
IWorkbenchPage page = fWorkbench.getActiveWorkbenchWindow()
.getActivePage();
page.closeAllEditors(false);
}
/**
* Listens for the standard message that indicates the MultiEditor failed
* ... usually caused by incorrect framework initialization that doesn't set
* the innerChildren.
*
* @since 3.1
*
*/
public static class EditorErrorListener implements ILogListener {
public boolean fNoError = true;
public String fErrorMsg = null;
public void logging(IStatus status, String plugin) {
fNoError = false;
fErrorMsg = status.getMessage();
Throwable ex = status.getException();
if (ex != null) {
fErrorMsg += ": " + ex.getMessage();
}
}
}
}