test_patch.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. # test_patch.py -- tests for patch.py
  2. # Copyright (C) 2010 Jelmer Vernooij <jelmer@samba.org>
  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 patch.py."""
  19. from io import BytesIO, StringIO
  20. from dulwich.objects import (
  21. Blob,
  22. Commit,
  23. S_IFGITLINK,
  24. Tree,
  25. )
  26. from dulwich.object_store import (
  27. MemoryObjectStore,
  28. )
  29. from dulwich.patch import (
  30. git_am_patch_split,
  31. write_blob_diff,
  32. write_commit_patch,
  33. write_object_diff,
  34. write_tree_diff,
  35. )
  36. from dulwich.tests import (
  37. SkipTest,
  38. TestCase,
  39. )
  40. from dulwich.tests.utils import (
  41. skipIfPY3,
  42. )
  43. class WriteCommitPatchTests(TestCase):
  44. def test_simple_bytesio(self):
  45. f = BytesIO()
  46. c = Commit()
  47. c.committer = c.author = b"Jelmer <jelmer@samba.org>"
  48. c.commit_time = c.author_time = 1271350201
  49. c.commit_timezone = c.author_timezone = 0
  50. c.message = b"This is the first line\nAnd this is the second line.\n"
  51. c.tree = Tree().id
  52. write_commit_patch(f, c, b"CONTENTS", (1, 1), version="custom")
  53. f.seek(0)
  54. lines = f.readlines()
  55. self.assertTrue(lines[0].startswith(b"From 0b0d34d1b5b596c928adc9a727a4b9e03d025298"))
  56. self.assertEqual(lines[1], b"From: Jelmer <jelmer@samba.org>\n")
  57. self.assertTrue(lines[2].startswith(b"Date: "))
  58. self.assertEqual([
  59. b"Subject: [PATCH 1/1] This is the first line\n",
  60. b"And this is the second line.\n",
  61. b"\n",
  62. b"\n",
  63. b"---\n"], lines[3:8])
  64. self.assertEqual([
  65. b"CONTENTS-- \n",
  66. b"custom\n"], lines[-2:])
  67. if len(lines) >= 12:
  68. # diffstat may not be present
  69. self.assertEqual(lines[8], b" 0 files changed\n")
  70. class ReadGitAmPatch(TestCase):
  71. def test_extract_string(self):
  72. text = b"""From ff643aae102d8870cac88e8f007e70f58f3a7363 Mon Sep 17 00:00:00 2001
  73. From: Jelmer Vernooij <jelmer@samba.org>
  74. Date: Thu, 15 Apr 2010 15:40:28 +0200
  75. Subject: [PATCH 1/2] Remove executable bit from prey.ico (triggers a lintian warning).
  76. ---
  77. pixmaps/prey.ico | Bin 9662 -> 9662 bytes
  78. 1 files changed, 0 insertions(+), 0 deletions(-)
  79. mode change 100755 => 100644 pixmaps/prey.ico
  80. --
  81. 1.7.0.4
  82. """
  83. c, diff, version = git_am_patch_split(StringIO(text.decode("utf-8")), "utf-8")
  84. self.assertEqual(b"Jelmer Vernooij <jelmer@samba.org>", c.committer)
  85. self.assertEqual(b"Jelmer Vernooij <jelmer@samba.org>", c.author)
  86. self.assertEqual(b"Remove executable bit from prey.ico "
  87. b"(triggers a lintian warning).\n", c.message)
  88. self.assertEqual(b""" pixmaps/prey.ico | Bin 9662 -> 9662 bytes
  89. 1 files changed, 0 insertions(+), 0 deletions(-)
  90. mode change 100755 => 100644 pixmaps/prey.ico
  91. """, diff)
  92. self.assertEqual(b"1.7.0.4", version)
  93. def test_extract_bytes(self):
  94. text = b"""From ff643aae102d8870cac88e8f007e70f58f3a7363 Mon Sep 17 00:00:00 2001
  95. From: Jelmer Vernooij <jelmer@samba.org>
  96. Date: Thu, 15 Apr 2010 15:40:28 +0200
  97. Subject: [PATCH 1/2] Remove executable bit from prey.ico (triggers a lintian warning).
  98. ---
  99. pixmaps/prey.ico | Bin 9662 -> 9662 bytes
  100. 1 files changed, 0 insertions(+), 0 deletions(-)
  101. mode change 100755 => 100644 pixmaps/prey.ico
  102. --
  103. 1.7.0.4
  104. """
  105. c, diff, version = git_am_patch_split(BytesIO(text))
  106. self.assertEqual(b"Jelmer Vernooij <jelmer@samba.org>", c.committer)
  107. self.assertEqual(b"Jelmer Vernooij <jelmer@samba.org>", c.author)
  108. self.assertEqual(b"Remove executable bit from prey.ico "
  109. b"(triggers a lintian warning).\n", c.message)
  110. self.assertEqual(b""" pixmaps/prey.ico | Bin 9662 -> 9662 bytes
  111. 1 files changed, 0 insertions(+), 0 deletions(-)
  112. mode change 100755 => 100644 pixmaps/prey.ico
  113. """, diff)
  114. self.assertEqual(b"1.7.0.4", version)
  115. def test_extract_spaces(self):
  116. text = b"""From ff643aae102d8870cac88e8f007e70f58f3a7363 Mon Sep 17 00:00:00 2001
  117. From: Jelmer Vernooij <jelmer@samba.org>
  118. Date: Thu, 15 Apr 2010 15:40:28 +0200
  119. Subject: [Dulwich-users] [PATCH] Added unit tests for
  120. dulwich.object_store.tree_lookup_path.
  121. * dulwich/tests/test_object_store.py
  122. (TreeLookupPathTests): This test case contains a few tests that ensure the
  123. tree_lookup_path function works as expected.
  124. ---
  125. pixmaps/prey.ico | Bin 9662 -> 9662 bytes
  126. 1 files changed, 0 insertions(+), 0 deletions(-)
  127. mode change 100755 => 100644 pixmaps/prey.ico
  128. --
  129. 1.7.0.4
  130. """
  131. c, diff, version = git_am_patch_split(BytesIO(text), "utf-8")
  132. self.assertEqual(b'Added unit tests for dulwich.object_store.tree_lookup_path.\n\n* dulwich/tests/test_object_store.py\n (TreeLookupPathTests): This test case contains a few tests that ensure the\n tree_lookup_path function works as expected.\n', c.message)
  133. def test_extract_pseudo_from_header(self):
  134. text = b"""From ff643aae102d8870cac88e8f007e70f58f3a7363 Mon Sep 17 00:00:00 2001
  135. From: Jelmer Vernooij <jelmer@samba.org>
  136. Date: Thu, 15 Apr 2010 15:40:28 +0200
  137. Subject: [Dulwich-users] [PATCH] Added unit tests for
  138. dulwich.object_store.tree_lookup_path.
  139. From: Jelmer Vernooy <jelmer@debian.org>
  140. * dulwich/tests/test_object_store.py
  141. (TreeLookupPathTests): This test case contains a few tests that ensure the
  142. tree_lookup_path function works as expected.
  143. ---
  144. pixmaps/prey.ico | Bin 9662 -> 9662 bytes
  145. 1 files changed, 0 insertions(+), 0 deletions(-)
  146. mode change 100755 => 100644 pixmaps/prey.ico
  147. --
  148. 1.7.0.4
  149. """
  150. c, diff, version = git_am_patch_split(BytesIO(text), "utf-8")
  151. self.assertEqual(b"Jelmer Vernooy <jelmer@debian.org>", c.author)
  152. self.assertEqual(b'Added unit tests for dulwich.object_store.tree_lookup_path.\n\n* dulwich/tests/test_object_store.py\n (TreeLookupPathTests): This test case contains a few tests that ensure the\n tree_lookup_path function works as expected.\n', c.message)
  153. def test_extract_no_version_tail(self):
  154. text = b"""From ff643aae102d8870cac88e8f007e70f58f3a7363 Mon Sep 17 00:00:00 2001
  155. From: Jelmer Vernooij <jelmer@samba.org>
  156. Date: Thu, 15 Apr 2010 15:40:28 +0200
  157. Subject: [Dulwich-users] [PATCH] Added unit tests for
  158. dulwich.object_store.tree_lookup_path.
  159. From: Jelmer Vernooy <jelmer@debian.org>
  160. ---
  161. pixmaps/prey.ico | Bin 9662 -> 9662 bytes
  162. 1 files changed, 0 insertions(+), 0 deletions(-)
  163. mode change 100755 => 100644 pixmaps/prey.ico
  164. """
  165. c, diff, version = git_am_patch_split(BytesIO(text), "utf-8")
  166. self.assertEqual(None, version)
  167. def test_extract_mercurial(self):
  168. raise SkipTest("git_am_patch_split doesn't handle Mercurial patches properly yet")
  169. expected_diff = """diff --git a/dulwich/tests/test_patch.py b/dulwich/tests/test_patch.py
  170. --- a/dulwich/tests/test_patch.py
  171. +++ b/dulwich/tests/test_patch.py
  172. @@ -158,7 +158,7 @@
  173. '''
  174. c, diff, version = git_am_patch_split(BytesIO(text))
  175. - self.assertIs(None, version)
  176. + self.assertEqual(None, version)
  177. class DiffTests(TestCase):
  178. """
  179. text = """From dulwich-users-bounces+jelmer=samba.org@lists.launchpad.net Mon Nov 29 00:58:18 2010
  180. Date: Sun, 28 Nov 2010 17:57:27 -0600
  181. From: Augie Fackler <durin42@gmail.com>
  182. To: dulwich-users <dulwich-users@lists.launchpad.net>
  183. Subject: [Dulwich-users] [PATCH] test_patch: fix tests on Python 2.6
  184. Content-Transfer-Encoding: 8bit
  185. Change-Id: I5e51313d4ae3a65c3f00c665002a7489121bb0d6
  186. %s
  187. _______________________________________________
  188. Mailing list: https://launchpad.net/~dulwich-users
  189. Post to : dulwich-users@lists.launchpad.net
  190. Unsubscribe : https://launchpad.net/~dulwich-users
  191. More help : https://help.launchpad.net/ListHelp
  192. """ % expected_diff
  193. c, diff, version = git_am_patch_split(BytesIO(text))
  194. self.assertEqual(expected_diff, diff)
  195. self.assertEqual(None, version)
  196. @skipIfPY3
  197. class DiffTests(TestCase):
  198. """Tests for write_blob_diff and write_tree_diff."""
  199. def test_blob_diff(self):
  200. f = BytesIO()
  201. write_blob_diff(f, ("foo.txt", 0o644, Blob.from_string("old\nsame\n")),
  202. ("bar.txt", 0o644, Blob.from_string("new\nsame\n")))
  203. self.assertEqual([
  204. "diff --git a/foo.txt b/bar.txt",
  205. "index 3b0f961..a116b51 644",
  206. "--- a/foo.txt",
  207. "+++ b/bar.txt",
  208. "@@ -1,2 +1,2 @@",
  209. "-old",
  210. "+new",
  211. " same"
  212. ], f.getvalue().splitlines())
  213. def test_blob_add(self):
  214. f = BytesIO()
  215. write_blob_diff(f, (None, None, None),
  216. ("bar.txt", 0o644, Blob.from_string("new\nsame\n")))
  217. self.assertEqual([
  218. 'diff --git /dev/null b/bar.txt',
  219. 'new mode 644',
  220. 'index 0000000..a116b51 644',
  221. '--- /dev/null',
  222. '+++ b/bar.txt',
  223. '@@ -1,0 +1,2 @@',
  224. '+new',
  225. '+same'
  226. ], f.getvalue().splitlines())
  227. def test_blob_remove(self):
  228. f = BytesIO()
  229. write_blob_diff(f, ("bar.txt", 0o644, Blob.from_string("new\nsame\n")),
  230. (None, None, None))
  231. self.assertEqual([
  232. 'diff --git a/bar.txt /dev/null',
  233. 'deleted mode 644',
  234. 'index a116b51..0000000',
  235. '--- a/bar.txt',
  236. '+++ /dev/null',
  237. '@@ -1,2 +1,0 @@',
  238. '-new',
  239. '-same'
  240. ], f.getvalue().splitlines())
  241. def test_tree_diff(self):
  242. f = BytesIO()
  243. store = MemoryObjectStore()
  244. added = Blob.from_string("add\n")
  245. removed = Blob.from_string("removed\n")
  246. changed1 = Blob.from_string("unchanged\nremoved\n")
  247. changed2 = Blob.from_string("unchanged\nadded\n")
  248. unchanged = Blob.from_string("unchanged\n")
  249. tree1 = Tree()
  250. tree1.add("removed.txt", 0o644, removed.id)
  251. tree1.add("changed.txt", 0o644, changed1.id)
  252. tree1.add("unchanged.txt", 0o644, changed1.id)
  253. tree2 = Tree()
  254. tree2.add("added.txt", 0o644, added.id)
  255. tree2.add("changed.txt", 0o644, changed2.id)
  256. tree2.add("unchanged.txt", 0o644, changed1.id)
  257. store.add_objects([(o, None) for o in [
  258. tree1, tree2, added, removed, changed1, changed2, unchanged]])
  259. write_tree_diff(f, store, tree1.id, tree2.id)
  260. self.assertEqual([
  261. 'diff --git /dev/null b/added.txt',
  262. 'new mode 644',
  263. 'index 0000000..76d4bb8 644',
  264. '--- /dev/null',
  265. '+++ b/added.txt',
  266. '@@ -1,0 +1,1 @@',
  267. '+add',
  268. 'diff --git a/changed.txt b/changed.txt',
  269. 'index bf84e48..1be2436 644',
  270. '--- a/changed.txt',
  271. '+++ b/changed.txt',
  272. '@@ -1,2 +1,2 @@',
  273. ' unchanged',
  274. '-removed',
  275. '+added',
  276. 'diff --git a/removed.txt /dev/null',
  277. 'deleted mode 644',
  278. 'index 2c3f0b3..0000000',
  279. '--- a/removed.txt',
  280. '+++ /dev/null',
  281. '@@ -1,1 +1,0 @@',
  282. '-removed',
  283. ], f.getvalue().splitlines())
  284. def test_tree_diff_submodule(self):
  285. f = BytesIO()
  286. store = MemoryObjectStore()
  287. tree1 = Tree()
  288. tree1.add("asubmodule", S_IFGITLINK,
  289. "06d0bdd9e2e20377b3180e4986b14c8549b393e4")
  290. tree2 = Tree()
  291. tree2.add("asubmodule", S_IFGITLINK,
  292. "cc975646af69f279396d4d5e1379ac6af80ee637")
  293. store.add_objects([(o, None) for o in [tree1, tree2]])
  294. write_tree_diff(f, store, tree1.id, tree2.id)
  295. self.assertEqual([
  296. 'diff --git a/asubmodule b/asubmodule',
  297. 'index 06d0bdd..cc97564 160000',
  298. '--- a/asubmodule',
  299. '+++ b/asubmodule',
  300. '@@ -1,1 +1,1 @@',
  301. '-Submodule commit 06d0bdd9e2e20377b3180e4986b14c8549b393e4',
  302. '+Submodule commit cc975646af69f279396d4d5e1379ac6af80ee637',
  303. ], f.getvalue().splitlines())
  304. def test_object_diff_blob(self):
  305. f = BytesIO()
  306. b1 = Blob.from_string("old\nsame\n")
  307. b2 = Blob.from_string("new\nsame\n")
  308. store = MemoryObjectStore()
  309. store.add_objects([(b1, None), (b2, None)])
  310. write_object_diff(f, store, ("foo.txt", 0o644, b1.id),
  311. ("bar.txt", 0o644, b2.id))
  312. self.assertEqual([
  313. "diff --git a/foo.txt b/bar.txt",
  314. "index 3b0f961..a116b51 644",
  315. "--- a/foo.txt",
  316. "+++ b/bar.txt",
  317. "@@ -1,2 +1,2 @@",
  318. "-old",
  319. "+new",
  320. " same"
  321. ], f.getvalue().splitlines())
  322. def test_object_diff_add_blob(self):
  323. f = BytesIO()
  324. store = MemoryObjectStore()
  325. b2 = Blob.from_string("new\nsame\n")
  326. store.add_object(b2)
  327. write_object_diff(f, store, (None, None, None),
  328. ("bar.txt", 0o644, b2.id))
  329. self.assertEqual([
  330. 'diff --git /dev/null b/bar.txt',
  331. 'new mode 644',
  332. 'index 0000000..a116b51 644',
  333. '--- /dev/null',
  334. '+++ b/bar.txt',
  335. '@@ -1,0 +1,2 @@',
  336. '+new',
  337. '+same'
  338. ], f.getvalue().splitlines())
  339. def test_object_diff_remove_blob(self):
  340. f = BytesIO()
  341. b1 = Blob.from_string("new\nsame\n")
  342. store = MemoryObjectStore()
  343. store.add_object(b1)
  344. write_object_diff(f, store, ("bar.txt", 0o644, b1.id),
  345. (None, None, None))
  346. self.assertEqual([
  347. 'diff --git a/bar.txt /dev/null',
  348. 'deleted mode 644',
  349. 'index a116b51..0000000',
  350. '--- a/bar.txt',
  351. '+++ /dev/null',
  352. '@@ -1,2 +1,0 @@',
  353. '-new',
  354. '-same'
  355. ], f.getvalue().splitlines())
  356. def test_object_diff_bin_blob_force(self):
  357. f = BytesIO()
  358. # Prepare two slightly different PNG headers
  359. b1 = Blob.from_string(
  360. "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52"
  361. "\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x04\x00\x00\x00\x05\x04\x8b")
  362. b2 = Blob.from_string(
  363. "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52"
  364. "\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x03\x00\x00\x00\x98\xd3\xb3")
  365. store = MemoryObjectStore()
  366. store.add_objects([(b1, None), (b2, None)])
  367. write_object_diff(f, store, ('foo.png', 0o644, b1.id),
  368. ('bar.png', 0o644, b2.id), diff_binary=True)
  369. self.assertEqual([
  370. 'diff --git a/foo.png b/bar.png',
  371. 'index f73e47d..06364b7 644',
  372. '--- a/foo.png',
  373. '+++ b/bar.png',
  374. '@@ -1,4 +1,4 @@',
  375. ' \x89PNG',
  376. ' \x1a',
  377. ' \x00\x00\x00',
  378. '-IHDR\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x04\x00\x00\x00\x05\x04\x8b',
  379. '\\ No newline at end of file',
  380. '+IHDR\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x03\x00\x00\x00\x98\xd3\xb3',
  381. '\\ No newline at end of file'
  382. ], f.getvalue().splitlines())
  383. def test_object_diff_bin_blob(self):
  384. f = BytesIO()
  385. # Prepare two slightly different PNG headers
  386. b1 = Blob.from_string(
  387. "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52"
  388. "\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x04\x00\x00\x00\x05\x04\x8b")
  389. b2 = Blob.from_string(
  390. "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52"
  391. "\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x03\x00\x00\x00\x98\xd3\xb3")
  392. store = MemoryObjectStore()
  393. store.add_objects([(b1, None), (b2, None)])
  394. write_object_diff(f, store, ('foo.png', 0o644, b1.id),
  395. ('bar.png', 0o644, b2.id))
  396. self.assertEqual([
  397. 'diff --git a/foo.png b/bar.png',
  398. 'index f73e47d..06364b7 644',
  399. 'Binary files a/foo.png and b/bar.png differ'
  400. ], f.getvalue().splitlines())
  401. def test_object_diff_add_bin_blob(self):
  402. f = BytesIO()
  403. b2 = Blob.from_string(
  404. '\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52'
  405. '\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x03\x00\x00\x00\x98\xd3\xb3')
  406. store = MemoryObjectStore()
  407. store.add_object(b2)
  408. write_object_diff(f, store, (None, None, None),
  409. ('bar.png', 0o644, b2.id))
  410. self.assertEqual([
  411. 'diff --git /dev/null b/bar.png',
  412. 'new mode 644',
  413. 'index 0000000..06364b7 644',
  414. 'Binary files /dev/null and b/bar.png differ'
  415. ], f.getvalue().splitlines())
  416. def test_object_diff_remove_bin_blob(self):
  417. f = BytesIO()
  418. b1 = Blob.from_string(
  419. '\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52'
  420. '\x00\x00\x01\xd5\x00\x00\x00\x9f\x08\x04\x00\x00\x00\x05\x04\x8b')
  421. store = MemoryObjectStore()
  422. store.add_object(b1)
  423. write_object_diff(f, store, ('foo.png', 0o644, b1.id),
  424. (None, None, None))
  425. self.assertEqual([
  426. 'diff --git a/foo.png /dev/null',
  427. 'deleted mode 644',
  428. 'index f73e47d..0000000',
  429. 'Binary files a/foo.png and /dev/null differ'
  430. ], f.getvalue().splitlines())
  431. def test_object_diff_kind_change(self):
  432. f = BytesIO()
  433. b1 = Blob.from_string("new\nsame\n")
  434. store = MemoryObjectStore()
  435. store.add_object(b1)
  436. write_object_diff(f, store, ("bar.txt", 0o644, b1.id),
  437. ("bar.txt", 0o160000, "06d0bdd9e2e20377b3180e4986b14c8549b393e4"))
  438. self.assertEqual([
  439. 'diff --git a/bar.txt b/bar.txt',
  440. 'old mode 644',
  441. 'new mode 160000',
  442. 'index a116b51..06d0bdd 160000',
  443. '--- a/bar.txt',
  444. '+++ b/bar.txt',
  445. '@@ -1,2 +1,1 @@',
  446. '-new',
  447. '-same',
  448. '+Submodule commit 06d0bdd9e2e20377b3180e4986b14c8549b393e4',
  449. ], f.getvalue().splitlines())