blob: 916342292de3bc79257340e757d040281b88ea71 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2021 Versant Corp. and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Markus Alexander Kuppe (Versant Corp.) - https://bugs.eclipse.org/248103
******************************************************************************/
package org.eclipse.ui.tests.propertysheet;
import java.lang.reflect.Field;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.NotEnabledException;
import org.eclipse.core.commands.NotHandledException;
import org.eclipse.core.commands.common.NotDefinedException;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.ILogListener;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.widgets.Event;
import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.IPerspectiveDescriptor;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.internal.util.BundleUtility;
import org.eclipse.ui.part.IPage;
import org.eclipse.ui.tests.SelectionProviderView;
import org.eclipse.ui.tests.harness.util.FileUtil;
import org.eclipse.ui.tests.session.NonRestorableView;
import org.eclipse.ui.views.properties.IPropertySheetEntry;
import org.eclipse.ui.views.properties.NewPropertySheetHandler;
import org.eclipse.ui.views.properties.PropertySheet;
import org.eclipse.ui.views.properties.PropertySheetEntry;
import org.eclipse.ui.views.properties.PropertySheetPage;
import org.eclipse.ui.views.properties.PropertyShowInContext;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.osgi.framework.Bundle;
/**
* @since 3.4
*/
@RunWith(JUnit4.class)
public class MultiInstancePropertySheetTest extends AbstractPropertySheetTest {
/**
* TestPropertySheetPage exposes certain members for testability
*/
private TestPropertySheetPage testPropertySheetPage;
private SelectionProviderView selectionProviderView;
/**
* An exception of an error status that has been logged by the platform.
*/
private Exception e;
/**
* A log listener to monitor the platform's log for any error statuses. As
* many listeners are notified of events through a SafeRunner, errors caused
* by mishandling of events are not propagated back to our test methods.
*/
private ILogListener logListener = (status, plugin) -> {
// check if it's an error
if (status.getSeverity() == IStatus.ERROR) {
// retrieve the underlying exception and wrap it if possible
Throwable t = status.getException();
if (t != null) {
e = new Exception(t);
} else {
e = new Exception(status.getMessage());
}
}
};
private IProject project;
public MultiInstancePropertySheetTest() {
super(MultiInstancePropertySheetTest.class.getSimpleName());
}
@Override
protected void doSetUp() throws Exception {
super.doSetUp();
testPropertySheetPage = new TestPropertySheetPage();
// open the property sheet with the TestPropertySheetPage
Platform.getAdapterManager().registerAdapters(testPropertySheetPage,
PropertySheet.class);
PropertySheetPerspectiveFactory.applyPerspective(activePage);
propertySheet = (PropertySheet) activePage
.showView(IPageLayout.ID_PROP_SHEET);
selectionProviderView = (SelectionProviderView) activePage
.showView(SelectionProviderView.ID);
}
@Override
protected void doTearDown() throws Exception {
activePage.resetPerspective();
super.doTearDown();
// reset the exception to null
e = null;
// remove our log listener
Platform.removeLogListener(logListener);
Platform.getAdapterManager().unregisterAdapters(testPropertySheetPage,
PropertySheet.class);
testPropertySheetPage.dispose();
testPropertySheetPage = null;
selectionProviderView = null;
if (project != null) {
FileUtil.deleteProject(project);
project = null;
}
}
/**
* The if the registered {@link TestPropertySheetPage} is set as the default
* page of the PropertySheet
*
* @throws PartInitException
*/
@Test
public void testDefaultPage() throws PartInitException {
PropertySheet propertySheet = (PropertySheet) activePage
.showView(IPageLayout.ID_PROP_SHEET);
assertTrue(propertySheet.getCurrentPage() instanceof PropertySheetPage);
}
/**
* Test if the registered {@link TestPropertySheetPage} is set as the
* default page of the PropertyShecet
*
* @throws PartInitException
*/
@Test
public void testDefaultPageAdapter() throws PartInitException {
PropertySheet propertySheet = (PropertySheet) activePage
.showView(IPageLayout.ID_PROP_SHEET);
assertTrue(propertySheet.getCurrentPage() instanceof TestPropertySheetPage);
}
/**
* Test if the PropertySheet allows multiple instances
*
* @throws PartInitException
*/
@Test
public void testAllowsMultiple() throws PartInitException {
activePage.showView(IPageLayout.ID_PROP_SHEET);
try {
activePage.showView(IPageLayout.ID_PROP_SHEET, "aSecondaryId",
IWorkbenchPage.VIEW_ACTIVATE);
} catch (PartInitException e) {
fail(e.getMessage());
}
}
/**
* Test if the PropertySheet follows selection
*
* @throws Throwable
*/
@Test
public void testFollowsSelection() throws Throwable {
// selection before selection changes
TestPropertySheetPage firstPage = (TestPropertySheetPage) propertySheet
.getCurrentPage();
Object firstSelection = firstPage.getSelection();
assertNotNull(firstSelection);
// change the selection explicitly
selectionProviderView.setSelection(new Object());
TestPropertySheetPage secondPage = (TestPropertySheetPage) propertySheet
.getCurrentPage();
assertNotSame("PropertySheet hasn't changed selection", firstSelection,
secondPage.getSelection());
}
/**
* Test if the PropertySheet follows part events
*
* @throws Throwable
*/
@Test
public void testFollowsParts() throws Throwable {
// selection before selection changes
TestPropertySheetPage firstPage = (TestPropertySheetPage) propertySheet
.getCurrentPage();
Object firstPart = firstPage.getPart();
assertNotNull(firstPart);
// change the part explicitly (reusing the NonRestorableView here)
TestPropertySheetPage testPropertySheetPage2 = new TestPropertySheetPage();
Platform.getAdapterManager().registerAdapters(testPropertySheetPage2,
org.eclipse.ui.tests.session.NonRestorableView.class);
activePage.showView(NonRestorableView.ID);
TestPropertySheetPage secondPage = (TestPropertySheetPage) propertySheet
.getCurrentPage();
assertEquals(testPropertySheetPage2, secondPage);
assertNotSame("PropertySheet hasn't changed selection", firstPart,
secondPage.getSelection());
Platform.getAdapterManager().unregisterAdapters(testPropertySheetPage2,
org.eclipse.ui.tests.session.NonRestorableView.class);
}
/**
* Test if pinning works in the PropertySheet
*
* @throws Throwable
*/
@Test
public void testPinning() throws Throwable {
// execute the pin action on the property sheet
IAction action = getPinPropertySheetAction(propertySheet);
action.setChecked(true);
// get the content of the pinned property sheet for later comparison
TestPropertySheetPage firstPage = (TestPropertySheetPage) propertySheet
.getCurrentPage();
assertNotNull(firstPage);
Object firstSelection = firstPage.getSelection();
assertNotNull(firstSelection);
IWorkbenchPart firstPart = firstPage.getPart();
assertNotNull(firstPart);
// change the selection/part
selectionProviderView.setSelection(new Object());
TestPropertySheetPage testPropertySheetPage2 = new TestPropertySheetPage();
Platform.getAdapterManager().registerAdapters(testPropertySheetPage2,
org.eclipse.ui.tests.session.NonRestorableView.class);
activePage.showView(NonRestorableView.ID);
TestPropertySheetPage secondPage = (TestPropertySheetPage) propertySheet
.getCurrentPage();
assertEquals("PropertySheet has changed page", firstPage, secondPage);
assertEquals("PropertySheetPage has changed selection", firstSelection,
secondPage.getSelection());
assertEquals("PropertySheetPage has changed part", firstPart,
secondPage.getPart());
Platform.getAdapterManager().unregisterAdapters(testPropertySheetPage2,
org.eclipse.ui.tests.session.NonRestorableView.class);
}
/**
* Test if the PropertySheet unpinns if the contributing part is closed
*
* @throws Throwable
*/
@Test
public void testUnpinningWhenPinnedPartIsClosed() throws Throwable {
// execute the pin action on the property sheet
IAction action = getPinPropertySheetAction(propertySheet);
action.setChecked(true);
// close the part the property sheet is pinned to
activePage.hideView(selectionProviderView);
// the action and therefore the property sheet should be unpinned
assertFalse(action.isChecked());
}
/**
* Test if the PropertySheet's new handler creates a new instance
*
* @throws NotHandledException
* @throws NotEnabledException
* @throws NotDefinedException
* @throws ExecutionException
*/
@Test
public void testNewPropertySheet() throws Exception {
assertEquals(1, countPropertySheetViews());
Platform.addLogListener(logListener);
executeNewPropertySheetHandler();
assertEquals(2, countPropertySheetViews());
if (e != null) {
throw e;
}
}
/**
* Test if the PropertySheet's new handler creates a new instance without
* errors if the original view has no selection
*/
@Test
public void testNewPropertySheetNoSelection() throws Exception {
activePage.hideView(selectionProviderView);
propertySheet = (PropertySheet) activePage.showView(IPageLayout.ID_PROP_SHEET);
assertEquals(1, countPropertySheetViews());
PropertyShowInContext context = (PropertyShowInContext) propertySheet.getShowInContext();
assertNull(context.getPart());
Platform.addLogListener(logListener);
executeNewPropertySheetHandler();
assertEquals(2, countPropertySheetViews());
if (e != null) {
throw e;
}
}
private void executeNewPropertySheetHandler() throws Exception {
// Activate bundle to make sure HandlerProxy.isOkToLoad() allows to load the
// handler to avoid NotHandledException error
String bundleId = "org.eclipse.ui.views";
boolean activated = BundleUtility.isActivated(bundleId);
if (!activated) {
Bundle bundle = Platform.getBundle(bundleId);
bundle.start();
}
// the propertysheet is the active part if its view toolbar command gets
// pressed
activePage.activate(propertySheet);
IHandlerService handlerService = PlatformUI
.getWorkbench().getService(IHandlerService.class);
Event event = new Event();
handlerService.executeCommand(NewPropertySheetHandler.ID, event);
}
/**
* Test if the PropertySheet pins the parent if a second instance is opened
*
* @throws NotHandledException
* @throws NotEnabledException
* @throws NotDefinedException
* @throws ExecutionException
*/
@Test
public void testParentIsPinned() throws Exception {
executeNewPropertySheetHandler();
IAction pinAction = getPinPropertySheetAction(propertySheet);
assertTrue("Parent property sheet isn't pinned", pinAction.isChecked());
}
/**
* Test if the PropertySheet pins the parent if a second instance is opened
*
* @throws Throwable
*/
@Test
public void testPinningWithMultipleInstances() throws Throwable {
executeNewPropertySheetHandler();
testPinning();
}
/**
* Tests that pinning a property sheet ensures that the content continues to
* be rendered even if the original target part is not visible and is behind
* another part in the part stack.
*
* @throws CoreException
*/
@Test
public void testBug268676HidingPinnedTargetPart() throws CoreException {
IPerspectiveDescriptor desc = activePage.getWorkbenchWindow().getWorkbench()
.getPerspectiveRegistry().findPerspectiveWithId(IDE.RESOURCE_PERSPECTIVE_ID);
// open the 'Resource' perspective
activePage.setPerspective(desc);
activePage.hideView(selectionProviderView);
propertySheet = (PropertySheet) activePage.showView(IPageLayout.ID_PROP_SHEET);
// create a project for properties rendering purposes
project = FileUtil.createProject("projectToSelect");
ISelection selection = new StructuredSelection(project);
// show the 'Navigator'
IViewPart navigator = activePage.showView(IPageLayout.ID_RES_NAV);
// have the 'Navigator' select it
navigator.getSite().getSelectionProvider().setSelection(selection);
// verify that the 'Navigator' uses a regular property sheet page
assertTrue("The 'Properties' view should render the content of the 'Navigator' in a regular property sheet page",
propertySheet.getCurrentPage() instanceof PropertySheetPage);
// show the 'Project Explorer'
IViewPart projectExplorer = activePage.showView(IPageLayout.ID_PROJECT_EXPLORER);
// have the 'Project Explorer' select it
projectExplorer.getSite().getSelectionProvider().setSelection(selection);
assertFalse("The 'Navigator' should be hidden behind the 'Project Explorer'",
activePage.isPartVisible(navigator));
assertTrue("The 'Project Explorer' should be visible in front of the 'Navigator'",
activePage.isPartVisible(projectExplorer));
// verify that the 'Project Explorer' uses a non-standard property sheet page
assertFalse("The 'Properties' view should be showing the content of the 'Project Explorer' in a tabbed property sheet, not a regular one",
propertySheet.getCurrentPage() instanceof PropertySheetPage);
// pin the tabbed property sheet page
IAction action = getPinPropertySheetAction(propertySheet);
action.setChecked(true);
// now activate the 'Navigator' so a partHidden event is fired for the
// 'Project Explorer', just because it is hidden should _not_ mean the
// _pinned_ 'Properties' view should stop rendering its content though,
// this is the bug being tested
activePage.activate(navigator);
// verify that the page is not a non-standard property sheet page
assertFalse("The 'Properties' view should still be on the content of the 'Project Explorer' rendering a tabbed property sheet",
propertySheet.getCurrentPage() instanceof PropertySheetPage);
}
/**
* Tests bug 278514. If a view that contributes to the 'Properties' view and
* the 'Properties' view is opened is opened in one perspective and the page
* then switches to another perspective where the contributing view is there
* but not the 'Properties' view, an NPE is thrown.
*
* <ol>
* <li>close all perspectives</li>
* <li>open perspective A</li>
* <li>open contributing view</li>
* <li>switch to perspective B</li>
* <li>open contributing view</li>
* <li>open 'Properties' view</li>
* <li>set a selection in the contributing view so the 'Properties' view
* shows something</li>
* <li>switch back to perspective A</li>
* <li>NPE is thrown</li>
* </ol>
*
* @param viewId the id of the contributing view
* @param standardPage <code>true</code> if the contributing view contributes properties without specifying a custom page, <code>false</code> otherwise
* @throws Exception if an exception occurred while switching perspectives
*/
private void testBug278514(String viewId, boolean standardPage) throws Exception {
// close all perspectives
activePage.closeAllPerspectives(false, false);
// open the 'Resource' perspective
IPerspectiveDescriptor desc = activePage.getWorkbenchWindow().getWorkbench()
.getPerspectiveRegistry().findPerspectiveWithId(IDE.RESOURCE_PERSPECTIVE_ID);
activePage.setPerspective(desc);
// hide all 'Properties' view instances, if any
IViewReference[] viewReferences = activePage.getViewReferences();
for (IViewReference viewReference : viewReferences) {
if (IPageLayout.ID_PROP_SHEET.equals(viewReference.getId())) {
activePage.hideView(viewReference);
}
}
processUiEvents();
// TODO this is required here because the default page is never properly
// disposed.
testPropertySheetPage.dispose();
// show the view that will contribute to the 'Properties' view
activePage.showView(viewId);
// open another perspective
PropertySheetPerspectiveFactory.applyPerspective(activePage);
// show the 'Properties' view
propertySheet = (PropertySheet) activePage.showView(IPageLayout.ID_PROP_SHEET);
// create a project for properties rendering purposes
project = FileUtil.createProject("projectToSelect");
ISelection selection = new StructuredSelection(project);
// show the contributing view
IViewPart contributingView = activePage.showView(viewId);
// have the contributing view select the created project, this should populate the 'Properties' view
contributingView.getSite().getSelectionProvider().setSelection(selection);
if (standardPage) {
// verify that the contributing view uses standard property sheet page
assertTrue("The 'Properties' view should be showing the content of the contributing view (" + contributingView.getTitle() + ") in a regular property page",
propertySheet.getCurrentPage() instanceof PropertySheetPage);
} else {
// verify that the contributing view uses non-standard property sheet page
assertFalse("The 'Properties' view should be showing the content of the contributing view (" + contributingView.getTitle() + ") in a non-standard customiezd page",
propertySheet.getCurrentPage() instanceof PropertySheetPage);
}
// add our log listener so we can monitor for failures when switching perspectives
Platform.addLogListener(logListener);
// switch the perspective, the original bug threw an NPE within a listener
activePage.setPerspective(desc);
if (e != null) {
// if we caught an exception in our log listener, throw it so this test fails
throw e;
}
}
/**
* Tests bug 278514 using a view that contributes to the 'Properties' view
* without using a customized page. This test uses the 'Navigator' view to
* achieve this.
*
* @see #testBug278514(String, boolean)
*/
@Test
public void testBug278514NormalProperties() throws Exception {
testBug278514(IPageLayout.ID_PROGRESS_VIEW, true);
}
/**
* Tests bug 278514 using a view that contributes to the 'Properties' view
* that uses a custom properties page. This test uses the 'Project Explorer'
* view to achieve this. The 'Project Explorer' renders content within the
* 'Properties' view using tabbed properties.
*
* @see #testBug278514(String, boolean)
*/
@Test
public void testBug278514TabbedProperties() throws Exception {
testBug278514(IPageLayout.ID_PROJECT_EXPLORER, false);
}
@Test
public void testPageDispose() throws Exception {
// close all perspectives
activePage.closeAllPerspectives(false, false);
// open the 'Resource' perspective
IPerspectiveDescriptor desc = activePage.getWorkbenchWindow().getWorkbench().getPerspectiveRegistry()
.findPerspectiveWithId(IDE.RESOURCE_PERSPECTIVE_ID);
activePage.setPerspective(desc);
// hide all 'Properties' view instances, if any
IViewReference[] viewReferences = activePage.getViewReferences();
for (IViewReference viewReference : viewReferences) {
if (IPageLayout.ID_PROP_SHEET.equals(viewReference.getId())) {
activePage.hideView(viewReference);
}
}
processUiEvents();
// TODO this is required here because the default page is never properly
// disposed.
testPropertySheetPage.dispose();
// at this place we get an "already disposed" error because we are tying
// to reuse default page
propertySheet = (PropertySheet) activePage.showView(IPageLayout.ID_PROP_SHEET);
}
/**
* Tests bug 425525 using a view that contributes to the 'Properties' view
* without using a customized page. This test uses the 'Navigator' view to
* achieve this.
*
* @see #testBug425525(String, boolean)
*/
@Test
public void testInitialSelectionWithNormalProperties() throws Exception {
testBug425525(IPageLayout.ID_RES_NAV, true);
}
/**
* Tests bug 425525 using a view that contributes to the 'Properties' view
* that uses a custom properties page. This test uses the 'Project Explorer'
* view to achieve this. The 'Project Explorer' renders content within the
* 'Properties' view using tabbed properties.
*
* @see #testBug425525(String, boolean)
*/
@Test
public void testInitialSelectionWithTabbedProperties() throws Exception {
testBug425525(IPageLayout.ID_PROJECT_EXPLORER, false);
}
private void testBug425525(String viewId, boolean standardPage) throws Exception {
Platform.getAdapterManager().unregisterAdapters(testPropertySheetPage, PropertySheet.class);
// close all perspectives
activePage.closeAllPerspectives(false, false);
// open the 'Resource' perspective
IPerspectiveDescriptor desc = activePage.getWorkbenchWindow().getWorkbench().getPerspectiveRegistry()
.findPerspectiveWithId(IDE.RESOURCE_PERSPECTIVE_ID);
activePage.setPerspective(desc);
// hide all 'Properties' view instances, if any
IViewReference[] viewReferences = activePage.getViewReferences();
for (IViewReference viewReference : viewReferences) {
if (IPageLayout.ID_PROP_SHEET.equals(viewReference.getId())) {
activePage.hideView(viewReference);
}
}
processUiEvents();
// TODO this is required here because the default page is never properly
// disposed.
testPropertySheetPage.dispose();
// create a project for properties rendering purposes
project = FileUtil.createProject("projectToSelect");
ISelection selection = new StructuredSelection(project);
// show the contributing view
IViewPart contributingView = activePage.showView(viewId);
// have the contributing view select the created project, this should
// populate the 'Properties' view
contributingView.getSite().getSelectionProvider().setSelection(selection);
processUiEvents();
// show the 'Properties' view: it should pick up content from the only
// one relevant part: view with given id
propertySheet = (PropertySheet) activePage.showView(IPageLayout.ID_PROP_SHEET);
IPage currentPage = propertySheet.getCurrentPage();
if (standardPage) {
if (currentPage instanceof PropertySheetPage) {
PropertySheetPage psp = (PropertySheetPage) currentPage;
Field root = PropertySheetPage.class.getDeclaredField("rootEntry");
root.setAccessible(true);
PropertySheetEntry pse = (PropertySheetEntry) root.get(psp);
IPropertySheetEntry[] entries = pse.getChildEntries();
assertTrue("The 'Properties' view should be showing the content of the contributing view ("
+ contributingView.getTitle() + "), but shows nothing", entries.length > 0);
} else {
assertTrue(
"The 'Properties' view should be showing the content of the contributing view ("
+ contributingView.getTitle() + ") in a regular property page",
currentPage instanceof PropertySheetPage);
}
} else {
// verify that the contributing view uses non-standard property
// sheet page
assertFalse(
"The 'Properties' view should be showing the content of the contributing view ("
+ contributingView.getTitle() + ") in a non-standard customiezd page",
currentPage instanceof PropertySheetPage);
}
}
private void processUiEvents() {
while (fWorkbench.getDisplay().readAndDispatch()) {
}
}
}