test_graph.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. # -*- coding: utf-8 -*-
  2. # test_index.py -- Tests for merge
  3. # encoding: utf-8
  4. # Copyright (c) 2020 Kevin B. Hendricks, Stratford Ontario Canada
  5. #
  6. # Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
  7. # General Public License as public by the Free Software Foundation; version 2.0
  8. # or (at your option) any later version. You can redistribute it and/or
  9. # modify it under the terms of either of these two licenses.
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. #
  17. # You should have received a copy of the licenses; if not, see
  18. # <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
  19. # and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
  20. # License, Version 2.0.
  21. """Tests for dulwich.graph."""
  22. from dulwich.tests import TestCase
  23. from dulwich.tests.utils import make_commit
  24. from dulwich.repo import MemoryRepo
  25. from dulwich.graph import _find_lcas, can_fast_forward
  26. class FindMergeBaseTests(TestCase):
  27. @staticmethod
  28. def run_test(dag, inputs):
  29. def lookup_parents(commit_id):
  30. return dag[commit_id]
  31. c1 = inputs[0]
  32. c2s = inputs[1:]
  33. return set(_find_lcas(lookup_parents, c1, c2s))
  34. def test_multiple_lca(self):
  35. # two lowest common ancestors
  36. graph = {
  37. '5': ['1', '2'],
  38. '4': ['3', '1'],
  39. '3': ['2'],
  40. '2': ['0'],
  41. '1': [],
  42. '0': []
  43. }
  44. self.assertEqual(self.run_test(graph, ['4', '5']), set(['1', '2']))
  45. def test_no_common_ancestor(self):
  46. # no common ancestor
  47. graph = {
  48. '4': ['2'],
  49. '3': ['1'],
  50. '2': [],
  51. '1': ['0'],
  52. '0': [],
  53. }
  54. self.assertEqual(self.run_test(graph, ['4', '3']), set([]))
  55. def test_ancestor(self):
  56. # ancestor
  57. graph = {
  58. 'G': ['D', 'F'],
  59. 'F': ['E'],
  60. 'D': ['C'],
  61. 'C': ['B'],
  62. 'E': ['B'],
  63. 'B': ['A'],
  64. 'A': []
  65. }
  66. self.assertEqual(self.run_test(graph, ['D', 'C']), set(['C']))
  67. def test_direct_parent(self):
  68. # parent
  69. graph = {
  70. 'G': ['D', 'F'],
  71. 'F': ['E'],
  72. 'D': ['C'],
  73. 'C': ['B'],
  74. 'E': ['B'],
  75. 'B': ['A'],
  76. 'A': []
  77. }
  78. self.assertEqual(self.run_test(graph, ['G', 'D']), set(['D']))
  79. def test_another_crossover(self):
  80. # Another cross over
  81. graph = {
  82. 'G': ['D', 'F'],
  83. 'F': ['E', 'C'],
  84. 'D': ['C', 'E'],
  85. 'C': ['B'],
  86. 'E': ['B'],
  87. 'B': ['A'],
  88. 'A': []
  89. }
  90. self.assertEqual(self.run_test(graph, ['D', 'F']), set(['E', 'C']))
  91. def test_three_way_merge_lca(self):
  92. # three way merge commit straight from git docs
  93. graph = {
  94. 'C': ['C1'],
  95. 'C1': ['C2'],
  96. 'C2': ['C3'],
  97. 'C3': ['C4'],
  98. 'C4': ['2'],
  99. 'B': ['B1'],
  100. 'B1': ['B2'],
  101. 'B2': ['B3'],
  102. 'B3': ['1'],
  103. 'A': ['A1'],
  104. 'A1': ['A2'],
  105. 'A2': ['A3'],
  106. 'A3': ['1'],
  107. '1': ['2'],
  108. '2': [],
  109. }
  110. # assumes a theoretical merge M exists that merges B and C first
  111. # which actually means find the first LCA from either of B OR C with A
  112. self.assertEqual(self.run_test(graph, ['A', 'B', 'C']), set(['1']))
  113. def test_octopus(self):
  114. # octopus algorithm test
  115. # test straight from git docs of A, B, and C
  116. # but this time use octopus to find lcas of A, B, and C simultaneously
  117. graph = {
  118. 'C': ['C1'],
  119. 'C1': ['C2'],
  120. 'C2': ['C3'],
  121. 'C3': ['C4'],
  122. 'C4': ['2'],
  123. 'B': ['B1'],
  124. 'B1': ['B2'],
  125. 'B2': ['B3'],
  126. 'B3': ['1'],
  127. 'A': ['A1'],
  128. 'A1': ['A2'],
  129. 'A2': ['A3'],
  130. 'A3': ['1'],
  131. '1': ['2'],
  132. '2': [],
  133. }
  134. def lookup_parents(cid):
  135. return graph[cid]
  136. lcas = ['A']
  137. others = ['B', 'C']
  138. for cmt in others:
  139. next_lcas = []
  140. for ca in lcas:
  141. res = _find_lcas(lookup_parents, cmt, [ca])
  142. next_lcas.extend(res)
  143. lcas = next_lcas[:]
  144. self.assertEqual(set(lcas), set(['2']))
  145. class CanFastForwardTests(TestCase):
  146. def test_ff(self):
  147. r = MemoryRepo()
  148. base = make_commit()
  149. c1 = make_commit(parents=[base.id])
  150. c2 = make_commit(parents=[c1.id])
  151. r.object_store.add_objects([(base, None), (c1, None), (c2, None)])
  152. self.assertTrue(can_fast_forward(r, c1.id, c1.id))
  153. self.assertTrue(can_fast_forward(r, base.id, c1.id))
  154. self.assertTrue(can_fast_forward(r, c1.id, c2.id))
  155. self.assertFalse(can_fast_forward(r, c2.id, c1.id))
  156. def test_diverged(self):
  157. r = MemoryRepo()
  158. base = make_commit()
  159. c1 = make_commit(parents=[base.id])
  160. c2a = make_commit(parents=[c1.id], message=b'2a')
  161. c2b = make_commit(parents=[c1.id], message=b'2b')
  162. r.object_store.add_objects(
  163. [(base, None), (c1, None), (c2a, None), (c2b, None)])
  164. self.assertTrue(can_fast_forward(r, c1.id, c2a.id))
  165. self.assertTrue(can_fast_forward(r, c1.id, c2b.id))
  166. self.assertFalse(can_fast_forward(r, c2a.id, c2b.id))
  167. self.assertFalse(can_fast_forward(r, c2b.id, c2a.id))