| /** |
| * Copyright (c) 2012, 2016 Obeo 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: |
| * Obeo - initial API and implementation |
| * Stefan Dirix - update priority value for UML merger |
| * Philip Langer - bug 501864 |
| * Martin Fleck - bug 507177 |
| */ |
| package org.eclipse.emf.compare.uml2.tests; |
| |
| import static com.google.common.base.Predicates.instanceOf; |
| import static com.google.common.base.Predicates.not; |
| import static com.google.common.collect.Iterators.all; |
| import static org.eclipse.emf.compare.merge.AbstractMerger.SUB_DIFF_AWARE_OPTION; |
| import static org.eclipse.emf.compare.utils.EMFComparePredicates.hasDirectOrIndirectConflict; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertTrue; |
| |
| import com.google.common.base.Predicate; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Iterators; |
| import com.google.common.collect.Maps; |
| |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.emf.common.EMFPlugin; |
| import org.eclipse.emf.common.notify.Notifier; |
| import org.eclipse.emf.common.util.BasicMonitor; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.compare.AttributeChange; |
| import org.eclipse.emf.compare.ComparePackage; |
| import org.eclipse.emf.compare.Comparison; |
| import org.eclipse.emf.compare.ConflictKind; |
| import org.eclipse.emf.compare.Diff; |
| import org.eclipse.emf.compare.EMFCompare; |
| import org.eclipse.emf.compare.EMFCompare.Builder; |
| import org.eclipse.emf.compare.ReferenceChange; |
| import org.eclipse.emf.compare.merge.BatchMerger; |
| import org.eclipse.emf.compare.merge.IBatchMerger; |
| import org.eclipse.emf.compare.merge.IMergeOptionAware; |
| import org.eclipse.emf.compare.merge.IMerger; |
| import org.eclipse.emf.compare.postprocessor.IPostProcessor; |
| import org.eclipse.emf.compare.postprocessor.PostProcessorDescriptorRegistryImpl; |
| import org.eclipse.emf.compare.scope.DefaultComparisonScope; |
| import org.eclipse.emf.compare.scope.IComparisonScope; |
| import org.eclipse.emf.compare.tests.postprocess.data.TestPostProcessor; |
| import org.eclipse.emf.compare.uml2.internal.StereotypedElementChange; |
| import org.eclipse.emf.compare.uml2.internal.UMLDiff; |
| import org.eclipse.emf.compare.uml2.internal.merge.OpaqueElementBodyChangeMerger; |
| import org.eclipse.emf.compare.uml2.internal.merge.UMLMerger; |
| import org.eclipse.emf.compare.uml2.internal.merge.UMLReferenceChangeMerger; |
| import org.eclipse.emf.compare.uml2.internal.postprocessor.OpaqueElementBodyChangePostProcessor; |
| import org.eclipse.emf.compare.uml2.internal.postprocessor.UMLPostProcessor; |
| import org.eclipse.emf.compare.uml2.profile.test.uml2comparetestprofile.UML2CompareTestProfilePackage; |
| import org.eclipse.emf.compare.utils.ReferenceUtil; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EPackage; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.EcorePackage; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.resource.ResourceSet; |
| import org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl; |
| import org.eclipse.uml2.uml.UMLPackage; |
| import org.eclipse.uml2.uml.internal.resource.UMLResourceFactoryImpl; |
| import org.junit.After; |
| import org.junit.Before; |
| |
| /** |
| * @author <a href="mailto:cedric.notot@obeo.fr">Cedric Notot</a> |
| */ |
| @SuppressWarnings({"nls", "restriction" }) |
| public abstract class AbstractUMLTest { |
| |
| private IMerger.Registry mergerRegistry; |
| |
| /** Cached cascading options before the last time the filter was enabled or disabled. */ |
| private static final Map<IMergeOptionAware, Object> CACHED_OPTIONS = Maps.newHashMap(); |
| |
| /** |
| * Each sublass of AbstractUMLTest have to call this method in a @BeforeClass annotated method. This allow |
| * each test to customize its context. |
| */ |
| public static void fillRegistries() { |
| if (!EMFPlugin.IS_ECLIPSE_RUNNING) { |
| EPackage.Registry.INSTANCE.put(EcorePackage.eNS_URI, EcorePackage.eINSTANCE); |
| EPackage.Registry.INSTANCE.put(ComparePackage.eNS_URI, ComparePackage.eINSTANCE); |
| EPackage.Registry.INSTANCE.put(UMLPackage.eNS_URI, UMLPackage.eINSTANCE); |
| |
| Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("ecore", |
| new EcoreResourceFactoryImpl()); |
| Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("uml", //$NON-NLS-1$ |
| new UMLResourceFactoryImpl()); |
| } |
| } |
| |
| /** |
| * Each sublass of AbstractUMLTest have to call this method in a @BeforeClass annotated method. This allow |
| * each test to safely delete its context. |
| */ |
| public static void resetRegistries() { |
| if (!EMFPlugin.IS_ECLIPSE_RUNNING) { |
| Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().remove("uml"); |
| Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().remove("ecore"); |
| |
| EPackage.Registry.INSTANCE.remove(UML2CompareTestProfilePackage.eNS_URI); |
| EPackage.Registry.INSTANCE.remove(ComparePackage.eNS_URI); |
| EPackage.Registry.INSTANCE.remove(EcorePackage.eNS_URI); |
| } |
| } |
| |
| @Before |
| public void before() { |
| // post-processor and merger registry is not filled in runtime (org.eclipse.emf.compare.rcp not |
| // loaded) |
| mergerRegistry = IMerger.RegistryImpl.createStandaloneInstance(); |
| final IMerger umlMerger = new UMLMerger(); |
| final IMerger umlReferenceChangeMerger = new UMLReferenceChangeMerger(); |
| final IMerger opaqueElementBodyChangeMerger = new OpaqueElementBodyChangeMerger(); |
| umlMerger.setRanking(20); |
| umlReferenceChangeMerger.setRanking(25); |
| opaqueElementBodyChangeMerger.setRanking(25); |
| mergerRegistry.add(umlMerger); |
| mergerRegistry.add(umlReferenceChangeMerger); |
| mergerRegistry.add(opaqueElementBodyChangeMerger); |
| } |
| |
| /** |
| * Used to register new post processors. |
| * |
| * @param postProcessorRegistry |
| */ |
| protected void registerPostProcessors( |
| final IPostProcessor.Descriptor.Registry<String> postProcessorRegistry) { |
| postProcessorRegistry.put(UMLPostProcessor.class.getName(), |
| new TestPostProcessor.TestPostProcessorDescriptor( |
| Pattern.compile("http://www.eclipse.org/uml2/\\d\\.0\\.0/UML"), null, |
| new UMLPostProcessor(), 20)); |
| postProcessorRegistry.put(OpaqueElementBodyChangePostProcessor.class.getName(), |
| new TestPostProcessor.TestPostProcessorDescriptor( |
| Pattern.compile("http://www.eclipse.org/uml2/\\d\\.0\\.0/UML"), null, |
| new OpaqueElementBodyChangePostProcessor(), 25)); |
| } |
| |
| @After |
| public void cleanup() { |
| for (ResourceSet set : getInput().getSets()) { |
| for (Resource res : set.getResources()) { |
| res.unload(); |
| } |
| set.getResources().clear(); |
| } |
| getInput().getSets().clear(); |
| |
| } |
| |
| protected EMFCompare getCompare() { |
| Builder builder = EMFCompare.builder(); |
| final IPostProcessor.Descriptor.Registry<String> postProcessorRegistry = new PostProcessorDescriptorRegistryImpl<String>(); |
| registerPostProcessors(postProcessorRegistry); |
| builder.setPostProcessorRegistry(postProcessorRegistry); |
| return builder.build(); |
| } |
| |
| protected Comparison compare(Notifier left, Notifier right) { |
| return compare(left, right, null); |
| } |
| |
| protected Comparison compare(Notifier left, Notifier right, Notifier origin) { |
| IComparisonScope scope = new DefaultComparisonScope(left, right, origin); |
| return getCompare().compare(scope); |
| } |
| |
| protected IMerger.Registry getMergerRegistry() { |
| return mergerRegistry; |
| } |
| |
| protected enum TestKind { |
| ADD, DELETE; |
| } |
| |
| protected static int count(List<Diff> differences, Predicate<Object> p) { |
| int count = 0; |
| final Iterator<Diff> result = Iterators.filter(differences.iterator(), p); |
| while (result.hasNext()) { |
| count++; |
| result.next(); |
| } |
| return count; |
| } |
| |
| public static Predicate<? super Diff> onRealFeature(final EStructuralFeature feature) { |
| return new Predicate<Diff>() { |
| public boolean apply(Diff input) { |
| final EStructuralFeature affectedFeature; |
| if (input instanceof AttributeChange) { |
| affectedFeature = ((AttributeChange)input).getAttribute(); |
| } else if (input instanceof ReferenceChange) { |
| affectedFeature = ((ReferenceChange)input).getReference(); |
| } else { |
| return false; |
| } |
| return feature == affectedFeature; |
| } |
| }; |
| } |
| |
| public static Predicate<? super Diff> isChangeAdd() { |
| return new Predicate<Diff>() { |
| public boolean apply(Diff input) { |
| if (input instanceof ReferenceChange) { |
| return ReferenceUtil |
| .getAsList(input.getMatch().getLeft(), ((ReferenceChange)input).getReference()) |
| .contains(((ReferenceChange)input).getValue()); |
| } else if (input instanceof AttributeChange) { |
| return ReferenceUtil |
| .getAsList(input.getMatch().getLeft(), ((AttributeChange)input).getAttribute()) |
| .contains(((AttributeChange)input).getValue()); |
| } |
| return false; |
| } |
| }; |
| } |
| |
| protected static Predicate<Diff> discriminantInstanceOf(final EClass clazz) { |
| return new Predicate<Diff>() { |
| public boolean apply(Diff input) { |
| return input instanceof UMLDiff && clazz.isInstance(((UMLDiff)input).getDiscriminant()); |
| } |
| |
| }; |
| } |
| |
| protected abstract AbstractUMLInputData getInput(); |
| |
| protected void testMergeLeftToRight(Notifier left, Notifier right, Notifier origin) { |
| testMergeLeftToRight(left, right, origin, false); |
| } |
| |
| protected void testMergeRightToLeft(Notifier left, Notifier right, Notifier origin) { |
| testMergeRightToLeft(left, right, origin, false); |
| } |
| |
| protected void testMergeLeftToRight(Notifier left, Notifier right, Notifier origin, |
| boolean pseudoAllowed) { |
| final IComparisonScope scope = new DefaultComparisonScope(left, right, origin); |
| final Comparison comparisonBefore = getCompare().compare(scope); |
| EList<Diff> differencesBefore = comparisonBefore.getDifferences(); |
| final IBatchMerger merger = new BatchMerger(mergerRegistry); |
| merger.copyAllLeftToRight(differencesBefore, new BasicMonitor()); |
| final Comparison comparisonAfter = getCompare().compare(scope); |
| EList<Diff> differencesAfter = comparisonAfter.getDifferences(); |
| final boolean diffs; |
| if (pseudoAllowed) { |
| diffs = all(differencesAfter.iterator(), hasDirectOrIndirectConflict(ConflictKind.PSEUDO)); |
| } else { |
| diffs = differencesAfter.isEmpty(); |
| } |
| assertTrue("Comparison#getDifferences() must be empty after copyAllLeftToRight", diffs); |
| } |
| |
| protected void testMergeRightToLeft(Notifier left, Notifier right, Notifier origin, |
| boolean pseudoAllowed) { |
| final IComparisonScope scope = new DefaultComparisonScope(left, right, origin); |
| final Comparison comparisonBefore = getCompare().compare(scope); |
| EList<Diff> differencesBefore = comparisonBefore.getDifferences(); |
| final IBatchMerger merger = new BatchMerger(mergerRegistry); |
| merger.copyAllRightToLeft(differencesBefore, new BasicMonitor()); |
| final Comparison comparisonAfter = getCompare().compare(scope); |
| EList<Diff> differencesAfter = comparisonAfter.getDifferences(); |
| final boolean diffs; |
| if (pseudoAllowed) { |
| diffs = all(differencesAfter.iterator(), hasDirectOrIndirectConflict(ConflictKind.PSEUDO)); |
| } else { |
| diffs = differencesAfter.isEmpty(); |
| } |
| assertTrue("Comparison#getDifferences() must be empty after copyAllRightToLeft", diffs); |
| } |
| |
| protected void testIntersections(Comparison comparison) { |
| for (Diff diff : comparison.getDifferences()) { |
| int realRefinesSize = Iterables.size( |
| Iterables.filter(diff.getRefines(), not(instanceOf(StereotypedElementChange.class)))); |
| assertFalse("Wrong number of refines (without StereotypedElementChange) on" + diff, |
| realRefinesSize > 1); |
| int stereotypedElementChangeRefines = Iterables |
| .size(Iterables.filter(diff.getRefines(), instanceOf(StereotypedElementChange.class))); |
| assertFalse("Wrong number of refines (of type StereotypedElementChange) on " + diff, |
| stereotypedElementChangeRefines > 1); |
| |
| } |
| } |
| |
| /** |
| * Enables the cascading filter by setting the filter option in all mergers to true. Any changes done by |
| * this method can be restored by calling {@link #restoreCascadingFilter()}. |
| */ |
| protected void enableCascadingFilter() { |
| setCascadingFilter(true); |
| } |
| |
| /** |
| * Disables the cascading filter by setting the filter option in all mergers to false. Any changes done by |
| * this method can be restored by calling {@link #restoreCascadingFilter()}. |
| */ |
| protected void disableCascadingFilter() { |
| setCascadingFilter(false); |
| } |
| |
| /** |
| * Sets the cascading filter option (subdiff-awareness) of all mergers to the given state. Any changes |
| * done by this method can be restored by calling {@link #restoreCascadingFilter()}. |
| * |
| * @param enabled |
| * filter state |
| */ |
| private void setCascadingFilter(boolean enabled) { |
| for (IMergeOptionAware merger : Iterables.filter(mergerRegistry.getMergers(null), |
| IMergeOptionAware.class)) { |
| Map<Object, Object> mergeOptions = merger.getMergeOptions(); |
| Object previousValue = mergeOptions.get(SUB_DIFF_AWARE_OPTION); |
| CACHED_OPTIONS.put(merger, previousValue); |
| mergeOptions.put(SUB_DIFF_AWARE_OPTION, Boolean.valueOf(enabled)); |
| } |
| } |
| |
| /** |
| * Restores the cascading filter options changed by the last call to {@link #enableCascadingFilter()}, |
| * {@link #disableCascadingFilter()}, or {@link #setCascadingFilter(boolean)}. |
| */ |
| protected void restoreCascadingFilter() { |
| // restore previous values |
| for (Entry<IMergeOptionAware, Object> entry : CACHED_OPTIONS.entrySet()) { |
| IMergeOptionAware merger = entry.getKey(); |
| merger.getMergeOptions().put(SUB_DIFF_AWARE_OPTION, entry.getValue()); |
| } |
| } |
| } |