test_mailmap.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. # test_mailmap.py -- Tests for dulwich.mailmap
  2. # Copyright (C) 2018 Jelmer Vernooij <jelmer@jelmer.uk>
  3. #
  4. # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
  5. # Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
  6. # General Public License as published by the Free Software Foundation; version 2.0
  7. # or (at your option) any later version. You can redistribute it and/or
  8. # modify it under the terms of either of these two licenses.
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. #
  16. # You should have received a copy of the licenses; if not, see
  17. # <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
  18. # and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
  19. # License, Version 2.0.
  20. #
  21. """Tests for dulwich.mailmap."""
  22. from io import BytesIO
  23. from unittest import TestCase
  24. from dulwich.mailmap import Mailmap, read_mailmap
  25. class ReadMailmapTests(TestCase):
  26. def test_read(self) -> None:
  27. b = BytesIO(
  28. b"""\
  29. Jane Doe <jane@desktop.(none)>
  30. Joe R. Developer <joe@example.com>
  31. # A comment
  32. <cto@company.xx> <cto@coompany.xx> # Comment
  33. Some Dude <some@dude.xx> nick1 <bugs@company.xx>
  34. Other Author <other@author.xx> nick2 <bugs@company.xx>
  35. Other Author <other@author.xx> <nick2@company.xx>
  36. Santa Claus <santa.claus@northpole.xx> <me@company.xx>
  37. """
  38. )
  39. self.assertEqual(
  40. [
  41. ((b"Jane Doe", b"jane@desktop.(none)"), None),
  42. ((b"Joe R. Developer", b"joe@example.com"), None),
  43. ((None, b"cto@company.xx"), (None, b"cto@coompany.xx")),
  44. (
  45. (b"Some Dude", b"some@dude.xx"),
  46. (b"nick1", b"bugs@company.xx"),
  47. ),
  48. (
  49. (b"Other Author", b"other@author.xx"),
  50. (b"nick2", b"bugs@company.xx"),
  51. ),
  52. (
  53. (b"Other Author", b"other@author.xx"),
  54. (None, b"nick2@company.xx"),
  55. ),
  56. (
  57. (b"Santa Claus", b"santa.claus@northpole.xx"),
  58. (None, b"me@company.xx"),
  59. ),
  60. ],
  61. list(read_mailmap(b)),
  62. )
  63. class MailmapTests(TestCase):
  64. def test_lookup(self) -> None:
  65. m = Mailmap()
  66. m.add_entry((b"Jane Doe", b"jane@desktop.(none)"), (None, None))
  67. m.add_entry((b"Joe R. Developer", b"joe@example.com"), None)
  68. m.add_entry((None, b"cto@company.xx"), (None, b"cto@coompany.xx"))
  69. m.add_entry((b"Some Dude", b"some@dude.xx"), (b"nick1", b"bugs@company.xx"))
  70. m.add_entry(
  71. (b"Other Author", b"other@author.xx"),
  72. (b"nick2", b"bugs@company.xx"),
  73. )
  74. m.add_entry((b"Other Author", b"other@author.xx"), (None, b"nick2@company.xx"))
  75. m.add_entry(
  76. (b"Santa Claus", b"santa.claus@northpole.xx"),
  77. (None, b"me@company.xx"),
  78. )
  79. self.assertEqual(
  80. b"Jane Doe <jane@desktop.(none)>",
  81. m.lookup(b"Jane Doe <jane@desktop.(none)>"),
  82. )
  83. self.assertEqual(
  84. b"Jane Doe <jane@desktop.(none)>",
  85. m.lookup(b"Jane Doe <jane@example.com>"),
  86. )
  87. self.assertEqual(
  88. b"Jane Doe <jane@desktop.(none)>",
  89. m.lookup(b"Jane D. <jane@desktop.(none)>"),
  90. )
  91. self.assertEqual(
  92. b"Some Dude <some@dude.xx>", m.lookup(b"nick1 <bugs@company.xx>")
  93. )
  94. self.assertEqual(b"CTO <cto@company.xx>", m.lookup(b"CTO <cto@coompany.xx>"))
  95. def test_lookup_with_identity_tuple(self) -> None:
  96. """Test lookup using an identity tuple instead of a string."""
  97. m = Mailmap()
  98. m.add_entry(
  99. (b"Real Name", b"real@example.com"), (b"Alias", b"alias@example.com")
  100. )
  101. # Test lookup with a tuple
  102. self.assertEqual(
  103. (b"Real Name", b"real@example.com"),
  104. m.lookup((b"Alias", b"alias@example.com")),
  105. )
  106. # Test lookup with another tuple that doesn't match anything
  107. self.assertEqual(
  108. (b"Unknown", b"unknown@example.com"),
  109. m.lookup((b"Unknown", b"unknown@example.com")),
  110. )
  111. def test_lookup_with_no_match(self) -> None:
  112. """Test lookup when no match is found."""
  113. m = Mailmap()
  114. m.add_entry(
  115. (b"Real Name", b"real@example.com"), (b"Alias", b"alias@example.com")
  116. )
  117. # No match should return the original identity
  118. original = b"Unknown <unknown@example.com>"
  119. self.assertEqual(original, m.lookup(original))
  120. def test_lookup_partial_matches(self) -> None:
  121. """Test lookup with partial matches (name or email only)."""
  122. m = Mailmap()
  123. # Add entry with only name
  124. m.add_entry((b"Real Name", None), (b"Any Name", None))
  125. # Add entry with only email
  126. m.add_entry((None, b"real@example.com"), (None, b"other@example.com"))
  127. # Match by name
  128. self.assertEqual(
  129. b"Real Name <any@example.com>", m.lookup(b"Any Name <any@example.com>")
  130. )
  131. # Match by email
  132. self.assertEqual(
  133. b"Any Name <real@example.com>", m.lookup(b"Any Name <other@example.com>")
  134. )
  135. def test_add_entry_name_or_email_only(self) -> None:
  136. """Test adding entries with only name or only email."""
  137. m = Mailmap()
  138. # Entry with only canonical name
  139. m.add_entry((b"Real Name", None), (b"Alias", b"alias@example.com"))
  140. # Entry with only canonical email
  141. m.add_entry((None, b"real@example.com"), (b"Other", b"other@example.com"))
  142. # Lookup should properly combine the identity parts
  143. self.assertEqual(
  144. b"Real Name <alias@example.com>", m.lookup(b"Alias <alias@example.com>")
  145. )
  146. self.assertEqual(
  147. b"Other <real@example.com>", m.lookup(b"Other <other@example.com>")
  148. )
  149. def test_init_with_iterator(self) -> None:
  150. """Test initializing Mailmap with an iterator of entries."""
  151. entries = [
  152. ((b"Real Name", b"real@example.com"), (b"Alias", b"alias@example.com")),
  153. ((b"Another", b"another@example.com"), None),
  154. ]
  155. m = Mailmap(iter(entries))
  156. # Verify the entries were added
  157. self.assertEqual(
  158. b"Real Name <real@example.com>", m.lookup(b"Alias <alias@example.com>")
  159. )
  160. self.assertEqual(
  161. b"Another <another@example.com>",
  162. m.lookup(b"Another <another@example.com>"),
  163. )
  164. def test_lookup_with_none_name(self) -> None:
  165. """Test lookup when canonical name is None (only email is canonical)."""
  166. m = Mailmap()
  167. # Add entry with canonical email but no canonical name
  168. m.add_entry((None, b"real@example.com"), (b"Alias", b"alias@example.com"))
  169. # When canonical name is None, use original name
  170. result = m.lookup(b"Alias <alias@example.com>")
  171. self.assertEqual(b"Alias <real@example.com>", result)
  172. # Test formatting when name becomes empty
  173. m2 = Mailmap()
  174. m2.add_entry((None, b"real@example.com"), (None, b"alias@example.com"))
  175. result2 = m2.lookup(b" <alias@example.com>")
  176. # When both names are None/empty, result has empty name
  177. self.assertEqual(b" <real@example.com>", result2)
  178. def test_lookup_with_none_email(self) -> None:
  179. """Test lookup when canonical email is None (only name is canonical)."""
  180. m = Mailmap()
  181. # Add entry with canonical name but no canonical email
  182. m.add_entry((b"Real Name", None), (b"Alias", b"alias@example.com"))
  183. # When canonical email is None, use original email
  184. result = m.lookup(b"Alias <alias@example.com>")
  185. self.assertEqual(b"Real Name <alias@example.com>", result)
  186. # Test formatting when email becomes empty
  187. m2 = Mailmap()
  188. m2.add_entry((b"Real Name", None), (b"Alias", None))
  189. result2 = m2.lookup(b"Alias <>")
  190. # When both emails are None/empty, result has empty email
  191. self.assertEqual(b"Real Name <>", result2)
  192. def test_from_path(self) -> None:
  193. """Test creating Mailmap from a file path."""
  194. import os
  195. import tempfile
  196. # Create a temporary mailmap file
  197. with tempfile.NamedTemporaryFile(
  198. mode="wb", delete=False, suffix=".mailmap"
  199. ) as f:
  200. f.write(b"Real Name <real@example.com> <alias@example.com>\n")
  201. f.write(b"Another Person <another@example.com>\n")
  202. mailmap_path = f.name
  203. try:
  204. # Load from path
  205. m = Mailmap.from_path(mailmap_path)
  206. # Verify entries were loaded
  207. self.assertEqual(
  208. b"Real Name <real@example.com>", m.lookup(b"Alias <alias@example.com>")
  209. )
  210. finally:
  211. os.unlink(mailmap_path)