@Test
void mergeWhenUserHasChangedAllContent() throws Exception
{

    MergeResult<String> result;
    // Test 1: All content has changed between previous and current
    result = this.diffManager.merge(Arrays.asList("Line 1", "Line 2", "Line 3"),
        Arrays.asList("Line 1", "Line 2 modified", "Line 3", "Line 4 Added"),
        Arrays.asList("New content", "That is completely different"), null);
    assertEquals(1, result.getLog().getLogs(LogLevel.ERROR).size());
    assertTrue(result.getLog().getLogs(LogLevel.ERROR).get(0).toString()
        .contains("Conflict between"));
    assertEquals(1, result.getConflicts().size());
    Conflict<String> conflict = createConflict(0,
        Type.CHANGE, 0, 0, Arrays.asList("Line 1", "Line 2", "Line 3"),
        Arrays.asList("New content", "That is completely different"),
        Type.CHANGE, 0, 0, Arrays.asList("Line 1", "Line 2", "Line 3"),
        Arrays.asList("Line 1", "Line 2 modified", "Line 3"));
    assertEquals(conflict, result.getConflicts().get(0));
    assertEquals(Arrays.asList("New content", "That is completely different"), result.getMerged());
    MergeConfiguration<String> mergeConfiguration = new MergeConfiguration<>();
    mergeConfiguration.setFallbackOnConflict(MergeConfiguration.Version.PREVIOUS);
    result = this.diffManager.merge(Arrays.asList("Line 1", "Line 2", "Line 3"),
        Arrays.asList("Line 1", "Line 2 modified", "Line 3", "Line 4 Added"),
        Arrays.asList("New content", "That is completely different"), mergeConfiguration);
    assertEquals(1, result.getLog().getLogs(LogLevel.ERROR).size());
    assertTrue(result.getLog().getLogs(LogLevel.ERROR).get(0).toString()
        .contains("Conflict between"));
    assertEquals(conflict, result.getConflicts().get(0));
    assertEquals(Arrays.asList("Line 1", "Line 2", "Line 3"), result.getMerged());
    mergeConfiguration.setFallbackOnConflict(MergeConfiguration.Version.NEXT);
    result = this.diffManager.merge(Arrays.asList("Line 1", "Line 2", "Line 3"),
        Arrays.asList("Line 1", "Line 2 modified", "Line 3", "Line 4 Added"),
        Arrays.asList("New content", "That is completely different"), mergeConfiguration);
    assertEquals(1, result.getLog().getLogs(LogLevel.ERROR).size());
    assertTrue(result.getLog().getLogs(LogLevel.ERROR).get(0).toString()
        .contains("Conflict between"));
    assertEquals(conflict, result.getConflicts().get(0));
    assertEquals(Arrays.asList("Line 1", "Line 2 modified", "Line 3", "Line 4 Added"), result.getMerged());
    // Test 2: All content has been deleted between previous and current
    result = this.diffManager.merge(Arrays.asList("Line 1", "Line 2", "Line 3"),
        Arrays.asList("Line 1", "Line 2 modified", "Line 3", "Line 4 Added"),
        Collections.emptyList(), null);
    conflict = createConflict(0,
        Type.CHANGE, 0, 0, Arrays.asList("Line 1", "Line 2", "Line 3"), Collections.emptyList(),
        Type.CHANGE, 0, 0, Arrays.asList("Line 1", "Line 2", "Line 3"), Arrays.asList("Line 1", "Line 2 modified",
            "Line 3"));
    assertEquals(1, result.getLog().getLogs(LogLevel.ERROR).size());
    assertTrue(result.getLog().getLogs(LogLevel.ERROR).get(0).toString()
        .contains("Conflict between"));
    assertEquals(conflict, result.getConflicts().get(0));
    assertEquals(Collections.emptyList(), result.getMerged());
    mergeConfiguration.setFallbackOnConflict(MergeConfiguration.Version.PREVIOUS);
    result = this.diffManager.merge(Arrays.asList("Line 1", "Line 2", "Line 3"),
        Arrays.asList("Line 1", "Line 2 modified", "Line 3", "Line 4 Added"),
        Collections.emptyList(), mergeConfiguration);
    assertEquals(1, result.getLog().getLogs(LogLevel.ERROR).size());
    assertTrue(result.getLog().getLogs(LogLevel.ERROR).get(0).toString()
        .contains("Conflict between"));
    assertEquals(conflict, result.getConflicts().get(0));
    assertEquals(Arrays.asList("Line 1", "Line 2", "Line 3"), result.getMerged());
    mergeConfiguration.setFallbackOnConflict(MergeConfiguration.Version.NEXT);
    result = this.diffManager.merge(Arrays.asList("Line 1", "Line 2", "Line 3"),
        Arrays.asList("Line 1", "Line 2 modified", "Line 3", "Line 4 Added"),
        Collections.emptyList(), mergeConfiguration);
    assertEquals(1, result.getLog().getLogs(LogLevel.ERROR).size());
    assertTrue(result.getLog().getLogs(LogLevel.ERROR).get(0).toString()
        .contains("Conflict between"));
    assertEquals(conflict, result.getConflicts().get(0));
    assertEquals(Arrays.asList("Line 1", "Line 2 modified", "Line 3", "Line 4 Added"), result.getMerged());
    // Test 3: All content has been deleted between previous and next
    result = this.diffManager.merge(Arrays.asList("Line 1", "Line 2", "Line 3"),
        Collections.emptyList(),
        Arrays.asList("Line 1", "Line 2 modified", "Line 3", "Line 4 Added"), null);
    conflict = createConflict(0,
        Type.CHANGE, 0, 0, Arrays.asList("Line 1", "Line 2", "Line 3"),
        Arrays.asList("Line 1", "Line 2 modified", "Line 3"),
        Type.CHANGE, 0, 0, Arrays.asList("Line 1", "Line 2", "Line 3"), Collections.emptyList());
    assertEquals(1, result.getLog().getLogs(LogLevel.ERROR).size());
    assertTrue(result.getLog().getLogs(LogLevel.ERROR).get(0).toString()
        .contains("Conflict between"));
    assertEquals(conflict, result.getConflicts().get(0));
    assertEquals(Arrays.asList("Line 1", "Line 2 modified", "Line 3", "Line 4 Added"), result.getMerged());
    mergeConfiguration.setFallbackOnConflict(MergeConfiguration.Version.PREVIOUS);
    result = this.diffManager.merge(Arrays.asList("Line 1", "Line 2", "Line 3"),
        Collections.emptyList(),
        Arrays.asList("Line 1", "Line 2 modified", "Line 3", "Line 4 Added"), mergeConfiguration);
    assertEquals(1, result.getLog().getLogs(LogLevel.ERROR).size());
    assertTrue(result.getLog().getLogs(LogLevel.ERROR).get(0).toString()
        .contains("Conflict between"));
    assertEquals(conflict, result.getConflicts().get(0));
    assertEquals(Arrays.asList("Line 1", "Line 2", "Line 3"), result.getMerged());
    mergeConfiguration.setFallbackOnConflict(MergeConfiguration.Version.NEXT);
    result = this.diffManager.merge(Arrays.asList("Line 1", "Line 2", "Line 3"),
        Collections.emptyList(),
        Arrays.asList("Line 1", "Line 2 modified", "Line 3", "Line 4 Added"), mergeConfiguration);
    assertEquals(1, result.getLog().getLogs(LogLevel.ERROR).size());
    assertTrue(result.getLog().getLogs(LogLevel.ERROR).get(0).toString()
        .contains("Conflict between"));
    assertEquals(conflict, result.getConflicts().get(0));
    assertEquals(Collections.emptyList(), result.getMerged());
    // Test 4: All content has changed between previous and current, and current and next are identical
    result = this.diffManager.merge(Arrays.asList("Line 1", "Line 2", "Line 3"),
        Arrays.asList("New content", "That is completely different"),
        Arrays.asList("New content", "That is completely different"), null);
    assertEquals(0, result.getLog().getLogs(LogLevel.ERROR).size());
    assertEquals(0, result.getConflicts().size());
    assertEquals(Arrays.asList("New content", "That is completely different"), result.getMerged());
    mergeConfiguration.setFallbackOnConflict(MergeConfiguration.Version.PREVIOUS);
    result = this.diffManager.merge(Arrays.asList("Line 1", "Line 2", "Line 3"),
        Arrays.asList("New content", "That is completely different"),
        Arrays.asList("New content", "That is completely different"), mergeConfiguration);
    assertEquals(0, result.getLog().getLogs(LogLevel.ERROR).size());
    assertEquals(0, result.getConflicts().size());
    assertEquals(Arrays.asList("New content", "That is completely different"), result.getMerged());
    mergeConfiguration = null;
    conflict = this.createConflict(2,
        Type.CHANGE, 2, 2, Arrays.asList("started to walk"), Arrays.asList("started to walk", "dressed in black"),
        Type.CHANGE, 2, 2, Arrays.asList("started to walk"), Arrays.asList("dressed in black"));
    result = this.diffManager
        .merge(
            Arrays.asList(
                "Once upon a time",
                "a wolf",
                "started to walk",
                "in the forest"
            ),
            Arrays.asList(
                "Once upon a time",
                "a wolf",
                "dressed in black",
                "in the forest"
            ),
            Arrays.asList(
                "Once upon a time",
                "a wolf",
                "started to walk",
                "dressed in black",
                "in the forest"
            ), mergeConfiguration);
    assertEquals(1, result.getLog().getLogs(LogLevel.ERROR).size());
    assertEquals(1, result.getConflicts().size());
    assertEquals(conflict, result.getConflicts().get(0));
    assertEquals(
        Arrays.asList("Once upon a time", "a wolf", "started to walk", "dressed in black", "in the forest"),
        result.getMerged());
    // Test 6: We change everything but our first changes are the same at the beginning of current and next:
    // it still must led to a conflict, since we don't know if we should have "Another line" or not.
    mergeConfiguration = null;
    conflict = createConflict(0,
        Type.CHANGE, 0, 0, Arrays.asList("A first edit from a tab."),
        Arrays.asList("A second edit from another tab.", "Another line"),
        Type.CHANGE, 0, 0, Arrays.asList("A first edit from a tab."),
        Arrays.asList("A second edit from another tab."));
    result = this.diffManager.merge(
        Arrays.asList("A first edit from a tab."),
        Arrays.asList("A second edit from another tab."),
        Arrays.asList("A second edit from another tab.", "Another line"),
        mergeConfiguration);
    assertEquals(1, result.getLog().getLogs(LogLevel.ERROR).size());
    assertEquals(1, result.getConflicts().size());
    assertEquals(conflict, result.getConflicts().get(0));
    assertEquals(Arrays.asList("A second edit from another tab.", "Another line"), result.getMerged());
    // Test 7: Change 3 lines between previous and next, and only 2 between previous and current:
    // only 1 conflicts should occur, concerning the whole list of changes.
    conflict = this.createConflict(0,
        Type.CHANGE, 0, 0,
        Arrays.asList(
            "A fifth edit from another tab.",
            "Another line.",
            "Yet another line with other few changes."
        ),
        Arrays.asList(
            "A sixth edit from the first tab.",
            "Another line.",
            "Yet another line edited from the first tab."
        ),
        Type.CHANGE, 0, 0, Arrays.asList(
            "A fifth edit from another tab.",
            "Another line.",
            "Yet another line with other few changes."
        ),
        Arrays.asList(
            "A sixth edit from the second tab.",
            "Another line with small changes.",
            "Another edit from the second tab."
        ));
    result = this.diffManager.merge(
        Arrays.asList(
            "A fifth edit from another tab.",
            "Another line.",
            "Yet another line with other few changes."
        ),
        Arrays.asList(
            "A sixth edit from the second tab.",
            "Another line with small changes.",
            "Another edit from the second tab."
        ),
        Arrays.asList(
            "A sixth edit from the first tab.",
            "Another line.",
            "Yet another line edited from the first tab."
        ), mergeConfiguration);
    assertEquals(1, result.getConflicts().size());
    assertEquals(conflict, result.getConflicts().get(0));
    // Test 7: Delete lines between previous and next, and only 1 change between previous and current:
    // only 1 conflicts should occur, concerning the whole list of changes.
    conflict = this.createConflict(0,
        Type.CHANGE, 0, 0,
        Arrays.asList(
            "A fifth edit from another tab.",
            "Another line.",
            "Yet another line with other few changes."
        ),
        Arrays.asList(
            "A sixth edit from the first tab.",
            "Another line.",
            "Yet another line edited from the first tab."
        ),
        Type.CHANGE, 0, 0, Arrays.asList(
            "A fifth edit from another tab.",
            "Another line.",
            "Yet another line with other few changes."
        ),
        Collections.emptyList());
    result = this.diffManager.merge(
        Arrays.asList(
            "A fifth edit from another tab.",
            "Another line.",
            "Yet another line with other few changes."
        ),
        Collections.emptyList(),
        Arrays.asList(
            "A sixth edit from the first tab.",
            "Another line.",
            "Yet another line edited from the first tab."
        ), mergeConfiguration);
    assertEquals(1, result.getConflicts().size());
    assertEquals(conflict, result.getConflicts().get(0));
    // Test 8: Conflicts with empty lines
    conflict = this.createConflict(0,
        Type.CHANGE, 0, 0, Arrays.asList("Some content."), Arrays.asList("Another content", "On multiple lines."),
        Type.CHANGE, 0, 0, Arrays.asList("Some content."), Arrays.asList(
            "Some content.",
            "",
            "And now if I try",
            "to put something",
            "",
            "with empty lines",
            "like this"
        ));
    result = this.diffManager.merge(
        Arrays.asList(
            "Some content."
        ),
        Arrays.asList(
            "Some content.",
            "",
            "And now if I try",
            "to put something",
            "",
            "with empty lines",
            "like this"
        ),
        Arrays.asList(
            "Another content",
            "On multiple lines."
        ), mergeConfiguration);
    assertEquals(1, result.getConflicts().size());
    assertEquals(conflict, result.getConflicts().get(0));
    // Test 9: Multiple conflicts and insertion of conflicting data
    conflict = this.createConflict(1,
        Type.CHANGE, 1, 1, Arrays.asList("Second line.", "Third line."), Arrays.asList("Second line.", "Line N°4"),
        Type.CHANGE, 1, 1, Arrays.asList("Second line.", "Third line."), Arrays.asList("Line N°2", "Third line."));
    Conflict<String> conflict2 = this.createConflict(5,
        Type.CHANGE, 5, 5, Collections.emptyList(), Arrays.asList("6th line."),
        Type.CHANGE, 5, 5, Collections.emptyList(), Arrays.asList("Sixth line."));
    result = this.diffManager.merge(
        Arrays.asList(
            "First line.",
            "Second line.",
            "Third line.",
            "Fourth line.",
            "Fifth line.",
            "Seventh line."
        ), Arrays.asList(
            "First line.",
            "Line N°2",
            "Third line.",
            "Fifth line.",
            "Sixth line.",
            "Seventh line."
        ), Arrays.asList(
            "First line.",
            "Second line.",
            "Line N°4",
            "Fifth line.",
            "6th line.",
            "Seventh line."
        ), mergeConfiguration);
    assertEquals(2, result.getConflicts().size());
    assertEquals(conflict, result.getConflicts().get(0));
    assertEquals(conflict2, result.getConflicts().get(1));
    assertEquals(Arrays.asList(
        "First line.",
        "Second line.",
        "Line N°4",
        "Fifth line.",
        "6th line.",
        "Seventh line."
    ), result.getMerged());
    conflict = this.createConflict(0,
        Type.CHANGE, 0, 0, Arrays.asList("A second edit from another tab.", "A new line."),
        Arrays.asList("A fourth edit from second tab.", "Another line."),
        Type.CHANGE, 0, 0, Arrays.asList("A second edit from another tab.", "A new line."),
        Arrays.asList("A third edit from another tab.", "Another line.", "Yet another line."));
    result = this.diffManager.merge(
        Arrays.asList("A second edit from another tab.", "A new line."),
        Arrays.asList("A third edit from another tab.", "Another line.", "Yet another line."),
        Arrays.asList("A fourth edit from second tab.", "Another line."),
        mergeConfiguration);
    assertEquals(1, result.getConflicts().size());
    assertEquals(conflict, result.getConflicts().get(0));
    mergeConfiguration = null;
    conflict = this.createConflict(1,
        Type.CHANGE, 1, 1, Arrays.asList("Anything else"), Arrays.asList("",
            "And now if I try",
            "to put something",
            "",
            "with empty lines",
            "like this"),
        Type.CHANGE, 1, 1, Arrays.asList("Anything else"), Arrays.asList("On multiple lines."));
    result = diffManager.merge(Arrays.asList(
        "Some content.",
        "Anything else"
    ), Arrays.asList(
        "Some content.",
        "On multiple lines."
    ), Arrays.asList(
        "Some content.",
        "",
        "And now if I try",
        "to put something",
        "",
        "with empty lines",
        "like this"
    ), mergeConfiguration);
    assertEquals(1, result.getConflicts().size());
    assertEquals(conflict, result.getConflicts().get(0));
}