2
0

test_annotate.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. # test_annotate.py -- tests for annotate
  2. # Copyright (C) 2015 Jelmer Vernooij <jelmer@jelmer.uk>
  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; version 2
  7. # of the License or (at your option) a later version.
  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 annotate support."""
  19. import os
  20. import tempfile
  21. import unittest
  22. from typing import Any
  23. from unittest import TestCase
  24. from dulwich.annotate import annotate_lines, update_lines
  25. from dulwich.objects import Blob, Commit, Tree
  26. from dulwich.repo import Repo
  27. class UpdateLinesTestCase(TestCase):
  28. """Tests for update_lines function."""
  29. def test_update_lines_equal(self) -> None:
  30. """Test update_lines when all lines are equal."""
  31. old_lines: list[tuple[tuple[Any, Any], bytes]] = [
  32. (("commit1", "entry1"), b"line1"),
  33. (("commit2", "entry2"), b"line2"),
  34. ]
  35. new_blob = b"line1\nline2"
  36. new_history_data = ("commit3", "entry3")
  37. result = update_lines(old_lines, new_history_data, new_blob) # type: ignore[arg-type]
  38. self.assertEqual(old_lines, result)
  39. def test_update_lines_insert(self) -> None:
  40. """Test update_lines when new lines are inserted."""
  41. old_lines: list[tuple[tuple[Any, Any], bytes]] = [
  42. (("commit1", "entry1"), b"line1"),
  43. (("commit2", "entry2"), b"line3"),
  44. ]
  45. new_blob = b"line1\nline2\nline3"
  46. new_history_data = ("commit3", "entry3")
  47. result = update_lines(old_lines, new_history_data, new_blob) # type: ignore[arg-type]
  48. expected = [
  49. (("commit1", "entry1"), b"line1"),
  50. (("commit3", "entry3"), b"line2"),
  51. (("commit2", "entry2"), b"line3"),
  52. ]
  53. self.assertEqual(expected, result)
  54. def test_update_lines_delete(self) -> None:
  55. """Test update_lines when lines are deleted."""
  56. old_lines: list[tuple[tuple[Any, Any], bytes]] = [
  57. (("commit1", "entry1"), b"line1"),
  58. (("commit2", "entry2"), b"line2"),
  59. (("commit3", "entry3"), b"line3"),
  60. ]
  61. new_blob = b"line1\nline3"
  62. new_history_data = ("commit4", "entry4")
  63. result = update_lines(old_lines, new_history_data, new_blob) # type: ignore[arg-type]
  64. expected = [
  65. (("commit1", "entry1"), b"line1"),
  66. (("commit3", "entry3"), b"line3"),
  67. ]
  68. self.assertEqual(expected, result)
  69. def test_update_lines_replace(self) -> None:
  70. """Test update_lines when lines are replaced."""
  71. old_lines: list[tuple[tuple[Any, Any], bytes]] = [
  72. (("commit1", "entry1"), b"line1"),
  73. (("commit2", "entry2"), b"line2"),
  74. ]
  75. new_blob = b"line1\nline2_modified"
  76. new_history_data = ("commit3", "entry3")
  77. result = update_lines(old_lines, new_history_data, new_blob) # type: ignore[arg-type]
  78. expected = [
  79. (("commit1", "entry1"), b"line1"),
  80. (("commit3", "entry3"), b"line2_modified"),
  81. ]
  82. self.assertEqual(expected, result)
  83. def test_update_lines_empty_old(self) -> None:
  84. """Test update_lines with empty old lines."""
  85. old_lines: list[tuple[tuple[Any, Any], bytes]] = []
  86. new_blob = b"line1\nline2"
  87. new_history_data = ("commit1", "entry1")
  88. result = update_lines(old_lines, new_history_data, new_blob) # type: ignore[arg-type]
  89. expected = [
  90. (("commit1", "entry1"), b"line1"),
  91. (("commit1", "entry1"), b"line2"),
  92. ]
  93. self.assertEqual(expected, result)
  94. def test_update_lines_empty_new(self) -> None:
  95. """Test update_lines with empty new blob."""
  96. old_lines: list[tuple[tuple[Any, Any], bytes]] = [
  97. (("commit1", "entry1"), b"line1")
  98. ]
  99. new_blob = b""
  100. new_history_data = ("commit2", "entry2")
  101. result = update_lines(old_lines, new_history_data, new_blob) # type: ignore[arg-type]
  102. self.assertEqual([], result)
  103. class AnnotateLinesTestCase(TestCase):
  104. """Tests for annotate_lines function."""
  105. def setUp(self) -> None:
  106. self.temp_dir = tempfile.mkdtemp()
  107. self.repo = Repo.init(self.temp_dir)
  108. def tearDown(self) -> None:
  109. self.repo.close()
  110. import shutil
  111. shutil.rmtree(self.temp_dir)
  112. def _make_commit(
  113. self, blob_content: bytes, message: str, parent: bytes | None = None
  114. ) -> bytes:
  115. """Helper to create a commit with a single file."""
  116. # Create blob
  117. blob = Blob()
  118. blob.data = blob_content
  119. self.repo.object_store.add_object(blob)
  120. # Create tree
  121. tree = Tree()
  122. tree.add(b"test.txt", 0o100644, blob.id)
  123. self.repo.object_store.add_object(tree)
  124. # Create commit
  125. commit = Commit()
  126. commit.tree = tree.id
  127. commit.author = commit.committer = b"Test Author <test@example.com>"
  128. commit.author_time = commit.commit_time = 1000000000
  129. commit.author_timezone = commit.commit_timezone = 0
  130. commit.encoding = b"UTF-8"
  131. commit.message = message.encode("utf-8")
  132. if parent:
  133. commit.parents = [parent]
  134. else:
  135. commit.parents = []
  136. self.repo.object_store.add_object(commit)
  137. return commit.id
  138. def test_annotate_lines_single_commit(self) -> None:
  139. """Test annotating a file with a single commit."""
  140. commit_id = self._make_commit(b"line1\nline2\nline3\n", "Initial commit")
  141. result = annotate_lines(self.repo.object_store, commit_id, b"test.txt")
  142. self.assertEqual(3, len(result))
  143. for (commit, entry), line in result:
  144. self.assertEqual(commit_id, commit.id)
  145. self.assertIn(line, [b"line1\n", b"line2\n", b"line3\n"])
  146. def test_annotate_lines_multiple_commits(self) -> None:
  147. """Test annotating a file with multiple commits."""
  148. # First commit
  149. commit1_id = self._make_commit(b"line1\nline2\n", "Initial commit")
  150. # Second commit - add a line
  151. commit2_id = self._make_commit(
  152. b"line1\nline1.5\nline2\n", "Add line between", parent=commit1_id
  153. )
  154. # Third commit - modify a line
  155. commit3_id = self._make_commit(
  156. b"line1_modified\nline1.5\nline2\n", "Modify first line", parent=commit2_id
  157. )
  158. result = annotate_lines(self.repo.object_store, commit3_id, b"test.txt")
  159. self.assertEqual(3, len(result))
  160. # First line should be from commit3
  161. self.assertEqual(commit3_id, result[0][0][0].id)
  162. self.assertEqual(b"line1_modified\n", result[0][1])
  163. # Second line should be from commit2
  164. self.assertEqual(commit2_id, result[1][0][0].id)
  165. self.assertEqual(b"line1.5\n", result[1][1])
  166. # Third line should be from commit1
  167. self.assertEqual(commit1_id, result[2][0][0].id)
  168. self.assertEqual(b"line2\n", result[2][1])
  169. def test_annotate_lines_nonexistent_path(self) -> None:
  170. """Test annotating a nonexistent file."""
  171. commit_id = self._make_commit(b"content\n", "Initial commit")
  172. result = annotate_lines(self.repo.object_store, commit_id, b"nonexistent.txt")
  173. self.assertEqual([], result)