| /* |
| * Copyright (C) 2010, Sasa Zivkov <sasa.zivkov@sap.com> and others |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Distribution License v. 1.0 which is available at |
| * https://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| package org.eclipse.jgit.notes; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import java.io.IOException; |
| import java.util.Iterator; |
| |
| import org.eclipse.jgit.junit.RepositoryTestCase; |
| import org.eclipse.jgit.junit.TestRepository; |
| import org.eclipse.jgit.lib.ObjectInserter; |
| import org.eclipse.jgit.lib.ObjectReader; |
| import org.eclipse.jgit.lib.Repository; |
| import org.eclipse.jgit.merge.MergeStrategy; |
| import org.eclipse.jgit.revwalk.RevBlob; |
| import org.eclipse.jgit.revwalk.RevCommit; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| public class NoteMapMergerTest extends RepositoryTestCase { |
| private TestRepository<Repository> tr; |
| |
| private ObjectReader reader; |
| |
| private ObjectInserter inserter; |
| |
| private NoteMap noRoot; |
| |
| private NoteMap empty; |
| |
| private NoteMap map_a; |
| |
| private NoteMap map_a_b; |
| |
| private RevBlob noteAId; |
| |
| private String noteAContent; |
| |
| private RevBlob noteABlob; |
| |
| private RevBlob noteBId; |
| |
| private String noteBContent; |
| |
| private RevBlob noteBBlob; |
| |
| private RevCommit sampleTree_a; |
| |
| private RevCommit sampleTree_a_b; |
| |
| @Override |
| @Before |
| public void setUp() throws Exception { |
| super.setUp(); |
| tr = new TestRepository<>(db); |
| reader = db.newObjectReader(); |
| inserter = db.newObjectInserter(); |
| |
| noRoot = NoteMap.newMap(null, reader); |
| empty = NoteMap.newEmptyMap(); |
| |
| noteAId = tr.blob("a"); |
| noteAContent = "noteAContent"; |
| noteABlob = tr.blob(noteAContent); |
| sampleTree_a = tr.commit() |
| .add(noteAId.name(), noteABlob) |
| .create(); |
| tr.parseBody(sampleTree_a); |
| map_a = NoteMap.read(reader, sampleTree_a); |
| |
| noteBId = tr.blob("b"); |
| noteBContent = "noteBContent"; |
| noteBBlob = tr.blob(noteBContent); |
| sampleTree_a_b = tr.commit() |
| .add(noteAId.name(), noteABlob) |
| .add(noteBId.name(), noteBBlob) |
| .create(); |
| tr.parseBody(sampleTree_a_b); |
| map_a_b = NoteMap.read(reader, sampleTree_a_b); |
| } |
| |
| @Override |
| @After |
| public void tearDown() throws Exception { |
| reader.close(); |
| inserter.close(); |
| super.tearDown(); |
| } |
| |
| @Test |
| public void testNoChange() throws IOException { |
| NoteMapMerger merger = new NoteMapMerger(db, null, null); |
| NoteMap result; |
| |
| assertEquals(0, countNotes(merger.merge(noRoot, noRoot, noRoot))); |
| assertEquals(0, countNotes(merger.merge(empty, empty, empty))); |
| |
| result = merger.merge(map_a, map_a, map_a); |
| assertEquals(1, countNotes(result)); |
| assertEquals(noteABlob, result.get(noteAId)); |
| } |
| |
| @Test |
| public void testOursEqualsTheirs() throws Exception { |
| NoteMapMerger merger = new NoteMapMerger(db, null, null); |
| NoteMap result; |
| |
| assertEquals(0, countNotes(merger.merge(empty, noRoot, noRoot))); |
| assertEquals(0, countNotes(merger.merge(map_a, noRoot, noRoot))); |
| |
| assertEquals(0, countNotes(merger.merge(noRoot, empty, empty))); |
| assertEquals(0, countNotes(merger.merge(map_a, empty, empty))); |
| |
| result = merger.merge(noRoot, map_a, map_a); |
| assertEquals(1, countNotes(result)); |
| assertEquals(noteABlob, result.get(noteAId)); |
| |
| result = merger.merge(empty, map_a, map_a); |
| assertEquals(1, countNotes(result)); |
| assertEquals(noteABlob, result.get(noteAId)); |
| |
| result = merger.merge(map_a_b, map_a, map_a); |
| assertEquals(1, countNotes(result)); |
| assertEquals(noteABlob, result.get(noteAId)); |
| |
| result = merger.merge(map_a, map_a_b, map_a_b); |
| assertEquals(2, countNotes(result)); |
| assertEquals(noteABlob, result.get(noteAId)); |
| assertEquals(noteBBlob, result.get(noteBId)); |
| } |
| |
| @Test |
| public void testBaseEqualsOurs() throws Exception { |
| NoteMapMerger merger = new NoteMapMerger(db, null, null); |
| NoteMap result; |
| |
| assertEquals(0, countNotes(merger.merge(noRoot, noRoot, empty))); |
| result = merger.merge(noRoot, noRoot, map_a); |
| assertEquals(1, countNotes(result)); |
| assertEquals(noteABlob, result.get(noteAId)); |
| |
| assertEquals(0, countNotes(merger.merge(empty, empty, noRoot))); |
| result = merger.merge(empty, empty, map_a); |
| assertEquals(1, countNotes(result)); |
| assertEquals(noteABlob, result.get(noteAId)); |
| |
| assertEquals(0, countNotes(merger.merge(map_a, map_a, noRoot))); |
| assertEquals(0, countNotes(merger.merge(map_a, map_a, empty))); |
| result = merger.merge(map_a, map_a, map_a_b); |
| assertEquals(2, countNotes(result)); |
| assertEquals(noteABlob, result.get(noteAId)); |
| assertEquals(noteBBlob, result.get(noteBId)); |
| } |
| |
| @Test |
| public void testBaseEqualsTheirs() throws Exception { |
| NoteMapMerger merger = new NoteMapMerger(db, null, null); |
| NoteMap result; |
| |
| assertEquals(0, countNotes(merger.merge(noRoot, empty, noRoot))); |
| result = merger.merge(noRoot, map_a, noRoot); |
| assertEquals(1, countNotes(result)); |
| assertEquals(noteABlob, result.get(noteAId)); |
| |
| assertEquals(0, countNotes(merger.merge(empty, noRoot, empty))); |
| result = merger.merge(empty, map_a, empty); |
| assertEquals(1, countNotes(result)); |
| assertEquals(noteABlob, result.get(noteAId)); |
| |
| assertEquals(0, countNotes(merger.merge(map_a, noRoot, map_a))); |
| assertEquals(0, countNotes(merger.merge(map_a, empty, map_a))); |
| result = merger.merge(map_a, map_a_b, map_a); |
| assertEquals(2, countNotes(result)); |
| assertEquals(noteABlob, result.get(noteAId)); |
| assertEquals(noteBBlob, result.get(noteBId)); |
| } |
| |
| @Test |
| public void testAddDifferentNotes() throws Exception { |
| NoteMapMerger merger = new NoteMapMerger(db, null, null); |
| NoteMap result; |
| |
| NoteMap map_a_c = NoteMap.read(reader, sampleTree_a); |
| RevBlob noteCId = tr.blob("c"); |
| RevBlob noteCBlob = tr.blob("noteCContent"); |
| map_a_c.set(noteCId, noteCBlob); |
| map_a_c.writeTree(inserter); |
| |
| result = merger.merge(map_a, map_a_b, map_a_c); |
| assertEquals(3, countNotes(result)); |
| assertEquals(noteABlob, result.get(noteAId)); |
| assertEquals(noteBBlob, result.get(noteBId)); |
| assertEquals(noteCBlob, result.get(noteCId)); |
| } |
| |
| @Test |
| public void testAddSameNoteDifferentContent() throws Exception { |
| NoteMapMerger merger = new NoteMapMerger(db, new DefaultNoteMerger(), |
| null); |
| NoteMap result; |
| |
| NoteMap map_a_b1 = NoteMap.read(reader, sampleTree_a); |
| String noteBContent1 = noteBContent + "change"; |
| RevBlob noteBBlob1 = tr.blob(noteBContent1); |
| map_a_b1.set(noteBId, noteBBlob1); |
| map_a_b1.writeTree(inserter); |
| |
| result = merger.merge(map_a, map_a_b, map_a_b1); |
| assertEquals(2, countNotes(result)); |
| assertEquals(noteABlob, result.get(noteAId)); |
| assertEquals(tr.blob(noteBContent + noteBContent1), result.get(noteBId)); |
| } |
| |
| @Test |
| public void testEditSameNoteDifferentContent() throws Exception { |
| NoteMapMerger merger = new NoteMapMerger(db, new DefaultNoteMerger(), |
| null); |
| NoteMap result; |
| |
| NoteMap map_a1 = NoteMap.read(reader, sampleTree_a); |
| String noteAContent1 = noteAContent + "change1"; |
| RevBlob noteABlob1 = tr.blob(noteAContent1); |
| map_a1.set(noteAId, noteABlob1); |
| map_a1.writeTree(inserter); |
| |
| NoteMap map_a2 = NoteMap.read(reader, sampleTree_a); |
| String noteAContent2 = noteAContent + "change2"; |
| RevBlob noteABlob2 = tr.blob(noteAContent2); |
| map_a2.set(noteAId, noteABlob2); |
| map_a2.writeTree(inserter); |
| |
| result = merger.merge(map_a, map_a1, map_a2); |
| assertEquals(1, countNotes(result)); |
| assertEquals(tr.blob(noteAContent1 + noteAContent2), |
| result.get(noteAId)); |
| } |
| |
| @Test |
| public void testEditDifferentNotes() throws Exception { |
| NoteMapMerger merger = new NoteMapMerger(db, null, null); |
| NoteMap result; |
| |
| NoteMap map_a1_b = NoteMap.read(reader, sampleTree_a_b); |
| String noteAContent1 = noteAContent + "change"; |
| RevBlob noteABlob1 = tr.blob(noteAContent1); |
| map_a1_b.set(noteAId, noteABlob1); |
| map_a1_b.writeTree(inserter); |
| |
| NoteMap map_a_b1 = NoteMap.read(reader, sampleTree_a_b); |
| String noteBContent1 = noteBContent + "change"; |
| RevBlob noteBBlob1 = tr.blob(noteBContent1); |
| map_a_b1.set(noteBId, noteBBlob1); |
| map_a_b1.writeTree(inserter); |
| |
| result = merger.merge(map_a_b, map_a1_b, map_a_b1); |
| assertEquals(2, countNotes(result)); |
| assertEquals(noteABlob1, result.get(noteAId)); |
| assertEquals(noteBBlob1, result.get(noteBId)); |
| } |
| |
| @Test |
| public void testDeleteDifferentNotes() throws Exception { |
| NoteMapMerger merger = new NoteMapMerger(db, null, null); |
| |
| NoteMap map_b = NoteMap.read(reader, sampleTree_a_b); |
| map_b.set(noteAId, null); // delete note a |
| map_b.writeTree(inserter); |
| |
| assertEquals(0, countNotes(merger.merge(map_a_b, map_a, map_b))); |
| } |
| |
| @Test |
| public void testEditDeleteConflict() throws Exception { |
| NoteMapMerger merger = new NoteMapMerger(db, new DefaultNoteMerger(), |
| null); |
| NoteMap result; |
| |
| NoteMap map_a_b1 = NoteMap.read(reader, sampleTree_a_b); |
| String noteBContent1 = noteBContent + "change"; |
| RevBlob noteBBlob1 = tr.blob(noteBContent1); |
| map_a_b1.set(noteBId, noteBBlob1); |
| map_a_b1.writeTree(inserter); |
| |
| result = merger.merge(map_a_b, map_a_b1, map_a); |
| assertEquals(2, countNotes(result)); |
| assertEquals(noteABlob, result.get(noteAId)); |
| assertEquals(noteBBlob1, result.get(noteBId)); |
| } |
| |
| @Test |
| public void testLargeTreesWithoutConflict() throws Exception { |
| NoteMapMerger merger = new NoteMapMerger(db, null, null); |
| NoteMap map1 = createLargeNoteMap("note_1_", "content_1_", 300, 0); |
| NoteMap map2 = createLargeNoteMap("note_2_", "content_2_", 300, 0); |
| |
| NoteMap result = merger.merge(empty, map1, map2); |
| assertEquals(600, countNotes(result)); |
| // check a few random notes |
| assertEquals(tr.blob("content_1_59"), result.get(tr.blob("note_1_59"))); |
| assertEquals(tr.blob("content_2_10"), result.get(tr.blob("note_2_10"))); |
| assertEquals(tr.blob("content_2_99"), result.get(tr.blob("note_2_99"))); |
| } |
| |
| @Test |
| public void testLargeTreesWithConflict() throws Exception { |
| NoteMapMerger merger = new NoteMapMerger(db, new DefaultNoteMerger(), |
| null); |
| NoteMap largeTree1 = createLargeNoteMap("note_1_", "content_1_", 300, 0); |
| NoteMap largeTree2 = createLargeNoteMap("note_1_", "content_2_", 300, 0); |
| |
| NoteMap result = merger.merge(empty, largeTree1, largeTree2); |
| assertEquals(300, countNotes(result)); |
| // check a few random notes |
| assertEquals(tr.blob("content_1_59content_2_59"), |
| result.get(tr.blob("note_1_59"))); |
| assertEquals(tr.blob("content_1_10content_2_10"), |
| result.get(tr.blob("note_1_10"))); |
| assertEquals(tr.blob("content_1_99content_2_99"), |
| result.get(tr.blob("note_1_99"))); |
| } |
| |
| private NoteMap createLargeNoteMap(String noteNamePrefix, |
| String noteContentPrefix, int notesCount, int firstIndex) |
| throws Exception { |
| NoteMap result = NoteMap.newEmptyMap(); |
| for (int i = 0; i < notesCount; i++) { |
| result.set(tr.blob(noteNamePrefix + (firstIndex + i)), |
| tr.blob(noteContentPrefix + (firstIndex + i))); |
| } |
| result.writeTree(inserter); |
| return result; |
| } |
| |
| @Test |
| public void testFanoutAndLeafWithoutConflict() throws Exception { |
| NoteMapMerger merger = new NoteMapMerger(db, null, null); |
| |
| NoteMap largeTree = createLargeNoteMap("note_1_", "content_1_", 300, 0); |
| NoteMap result = merger.merge(map_a, map_a_b, largeTree); |
| assertEquals(301, countNotes(result)); |
| } |
| |
| @Test |
| public void testFanoutAndLeafWitConflict() throws Exception { |
| NoteMapMerger merger = new NoteMapMerger(db, new DefaultNoteMerger(), |
| null); |
| |
| NoteMap largeTree_b1 = createLargeNoteMap("note_1_", "content_1_", 300, |
| 0); |
| String noteBContent1 = noteBContent + "change"; |
| largeTree_b1.set(noteBId, tr.blob(noteBContent1)); |
| largeTree_b1.writeTree(inserter); |
| |
| NoteMap result = merger.merge(map_a, map_a_b, largeTree_b1); |
| assertEquals(301, countNotes(result)); |
| assertEquals(tr.blob(noteBContent + noteBContent1), result.get(noteBId)); |
| } |
| |
| @Test |
| public void testCollapseFanoutAfterMerge() throws Exception { |
| NoteMapMerger merger = new NoteMapMerger(db, null, null); |
| |
| NoteMap largeTree = createLargeNoteMap("note_", "content_", 257, 0); |
| assertTrue(largeTree.getRoot() instanceof FanoutBucket); |
| NoteMap deleteFirstHundredNotes = createLargeNoteMap("note_", "content_", 157, |
| 100); |
| NoteMap deleteLastHundredNotes = createLargeNoteMap("note_", |
| "content_", 157, 0); |
| NoteMap result = merger.merge(largeTree, deleteFirstHundredNotes, |
| deleteLastHundredNotes); |
| assertEquals(57, countNotes(result)); |
| assertTrue(result.getRoot() instanceof LeafBucket); |
| } |
| |
| @Test |
| public void testNonNotesWithoutNonNoteConflict() throws Exception { |
| NoteMapMerger merger = new NoteMapMerger(db, null, |
| MergeStrategy.RESOLVE); |
| RevCommit treeWithNonNotes = |
| tr.commit() |
| .add(noteAId.name(), noteABlob) // this is a note |
| .add("a.txt", tr.blob("content of a.txt")) // this is a non-note |
| .create(); |
| tr.parseBody(treeWithNonNotes); |
| NoteMap base = NoteMap.read(reader, treeWithNonNotes); |
| |
| treeWithNonNotes = |
| tr.commit() |
| .add(noteAId.name(), noteABlob) |
| .add("a.txt", tr.blob("content of a.txt")) |
| .add("b.txt", tr.blob("content of b.txt")) |
| .create(); |
| tr.parseBody(treeWithNonNotes); |
| NoteMap ours = NoteMap.read(reader, treeWithNonNotes); |
| |
| treeWithNonNotes = |
| tr.commit() |
| .add(noteAId.name(), noteABlob) |
| .add("a.txt", tr.blob("content of a.txt")) |
| .add("c.txt", tr.blob("content of c.txt")) |
| .create(); |
| tr.parseBody(treeWithNonNotes); |
| NoteMap theirs = NoteMap.read(reader, treeWithNonNotes); |
| |
| NoteMap result = merger.merge(base, ours, theirs); |
| assertEquals(3, countNonNotes(result)); |
| } |
| |
| @Test |
| public void testNonNotesWithNonNoteConflict() throws Exception { |
| NoteMapMerger merger = new NoteMapMerger(db, null, |
| MergeStrategy.RESOLVE); |
| RevCommit treeWithNonNotes = |
| tr.commit() |
| .add(noteAId.name(), noteABlob) // this is a note |
| .add("a.txt", tr.blob("content of a.txt")) // this is a non-note |
| .create(); |
| tr.parseBody(treeWithNonNotes); |
| NoteMap base = NoteMap.read(reader, treeWithNonNotes); |
| |
| treeWithNonNotes = |
| tr.commit() |
| .add(noteAId.name(), noteABlob) |
| .add("a.txt", tr.blob("change 1")) |
| .create(); |
| tr.parseBody(treeWithNonNotes); |
| NoteMap ours = NoteMap.read(reader, treeWithNonNotes); |
| |
| treeWithNonNotes = |
| tr.commit() |
| .add(noteAId.name(), noteABlob) |
| .add("a.txt", tr.blob("change 2")) |
| .create(); |
| tr.parseBody(treeWithNonNotes); |
| NoteMap theirs = NoteMap.read(reader, treeWithNonNotes); |
| |
| try { |
| merger.merge(base, ours, theirs); |
| fail("NotesMergeConflictException was expected"); |
| } catch (NotesMergeConflictException e) { |
| // expected |
| } |
| } |
| |
| private static int countNotes(NoteMap map) { |
| int c = 0; |
| Iterator<Note> it = map.iterator(); |
| while (it.hasNext()) { |
| it.next(); |
| c++; |
| } |
| return c; |
| } |
| |
| private static int countNonNotes(NoteMap map) { |
| int c = 0; |
| NonNoteEntry nonNotes = map.getRoot().nonNotes; |
| while (nonNotes != null) { |
| c++; |
| nonNotes = nonNotes.next; |
| } |
| return c; |
| } |
| } |