test_diff_tree.py 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. # test_diff_tree.py -- Tests for file and tree diff utilities.
  2. # Copyright (C) 2010 Google, Inc.
  3. #
  4. # This program is free software; you can redistribute it and/or
  5. # modify it under the terms of the GNU General Public License
  6. # as published by the Free Software Foundation; either version 2
  7. # or (at your option) a later version of the License.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  17. # MA 02110-1301, USA.
  18. """Tests for file and tree diff utilities."""
  19. from dulwich.diff_tree import (
  20. CHANGE_ADD,
  21. CHANGE_MODIFY,
  22. CHANGE_DELETE,
  23. CHANGE_UNCHANGED,
  24. _NULL_ENTRY,
  25. TreeChange,
  26. _merge_entries,
  27. tree_changes,
  28. )
  29. from dulwich.index import (
  30. commit_tree,
  31. )
  32. from dulwich.object_store import (
  33. MemoryObjectStore,
  34. )
  35. from dulwich.objects import (
  36. Blob,
  37. )
  38. from dulwich.tests import (
  39. TestCase,
  40. )
  41. from dulwich.tests.utils import (
  42. make_object,
  43. )
  44. class TreeChangesTest(TestCase):
  45. def setUp(self):
  46. super(TreeChangesTest, self).setUp()
  47. self.store = MemoryObjectStore()
  48. self.empty_tree = self.commit_tree([])
  49. def commit_tree(self, blobs):
  50. commit_blobs = []
  51. for path, blob, mode in blobs:
  52. self.store.add_object(blob)
  53. commit_blobs.append((path, blob.id, mode))
  54. return self.store[commit_tree(self.store, commit_blobs)]
  55. def test_merge_entries(self):
  56. blob_a1 = make_object(Blob, data='a1')
  57. blob_a2 = make_object(Blob, data='a2')
  58. blob_b1 = make_object(Blob, data='b1')
  59. blob_c2 = make_object(Blob, data='c2')
  60. tree1 = self.commit_tree([('a', blob_a1, 0100644),
  61. ('b', blob_b1, 0100755)])
  62. tree2 = self.commit_tree([('a', blob_a2, 0100644),
  63. ('c', blob_c2, 0100755)])
  64. self.assertEqual([], _merge_entries('', self.empty_tree,
  65. self.empty_tree))
  66. self.assertEqual([
  67. ((None, None, None), ('a', 0100644, blob_a1.id)),
  68. ((None, None, None), ('b', 0100755, blob_b1.id)),
  69. ], _merge_entries('', self.empty_tree, tree1))
  70. self.assertEqual([
  71. ((None, None, None), ('x/a', 0100644, blob_a1.id)),
  72. ((None, None, None), ('x/b', 0100755, blob_b1.id)),
  73. ], _merge_entries('x', self.empty_tree, tree1))
  74. self.assertEqual([
  75. (('a', 0100644, blob_a2.id), (None, None, None)),
  76. (('c', 0100755, blob_c2.id), (None, None, None)),
  77. ], _merge_entries('', tree2, self.empty_tree))
  78. self.assertEqual([
  79. (('a', 0100644, blob_a1.id), ('a', 0100644, blob_a2.id)),
  80. (('b', 0100755, blob_b1.id), (None, None, None)),
  81. ((None, None, None), ('c', 0100755, blob_c2.id)),
  82. ], _merge_entries('', tree1, tree2))
  83. self.assertEqual([
  84. (('a', 0100644, blob_a2.id), ('a', 0100644, blob_a1.id)),
  85. ((None, None, None), ('b', 0100755, blob_b1.id)),
  86. (('c', 0100755, blob_c2.id), (None, None, None)),
  87. ], _merge_entries('', tree2, tree1))
  88. def assertChangesEqual(self, expected, tree1, tree2, **kwargs):
  89. actual = list(tree_changes(self.store, tree1.id, tree2.id, **kwargs))
  90. self.assertEqual(expected, actual)
  91. # For brevity, the following tests use tuples instead of TreeEntry objects.
  92. def test_tree_changes_empty(self):
  93. self.assertChangesEqual([], self.empty_tree, self.empty_tree)
  94. def test_tree_changes_no_changes(self):
  95. blob = make_object(Blob, data='blob')
  96. tree = self.commit_tree([('a', blob, 0100644),
  97. ('b/c', blob, 0100644)])
  98. self.assertChangesEqual([], self.empty_tree, self.empty_tree)
  99. self.assertChangesEqual([], tree, tree)
  100. self.assertChangesEqual(
  101. [TreeChange(CHANGE_UNCHANGED, ('a', 0100644, blob.id),
  102. ('a', 0100644, blob.id)),
  103. TreeChange(CHANGE_UNCHANGED, ('b/c', 0100644, blob.id),
  104. ('b/c', 0100644, blob.id))],
  105. tree, tree, want_unchanged=True)
  106. def test_tree_changes_add_delete(self):
  107. blob_a = make_object(Blob, data='a')
  108. blob_b = make_object(Blob, data='b')
  109. tree = self.commit_tree([('a', blob_a, 0100644),
  110. ('x/b', blob_b, 0100755)])
  111. self.assertChangesEqual(
  112. [TreeChange(CHANGE_ADD, _NULL_ENTRY, ('a', 0100644, blob_a.id)),
  113. TreeChange(CHANGE_ADD, _NULL_ENTRY, ('x/b', 0100755, blob_b.id))],
  114. self.empty_tree, tree)
  115. self.assertChangesEqual(
  116. [TreeChange(CHANGE_DELETE, ('a', 0100644, blob_a.id), _NULL_ENTRY),
  117. TreeChange(CHANGE_DELETE, ('x/b', 0100755, blob_b.id), _NULL_ENTRY)],
  118. tree, self.empty_tree)
  119. def test_tree_changes_modify_contents(self):
  120. blob_a1 = make_object(Blob, data='a1')
  121. blob_a2 = make_object(Blob, data='a2')
  122. tree1 = self.commit_tree([('a', blob_a1, 0100644)])
  123. tree2 = self.commit_tree([('a', blob_a2, 0100644)])
  124. self.assertChangesEqual(
  125. [TreeChange(CHANGE_MODIFY, ('a', 0100644, blob_a1.id),
  126. ('a', 0100644, blob_a2.id))], tree1, tree2)
  127. def test_tree_changes_modify_mode(self):
  128. blob_a = make_object(Blob, data='a')
  129. tree1 = self.commit_tree([('a', blob_a, 0100644)])
  130. tree2 = self.commit_tree([('a', blob_a, 0100755)])
  131. self.assertChangesEqual(
  132. [TreeChange(CHANGE_MODIFY, ('a', 0100644, blob_a.id),
  133. ('a', 0100755, blob_a.id))], tree1, tree2)
  134. def test_tree_changes_change_type(self):
  135. blob_a1 = make_object(Blob, data='a')
  136. blob_a2 = make_object(Blob, data='/foo/bar')
  137. tree1 = self.commit_tree([('a', blob_a1, 0100644)])
  138. tree2 = self.commit_tree([('a', blob_a2, 0120000)])
  139. self.assertChangesEqual(
  140. [TreeChange(CHANGE_DELETE, ('a', 0100644, blob_a1.id), _NULL_ENTRY),
  141. TreeChange(CHANGE_ADD, _NULL_ENTRY, ('a', 0120000, blob_a2.id))],
  142. tree1, tree2)
  143. def test_tree_changes_to_tree(self):
  144. blob_a = make_object(Blob, data='a')
  145. blob_x = make_object(Blob, data='x')
  146. tree1 = self.commit_tree([('a', blob_a, 0100644)])
  147. tree2 = self.commit_tree([('a/x', blob_x, 0100644)])
  148. self.assertChangesEqual(
  149. [TreeChange(CHANGE_DELETE, ('a', 0100644, blob_a.id), _NULL_ENTRY),
  150. TreeChange(CHANGE_ADD, _NULL_ENTRY, ('a/x', 0100644, blob_x.id))],
  151. tree1, tree2)
  152. def test_tree_changes_complex(self):
  153. blob_a_1 = make_object(Blob, data='a1_1')
  154. blob_bx1_1 = make_object(Blob, data='bx1_1')
  155. blob_bx2_1 = make_object(Blob, data='bx2_1')
  156. blob_by1_1 = make_object(Blob, data='by1_1')
  157. blob_by2_1 = make_object(Blob, data='by2_1')
  158. tree1 = self.commit_tree([
  159. ('a', blob_a_1, 0100644),
  160. ('b/x/1', blob_bx1_1, 0100644),
  161. ('b/x/2', blob_bx2_1, 0100644),
  162. ('b/y/1', blob_by1_1, 0100644),
  163. ('b/y/2', blob_by2_1, 0100644),
  164. ])
  165. blob_a_2 = make_object(Blob, data='a1_2')
  166. blob_bx1_2 = blob_bx1_1
  167. blob_by_2 = make_object(Blob, data='by_2')
  168. blob_c_2 = make_object(Blob, data='c_2')
  169. tree2 = self.commit_tree([
  170. ('a', blob_a_2, 0100644),
  171. ('b/x/1', blob_bx1_2, 0100644),
  172. ('b/y', blob_by_2, 0100644),
  173. ('c', blob_c_2, 0100644),
  174. ])
  175. self.assertChangesEqual(
  176. [TreeChange(CHANGE_MODIFY, ('a', 0100644, blob_a_1.id),
  177. ('a', 0100644, blob_a_2.id)),
  178. TreeChange(CHANGE_DELETE, ('b/x/2', 0100644, blob_bx2_1.id),
  179. _NULL_ENTRY),
  180. TreeChange(CHANGE_ADD, _NULL_ENTRY, ('b/y', 0100644, blob_by_2.id)),
  181. TreeChange(CHANGE_DELETE, ('b/y/1', 0100644, blob_by1_1.id),
  182. _NULL_ENTRY),
  183. TreeChange(CHANGE_DELETE, ('b/y/2', 0100644, blob_by2_1.id),
  184. _NULL_ENTRY),
  185. TreeChange(CHANGE_ADD, _NULL_ENTRY, ('c', 0100644, blob_c_2.id))],
  186. tree1, tree2)
  187. def test_tree_changes_name_order(self):
  188. blob = make_object(Blob, data='a')
  189. tree1 = self.commit_tree([
  190. ('a', blob, 0100644),
  191. ('a.', blob, 0100644),
  192. ('a..', blob, 0100644),
  193. ])
  194. # Tree order is the reverse of this, so if we used tree order, 'a..'
  195. # would not be merged.
  196. tree2 = self.commit_tree([
  197. ('a/x', blob, 0100644),
  198. ('a./x', blob, 0100644),
  199. ('a..', blob, 0100644),
  200. ])
  201. self.assertChangesEqual(
  202. [TreeChange(CHANGE_DELETE, ('a', 0100644, blob.id), _NULL_ENTRY),
  203. TreeChange(CHANGE_ADD, _NULL_ENTRY, ('a/x', 0100644, blob.id)),
  204. TreeChange(CHANGE_DELETE, ('a.', 0100644, blob.id), _NULL_ENTRY),
  205. TreeChange(CHANGE_ADD, _NULL_ENTRY, ('a./x', 0100644, blob.id))],
  206. tree1, tree2)
  207. def test_tree_changes_prune(self):
  208. blob_a1 = make_object(Blob, data='a1')
  209. blob_a2 = make_object(Blob, data='a2')
  210. blob_x = make_object(Blob, data='x')
  211. tree1 = self.commit_tree([('a', blob_a1, 0100644),
  212. ('b/x', blob_x, 0100644)])
  213. tree2 = self.commit_tree([('a', blob_a2, 0100644),
  214. ('b/x', blob_x, 0100644)])
  215. # Remove identical items so lookups will fail unless we prune.
  216. subtree = self.store[tree1['b'][1]]
  217. for entry in subtree.iteritems():
  218. del self.store[entry.sha]
  219. del self.store[subtree.id]
  220. self.assertChangesEqual(
  221. [TreeChange(CHANGE_MODIFY, ('a', 0100644, blob_a1.id),
  222. ('a', 0100644, blob_a2.id))],
  223. tree1, tree2)