/*******************************************************************************
* Copyright (c) 2016 EclipseSource Services GmbH 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:
* Martin Fleck - initial API and implementation
*******************************************************************************/
package org.eclipse.emf.compare.ide.ui.tests.structuremergeviewer.actions;
import static java.util.Arrays.asList;
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.DifferenceState.UNRESOLVED;
import static org.eclipse.emf.compare.internal.merge.MergeMode.LEFT_TO_RIGHT;
import java.util.List;
import org.eclipse.emf.compare.Comparison;
import org.eclipse.emf.compare.Conflict;
import org.eclipse.emf.compare.ConflictKind;
import org.eclipse.emf.compare.Diff;
import org.eclipse.emf.compare.DifferenceSource;
import org.eclipse.emf.compare.Match;
import org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.actions.MergeNonConflictingRunnable;
import org.eclipse.emf.compare.internal.merge.MergeMode;
import org.eclipse.emf.compare.internal.spec.ComparisonSpec;
import org.eclipse.emf.compare.internal.spec.ConflictSpec;
import org.eclipse.emf.compare.internal.spec.DiffSpec;
import org.eclipse.emf.compare.internal.spec.MatchSpec;
import org.eclipse.emf.compare.merge.AbstractMerger;
import org.eclipse.emf.compare.merge.IMerger;
import org.junit.Assert;
import org.junit.Test;
/**
* Tests for ensuring that refinement diffs are only merged together as a whole or not at all when merging
* non-conflicting changes.
*
* @author Martin Fleck <mfleck@eclipsesource.com>
*/
@SuppressWarnings({"restriction", "nls" })
public class MergeNonConflictingRunnableRefinementTest {
private static final IMerger.Registry MERGER_REGISTRY = IMerger.RegistryImpl.createStandaloneInstance();
static {
MERGER_REGISTRY.add(new AbstractMerger() {
public boolean isMergerFor(Diff target) {
return true;
}
});
}
/**
* Creates a new difference.
*
* @param name
* name used in {@link #toString()} for convenience.
* @param source
* difference source.
* @return newly created difference
*/
protected Diff createDiff(final String name, DifferenceSource source) {
Diff diff = new DiffSpec() {
@Override
public String toString() {
return name;
}
};
diff.setSource(source);
return diff;
}
/**
* Creates a new conflict.
*
* @param kind
* conflict kind.
* @param diffs
* differences that participate in the conflict.
* @return newly created conflict
*/
protected Conflict createConflict(ConflictKind kind, Diff... diffs) {
Conflict conflict = new ConflictSpec();
conflict.setKind(kind);
conflict.getDifferences().addAll(asList(diffs));
return conflict;
}
/**
* Creates a new comparison with a single match that holds all differences.
*
* @param differences
* differences in the comparison
* @param conflicts
* conflicts in the comparison
* @return newly created comparison
*/
protected Comparison createComparison(List<Diff> differences, List<Conflict> conflicts) {
Comparison comparison = new ComparisonSpec();
Match match = new MatchSpec();
match.getDifferences().addAll(differences);
comparison.getMatches().add(match);
comparison.getConflicts().addAll(conflicts);
comparison.setThreeWay(false);
return comparison;
}
/**
* With the following refinement graph. Upper layers are refined by lower layers:
*
* <pre>
* Left | Right
* --------+---------
* A | A
* / \ | / \
* B C | B C
*
* Merge: Left To Right
* Real Conflict: Left.B, Right.B
* Expected: No Diffs merged
* Start Diff: Left.B
* </pre>
*/
@Test
public void testMergeNonConflictingWithBStartB() {
final boolean isLeftToRight = true;
final MergeMode mergeMode = LEFT_TO_RIGHT;
// Create diffs
Diff leftA = createDiff("LeftA", LEFT);
Diff leftB = createDiff("LeftB", LEFT);
Diff leftC = createDiff("LeftC", LEFT);
leftA.getRefines().add(leftB);
leftA.getRefines().add(leftC);
// Create diffs
Diff rightA = createDiff("RightA", RIGHT);
Diff rightB = createDiff("RightB", RIGHT);
Diff rightC = createDiff("RightC", RIGHT);
rightA.getRefines().add(rightB);
rightA.getRefines().add(rightC);
// Add conflicts
Conflict conflict = createConflict(REAL, leftB, rightB);
Comparison comparison = createComparison(asList(leftB, leftC, leftA, rightB, rightC, rightA),
asList(conflict));
mergeNonConflictingChanges(comparison, mergeMode, isLeftToRight);
verifyAllUnresolved(comparison.getDifferences());
}
/**
* With the following refinement graph. Upper layers are refined by lower layers:
*
* <pre>
* Left | Right
* --------+---------
* A | A
* / \ | / \
* B C | B C
*
* Merge: Left To Right
* Real Conflict: Left.B, Right.B
* Expected: No Diffs merged
* Start Diff: Left.C
* </pre>
*/
@Test
public void testMergeNonConflictingWithBStartC() {
final boolean isLeftToRight = true;
final MergeMode mergeMode = LEFT_TO_RIGHT;
// Create diffs
Diff leftA = createDiff("LeftA", LEFT);
Diff leftB = createDiff("LeftB", LEFT);
Diff leftC = createDiff("LeftC", LEFT);
leftA.getRefines().add(leftB);
leftA.getRefines().add(leftC);
// Create diffs
Diff rightA = createDiff("RightA", RIGHT);
Diff rightB = createDiff("RightB", RIGHT);
Diff rightC = createDiff("RightC", RIGHT);
rightA.getRefines().add(rightB);
rightA.getRefines().add(rightC);
// Add conflicts
Conflict conflict = createConflict(REAL, leftB, rightB);
Comparison comparison = createComparison(asList(leftC, leftB, leftA, rightB, rightC, rightA),
asList(conflict));
mergeNonConflictingChanges(comparison, mergeMode, isLeftToRight);
verifyAllUnresolved(comparison.getDifferences());
}
/**
* With the following refinement graph. Upper layers are refined by lower layers:
*
* <pre>
* Left | Right
* --------+---------
* A | A
* / \ | / \
* B C | B C
*
* Merge: Left To Right
* Real Conflict: Left.B, Right.B
* Expected: No Diffs merged
* Start Diff: Left.A
* </pre>
*/
@Test
public void testMergeNonConflictingWithBStartA() {
final boolean isLeftToRight = true;
final MergeMode mergeMode = LEFT_TO_RIGHT;
// Create diffs
Diff leftA = createDiff("LeftA", LEFT);
Diff leftB = createDiff("LeftB", LEFT);
Diff leftC = createDiff("LeftC", LEFT);
leftA.getRefines().add(leftB);
leftA.getRefines().add(leftC);
// Create diffs
Diff rightA = createDiff("RightA", RIGHT);
Diff rightB = createDiff("RightB", RIGHT);
Diff rightC = createDiff("RightC", RIGHT);
rightA.getRefines().add(rightB);
rightA.getRefines().add(rightC);
// Add conflicts
Conflict conflict = createConflict(REAL, leftB, rightB);
Comparison comparison = createComparison(asList(leftA, leftC, leftB, rightB, rightC, rightA),
asList(conflict));
mergeNonConflictingChanges(comparison, mergeMode, isLeftToRight);
verifyAllUnresolved(comparison.getDifferences());
}
/**
* With the following refinement graph. Upper layers are refined by lower layers:
*
* <pre>
* Left | Right
* --------+---------
* A | A
* / \ | / \
* B C | B C
*
* Merge: Left To Right
* Real Conflict: Left.A, Right.B
* Expected: No Diffs merged
* Start Diff: Left.B
* </pre>
*/
@Test
public void testMergeNonConflictingWithAStartB() {
final boolean isLeftToRight = true;
final MergeMode mergeMode = LEFT_TO_RIGHT;
// Create diffs
Diff leftA = createDiff("LeftA", LEFT);
Diff leftB = createDiff("LeftB", LEFT);
Diff leftC = createDiff("LeftC", LEFT);
leftA.getRefines().add(leftB);
leftA.getRefines().add(leftC);
// Create diffs
Diff rightA = createDiff("RightA", RIGHT);
Diff rightB = createDiff("RightB", RIGHT);
Diff rightC = createDiff("RightC", RIGHT);
rightA.getRefines().add(rightB);
rightA.getRefines().add(rightC);
// Add conflicts
Conflict conflict = createConflict(REAL, leftA, rightB);
Comparison comparison = createComparison(asList(leftB, leftC, leftA, rightB, rightC, rightA),
asList(conflict));
mergeNonConflictingChanges(comparison, mergeMode, isLeftToRight);
verifyAllUnresolved(comparison.getDifferences());
}
/**
* With the following refinement graph. Upper layers are refined by lower layers:
*
* <pre>
* Left | Right
* --------+---------
* A | A
* / \ | / \
* B C | B C
*
* Merge: Left To Right
* Real Conflict: Left.A, Right.B
* Expected: No Diffs merged
* Start Diff: Left.C
* </pre>
*/
@Test
public void testMergeNonConflictingWithAStartC() {
final boolean isLeftToRight = true;
final MergeMode mergeMode = LEFT_TO_RIGHT;
// Create diffs
Diff leftA = createDiff("LeftA", LEFT);
Diff leftB = createDiff("LeftB", LEFT);
Diff leftC = createDiff("LeftC", LEFT);
leftA.getRefines().add(leftB);
leftA.getRefines().add(leftC);
// Create diffs
Diff rightA = createDiff("RightA", RIGHT);
Diff rightB = createDiff("RightB", RIGHT);
Diff rightC = createDiff("RightC", RIGHT);
rightA.getRefines().add(rightB);
rightA.getRefines().add(rightC);
// Add conflicts
Conflict conflict = createConflict(REAL, leftA, rightB);
Comparison comparison = createComparison(asList(leftC, leftB, leftA, rightB, rightC, rightA),
asList(conflict));
mergeNonConflictingChanges(comparison, mergeMode, isLeftToRight);
verifyAllUnresolved(comparison.getDifferences());
}
/**
* With the following refinement graph. Upper layers are refined by lower layers:
*
* <pre>
* Left | Right
* --------+---------
* A | A
* / \ | / \
* B C | B C
*
* Merge: Left To Right
* Real Conflict: Left.A, Right.B
* Expected: No Diffs merged
* Start Diff: Left.A
* </pre>
*/
@Test
public void testMergeNonConflictingWithAStartA() {
final boolean isLeftToRight = true;
final MergeMode mergeMode = LEFT_TO_RIGHT;
// Create diffs
Diff leftA = createDiff("LeftA", LEFT);
Diff leftB = createDiff("LeftB", LEFT);
Diff leftC = createDiff("LeftC", LEFT);
leftA.getRefines().add(leftB);
leftA.getRefines().add(leftC);
// Create diffs
Diff rightA = createDiff("RightA", RIGHT);
Diff rightB = createDiff("RightB", RIGHT);
Diff rightC = createDiff("RightC", RIGHT);
rightA.getRefines().add(rightB);
rightA.getRefines().add(rightC);
// Add conflicts
Conflict conflict = createConflict(REAL, leftA, rightB);
Comparison comparison = createComparison(asList(leftA, leftC, leftB, rightB, rightC, rightA),
asList(conflict));
mergeNonConflictingChanges(comparison, mergeMode, isLeftToRight);
verifyAllUnresolved(comparison.getDifferences());
}
/**
* With the following refinement graph. Upper layers are refined by lower layers:
*
* <pre>
* Left | Right
* ----------+-----------
* A | A
* / \ | / \
* B C | B C
* / \ |
* D E |
*
* Merge: Left To Right
* Real Conflict: Left.C, Right.B
* Expected: No Diffs merged
* Start Diff: Left.E
* </pre>
*/
@Test
public void testMergeNonConflictingTwoTiersWithCStartE() {
final boolean isLeftToRight = true;
final MergeMode mergeMode = LEFT_TO_RIGHT;
// Create diffs
Diff leftA = createDiff("LeftA", LEFT);
Diff leftB = createDiff("LeftB", LEFT);
Diff leftC = createDiff("LeftC", LEFT);
Diff leftD = createDiff("LeftD", LEFT);
Diff leftE = createDiff("LeftE", LEFT);
leftA.getRefines().add(leftB);
leftA.getRefines().add(leftC);
leftB.getRefines().add(leftD);
leftB.getRefines().add(leftE);
// Create diffs
Diff rightA = createDiff("RightA", RIGHT);
Diff rightB = createDiff("RightB", RIGHT);
Diff rightC = createDiff("RightC", RIGHT);
rightA.getRefines().add(rightB);
rightA.getRefines().add(rightC);
// Add conflicts
Conflict conflict = createConflict(REAL, leftC, rightB);
Comparison comparison = createComparison(
asList(leftE, leftC, leftB, leftA, leftD, rightB, rightC, rightA), asList(conflict));
mergeNonConflictingChanges(comparison, mergeMode, isLeftToRight);
verifyAllUnresolved(comparison.getDifferences());
}
/**
* With the following refinement graph. Upper layers are refined by lower layers:
*
* <pre>
* Left | Right
* --------------+-----------
* A B | A
* / \ / \ | / \
* C D E | B C
*
* Merge: Left To Right
* Real Conflict: Left.D, Right.B
* Expected: No Diffs merged
* Start Diff: Left.C
* </pre>
*/
@Test
public void testMergeNonConflictingSharedRefiningWithCStartC() {
final boolean isLeftToRight = true;
final MergeMode mergeMode = LEFT_TO_RIGHT;
// Create diffs
Diff leftA = createDiff("LeftA", LEFT);
Diff leftB = createDiff("LeftB", LEFT);
Diff leftC = createDiff("LeftC", LEFT);
Diff leftD = createDiff("LeftD", LEFT);
Diff leftE = createDiff("LeftE", LEFT);
leftA.getRefines().add(leftC);
leftA.getRefines().add(leftD);
leftB.getRefines().add(leftD);
leftB.getRefines().add(leftE);
// Create diffs
Diff rightA = createDiff("RightA", RIGHT);
Diff rightB = createDiff("RightB", RIGHT);
Diff rightC = createDiff("RightC", RIGHT);
rightA.getRefines().add(rightB);
rightA.getRefines().add(rightC);
// Add conflicts
Conflict conflict = createConflict(REAL, leftD, rightB);
Comparison comparison = createComparison(
asList(leftC, leftE, leftB, leftA, leftD, rightB, rightC, rightA), asList(conflict));
mergeNonConflictingChanges(comparison, mergeMode, isLeftToRight);
verifyAllUnresolved(comparison.getDifferences());
}
/**
* With the following refinement graph. Upper layers refine lower layers:
*
* <pre>
* Left | Right
* --------------+-----------
* A B | A
* / \ / \ | / \
* C D E | B C
*
* Merge: Left To Right
* Real Conflict: Left.D, Right.B
* Expected: No Diffs merged
* Start Diff: Left.A
* </pre>
*/
@Test
public void testMergeNonConflictingSharedRefiningWithCStartA() {
final boolean isLeftToRight = true;
final MergeMode mergeMode = LEFT_TO_RIGHT;
// Create diffs
Diff leftA = createDiff("LeftA", LEFT);
Diff leftB = createDiff("LeftB", LEFT);
Diff leftC = createDiff("LeftC", LEFT);
Diff leftD = createDiff("LeftD", LEFT);
Diff leftE = createDiff("LeftE", LEFT);
leftA.getRefines().add(leftC);
leftA.getRefines().add(leftD);
leftB.getRefines().add(leftD);
leftB.getRefines().add(leftE);
// Create diffs
Diff rightA = createDiff("RightA", RIGHT);
Diff rightB = createDiff("RightB", RIGHT);
Diff rightC = createDiff("RightC", RIGHT);
rightA.getRefines().add(rightB);
rightA.getRefines().add(rightC);
// Add conflicts
Conflict conflict = createConflict(REAL, leftD, rightB);
Comparison comparison = createComparison(
asList(leftA, leftB, leftE, leftC, leftD, rightB, rightC, rightA), asList(conflict));
mergeNonConflictingChanges(comparison, mergeMode, isLeftToRight);
verifyAllUnresolved(comparison.getDifferences());
}
/**
* Verifies that all differences are unresolved.
*
* @param diffs
* differences
*/
protected void verifyAllUnresolved(List<Diff> diffs) {
for (Diff diff : diffs) {
Assert.assertSame(UNRESOLVED, diff.getState());
}
}
/**
* Accepts all non-conflicting changes. The left changes will be accepted and the right changes will be
* merged into the left-hand side.
*
* @param comparison
* comparison with differences
* @param leftToRight
* direction of merge
* @return affected differences
*/
private void mergeNonConflictingChanges(Comparison comparison, MergeMode mergeMode, boolean leftToRight) {
final boolean isLeftEditable;
final boolean isRightEditable;
switch (mergeMode) {
case LEFT_TO_RIGHT:
// fall through
case RIGHT_TO_LEFT:
isLeftEditable = true;
isRightEditable = true;
break;
case ACCEPT:
// fall through
case REJECT:
isLeftEditable = true;
isRightEditable = false;
break;
default:
throw new IllegalArgumentException();
}
MergeNonConflictingRunnable mergeNonConflicting = new MergeNonConflictingRunnable(isLeftEditable,
isRightEditable, mergeMode);
mergeNonConflicting.merge(comparison, leftToRight, MERGER_REGISTRY);
}
}