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.

MergeAlgorithmTest.java 10KB

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