選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

MergeAlgorithmTest.java 8.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. /*
  2. * Copyright (C) 2009, Christian Halstrick <christian.halstrick@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. package org.eclipse.jgit.merge;
  11. import static java.nio.charset.StandardCharsets.UTF_8;
  12. import static org.junit.Assert.assertEquals;
  13. import java.io.ByteArrayOutputStream;
  14. import java.io.IOException;
  15. import org.eclipse.jgit.diff.RawText;
  16. import org.eclipse.jgit.diff.RawTextComparator;
  17. import org.eclipse.jgit.lib.Constants;
  18. import org.junit.Assume;
  19. import org.junit.Test;
  20. import org.junit.experimental.theories.DataPoints;
  21. import org.junit.experimental.theories.Theories;
  22. import org.junit.runner.RunWith;
  23. @RunWith(Theories.class)
  24. public class MergeAlgorithmTest {
  25. MergeFormatter fmt=new MergeFormatter();
  26. private final boolean newlineAtEnd;
  27. @DataPoints
  28. public static boolean[] newlineAtEndDataPoints = { false, true };
  29. public MergeAlgorithmTest(boolean newlineAtEnd) {
  30. this.newlineAtEnd = newlineAtEnd;
  31. }
  32. /**
  33. * Check for a conflict where the second text was changed similar to the
  34. * first one, but the second texts modification covers one more line.
  35. *
  36. * @throws IOException
  37. */
  38. @Test
  39. public void testTwoConflictingModifications() throws IOException {
  40. assertEquals(t("a<b=Z>Zdefghij"),
  41. merge("abcdefghij", "abZdefghij", "aZZdefghij"));
  42. }
  43. /**
  44. * Test a case where we have three consecutive chunks. The first text
  45. * modifies all three chunks. The second text modifies the first and the
  46. * last chunk. This should be reported as one conflicting region.
  47. *
  48. * @throws IOException
  49. */
  50. @Test
  51. public void testOneAgainstTwoConflictingModifications() throws IOException {
  52. assertEquals(t("aZ<Z=c>Zefghij"),
  53. merge("abcdefghij", "aZZZefghij", "aZcZefghij"));
  54. }
  55. /**
  56. * Test a merge where only the second text contains modifications. Expect as
  57. * merge result the second text.
  58. *
  59. * @throws IOException
  60. */
  61. @Test
  62. public void testNoAgainstOneModification() throws IOException {
  63. assertEquals(t("aZcZefghij"),
  64. merge("abcdefghij", "abcdefghij", "aZcZefghij"));
  65. }
  66. /**
  67. * Both texts contain modifications but not on the same chunks. Expect a
  68. * non-conflict merge result.
  69. *
  70. * @throws IOException
  71. */
  72. @Test
  73. public void testTwoNonConflictingModifications() throws IOException {
  74. assertEquals(t("YbZdefghij"),
  75. merge("abcdefghij", "abZdefghij", "Ybcdefghij"));
  76. }
  77. /**
  78. * Merge two complicated modifications. The merge algorithm has to extend
  79. * and combine conflicting regions to get to the expected merge result.
  80. *
  81. * @throws IOException
  82. */
  83. @Test
  84. public void testTwoComplicatedModifications() throws IOException {
  85. assertEquals(t("a<ZZZZfZhZj=bYdYYYYiY>"),
  86. merge("abcdefghij", "aZZZZfZhZj", "abYdYYYYiY"));
  87. }
  88. /**
  89. * Merge two modifications with a shared delete at the end. The underlying
  90. * diff algorithm has to provide consistent edit results to get the expected
  91. * merge result.
  92. *
  93. * @throws IOException
  94. */
  95. @Test
  96. public void testTwoModificationsWithSharedDelete() throws IOException {
  97. assertEquals(t("Cb}n}"),
  98. merge("ab}n}n}", "ab}n}", "Cb}n}"));
  99. }
  100. /**
  101. * Merge modifications with a shared insert in the middle. The
  102. * underlying diff algorithm has to provide consistent edit
  103. * results to get the expected merge result.
  104. *
  105. * @throws IOException
  106. */
  107. @Test
  108. public void testModificationsWithMiddleInsert() throws IOException {
  109. assertEquals(t("aBcd123123uvwxPq"),
  110. merge("abcd123uvwxpq", "aBcd123123uvwxPq", "abcd123123uvwxpq"));
  111. }
  112. /**
  113. * Merge modifications with a shared delete in the middle. The
  114. * underlying diff algorithm has to provide consistent edit
  115. * results to get the expected merge result.
  116. *
  117. * @throws IOException
  118. */
  119. @Test
  120. public void testModificationsWithMiddleDelete() throws IOException {
  121. assertEquals(t("Abz}z123Q"),
  122. merge("abz}z}z123q", "Abz}z123Q", "abz}z123q"));
  123. }
  124. /**
  125. * Test a conflicting region at the very start of the text.
  126. *
  127. * @throws IOException
  128. */
  129. @Test
  130. public void testConflictAtStart() throws IOException {
  131. assertEquals(t("<Z=Y>bcdefghij"),
  132. merge("abcdefghij", "Zbcdefghij", "Ybcdefghij"));
  133. }
  134. /**
  135. * Test a conflicting region at the very end of the text.
  136. *
  137. * @throws IOException
  138. */
  139. @Test
  140. public void testConflictAtEnd() throws IOException {
  141. assertEquals(t("abcdefghi<Z=Y>"),
  142. merge("abcdefghij", "abcdefghiZ", "abcdefghiY"));
  143. }
  144. /**
  145. * Check for a conflict where the second text was changed similar to the
  146. * first one, but the second texts modification covers one more line.
  147. *
  148. * @throws IOException
  149. */
  150. @Test
  151. public void testSameModification() throws IOException {
  152. assertEquals(t("abZdefghij"),
  153. merge("abcdefghij", "abZdefghij", "abZdefghij"));
  154. }
  155. /**
  156. * Check that a deleted vs. a modified line shows up as conflict (see Bug
  157. * 328551)
  158. *
  159. * @throws IOException
  160. */
  161. @Test
  162. public void testDeleteVsModify() throws IOException {
  163. assertEquals(t("ab<=Z>defghij"),
  164. merge("abcdefghij", "abdefghij", "abZdefghij"));
  165. }
  166. @Test
  167. public void testInsertVsModify() throws IOException {
  168. assertEquals(t("a<bZ=XY>"), merge("ab", "abZ", "aXY"));
  169. }
  170. @Test
  171. public void testAdjacentModifications() throws IOException {
  172. assertEquals(t("a<Zc=bY>d"), merge("abcd", "aZcd", "abYd"));
  173. }
  174. @Test
  175. public void testSeparateModifications() throws IOException {
  176. assertEquals(t("aZcYe"), merge("abcde", "aZcde", "abcYe"));
  177. }
  178. @Test
  179. public void testBlankLines() throws IOException {
  180. assertEquals(t("aZc\nYe"), merge("abc\nde", "aZc\nde", "abc\nYe"));
  181. }
  182. /**
  183. * Test merging two contents which do one similar modification and one
  184. * insertion is only done by one side, in the middle. Between modification
  185. * and insertion is a block which is common between the two contents and the
  186. * common base
  187. *
  188. * @throws IOException
  189. */
  190. @Test
  191. public void testTwoSimilarModsAndOneInsert() throws IOException {
  192. assertEquals(t("aBcDde"), merge("abcde", "aBcde", "aBcDde"));
  193. assertEquals(t("IAAAJCAB"), merge("iACAB", "IACAB", "IAAAJCAB"));
  194. assertEquals(t("HIAAAJCAB"), merge("HiACAB", "HIACAB", "HIAAAJCAB"));
  195. assertEquals(t("AGADEFHIAAAJCAB"),
  196. merge("AGADEFHiACAB", "AGADEFHIACAB", "AGADEFHIAAAJCAB"));
  197. }
  198. /**
  199. * Test merging two contents which do one similar modification and one
  200. * insertion is only done by one side, at the end. Between modification and
  201. * insertion is a block which is common between the two contents and the
  202. * common base
  203. *
  204. * @throws IOException
  205. */
  206. @Test
  207. public void testTwoSimilarModsAndOneInsertAtEnd() throws IOException {
  208. Assume.assumeTrue(newlineAtEnd);
  209. assertEquals(t("IAAJ"), merge("iA", "IA", "IAAJ"));
  210. assertEquals(t("IAJ"), merge("iA", "IA", "IAJ"));
  211. assertEquals(t("IAAAJ"), merge("iA", "IA", "IAAAJ"));
  212. }
  213. @Test
  214. public void testTwoSimilarModsAndOneInsertAtEndNoNewlineAtEnd()
  215. throws IOException {
  216. Assume.assumeFalse(newlineAtEnd);
  217. assertEquals(t("I<A=AAJ>"), merge("iA", "IA", "IAAJ"));
  218. assertEquals(t("I<A=AJ>"), merge("iA", "IA", "IAJ"));
  219. assertEquals(t("I<A=AAAJ>"), merge("iA", "IA", "IAAAJ"));
  220. }
  221. /**
  222. * Test situations where (at least) one input value is the empty text
  223. *
  224. * @throws IOException
  225. */
  226. @Test
  227. public void testEmptyTexts() throws IOException {
  228. // test modification against deletion
  229. assertEquals(t("<AB=>"), merge("A", "AB", ""));
  230. assertEquals(t("<=AB>"), merge("A", "", "AB"));
  231. // test unmodified against deletion
  232. assertEquals(t(""), merge("AB", "AB", ""));
  233. assertEquals(t(""), merge("AB", "", "AB"));
  234. // test deletion against deletion
  235. assertEquals(t(""), merge("AB", "", ""));
  236. }
  237. private String merge(String commonBase, String ours, String theirs) throws IOException {
  238. MergeResult r = new MergeAlgorithm().merge(RawTextComparator.DEFAULT,
  239. T(commonBase), T(ours), T(theirs));
  240. ByteArrayOutputStream bo=new ByteArrayOutputStream(50);
  241. fmt.formatMerge(bo, r, "B", "O", "T", UTF_8);
  242. return new String(bo.toByteArray(), UTF_8);
  243. }
  244. public String t(String text) {
  245. StringBuilder r = new StringBuilder();
  246. for (int i = 0; i < text.length(); i++) {
  247. char c = text.charAt(i);
  248. switch (c) {
  249. case '<':
  250. r.append("<<<<<<< O\n");
  251. break;
  252. case '=':
  253. r.append("=======\n");
  254. break;
  255. case '>':
  256. r.append(">>>>>>> T\n");
  257. break;
  258. default:
  259. r.append(c);
  260. if (newlineAtEnd || i < text.length() - 1)
  261. r.append('\n');
  262. }
  263. }
  264. return r.toString();
  265. }
  266. public RawText T(String text) {
  267. return new RawText(Constants.encode(t(text)));
  268. }
  269. }