/*******************************************************************************
* Copyright (c) 2014, 2015 EclipseSource Muenchen 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:
* Philip Langer - initial API and implementation
*******************************************************************************/
package org.eclipse.emf.compare.tests.merge;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.util.ArrayList;
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.EMFCompare;
import org.eclipse.emf.compare.merge.BatchMerger;
import org.eclipse.emf.compare.merge.IBatchMerger;
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.ecore.resource.Resource;
import org.junit.Test;
/**
* Tests merging of concurrent changes of multi-line string attributes.
*
* @author Philip Langer <planger@eclipsesource.com>
*/
@SuppressWarnings("nls")
public class MultiLineAttributeMergeTest {
private static final String NL = "\n";
private IMerger.Registry mergerRegistry = IMerger.RegistryImpl.createStandaloneInstance();
@Test
public void changeSingleLineTextOnOneSide() throws IOException {
final String origin = "They don't call it a Quarter Pounder with Cheese?";
final String left = "They don't call it a Quarter Pounder with Cheese?";
final String right = "They do not call it a Quarter Pounder with Cheese?"; // changed
final String merged = right;
assertRejectedAndMergedBidirectional(origin, left, right, merged);
}
@Test
public void addSingleLineTextOnOneSide() throws IOException {
final String origin = "";
final String left = "They don't call it a Quarter Pounder with Cheese?";
final String right = "";
final String merged = left;
assertRejectedAndMergedBidirectional(origin, left, right, merged);
}
@Test
public void unsetSingleLineTextOnOneSide() throws IOException {
final String origin = "They don't call it a Quarter Pounder with Cheese?";
final String left = "They don't call it a Quarter Pounder with Cheese?";
final String right = null; // unset
final String merged = right;
assertRejectedAndMergedBidirectional(origin, left, right, merged);
}
@Test
public void removeSingleLineTextOnOneSide() throws IOException {
final String origin = "They don't call it a Quarter Pounder with Cheese?";
final String left = "They don't call it a Quarter Pounder with Cheese?";
final String right = ""; // removed
final String merged = null; // empty String is interpreted as unset by EMF Compare
assertRejectedAndMergedBidirectional(origin, left, right, merged);
}
@Test
public void changeAndRemoveLinesInMultiLineTextOnOneSide() throws IOException {
final String origin = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "Nah, they got the metric system, they wouldn't know what a Quarter Pounder is." + NL //
+ "What do they call it?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right." + NL //
+ "What do they call a Big Mac?" + NL //
+ "A Big Mac's a Big Mac, but they call it Le Big Mac.";
final String left = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "Nah, they got the metric system, they wouldn't know what a Quarter Pounder is." + NL //
+ "What do they call it?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right." + NL //
+ "What do they call a Big Mac?" + NL //
+ "A Big Mac's a Big Mac, but they call it Le Big Mac.";
final String right = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "Nah, they got the metric system, they wouldn't know what a Quarter Pounder is." + NL //
+ "What _do_ they call it?" + NL // changed
+ "They call it a Royale with Cheese." + NL //
+ "" // removed
+ "That's right." + NL //
+ "What do they call a Big Mac?" + NL //
+ "A Big Mac's a Big Mac, but they call it Le Big Mac.";
final String merged = right;
assertRejectedAndMergedBidirectional(origin, left, right, merged);
}
@Test
public void changeAndRemoveDifferentLinesInMultiLineOnTextBothSides() throws IOException {
final String origin = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "Nah, they got the metric system, they wouldn't know what a Quarter Pounder is." + NL //
+ "What do they call it?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right." + NL //
+ "What do they call a Big Mac?" + NL //
+ "A Big Mac's a Big Mac, but they call it Le Big Mac.";
final String left = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "Nah, they got the metric system, they wouldn't know what a Quarter Pounder is." + NL //
+ "What do they call it?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right." + NL //
+ "What do they call a \"Big Mac\"?" // changed
+ ""; // removed
final String right = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "Nah, they got the metric system, they wouldn't know what a Quarter Pounder is." + NL //
+ "What _do_ they call it?" + NL // changed
+ "They call it a Royale with Cheese." + NL //
+ "" // removed
+ "That's right." + NL //
+ "What do they call a Big Mac?" + NL //
+ "A Big Mac's a Big Mac, but they call it Le Big Mac.";
final String merged = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "Nah, they got the metric system, they wouldn't know what a Quarter Pounder is." + NL //
+ "What _do_ they call it?" + NL // changed right
+ "They call it a Royale with Cheese." + NL //
+ "" // removed right
+ "That's right." + NL //
+ "What do they call a \"Big Mac\"?" // changed left
+ ""; // removed left
assertRejectedAndMergedBidirectional(origin, left, right, merged);
}
@Test
public void addDifferentLineAtDifferentLineInMultiLineText() throws IOException {
final String origin = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right.";
final String left = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "Nah, they got the metric system, they wouldn't know what a Quarter Pounder is." + NL // added
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right.";
final String right = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right." + NL //
+ "What do they call a Big Mac?"; // added
final String merged = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "Nah, they got the metric system, they wouldn't know what a Quarter Pounder is." + NL // added
// left
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right." + NL //
+ "What do they call a Big Mac?"; // added right;
assertRejectedAndMergedBidirectional(origin, left, right, merged);
}
@Test
public void acceptingLeftInsertOnRightSideAndRejectingRightInsertOnRightSideAfterwards()
throws IOException {
final String origin = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right.";
final String left = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "Nah, they got the metric system." + NL // added
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right.";
final String right = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "What do they call a Big Mac?" + NL // added
+ "That's right.";
assertAcceptingLeftOnRightSideAndRejectingRightOnRightSideIsLeftValue(origin, left, right);
assertAcceptingRightOnLeftSideAndRejectingLeftOnLeftSideIsRightValue(origin, right, left);
}
@Test
public void acceptingLeftInsertOnRightSideAndRejectingRightDeleteOnRightSideAfterwards()
throws IOException {
final String origin = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right.";
final String left = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "Nah, they got the metric system." + NL // added
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right.";
final String right = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "" // deleted
+ "That's right.";
assertAcceptingLeftOnRightSideAndRejectingRightOnRightSideIsLeftValue(origin, left, right);
assertAcceptingRightOnLeftSideAndRejectingLeftOnLeftSideIsRightValue(origin, right, left);
}
@Test
public void acceptingLeftInsertOnRightSideAndRejectingRightChangeOnRightSideAfterwards()
throws IOException {
final String origin = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right.";
final String left = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "Nah, they got the metric system." + NL // added
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right.";
final String right = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right!!!!"; // changed
assertAcceptingLeftOnRightSideAndRejectingRightOnRightSideIsLeftValue(origin, left, right);
assertAcceptingRightOnLeftSideAndRejectingLeftOnLeftSideIsRightValue(origin, right, left);
}
@Test
public void acceptingLeftChangeOnRightSideAndRejectingRightInsertOnRightSideAfterwards()
throws IOException {
final String origin = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right.";
final String left = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese!!!" + NL // changed
+ "Royale with Cheese." + NL //
+ "That's right.";
final String right = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "What do they call a Big Mac?" + NL// added
+ "That's right."; //
assertAcceptingLeftOnRightSideAndRejectingRightOnRightSideIsLeftValue(origin, left, right);
assertAcceptingRightOnLeftSideAndRejectingLeftOnLeftSideIsRightValue(origin, right, left);
}
@Test
public void acceptingLeftChangeOnRightSideAndRejectingRightDeleteOnRightSideAfterwards()
throws IOException {
final String origin = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right." + NL //
+ "What do they call a Big Mac?";
final String left = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese!!!" + NL // changed
+ "Royale with Cheese." + NL //
+ "That's right." + NL //
+ "What do they call a Big Mac?";
final String right = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "" // deleted
+ "What do they call a Big Mac?";
assertAcceptingLeftOnRightSideAndRejectingRightOnRightSideIsLeftValue(origin, left, right);
assertAcceptingRightOnLeftSideAndRejectingLeftOnLeftSideIsRightValue(origin, right, left);
}
@Test
public void acceptingLeftChangeOnRightSideAndRejectingRightChangeOnRightSideAfterwards()
throws IOException {
final String origin = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right." + NL //
+ "What do they call a Big Mac?";
final String left = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese!!!" + NL // changed
+ "Royale with Cheese." + NL //
+ "That's right." + NL //
+ "What do they call a Big Mac?";
final String right = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right." + NL //
+ "What do they call a Big Mac?????"; // changed
assertAcceptingLeftOnRightSideAndRejectingRightOnRightSideIsLeftValue(origin, left, right);
assertAcceptingRightOnLeftSideAndRejectingLeftOnLeftSideIsRightValue(origin, right, left);
}
@Test
public void acceptingLeftDeleteOnRightSideAndRejectingRightInsertOnRightSideAfterwards()
throws IOException {
final String origin = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right." + NL //
+ "What do they call a Big Mac?";
final String left = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "" // deleted
+ "Royale with Cheese." + NL //
+ "That's right." + NL //
+ "What do they call a Big Mac?";
final String right = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right." + NL //
+ "That's freaking right." + NL // added
+ "What do they call a Big Mac?";
assertAcceptingLeftOnRightSideAndRejectingRightOnRightSideIsLeftValue(origin, left, right);
assertAcceptingRightOnLeftSideAndRejectingLeftOnLeftSideIsRightValue(origin, right, left);
}
@Test
public void acceptingLeftDeleteOnRightSideAndRejectingRightChangeOnRightSideAfterwards()
throws IOException {
final String origin = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right." + NL //
+ "What do they call a Big Mac?";
final String left = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "" // deleted
+ "Royale with Cheese." + NL //
+ "That's right." + NL //
+ "What do they call a Big Mac?";
final String right = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right." + NL //
+ "What do they call a Big Mac?????"; // changed
assertAcceptingLeftOnRightSideAndRejectingRightOnRightSideIsLeftValue(origin, left, right);
assertAcceptingRightOnLeftSideAndRejectingLeftOnLeftSideIsRightValue(origin, right, left);
}
@Test
public void acceptingLeftDeleteOnRightSideAndRejectingRightDeleteOnRightSideAfterwards()
throws IOException {
final String origin = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "That's right." + NL //
+ "What do they call a Big Mac?";
final String left = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "" // deleted
+ "Royale with Cheese." + NL //
+ "That's right." + NL //
+ "What do they call a Big Mac?";
final String right = "They don't call it a Quarter Pounder with Cheese?" + NL //
+ "They call it a Royale with Cheese." + NL //
+ "Royale with Cheese." + NL //
+ "" // deleted
+ "What do they call a Big Mac?"; // changed
assertAcceptingLeftOnRightSideAndRejectingRightOnRightSideIsLeftValue(origin, left, right);
assertAcceptingRightOnLeftSideAndRejectingLeftOnLeftSideIsRightValue(origin, right, left);
}
private void assertAcceptingLeftOnRightSideAndRejectingRightOnRightSideIsLeftValue(final String origin,
final String left, final String right) throws IOException {
final ThreeWayAttributeMergeScenario scenario = createMergeScenario(origin, left, right);
final Comparison comparison = compare(scenario);
acceptLeft(comparison);
rejectRight(comparison);
final String rightValue = scenario.getRightAttributeValue();
assertEquals(nullIfEmpty(left), nullIfEmpty(rightValue));
}
private void assertAcceptingRightOnLeftSideAndRejectingLeftOnLeftSideIsRightValue(final String origin,
final String left, final String right) throws IOException {
final ThreeWayAttributeMergeScenario scenario = createMergeScenario(origin, left, right);
final Comparison comparison = compare(scenario);
acceptRight(comparison);
rejectLeft(comparison);
final String leftValue = scenario.getLeftAttributeValue();
assertEquals(nullIfEmpty(right), nullIfEmpty(leftValue));
}
private void assertRejectedAndMergedBidirectional(String origin, String left, String right, String merged)
throws IOException {
assertRejectLeft(origin, left, right);
assertRejectRight(origin, left, right);
assertMergedLeftToRight(origin, left, right, merged);
assertMergedRightToLeft(origin, left, right, merged);
}
private void assertRejectLeft(String origin, String left, String right) throws IOException {
final ThreeWayAttributeMergeScenario scenario = createMergeScenario(origin, left, right);
final Comparison comparison = compare(scenario);
rejectLeft(comparison);
final String attributeValue = scenario.getLeftAttributeValue();
assertEquals(nullIfEmpty(origin), nullIfEmpty(attributeValue));
}
private void assertRejectRight(String origin, String left, String right) throws IOException {
final ThreeWayAttributeMergeScenario scenario = createMergeScenario(origin, left, right);
final Comparison comparison = compare(scenario);
rejectRight(comparison);
final String attributeValue = scenario.getRightAttributeValue();
assertEquals(nullIfEmpty(origin), nullIfEmpty(attributeValue));
}
private void assertMergedLeftToRight(String origin, String left, String right, String merged)
throws IOException {
final ThreeWayAttributeMergeScenario scenario = createMergeScenario(origin, left, right);
final Comparison comparison = compare(scenario);
acceptLeft(comparison);
final String attributeValue = scenario.getRightAttributeValue();
assertEquals(nullIfEmpty(merged), nullIfEmpty(attributeValue));
}
private void assertMergedRightToLeft(String origin, String left, String right, String merged)
throws IOException {
final ThreeWayAttributeMergeScenario scenario = createMergeScenario(origin, left, right);
final Comparison comparison = compare(scenario);
acceptRight(comparison);
final String attributeValue = scenario.getLeftAttributeValue();
assertEquals(nullIfEmpty(merged), nullIfEmpty(attributeValue));
}
private void rejectLeft(Comparison comparison) {
final List<Diff> leftDiffs = getDiffsForSource(comparison.getDifferences(), DifferenceSource.LEFT);
final IBatchMerger merger = new BatchMerger(mergerRegistry);
merger.copyAllRightToLeft(leftDiffs, new BasicMonitor());
}
private void rejectRight(Comparison comparison) {
final List<Diff> rightDiffs = getDiffsForSource(comparison.getDifferences(), DifferenceSource.RIGHT);
final IBatchMerger merger = new BatchMerger(mergerRegistry);
merger.copyAllLeftToRight(rightDiffs, new BasicMonitor());
}
private void acceptRight(Comparison comparison) {
final List<Diff> rightDiffs = getDiffsForSource(comparison.getDifferences(), DifferenceSource.RIGHT);
final IBatchMerger merger = new BatchMerger(mergerRegistry);
merger.copyAllRightToLeft(rightDiffs, new BasicMonitor());
}
private void acceptLeft(Comparison comparison) {
final List<Diff> leftDiffs = getDiffsForSource(comparison.getDifferences(), DifferenceSource.LEFT);
final IBatchMerger merger = new BatchMerger(mergerRegistry);
merger.copyAllLeftToRight(leftDiffs, new BasicMonitor());
}
private List<Diff> getDiffsForSource(List<Diff> differences, DifferenceSource source) {
List<Diff> diffsForSource = new ArrayList<Diff>();
for (Diff diff : differences) {
if (source.equals(diff.getSource())) {
diffsForSource.add(diff);
}
}
return diffsForSource;
}
private ThreeWayAttributeMergeScenario createMergeScenario(String origin, String left, String right)
throws IOException {
return new ThreeWayAttributeMergeScenario(origin, left, right);
}
private Comparison compare(final ThreeWayAttributeMergeScenario scenario) {
final Resource origin = scenario.getOriginResource();
final Resource left = scenario.getLeftResource();
final Resource right = scenario.getRightResource();
final IComparisonScope scope = new DefaultComparisonScope(left, right, origin);
final Comparison comparison = EMFCompare.builder().build().compare(scope);
return comparison;
}
private String nullIfEmpty(String string) {
if (string == null || string.isEmpty()) {
return null;
} else {
return string;
}
}
}