/******************************************************************************* * 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 - bugs 441172, 452147, 460902 and 460923 *******************************************************************************/ package org.eclipse.emf.compare.tests.merge; import static com.google.common.base.Predicates.and; import static com.google.common.base.Predicates.instanceOf; import static org.eclipse.emf.compare.utils.EMFComparePredicates.added; import static org.eclipse.emf.compare.utils.EMFComparePredicates.addedToReference; import static org.eclipse.emf.compare.utils.EMFComparePredicates.changedReference; import static org.eclipse.emf.compare.utils.EMFComparePredicates.fromSide; import static org.eclipse.emf.compare.utils.EMFComparePredicates.moved; import static org.eclipse.emf.compare.utils.EMFComparePredicates.removed; import static org.eclipse.emf.compare.utils.EMFComparePredicates.removedFromReference; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.eclipse.emf.common.util.BasicMonitor; import org.eclipse.emf.compare.Comparison; import org.eclipse.emf.compare.Diff; import org.eclipse.emf.compare.DifferenceSource; import org.eclipse.emf.compare.DifferenceState; import org.eclipse.emf.compare.EMFCompare; import org.eclipse.emf.compare.FeatureMapChange; import org.eclipse.emf.compare.Match; import org.eclipse.emf.compare.ReferenceChange; import org.eclipse.emf.compare.internal.utils.ComparisonUtil; import org.eclipse.emf.compare.merge.BatchMerger; 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.conflict.data.ConflictInputData; import org.eclipse.emf.compare.tests.equi.data.EquiInputData; import org.eclipse.emf.compare.tests.fullcomparison.data.identifier.IdentifierMatchInputData; import org.eclipse.emf.compare.tests.merge.data.TwoWayMergeInputData; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.junit.Test; @SuppressWarnings("nls") public class MultipleMergeTest { // We'll use input from various other tests private ConflictInputData conflictInput = new ConflictInputData(); private EquiInputData equivalenceInput = new EquiInputData(); private TwoWayMergeInputData twoWayInput = new TwoWayMergeInputData(); private BatchMerger batchMerger = new BatchMerger(IMerger.RegistryImpl.createStandaloneInstance()); /** * @see ComplexMergeTest for a parametric test of all combinations of merge order. This test is here to * detail step by step how the merge is supposed to take place and facilitate debugging in case of a * problem. * @throws IOException */ @Test public void testComplexUseCaseLtoR1() throws IOException { final Resource left = conflictInput.getComplexLeft(); final Resource origin = conflictInput.getComplexOrigin(); final Resource right = conflictInput.getComplexRight(); final IComparisonScope scope = new DefaultComparisonScope(left, right, origin); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); /* * This use case features 12 distinct differences of all types, adding up to 4 real conflicts and 1 * pseudo conflict. */ // 1 - Left : Node8 added // 2 - Left : Node9 added // 3 - Left : Node1 moved // 4 - Left : Node0 added // 5 - Left : Node5 removed // 6 - Left : Node6 removed // 7 - Left : Node7 removed // 8 - Right : Node6 moved // 9 - Right : Node9 added // 10 - Right : Node0 added // 11 - Right : Node1 moved // 12 - Right : Node5 removed // Real conflict : 6 and 8 (Moving and deleting the same value) // Real conflict : 2 and 9 (Adding the same value at different indices) // Real conflict : 4 and 10 (Adding the same value at different indices) // Pseudo conflict : 3 and 11 (Moving the same value to the same index on both sides) // Pseudo conflict : 5 and 12 (Removing the same value on both sides) // For reference // "original" is : {Node1, Node2, Node3, Node4, Node5, Node6, Node7} // "left" is : {Node8, Node9, Node2, Node3, Node4, Node1, Node0} // "right" is : {Node6, Node2, Node9, Node3, Node0, Node1, Node4, Node7} // Merge all, left to right, in order. Resolve conflicts by taking left side. // First, reject all conflicts on the right // left: 8923410, right: 62930147 final ReferenceChange rightAdOfNode9 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.RIGHT), added("Root.Node9"))); batchMerger.copyAllLeftToRight(Arrays.asList(rightAdOfNode9), new BasicMonitor()); // left: 8923410, right: 6230147 final ReferenceChange rightMoveOfNode1 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.RIGHT), moved("Root.Node1", "containmentRef1"))); // revert move of Node 1 in right. It should be re-positioned right before 2 batchMerger.copyAllLeftToRight(Arrays.asList(rightMoveOfNode1), new BasicMonitor()); assertValueIndexIs(rightMoveOfNode1, false, 1); // left: 8923410, right: 6123047 final ReferenceChange rightAddOfNode0 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.RIGHT), added("Root.Node0"))); // revert addition of 0 in right batchMerger.copyAllLeftToRight(Arrays.asList(rightAddOfNode0), new BasicMonitor()); // left: 8923410, right: 612347 final ReferenceChange rightMoveOfNode6 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.RIGHT), moved("Root.Node6", "containmentRef1"))); // Revert move of 6 in right. batchMerger.copyAllLeftToRight(Arrays.asList(rightMoveOfNode6), new BasicMonitor()); assertValueIndexIs(rightMoveOfNode6, false, 4); // left: 8923410, right: 123467 final ReferenceChange rightDeleteOfNode5 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.RIGHT), removed("Root.Node5"))); // delete of Node 5 (pseudo-conflict) => no change batchMerger.copyAllLeftToRight(Arrays.asList(rightDeleteOfNode5), new BasicMonitor()); assertValueIndexIs(rightDeleteOfNode5, false, -1); // left: 8923410, right: 123467 // And now, accept all other changes // add Node8 final ReferenceChange leftAddOfNode8 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.LEFT), added("Root.Node8"))); // LCS is currently {2, 3, 4}. Insertion index is right before 2. batchMerger.copyAllLeftToRight(Arrays.asList(leftAddOfNode8), new BasicMonitor()); assertValueIndexIs(leftAddOfNode8, false, 1); // left: 8923410, right: 1823467 // add Node9 final ReferenceChange leftAddOfNode9 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.LEFT), added("Root.Node9"))); // LCS is now {8, 2, 3, 4}. Insertion should be right after 8 batchMerger.copyAllLeftToRight(Arrays.asList(leftAddOfNode9), new BasicMonitor()); assertValueIndexIs(leftAddOfNode9, false, 2); // left: 8923410, right: 18923467 // move Node1 final ReferenceChange leftMoveOfNode1 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.LEFT), moved("Root.Node1", "containmentRef1"))); // LCS is {8, 9, 2, 3, 4}. 1 should be moved right after 4. batchMerger.copyAllLeftToRight(Arrays.asList(leftMoveOfNode1), new BasicMonitor()); assertValueIndexIs(leftMoveOfNode1, false, 5); // left: 8923410, right: 89234167 // add Node0 final ReferenceChange leftAddOfNode0 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.LEFT), added("Root.Node0"))); // LCS is now {8, 9, 2, 3, 4, 1}. 0 should be added right after 1 batchMerger.copyAllLeftToRight(Arrays.asList(leftAddOfNode0), new BasicMonitor()); assertValueIndexIs(leftAddOfNode0, false, 6); // left: 8923410, right: 892341067 // remove Node5 (again since pseudo-conflict) should have no effect // These diff won't even be presented to the user, but let's merge it anyway. final ReferenceChange leftDeleteOfNode5 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.LEFT), removed("Root.Node5"))); batchMerger.copyAllLeftToRight(Arrays.asList(leftDeleteOfNode5), new BasicMonitor()); assertValueIndexIs(leftDeleteOfNode5, false, -1); // left: 8923410, right: 892341067 // remove Node6 final ReferenceChange leftDeleteOfNode6 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.LEFT), removed("Root.Node6"))); batchMerger.copyAllLeftToRight(Arrays.asList(leftDeleteOfNode6), new BasicMonitor()); assertValueIndexIs(leftDeleteOfNode6, false, -1); // left: 8923410, right: 89234107 // merge 7 (remove Node7) final ReferenceChange diff7 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.LEFT), removed("Root.Node7"))); batchMerger.copyAllLeftToRight(Arrays.asList(diff7), new BasicMonitor()); assertValueIndexIs(diff7, false, -1); // left: 8923410, right: 8923410 // Left and Right should now be equal final EObject leftContainer = diff7.getMatch().getLeft(); final EObject rightContainer = diff7.getMatch().getRight(); final List<EObject> leftContents = getAsList(leftContainer, diff7.getReference()); final List<EObject> rightContents = getAsList(rightContainer, diff7.getReference()); assertEqualContents(comparison, leftContents, rightContents); } /** * Same as previous but right to left. * * @throws IOException */ @Test public void testComplexUseCaseRtoL1() throws IOException { final Resource left = conflictInput.getComplexLeft(); final Resource origin = conflictInput.getComplexOrigin(); final Resource right = conflictInput.getComplexRight(); final IComparisonScope scope = new DefaultComparisonScope(left, right, origin); final Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); // See description of the changes in #testComplexUseCaseLtoR1 // Merge all, right to left, in order. Resolve conflicts by taking right side. // left: 8923410, right: 62930147 // Revert delete of 6 on the left side final ReferenceChange leftDeleteOfNode6 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.LEFT), removed("Root.Node6"))); batchMerger.copyAllRightToLeft(Arrays.asList(leftDeleteOfNode6), new BasicMonitor()); assertValueIndexIs(leftDeleteOfNode6, true, 5); // left: 89234610, right: 62930147 // Revert add of 9 on the left side final ReferenceChange leftAddOfNode9 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.LEFT), added("Root.Node9"))); batchMerger.copyAllRightToLeft(Arrays.asList(leftAddOfNode9), new BasicMonitor()); assertValueIndexIs(leftAddOfNode9, true, -1); // left: 8234610, right: 62930147 // Revert delete of node 5, pseudo conflict -> does nothing final ReferenceChange leftDeleteOfNode5 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.LEFT), removed("Root.Node5"))); batchMerger.copyAllRightToLeft(Arrays.asList(leftDeleteOfNode5), new BasicMonitor()); assertValueIndexIs(leftDeleteOfNode5, true, -1); // left: 8234610, right: 62930147 // Revert add of 0 on the left side final ReferenceChange leftAddOfNode0 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.LEFT), added("Root.Node0"))); batchMerger.copyAllRightToLeft(Arrays.asList(leftAddOfNode0), new BasicMonitor()); assertValueIndexIs(leftAddOfNode0, true, -1); // left: 823461, right: 62930147 // Revert move of 1 in left final ReferenceChange leftMoveOfNode1 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.LEFT), moved("Root.Node1", "containmentRef1"))); batchMerger.copyAllRightToLeft(Arrays.asList(leftMoveOfNode1), new BasicMonitor()); assertValueIndexIs(leftMoveOfNode1, true, 1); // left: 812346, right: 62930147 // And now, let's merge all the others right to left // move 6 final ReferenceChange rightMoveOfNode6 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.RIGHT), moved("Root.Node6", "containmentRef1"))); batchMerger.copyAllRightToLeft(Arrays.asList(rightMoveOfNode6), new BasicMonitor()); assertValueIndexIs(rightMoveOfNode6, true, 2); // left: 816234, right: 62930147 // add 9 final ReferenceChange rightAddOfNode9 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.RIGHT), added("Root.Node9"))); batchMerger.copyAllRightToLeft(Arrays.asList(rightAddOfNode9), new BasicMonitor()); assertValueIndexIs(rightAddOfNode9, true, 4); // left: 8162934, right: 62930147 // add 0 final ReferenceChange rightAddOfNode0 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.RIGHT), added("Root.Node0"))); batchMerger.copyAllRightToLeft(Arrays.asList(rightAddOfNode0), new BasicMonitor()); assertValueIndexIs(rightAddOfNode0, true, 6); // left: 81629304, right: 62930147 // move 1 final ReferenceChange rightMoveOfNode1 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.RIGHT), moved("Root.Node1", "containmentRef1"))); batchMerger.copyAllRightToLeft(Arrays.asList(rightMoveOfNode1), new BasicMonitor()); assertValueIndexIs(rightMoveOfNode1, true, 6); // left: 86293014, right: 62930147 // remove 5 (again, pseudo-conflict) -> no effect final ReferenceChange rightDeleteOfNode5 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.RIGHT), removed("Root.Node5"))); batchMerger.copyAllRightToLeft(Arrays.asList(rightDeleteOfNode5), new BasicMonitor()); assertValueIndexIs(rightDeleteOfNode5, true, -1); // left: 86293014, right: 62930147 // revert add 8 final ReferenceChange leftAddOfNode8 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.LEFT), added("Root.Node8"))); batchMerger.copyAllRightToLeft(Arrays.asList(leftAddOfNode8), new BasicMonitor()); assertValueIndexIs(leftAddOfNode8, false, -1); // left: 6293014, right: 62930147 // revert delete 7 final ReferenceChange leftDeleteOfNode7 = (ReferenceChange)Iterators.find(differences.iterator(), and(fromSide(DifferenceSource.LEFT), removed("Root.Node7"))); batchMerger.copyAllRightToLeft(Arrays.asList(leftDeleteOfNode7), new BasicMonitor()); assertValueIndexIs(leftDeleteOfNode7, false, 7); // left: 62930147, right: 62930147 // Left and Right should now be equal final EObject leftContainer = leftDeleteOfNode7.getMatch().getLeft(); final EObject rightContainer = leftDeleteOfNode7.getMatch().getRight(); final List<EObject> leftContents = getAsList(leftContainer, leftDeleteOfNode7.getReference()); final List<EObject> rightContents = getAsList(rightContainer, leftDeleteOfNode7.getReference()); assertEqualContents(comparison, leftContents, rightContents); } @Test public void testEquivalenceA1LtoR() throws IOException { final Resource left = equivalenceInput.getA1Left(); final Resource right = equivalenceInput.getA1Right(); final IComparisonScope scope = new DefaultComparisonScope(left, right, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); // Initially 6 differences, equivalent by pairs assertEquals(6, differences.size()); // diff1 is equivalent to diff2 // diff3 is equivalent to diff4 // diff5 is equivalent to diff6 final ReferenceChange diff1 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.A", "destination", null, "Requirements.B")); final ReferenceChange diff2 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.B", "source", null, "Requirements.A")); final ReferenceChange diff3 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements.C", "destination", "Requirements.D")); final ReferenceChange diff4 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.D", "source", null, "Requirements.C")); final ReferenceChange diff5 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements.E", "destination", "Requirements.F")); final ReferenceChange diff6 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements.F", "source", "Requirements.E")); batchMerger.copyAllLeftToRight(Arrays.asList(diff1), new BasicMonitor()); // Check that diff1 got properly merged assertMerged(comparison, diff1, false, false); // And validate that diff2 got merged as an equivalent diff assertMerged(comparison, diff2, false, false); batchMerger.copyAllLeftToRight(Arrays.asList(diff3), new BasicMonitor()); // Check that diff3 got properly merged assertMerged(comparison, diff3, false, false); // And validate that diff4 got merged as an equivalent diff assertMerged(comparison, diff4, false, false); batchMerger.copyAllLeftToRight(Arrays.asList(diff5), new BasicMonitor()); // Check that diff5 got properly merged assertMerged(comparison, diff5, false, false); // And validate that diff6 got merged as an equivalent diff assertMerged(comparison, diff6, false, false); comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } @Test public void testEquivalenceA1RtoL() throws IOException { final Resource left = equivalenceInput.getA1Left(); final Resource right = equivalenceInput.getA1Right(); final IComparisonScope scope = new DefaultComparisonScope(left, right, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); // Initially 6 differences, equivalent by pairs assertEquals(6, differences.size()); // diff1 is equivalent to diff2 // diff3 is equivalent to diff4 // diff5 is equivalent to diff6 final ReferenceChange diff1 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.A", "destination", null, "Requirements.B")); final ReferenceChange diff2 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.B", "source", null, "Requirements.A")); final ReferenceChange diff3 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements.C", "destination", "Requirements.D")); final ReferenceChange diff4 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.D", "source", null, "Requirements.C")); final ReferenceChange diff5 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements.E", "destination", "Requirements.F")); final ReferenceChange diff6 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements.F", "source", "Requirements.E")); batchMerger.copyAllRightToLeft(Arrays.asList(diff1), new BasicMonitor()); // Check that diff1 got properly merged (we're unsetting values) assertDiscarded(comparison, diff1, true, true); // And validate that diff2 got merged as an equivalent diff assertDiscarded(comparison, diff2, true, true); batchMerger.copyAllRightToLeft(Arrays.asList(diff3), new BasicMonitor()); assertDiscarded(comparison, diff3, true, true); assertDiscarded(comparison, diff4, true, true); batchMerger.copyAllRightToLeft(Arrays.asList(diff5), new BasicMonitor()); assertDiscarded(comparison, diff5, true, true); assertDiscarded(comparison, diff6, true, true); comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } @Test public void testEquivalenceA4LtoR() throws IOException { final Resource left = equivalenceInput.getA4Left(); final Resource right = equivalenceInput.getA4Right(); final IComparisonScope scope = new DefaultComparisonScope(left, right, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); // Initially 4 differences, equivalent by pairs assertEquals(4, differences.size()); // diff1 is equivalent to diff2 // diff3 is equivalent to diff4 final ReferenceChange diff1 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements.A", "destination", "Requirements.B")); final ReferenceChange diff2 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements.B", "source", "Requirements.A")); final ReferenceChange diff3 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements.B", "destination", "Requirements.A")); final ReferenceChange diff4 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements.A", "source", "Requirements.B")); batchMerger.copyAllLeftToRight(Arrays.asList(diff1), new BasicMonitor()); assertMerged(comparison, diff1, false, false); assertMerged(comparison, diff2, false, false); batchMerger.copyAllLeftToRight(Arrays.asList(diff3), new BasicMonitor()); assertMerged(comparison, diff3, false, false); assertMerged(comparison, diff4, false, false); comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } @Test public void testEquivalenceA4RtoL() throws IOException { final Resource left = equivalenceInput.getA4Left(); final Resource right = equivalenceInput.getA4Right(); final IComparisonScope scope = new DefaultComparisonScope(left, right, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); // Initially 4 differences, equivalent by pairs assertEquals(4, differences.size()); // diff1 is equivalent to diff2 // diff3 is equivalent to diff4 final ReferenceChange diff1 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements.A", "destination", "Requirements.B")); final ReferenceChange diff2 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements.B", "source", "Requirements.A")); final ReferenceChange diff3 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements.B", "destination", "Requirements.A")); final ReferenceChange diff4 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements.A", "source", "Requirements.B")); batchMerger.copyAllRightToLeft(Arrays.asList(diff1), new BasicMonitor()); assertDiscarded(comparison, diff1, true, true); assertDiscarded(comparison, diff2, true, true); batchMerger.copyAllRightToLeft(Arrays.asList(diff3), new BasicMonitor()); assertDiscarded(comparison, diff3, true, true); assertDiscarded(comparison, diff4, true, true); comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } @Test public void testEquivalenceA5LtoR() throws IOException { final Resource left = equivalenceInput.getA5Left(); final Resource right = equivalenceInput.getA5Right(); final IComparisonScope scope = new DefaultComparisonScope(left, right, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); // Initially 12 differences, 3 equivalent pairs, some dependencies assertEquals(12, differences.size()); // diff1 is equivalent to diff2 // diff3 is equivalent to diff4 // diff5 is equivalent to diff6 // diff1 depends on diff7 and diff 8 // diff2 also depends on 7 and 8 (equivalent diffs have the same requires) // 3 and 4 both depend on 9 and 10 // 5 and 6 both depend on 11 and 12 final ReferenceChange diff1 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.A", "destination", null, "Requirements.B")); final ReferenceChange diff2 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.B", "source", null, "Requirements.A")); final ReferenceChange diff3 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements.C", "destination", "Requirements.D")); final ReferenceChange diff4 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.D", "source", null, "Requirements.C")); final ReferenceChange diff5 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements.E", "destination", "Requirements.F")); final ReferenceChange diff6 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements.F", "source", "Requirements.E")); final ReferenceChange diff7 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements", "containmentRef1", "Requirements.A")); final ReferenceChange diff8 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements", "containmentRef1", "Requirements.B")); final ReferenceChange diff9 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements", "containmentRef1", "Requirements.C")); final ReferenceChange diff10 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements", "containmentRef1", "Requirements.D")); final ReferenceChange diff11 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements", "containmentRef1", "Requirements.E")); final ReferenceChange diff12 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements", "containmentRef1", "Requirements.F")); batchMerger.copyAllLeftToRight(Arrays.asList(diff1), new BasicMonitor()); assertMerged(comparison, diff1, false, false); assertMerged(comparison, diff2, false, false); assertSame(DifferenceState.MERGED, diff7.getState()); assertSame(DifferenceState.MERGED, diff8.getState()); batchMerger.copyAllLeftToRight(Arrays.asList(diff3), new BasicMonitor()); assertMerged(comparison, diff3, false, false); assertMerged(comparison, diff4, false, false); assertSame(DifferenceState.MERGED, diff9.getState()); assertSame(DifferenceState.MERGED, diff10.getState()); batchMerger.copyAllLeftToRight(Arrays.asList(diff5), new BasicMonitor()); assertMerged(comparison, diff5, false, false); assertMerged(comparison, diff6, false, false); assertSame(DifferenceState.MERGED, diff11.getState()); assertSame(DifferenceState.MERGED, diff12.getState()); comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } @Test public void testEquivalenceA5RtoL() throws IOException { final Resource left = equivalenceInput.getA5Left(); final Resource right = equivalenceInput.getA5Right(); final IComparisonScope scope = new DefaultComparisonScope(left, right, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); // Initially 12 differences, 3 equivalent pairs, some dependencies assertEquals(12, differences.size()); // diff1 is equivalent to diff2 // diff3 is equivalent to diff4 // diff5 is equivalent to diff6 // diff1 depends on diff7 and diff 8 // diff2 also depends on 7 and 8 (equivalent diffs have the same requires) // 3 and 4 both depend on 9 and 10 // 5 and 6 both depend on 11 and 12 final ReferenceChange diff1 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.A", "destination", null, "Requirements.B")); final ReferenceChange diff2 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.B", "source", null, "Requirements.A")); final ReferenceChange diff3 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements.C", "destination", "Requirements.D")); final ReferenceChange diff4 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.D", "source", null, "Requirements.C")); final ReferenceChange diff5 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements.E", "destination", "Requirements.F")); final ReferenceChange diff6 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements.F", "source", "Requirements.E")); final ReferenceChange diff7 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements", "containmentRef1", "Requirements.A")); final ReferenceChange diff8 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements", "containmentRef1", "Requirements.B")); final ReferenceChange diff9 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements", "containmentRef1", "Requirements.C")); final ReferenceChange diff10 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements", "containmentRef1", "Requirements.D")); final ReferenceChange diff11 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements", "containmentRef1", "Requirements.E")); final ReferenceChange diff12 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements", "containmentRef1", "Requirements.F")); // Removing the link between A and B does not necessarily means removing A and B // The "required" diffs will ne be merged batchMerger.copyAllRightToLeft(Arrays.asList(diff1), new BasicMonitor()); assertDiscarded(comparison, diff1, true, true); assertDiscarded(comparison, diff2, true, true); assertSame(DifferenceState.UNRESOLVED, diff7.getState()); assertSame(DifferenceState.UNRESOLVED, diff8.getState()); batchMerger.copyAllRightToLeft(Arrays.asList(diff3), new BasicMonitor()); assertDiscarded(comparison, diff3, true, true); assertDiscarded(comparison, diff4, true, true); assertSame(DifferenceState.UNRESOLVED, diff9.getState()); assertSame(DifferenceState.UNRESOLVED, diff10.getState()); batchMerger.copyAllRightToLeft(Arrays.asList(diff5), new BasicMonitor()); assertDiscarded(comparison, diff5, true, true); assertDiscarded(comparison, diff6, true, true); assertSame(DifferenceState.UNRESOLVED, diff11.getState()); assertSame(DifferenceState.UNRESOLVED, diff12.getState()); // Merge the 6 remaining diffs batchMerger.copyAllRightToLeft(Arrays.asList(diff7), new BasicMonitor()); batchMerger.copyAllRightToLeft(Arrays.asList(diff8), new BasicMonitor()); batchMerger.copyAllRightToLeft(Arrays.asList(diff9), new BasicMonitor()); batchMerger.copyAllRightToLeft(Arrays.asList(diff10), new BasicMonitor()); batchMerger.copyAllRightToLeft(Arrays.asList(diff11), new BasicMonitor()); batchMerger.copyAllRightToLeft(Arrays.asList(diff12), new BasicMonitor()); comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } @Test public void testEquivalenceC2LtoR1() throws IOException { final Resource left = equivalenceInput.getC2Left(); final Resource right = equivalenceInput.getC2Right(); final IComparisonScope scope = new DefaultComparisonScope(left, right, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); // Initially 4 differences // 1 : added C // 2 : changed reference "destination" of A to C // 3 : unset reference "source" of B // 4 : changed reference "source" of C to A // 2, 3 and 4 are equivalent // 2 and 4 require 1 assertEquals(4, differences.size()); final ReferenceChange diff1 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements", "containmentRef1", "Requirements.C")); final ReferenceChange diff2 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.A", "destination", "Requirements.B", "Requirements.C")); final ReferenceChange diff3 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.B", "source", "Requirements.A", null)); final ReferenceChange diff4 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.C", "source", null, "Requirements.A")); batchMerger.copyAllLeftToRight(Arrays.asList(diff2), new BasicMonitor()); assertSame(DifferenceState.MERGED, diff1.getState()); assertMerged(comparison, diff2, false, false); assertMerged(comparison, diff3, true, false); assertMerged(comparison, diff4, false, false); comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } @Test public void testEquivalenceC2LtoR2() throws IOException { final Resource left = equivalenceInput.getC2Left(); final Resource right = equivalenceInput.getC2Right(); final IComparisonScope scope = new DefaultComparisonScope(left, right, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); // Initially 4 differences // 1 : added C // 2 : changed reference "destination" of A to C // 3 : unset reference "source" of B // 4 : changed reference "source" of C to A // 2, 3 and 4 are equivalent // 2 and 4 require 1 assertEquals(4, differences.size()); final ReferenceChange diff1 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements", "containmentRef1", "Requirements.C")); final ReferenceChange diff2 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.A", "destination", "Requirements.B", "Requirements.C")); final ReferenceChange diff3 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.B", "source", "Requirements.A", null)); final ReferenceChange diff4 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.C", "source", null, "Requirements.A")); batchMerger.copyAllLeftToRight(Arrays.asList(diff1), new BasicMonitor()); assertSame(DifferenceState.MERGED, diff1.getState()); assertSame(DifferenceState.UNRESOLVED, diff2.getState()); assertSame(DifferenceState.UNRESOLVED, diff3.getState()); assertSame(DifferenceState.UNRESOLVED, diff4.getState()); batchMerger.copyAllLeftToRight(Arrays.asList(diff2), new BasicMonitor()); assertMerged(comparison, diff2, false, false); assertMerged(comparison, diff3, true, false); assertMerged(comparison, diff4, false, false); comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } @Test public void testEquivalenceC2RtoL() throws IOException { final Resource left = equivalenceInput.getC2Left(); final Resource right = equivalenceInput.getC2Right(); final IComparisonScope scope = new DefaultComparisonScope(left, right, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); // Initially 4 differences // 1 : added C // 2 : changed reference "destination" of A to C // 3 : unset reference "source" of B // 4 : changed reference "source" of C to A // 2, 3 and 4 are equivalent // 2 and 4 require 1 assertEquals(4, differences.size()); final ReferenceChange diff1 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements", "containmentRef1", "Requirements.C")); final ReferenceChange diff2 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.A", "destination", "Requirements.B", "Requirements.C")); final ReferenceChange diff3 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.B", "source", "Requirements.A", null)); final ReferenceChange diff4 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.C", "source", null, "Requirements.A")); batchMerger.copyAllRightToLeft(Arrays.asList(diff2), new BasicMonitor()); assertSame(DifferenceState.UNRESOLVED, diff1.getState()); assertDiscarded(comparison, diff3, false, true); assertDiscarded(comparison, diff4, true, true); /* * Diff 2 is a little more complicated than the usual. We are on a mono-valued refrence, and the merge * operation is actually resetting the value to its original state. We will need to check that. */ final EObject nodeA = diff2.getMatch().getLeft(); final EObject nodeB = diff3.getMatch().getLeft(); assertEquals("A", nodeA.eGet(nodeA.eClass().getEStructuralFeature("name"))); assertEquals("B", nodeB.eGet(nodeB.eClass().getEStructuralFeature("name"))); assertSame(nodeB, nodeA.eGet(diff2.getReference())); batchMerger.copyAllRightToLeft(Arrays.asList(diff1), new BasicMonitor()); comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } @Test public void testEquivalenceC4LtoR() throws IOException { final Resource left = equivalenceInput.getC4Left(); final Resource right = equivalenceInput.getC4Right(); final IComparisonScope scope = new DefaultComparisonScope(left, right, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); // Initially 5 differences // 1 : added C // 2 : removed B // 3 : changed reference "destination" of A to C // 4 : unset reference "source" of B // 5 : changed reference "source" of C to A // 3, 4 and 5 are equivalent // 2 requires 3 and 4 // 3 and 5 require 1 assertEquals(5, differences.size()); final ReferenceChange diff1 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements", "containmentRef1", "Requirements.C")); final ReferenceChange diff2 = (ReferenceChange)Iterators.find(differences.iterator(), removedFromReference("Requirements", "containmentRef1", "Requirements.B")); final ReferenceChange diff3 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.A", "destination", "Requirements.B", "Requirements.C")); final ReferenceChange diff4 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.B", "source", "Requirements.A", null)); final ReferenceChange diff5 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.C", "source", null, "Requirements.A")); batchMerger.copyAllLeftToRight(Arrays.asList(diff1), new BasicMonitor()); assertMerged(comparison, diff1, false, false); assertSame(DifferenceState.UNRESOLVED, diff2.getState()); assertSame(DifferenceState.UNRESOLVED, diff3.getState()); assertSame(DifferenceState.UNRESOLVED, diff4.getState()); assertSame(DifferenceState.UNRESOLVED, diff5.getState()); batchMerger.copyAllLeftToRight(Arrays.asList(diff2), new BasicMonitor()); // B has been deleted : unset element in right assertMerged(comparison, diff2, true, false); // Change A.destination from "B" to "C" assertMerged(comparison, diff3, false, false); // Unsetting B.source. B is no longer in either right or left. We'll have to manually check. assertSame(DifferenceState.MERGED, diff4.getState()); assertNull(diff4.getMatch().getLeft()); assertNull(diff4.getMatch().getRight()); // 2 required 3, which is equivalent to 5. 5 should thus have been merged too assertMerged(comparison, diff5, false, false); comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } @Test public void testEquivalenceC4RtoL() throws IOException { final Resource left = equivalenceInput.getC4Left(); final Resource right = equivalenceInput.getC4Right(); final IComparisonScope scope = new DefaultComparisonScope(left, right, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); // Initially 5 differences // 1 : added C // 2 : removed B // 3 : changed reference "destination" of A to C // 4 : unset reference "source" of B // 5 : changed reference "source" of C to A // 3, 4 and 5 are equivalent // 2 requires 3 and 4 // 3 and 5 require 1 assertEquals(5, differences.size()); final ReferenceChange diff1 = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Requirements", "containmentRef1", "Requirements.C")); final ReferenceChange diff2 = (ReferenceChange)Iterators.find(differences.iterator(), removedFromReference("Requirements", "containmentRef1", "Requirements.B")); final ReferenceChange diff3 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.A", "destination", "Requirements.B", "Requirements.C")); final ReferenceChange diff4 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.B", "source", "Requirements.A", null)); final ReferenceChange diff5 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Requirements.C", "source", null, "Requirements.A")); // 1 is required by 3, which is required by 2 and equivalent to 4 and 5. // Resetting 1 should thus reset all other diffs. batchMerger.copyAllRightToLeft(Arrays.asList(diff1), new BasicMonitor()); assertDiscarded(comparison, diff1, true, true); assertDiscarded(comparison, diff2, false, true); // C has been removed, thus the value match of diff3 has neither left nor right. assertSame(DifferenceState.DISCARDED, diff3.getState()); final EObject nodeA = diff3.getMatch().getLeft(); final EObject nodeB = diff4.getMatch().getLeft(); assertSame(nodeB, nodeA.eGet(diff3.getReference())); assertDiscarded(comparison, diff4, false, true); assertSame(DifferenceState.DISCARDED, diff5.getState()); assertNull(diff5.getMatch().getLeft()); assertNull(diff5.getMatch().getRight()); comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } @Test public void testEquivalenceC5LtoR() throws IOException { final Resource left = equivalenceInput.getC5Left(); final Resource right = equivalenceInput.getC5Right(); final IComparisonScope scope = new DefaultComparisonScope(left, right, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); // Initially 5 differences // 1 : Change: Set Node1.source to Node3 // 2 : Change: Set Node3.destination to Node1 // 3 : Change: Unset Node4.destination // 4 : Add: Node3 // 5 : Delete Node4 // 1-2-3 are equivalent assertEquals(5, differences.size()); final ReferenceChange diff1 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Root.Node1", "source", "Root.Node4", "Root.Node3")); final ReferenceChange diff2 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Root.Node3", "destination", null, "Root.Node1")); final ReferenceChange diff3 = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Root.Node4", "destination", "Root.Node1", null)); final ReferenceChange diff4 = (ReferenceChange)Iterators.find(differences.iterator(), added("Root.Node3")); final ReferenceChange diff5 = (ReferenceChange)Iterators.find(differences.iterator(), removed("Root.Node4")); /* * Merge diff3 first. If the merger blindly merges diff3 without proper looking at the equivalences, * diff1 and diff2 will also be set to status "merged" although the reference to be set is still * missing. */ batchMerger.copyAllLeftToRight(Arrays.asList(diff3), new BasicMonitor()); batchMerger.copyAllLeftToRight(Arrays.asList(diff1), new BasicMonitor()); batchMerger.copyAllLeftToRight(Arrays.asList(diff2), new BasicMonitor()); batchMerger.copyAllLeftToRight(Arrays.asList(diff4), new BasicMonitor()); batchMerger.copyAllLeftToRight(Arrays.asList(diff5), new BasicMonitor()); // check if no differences between models are left comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } @Test public void testEquivalenceC5RtoL() throws IOException { final Resource left = equivalenceInput.getC5Left(); final Resource right = equivalenceInput.getC5Right(); // test the other way by reusing the models from C5LtoR final IComparisonScope scope = new DefaultComparisonScope(right, left, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); final ReferenceChange unsetDiff = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Root.Node4", "destination", null, "Root.Node1")); /* * Merge the unsetDiff first. If the merger blindly merges the unsetDiff without proper looking at its * equivalences, the equivalences will also be set to status "merged" although the reference to be set * is still missing. */ batchMerger.copyAllRightToLeft(Arrays.asList(unsetDiff), new BasicMonitor()); // merge the remaining diffs for (Diff diff : differences) { if (diff != unsetDiff) { batchMerger.copyAllRightToLeft(Arrays.asList(diff), new BasicMonitor()); } } // check if no differences between models are left comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } @Test public void testEquivalenceC6LtoR() throws IOException { final Resource left = equivalenceInput.getC6Left(); final Resource right = equivalenceInput.getC6Right(); final IComparisonScope scope = new DefaultComparisonScope(left, right, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); assertEquals(3, differences.size()); // Initially 3 differences // 1 : Change: Set Node1.source to Node3 // 2 : Change: Set Node3.destination to Node1 // 3 : Change: Unset Node4.destination // 1-2-3 are equivalent final ReferenceChange unsetDiff = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Root.Node4", "destination", "Root.Node1", null)); /* * Merge the unsetDiff first. If the merger blindly merges the unsetDiff without proper looking at its * equivalences, the equivalences will also be set to status "merged" although the reference to be set * is still missing. */ batchMerger.copyAllLeftToRight(Arrays.asList(unsetDiff), new BasicMonitor()); // merge the remaining differences for (Diff diff : differences) { if (diff != unsetDiff) { batchMerger.copyAllLeftToRight(Arrays.asList(diff), new BasicMonitor()); } } // check if no differences between models are left comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } @Test public void testEquivalenceC6RtoL() throws IOException { final Resource left = equivalenceInput.getC6Left(); final Resource right = equivalenceInput.getC6Right(); // test the other way by reusing the models from C6LtoR final IComparisonScope scope = new DefaultComparisonScope(right, left, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); assertEquals(3, differences.size()); final ReferenceChange unsetDiff = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Root.Node4", "destination", null, "Root.Node1")); /* * Merge the unsetDiff first. If the merger blindly merges the unsetDiff without proper looking at its * equivalences, the equivalences will also be set to status "merged" although the reference to be set * is still missing. */ batchMerger.copyAllRightToLeft(Arrays.asList(unsetDiff), new BasicMonitor()); // merge the remaining differences for (Diff diff : differences) { if (diff != unsetDiff) { batchMerger.copyAllRightToLeft(Arrays.asList(diff), new BasicMonitor()); } } // check if no differences between models are left comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } /** * Tests a scenario in which two nodes contain interlocked one-to-one references. The merger must handle * these cases with care since diffs can become redundant during merging. */ @Test public void testOneToOneRefMergeL2R() throws IOException { final Resource left = twoWayInput.getOneToOneMergeL2RLeft(); final Resource right = twoWayInput.getOneToOneRefMergeL2RRight(); final IComparisonScope scope = new DefaultComparisonScope(left, right, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); // differences: // 1. Change c.source to c // 2. Change c.destination to c // 3. Change d.source to null // 4. Change d.destination to null // 1,4 and 2,3 are equivalent final ReferenceChange setCSourceDiff = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Root.c", "source", "Root.d", "Root.c")); final ReferenceChange setDSourceDiff = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Root.d", "source", "Root.c", null)); // By merging diff1 (setCSourceDiff) the model will be in a state where the remaining diffs // describe actions which already occurred. batchMerger.copyAllLeftToRight(Arrays.asList(setCSourceDiff), new BasicMonitor()); // Check if the non-equivalent diff is also set to merged assertEquals(DifferenceState.MERGED, setDSourceDiff.getState()); // Check if no differences between models are left comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } /** * Tests a scenario in which two nodes contain interlocked one-to-one references. The merger must handle * these cases with care since diffs can become redundant during merging. */ @Test public void testOneToOneRefMergeR2L() throws IOException { final Resource left = twoWayInput.getOneToOneMergeR2LLeft(); final Resource right = twoWayInput.getOneToOneRefMergeR2LRight(); final IComparisonScope scope = new DefaultComparisonScope(left, right, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); // differences: // 1. Change c.source to d // 2. Change c.destination to d // 3. Change d.source to c // 4. Change d.destination to c // 1,4 and 2,3 are equivalent final ReferenceChange setCSourceDiff = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Root.c", "source", "Root.c", "Root.d")); final ReferenceChange setDSourceDiff = (ReferenceChange)Iterators.find(differences.iterator(), changedReference("Root.d", "source", null, "Root.c")); // By merging diff1 (setCSourceDiff) R2L the model will be in a state where the remaining diffs // describe actions which already occurred. batchMerger.copyAllRightToLeft(Arrays.asList(setCSourceDiff), new BasicMonitor()); // Check if the non-equivalent diff is marked as merged assertEquals(DifferenceState.DISCARDED, setDSourceDiff.getState()); // Check if no differences between models are left comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } @Test public void testEquivalenceC7LtoR() throws IOException { final Resource left = equivalenceInput.getC7Left(); final Resource right = equivalenceInput.getC7Right(); final IComparisonScope scope = new DefaultComparisonScope(left, right, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); assertEquals(4, differences.size()); final ReferenceChange deleteDiff = (ReferenceChange)Iterators.find(differences.iterator(), removedFromReference("Root.a", "destination", "Root.a")); /* * Merge the deleteDiff first. If the merger blindly merges the deleteDiff without proper looking at * its equivalences, the equivalences will also be set to status "merged" although the reference to be * added is still missing. */ batchMerger.copyAllLeftToRight(Arrays.asList(deleteDiff), new BasicMonitor()); // merge the remaining differences for (Diff diff : differences) { if (diff != deleteDiff) { batchMerger.copyAllLeftToRight(Arrays.asList(diff), new BasicMonitor()); } } // check if no differences between models are left comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } @Test public void testEquivalenceC7RtoL() throws IOException { final Resource left = equivalenceInput.getC7Left(); final Resource right = equivalenceInput.getC7Right(); final IComparisonScope scope = new DefaultComparisonScope(left, right, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); assertEquals(4, differences.size()); final ReferenceChange deleteDiff = (ReferenceChange)Iterators.find(differences.iterator(), addedToReference("Root.b", "destination", "Root.a")); /* * Merge the diff resulting in a delete first. If the merger blindly merges the deleteDiff without * proper looking at its equivalences, the equivalences will also be set to status "merged" although * the reference to be added is still missing. */ batchMerger.copyAllRightToLeft(Arrays.asList(deleteDiff), new BasicMonitor()); // merge the remaining differences for (Diff diff : differences) { if (diff != deleteDiff) { batchMerger.copyAllRightToLeft(Arrays.asList(diff), new BasicMonitor()); } } // check if no differences between models are left comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } @Test public void testFeatureMapDependencyL2R() throws IOException { ResourceSetImpl resourceSet = new ResourceSetImpl(); final Resource left = twoWayInput.getFeatureMapDependencyL2RLeft(resourceSet); final Resource right = twoWayInput.getFeatureMapDependencyL2RRight(resourceSet); final IComparisonScope scope = new DefaultComparisonScope(left, right, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); // There should be 3 differences // 1. Add first key (ReferenceChange) // 2. Add first key (FeatureMapChange) // 3. Add NodeFeatureMapContainment (ReferenceChange) assertEquals(3, comparison.getDifferences().size()); FeatureMapChange addFirstKey = (FeatureMapChange)Iterators.find(differences.iterator(), instanceOf(FeatureMapChange.class)); assertNotNull(addFirstKey); // Execute FeatureMapChange to test if it properly resolves its dependencies batchMerger.copyAllLeftToRight(Arrays.asList(addFirstKey), new BasicMonitor()); // Execute the remaining differences for (Diff diff : differences) { if (diff != addFirstKey) { batchMerger.copyAllLeftToRight(Arrays.asList(diff), new BasicMonitor()); } } // Check if no differences are left comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } @Test public void testRemoveFeatureMapR2L() throws IOException { ResourceSetImpl resourceSet = new ResourceSetImpl(); final Resource left = twoWayInput.getRemoveFeatureMapR2LLeft(resourceSet); final Resource right = twoWayInput.getRemoveFeatureMapR2LRight(resourceSet); final IComparisonScope scope = new DefaultComparisonScope(left, right, null); Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); // There should be 5 differences // 1. Remove first key (ReferenceChange) // 2. Remove first key (FeatureMapChange) // 3. Remove second key (ReferenceChange) // 4. Remove second key (FeatureMapChange) // 5. Remove NodeFeatureMapContainment (ReferenceChange) assertEquals(5, comparison.getDifferences().size()); // Also test dependencies by merging the FeatureMapChanges first Iterator<Diff> featureMapChangesIt = Iterators.filter(differences.iterator(), instanceOf(FeatureMapChange.class)); List<Diff> featureMapChanges = Lists.newArrayList(featureMapChangesIt); assertEquals(2, featureMapChanges.size()); // Execute FeatureMapChanges first to test if they properly resolve their dependencies for (Diff diff : featureMapChanges) { batchMerger.copyAllRightToLeft(Arrays.asList(diff), new BasicMonitor()); } // Execute the remaining differences for (Diff diff : differences) { if (!featureMapChanges.contains(diff)) { batchMerger.copyAllRightToLeft(Arrays.asList(diff), new BasicMonitor()); } } // Check if no differences are left comparison = EMFCompare.builder().build().compare(scope); assertEquals(0, comparison.getDifferences().size()); } @Test public void testMergeAllDiffsTwice() throws IOException { final IdentifierMatchInputData inputData = new IdentifierMatchInputData(); final Resource left = inputData.getExtlibraryLeft(); final Resource origin = inputData.getExtlibraryOrigin(); final Resource right = inputData.getExtlibraryRight(); final IComparisonScope scope = new DefaultComparisonScope(left, right, origin); final Comparison comparison = EMFCompare.builder().build().compare(scope); final List<Diff> differences = comparison.getDifferences(); assertFalse(differences.isEmpty()); // Just test no NPE is raising for (Diff diff : differences) { ComparisonUtil.getSubDiffs(true).apply(diff); batchMerger.copyAllLeftToRight(Arrays.asList(diff), new BasicMonitor()); ComparisonUtil.getSubDiffs(true).apply(diff); batchMerger.copyAllLeftToRight(Arrays.asList(diff), new BasicMonitor()); } } /** * Ensures that the two given lists contain the same elements in the same order. The kind of list does not * matter. * * @param list1 * First of the two lists to compare. * @param list2 * Second of the two lists to compare. */ private static <T extends EObject> void assertEqualContents(Comparison comparison, List<T> list1, List<T> list2) { final int size = list1.size(); assertEquals(size, list2.size()); for (int i = 0; i < size; i++) { final EObject eObject1 = list1.get(i); final EObject eObject2 = list2.get(i); final Match match = comparison.getMatch(eObject1); if (match.getLeft() == eObject1) { assertEquals(match.getRight(), eObject2); } else { assertEquals(match.getRight(), eObject1); assertEquals(match.getLeft(), eObject2); } } } /* NOTE : not meant for containment changes */ private static void assertMerged(Comparison comparison, ReferenceChange referenceChange, boolean unset, boolean rightToLeft) { assertSame(referenceChange.getState(), DifferenceState.MERGED); checkFinalState(comparison, referenceChange, unset, rightToLeft); } /* NOTE : not meant for containment changes */ private static void assertDiscarded(Comparison comparison, ReferenceChange referenceChange, boolean unset, boolean rightToLeft) { assertSame(referenceChange.getState(), DifferenceState.DISCARDED); checkFinalState(comparison, referenceChange, unset, rightToLeft); } private static void checkFinalState(Comparison comparison, ReferenceChange referenceChange, boolean unset, boolean rightToLeft) { final EObject container; if (rightToLeft) { container = referenceChange.getMatch().getLeft(); } else { container = referenceChange.getMatch().getRight(); } final EReference ref = referenceChange.getReference(); final Match valueMatch = comparison.getMatch(referenceChange.getValue()); if (unset && ref.isContainment()) { assertNull(valueMatch); } else if (ref.isMany()) { @SuppressWarnings("unchecked") final List<EObject> refValue = (List<EObject>)container.eGet(ref); if (rightToLeft && unset) { assertTrue(!refValue.contains(valueMatch.getLeft())); } else if (rightToLeft) { assertTrue(refValue.contains(valueMatch.getLeft())); } else if (unset) { assertTrue(!refValue.contains(valueMatch.getRight())); } else { assertTrue(refValue.contains(valueMatch.getRight())); } } else { final EObject refValue = (EObject)container.eGet(ref); if (unset) { assertSame(null, refValue); } else if (rightToLeft) { assertSame(valueMatch.getLeft(), refValue); } else { assertSame(valueMatch.getRight(), refValue); } } } private static void assertValueIndexIs(ReferenceChange diff, boolean rightToLeft, int expectedIndex) { final Match containerMatch = diff.getMatch(); final Match valueMatch = containerMatch.getComparison().getMatch(diff.getValue()); if (rightToLeft) { if (expectedIndex != -1) { assertNotNull(valueMatch.getLeft()); assertSame(containerMatch.getLeft().eResource(), valueMatch.getLeft().eResource()); final EObject addedToLeft = valueMatch.getLeft(); final List<EObject> values = getAsList(containerMatch.getLeft(), diff.getReference()); assertEquals(expectedIndex, values.indexOf(addedToLeft)); } else { assertTrue(valueMatch == null || valueMatch.getLeft() == null); } } else { if (expectedIndex != -1) { assertNotNull(valueMatch.getRight()); assertSame(containerMatch.getRight().eResource(), valueMatch.getRight().eResource()); final EObject addedToRight = valueMatch.getRight(); final List<EObject> values = getAsList(containerMatch.getRight(), diff.getReference()); assertEquals(expectedIndex, values.indexOf(addedToRight)); } else { assertTrue(valueMatch == null || valueMatch.getRight() == null); } } } @SuppressWarnings("unchecked") private static List<EObject> getAsList(EObject object, EReference feature) { if (object != null) { Object value = object.eGet(feature, false); final List<EObject> asList; if (value instanceof List) { asList = (List<EObject>)value; } else if (value instanceof Iterable) { asList = ImmutableList.copyOf((Iterable<EObject>)value); } else if (value != null) { asList = ImmutableList.of((EObject)value); } else { asList = Collections.emptyList(); } return asList; } return Collections.emptyList(); } }