View Javadoc
1   /*
2    * Copyright (C) 2010, Sasa Zivkov <sasa.zivkov@sap.com> and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  
11  package org.eclipse.jgit.notes;
12  
13  import static org.junit.Assert.assertEquals;
14  import static org.junit.Assert.assertTrue;
15  import static org.junit.Assert.fail;
16  
17  import java.io.IOException;
18  import java.util.Iterator;
19  
20  import org.eclipse.jgit.junit.RepositoryTestCase;
21  import org.eclipse.jgit.junit.TestRepository;
22  import org.eclipse.jgit.lib.ObjectInserter;
23  import org.eclipse.jgit.lib.ObjectReader;
24  import org.eclipse.jgit.lib.Repository;
25  import org.eclipse.jgit.merge.MergeStrategy;
26  import org.eclipse.jgit.revwalk.RevBlob;
27  import org.eclipse.jgit.revwalk.RevCommit;
28  import org.junit.After;
29  import org.junit.Before;
30  import org.junit.Test;
31  
32  public class NoteMapMergerTest extends RepositoryTestCase {
33  	private TestRepository<Repository> tr;
34  
35  	private ObjectReader reader;
36  
37  	private ObjectInserter inserter;
38  
39  	private NoteMap noRoot;
40  
41  	private NoteMap empty;
42  
43  	private NoteMap map_a;
44  
45  	private NoteMap map_a_b;
46  
47  	private RevBlob noteAId;
48  
49  	private String noteAContent;
50  
51  	private RevBlob noteABlob;
52  
53  	private RevBlob noteBId;
54  
55  	private String noteBContent;
56  
57  	private RevBlob noteBBlob;
58  
59  	private RevCommit sampleTree_a;
60  
61  	private RevCommit sampleTree_a_b;
62  
63  	@Override
64  	@Before
65  	public void setUp() throws Exception {
66  		super.setUp();
67  		tr = new TestRepository<>(db);
68  		reader = db.newObjectReader();
69  		inserter = db.newObjectInserter();
70  
71  		noRoot = NoteMap.newMap(null, reader);
72  		empty = NoteMap.newEmptyMap();
73  
74  		noteAId = tr.blob("a");
75  		noteAContent = "noteAContent";
76  		noteABlob = tr.blob(noteAContent);
77  		sampleTree_a = tr.commit()
78  				.add(noteAId.name(), noteABlob)
79  				.create();
80  		tr.parseBody(sampleTree_a);
81  		map_a = NoteMap.read(reader, sampleTree_a);
82  
83  		noteBId = tr.blob("b");
84  		noteBContent = "noteBContent";
85  		noteBBlob = tr.blob(noteBContent);
86  		sampleTree_a_b = tr.commit()
87  				.add(noteAId.name(), noteABlob)
88  				.add(noteBId.name(), noteBBlob)
89  				.create();
90  		tr.parseBody(sampleTree_a_b);
91  		map_a_b = NoteMap.read(reader, sampleTree_a_b);
92  	}
93  
94  	@Override
95  	@After
96  	public void tearDown() throws Exception {
97  		reader.close();
98  		inserter.close();
99  		super.tearDown();
100 	}
101 
102 	@Test
103 	public void testNoChange() throws IOException {
104 		NoteMapMerger merger = new NoteMapMerger(db, null, null);
105 		NoteMap result;
106 
107 		assertEquals(0, countNotes(merger.merge(noRoot, noRoot, noRoot)));
108 		assertEquals(0, countNotes(merger.merge(empty, empty, empty)));
109 
110 		result = merger.merge(map_a, map_a, map_a);
111 		assertEquals(1, countNotes(result));
112 		assertEquals(noteABlob, result.get(noteAId));
113 	}
114 
115 	@Test
116 	public void testOursEqualsTheirs() throws Exception {
117 		NoteMapMerger merger = new NoteMapMerger(db, null, null);
118 		NoteMap result;
119 
120 		assertEquals(0, countNotes(merger.merge(empty, noRoot, noRoot)));
121 		assertEquals(0, countNotes(merger.merge(map_a, noRoot, noRoot)));
122 
123 		assertEquals(0, countNotes(merger.merge(noRoot, empty, empty)));
124 		assertEquals(0, countNotes(merger.merge(map_a, empty, empty)));
125 
126 		result = merger.merge(noRoot, map_a, map_a);
127 		assertEquals(1, countNotes(result));
128 		assertEquals(noteABlob, result.get(noteAId));
129 
130 		result = merger.merge(empty, map_a, map_a);
131 		assertEquals(1, countNotes(result));
132 		assertEquals(noteABlob, result.get(noteAId));
133 
134 		result = merger.merge(map_a_b, map_a, map_a);
135 		assertEquals(1, countNotes(result));
136 		assertEquals(noteABlob, result.get(noteAId));
137 
138 		result = merger.merge(map_a, map_a_b, map_a_b);
139 		assertEquals(2, countNotes(result));
140 		assertEquals(noteABlob, result.get(noteAId));
141 		assertEquals(noteBBlob, result.get(noteBId));
142 	}
143 
144 	@Test
145 	public void testBaseEqualsOurs() throws Exception {
146 		NoteMapMerger merger = new NoteMapMerger(db, null, null);
147 		NoteMap result;
148 
149 		assertEquals(0, countNotes(merger.merge(noRoot, noRoot, empty)));
150 		result = merger.merge(noRoot, noRoot, map_a);
151 		assertEquals(1, countNotes(result));
152 		assertEquals(noteABlob, result.get(noteAId));
153 
154 		assertEquals(0, countNotes(merger.merge(empty, empty, noRoot)));
155 		result = merger.merge(empty, empty, map_a);
156 		assertEquals(1, countNotes(result));
157 		assertEquals(noteABlob, result.get(noteAId));
158 
159 		assertEquals(0, countNotes(merger.merge(map_a, map_a, noRoot)));
160 		assertEquals(0, countNotes(merger.merge(map_a, map_a, empty)));
161 		result = merger.merge(map_a, map_a, map_a_b);
162 		assertEquals(2, countNotes(result));
163 		assertEquals(noteABlob, result.get(noteAId));
164 		assertEquals(noteBBlob, result.get(noteBId));
165 	}
166 
167 	@Test
168 	public void testBaseEqualsTheirs() throws Exception {
169 		NoteMapMerger merger = new NoteMapMerger(db, null, null);
170 		NoteMap result;
171 
172 		assertEquals(0, countNotes(merger.merge(noRoot, empty, noRoot)));
173 		result = merger.merge(noRoot, map_a, noRoot);
174 		assertEquals(1, countNotes(result));
175 		assertEquals(noteABlob, result.get(noteAId));
176 
177 		assertEquals(0, countNotes(merger.merge(empty, noRoot, empty)));
178 		result = merger.merge(empty, map_a, empty);
179 		assertEquals(1, countNotes(result));
180 		assertEquals(noteABlob, result.get(noteAId));
181 
182 		assertEquals(0, countNotes(merger.merge(map_a, noRoot, map_a)));
183 		assertEquals(0, countNotes(merger.merge(map_a, empty, map_a)));
184 		result = merger.merge(map_a, map_a_b, map_a);
185 		assertEquals(2, countNotes(result));
186 		assertEquals(noteABlob, result.get(noteAId));
187 		assertEquals(noteBBlob, result.get(noteBId));
188 	}
189 
190 	@Test
191 	public void testAddDifferentNotes() throws Exception {
192 		NoteMapMerger merger = new NoteMapMerger(db, null, null);
193 		NoteMap result;
194 
195 		NoteMap map_a_c = NoteMap.read(reader, sampleTree_a);
196 		RevBlob noteCId = tr.blob("c");
197 		RevBlob noteCBlob = tr.blob("noteCContent");
198 		map_a_c.set(noteCId, noteCBlob);
199 		map_a_c.writeTree(inserter);
200 
201 		result = merger.merge(map_a, map_a_b, map_a_c);
202 		assertEquals(3, countNotes(result));
203 		assertEquals(noteABlob, result.get(noteAId));
204 		assertEquals(noteBBlob, result.get(noteBId));
205 		assertEquals(noteCBlob, result.get(noteCId));
206 	}
207 
208 	@Test
209 	public void testAddSameNoteDifferentContent() throws Exception {
210 		NoteMapMerger merger = new NoteMapMerger(db, new DefaultNoteMerger(),
211 				null);
212 		NoteMap result;
213 
214 		NoteMap map_a_b1 = NoteMap.read(reader, sampleTree_a);
215 		String noteBContent1 = noteBContent + "change";
216 		RevBlob noteBBlob1 = tr.blob(noteBContent1);
217 		map_a_b1.set(noteBId, noteBBlob1);
218 		map_a_b1.writeTree(inserter);
219 
220 		result = merger.merge(map_a, map_a_b, map_a_b1);
221 		assertEquals(2, countNotes(result));
222 		assertEquals(noteABlob, result.get(noteAId));
223 		assertEquals(tr.blob(noteBContent + noteBContent1), result.get(noteBId));
224 	}
225 
226 	@Test
227 	public void testEditSameNoteDifferentContent() throws Exception {
228 		NoteMapMerger merger = new NoteMapMerger(db, new DefaultNoteMerger(),
229 				null);
230 		NoteMap result;
231 
232 		NoteMap map_a1 = NoteMap.read(reader, sampleTree_a);
233 		String noteAContent1 = noteAContent + "change1";
234 		RevBlob noteABlob1 = tr.blob(noteAContent1);
235 		map_a1.set(noteAId, noteABlob1);
236 		map_a1.writeTree(inserter);
237 
238 		NoteMap map_a2 = NoteMap.read(reader, sampleTree_a);
239 		String noteAContent2 = noteAContent + "change2";
240 		RevBlob noteABlob2 = tr.blob(noteAContent2);
241 		map_a2.set(noteAId, noteABlob2);
242 		map_a2.writeTree(inserter);
243 
244 		result = merger.merge(map_a, map_a1, map_a2);
245 		assertEquals(1, countNotes(result));
246 		assertEquals(tr.blob(noteAContent1 + noteAContent2),
247 				result.get(noteAId));
248 	}
249 
250 	@Test
251 	public void testEditDifferentNotes() throws Exception {
252 		NoteMapMerger merger = new NoteMapMerger(db, null, null);
253 		NoteMap result;
254 
255 		NoteMap map_a1_b = NoteMap.read(reader, sampleTree_a_b);
256 		String noteAContent1 = noteAContent + "change";
257 		RevBlob noteABlob1 = tr.blob(noteAContent1);
258 		map_a1_b.set(noteAId, noteABlob1);
259 		map_a1_b.writeTree(inserter);
260 
261 		NoteMap map_a_b1 = NoteMap.read(reader, sampleTree_a_b);
262 		String noteBContent1 = noteBContent + "change";
263 		RevBlob noteBBlob1 = tr.blob(noteBContent1);
264 		map_a_b1.set(noteBId, noteBBlob1);
265 		map_a_b1.writeTree(inserter);
266 
267 		result = merger.merge(map_a_b, map_a1_b, map_a_b1);
268 		assertEquals(2, countNotes(result));
269 		assertEquals(noteABlob1, result.get(noteAId));
270 		assertEquals(noteBBlob1, result.get(noteBId));
271 	}
272 
273 	@Test
274 	public void testDeleteDifferentNotes() throws Exception {
275 		NoteMapMerger merger = new NoteMapMerger(db, null, null);
276 
277 		NoteMap map_b = NoteMap.read(reader, sampleTree_a_b);
278 		map_b.set(noteAId, null); // delete note a
279 		map_b.writeTree(inserter);
280 
281 		assertEquals(0, countNotes(merger.merge(map_a_b, map_a, map_b)));
282 	}
283 
284 	@Test
285 	public void testEditDeleteConflict() throws Exception {
286 		NoteMapMerger merger = new NoteMapMerger(db, new DefaultNoteMerger(),
287 				null);
288 		NoteMap result;
289 
290 		NoteMap map_a_b1 = NoteMap.read(reader, sampleTree_a_b);
291 		String noteBContent1 = noteBContent + "change";
292 		RevBlob noteBBlob1 = tr.blob(noteBContent1);
293 		map_a_b1.set(noteBId, noteBBlob1);
294 		map_a_b1.writeTree(inserter);
295 
296 		result = merger.merge(map_a_b, map_a_b1, map_a);
297 		assertEquals(2, countNotes(result));
298 		assertEquals(noteABlob, result.get(noteAId));
299 		assertEquals(noteBBlob1, result.get(noteBId));
300 	}
301 
302 	@Test
303 	public void testLargeTreesWithoutConflict() throws Exception {
304 		NoteMapMerger merger = new NoteMapMerger(db, null, null);
305 		NoteMap map1 = createLargeNoteMap("note_1_", "content_1_", 300, 0);
306 		NoteMap map2 = createLargeNoteMap("note_2_", "content_2_", 300, 0);
307 
308 		NoteMap result = merger.merge(empty, map1, map2);
309 		assertEquals(600, countNotes(result));
310 		// check a few random notes
311 		assertEquals(tr.blob("content_1_59"), result.get(tr.blob("note_1_59")));
312 		assertEquals(tr.blob("content_2_10"), result.get(tr.blob("note_2_10")));
313 		assertEquals(tr.blob("content_2_99"), result.get(tr.blob("note_2_99")));
314 	}
315 
316 	@Test
317 	public void testLargeTreesWithConflict() throws Exception {
318 		NoteMapMerger merger = new NoteMapMerger(db, new DefaultNoteMerger(),
319 				null);
320 		NoteMap largeTree1 = createLargeNoteMap("note_1_", "content_1_", 300, 0);
321 		NoteMap largeTree2 = createLargeNoteMap("note_1_", "content_2_", 300, 0);
322 
323 		NoteMap result = merger.merge(empty, largeTree1, largeTree2);
324 		assertEquals(300, countNotes(result));
325 		// check a few random notes
326 		assertEquals(tr.blob("content_1_59content_2_59"),
327 				result.get(tr.blob("note_1_59")));
328 		assertEquals(tr.blob("content_1_10content_2_10"),
329 				result.get(tr.blob("note_1_10")));
330 		assertEquals(tr.blob("content_1_99content_2_99"),
331 				result.get(tr.blob("note_1_99")));
332 	}
333 
334 	private NoteMap createLargeNoteMap(String noteNamePrefix,
335 			String noteContentPrefix, int notesCount, int firstIndex)
336 			throws Exception {
337 		NoteMap result = NoteMap.newEmptyMap();
338 		for (int i = 0; i < notesCount; i++) {
339 			result.set(tr.blob(noteNamePrefix + (firstIndex + i)),
340 					tr.blob(noteContentPrefix + (firstIndex + i)));
341 		}
342 		result.writeTree(inserter);
343 		return result;
344 	}
345 
346 	@Test
347 	public void testFanoutAndLeafWithoutConflict() throws Exception {
348 		NoteMapMerger merger = new NoteMapMerger(db, null, null);
349 
350 		NoteMap largeTree = createLargeNoteMap("note_1_", "content_1_", 300, 0);
351 		NoteMap result = merger.merge(map_a, map_a_b, largeTree);
352 		assertEquals(301, countNotes(result));
353 	}
354 
355 	@Test
356 	public void testFanoutAndLeafWitConflict() throws Exception {
357 		NoteMapMerger merger = new NoteMapMerger(db, new DefaultNoteMerger(),
358 				null);
359 
360 		NoteMap largeTree_b1 = createLargeNoteMap("note_1_", "content_1_", 300,
361 				0);
362 		String noteBContent1 = noteBContent + "change";
363 		largeTree_b1.set(noteBId, tr.blob(noteBContent1));
364 		largeTree_b1.writeTree(inserter);
365 
366 		NoteMap result = merger.merge(map_a, map_a_b, largeTree_b1);
367 		assertEquals(301, countNotes(result));
368 		assertEquals(tr.blob(noteBContent + noteBContent1), result.get(noteBId));
369 	}
370 
371 	@Test
372 	public void testCollapseFanoutAfterMerge() throws Exception {
373 		NoteMapMerger merger = new NoteMapMerger(db, null, null);
374 
375 		NoteMap largeTree = createLargeNoteMap("note_", "content_", 257, 0);
376 		assertTrue(largeTree.getRoot() instanceof FanoutBucket);
377 		NoteMap deleteFirstHundredNotes = createLargeNoteMap("note_", "content_", 157,
378 				100);
379 		NoteMap deleteLastHundredNotes = createLargeNoteMap("note_",
380 				"content_", 157, 0);
381 		NoteMap result = merger.merge(largeTree, deleteFirstHundredNotes,
382 				deleteLastHundredNotes);
383 		assertEquals(57, countNotes(result));
384 		assertTrue(result.getRoot() instanceof LeafBucket);
385 	}
386 
387 	@Test
388 	public void testNonNotesWithoutNonNoteConflict() throws Exception {
389 		NoteMapMerger merger = new NoteMapMerger(db, null,
390 				MergeStrategy.RESOLVE);
391 		RevCommit treeWithNonNotes =
392 			tr.commit()
393 				.add(noteAId.name(), noteABlob) // this is a note
394 				.add("a.txt", tr.blob("content of a.txt")) // this is a non-note
395 				.create();
396 		tr.parseBody(treeWithNonNotes);
397 		NoteMap base = NoteMap.read(reader, treeWithNonNotes);
398 
399 		treeWithNonNotes =
400 			tr.commit()
401 				.add(noteAId.name(), noteABlob)
402 				.add("a.txt", tr.blob("content of a.txt"))
403 				.add("b.txt", tr.blob("content of b.txt"))
404 				.create();
405 		tr.parseBody(treeWithNonNotes);
406 		NoteMap ours = NoteMap.read(reader, treeWithNonNotes);
407 
408 		treeWithNonNotes =
409 			tr.commit()
410 				.add(noteAId.name(), noteABlob)
411 				.add("a.txt", tr.blob("content of a.txt"))
412 				.add("c.txt", tr.blob("content of c.txt"))
413 				.create();
414 		tr.parseBody(treeWithNonNotes);
415 		NoteMap theirs = NoteMap.read(reader, treeWithNonNotes);
416 
417 		NoteMap result = merger.merge(base, ours, theirs);
418 		assertEquals(3, countNonNotes(result));
419 	}
420 
421 	@Test
422 	public void testNonNotesWithNonNoteConflict() throws Exception {
423 		NoteMapMerger merger = new NoteMapMerger(db, null,
424 				MergeStrategy.RESOLVE);
425 		RevCommit treeWithNonNotes =
426 			tr.commit()
427 				.add(noteAId.name(), noteABlob) // this is a note
428 				.add("a.txt", tr.blob("content of a.txt")) // this is a non-note
429 				.create();
430 		tr.parseBody(treeWithNonNotes);
431 		NoteMap base = NoteMap.read(reader, treeWithNonNotes);
432 
433 		treeWithNonNotes =
434 			tr.commit()
435 				.add(noteAId.name(), noteABlob)
436 				.add("a.txt", tr.blob("change 1"))
437 				.create();
438 		tr.parseBody(treeWithNonNotes);
439 		NoteMap ours = NoteMap.read(reader, treeWithNonNotes);
440 
441 		treeWithNonNotes =
442 			tr.commit()
443 				.add(noteAId.name(), noteABlob)
444 				.add("a.txt", tr.blob("change 2"))
445 				.create();
446 		tr.parseBody(treeWithNonNotes);
447 		NoteMap theirs = NoteMap.read(reader, treeWithNonNotes);
448 
449 		try {
450 			merger.merge(base, ours, theirs);
451 			fail("NotesMergeConflictException was expected");
452 		} catch (NotesMergeConflictException e) {
453 			// expected
454 		}
455 	}
456 
457 	private static int countNotes(NoteMap map) {
458 		int c = 0;
459 		Iterator<Note> it = map.iterator();
460 		while (it.hasNext()) {
461 			it.next();
462 			c++;
463 		}
464 		return c;
465 	}
466 
467 	private static int countNonNotes(NoteMap map) {
468 		int c = 0;
469 		NonNoteEntry nonNotes = map.getRoot().nonNotes;
470 		while (nonNotes != null) {
471 			c++;
472 			nonNotes = nonNotes.next;
473 		}
474 		return c;
475 	}
476 }