blob: 7095eda20e63a658faf5c550c98ed99767651ec2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 EclipseSource Muenchen GmbH 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:
* Alexandra Buzila - initial API and implementation
******************************************************************************/
package org.eclipse.e4.ui.tests.workbench;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.internal.registry.ExtensionRegistry;
import org.eclipse.core.runtime.ContributorFactorySimple;
import org.eclipse.core.runtime.IContributor;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.RegistryFactory;
import org.eclipse.e4.core.contexts.ContextInjectionFactory;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.services.log.Logger;
import org.eclipse.e4.ui.internal.workbench.E4XMIResource;
import org.eclipse.e4.ui.internal.workbench.E4XMIResourceFactory;
import org.eclipse.e4.ui.internal.workbench.ExtensionsSort;
import org.eclipse.e4.ui.internal.workbench.ModelAssembler;
import org.eclipse.e4.ui.internal.workbench.swt.E4Application;
import org.eclipse.e4.ui.model.application.MApplication;
import org.eclipse.e4.ui.model.application.MApplicationElement;
import org.eclipse.e4.ui.model.application.impl.ApplicationFactoryImpl;
import org.eclipse.e4.ui.model.application.ui.MUIElement;
import org.eclipse.e4.ui.model.application.ui.advanced.MArea;
import org.eclipse.e4.ui.model.application.ui.advanced.MPlaceholder;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.eclipse.e4.ui.model.application.ui.basic.MTrimmedWindow;
import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
import org.eclipse.e4.ui.model.fragment.MFragmentFactory;
import org.eclipse.e4.ui.model.fragment.MModelFragment;
import org.eclipse.e4.ui.model.fragment.MModelFragments;
import org.eclipse.e4.ui.model.fragment.MStringModelFragment;
import org.eclipse.e4.ui.workbench.Selector;
import org.eclipse.e4.ui.workbench.modeling.EModelService;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
@SuppressWarnings("nls")
public class ModelAssemblerTests {
private static final String EXTENSION_POINT_ID = "org.eclipse.e4.workbench.model";
private static final String BUNDLE_SYMBOLIC_NAME = "org.eclipse.e4.ui.tests";
private static final String APPLICATION_ID = "org.eclipse.e4.ui.tests.modelassembler.app";
private IEclipseContext appContext;
private MApplication application;
private E4XMIResourceFactory factory;
private ResourceSetImpl resourceSet;
private E4XMIResource appResource;
private ModelAssembler assembler;
private Logger logger;
private EModelService modelService;
@Before
public void setup() {
appContext = E4Application.createDefaultContext();
application = ApplicationFactoryImpl.eINSTANCE.createApplication();
application.setElementId(APPLICATION_ID);
application.setContext(appContext);
logger = mock(Logger.class);
appContext.set(Logger.class, logger);
appContext.set(MApplication.class, application);
factory = new E4XMIResourceFactory();
appResource = (E4XMIResource) factory.createResource(URI.createURI("virtualuri"));
resourceSet = new ResourceSetImpl();
resourceSet.getResources().add(appResource);
appResource.getContents().add((EObject) application);
assembler = new ModelAssembler();
ContextInjectionFactory.inject(assembler, appContext);
modelService = application.getContext().get(EModelService.class);
}
/**
* Test the handling of a fragment contribution with no elements to merge.
*
* @throws Exception
*/
@Test
public void testFragments_emptyFragment() throws Exception {
MModelFragment fragment = MFragmentFactory.INSTANCE.createStringModelFragment();
final String contributorURI = "testFragments_emptyFragment_contribURI";
List<MApplicationElement> elements = assembler.processModelFragment(fragment, contributorURI, true);
assertTrue(elements.isEmpty());
List<MApplicationElement> modelElements = modelService.findElements(application, MApplicationElement.class,
EModelService.ANYWHERE, new Selector() {
@Override
public boolean select(MApplicationElement element) {
return element.getContributorURI() != null
&& element.getContributorURI().equals(contributorURI);
}
});
assertTrue(modelElements.isEmpty());
verifyZeroInteractions(logger);
}
/**
* Tests that fragments are correctly contributed to the application model.
*
* @throws Exception
*/
@Test
public void testFragments_workingFragment() throws Exception {
// the contributed element
MWindow window = modelService.createModelElement(MWindow.class);
final String contributedElementId = "testFragments_workingFragment-contributedWindow";
window.setElementId(contributedElementId);
// create fragment
MStringModelFragment fragment = MFragmentFactory.INSTANCE.createStringModelFragment();
fragment.setFeaturename("children");
final String fragmentParentId = "org.eclipse.e4.ui.tests.modelassembler.app";
fragment.setParentElementId(fragmentParentId);
fragment.getElements().add(window);
// add fragment to resource
Resource fragmentResource = factory.createResource(URI.createURI("fragmentvirtualuri"));
resourceSet.getResources().add(fragmentResource);
fragmentResource.getContents().add((EObject) fragment);
assertEquals(null, modelService.find(contributedElementId, application));
final String contributorURI = "testFragments_emptyFragment_contribURI";
List<MApplicationElement> elements = assembler.processModelFragment(fragment, contributorURI, false);
assertEquals(window, modelService.find(contributedElementId, application));
assertEquals(1, elements.size());
assertEquals(contributorURI, elements.get(0).getContributorURI());
assertTrue(elements.contains(window));
MUIElement found = modelService.find(contributedElementId, application);
assertEquals(window, found);
assertEquals(fragmentParentId, found.getParent().getElementId());
verifyZeroInteractions(logger);
}
@Test
@Ignore // currently ignored due to bug 487748
public void testFragments_existingXMIID_checkExists() throws Exception {
// create fragment
MStringModelFragment fragment = MFragmentFactory.INSTANCE.createStringModelFragment();
fragment.setFeaturename("children");
fragment.setParentElementId("org.eclipse.e4.ui.tests.modelassembler.app");
// create fragment resource
E4XMIResource fragmentResource = (E4XMIResource) factory.createResource(URI.createURI("fragmentvirtualuri"));
resourceSet.getResources().add(fragmentResource);
fragmentResource.getContents().add((EObject) fragment);
final String contributedElementId = "testFragments_existingElementID-contributedWindow";
MWindow window1 = modelService.createModelElement(MWindow.class);
window1.setElementId(contributedElementId);
MWindow window2 = modelService.createModelElement(MWindow.class);
window2.setElementId(contributedElementId);
// add window1 to app and window2 to fragment
application.getChildren().add(window1);
fragment.getElements().add(window2);
// set the same resource xmi id to window1 and window2
final String xmiId = "testFragments_existingXMIID_XMIID";
appResource.setID((EObject) window1, xmiId);
fragmentResource.setID((EObject) window2, xmiId);
final String contributorURI = "testFragments_existingElementID_contribURI";
window1.setContributorURI(contributorURI);
window2.setContributorURI(contributorURI);
List<MApplicationElement> elements = assembler.processModelFragment(fragment,
contributorURI, true);
// fragment wasn't merged as the contributed element was already part of
// the application model
assertEquals(0, elements.size());
MUIElement found = modelService.find(contributedElementId, application);
assertEquals(window1, found);
verifyZeroInteractions(logger);
}
/**
* Tests that fragments configured to be always merged are correctly
* contributed to the application model, even if the model already contains
* the contributed element.
*
* @throws Exception
*/
@Test
public void testFragments_existingXMIID_ignoreExists() throws Exception {
// create fragment
MStringModelFragment fragment = MFragmentFactory.INSTANCE.createStringModelFragment();
fragment.setFeaturename("children");
fragment.setParentElementId("org.eclipse.e4.ui.tests.modelassembler.app");
// create fragment resource
E4XMIResource fragmentResource = (E4XMIResource) factory.createResource(URI.createURI("fragmentvirtualuri"));
resourceSet.getResources().add(fragmentResource);
fragmentResource.getContents().add((EObject) fragment);
final String contributedElementId = "testFragments_existingElementID-contributedWindow";
MWindow window1 = modelService.createModelElement(MWindow.class);
window1.setElementId(contributedElementId);
MWindow window2 = modelService.createModelElement(MWindow.class);
window2.setElementId(contributedElementId);
// add window1 to app and window2 to fragment
application.getChildren().add(window1);
fragment.getElements().add(window2);
// set the same resource xmi id to window1 and window2
final String xmiId = "testFragments_existingXMIID_XMIID";
appResource.setID((EObject) window1, xmiId);
fragmentResource.setID((EObject) window2, xmiId);
final String contributorID = "testFragments_existingElementID_contribURI";
List<MApplicationElement> elements = assembler.processModelFragment(fragment, contributorID, false);
assertEquals(elements.size(), 1);
MUIElement found = modelService.find(contributedElementId, application);
assertEquals(found, window2);
assertEquals(contributorID, found.getContributorURI());
verifyZeroInteractions(logger);
}
/** Tests that correctly configured imports are correctly handled. */
@Test
public void testImports() {
List<MApplicationElement> imports = new ArrayList<MApplicationElement>();
List<MApplicationElement> addedElements = new ArrayList<MApplicationElement>();
final String windowElementId = "testImports_emptyList_window1";
MTrimmedWindow importWindow1 = modelService.createModelElement(MTrimmedWindow.class);
importWindow1.setElementId(windowElementId);
MModelFragments fragment = MFragmentFactory.INSTANCE.createModelFragments();
fragment.getImports().add(importWindow1);
imports.add(importWindow1);
MTrimmedWindow realWindow1 = modelService.createModelElement(MTrimmedWindow.class);
realWindow1.setElementId(windowElementId);
application.getChildren().add(realWindow1);
MPlaceholder placeholder = modelService.createModelElement(MPlaceholder.class);
placeholder.setRef(importWindow1);
addedElements.add(placeholder);
assembler.resolveImports(imports, addedElements);
assertEquals(realWindow1, placeholder.getRef());
verifyZeroInteractions(logger);
}
/** Tests the processing of an import with a null/incorrect element id. */
@Test
public void testImports_noImportElementId() {
List<MApplicationElement> imports = new ArrayList<MApplicationElement>();
List<MApplicationElement> addedElements = new ArrayList<MApplicationElement>();
MTrimmedWindow importWindow1 = modelService.createModelElement(MTrimmedWindow.class);
importWindow1.setElementId(null);
MModelFragments fragment = MFragmentFactory.INSTANCE.createModelFragments();
fragment.getImports().add(importWindow1);
imports.add(importWindow1);
MTrimmedWindow realWindow1 = modelService.createModelElement(MTrimmedWindow.class);
realWindow1.setElementId("testImports_emptyList_window1");
application.getChildren().add(realWindow1);
MPlaceholder placeholder = modelService.createModelElement(MPlaceholder.class);
placeholder.setRef(importWindow1);
addedElements.add(placeholder);
assembler.resolveImports(imports, addedElements);
assertEquals(null, placeholder.getRef());
verify(logger).warn("Could not resolve an import element for 'null'");
verify(logger).warn("Could not resolve import for null");
verifyZeroInteractions(logger);
}
/**
* Make sure that all fragments and imports are resolved before the
* post-processors are run. For reference, see
* <a href="https://bugs.eclipse.org/475934">bug 475934</a>.
*
* @throws Exception
* if anything went wrong during the test
*
*/
@Test
public void testModelProcessingOrder() throws Exception {
/* setup application model */
/* this creates a window, containing a part and an area */
MTrimmedWindow trimmedWindow = modelService.createModelElement(MTrimmedWindow.class);
trimmedWindow.setElementId("testModelProcessingOrder-trimmedWindow");
application.getChildren().add(trimmedWindow);
MPart part = modelService.createModelElement(MPart.class);
part.setElementId("testModelProcessingOrder-part");
trimmedWindow.getChildren().add(part);
MArea area = modelService.createModelElement(MArea.class);
area.setElementId("testModelProcessingOrder-area");
trimmedWindow.getChildren().add(area);
/* contribute fragment with imports and post-processor */
IContributor contributor = ContributorFactorySimple.createContributor(BUNDLE_SYMBOLIC_NAME);
IExtensionRegistry registry = createTestExtensionRegistry();
assertEquals(0, registry.getConfigurationElementsFor(EXTENSION_POINT_ID).length);
// The fragment contributes a Placeholder to the application's Area. The
// Placeholder references the Part that we created above.
// Besides the Placeholder, the xml also contributes a
// post-processor(org.eclipse.e4.ui.tests.workbench.ModelAssemblerProcessingOrderPostProcessor).
// It will iterate over the elements of the application model and will
// make sure that no imports are left unresolved. The post-processor
// will throw an error if such elements are found and this test will
// fail.
String dataFilePath = "org.eclipse.e4.ui.tests/data/ModelAssembler/modelProcessingOrder.xml";
registry.addContribution(getContentsAsInputStream(dataFilePath), contributor, false, null, null, null);
assembler.processModel(true);
// the testing was done in the post-processor; if we didn't fail there,
// everything went fine.
verifyZeroInteractions(logger);
}
/**
* Tests that pre-processors running from a non-persisted state that are
* marked as "always" are executed.
*
* @throws Exception
*/
@Test
public void testPreProcessor_nonPersistedState_always() throws Exception {
testProcessor("org.eclipse.e4.ui.tests/data/ModelAssembler/processors_always.xml", true, false);
assertEquals(1, application.getDescriptors().size());
assertEquals("simpleprocessor.pre", application.getDescriptors().get(0).getElementId());
verifyZeroInteractions(logger);
}
/**
* Tests that pre-processors running from a persisted state that are marked
* as "always" are executed.
*
* @throws Exception
*/
@Test
public void testPreProcessor_persistedState_always() throws Exception {
testProcessor("org.eclipse.e4.ui.tests/data/ModelAssembler/processors_always.xml", false, false);
assertEquals(1, application.getDescriptors().size());
assertEquals("simpleprocessor.pre", application.getDescriptors().get(0).getElementId());
verifyZeroInteractions(logger);
}
/**
* Tests that pre-processors running from a non-persisted state and marked
* as "initial" are executed.
*
* @throws Exception
*/
@Test
public void testPreProcessor_nonPersistedState_initial() throws Exception {
testProcessor("org.eclipse.e4.ui.tests/data/ModelAssembler/processors_initial.xml", true, false);
assertEquals(1, application.getDescriptors().size());
assertEquals("simpleprocessor.pre", application.getDescriptors().get(0).getElementId());
verifyZeroInteractions(logger);
}
/**
* Tests that pre-processors running from a persisted state and marked as
* "initial" are not executed.
*
* @throws Exception
*/
@Test
public void testPreProcessor_persistedState_initial() throws Exception {
testProcessor("org.eclipse.e4.ui.tests/data/ModelAssembler/processors_initial.xml", false, false);
assertEquals(0, application.getDescriptors().size());
verifyZeroInteractions(logger);
}
/**
* Tests the execution of post-processors that should always be applied,
* running from a persisted state.
*
* @throws Exception
*/
@Test
public void testPostProcessor_persistedState_always() throws Exception {
testProcessor("org.eclipse.e4.ui.tests/data/ModelAssembler/processors_always.xml", false, true);
assertEquals(1, application.getDescriptors().size());
assertEquals("simpleprocessor.post", application.getDescriptors().get(0).getElementId());
verifyZeroInteractions(logger);
}
/**
* Tests the execution of post-processors that should always be applied,
* running from a non-persisted state.
*
* @throws Exception
*/
@Test
public void testPostProcessor_nonPersistedState_always() throws Exception {
testProcessor("org.eclipse.e4.ui.tests/data/ModelAssembler/processors_always.xml", true, true);
assertEquals(1, application.getDescriptors().size());
assertEquals("simpleprocessor.post", application.getDescriptors().get(0).getElementId());
verifyZeroInteractions(logger);
}
/**
* Tests the execution of post-processors running from a non-persisted state
* declared to be applied as "initial".
*
* @throws Exception
*/
@Test
public void testPostProcessor_NonPersistedState_initial() throws Exception {
testProcessor("org.eclipse.e4.ui.tests/data/ModelAssembler/processors_initial.xml", true, true);
assertEquals(1, application.getDescriptors().size());
assertEquals("simpleprocessor.post", application.getDescriptors().get(0).getElementId());
verifyZeroInteractions(logger);
}
/**
* Processors running from a persisted state declared to be applied as
* "initial" should not be run.
*
* @throws Exception
*/
@Test
public void testPostProcessor_persistedState_initial() throws Exception {
testProcessor("org.eclipse.e4.ui.tests/data/ModelAssembler/processors_initial.xml", false, true);
assertEquals(0, application.getDescriptors().size());
verifyZeroInteractions(logger);
}
/**
* Test handling of processor contribution without any processor class. A
* warning should be logged in such cases.
*
* @throws Exception
*/
@Test
public void testProcessor_noProcessor() throws Exception {
testProcessor("org.eclipse.e4.ui.tests/data/ModelAssembler/processor_null.xml", true, false);
verify(logger).warn("Unable to create processor null from org.eclipse.e4.ui.tests");
assertEquals(0, application.getDescriptors().size());
verifyZeroInteractions(logger);
}
/**
* Tests a contribution containing an nonexistent processor class. A warning
* should be logged in such cases.
*
* @throws Exception
*/
@Test
public void testProcessor_processorNotFound() throws Exception {
testProcessor("org.eclipse.e4.ui.tests/data/ModelAssembler/processor_wrongProcessorClass.xml", true, false);
verify(logger).warn(
"Unable to create processor org.eclipse.e4.ui.tests.workbench.SimplePreProcessor_NotFound from org.eclipse.e4.ui.tests");
assertEquals(0, application.getDescriptors().size());
verifyZeroInteractions(logger);
}
/**
* Tests a processor contribution that adds to the context an element with
* an id that does not exist in the application model. A warning should be
* logged, but the processors should still be executed.
*
* @throws Exception
*/
@Test
public void testProcessor_wrongAppId() throws Exception {
application.setElementId("newID");
testProcessor("org.eclipse.e4.ui.tests/data/ModelAssembler/processors_initial.xml", true, true);
verify(logger).warn("Could not find element with id 'org.eclipse.e4.ui.tests.modelassembler.app'");
verifyZeroInteractions(logger);
assertEquals(1, application.getDescriptors().size());
assertEquals("simpleprocessor.post", application.getDescriptors().get(0).getElementId());
}
private void testProcessor(String filePath, boolean initial, boolean afterFragments) throws Exception {
IContributor contributor = ContributorFactorySimple.createContributor(BUNDLE_SYMBOLIC_NAME);
IExtensionRegistry registry = createTestExtensionRegistry();
assertEquals(0, registry.getConfigurationElementsFor(EXTENSION_POINT_ID).length);
registry.addContribution(getContentsAsInputStream(filePath), contributor, false, null, null, null);
IExtensionPoint extPoint = registry.getExtensionPoint(EXTENSION_POINT_ID);
IExtension[] extensions = new ExtensionsSort().sort(extPoint.getExtensions());
assertEquals(0, application.getDescriptors().size());
assembler.runProcessors(extensions, initial, afterFragments);
}
private IExtensionRegistry createTestExtensionRegistry() {
IExtensionRegistry defaultRegistry = RegistryFactory.getRegistry();
IExtensionPoint extensionPoint = defaultRegistry.getExtensionPoint(EXTENSION_POINT_ID);
ExtensionRegistry registry = (ExtensionRegistry) RegistryFactory.createRegistry(null, null, null);
registry.addExtensionPoint(extensionPoint.getUniqueIdentifier(), extensionPoint.getContributor(), false,
extensionPoint.getLabel(), extensionPoint.getSchemaReference(), null);
appContext.set(IExtensionRegistry.class, registry);
return registry;
}
private InputStream getContentsAsInputStream(String filePath) throws IOException {
URI uri = URI.createPlatformPluginURI(filePath, true);
return URIConverter.INSTANCE.createInputStream(uri);
}
}