org.eclipse.emf.compare.ide.ui.tests.merge.StrategyRecursiveModelTest.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.emf.compare.ide.ui.tests.merge.StrategyRecursiveModelTest.java

Source

/*******************************************************************************
 * Copyright (C) 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
 *******************************************************************************/
package org.eclipse.emf.compare.ide.ui.tests.merge;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.egit.core.Activator;
import org.eclipse.egit.core.GitCorePreferences;
import org.eclipse.egit.core.project.RepositoryMapping;
import org.eclipse.emf.compare.ide.ui.tests.models.ModelTestCase;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.IndexDiff.StageState;
import org.eclipse.jgit.lib.Repository;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 * This test requires two extensions which are registered in fragment.xml:
 * <ol>
 * <li><code>org.eclipse.core.resources.modelProviders</code>: SampleModelProvider provides logical models for
 * <code>*.sample</code> files and the</li>
 * <li><code>org.eclipse.core.runtime.adapters</code>: SampleModelProvider adapts to IResourceMappingMerger</li>
 * </ol>
 * 
 * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
 * @author <a href="mailto:laurent.delaigue@obeo.fr">Laurent Delaigue</a>
 */
@SuppressWarnings("restriction")
// @RunWith(EMFCompareGitTestRunner.class)
public class StrategyRecursiveModelTest extends ModelTestCase {
    protected static final String MASTER = Constants.R_HEADS + Constants.MASTER;

    protected static final String BRANCH = Constants.R_HEADS + "branch"; //$NON-NLS-1$

    protected static final String INITIAL_CONTENT_FILE1 = "some content for the first file"; //$NON-NLS-1$

    protected static final String INITIAL_CONTENT_FILE2 = "some content for the second file"; //$NON-NLS-1$

    // The Team merger uses this when merging, regardless of what exists in the
    // file
    protected static final String SYSTEM_EOL = System.getProperty("line.separator"); //$NON-NLS-1$

    protected static final String BRANCH_CHANGE = "branch changes" + SYSTEM_EOL; //$NON-NLS-1$

    protected static final String MASTER_CHANGE = "master changes" + SYSTEM_EOL; //$NON-NLS-1$

    protected Repository repo;

    protected IProject iProject;

