/** * 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 */ package org.eclipse.emf.compare.tests.merge; import static org.eclipse.emf.compare.ConflictKind.PSEUDO; import static org.eclipse.emf.compare.ConflictKind.REAL; import static org.eclipse.emf.compare.DifferenceSource.LEFT; import static org.eclipse.emf.compare.DifferenceSource.RIGHT; import static org.eclipse.emf.compare.utils.EMFComparePredicates.fromSide; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import com.google.common.collect.Collections2; import java.io.IOException; import java.util.Collection; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.compare.Comparison; import org.eclipse.emf.compare.Conflict; import org.eclipse.emf.compare.Diff; import org.eclipse.emf.compare.DifferenceSource; import org.eclipse.emf.compare.EMFCompare; import org.eclipse.emf.compare.merge.IMerger; import org.eclipse.emf.compare.scope.DefaultComparisonScope; import org.eclipse.emf.compare.scope.IComparisonScope; import org.eclipse.emf.compare.tests.checkers.MergeDependenciesChecker; import org.eclipse.emf.compare.tests.merge.data.IndividualDiffInputData; import org.eclipse.emf.compare.utils.EMFComparePredicates; import org.eclipse.emf.ecore.resource.Resource; import org.junit.Test; /** * This test class verifies that MergeDependenciesUtil computes the correct requirements. It focuses on * conflicts and pseudo-conflicts with dependencies. * * @see bug <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=484579">484579</a> for more details. */ @SuppressWarnings("nls") public class ConflictImplicationsTest_Bug484579 { private IndividualDiffInputData input = new IndividualDiffInputData(); private final IMerger.Registry mergerRegistry = IMerger.RegistryImpl.createStandaloneInstance(); /** * Assert that the rejection of the deletion of PackageY (right to left) will not lead to unwanted * dependencies dues to the presence of a pseudo conflicts (with dependencies) on a child element of * packageY. * * <pre> * The ancestor model: * - PackageA * - ClassB * - PackageX * - PackageY * - ClassZ -> ClassB * * The left model: * - PackageX * * The right model: * - PackageA2 * - ClassB2 * - PackageX * - PackageY * </pre> * * The comparison on those models leads to a pseudo-conflict (deletion of ClassZ -> ClassB) and two * conflicts (renaming of PackageA and ClassB). * * @throws IOException */ @Test public void testImplicationBetweenConflicts() throws IOException { Diff deletePackageY = setupImplicationBetweenConflicts(); MergeDependenciesChecker checker = getChecker(deletePackageY); checker.rightToLeft().implies(1).rejects(0).check(); checker.leftToRight().implies(1).rejects(0).check(); } /** * Test the dependencies between two conflicting diffs if on one side an element is deleted (which implies * other diff merging) and on the other the element is moved (which may implies other dependencies). * * @throws IOException */ @Test public void testImplicationsBetweenMoveAndDeleteConflicts() throws IOException { Collection<Conflict> realConflicts = setupImplicationBetweenMoveAndDelete(); // Conflict should contain 2 diffs, one from left, the other from right Conflict next = realConflicts.iterator().next(); Collection<Diff> conflict1FromLeft = Collections2.filter(next.getDifferences(), fromSide(LEFT)); Collection<Diff> conflict1FromRight = Collections2.filter(next.getDifferences(), fromSide(RIGHT)); assertEquals(1, conflict1FromLeft.size()); assertEquals(1, conflict1FromRight.size()); // Get each diff of the conflicts Diff deletePackageD = conflict1FromLeft.iterator().next(); Diff movePackageD = conflict1FromRight.iterator().next(); MergeDependenciesChecker checker = getChecker(deletePackageD); checker.rightToLeft().implies(1).rejects(0).check(); checker.leftToRight().implies(2).rejects(1).check(); checker = getChecker(movePackageD); checker.rightToLeft().implies(1).rejects(1).check(); checker.leftToRight().implies(1).rejects(0).check(); } /** * Get a MergeDependenciesChecker for the diff. * * @param diff * The diff to check * @return an instance of MergeDependenciesChecker for the given diff */ private MergeDependenciesChecker getChecker(Diff diff) { return MergeDependenciesChecker.getDependenciesChecker(mergerRegistry, diff); } /** * Setup the test checking the implication between conflicts. This setup will assert that the dependencies * of all the diffs are correct, then return the exact diff to test in order to validate the point. * * @return the diff we want to check * @throws IOException */ private Diff setupImplicationBetweenConflicts() throws IOException { final Resource origin = input.getConflictAndPseudoConflictImplicationsAncestor(); final Resource left = input.getConflictAndPseudoConflictImplicationsLeft(); final Resource right = input.getConflictAndPseudoConflictImplicationsRight(); MergeDependenciesChecker checker = null; final IComparisonScope scope = new DefaultComparisonScope(left, right, origin); Comparison comparison = EMFCompare.builder().build().compare(scope); EList<Conflict> conflicts = comparison.getConflicts(); EList<Diff> differences = comparison.getDifferences(); assertEquals(9, comparison.getDifferences().size()); assertEquals(4, conflicts.size()); Collection<Conflict> pseudoConflicts = Collections2.filter(conflicts, EMFComparePredicates.containsConflictOfTypes(PSEUDO)); Collection<Conflict> realConflicts = Collections2.filter(conflicts, EMFComparePredicates.containsConflictOfTypes(REAL)); assertEquals(2, pseudoConflicts.size()); assertEquals(2, realConflicts.size()); Conflict pseudo1 = null; Conflict pseudo2 = null; for (Conflict conflict : pseudoConflicts) { if (conflict.getLeftDifferences().get(0).getMatch().getOrigin().toString().contains("ClassZ")) { pseudo1 = conflict; } else if (conflict.getLeftDifferences().get(0).getMatch().getOrigin().toString() .contains("PackageY")) { pseudo2 = conflict; } } assertNotNull(pseudo1); assertNotNull(pseudo2); Conflict real1 = null; Conflict real2 = null; for (Conflict conflict : realConflicts) { if (conflict.getLeftDifferences().get(0).getMatch().getDifferences().size() == 1) { real2 = conflict; } else if (conflict.getLeftDifferences().get(0).getMatch().getDifferences().size() == 2) { real1 = conflict; } } assertNotNull(real1); assertNotNull(real2); // Conflict should contain 2 diffs, one from left, the other from right Collection<Diff> conflict1FromLeft = Collections2.filter(real1.getDifferences(), fromSide(LEFT)); Collection<Diff> conflict1FromRight = Collections2.filter(real1.getDifferences(), fromSide(RIGHT)); assertEquals(1, conflict1FromLeft.size()); assertEquals(1, conflict1FromRight.size()); // Conflict should contain 2 diffs, one from left, the other from right Collection<Diff> conflict2FromLeft = Collections2.filter(real2.getDifferences(), fromSide(LEFT)); Collection<Diff> conflict2FromRight = Collections2.filter(real2.getDifferences(), fromSide(RIGHT)); assertEquals(1, conflict2FromLeft.size()); assertEquals(1, conflict2FromRight.size()); // Get each diff of the conflicts Diff deleteClassB = conflict1FromLeft.iterator().next(); differences.remove(deleteClassB); Diff renameClassB = conflict1FromRight.iterator().next(); differences.remove(renameClassB); Diff deletePackageA = conflict2FromLeft.iterator().next(); differences.remove(deletePackageA); Diff renamePackageA = conflict2FromRight.iterator().next(); differences.remove(renamePackageA); // Deleting classB from supertypes of classZ. // Rejecting the left side implies 6 others, accepting only implies the pseudo conflicting one // and inversely on the right side diff. for (Diff diff : pseudo1.getDifferences()) { differences.remove(diff); checker = getChecker(diff); if (diff.getSource() == DifferenceSource.LEFT) { checker.rightToLeft().implies(4).rejects(0).check(); checker.leftToRight().implies(2).rejects(0).check(); } else { checker.rightToLeft().implies(2).rejects(0).check(); checker.leftToRight().implies(2).rejects(0).check(); } } // Deleting classZ // Rejecting the left side only implies the pseudo-conflicting and rejecting its container's deletion, // while accepting implies both diffs from the other pseudo-conflict. The diff from the right side is // the reverse. // Note that we're not considering cascading diffs. for (Diff diff : pseudo2.getDifferences()) { differences.remove(diff); checker = getChecker(diff); if (diff.getSource() == DifferenceSource.LEFT) { checker.rightToLeft().implies(3).rejects(0).check(); checker.leftToRight().implies(2).rejects(0).check(); } else { checker.rightToLeft().implies(2).rejects(0).check(); checker.leftToRight().implies(2).rejects(0).check(); } } checker = getChecker(deleteClassB); checker.rightToLeft().implies(2).rejects(0).check(); checker.leftToRight().implies(1).rejects(1).check(); checker = getChecker(renameClassB); checker.rightToLeft().implies(1).rejects(2).check(); checker.leftToRight().implies(1).rejects(0).check(); checker = getChecker(deletePackageA); checker.rightToLeft().implies(1).rejects(0).check(); checker.leftToRight().implies(2).rejects(2).check(); checker = getChecker(renamePackageA); checker.rightToLeft().implies(1).rejects(1).check(); checker.leftToRight().implies(1).rejects(0).check(); assertEquals(1, differences.size()); return differences.get(0); } /** * Setup the test checking implication between move and delete. * * @return the list of pseudoConflicts to test * @throws IOException */ private Collection<Conflict> setupImplicationBetweenMoveAndDelete() throws IOException { final Resource origin = input.getMoveConflictAndPseudoConflictImplicationsAncestor(); final Resource left = input.getMoveConflictAndPseudoConflictImplicationsLeft(); final Resource right = input.getMoveConflictAndPseudoConflictImplicationsRight(); final IComparisonScope scope = new DefaultComparisonScope(left, right, origin); Comparison comparison = EMFCompare.builder().build().compare(scope); EList<Conflict> conflicts = comparison.getConflicts(); assertEquals(17, comparison.getDifferences().size()); assertEquals(7, conflicts.size()); Collection<Conflict> pseudoConflicts = Collections2.filter(conflicts, EMFComparePredicates.containsConflictOfTypes(PSEUDO)); Collection<Conflict> realConflicts = Collections2.filter(conflicts, EMFComparePredicates.containsConflictOfTypes(REAL)); assertEquals(6, pseudoConflicts.size()); assertEquals(1, realConflicts.size()); return realConflicts; } }