You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

NoteMapMergerTest.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. /*
  2. * Copyright (C) 2010, Sasa Zivkov <sasa.zivkov@sap.com>
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.notes;
  44. import static org.junit.Assert.assertEquals;
  45. import static org.junit.Assert.assertTrue;
  46. import static org.junit.Assert.fail;
  47. import java.io.IOException;
  48. import java.util.Iterator;
  49. import org.eclipse.jgit.junit.RepositoryTestCase;
  50. import org.eclipse.jgit.junit.TestRepository;
  51. import org.eclipse.jgit.lib.ObjectInserter;
  52. import org.eclipse.jgit.lib.ObjectReader;
  53. import org.eclipse.jgit.lib.Repository;
  54. import org.eclipse.jgit.merge.MergeStrategy;
  55. import org.eclipse.jgit.revwalk.RevBlob;
  56. import org.eclipse.jgit.revwalk.RevCommit;
  57. import org.junit.After;
  58. import org.junit.Before;
  59. import org.junit.Test;
  60. public class NoteMapMergerTest extends RepositoryTestCase {
  61. private TestRepository<Repository> tr;
  62. private ObjectReader reader;
  63. private ObjectInserter inserter;
  64. private NoteMap noRoot;
  65. private NoteMap empty;
  66. private NoteMap map_a;
  67. private NoteMap map_a_b;
  68. private RevBlob noteAId;
  69. private String noteAContent;
  70. private RevBlob noteABlob;
  71. private RevBlob noteBId;
  72. private String noteBContent;
  73. private RevBlob noteBBlob;
  74. private RevCommit sampleTree_a;
  75. private RevCommit sampleTree_a_b;
  76. @Override
  77. @Before
  78. public void setUp() throws Exception {
  79. super.setUp();
  80. tr = new TestRepository<>(db);
  81. reader = db.newObjectReader();
  82. inserter = db.newObjectInserter();
  83. noRoot = NoteMap.newMap(null, reader);
  84. empty = NoteMap.newEmptyMap();
  85. noteAId = tr.blob("a");
  86. noteAContent = "noteAContent";
  87. noteABlob = tr.blob(noteAContent);
  88. sampleTree_a = tr.commit()
  89. .add(noteAId.name(), noteABlob)
  90. .create();
  91. tr.parseBody(sampleTree_a);
  92. map_a = NoteMap.read(reader, sampleTree_a);
  93. noteBId = tr.blob("b");
  94. noteBContent = "noteBContent";
  95. noteBBlob = tr.blob(noteBContent);
  96. sampleTree_a_b = tr.commit()
  97. .add(noteAId.name(), noteABlob)
  98. .add(noteBId.name(), noteBBlob)
  99. .create();
  100. tr.parseBody(sampleTree_a_b);
  101. map_a_b = NoteMap.read(reader, sampleTree_a_b);
  102. }
  103. @Override
  104. @After
  105. public void tearDown() throws Exception {
  106. reader.close();
  107. inserter.close();
  108. super.tearDown();
  109. }
  110. @Test
  111. public void testNoChange() throws IOException {
  112. NoteMapMerger merger = new NoteMapMerger(db, null, null);
  113. NoteMap result;
  114. assertEquals(0, countNotes(merger.merge(noRoot, noRoot, noRoot)));
  115. assertEquals(0, countNotes(merger.merge(empty, empty, empty)));
  116. result = merger.merge(map_a, map_a, map_a);
  117. assertEquals(1, countNotes(result));
  118. assertEquals(noteABlob, result.get(noteAId));
  119. }
  120. @Test
  121. public void testOursEqualsTheirs() throws Exception {
  122. NoteMapMerger merger = new NoteMapMerger(db, null, null);
  123. NoteMap result;
  124. assertEquals(0, countNotes(merger.merge(empty, noRoot, noRoot)));
  125. assertEquals(0, countNotes(merger.merge(map_a, noRoot, noRoot)));
  126. assertEquals(0, countNotes(merger.merge(noRoot, empty, empty)));
  127. assertEquals(0, countNotes(merger.merge(map_a, empty, empty)));
  128. result = merger.merge(noRoot, map_a, map_a);
  129. assertEquals(1, countNotes(result));
  130. assertEquals(noteABlob, result.get(noteAId));
  131. result = merger.merge(empty, map_a, map_a);
  132. assertEquals(1, countNotes(result));
  133. assertEquals(noteABlob, result.get(noteAId));
  134. result = merger.merge(map_a_b, map_a, map_a);
  135. assertEquals(1, countNotes(result));
  136. assertEquals(noteABlob, result.get(noteAId));
  137. result = merger.merge(map_a, map_a_b, map_a_b);
  138. assertEquals(2, countNotes(result));
  139. assertEquals(noteABlob, result.get(noteAId));
  140. assertEquals(noteBBlob, result.get(noteBId));
  141. }
  142. @Test
  143. public void testBaseEqualsOurs() throws Exception {
  144. NoteMapMerger merger = new NoteMapMerger(db, null, null);
  145. NoteMap result;
  146. assertEquals(0, countNotes(merger.merge(noRoot, noRoot, empty)));
  147. result = merger.merge(noRoot, noRoot, map_a);
  148. assertEquals(1, countNotes(result));
  149. assertEquals(noteABlob, result.get(noteAId));
  150. assertEquals(0, countNotes(merger.merge(empty, empty, noRoot)));
  151. result = merger.merge(empty, empty, map_a);
  152. assertEquals(1, countNotes(result));
  153. assertEquals(noteABlob, result.get(noteAId));
  154. assertEquals(0, countNotes(merger.merge(map_a, map_a, noRoot)));
  155. assertEquals(0, countNotes(merger.merge(map_a, map_a, empty)));
  156. result = merger.merge(map_a, map_a, map_a_b);
  157. assertEquals(2, countNotes(result));
  158. assertEquals(noteABlob, result.get(noteAId));
  159. assertEquals(noteBBlob, result.get(noteBId));
  160. }
  161. @Test
  162. public void testBaseEqualsTheirs() throws Exception {
  163. NoteMapMerger merger = new NoteMapMerger(db, null, null);
  164. NoteMap result;
  165. assertEquals(0, countNotes(merger.merge(noRoot, empty, noRoot)));
  166. result = merger.merge(noRoot, map_a, noRoot);
  167. assertEquals(1, countNotes(result));
  168. assertEquals(noteABlob, result.get(noteAId));
  169. assertEquals(0, countNotes(merger.merge(empty, noRoot, empty)));
  170. result = merger.merge(empty, map_a, empty);
  171. assertEquals(1, countNotes(result));
  172. assertEquals(noteABlob, result.get(noteAId));
  173. assertEquals(0, countNotes(merger.merge(map_a, noRoot, map_a)));
  174. assertEquals(0, countNotes(merger.merge(map_a, empty, map_a)));
  175. result = merger.merge(map_a, map_a_b, map_a);
  176. assertEquals(2, countNotes(result));
  177. assertEquals(noteABlob, result.get(noteAId));
  178. assertEquals(noteBBlob, result.get(noteBId));
  179. }
  180. @Test
  181. public void testAddDifferentNotes() throws Exception {
  182. NoteMapMerger merger = new NoteMapMerger(db, null, null);
  183. NoteMap result;
  184. NoteMap map_a_c = NoteMap.read(reader, sampleTree_a);
  185. RevBlob noteCId = tr.blob("c");
  186. RevBlob noteCBlob = tr.blob("noteCContent");
  187. map_a_c.set(noteCId, noteCBlob);
  188. map_a_c.writeTree(inserter);
  189. result = merger.merge(map_a, map_a_b, map_a_c);
  190. assertEquals(3, countNotes(result));
  191. assertEquals(noteABlob, result.get(noteAId));
  192. assertEquals(noteBBlob, result.get(noteBId));
  193. assertEquals(noteCBlob, result.get(noteCId));
  194. }
  195. @Test
  196. public void testAddSameNoteDifferentContent() throws Exception {
  197. NoteMapMerger merger = new NoteMapMerger(db, new DefaultNoteMerger(),
  198. null);
  199. NoteMap result;
  200. NoteMap map_a_b1 = NoteMap.read(reader, sampleTree_a);
  201. String noteBContent1 = noteBContent + "change";
  202. RevBlob noteBBlob1 = tr.blob(noteBContent1);
  203. map_a_b1.set(noteBId, noteBBlob1);
  204. map_a_b1.writeTree(inserter);
  205. result = merger.merge(map_a, map_a_b, map_a_b1);
  206. assertEquals(2, countNotes(result));
  207. assertEquals(noteABlob, result.get(noteAId));
  208. assertEquals(tr.blob(noteBContent + noteBContent1), result.get(noteBId));
  209. }
  210. @Test
  211. public void testEditSameNoteDifferentContent() throws Exception {
  212. NoteMapMerger merger = new NoteMapMerger(db, new DefaultNoteMerger(),
  213. null);
  214. NoteMap result;
  215. NoteMap map_a1 = NoteMap.read(reader, sampleTree_a);
  216. String noteAContent1 = noteAContent + "change1";
  217. RevBlob noteABlob1 = tr.blob(noteAContent1);
  218. map_a1.set(noteAId, noteABlob1);
  219. map_a1.writeTree(inserter);
  220. NoteMap map_a2 = NoteMap.read(reader, sampleTree_a);
  221. String noteAContent2 = noteAContent + "change2";
  222. RevBlob noteABlob2 = tr.blob(noteAContent2);
  223. map_a2.set(noteAId, noteABlob2);
  224. map_a2.writeTree(inserter);
  225. result = merger.merge(map_a, map_a1, map_a2);
  226. assertEquals(1, countNotes(result));
  227. assertEquals(tr.blob(noteAContent1 + noteAContent2),
  228. result.get(noteAId));
  229. }
  230. @Test
  231. public void testEditDifferentNotes() throws Exception {
  232. NoteMapMerger merger = new NoteMapMerger(db, null, null);
  233. NoteMap result;
  234. NoteMap map_a1_b = NoteMap.read(reader, sampleTree_a_b);
  235. String noteAContent1 = noteAContent + "change";
  236. RevBlob noteABlob1 = tr.blob(noteAContent1);
  237. map_a1_b.set(noteAId, noteABlob1);
  238. map_a1_b.writeTree(inserter);
  239. NoteMap map_a_b1 = NoteMap.read(reader, sampleTree_a_b);
  240. String noteBContent1 = noteBContent + "change";
  241. RevBlob noteBBlob1 = tr.blob(noteBContent1);
  242. map_a_b1.set(noteBId, noteBBlob1);
  243. map_a_b1.writeTree(inserter);
  244. result = merger.merge(map_a_b, map_a1_b, map_a_b1);
  245. assertEquals(2, countNotes(result));
  246. assertEquals(noteABlob1, result.get(noteAId));
  247. assertEquals(noteBBlob1, result.get(noteBId));
  248. }
  249. @Test
  250. public void testDeleteDifferentNotes() throws Exception {
  251. NoteMapMerger merger = new NoteMapMerger(db, null, null);
  252. NoteMap map_b = NoteMap.read(reader, sampleTree_a_b);
  253. map_b.set(noteAId, null); // delete note a
  254. map_b.writeTree(inserter);
  255. assertEquals(0, countNotes(merger.merge(map_a_b, map_a, map_b)));
  256. }
  257. @Test
  258. public void testEditDeleteConflict() throws Exception {
  259. NoteMapMerger merger = new NoteMapMerger(db, new DefaultNoteMerger(),
  260. null);
  261. NoteMap result;
  262. NoteMap map_a_b1 = NoteMap.read(reader, sampleTree_a_b);
  263. String noteBContent1 = noteBContent + "change";
  264. RevBlob noteBBlob1 = tr.blob(noteBContent1);
  265. map_a_b1.set(noteBId, noteBBlob1);
  266. map_a_b1.writeTree(inserter);
  267. result = merger.merge(map_a_b, map_a_b1, map_a);
  268. assertEquals(2, countNotes(result));
  269. assertEquals(noteABlob, result.get(noteAId));
  270. assertEquals(noteBBlob1, result.get(noteBId));
  271. }
  272. @Test
  273. public void testLargeTreesWithoutConflict() throws Exception {
  274. NoteMapMerger merger = new NoteMapMerger(db, null, null);
  275. NoteMap map1 = createLargeNoteMap("note_1_", "content_1_", 300, 0);
  276. NoteMap map2 = createLargeNoteMap("note_2_", "content_2_", 300, 0);
  277. NoteMap result = merger.merge(empty, map1, map2);
  278. assertEquals(600, countNotes(result));
  279. // check a few random notes
  280. assertEquals(tr.blob("content_1_59"), result.get(tr.blob("note_1_59")));
  281. assertEquals(tr.blob("content_2_10"), result.get(tr.blob("note_2_10")));
  282. assertEquals(tr.blob("content_2_99"), result.get(tr.blob("note_2_99")));
  283. }
  284. @Test
  285. public void testLargeTreesWithConflict() throws Exception {
  286. NoteMapMerger merger = new NoteMapMerger(db, new DefaultNoteMerger(),
  287. null);
  288. NoteMap largeTree1 = createLargeNoteMap("note_1_", "content_1_", 300, 0);
  289. NoteMap largeTree2 = createLargeNoteMap("note_1_", "content_2_", 300, 0);
  290. NoteMap result = merger.merge(empty, largeTree1, largeTree2);
  291. assertEquals(300, countNotes(result));
  292. // check a few random notes
  293. assertEquals(tr.blob("content_1_59content_2_59"),
  294. result.get(tr.blob("note_1_59")));
  295. assertEquals(tr.blob("content_1_10content_2_10"),
  296. result.get(tr.blob("note_1_10")));
  297. assertEquals(tr.blob("content_1_99content_2_99"),
  298. result.get(tr.blob("note_1_99")));
  299. }
  300. private NoteMap createLargeNoteMap(String noteNamePrefix,
  301. String noteContentPrefix, int notesCount, int firstIndex)
  302. throws Exception {
  303. NoteMap result = NoteMap.newEmptyMap();
  304. for (int i = 0; i < notesCount; i++) {
  305. result.set(tr.blob(noteNamePrefix + (firstIndex + i)),
  306. tr.blob(noteContentPrefix + (firstIndex + i)));
  307. }
  308. result.writeTree(inserter);
  309. return result;
  310. }
  311. @Test
  312. public void testFanoutAndLeafWithoutConflict() throws Exception {
  313. NoteMapMerger merger = new NoteMapMerger(db, null, null);
  314. NoteMap largeTree = createLargeNoteMap("note_1_", "content_1_", 300, 0);
  315. NoteMap result = merger.merge(map_a, map_a_b, largeTree);
  316. assertEquals(301, countNotes(result));
  317. }
  318. @Test
  319. public void testFanoutAndLeafWitConflict() throws Exception {
  320. NoteMapMerger merger = new NoteMapMerger(db, new DefaultNoteMerger(),
  321. null);
  322. NoteMap largeTree_b1 = createLargeNoteMap("note_1_", "content_1_", 300,
  323. 0);
  324. String noteBContent1 = noteBContent + "change";
  325. largeTree_b1.set(noteBId, tr.blob(noteBContent1));
  326. largeTree_b1.writeTree(inserter);
  327. NoteMap result = merger.merge(map_a, map_a_b, largeTree_b1);
  328. assertEquals(301, countNotes(result));
  329. assertEquals(tr.blob(noteBContent + noteBContent1), result.get(noteBId));
  330. }
  331. @Test
  332. public void testCollapseFanoutAfterMerge() throws Exception {
  333. NoteMapMerger merger = new NoteMapMerger(db, null, null);
  334. NoteMap largeTree = createLargeNoteMap("note_", "content_", 257, 0);
  335. assertTrue(largeTree.getRoot() instanceof FanoutBucket);
  336. NoteMap deleteFirstHundredNotes = createLargeNoteMap("note_", "content_", 157,
  337. 100);
  338. NoteMap deleteLastHundredNotes = createLargeNoteMap("note_",
  339. "content_", 157, 0);
  340. NoteMap result = merger.merge(largeTree, deleteFirstHundredNotes,
  341. deleteLastHundredNotes);
  342. assertEquals(57, countNotes(result));
  343. assertTrue(result.getRoot() instanceof LeafBucket);
  344. }
  345. @Test
  346. public void testNonNotesWithoutNonNoteConflict() throws Exception {
  347. NoteMapMerger merger = new NoteMapMerger(db, null,
  348. MergeStrategy.RESOLVE);
  349. RevCommit treeWithNonNotes =
  350. tr.commit()
  351. .add(noteAId.name(), noteABlob) // this is a note
  352. .add("a.txt", tr.blob("content of a.txt")) // this is a non-note
  353. .create();
  354. tr.parseBody(treeWithNonNotes);
  355. NoteMap base = NoteMap.read(reader, treeWithNonNotes);
  356. treeWithNonNotes =
  357. tr.commit()
  358. .add(noteAId.name(), noteABlob)
  359. .add("a.txt", tr.blob("content of a.txt"))
  360. .add("b.txt", tr.blob("content of b.txt"))
  361. .create();
  362. tr.parseBody(treeWithNonNotes);
  363. NoteMap ours = NoteMap.read(reader, treeWithNonNotes);
  364. treeWithNonNotes =
  365. tr.commit()
  366. .add(noteAId.name(), noteABlob)
  367. .add("a.txt", tr.blob("content of a.txt"))
  368. .add("c.txt", tr.blob("content of c.txt"))
  369. .create();
  370. tr.parseBody(treeWithNonNotes);
  371. NoteMap theirs = NoteMap.read(reader, treeWithNonNotes);
  372. NoteMap result = merger.merge(base, ours, theirs);
  373. assertEquals(3, countNonNotes(result));
  374. }
  375. @Test
  376. public void testNonNotesWithNonNoteConflict() throws Exception {
  377. NoteMapMerger merger = new NoteMapMerger(db, null,
  378. MergeStrategy.RESOLVE);
  379. RevCommit treeWithNonNotes =
  380. tr.commit()
  381. .add(noteAId.name(), noteABlob) // this is a note
  382. .add("a.txt", tr.blob("content of a.txt")) // this is a non-note
  383. .create();
  384. tr.parseBody(treeWithNonNotes);
  385. NoteMap base = NoteMap.read(reader, treeWithNonNotes);
  386. treeWithNonNotes =
  387. tr.commit()
  388. .add(noteAId.name(), noteABlob)
  389. .add("a.txt", tr.blob("change 1"))
  390. .create();
  391. tr.parseBody(treeWithNonNotes);
  392. NoteMap ours = NoteMap.read(reader, treeWithNonNotes);
  393. treeWithNonNotes =
  394. tr.commit()
  395. .add(noteAId.name(), noteABlob)
  396. .add("a.txt", tr.blob("change 2"))
  397. .create();
  398. tr.parseBody(treeWithNonNotes);
  399. NoteMap theirs = NoteMap.read(reader, treeWithNonNotes);
  400. try {
  401. merger.merge(base, ours, theirs);
  402. fail("NotesMergeConflictException was expected");
  403. } catch (NotesMergeConflictException e) {
  404. // expected
  405. }
  406. }
  407. private static int countNotes(NoteMap map) {
  408. int c = 0;
  409. Iterator<Note> it = map.iterator();
  410. while (it.hasNext()) {
  411. it.next();
  412. c++;
  413. }
  414. return c;
  415. }
  416. private static int countNonNotes(NoteMap map) {
  417. int c = 0;
  418. NonNoteEntry nonNotes = map.getRoot().nonNotes;
  419. while (nonNotes != null) {
  420. c++;
  421. nonNotes = nonNotes.next;
  422. }
  423. return c;
  424. }
  425. }