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 8.9KB

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