    @Override
    @Before
    public void setUp() throws Exception {
        super.setUp();

        iProject = project.getProject();
        repo = RepositoryMapping.getMapping(iProject).getRepository();

        // make initial commit
        Git git = new Git(repo);
        try {
            git.commit().setAuthor("JUnit", "junit@jgit.org").setMessage("Initial commit").call(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
        } finally {
            git.close();
        }

        // Select strategy recursive as preferred strategy
        InstanceScope.INSTANCE.getNode(Activator.getPluginId()).put(GitCorePreferences.core_preferredMergeStrategy,
                "model recursive"); //$NON-NLS-1$
    }

    @After
    public void clearGitResources() throws Exception {
        repository.disconnect(iProject);
        repo = null;

        super.tearDown();
    }

    // /**
    // * This test will initialize a repository with two branches with a few changes each, then try to merge
    // the
    // * branch into master.
    // * <p>
    // * The repository will contain two files, file1.sample and file2.sample, both being in the same
    // container
    // * and thus considered to be components of a single logical model by the SampleModelProvider.
    // * </p>
    // * <p>
    // * file1 will be modified on both master and the branch, these changes being considered as an
    // * unresolveable conflict by git (and JGit), but considered as an auto-mergeable conflict by the
    // * SampleResourceMappingMerger. file2 will only be modified on the branch : it will be deleted.
    // * </p>
    // * <p>
    // * We expect the merge to end successfully, ending with a repository that has no uncommited change.
    // * </p>
    // *
    // * @throws Exception
    // */
    // @Merge(localBranch = "master", remoteBranch = "branch")
    // @Input("data/strategyRecursiveModel/deletedRemoteNoConflict.zip")
    // public void mergeModelWithDeletedRemoteNoConflict(Status status, Repository repository,
    // List<IProject> projects) throws Exception {
    // IProject project = projects.get(0);
    // IFile iFile1 = project.getFile("file1.sample");
    // IFile iFile2 = project.getFile("file2.sample");
    //
    // assertFalse(status.hasUncommittedChanges());
    // assertTrue(status.getConflicting().isEmpty());
    //
    // assertContentEquals(iFile1, BRANCH_CHANGE + INITIAL_CONTENT_FILE1 + MASTER_CHANGE);
    // assertFalse(iFile2.exists());
    // }
    //
    // /**
    // * This test will initialize a repository with two branches with a few changes each, then try to merge
    // the
    // * branch into master.
    // * <p>
    // * The repository will contain two files, file1.sample and file2.sample, both being in the same
    // container
    // * and thus considered to be components of a single logical model by the SampleModelProvider.
    // * </p>
    // * <p>
    // * file1 will be modified on both master and the branch, these changes being considered as an
    // * unresolveable conflict by git (and JGit), but considered as an auto-mergeable conflict by the
    // * SampleResourceMappingMerger. file2 will only be modified on master where it will be deleted.
    // * </p>
    // * <p>
    // * We expect the merge to end successfully, ending with a repository that has no uncommited change.
    // * </p>
    // *
    // * @throws Exception
    // */
    // @Merge(localBranch = "master", remoteBranch = "branch")
    // @Input("data/strategyRecursiveModel/deletedLocalNoConflict.zip")
    // public void mergeModelWithDeletedLocalNoConflict(Status status, Repository repository,
    // List<IProject> projects) throws Exception {
    // IProject project = projects.get(0);
    // IFile iFile1 = project.getFile("file1.sample");
    // IFile iFile2 = project.getFile("file2.sample");
    // assertFalse(status.hasUncommittedChanges());
    // assertTrue(status.getConflicting().isEmpty());
    //
    // assertContentEquals(iFile1, BRANCH_CHANGE + INITIAL_CONTENT_FILE1 + MASTER_CHANGE);
    // assertFalse(iFile2.exists());
    // }

    /**
     * This test will initialize a repository with two branches with a few changes each, then try to merge the
     * branch into master.
     * <p>
     * The repository will contain two files, file1.sample and file2.sample, both being in the same container
     * and thus considered to be components of a single logical model by the SampleModelProvider.
     * </p>
     * <p>
     * file1 will be modified on both master and the branch, these changes being considered as an
     * unresolveable conflict by git (and JGit), but considered as an auto-mergeable conflict by the
     * SampleResourceMappingMerger. file2 will be deleted on both master and the branch. This is a
     * pseudo-conflict and should thus be automatically merged.
     * </p>
     * <p>
     * We expect the merge to end successfully, ending with a repository that has no uncommited change.
     * </p>
     *
     * @throws Exception
     */
    @Test
    public void mergeModelWithPseudoConflictDeletion() throws Exception {
        File file1 = repository.createFile(iProject, "file1." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$
        File file2 = repository.createFile(iProject, "file2." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$

        repository.appendContentAndCommit(iProject, file1, INITIAL_CONTENT_FILE1, "first file - initial commit"); //$NON-NLS-1$
        repository.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_FILE2, "second file - initial commit"); //$NON-NLS-1$

        IFile iFile1 = repository.getIFile(iProject, file1);
        IFile iFile2 = repository.getIFile(iProject, file2);

        repository.createAndCheckoutBranch(MASTER, BRANCH);

        setContentsAndCommit(repository, iFile1, BRANCH_CHANGE + INITIAL_CONTENT_FILE1, "branch commit"); //$NON-NLS-1$
        iFile2.delete(true, new NullProgressMonitor());
        repository.addAndCommit(iProject, "branch commit - deleted file2." + SAMPLE_FILE_EXTENSION, file2); //$NON-NLS-1$

        repository.checkoutBranch(MASTER);

        setContentsAndCommit(repository, iFile1, INITIAL_CONTENT_FILE1 + MASTER_CHANGE, "master commit"); //$NON-NLS-1$
        iFile2.delete(true, new NullProgressMonitor());
        repository.addAndCommit(iProject, "master commit - deleted file2." + SAMPLE_FILE_EXTENSION, file2); //$NON-NLS-1$
        iProject.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
        // end setup

        merge(repo, BRANCH);

        final Status status = status(repo);
        assertFalse(status.hasUncommittedChanges());
        assertTrue(status.getConflicting().isEmpty());

        assertContentEquals(iFile1, BRANCH_CHANGE + INITIAL_CONTENT_FILE1 + MASTER_CHANGE);
        assertFalse(iFile2.exists());
    }

    /**
     * This test will initialize a repository with two branches with a few changes each, then try to merge the
     * branch into master.
     * <p>
     * The repository will contain two files, file1.sample and file2.sample, both being in the same container
     * and thus considered to be components of a single logical model by the SampleModelProvider.
     * </p>
     * <p>
     * file1 will be modified on both master and the branch in such a way that it will be an unresolveable
     * conflict for both JGit and the model merger. file2 will be deleted from the branch.
     * </p>
     * <p>
     * The merge must end in a conflict. The SampleResourceMappingMerger pre-merges what can be, so file2 will
     * be deleted from the working tree while file1 will be left untouched. file2 will be added to the index,
     * but file 1 will be marked as a conflict.
     * </p>
     *
     * @throws Exception
     */
    @Test
    public void mergeModelWithDeletedRemoteModelConflict() throws Exception {
        File file1 = repository.createFile(iProject, "file1." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$
        File file2 = repository.createFile(iProject, "file2." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$

        repository.appendContentAndCommit(iProject, file1, INITIAL_CONTENT_FILE1, "first file - initial commit"); //$NON-NLS-1$
        repository.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_FILE2, "second file - initial commit"); //$NON-NLS-1$

        IFile iFile1 = repository.getIFile(iProject, file1);
        IFile iFile2 = repository.getIFile(iProject, file2);
        String repoRelativePath1 = repository.getRepoRelativePath(iFile1.getLocation().toPortableString());
        String repoRelativePath2 = repository.getRepoRelativePath(iFile2.getLocation().toPortableString());

        repository.createAndCheckoutBranch(MASTER, BRANCH);

        setContentsAndCommit(repository, iFile1, BRANCH_CHANGE + INITIAL_CONTENT_FILE1, "branch commit"); //$NON-NLS-1$
        iFile2.delete(true, new NullProgressMonitor());
        repository.addAndCommit(iProject, "branch commit - deleted file2." + SAMPLE_FILE_EXTENSION, file2); //$NON-NLS-1$

        repository.checkoutBranch(MASTER);

        setContentsAndCommit(repository, iFile1, MASTER_CHANGE + INITIAL_CONTENT_FILE1, "master commit"); //$NON-NLS-1$
        iProject.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
        // end setup

        merge(repo, BRANCH);

        final Status status = status(repo);
        assertTrue(status.hasUncommittedChanges());
        assertFalse(status.getConflicting().isEmpty());
        assertTrue(status.getConflicting().contains(repoRelativePath1));
        assertTrue(status.getRemoved().contains(repoRelativePath2));

        assertContentEquals(iFile1, MASTER_CHANGE + INITIAL_CONTENT_FILE1);
        assertFalse(iFile2.exists());

        Map<String, StageState> map = status.getConflictingStageState();
        assertEquals(StageState.BOTH_MODIFIED, map.get(repoRelativePath1));
    }

    /**
     * This test will initialize a repository with two branches with a few changes each, then try to merge the
     * branch into master.
     * <p>
     * The repository will contain two files, file1.sample and file2.sample, both being in the same container
     * and thus considered to be components of a single logical model by the SampleModelProvider.
     * </p>
     * <p>
     * file1 will be modified on both master and the branch in such a way that it will be an unresolveable
     * conflict for both JGit and the model merger. file2 will be deleted from master.
     * </p>
     * <p>
     * The merge must end in a conflict. Since file2 has been deleted locally and has not been changed on the
     * remote, it will not be seen as a part of the logical model (it won't even be part of the merge
     * operation). Thus, file1 will be marked as a conflict, untouched as compared to its previous (master)
     * state, and file2 will not be part of the index.
     * </p>
     *
     * @throws Exception
     */
    @Test
    public void mergeModelWithDeletedLocalModelConflict() throws Exception {
        File file1 = repository.createFile(iProject, "file1." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$
        File file2 = repository.createFile(iProject, "file2." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$

        repository.appendContentAndCommit(iProject, file1, INITIAL_CONTENT_FILE1, "first file - initial commit"); //$NON-NLS-1$
        repository.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_FILE2, "second file - initial commit"); //$NON-NLS-1$

        IFile iFile1 = repository.getIFile(iProject, file1);
        IFile iFile2 = repository.getIFile(iProject, file2);
        String repoRelativePath1 = repository.getRepoRelativePath(iFile1.getLocation().toPortableString());
        String repoRelativePath2 = repository.getRepoRelativePath(iFile2.getLocation().toPortableString());

        repository.createAndCheckoutBranch(MASTER, BRANCH);

        setContentsAndCommit(repository, iFile1, BRANCH_CHANGE + INITIAL_CONTENT_FILE1, "branch commit"); //$NON-NLS-1$

        repository.checkoutBranch(MASTER);

        setContentsAndCommit(repository, iFile1, MASTER_CHANGE + INITIAL_CONTENT_FILE1, "master commit"); //$NON-NLS-1$
        iFile2.delete(true, new NullProgressMonitor());
        repository.addAndCommit(iProject, "master commit - deleted file2." + SAMPLE_FILE_EXTENSION, file2); //$NON-NLS-1$
        iProject.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
        // end setup

        merge(repo, BRANCH);

        final Status status = status(repo);
        assertTrue(status.hasUncommittedChanges());
        assertFalse(status.getConflicting().isEmpty());
        assertTrue(status.getConflicting().contains(repoRelativePath1));
        assertFalse(status.getConflicting().contains(repoRelativePath2));

        assertContentEquals(iFile1, MASTER_CHANGE + INITIAL_CONTENT_FILE1);
        assertFalse(iFile2.exists());

        Map<String, StageState> map = status.getConflictingStageState();
        assertEquals(StageState.BOTH_MODIFIED, map.get(repoRelativePath1));
    }

    /**
     * This test will initialize a repository with two branches with a few changes each, then try to merge the
     * branch into master.
     * <p>
     * The repository will contain two files, file1.sample and file2.sample, both being in the same container
     * and thus considered to be components of a single logical model by the SampleModelProvider.
     * </p>
     * <p>
     * file1 will be modified on both master and the branch, these changes being considered as an
     * unresolveable conflict by git (and JGit), but considered as an auto-mergeable conflict by the
     * SampleResourceMappingMerger. file2 will be deleted from the branch, but modified on master.
     * </p>
     * <p>
     * The merge will end in conflict, file1 being pre-merged since it does not present a conflict, and file2
     * should remain in its "master" state. file1 will have been added to the index, but file2 will be marked
     * as conflicting.
     * </p>
     *
     * @throws Exception
     */
    @Test
    public void mergeModelWithDeletedRemoteFileConflict() throws Exception {
        File file1 = repository.createFile(iProject, "file1." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$
        File file2 = repository.createFile(iProject, "file2." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$

        repository.appendContentAndCommit(iProject, file1, INITIAL_CONTENT_FILE1, "first file - initial commit"); //$NON-NLS-1$
        repository.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_FILE2, "second file - initial commit"); //$NON-NLS-1$

        IFile iFile1 = repository.getIFile(iProject, file1);
        IFile iFile2 = repository.getIFile(iProject, file2);
        String repoRelativePath1 = repository.getRepoRelativePath(iFile1.getLocation().toPortableString());
        String repoRelativePath2 = repository.getRepoRelativePath(iFile2.getLocation().toPortableString());

        repository.createAndCheckoutBranch(MASTER, BRANCH);

        setContentsAndCommit(repository, iFile1, BRANCH_CHANGE + INITIAL_CONTENT_FILE1, "branch commit"); //$NON-NLS-1$
        iFile2.delete(true, new NullProgressMonitor());
        repository.addAndCommit(iProject, "branch commit - deleted file2." + SAMPLE_FILE_EXTENSION, file2); //$NON-NLS-1$

        repository.checkoutBranch(MASTER);

        setContentsAndCommit(repository, iFile1, INITIAL_CONTENT_FILE1 + MASTER_CHANGE, "master commit - file1"); //$NON-NLS-1$
        setContentsAndCommit(repository, iFile2, INITIAL_CONTENT_FILE2 + MASTER_CHANGE, "master commit - file2"); //$NON-NLS-1$
        iProject.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
        // end setup

        merge(repo, BRANCH);

        final Status status = status(repo);
        assertTrue(status.hasUncommittedChanges());
        assertTrue(status.getChanged().contains(repoRelativePath1));
        assertTrue(status.getConflicting().contains(repoRelativePath2));

        assertContentEquals(iFile1, BRANCH_CHANGE + INITIAL_CONTENT_FILE1 + MASTER_CHANGE);
        assertContentEquals(iFile2, INITIAL_CONTENT_FILE2 + MASTER_CHANGE);

        Map<String, StageState> map = status.getConflictingStageState();
        assertEquals(StageState.DELETED_BY_THEM, map.get(repoRelativePath2));
    }

    /**
     * This test will initialize a repository with two branches with a few changes each, then try to merge the
     * branch into master.
     * <p>
     * The repository will contain two files, file1.sample and file2.sample, both being in the same container
     * and thus considered to be components of a single logical model by the SampleModelProvider.
     * </p>
     * <p>
     * file1 will be modified on both master and the branch, these changes being considered as an
     * unresolveable conflict by git (and JGit), but considered as an auto-mergeable conflict by the
     * SampleResourceMappingMerger. file2 will be deleted from master, but modified on the branch.
     * </p>
     * <p>
     * The merge will end in conflict. Since file2 has been deleted locally, it will not be seen as a part of
     * the logical model. file1 will be pre-merged since it does not present a conflict, and file2 will be
     * checked out in its "branch" state since we won't detect its logical model. Only file2 will be marked as
     * a conflict.
     * </p>
     *
     * @throws Exception
     */
    @Test
    public void mergeModelWithDeletedLocalFileConflict() throws Exception {
        File file1 = repository.createFile(iProject, "file1." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$
        File file2 = repository.createFile(iProject, "file2." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$

        repository.appendContentAndCommit(iProject, file1, INITIAL_CONTENT_FILE1, "first file - initial commit"); //$NON-NLS-1$
        repository.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_FILE2, "second file - initial commit"); //$NON-NLS-1$

        IFile iFile1 = repository.getIFile(iProject, file1);
        IFile iFile2 = repository.getIFile(iProject, file2);
        String repoRelativePath1 = repository.getRepoRelativePath(iFile1.getLocation().toPortableString());
        String repoRelativePath2 = repository.getRepoRelativePath(iFile2.getLocation().toPortableString());

        repository.createAndCheckoutBranch(MASTER, BRANCH);

        setContentsAndCommit(repository, iFile1, BRANCH_CHANGE + INITIAL_CONTENT_FILE1, "branch commit"); //$NON-NLS-1$
        setContentsAndCommit(repository, iFile2, BRANCH_CHANGE + INITIAL_CONTENT_FILE2, "branch commit"); //$NON-NLS-1$

        repository.checkoutBranch(MASTER);

        setContentsAndCommit(repository, iFile1, INITIAL_CONTENT_FILE1 + MASTER_CHANGE, "master commit"); //$NON-NLS-1$
        iFile2.delete(true, new NullProgressMonitor());
        repository.addAndCommit(iProject, "master commit - deleted file2." + SAMPLE_FILE_EXTENSION, file2); //$NON-NLS-1$

        iProject.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
        // end setup

        merge(repo, BRANCH);

        final Status status = status(repo);
        assertTrue(status.hasUncommittedChanges());
        assertFalse(status.getConflicting().contains(repoRelativePath1));
        assertTrue(status.getConflicting().contains(repoRelativePath2));

        assertContentEquals(iFile1, BRANCH_CHANGE + INITIAL_CONTENT_FILE1 + MASTER_CHANGE);
        assertContentEquals(iFile2, BRANCH_CHANGE + INITIAL_CONTENT_FILE2);

        Map<String, StageState> map = status.getConflictingStageState();
        assertEquals(StageState.DELETED_BY_US, map.get(repoRelativePath2));
    }

    /**
     * This test will initialize a repository with two branches with a few changes each, then try to merge the
     * branch into master.
     * <p>
     * The repository will contain two files, file1.sample and file2.sample, both being in the same container
     * and thus considered to be components of a single logical model by the SampleModelProvider.
     * </p>
     * <p>
     * file1 will be modified on both master and the branch, these changes being considered as an
     * unresolveable conflict by git (and JGit), but considered as an auto-mergeable conflict by the
     * SampleResourceMappingMerger. file2 will only exist on the branch since we'll add it there.
     * </p>
     * <p>
     * We expect the merge to end successfully, ending with a repository that has no uncommited change.
     * </p>
     *
     * @throws Exception
     */
    @Test
    public void mergeModelWithAddedRemoteNoConflict() throws Exception {
        File file1 = repository.createFile(iProject, "file1." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$
        repository.appendContentAndCommit(iProject, file1, INITIAL_CONTENT_FILE1, "first file - initial commit"); //$NON-NLS-1$
        IFile iFile1 = repository.getIFile(iProject, file1);

        repository.createAndCheckoutBranch(MASTER, BRANCH);

        setContentsAndCommit(repository, iFile1, BRANCH_CHANGE + INITIAL_CONTENT_FILE1, "branch commit"); //$NON-NLS-1$
        File file2 = repository.createFile(iProject, "file2." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$
        repository.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_FILE2,
                "second file - initial commit on branch"); //$NON-NLS-1$
        IFile iFile2 = repository.getIFile(iProject, file2);

        repository.checkoutBranch(MASTER);

        setContentsAndCommit(repository, iFile1, INITIAL_CONTENT_FILE1 + MASTER_CHANGE, "master commit"); //$NON-NLS-1$
        iProject.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
        // end setup

        merge(repo, BRANCH);

        final Status status = status(repo);
        assertFalse(status.hasUncommittedChanges());
        assertTrue(status.getConflicting().isEmpty());

        assertContentEquals(iFile1, BRANCH_CHANGE + INITIAL_CONTENT_FILE1 + MASTER_CHANGE);
        assertContentEquals(iFile2, INITIAL_CONTENT_FILE2);
    }

    /**
     * This test will initialize a repository with two branches with a few changes each, then try to merge the
     * branch into master.
     * <p>
     * The repository will contain two files, file1.sample and file2.sample, both being in the same container
     * and thus considered to be components of a single logical model by the SampleModelProvider.
     * </p>
     * <p>
     * file1 will be modified on both master and the branch, these changes being considered as an
     * unresolveable conflict by git (and JGit), but considered as an auto-mergeable conflict by the
     * SampleResourceMappingMerger. file2 will be added to master after the branch has diverged.
     * </p>
     * <p>
     * We expect the merge to end successfully, ending with a repository that has no uncommited change.
     * </p>
     *
     * @throws Exception
     */
    @Test
    public void mergeModelWithAddedLocalNoConflict() throws Exception {
        File file1 = repository.createFile(iProject, "file1." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$

        repository.appendContentAndCommit(iProject, file1, INITIAL_CONTENT_FILE1, "first file - initial commit"); //$NON-NLS-1$

        IFile iFile1 = repository.getIFile(iProject, file1);

        repository.createAndCheckoutBranch(MASTER, BRANCH);

        setContentsAndCommit(repository, iFile1, BRANCH_CHANGE + INITIAL_CONTENT_FILE1, "branch commit"); //$NON-NLS-1$

        repository.checkoutBranch(MASTER);

        setContentsAndCommit(repository, iFile1, INITIAL_CONTENT_FILE1 + MASTER_CHANGE, "master commit"); //$NON-NLS-1$
        File file2 = repository.createFile(iProject, "file2." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$
        IFile iFile2 = repository.getIFile(iProject, file2);
        repository.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_FILE2,
                "second file - initial commit on master"); //$NON-NLS-1$
        iProject.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
        // end setup

        merge(repo, BRANCH);

        final Status status = status(repo);
        assertFalse(status.hasUncommittedChanges());
        assertTrue(status.getConflicting().isEmpty());

        assertContentEquals(iFile1, BRANCH_CHANGE + INITIAL_CONTENT_FILE1 + MASTER_CHANGE);
        assertContentEquals(iFile2, INITIAL_CONTENT_FILE2);
    }

    /**
     * This test will initialize a repository with two branches with a few changes each, then try to merge the
     * branch into master.
     * <p>
     * The repository will contain two files, file1.sample and file2.sample, both being in the same container
     * and thus considered to be components of a single logical model by the SampleModelProvider.
     * </p>
     * <p>
     * file1 will be modified on both master and the branch, these changes being considered as an
     * unresolveable conflict by git (and JGit), but considered as an auto-mergeable conflict by the
     * SampleResourceMappingMerger. file2 will be added on both master and the branch with the same content.
     * This is a pseudo-conflict but the default text merger (TextStorageMerger) cannot handle such cases.
     * file2 will thus be marked as a conflict.
     * </p>
     * <p>
     * We expect the merge to end successfully, ending with a repository that has no uncommited change.
     * </p>
     *
     * @throws Exception
     */
    @Test
    public void mergeModelWithPseudoConflictAddition() throws Exception {
        File file1 = repository.createFile(iProject, "file1." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$

        repository.appendContentAndCommit(iProject, file1, INITIAL_CONTENT_FILE1, "first file - initial commit"); //$NON-NLS-1$

        IFile iFile1 = repository.getIFile(iProject, file1);

        repository.createAndCheckoutBranch(MASTER, BRANCH);

        setContentsAndCommit(repository, iFile1, BRANCH_CHANGE + INITIAL_CONTENT_FILE1, "branch commit"); //$NON-NLS-1$
        File file2 = repository.createFile(iProject, "file2." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$
        IFile iFile2 = repository.getIFile(iProject, file2);
        repository.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_FILE2,
                "second file - initial commit on branch"); //$NON-NLS-1$

        repository.checkoutBranch(MASTER);

        setContentsAndCommit(repository, iFile1, INITIAL_CONTENT_FILE1 + MASTER_CHANGE, "master commit"); //$NON-NLS-1$
        file2 = repository.createFile(iProject, "file2." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$
        iFile2 = repository.getIFile(iProject, file2);
        repository.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_FILE2,
                "second file - initial commit on master"); //$NON-NLS-1$
        iProject.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
        String repoRelativePath2 = repository.getRepoRelativePath(iFile2.getLocation().toPortableString());
        // end setup

        merge(repo, BRANCH);

        final Status status = status(repo);
        assertTrue(status.hasUncommittedChanges());
        assertTrue(status.getConflicting().contains(repoRelativePath2));

        assertContentEquals(iFile1, BRANCH_CHANGE + INITIAL_CONTENT_FILE1 + MASTER_CHANGE);
        assertContentEquals(iFile2, INITIAL_CONTENT_FILE2);

        Map<String, StageState> map = status.getConflictingStageState();
        assertEquals(StageState.BOTH_ADDED, map.get(repoRelativePath2));
    }

    /**
     * This test will initialize a repository with two branches with a few changes each, then try to merge the
     * branch into master.
     * <p>
     * The repository will contain two files, file1.sample and file2.sample, both being in the same container
     * and thus considered to be components of a single logical model by the SampleModelProvider.
     * </p>
     * <p>
     * file1 will be modified on both master and the branch in such a way that it will be an unresolveable
     * conflict for both JGit and the model merger. file2 will be added to the branch.
     * </p>
     * <p>
     * The merge must end in a conflict. The SampleResourceMappingMerger pre-merges what can be, so file2 will
     * be added to the working tree while file1 will be left untouched. file2 will be added to the index, but
     * file 1 will be marked as a conflict.
     * </p>
     *
     * @throws Exception
     */
    @Test
    public void mergeModelWithAddedRemoteModelConflict() throws Exception {
        File file1 = repository.createFile(iProject, "file1." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$
        File file2 = repository.createFile(iProject, "file2." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$

        repository.appendContentAndCommit(iProject, file1, INITIAL_CONTENT_FILE1, "first file - initial commit"); //$NON-NLS-1$

        IFile iFile1 = repository.getIFile(iProject, file1);
        String repoRelativePath1 = repository.getRepoRelativePath(iFile1.getLocation().toPortableString());

        repository.createAndCheckoutBranch(MASTER, BRANCH);

        setContentsAndCommit(repository, iFile1, BRANCH_CHANGE + INITIAL_CONTENT_FILE1, "branch commit"); //$NON-NLS-1$
        repository.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_FILE2,
                "second file - initial commit - branch"); //$NON-NLS-1$
        IFile iFile2 = repository.getIFile(iProject, file2);
        String repoRelativePath2 = repository.getRepoRelativePath(iFile2.getLocation().toPortableString());

        repository.checkoutBranch(MASTER);

        setContentsAndCommit(repository, iFile1, MASTER_CHANGE + INITIAL_CONTENT_FILE1, "master commit"); //$NON-NLS-1$
        iProject.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
        // end setup

        merge(repo, BRANCH);

        final Status status = status(repo);
        assertTrue(status.hasUncommittedChanges());
        assertFalse(status.getConflicting().isEmpty());
        assertTrue(status.getConflicting().contains(repoRelativePath1));
        assertTrue(status.getAdded().contains(repoRelativePath2));

        assertContentEquals(iFile1, MASTER_CHANGE + INITIAL_CONTENT_FILE1);
        assertContentEquals(iFile2, INITIAL_CONTENT_FILE2);

        Map<String, StageState> map = status.getConflictingStageState();
        assertEquals(StageState.BOTH_MODIFIED, map.get(repoRelativePath1));
    }

    /**
     * This test will initialize a repository with two branches with a few changes each, then try to merge the
     * branch into master.
     * <p>
     * The repository will contain two files, file1.sample and file2.sample, both being in the same container
     * and thus considered to be components of a single logical model by the SampleModelProvider.
     * </p>
     * <p>
     * file1 will be modified on both master and the branch in such a way that it will be an unresolveable
     * conflict for both JGit and the model merger. file2 will be added to master.
     * </p>
     * <p>
     * The merge must end in a conflict. file1 will be marked as a conflict, untouched as compared to its
     * previous (master) state. file2 will also be marked as conflicting, since the default merger doesn't
     * tell us that "added by us" files are to be made in sync.
     * </p>
     *
     * @throws Exception
     */
    @Test
    public void mergeModelWithAddedLocalModelConflict() throws Exception {
        File file1 = repository.createFile(iProject, "file1." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$
        File file2 = repository.createFile(iProject, "file2." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$

        repository.appendContentAndCommit(iProject, file1, INITIAL_CONTENT_FILE1, "first file - initial commit"); //$NON-NLS-1$

        IFile iFile1 = repository.getIFile(iProject, file1);
        String repoRelativePath1 = repository.getRepoRelativePath(iFile1.getLocation().toPortableString());

        repository.createAndCheckoutBranch(MASTER, BRANCH);

        setContentsAndCommit(repository, iFile1, BRANCH_CHANGE + INITIAL_CONTENT_FILE1, "branch commit"); //$NON-NLS-1$

        repository.checkoutBranch(MASTER);

        setContentsAndCommit(repository, iFile1, MASTER_CHANGE + INITIAL_CONTENT_FILE1, "master commit"); //$NON-NLS-1$
        repository.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_FILE2, "second file - initial commit"); //$NON-NLS-1$
        IFile iFile2 = repository.getIFile(iProject, file2);
        String repoRelativePath2 = repository.getRepoRelativePath(iFile2.getLocation().toPortableString());
        iProject.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
        // end setup

        merge(repo, BRANCH);

        final Status status = status(repo);
        assertTrue(status.hasUncommittedChanges());
        assertFalse(status.getConflicting().isEmpty());
        assertTrue(status.getConflicting().contains(repoRelativePath1));
        assertTrue(status.getConflicting().contains(repoRelativePath2));

        assertContentEquals(iFile1, MASTER_CHANGE + INITIAL_CONTENT_FILE1);
        assertContentEquals(iFile2, INITIAL_CONTENT_FILE2);

        Map<String, StageState> map = status.getConflictingStageState();
        assertEquals(StageState.BOTH_MODIFIED, map.get(repoRelativePath1));
        assertEquals(StageState.ADDED_BY_US, map.get(repoRelativePath2));
    }

    /**
     * This test will initialize a repository with two branches with a few changes each, then try to merge the
     * branch into master.
     * <p>
     * The repository will contain two files, file1.sample and file2.sample, both being in the same container
     * and thus considered to be components of a single logical model by the SampleModelProvider.
     * </p>
     * <p>
     * file1 will be modified on both master and the branch, these changes being considered as an
     * unresolveable conflict by git (and JGit), but considered as an auto-mergeable conflict by the
     * SampleResourceMappingMerger. file2 will be added to both master and the branch, with distinct content.
     * </p>
     * <p>
     * The merge will end in conflict, file1 being pre-merged since it does not present a conflict, and file2
     * should remain in its "master" state. file1 will have been added to the index, but file2 will be marked
     * as conflicting.
     * </p>
     *
     * @throws Exception
     */
    @Test
    public void mergeModelWithConflictAddition() throws Exception {
        File file1 = repository.createFile(iProject, "file1." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$
        File file2 = repository.createFile(iProject, "file2." + SAMPLE_FILE_EXTENSION); //$NON-NLS-1$

        repository.appendContentAndCommit(iProject, file1, INITIAL_CONTENT_FILE1, "first file - initial commit"); //$NON-NLS-1$

        IFile iFile1 = repository.getIFile(iProject, file1);
        String repoRelativePath1 = repository.getRepoRelativePath(iFile1.getLocation().toPortableString());

        repository.createAndCheckoutBranch(MASTER, BRANCH);

        setContentsAndCommit(repository, iFile1, BRANCH_CHANGE + INITIAL_CONTENT_FILE1, "branch commit"); //$NON-NLS-1$
        repository.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_FILE2 + "branch", //$NON-NLS-1$
                "second file - initial commit - branch"); //$NON-NLS-1$

        repository.checkoutBranch(MASTER);

        setContentsAndCommit(repository, iFile1, INITIAL_CONTENT_FILE1 + MASTER_CHANGE, "master commit - file1"); //$NON-NLS-1$
        repository.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_FILE2 + "master", //$NON-NLS-1$
                "second file - initial commit - master"); //$NON-NLS-1$
        IFile iFile2 = repository.getIFile(iProject, file2);
        String repoRelativePath2 = repository.getRepoRelativePath(iFile2.getLocation().toPortableString());
        iProject.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
        // end setup

        merge(repo, BRANCH);

        final Status status = status(repo);
        assertTrue(status.hasUncommittedChanges());
        assertTrue(status.getChanged().contains(repoRelativePath1));
        assertTrue(status.getConflicting().contains(repoRelativePath2));

        assertContentEquals(iFile1, BRANCH_CHANGE + INITIAL_CONTENT_FILE1 + MASTER_CHANGE);
        assertContentEquals(iFile2, INITIAL_CONTENT_FILE2 + "master"); //$NON-NLS-1$

        Map<String, StageState> map = status.getConflictingStageState();
        assertEquals(StageState.BOTH_ADDED, map.get(repoRelativePath2));
    }
}