| /******************************************************************************* |
| * Copyright (c) 2014, 2015 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 |
| * Philip Langer - extract super class AbstractGitLogicalModelTest |
| *******************************************************************************/ |
| package org.eclipse.emf.compare.ide.ui.tests.unit; |
| |
| 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 static org.junit.Assert.fail; |
| |
| import java.io.IOException; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.emf.compare.Comparison; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EPackage; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.jgit.api.Status; |
| import org.junit.Test; |
| |
| @SuppressWarnings("nls") |
| public class GitLogicalMergeTest extends AbstractGitLogicalModelTest { |
| |
| /** |
| * Sets up the repository so that there are three commits affecting a single file. |
| * <ul> |
| * <li>initial commit : A single file "file1" contains a package P1 which contains the classes C1 and C2. |
| * </li> |
| * <li>master commit : delete C2</li> |
| * <li>branch commit : make C2 a super-class of C1</li> |
| * </ul> |
| * <p> |
| * This is both a textual and a logical conflict. We expect both kind of merges to mark the file as |
| * conflicting, but textual merging should corrupt the model with its conflict markers whereas logical |
| * merge will leave it untouched. |
| * </p> |
| */ |
| private void setUp001() throws Exception { |
| // We don't need the second resource here |
| file2.delete(); |
| resource2 = null; |
| |
| final EPackage root = createPackage(null, "P1"); |
| createClass(root, "C1"); |
| EClass class2 = createClass(root, "C2"); |
| resource1.getContents().add(root); |
| save(resource1); |
| |
| repository.addAllAndCommit("initial-commit"); |
| |
| // Branch, but stay on master |
| repository.createBranch(MASTER, BRANCH); |
| |
| root.getEClassifiers().remove(class2); |
| save(resource1); |
| repository.addAndCommit(project, "master-commit", file1); |
| |
| repository.checkoutBranch(BRANCH); |
| reload(resource1); |
| |
| final EClass class1 = (EClass)findObject(resource1, "C1"); |
| class2 = (EClass)findObject(resource1, "C2"); |
| |
| class1.getESuperTypes().add(class2); |
| save(resource1); |
| repository.addAndCommit(project, "branch-commit", file1); |
| |
| repository.checkoutBranch(MASTER); |
| |
| final Status status = repository.status(); |
| assertFalse(status.hasUncommittedChanges()); |
| |
| // We expect one conflict, with one single diff on each side |
| compareBothDirectionsAndCheck(iFile1, MASTER, BRANCH, 1, 1, 1); |
| } |
| |
| /** |
| * Sets up the repository so that there are three commits affecting a single file. |
| * <ul> |
| * <li>initial commit : A single file "file1" contains a package P1 which contains the classes C1 and C2. |
| * </li> |
| * <li>master commit : rename C1 into C10</li> |
| * <li>branch commit : make C2 a super-class of C1</li> |
| * </ul> |
| * <p> |
| * This is a textual conflict, but there are no logical conflict. We expect the textual merge to mark the |
| * file as conflicting, corrupting the model with its markers, but the logical merge should pass without |
| * issues. |
| * </p> |
| * |
| * @throws Exception |
| */ |
| private void setUp002() throws Exception { |
| // We don't need the second resource here |
| file2.delete(); |
| resource2 = null; |
| |
| final EPackage root = createPackage(null, "P1"); |
| EClass class1 = createClass(root, "C1"); |
| createClass(root, "C2"); |
| resource1.getContents().add(root); |
| save(resource1); |
| |
| repository.addAllAndCommit("initial-commit"); |
| |
| // Branch, but stay on master |
| repository.createBranch(MASTER, BRANCH); |
| |
| class1.setName("C10"); |
| save(resource1); |
| repository.addAndCommit(project, "master-commit", file1); |
| |
| repository.checkoutBranch(BRANCH); |
| reload(resource1); |
| |
| class1 = (EClass)findObject(resource1, "C1"); |
| final EClass class2 = (EClass)findObject(resource1, "C2"); |
| |
| class1.getESuperTypes().add(class2); |
| save(resource1); |
| repository.addAndCommit(project, "branch-commit", file1); |
| |
| repository.checkoutBranch(MASTER); |
| |
| final Status status = repository.status(); |
| assertFalse(status.hasUncommittedChanges()); |
| |
| // One diff on each side, no conflicts |
| compareBothDirectionsAndCheck(iFile1, MASTER, BRANCH, 0, 1, 1); |
| } |
| |
| /** |
| * Sets up the repository so that there are three commits affecting a single file. |
| * <ul> |
| * <li>initial commit : A single file "file1" contains a package P1 which contains the classes C1 and C2. |
| * </li> |
| * <li>master commit : delete C2</li> |
| * <li>branch commit : delete C2</li> |
| * </ul> |
| * <p> |
| * This is a pseudo conflict at both logical and textual level, which can thus be automatically merged in |
| * both cases. |
| * </p> |
| * |
| * @throws Exception |
| */ |
| private void setUp003() throws Exception { |
| // We don't need the second resource here |
| file2.delete(); |
| resource2 = null; |
| |
| final EPackage root = createPackage(null, "P1"); |
| createClass(root, "C1"); |
| EClass class2 = createClass(root, "C2"); |
| resource1.getContents().add(root); |
| save(resource1); |
| |
| repository.addAllAndCommit("initial-commit"); |
| |
| // Branch, but stay on master |
| repository.createBranch(MASTER, BRANCH); |
| |
| root.getEClassifiers().remove(class2); |
| save(resource1); |
| repository.addAndCommit(project, "master-commit", file1); |
| |
| repository.checkoutBranch(BRANCH); |
| reload(resource1); |
| |
| class2 = (EClass)findObject(resource1, "C2"); |
| class2.getEPackage().getEClassifiers().remove(class2); |
| save(resource1); |
| repository.addAndCommit(project, "branch-commit", file1); |
| |
| repository.checkoutBranch(MASTER); |
| |
| final Status status = repository.status(); |
| assertFalse(status.hasUncommittedChanges()); |
| |
| // One diff on each side, one pseudo conflict |
| compareBothDirectionsAndCheck(iFile1, MASTER, BRANCH, 1, 1, 1); |
| } |
| |
| /** |
| * Sets up the repository so that there are three commits affecting two files with no links between them |
| * (not part of a single logical model). |
| * <ul> |
| * <li>initial commit : "file1" contains package P1 and classes C1 and C2. "file2" contains P2 containing |
| * C3 and C4.</li> |
| * <li>master commit : delete C2</li> |
| * <li>branch commit : make C2 a super-class of C1, delete C4</li> |
| * </ul> |
| * <p> |
| * This constitutes a conflict in file1, whereas the merge of file2 should end successfully. |
| * </p> |
| * |
| * @throws Exception |
| */ |
| private void setUp004() throws Exception { |
| final EPackage root1 = createPackage(null, "P1"); |
| createClass(root1, "C1"); |
| EClass class2 = createClass(root1, "C2"); |
| resource1.getContents().add(root1); |
| |
| final EPackage root2 = createPackage(null, "P2"); |
| createClass(root2, "C3"); |
| createClass(root2, "C4"); |
| resource2.getContents().add(root2); |
| |
| save(resource1, resource2); |
| repository.addAllAndCommit("initial-commit"); |
| |
| // Branch, but stay on master |
| repository.createBranch(MASTER, BRANCH); |
| |
| root1.getEClassifiers().remove(class2); |
| save(resource1); |
| repository.addAndCommit(project, "master-commit", file1); |
| |
| repository.checkoutBranch(BRANCH); |
| reload(resource1, resource2); |
| |
| final EClass class1 = (EClass)findObject(resource1, "C1"); |
| class2 = (EClass)findObject(resource1, "C2"); |
| final EClass class4 = (EClass)findObject(resource2, "C4"); |
| class1.getESuperTypes().add(class2); |
| class4.getEPackage().getEClassifiers().remove(class4); |
| |
| save(resource1, resource2); |
| repository.addAndCommit(project, "branch-commit", file1, file2); |
| |
| repository.checkoutBranch(MASTER); |
| |
| final Status status = repository.status(); |
| assertFalse(status.hasUncommittedChanges()); |
| |
| // One diff on each side, one conflict |
| compareBothDirectionsAndCheck(iFile1, MASTER, BRANCH, 1, 1, 1); |
| // one diff from the right |
| compareBothDirectionsAndCheck(iFile2, MASTER, BRANCH, 0, 0, 1); |
| } |
| |
| /** |
| * Sets up the repository so that there are three commits affecting two files with no links between them |
| * (not part of a single logical model). |
| * <ul> |
| * <li>initial commit : "file1" contains package P1 and classes C1 and C2. "file2" contains P2 containing |
| * C3 and C4.</li> |
| * <li>master commit : delete C2, delete C4</li> |
| * <li>branch commit : make C2 a super-class of C1, make C4 a super-class of C3</li> |
| * </ul> |
| * <p> |
| * This constitutes a conflict in both files for both kind of merge (textual or logical). |
| * </p> |
| * |
| * @throws Exception |
| */ |
| private void setUp005() throws Exception { |
| final EPackage root1 = createPackage(null, "P1"); |
| createClass(root1, "C1"); |
| EClass class2 = createClass(root1, "C2"); |
| resource1.getContents().add(root1); |
| |
| final EPackage root2 = createPackage(null, "P2"); |
| createClass(root2, "C3"); |
| EClass class4 = createClass(root2, "C4"); |
| resource2.getContents().add(root2); |
| |
| save(resource1, resource2); |
| repository.addAllAndCommit("initial-commit"); |
| |
| // Branch, but stay on master |
| repository.createBranch(MASTER, BRANCH); |
| |
| root1.getEClassifiers().remove(class2); |
| root2.getEClassifiers().remove(class4); |
| save(resource1, resource2); |
| repository.addAndCommit(project, "master-commit", file1, file2); |
| |
| repository.checkoutBranch(BRANCH); |
| reload(resource1, resource2); |
| |
| final EClass class1 = (EClass)findObject(resource1, "C1"); |
| class2 = (EClass)findObject(resource1, "C2"); |
| final EClass class3 = (EClass)findObject(resource2, "C3"); |
| class4 = (EClass)findObject(resource2, "C4"); |
| class1.getESuperTypes().add(class2); |
| class3.getESuperTypes().add(class4); |
| |
| save(resource1, resource2); |
| repository.addAndCommit(project, "branch-commit", file1, file2); |
| |
| repository.checkoutBranch(MASTER); |
| |
| final Status status = repository.status(); |
| assertFalse(status.hasUncommittedChanges()); |
| |
| // One diff on each side, one conflict |
| compareBothDirectionsAndCheck(iFile1, MASTER, BRANCH, 1, 1, 1); |
| // likewise |
| compareBothDirectionsAndCheck(iFile2, MASTER, BRANCH, 1, 1, 1); |
| } |
| |
| /** |
| * Sets up the repository so that there are three commits affecting two files with no links between them |
| * (not part of a single logical model). |
| * <ul> |
| * <li>initial commit : "file1" contains package P1 and classes C1 and C2. "file2" contains P2 containing |
| * C3 and C4.</li> |
| * <li>master commit : renamed C1 into C10, rename C3 into C30</li> |
| * <li>branch commit : make C2 a super-class of C1, make C4 a super-class of C3</li> |
| * </ul> |
| * <p> |
| * This is a textual conflict in both files, but there are no logical conflicts. Textual merge will |
| * corrupt both models on top of being unable to finish the merge, logical merge will end successfully. |
| * </p> |
| * |
| * @throws Exception |
| */ |
| private void setUp006() throws Exception { |
| final EPackage root1 = createPackage(null, "P1"); |
| EClass class1 = createClass(root1, "C1"); |
| createClass(root1, "C2"); |
| resource1.getContents().add(root1); |
| |
| final EPackage root2 = createPackage(null, "P2"); |
| EClass class3 = createClass(root2, "C3"); |
| createClass(root2, "C4"); |
| resource2.getContents().add(root2); |
| |
| save(resource1, resource2); |
| repository.addAllAndCommit("initial-commit"); |
| |
| // Branch, but stay on master |
| repository.createBranch(MASTER, BRANCH); |
| |
| class1.setName("C10"); |
| class3.setName("C30"); |
| save(resource1, resource2); |
| repository.addAndCommit(project, "master-commit", file1, file2); |
| |
| repository.checkoutBranch(BRANCH); |
| reload(resource1, resource2); |
| |
| class1 = (EClass)findObject(resource1, "C1"); |
| final EClass class2 = (EClass)findObject(resource1, "C2"); |
| class3 = (EClass)findObject(resource2, "C3"); |
| final EClass class4 = (EClass)findObject(resource2, "C4"); |
| class1.getESuperTypes().add(class2); |
| class3.getESuperTypes().add(class4); |
| |
| save(resource1, resource2); |
| repository.addAndCommit(project, "branch-commit", file1, file2); |
| |
| repository.checkoutBranch(MASTER); |
| |
| final Status status = repository.status(); |
| assertFalse(status.hasUncommittedChanges()); |
| |
| // One diff on each side, no conflict |
| compareBothDirectionsAndCheck(iFile1, MASTER, BRANCH, 0, 1, 1); |
| // likewise |
| compareBothDirectionsAndCheck(iFile2, MASTER, BRANCH, 0, 1, 1); |
| } |
| |
| /** |
| * Sets up the repository so that there are three commits affecting two interlinked files (part of the |
| * same logical model). The files won't be part of the same logical model initially, the link will be |
| * created in one of the commits we'll merge. |
| * <ul> |
| * <li>initial commit : "file1" contains package P1 and classes C1 and C2. "file2" contains P2 containing |
| * C3 and C4.</li> |
| * <li>master commit : delete C3</li> |
| * <li>branch commit : make C3 a super-class of C1</li> |
| * </ul> |
| * <p> |
| * There is no textual conflict in these changes, so a textual merge will end successfully, corrupting the |
| * model by breaking the links between them. However, logical merging will see the conflict and should |
| * mark both files as conflicting, without touching either of them. |
| * </p> |
| * <p> |
| * This case also presents the particularity of having a "single file" locally (file1 is not linked to |
| * file2) whereas the model is comprised of two files in the right. This will allow us to make sure that |
| * we are able to resolve the logical model in such cases. |
| * </p> |
| * |
| * @throws Exception |
| */ |
| private void setUp007() throws Exception { |
| final EPackage root1 = createPackage(null, "P1"); |
| createClass(root1, "C1"); |
| createClass(root1, "C2"); |
| resource1.getContents().add(root1); |
| |
| final EPackage root2 = createPackage(null, "P2"); |
| EClass class3 = createClass(root2, "C3"); |
| createClass(root2, "C4"); |
| resource2.getContents().add(root2); |
| |
| save(resource1, resource2); |
| repository.addAllAndCommit("initial-commit"); |
| |
| // Branch, but stay on master |
| repository.createBranch(MASTER, BRANCH); |
| |
| root2.getEClassifiers().remove(class3); |
| save(resource2); |
| repository.addAndCommit(project, "master-commit", file2); |
| |
| repository.checkoutBranch(BRANCH); |
| reload(resource1, resource2); |
| |
| final EClass class1 = (EClass)findObject(resource1, "C1"); |
| class3 = (EClass)findObject(resource2, "C3"); |
| class1.getESuperTypes().add(class3); |
| |
| save(resource1); |
| repository.addAndCommit(project, "branch-commit", file1); |
| |
| repository.checkoutBranch(MASTER); |
| |
| final Status status = repository.status(); |
| assertFalse(status.hasUncommittedChanges()); |
| |
| /* |
| * The files are unrelated locally, but they are part of the same logical model on the remote. We |
| * expect EMF Compare to detect that when starting from file1 (as the remote file1 references the |
| * remote file2), but not when starting from file2 (since in such a case, we have no way to discover |
| * the cross reference). |
| */ |
| compareBothDirectionsAndCheck(iFile1, MASTER, BRANCH, 1, 1, 1); |
| compareADirectionAndCheck(iFile2, MASTER, BRANCH, 0, 1, 0); |
| compareADirectionAndCheck(iFile2, BRANCH, MASTER, 1, 1, 1); |
| repository.checkoutBranch(MASTER); |
| } |
| |
| /** |
| * See {@link #setUp007()} for the description of this use case, except that we reverse the changes made |
| * to file1 and file2 so that the "leaf" of the logical model is found before its root in the tree, making |
| * sure that this case will not fail to merge properly. |
| * |
| * @see #setUp007() |
| * @throws Exception |
| */ |
| private void setUp007_reverse() throws Exception { |
| final EPackage root1 = createPackage(null, "P1"); |
| EClass class1 = createClass(root1, "C1"); |
| createClass(root1, "C2"); |
| resource1.getContents().add(root1); |
| |
| final EPackage root2 = createPackage(null, "P2"); |
| createClass(root2, "C3"); |
| createClass(root2, "C4"); |
| resource2.getContents().add(root2); |
| |
| save(resource1, resource2); |
| repository.addAllAndCommit("initial-commit"); |
| |
| // Branch, but stay on master |
| repository.createBranch(MASTER, BRANCH); |
| |
| root1.getEClassifiers().remove(class1); |
| save(resource1); |
| repository.addAndCommit(project, "master-commit", file1); |
| |
| repository.checkoutBranch(BRANCH); |
| reload(resource1, resource2); |
| |
| final EClass class3 = (EClass)findObject(resource2, "C3"); |
| class1 = (EClass)findObject(resource1, "C1"); |
| class3.getESuperTypes().add(class1); |
| |
| save(resource2); |
| repository.addAndCommit(project, "branch-commit", file2); |
| |
| repository.checkoutBranch(MASTER); |
| |
| final Status status = repository.status(); |
| assertFalse(status.hasUncommittedChanges()); |
| |
| /* |
| * The files are unrelated locally, but they are part of the same logical model on the remote. We |
| * expect EMF Compare to detect that when starting from file2 (as the remote file2 references the |
| * remote file1), but not when starting from file1 (since in such a case, we have no way to discover |
| * the cross reference). |
| */ |
| compareADirectionAndCheck(iFile1, MASTER, BRANCH, 0, 1, 0); |
| compareADirectionAndCheck(iFile1, BRANCH, MASTER, 1, 1, 1); |
| compareBothDirectionsAndCheck(iFile2, MASTER, BRANCH, 1, 1, 1); |
| } |
| |
| /** |
| * Sets up the repository so that there are three commits affecting two interlinked files (part of the |
| * same logical model). The files will be linked together as a single logical model in all three sides of |
| * the comparison. |
| * <ul> |
| * <li>initial commit : "file1" contains package P1 and classes C1 and C2. "file2" contains P2 containing |
| * C3 and C4. C3 is a super-type of C1, linking the two files together.</li> |
| * <li>master commit : renamed C1 into C10</li> |
| * <li>branch commit : replace C3 by C4 as a super-class of C1, rename C4 into C40</li> |
| * </ul> |
| * <p> |
| * There is a textual conflict in the first file, none in the second. We expect the textual merge to |
| * corrupt the model with its textual conflict markers. There are no logical conflicts, so logical merging |
| * should end successfully. |
| * </p> |
| * |
| * @throws Exception |
| */ |
| private void setUp008() throws Exception { |
| final EPackage root1 = createPackage(null, "P1"); |
| EClass class1 = createClass(root1, "C1"); |
| createClass(root1, "C2"); |
| resource1.getContents().add(root1); |
| |
| final EPackage root2 = createPackage(null, "P2"); |
| EClass class3 = createClass(root2, "C3"); |
| createClass(root2, "C4"); |
| resource2.getContents().add(root2); |
| |
| class1.getESuperTypes().add(class3); |
| |
| save(resource1, resource2); |
| repository.addAllAndCommit("initial-commit"); |
| |
| // Branch, but stay on master |
| repository.createBranch(MASTER, BRANCH); |
| |
| class1.setName("C10"); |
| save(resource1); |
| repository.addAndCommit(project, "master-commit", file1); |
| |
| repository.checkoutBranch(BRANCH); |
| reload(resource1, resource2); |
| |
| class1 = (EClass)findObject(resource1, "C1"); |
| final EClass class4 = (EClass)findObject(resource2, "C4"); |
| class1.getESuperTypes().clear(); |
| class1.getESuperTypes().add(class4); |
| class4.setName("C40"); |
| |
| save(resource1, resource2); |
| repository.addAndCommit(project, "branch-commit", file1, file2); |
| |
| repository.checkoutBranch(MASTER); |
| |
| final Status status = repository.status(); |
| assertFalse(status.hasUncommittedChanges()); |
| |
| compareBothDirectionsAndCheck(iFile1, MASTER, BRANCH, 0, 1, 3); |
| compareBothDirectionsAndCheck(iFile2, MASTER, BRANCH, 0, 1, 3); |
| } |
| |
| /** |
| * Sets up the repository so that there are three commits affecting two interlinked files (part of the |
| * same logical model). |
| * <ul> |
| * <li>initial commit : "file1" contains package P1 and classes C1 and C2. "file2" contains P2 containing |
| * C3 and C4.</li> |
| * <li>master commit : renamed C1 into C10</li> |
| * <li>branch commit : renamed C4 into C40</li> |
| * </ul> |
| * <p> |
| * There is no conflict with these changes, neither textual nor logical. |
| * </p> |
| * |
| * @throws Exception |
| */ |
| private void setUp009() throws Exception { |
| final EPackage root1 = createPackage(null, "P1"); |
| final EClass class1 = createClass(root1, "C1"); |
| createClass(root1, "C2"); |
| resource1.getContents().add(root1); |
| |
| final EPackage root2 = createPackage(null, "P2"); |
| final EClass class3 = createClass(root2, "C3"); |
| createClass(root2, "C4"); |
| resource2.getContents().add(root2); |
| |
| class1.getESuperTypes().add(class3); |
| |
| save(resource1, resource2); |
| repository.addAllAndCommit("initial-commit"); |
| |
| // Branch, but stay on master |
| repository.createBranch(MASTER, BRANCH); |
| |
| class1.setName("C10"); |
| save(resource1); |
| repository.addAndCommit(project, "master-commit", file1); |
| |
| repository.checkoutBranch(BRANCH); |
| reload(resource1, resource2); |
| |
| final EClass class4 = (EClass)findObject(resource2, "C4"); |
| class4.setName("C40"); |
| |
| save(resource2); |
| repository.addAndCommit(project, "branch-commit", file2); |
| |
| repository.checkoutBranch(MASTER); |
| |
| final Status status = repository.status(); |
| assertFalse(status.hasUncommittedChanges()); |
| |
| compareBothDirectionsAndCheck(iFile1, MASTER, BRANCH, 0, 1, 1); |
| compareBothDirectionsAndCheck(iFile2, MASTER, BRANCH, 0, 1, 1); |
| |
| } |
| |
| /* |
| * Try and merge textually. This has no relation with EMF Compare, but it does check that : A - the models |
| * were properly created by setUp001, and B - the textual merge ends up corrupting the models. |
| */ |
| @Test |
| public void merge001_text() throws Exception { |
| setUp001(); |
| |
| repository.mergeTextual(BRANCH); |
| |
| final Status status = repository.status(); |
| assertTrue(status.hasUncommittedChanges()); |
| assertTrue(status.getConflicting().contains(repository.getRepoRelativePath(file1))); |
| |
| try { |
| reload(resource1); |
| fail(); |
| } catch (IOException e) { |
| // expected behavior : the file has been corrupted by the textual merge |
| } |
| } |
| |
| /* |
| * Try and merge logically. There is a conflict, but contrarily to merge001_textual, we expect the model |
| * not to be corrupted by this attempt. |
| */ |
| @Test |
| public void merge001_logical() throws Exception { |
| setUp001(); |
| |
| repository.mergeLogical(BRANCH); |
| |
| final Status status = repository.status(); |
| assertTrue(status.hasUncommittedChanges()); |
| assertTrue(status.getConflicting().contains(repository.getRepoRelativePath(file1))); |
| |
| // Make sure we're still in the configuration we had on master |
| reload(resource1); |
| assertTrue(resource1.getErrors().isEmpty()); |
| assertNotNull(findObject(resource1, "C1")); |
| assertNull(findObject(resource1, "C2")); |
| } |
| |
| /* |
| * Try and merge textually. This has no relation with EMF Compare, but it does check that : A - the models |
| * were properly created by setUp002, and B - the textual merge ends up corrupting the model with a false |
| * conflict. |
| */ |
| @Test |
| public void merge002_text() throws Exception { |
| setUp002(); |
| |
| repository.mergeTextual(BRANCH); |
| |
| final Status status = repository.status(); |
| assertTrue(status.hasUncommittedChanges()); |
| assertTrue(status.getConflicting().contains(repository.getRepoRelativePath(file1))); |
| |
| try { |
| reload(resource1); |
| fail(); |
| } catch (IOException e) { |
| // expected behavior : the file has been corrupted by the textual merge |
| } |
| } |
| |
| /* |
| * Try and merge logically. The merge should end without issues, without being affected by the textual |
| * conflict. |
| */ |
| @Test |
| public void merge002_logical() throws Exception { |
| setUp002(); |
| |
| repository.mergeLogical(BRANCH); |
| |
| final Status status = repository.status(); |
| assertFalse(status.hasUncommittedChanges()); |
| assertTrue(status.getConflicting().isEmpty()); |
| |
| // Make sure that the merge actually changed the model |
| reload(resource1); |
| assertTrue(resource1.getErrors().isEmpty()); |
| final EPackage pack = (EPackage)findObject(resource1, "P1"); |
| final EClass class1 = (EClass)findObject(resource1, "C10"); |
| final EClass class2 = (EClass)findObject(resource1, "C2"); |
| assertNotNull(pack); |
| assertNotNull(class1); |
| assertNotNull(class2); |
| assertTrue(class1.getESuperTypes().contains(class2)); |
| assertEquals(2, pack.getEClassifiers().size()); |
| } |
| |
| /* |
| * Try and merge textually. This has no relation with EMF Compare, but it does check that : A - the models |
| * were properly created by setUp003, and B - the textual merge manages to auto-merge the pseudo-conflict |
| * without corrupting the model. |
| */ |
| @Test |
| public void merge003_text() throws Exception { |
| setUp003(); |
| |
| repository.mergeTextual(BRANCH); |
| |
| final Status status = repository.status(); |
| assertFalse(status.hasUncommittedChanges()); |
| assertTrue(status.getConflicting().isEmpty()); |
| |
| reload(resource1); |
| assertTrue(resource1.getErrors().isEmpty()); |
| final EPackage pack = (EPackage)findObject(resource1, "P1"); |
| final EClass class1 = (EClass)findObject(resource1, "C1"); |
| |
| assertNotNull(pack); |
| assertNotNull(class1); |
| assertEquals(1, pack.getEClassifiers().size()); |
| } |
| |
| /* |
| * Try and merge logically. The merge should end successfully, auto-merging the pseudo-conflict as needed. |
| */ |
| @Test |
| public void merge003_logical() throws Exception { |
| setUp003(); |
| |
| repository.mergeLogical(BRANCH); |
| |
| final Status status = repository.status(); |
| assertFalse(status.hasUncommittedChanges()); |
| assertTrue(status.getConflicting().isEmpty()); |
| |
| reload(resource1); |
| assertTrue(resource1.getErrors().isEmpty()); |
| final EPackage pack = (EPackage)findObject(resource1, "P1"); |
| final EClass class1 = (EClass)findObject(resource1, "C1"); |
| |
| assertNotNull(pack); |
| assertNotNull(class1); |
| assertEquals(1, pack.getEClassifiers().size()); |
| } |
| |
| /* |
| * Try and merge textually. This has no relation with EMF Compare, but it does check that : A - the models |
| * were properly created by setUp004, and B - the textual merge corrupts the model in which there was a |
| * conflict. |
| */ |
| @Test |
| public void merge004_text() throws Exception { |
| setUp004(); |
| |
| repository.mergeTextual(BRANCH); |
| |
| final Status status = repository.status(); |
| assertTrue(status.hasUncommittedChanges()); |
| assertTrue(status.getConflicting().contains(repository.getRepoRelativePath(file1))); |
| assertFalse(status.getConflicting().contains(repository.getRepoRelativePath(file2))); |
| |
| unload(resource1, resource2); |
| |
| try { |
| reload(resource1); |
| fail(); |
| } catch (IOException e) { |
| // expected behavior : the file has been corrupted by the textual merge |
| } |
| |
| reload(resource2); |
| assertTrue(resource2.getErrors().isEmpty()); |
| final EPackage pack = (EPackage)findObject(resource2, "P2"); |
| final EClass class3 = (EClass)findObject(resource2, "C3"); |
| |
| assertNotNull(pack); |
| assertNotNull(class3); |
| assertEquals(1, pack.getEClassifiers().size()); |
| } |
| |
| /* |
| * Try and merge logically. The merge should leave file1 as conflicting, leaving it untouched as compared |
| * to master. file2 should be properly merged. |
| */ |
| @Test |
| public void merge004_logical() throws Exception { |
| setUp004(); |
| |
| repository.mergeLogical(BRANCH); |
| |
| final Status status = repository.status(); |
| assertTrue(status.hasUncommittedChanges()); |
| assertTrue(status.getConflicting().contains(repository.getRepoRelativePath(file1))); |
| assertFalse(status.getConflicting().contains(repository.getRepoRelativePath(file2))); |
| |
| unload(resource1, resource2); |
| |
| reload(resource1); |
| assertTrue(resource1.getErrors().isEmpty()); |
| assertNotNull(findObject(resource1, "C1")); |
| assertNull(findObject(resource1, "C2")); |
| |
| reload(resource2); |
| assertTrue(resource2.getErrors().isEmpty()); |
| final EPackage pack = (EPackage)findObject(resource2, "P2"); |
| final EClass class3 = (EClass)findObject(resource2, "C3"); |
| |
| assertNotNull(pack); |
| assertNotNull(class3); |
| assertEquals(1, pack.getEClassifiers().size()); |
| } |
| |
| /* |
| * Try and merge textually. This has no relation with EMF Compare, but it does check that : A - the models |
| * were properly created by setUp005, and B - the textual merge corrupts the two models. |
| */ |
| @Test |
| public void merge005_text() throws Exception { |
| setUp005(); |
| |
| repository.mergeTextual(BRANCH); |
| |
| final Status status = repository.status(); |
| assertTrue(status.hasUncommittedChanges()); |
| assertTrue(status.getConflicting().contains(repository.getRepoRelativePath(file1))); |
| assertTrue(status.getConflicting().contains(repository.getRepoRelativePath(file2))); |
| |
| unload(resource1, resource2); |
| |
| try { |
| reload(resource1); |
| fail(); |
| } catch (IOException e) { |
| // expected behavior : the file has been corrupted by the textual merge |
| } |
| |
| try { |
| reload(resource2); |
| fail(); |
| } catch (IOException e) { |
| // expected behavior : the file has been corrupted by the textual merge |
| } |
| } |
| |
| /* |
| * Try and merge logically. both files should be marked as conflicting, untouched as compared to master. |
| */ |
| @Test |
| public void merge005_logical() throws Exception { |
| setUp005(); |
| |
| repository.mergeLogical(BRANCH); |
| |
| final Status status = repository.status(); |
| assertTrue(status.hasUncommittedChanges()); |
| assertTrue(status.getConflicting().contains(repository.getRepoRelativePath(file1))); |
| assertTrue(status.getConflicting().contains(repository.getRepoRelativePath(file2))); |
| |
| unload(resource1, resource2); |
| |
| reload(resource1); |
| assertTrue(resource1.getErrors().isEmpty()); |
| assertNotNull(findObject(resource1, "C1")); |
| assertNull(findObject(resource1, "C2")); |
| |
| reload(resource2); |
| assertTrue(resource2.getErrors().isEmpty()); |
| assertNotNull(findObject(resource2, "C3")); |
| assertNull(findObject(resource2, "C4")); |
| } |
| |
| /* |
| * Try and merge textually. This has no relation with EMF Compare, but it does check that : A - the models |
| * were properly created by setUp006, and B - the textual merge ends up corrupting the models with false |
| * conflicts. |
| */ |
| @Test |
| public void merge006_text() throws Exception { |
| setUp006(); |
| |
| repository.mergeTextual(BRANCH); |
| |
| final Status status = repository.status(); |
| assertTrue(status.hasUncommittedChanges()); |
| assertTrue(status.getConflicting().contains(repository.getRepoRelativePath(file1))); |
| assertTrue(status.getConflicting().contains(repository.getRepoRelativePath(file2))); |
| |
| unload(resource1, resource2); |
| |
| try { |
| reload(resource1); |
| fail(); |
| } catch (IOException e) { |
| // expected behavior : the file has been corrupted by the textual merge |
| } |
| |
| try { |
| reload(resource2); |
| fail(); |
| } catch (IOException e) { |
| // expected behavior : the file has been corrupted by the textual merge |
| } |
| } |
| |
| /* |
| * Try and merge logically. The merge should end without issues, without being affected by the textual |
| * conflicts. |
| */ |
| @Test |
| public void merge006_logical() throws Exception { |
| setUp006(); |
| |
| repository.mergeLogical(BRANCH); |
| |
| final Status status = repository.status(); |
| assertFalse(status.hasUncommittedChanges()); |
| assertTrue(status.getConflicting().isEmpty()); |
| |
| // Make sure that the merge actually changed the models |
| reload(resource1, resource2); |
| |
| assertTrue(resource1.getErrors().isEmpty()); |
| final EPackage pack1 = (EPackage)findObject(resource1, "P1"); |
| final EClass class1 = (EClass)findObject(resource1, "C10"); |
| final EClass class2 = (EClass)findObject(resource1, "C2"); |
| assertNotNull(pack1); |
| assertNotNull(class1); |
| assertNotNull(class2); |
| assertTrue(class1.getESuperTypes().contains(class2)); |
| assertEquals(2, pack1.getEClassifiers().size()); |
| |
| assertTrue(resource2.getErrors().isEmpty()); |
| final EPackage pack2 = (EPackage)findObject(resource2, "P2"); |
| final EClass class3 = (EClass)findObject(resource2, "C30"); |
| final EClass class4 = (EClass)findObject(resource2, "C4"); |
| assertNotNull(pack2); |
| assertNotNull(class3); |
| assertNotNull(class4); |
| assertTrue(class3.getESuperTypes().contains(class4)); |
| assertEquals(2, pack2.getEClassifiers().size()); |
| } |
| |
| /* |
| * Try and merge textually. This has no relation with EMF Compare, but it does check that : A - the models |
| * were properly created by setUp007, and B - the textual merge ends up corrupting the logical model even |
| * though it says the merge ends successfully. |
| */ |
| @Test |
| public void merge007_text() throws Exception { |
| setUp007(); |
| |
| repository.mergeTextual(BRANCH); |
| |
| final Status status = repository.status(); |
| assertFalse(status.hasUncommittedChanges()); |
| assertTrue(status.getConflicting().isEmpty()); |
| |
| unload(resource1, resource2); |
| |
| // This one has a broken reference towards C3 |
| reload(resource1); |
| assertTrue(resource1.getErrors().isEmpty()); |
| final EClass class1 = (EClass)findObject(resource1, "C1"); |
| final EClass superType = class1.getESuperTypes().get(0); |
| // the super type (what was "class C3") is a proxy |
| assertTrue(superType.eIsProxy()); |
| // even though its resource has been loaded in the resource set |
| final Resource res2 = resource1.getResourceSet().getResource(resource2.getURI(), false); |
| assertNotNull(res2); |
| // and it is unresolveable |
| assertSame(superType, EcoreUtil.resolve(superType, resource1.getResourceSet())); |
| |
| // But the second should be fine |
| reload(resource2); |
| final EPackage pack2 = (EPackage)findObject(resource2, "P2"); |
| final EClass class4 = (EClass)findObject(resource2, "C4"); |
| assertNotNull(pack2); |
| assertNotNull(class4); |
| assertEquals(1, pack2.getEClassifiers().size()); |
| } |
| |
| /* |
| * Try and merge logically. There is a logical conflict, so we expect both files to remain untouched and |
| * the conflict marked. |
| */ |
| @Test |
| public void merge007_logical() throws Exception { |
| setUp007(); |
| |
| repository.mergeLogical(BRANCH); |
| |
| final Status status = repository.status(); |
| assertTrue(status.hasUncommittedChanges()); |
| assertTrue(status.getConflicting().contains(repository.getRepoRelativePath(file1))); |
| assertTrue(status.getConflicting().contains(repository.getRepoRelativePath(file2))); |
| |
| unload(resource1, resource2); |
| |
| reload(resource1); |
| assertTrue(resource1.getErrors().isEmpty()); |
| final EClass class1 = (EClass)findObject(resource1, "C1"); |
| assertNotNull(class1); |
| assertTrue(class1.getESuperTypes().isEmpty()); |
| assertNotNull(findObject(resource1, "C2")); |
| |
| reload(resource2); |
| assertTrue(resource2.getErrors().isEmpty()); |
| assertNull(findObject(resource2, "C3")); |
| assertNotNull(findObject(resource2, "C4")); |
| } |
| |
| /* This should have the reverse effect as merge007_textual. */ |
| @Test |
| public void merge007_reverse_text() throws Exception { |
| setUp007_reverse(); |
| |
| repository.mergeTextual(BRANCH); |
| |
| final Status status = repository.status(); |
| assertFalse(status.hasUncommittedChanges()); |
| assertTrue(status.getConflicting().isEmpty()); |
| |
| unload(resource1, resource2); |
| |
| // This one should be fine |
| reload(resource1); |
| final EPackage pack1 = (EPackage)findObject(resource1, "P1"); |
| final EClass class2 = (EClass)findObject(resource1, "C2"); |
| assertNotNull(pack1); |
| assertNotNull(class2); |
| assertEquals(1, pack1.getEClassifiers().size()); |
| |
| // But the second one has a broken references towards C1 |
| reload(resource2); |
| assertTrue(resource2.getErrors().isEmpty()); |
| final EClass class3 = (EClass)findObject(resource2, "C3"); |
| final EClass superType = class3.getESuperTypes().get(0); |
| // the super type (what was "class C1") is a proxy |
| assertTrue(superType.eIsProxy()); |
| // even though its resource has been loaded in the resource set |
| final Resource res1 = resource2.getResourceSet().getResource(resource1.getURI(), false); |
| assertNotNull(res1); |
| // and it is unresolveable |
| assertSame(superType, EcoreUtil.resolve(superType, resource2.getResourceSet())); |
| } |
| |
| /* This should have the exact same effect as merge007_logical. */ |
| @Test |
| public void merge007_reverse_logical() throws Exception { |
| setUp007_reverse(); |
| |
| repository.mergeLogical(BRANCH); |
| |
| final Status status = repository.status(); |
| assertTrue(status.hasUncommittedChanges()); |
| assertTrue(status.getConflicting().contains(repository.getRepoRelativePath(file1))); |
| assertTrue(status.getConflicting().contains(repository.getRepoRelativePath(file2))); |
| |
| unload(resource1, resource2); |
| |
| reload(resource1); |
| assertTrue(resource1.getErrors().isEmpty()); |
| assertNull(findObject(resource1, "C1")); |
| assertNotNull(findObject(resource1, "C2")); |
| |
| reload(resource2); |
| assertTrue(resource2.getErrors().isEmpty()); |
| final EClass class3 = (EClass)findObject(resource2, "C3"); |
| assertNotNull(class3); |
| assertTrue(class3.getESuperTypes().isEmpty()); |
| assertNotNull(findObject(resource2, "C4")); |
| } |
| |
| /* |
| * Try and merge textually. This has no relation with EMF Compare, but it does check that : A - the models |
| * were properly created by setUp008, and B - the textual merge ends up corrupting the logical model even |
| * though it says the merge ends successfully. |
| */ |
| @Test |
| public void merge008_text() throws Exception { |
| setUp008(); |
| |
| repository.mergeTextual(BRANCH); |
| |
| final Status status = repository.status(); |
| assertTrue(status.hasUncommittedChanges()); |
| assertTrue(status.getConflicting().contains(repository.getRepoRelativePath(file1))); |
| |
| unload(resource1, resource2); |
| |
| try { |
| reload(resource1); |
| fail(); |
| } catch (IOException e) { |
| // expected behavior : the file has been corrupted by the textual merge |
| } |
| |
| // This file has been merged, so we should find C40 in it. |
| reload(resource2); |
| assertTrue(resource2.getErrors().isEmpty()); |
| final EPackage pack2 = (EPackage)findObject(resource2, "P2"); |
| assertNotNull(findObject(resource2, "C3")); |
| assertNotNull(findObject(resource2, "C40")); |
| assertEquals(2, pack2.getEClassifiers().size()); |
| } |
| |
| /* |
| * Try and merge logically. There is no logical conflict, so the merge should end successfully. |
| */ |
| @Test |
| public void merge008_logical() throws Exception { |
| setUp008(); |
| |
| repository.mergeLogical(BRANCH); |
| |
| final Status status = repository.status(); |
| assertFalse(status.hasUncommittedChanges()); |
| assertTrue(status.getConflicting().isEmpty()); |
| |
| reload(resource1, resource2); |
| assertTrue(resource1.getErrors().isEmpty()); |
| final EClass class1 = (EClass)findObject(resource1, "C10"); |
| assertNotNull(class1); |
| assertNotNull(findObject(resource1, "C2")); |
| |
| assertTrue(resource2.getErrors().isEmpty()); |
| final EClass class4 = (EClass)findObject(resource2, "C40"); |
| assertNotNull(findObject(resource2, "C3")); |
| assertNotNull(class4); |
| |
| assertTrue(class1.getESuperTypes().contains(class4)); |
| assertEquals(1, class1.getESuperTypes().size()); |
| } |
| |
| /* |
| * Try and merge textually. This has no relation with EMF Compare, but it does check that : A - the models |
| * were properly created by setUp009, and B - the textual merge wouldn't have corrupted the model in this |
| * case. |
| */ |
| @Test |
| public void merge009_text() throws Exception { |
| setUp009(); |
| |
| repository.mergeTextual(BRANCH); |
| |
| final Status status = repository.status(); |
| assertFalse(status.hasUncommittedChanges()); |
| assertTrue(status.getConflicting().isEmpty()); |
| |
| reload(resource1, resource2); |
| |
| assertTrue(resource1.getErrors().isEmpty()); |
| final EPackage pack1 = (EPackage)findObject(resource1, "P1"); |
| final EClass class1 = (EClass)findObject(resource1, "C10"); |
| assertNotNull(class1); |
| assertNotNull(findObject(resource1, "C2")); |
| assertEquals(2, pack1.getEClassifiers().size()); |
| |
| assertTrue(resource2.getErrors().isEmpty()); |
| final EPackage pack2 = (EPackage)findObject(resource2, "P2"); |
| final EClass class3 = (EClass)findObject(resource2, "C3"); |
| assertNotNull(class3); |
| assertNotNull(findObject(resource2, "C40")); |
| assertEquals(2, pack2.getEClassifiers().size()); |
| |
| assertTrue(class1.getESuperTypes().contains(class3)); |
| assertEquals(1, class1.getESuperTypes().size()); |
| } |
| |
| /* |
| * Try and merge logically. There is no logical conflict, so the merge should end successfully. |
| */ |
| @Test |
| public void merge009_logical() throws Exception { |
| setUp009(); |
| |
| repository.mergeLogical(BRANCH); |
| |
| final Status status = repository.status(); |
| assertFalse(status.hasUncommittedChanges()); |
| assertTrue(status.getConflicting().isEmpty()); |
| |
| reload(resource1, resource2); |
| |
| assertTrue(resource1.getErrors().isEmpty()); |
| final EPackage pack1 = (EPackage)findObject(resource1, "P1"); |
| final EClass class1 = (EClass)findObject(resource1, "C10"); |
| assertNotNull(class1); |
| assertNotNull(findObject(resource1, "C2")); |
| assertEquals(2, pack1.getEClassifiers().size()); |
| |
| assertTrue(resource2.getErrors().isEmpty()); |
| final EPackage pack2 = (EPackage)findObject(resource2, "P2"); |
| final EClass class3 = (EClass)findObject(resource2, "C3"); |
| assertNotNull(class3); |
| assertNotNull(findObject(resource2, "C40")); |
| assertEquals(2, pack2.getEClassifiers().size()); |
| |
| assertTrue(class1.getESuperTypes().contains(class3)); |
| assertEquals(1, class1.getESuperTypes().size()); |
| } |
| |
| private void compareADirectionAndCheck(IFile file, String source, String destination, |
| int expectedConflicts, int diffsInSource, int diffsInDestination) throws Exception { |
| repository.checkoutBranch(source); |
| Comparison compareResult = compare(source, destination, file); |
| |
| assertEquals(expectedConflicts, compareResult.getConflicts().size()); |
| assertDiffCount(compareResult.getDifferences(), diffsInSource, diffsInDestination); |
| } |
| } |