|
@@ -0,0 +1,230 @@
|
|
|
+# test_diff_tree.py -- Tests for file and tree diff utilities.
|
|
|
+# Copyright (C) 2010 Google, Inc.
|
|
|
+#
|
|
|
+# This program is free software; you can redistribute it and/or
|
|
|
+# modify it under the terms of the GNU General Public License
|
|
|
+# as published by the Free Software Foundation; either version 2
|
|
|
+# or (at your option) a later version of the License.
|
|
|
+#
|
|
|
+# This program is distributed in the hope that it will be useful,
|
|
|
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+# GNU General Public License for more details.
|
|
|
+#
|
|
|
+# You should have received a copy of the GNU General Public License
|
|
|
+# along with this program; if not, write to the Free Software
|
|
|
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
|
+# MA 02110-1301, USA.
|
|
|
+
|
|
|
+"""Tests for file and tree diff utilities."""
|
|
|
+
|
|
|
+from dulwich.diff_tree import (
|
|
|
+ CHANGE_ADD,
|
|
|
+ CHANGE_MODIFY,
|
|
|
+ CHANGE_DELETE,
|
|
|
+ CHANGE_UNCHANGED,
|
|
|
+ _NULL_ENTRY,
|
|
|
+ TreeChange,
|
|
|
+ _merge_entries,
|
|
|
+ tree_changes,
|
|
|
+ )
|
|
|
+from dulwich.index import (
|
|
|
+ commit_tree,
|
|
|
+ )
|
|
|
+from dulwich.object_store import (
|
|
|
+ MemoryObjectStore,
|
|
|
+ )
|
|
|
+from dulwich.objects import (
|
|
|
+ Blob,
|
|
|
+ )
|
|
|
+from dulwich.tests import (
|
|
|
+ TestCase,
|
|
|
+ )
|
|
|
+from dulwich.tests.utils import (
|
|
|
+ make_object,
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+class TreeChangesTest(TestCase):
|
|
|
+
|
|
|
+ def setUp(self):
|
|
|
+ self.store = MemoryObjectStore()
|
|
|
+ self.empty_tree = self.commit_tree([])
|
|
|
+
|
|
|
+ def commit_tree(self, blobs):
|
|
|
+ commit_blobs = []
|
|
|
+ for path, blob, mode in blobs:
|
|
|
+ self.store.add_object(blob)
|
|
|
+ commit_blobs.append((path, blob.id, mode))
|
|
|
+ return self.store[commit_tree(self.store, commit_blobs)]
|
|
|
+
|
|
|
+ def test_merge_entries(self):
|
|
|
+ blob_a1 = make_object(Blob, data='a1')
|
|
|
+ blob_a2 = make_object(Blob, data='a2')
|
|
|
+ blob_b1 = make_object(Blob, data='b1')
|
|
|
+ blob_c2 = make_object(Blob, data='c2')
|
|
|
+ tree1 = self.commit_tree([('a', blob_a1, 0100644),
|
|
|
+ ('b', blob_b1, 0100755)])
|
|
|
+ tree2 = self.commit_tree([('a', blob_a2, 0100644),
|
|
|
+ ('c', blob_c2, 0100755)])
|
|
|
+
|
|
|
+ self.assertEqual([], _merge_entries('', self.empty_tree,
|
|
|
+ self.empty_tree))
|
|
|
+ self.assertEqual([
|
|
|
+ ((None, None, None), ('a', 0100644, blob_a1.id)),
|
|
|
+ ((None, None, None), ('b', 0100755, blob_b1.id)),
|
|
|
+ ], _merge_entries('', self.empty_tree, tree1))
|
|
|
+ self.assertEqual([
|
|
|
+ ((None, None, None), ('x/a', 0100644, blob_a1.id)),
|
|
|
+ ((None, None, None), ('x/b', 0100755, blob_b1.id)),
|
|
|
+ ], _merge_entries('x', self.empty_tree, tree1))
|
|
|
+
|
|
|
+ self.assertEqual([
|
|
|
+ (('a', 0100644, blob_a2.id), (None, None, None)),
|
|
|
+ (('c', 0100755, blob_c2.id), (None, None, None)),
|
|
|
+ ], _merge_entries('', tree2, self.empty_tree))
|
|
|
+
|
|
|
+ self.assertEqual([
|
|
|
+ (('a', 0100644, blob_a1.id), ('a', 0100644, blob_a2.id)),
|
|
|
+ (('b', 0100755, blob_b1.id), (None, None, None)),
|
|
|
+ ((None, None, None), ('c', 0100755, blob_c2.id)),
|
|
|
+ ], _merge_entries('', tree1, tree2))
|
|
|
+
|
|
|
+ self.assertEqual([
|
|
|
+ (('a', 0100644, blob_a2.id), ('a', 0100644, blob_a1.id)),
|
|
|
+ ((None, None, None), ('b', 0100755, blob_b1.id)),
|
|
|
+ (('c', 0100755, blob_c2.id), (None, None, None)),
|
|
|
+ ], _merge_entries('', tree2, tree1))
|
|
|
+
|
|
|
+ def assertChangesEqual(self, expected, tree1, tree2, **kwargs):
|
|
|
+ actual = list(tree_changes(self.store, tree1.id, tree2.id, **kwargs))
|
|
|
+ self.assertEqual(expected, actual)
|
|
|
+
|
|
|
+ # For brevity, the following tests use tuples instead of TreeEntry objects.
|
|
|
+
|
|
|
+ def test_tree_changes_empty(self):
|
|
|
+ self.assertChangesEqual([], self.empty_tree, self.empty_tree)
|
|
|
+
|
|
|
+ def test_tree_changes_no_changes(self):
|
|
|
+ blob = make_object(Blob, data='blob')
|
|
|
+ tree = self.commit_tree([('a', blob, 0100644),
|
|
|
+ ('b/c', blob, 0100644)])
|
|
|
+ self.assertChangesEqual([], self.empty_tree, self.empty_tree)
|
|
|
+ self.assertChangesEqual([], tree, tree)
|
|
|
+ self.assertChangesEqual(
|
|
|
+ [TreeChange(CHANGE_UNCHANGED, ('a', 0100644, blob.id),
|
|
|
+ ('a', 0100644, blob.id)),
|
|
|
+ TreeChange(CHANGE_UNCHANGED, ('b/c', 0100644, blob.id),
|
|
|
+ ('b/c', 0100644, blob.id))],
|
|
|
+ tree, tree, want_unchanged=True)
|
|
|
+
|
|
|
+ def test_tree_changes_add_delete(self):
|
|
|
+ blob_a = make_object(Blob, data='a')
|
|
|
+ blob_b = make_object(Blob, data='b')
|
|
|
+ tree = self.commit_tree([('a', blob_a, 0100644),
|
|
|
+ ('x/b', blob_b, 0100755)])
|
|
|
+ self.assertChangesEqual(
|
|
|
+ [TreeChange(CHANGE_ADD, _NULL_ENTRY, ('a', 0100644, blob_a.id)),
|
|
|
+ TreeChange(CHANGE_ADD, _NULL_ENTRY, ('x/b', 0100755, blob_b.id))],
|
|
|
+ self.empty_tree, tree)
|
|
|
+ self.assertChangesEqual(
|
|
|
+ [TreeChange(CHANGE_DELETE, ('a', 0100644, blob_a.id), _NULL_ENTRY),
|
|
|
+ TreeChange(CHANGE_DELETE, ('x/b', 0100755, blob_b.id), _NULL_ENTRY)],
|
|
|
+ tree, self.empty_tree)
|
|
|
+
|
|
|
+ def test_tree_changes_modify_contents(self):
|
|
|
+ blob_a1 = make_object(Blob, data='a1')
|
|
|
+ blob_a2 = make_object(Blob, data='a2')
|
|
|
+ tree1 = self.commit_tree([('a', blob_a1, 0100644)])
|
|
|
+ tree2 = self.commit_tree([('a', blob_a2, 0100644)])
|
|
|
+ self.assertChangesEqual(
|
|
|
+ [TreeChange(CHANGE_MODIFY, ('a', 0100644, blob_a1.id),
|
|
|
+ ('a', 0100644, blob_a2.id))], tree1, tree2)
|
|
|
+
|
|
|
+ def test_tree_changes_modify_mode(self):
|
|
|
+ blob_a = make_object(Blob, data='a')
|
|
|
+ tree1 = self.commit_tree([('a', blob_a, 0100644)])
|
|
|
+ tree2 = self.commit_tree([('a', blob_a, 0100755)])
|
|
|
+ self.assertChangesEqual(
|
|
|
+ [TreeChange(CHANGE_MODIFY, ('a', 0100644, blob_a.id),
|
|
|
+ ('a', 0100755, blob_a.id))], tree1, tree2)
|
|
|
+
|
|
|
+ def test_tree_changes_change_type(self):
|
|
|
+ blob_a1 = make_object(Blob, data='a')
|
|
|
+ blob_a2 = make_object(Blob, data='/foo/bar')
|
|
|
+ tree1 = self.commit_tree([('a', blob_a1, 0100644)])
|
|
|
+ tree2 = self.commit_tree([('a', blob_a2, 0120000)])
|
|
|
+ self.assertChangesEqual(
|
|
|
+ [TreeChange(CHANGE_DELETE, ('a', 0100644, blob_a1.id), _NULL_ENTRY),
|
|
|
+ TreeChange(CHANGE_ADD, _NULL_ENTRY, ('a', 0120000, blob_a2.id))],
|
|
|
+ tree1, tree2)
|
|
|
+
|
|
|
+ def test_tree_changes_to_tree(self):
|
|
|
+ blob_a = make_object(Blob, data='a')
|
|
|
+ blob_x = make_object(Blob, data='x')
|
|
|
+ tree1 = self.commit_tree([('a', blob_a, 0100644)])
|
|
|
+ tree2 = self.commit_tree([('a/x', blob_x, 0100644)])
|
|
|
+ self.assertChangesEqual(
|
|
|
+ [TreeChange(CHANGE_DELETE, ('a', 0100644, blob_a.id), _NULL_ENTRY),
|
|
|
+ TreeChange(CHANGE_ADD, _NULL_ENTRY, ('a/x', 0100644, blob_x.id))],
|
|
|
+ tree1, tree2)
|
|
|
+
|
|
|
+ def test_tree_changes_complex(self):
|
|
|
+ blob_a_1 = make_object(Blob, data='a1_1')
|
|
|
+ blob_bx1_1 = make_object(Blob, data='bx1_1')
|
|
|
+ blob_bx2_1 = make_object(Blob, data='bx2_1')
|
|
|
+ blob_by1_1 = make_object(Blob, data='by1_1')
|
|
|
+ blob_by2_1 = make_object(Blob, data='by2_1')
|
|
|
+ tree1 = self.commit_tree([
|
|
|
+ ('a', blob_a_1, 0100644),
|
|
|
+ ('b/x/1', blob_bx1_1, 0100644),
|
|
|
+ ('b/x/2', blob_bx2_1, 0100644),
|
|
|
+ ('b/y/1', blob_by1_1, 0100644),
|
|
|
+ ('b/y/2', blob_by2_1, 0100644),
|
|
|
+ ])
|
|
|
+
|
|
|
+ blob_a_2 = make_object(Blob, data='a1_2')
|
|
|
+ blob_bx1_2 = blob_bx1_1
|
|
|
+ blob_by_2 = make_object(Blob, data='by_2')
|
|
|
+ blob_c_2 = make_object(Blob, data='c_2')
|
|
|
+ tree2 = self.commit_tree([
|
|
|
+ ('a', blob_a_2, 0100644),
|
|
|
+ ('b/x/1', blob_bx1_2, 0100644),
|
|
|
+ ('b/y', blob_by_2, 0100644),
|
|
|
+ ('c', blob_c_2, 0100644),
|
|
|
+ ])
|
|
|
+
|
|
|
+ self.assertChangesEqual(
|
|
|
+ [TreeChange(CHANGE_MODIFY, ('a', 0100644, blob_a_1.id),
|
|
|
+ ('a', 0100644, blob_a_2.id)),
|
|
|
+ TreeChange(CHANGE_DELETE, ('b/x/2', 0100644, blob_bx2_1.id),
|
|
|
+ _NULL_ENTRY),
|
|
|
+ TreeChange(CHANGE_ADD, _NULL_ENTRY, ('b/y', 0100644, blob_by_2.id)),
|
|
|
+ TreeChange(CHANGE_DELETE, ('b/y/1', 0100644, blob_by1_1.id),
|
|
|
+ _NULL_ENTRY),
|
|
|
+ TreeChange(CHANGE_DELETE, ('b/y/2', 0100644, blob_by2_1.id),
|
|
|
+ _NULL_ENTRY),
|
|
|
+ TreeChange(CHANGE_ADD, _NULL_ENTRY, ('c', 0100644, blob_c_2.id))],
|
|
|
+ tree1, tree2)
|
|
|
+
|
|
|
+ def test_tree_changes_name_order(self):
|
|
|
+ blob = make_object(Blob, data='a')
|
|
|
+ tree1 = self.commit_tree([
|
|
|
+ ('a', blob, 0100644),
|
|
|
+ ('a.', blob, 0100644),
|
|
|
+ ('a..', blob, 0100644),
|
|
|
+ ])
|
|
|
+ # Tree order is the reverse of this, so if we used tree order, 'a..'
|
|
|
+ # would not be merged.
|
|
|
+ tree2 = self.commit_tree([
|
|
|
+ ('a/x', blob, 0100644),
|
|
|
+ ('a./x', blob, 0100644),
|
|
|
+ ('a..', blob, 0100644),
|
|
|
+ ])
|
|
|
+
|
|
|
+ self.assertChangesEqual(
|
|
|
+ [TreeChange(CHANGE_DELETE, ('a', 0100644, blob.id), _NULL_ENTRY),
|
|
|
+ TreeChange(CHANGE_ADD, _NULL_ENTRY, ('a/x', 0100644, blob.id)),
|
|
|
+ TreeChange(CHANGE_DELETE, ('a.', 0100644, blob.id), _NULL_ENTRY),
|
|
|
+ TreeChange(CHANGE_ADD, _NULL_ENTRY, ('a./x', 0100644, blob.id))],
|
|
|
+ tree1, tree2)
|