/******************************************************************************* * Copyright (c) 2015 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.emf.compare.ide.ui.tests.structuremergeviewer.actions; import static com.google.common.collect.Iterables.filter; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import com.google.common.collect.Iterables; import java.io.IOException; import java.util.Collection; import java.util.Map; import org.eclipse.emf.compare.Comparison; import org.eclipse.emf.compare.Diff; import org.eclipse.emf.compare.DifferenceState; import org.eclipse.emf.compare.EMFCompare; import org.eclipse.emf.compare.domain.impl.EMFCompareEditingDomain; import org.eclipse.emf.compare.internal.merge.MergeMode; import org.eclipse.emf.compare.internal.merge.MergeOperation; import org.eclipse.emf.compare.merge.AbstractMerger; import org.eclipse.emf.compare.merge.IMergeOptionAware; import org.eclipse.emf.compare.merge.IMerger; import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin; import org.eclipse.emf.compare.rcp.ui.internal.configuration.IEMFCompareConfiguration; import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.impl.DefaultGroupProvider; import org.eclipse.emf.compare.rcp.ui.internal.structuremergeviewer.groups.provider.TreeNodeItemProviderSpec; import org.eclipse.emf.compare.scope.DefaultComparisonScope; import org.eclipse.emf.compare.tests.framework.AbstractInputData; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.edit.tree.TreeFactory; import org.eclipse.emf.edit.tree.TreeNode; import org.eclipse.jface.viewers.StructuredSelection; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; /** * This test is related to the bug <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=475586">475586</a> * The test case contains a delete diff on left side (Node A) and and a rename of Node A to Node C on the * right side. The Node A references (non-containment) a Node B. When the user accept the merge of the Node C * (corresponding to merge from right to left), the diff on Node A must be rejected. If the cascading filter * is enabled, the diff on Node B must also be rejected because it is a sub-diff a Node A and it is hidden. If * the cascading filter is disabled, the diff on Node B must not be rejected because it is a sub-diff a Node A * but it is visible. When the user reject the merge of the Node C (corresponding to mark the diff as merged), * the diff on Node A and Node C must be unresolved, whether the cascading filter is enabled or disabled. * * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> */ @SuppressWarnings({"restriction" }) public class TestBug475586 extends AbstractTestUITreeNodeItemProviderAdapter { private static TreeNodeItemProviderSpec itemProvider; private IMerger.Registry mergerRegistry; private TreeNode containmentRefDeleteA; private TreeNode nameSetC; private TreeNode singleValuedReferenceUnsetB; @Override @Before public void before() throws IOException { super.before(); itemProvider = (TreeNodeItemProviderSpec)treeItemProviderAdapterFactory.createTreeNodeAdapter(); mergerRegistry = EMFCompareRCPPlugin.getDefault().getMergerRegistry(); Bug475586InputData inputData = new Bug475586InputData(); final Resource left = inputData.getResource("left.nodes"); //$NON-NLS-1$ final Resource right = inputData.getResource("right.nodes"); //$NON-NLS-1$ final Resource ancestor = inputData.getResource("ancestor.nodes"); //$NON-NLS-1$ final DefaultComparisonScope scope = new DefaultComparisonScope(left, right, ancestor); final Comparison comparison = EMFCompare.builder().build().compare(scope); editingDomain = EMFCompareEditingDomain.create(left, right, ancestor); TreeNode nodeRoot = getMatchNodeRoot(comparison); // Get Node Single Value Reference A [containmentRef1 delete] difference TreeNode nodeA = nodeRoot.getChildren().get(0); containmentRefDeleteA = nodeA.getChildren().get(0); singleValuedReferenceUnsetB = nodeA.getChildren().get(1); nameSetC = nodeA.getChildren().get(2); } @Test @Ignore("The cascading filter must not be handled by the mergers.\n" + "It's the responsibility of the UI to compute a list of diffs 'hidden' by the cascading filter" + "for a selected diff and to call the BatchMerger to merge all these diffs at the same time.") public void testAcceptWithCascadingFilter() { final MergeMode accept = MergeMode.ACCEPT; final boolean leftEditable = true; final boolean rightEditable = false; final boolean cascadingFilter = true; IEMFCompareConfiguration emfCC = createConfiguration(leftEditable, rightEditable); MockMergeAction action = new MockMergeAction(emfCC, mergerRegistry, accept, null); // Accept C [name set] difference Diff nodeC = (Diff)nameSetC.getData(); assertFalse(accept.isLeftToRight(nodeC, leftEditable, rightEditable)); assertEquals(MergeOperation.MERGE, accept.getMergeAction(nodeC, leftEditable, rightEditable)); action.updateSelection(new StructuredSelection(nameSetC)); setCascadingDifferencesFilterEnabled(cascadingFilter); action.run(); assertEquals(DifferenceState.MERGED, nodeC.getState()); Diff nodeA = (Diff)containmentRefDeleteA.getData(); assertEquals(DifferenceState.MERGED, nodeA.getState()); Diff nodeB = (Diff)singleValuedReferenceUnsetB.getData(); assertEquals(DifferenceState.MERGED, nodeB.getState()); } @Test public void testRejectWithCascadingFilter() { final MergeMode reject = MergeMode.REJECT; final boolean leftEditable = true; final boolean rightEditable = false; final boolean cascadingFilter = true; IEMFCompareConfiguration emfCC = createConfiguration(leftEditable, rightEditable); MockMergeAction action = new MockMergeAction(emfCC, mergerRegistry, reject, null); // Reject C [name set] difference Diff nodeC = (Diff)nameSetC.getData(); assertTrue(reject.isLeftToRight(nodeC, leftEditable, rightEditable)); assertEquals(MergeOperation.MARK_AS_MERGE, reject.getMergeAction(nodeC, leftEditable, rightEditable)); action.updateSelection(new StructuredSelection(nameSetC)); setCascadingDifferencesFilterEnabled(cascadingFilter); action.run(); assertEquals(DifferenceState.DISCARDED, nodeC.getState()); Diff nodeA = (Diff)containmentRefDeleteA.getData(); assertEquals(DifferenceState.UNRESOLVED, nodeA.getState()); Diff nodeB = (Diff)singleValuedReferenceUnsetB.getData(); assertEquals(DifferenceState.UNRESOLVED, nodeB.getState()); } @Test public void testAcceptWithoutCascadingFilter() { final MergeMode accept = MergeMode.ACCEPT; final boolean leftEditable = true; final boolean rightEditable = false; final boolean cascadingFilter = false; IEMFCompareConfiguration emfCC = createConfiguration(leftEditable, rightEditable); MockMergeAction action = new MockMergeAction(emfCC, mergerRegistry, accept, null); // Accept C [name set] difference Diff nodeC = (Diff)nameSetC.getData(); assertFalse(accept.isLeftToRight(nodeC, leftEditable, rightEditable)); assertEquals(MergeOperation.MERGE, accept.getMergeAction(nodeC, leftEditable, rightEditable)); action.updateSelection(new StructuredSelection(nameSetC)); setCascadingDifferencesFilterEnabled(cascadingFilter); action.run(); assertEquals(DifferenceState.MERGED, nodeC.getState()); Diff nodeA = (Diff)containmentRefDeleteA.getData(); assertEquals(DifferenceState.DISCARDED, nodeA.getState()); Diff nodeB = (Diff)singleValuedReferenceUnsetB.getData(); assertEquals(DifferenceState.UNRESOLVED, nodeB.getState()); } @Test public void testRejectWithoutCascadingFilter() { final MergeMode reject = MergeMode.REJECT; final boolean leftEditable = true; final boolean rightEditable = false; final boolean cascadingFilter = false; IEMFCompareConfiguration emfCC = createConfiguration(leftEditable, rightEditable); MockMergeAction action = new MockMergeAction(emfCC, mergerRegistry, reject, null); // Reject C [name set] difference Diff nodeC = (Diff)nameSetC.getData(); assertTrue(reject.isLeftToRight(nodeC, leftEditable, rightEditable)); assertEquals(MergeOperation.MARK_AS_MERGE, reject.getMergeAction(nodeC, leftEditable, rightEditable)); action.updateSelection(new StructuredSelection(nameSetC)); setCascadingDifferencesFilterEnabled(cascadingFilter); action.run(); assertEquals(DifferenceState.DISCARDED, nodeC.getState()); Diff nodeA = (Diff)containmentRefDeleteA.getData(); assertEquals(DifferenceState.UNRESOLVED, nodeA.getState()); Diff nodeB = (Diff)singleValuedReferenceUnsetB.getData(); assertEquals(DifferenceState.UNRESOLVED, nodeB.getState()); } private void setCascadingDifferencesFilterEnabled(boolean enabled) { for (IMergeOptionAware merger : Iterables.filter(this.mergerRegistry.getMergers(null), IMergeOptionAware.class)) { Map<Object, Object> mergeOptions = merger.getMergeOptions(); mergeOptions.put(AbstractMerger.SUB_DIFF_AWARE_OPTION, Boolean.valueOf(enabled)); } } private static TreeNode getMatchNodeRoot(Comparison comparison) throws IOException { TreeNode treeNode = TreeFactory.eINSTANCE.createTreeNode(); treeNode.setData(comparison); treeNode.eAdapters().add(new DefaultGroupProvider()); Collection<?> children = itemProvider.getChildren(treeNode); Iterable<?> matches = filter(children, matchTreeNode); return (TreeNode)matches.iterator().next(); } /** * Input data for this bug. * * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> */ public class Bug475586InputData extends AbstractInputData { private static final String PATH_PREFIX = "data/_475586/"; //$NON-NLS-1$ public Resource getResource(String resourceName) throws IOException { StringBuilder resourceURL = new StringBuilder(PATH_PREFIX); resourceURL.append(resourceName); return loadFromClassLoader(resourceURL.toString()); } } }