123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """Unit test for cdiff"""
  4. import sys
  5. if sys.hexversion < 0x02050000:
  6. raise SystemExit("*** Requires python >= 2.5.0") # pragma: no cover
  7. import unittest
  8. import tempfile
  9. import subprocess
  10. import os
  11. sys.path.insert(0, '')
  12. import cdiff
  13. class Sequential(object):
  14. """A non-seekable iterator, mock of file object"""
  15. def __init__(self, items):
  16. self._items = items
  17. self._index = 0
  18. def __iter__(self):
  19. while True:
  20. try:
  21. item = self._items[self._index]
  22. except IndexError:
  23. raise StopIteration
  24. yield item
  25. self._index += 1
  26. def readline(self):
  27. try:
  28. item = self._items[self._index]
  29. except IndexError:
  30. return ''
  31. self._index += 1
  32. return item
  33. class PatchStreamTest(unittest.TestCase):
  34. def test_is_empty(self):
  35. stream = cdiff.PatchStream(Sequential([]))
  36. self.assertTrue(stream.is_empty())
  37. stream = cdiff.PatchStream(Sequential(['hello', 'world']))
  38. self.assertFalse(stream.is_empty())
  39. def test_read_stream_header(self):
  40. stream = cdiff.PatchStream(Sequential([]))
  41. self.assertEqual(stream.read_stream_header(1), [])
  42. items = ['hello', 'world', 'again']
  43. stream = cdiff.PatchStream(Sequential(items))
  44. self.assertEqual(stream.read_stream_header(2), items[:2])
  45. stream = cdiff.PatchStream(Sequential(items))
  46. self.assertEqual(stream.read_stream_header(4), items[:])
  47. def test_iter_after_read_stream_header(self):
  48. items = ['hello', 'world', 'again', 'and', 'again']
  49. stream = cdiff.PatchStream(Sequential(items))
  50. _ = stream.read_stream_header(2)
  51. out = list(stream)
  52. self.assertEqual(out, items)
  53. class DecodeTest(unittest.TestCase):
  54. def test_normal(self):
  55. utext = 'hello'.encode('utf-8')
  56. self.assertEqual('hello', cdiff.decode(utext))
  57. def test_malformed_utf8(self):
  58. text = '\x80\x02q\x01(U'
  59. self.assertEqual(text, cdiff.decode(text))
  60. class HunkTest(unittest.TestCase):
  61. def test_get_old_text(self):
  62. hunk = cdiff.Hunk([], '@@ -1,2 +1,2 @@', (1, 2), (1, 2))
  63. hunk.append(('-', 'foo\n'))
  64. hunk.append(('+', 'bar\n'))
  65. hunk.append((' ', 'common\n'))
  66. self.assertEqual(hunk._get_old_text(), ['foo\n', 'common\n'])
  67. def test_get_new_text(self):
  68. hunk = cdiff.Hunk([], '@@ -1,2 +1,2 @@', (1, 2), (1, 2))
  69. hunk.append(('-', 'foo\n'))
  70. hunk.append(('+', 'bar\n'))
  71. hunk.append((' ', 'common\n'))
  72. self.assertEqual(hunk._get_new_text(), ['bar\n', 'common\n'])
  73. class DiffMarkupTest(unittest.TestCase):
  74. def _init_diff(self):
  75. """Return a minimal diff contains all required samples
  76. header
  77. --- old
  78. +++ new
  79. hunk header
  80. @@ -1,4 +1,4 @@
  81. -hhello
  82. +helloo
  83. +spammm
  84. world
  85. -garb
  86. -Again
  87. +again
  88. """
  89. hunk = cdiff.Hunk(['hunk header\n'], '@@ -1,4 +1,4 @@\n',
  90. (1, 4), (1, 4))
  91. hunk.append(('-', 'hhello\n'))
  92. hunk.append(('+', 'helloo\n'))
  93. hunk.append(('+', 'spammm\n'))
  94. hunk.append((' ', 'world\n'))
  95. hunk.append(('-', 'garb\n'))
  96. hunk.append(('-', 'Again\n'))
  97. hunk.append(('+', 'again\n'))
  98. diff = cdiff.UnifiedDiff(['header\n'], '--- old\n', '+++ new\n', [hunk])
  99. return diff
  100. def test_markup_mix(self):
  101. marker = cdiff.DiffMarker()
  102. line = 'foo \x00-del\x01 \x00+add\x01 \x00^chg\x01 bar'
  103. base_color = 'red'
  104. self.assertEqual(
  105. marker._markup_mix(line, base_color),
  106. '\x1b[31mfoo \x1b[7m\x1b[31mdel\x1b[0m\x1b[31m '
  107. '\x1b[7m\x1b[31madd\x1b[0m\x1b[31m '
  108. '\x1b[4m\x1b[31mchg\x1b[0m\x1b[31m bar\x1b[0m')
  109. def test_markup_traditional_hunk_header(self):
  110. hunk = cdiff.Hunk(['hunk header\n'], '@@ -0 +0 @@\n', (0, 0), (0, 0))
  111. diff = cdiff.UnifiedDiff([], '--- old\n', '+++ new\n', [hunk])
  112. marker = cdiff.DiffMarker()
  113. out = list(marker._markup_traditional(diff))
  114. self.assertEqual(len(out), 4)
  115. self.assertEqual(out[0], '\x1b[33m--- old\n\x1b[0m')
  116. self.assertEqual(out[1], '\x1b[33m+++ new\n\x1b[0m')
  117. self.assertEqual(out[2], '\x1b[1;36mhunk header\n\x1b[0m')
  118. self.assertEqual(out[3], '\x1b[1;34m@@ -0 +0 @@\n\x1b[0m')
  119. def test_markup_traditional_old_changed(self):
  120. hunk = cdiff.Hunk([], '@@ -1 +0,0 @@\n', (1, 0), (0, 0))
  121. hunk.append(('-', 'spam\n'))
  122. diff = cdiff.UnifiedDiff([], '--- old\n', '+++ new\n', [hunk])
  123. marker = cdiff.DiffMarker()
  124. out = list(marker._markup_traditional(diff))
  125. self.assertEqual(len(out), 4)
  126. self.assertEqual(out[0], '\x1b[33m--- old\n\x1b[0m')
  127. self.assertEqual(out[1], '\x1b[33m+++ new\n\x1b[0m')
  128. self.assertEqual(out[2], '\x1b[1;34m@@ -1 +0,0 @@\n\x1b[0m')
  129. self.assertEqual(out[3], '\x1b[1;31m-spam\n\x1b[0m')
  130. def test_markup_traditional_new_changed(self):
  131. hunk = cdiff.Hunk([], '@@ -0,0 +1 @@\n', (0, 0), (1, 0))
  132. hunk.append(('+', 'spam\n'))
  133. diff = cdiff.UnifiedDiff([], '--- old\n', '+++ new\n', [hunk])
  134. marker = cdiff.DiffMarker()
  135. out = list(marker._markup_traditional(diff))
  136. self.assertEqual(len(out), 4)
  137. self.assertEqual(out[0], '\x1b[33m--- old\n\x1b[0m')
  138. self.assertEqual(out[1], '\x1b[33m+++ new\n\x1b[0m')
  139. self.assertEqual(out[2], '\x1b[1;34m@@ -0,0 +1 @@\n\x1b[0m')
  140. self.assertEqual(out[3], '\x1b[1;32m+spam\n\x1b[0m')
  141. def test_markup_traditional_both_changed(self):
  142. hunk = cdiff.Hunk([], '@@ -1,2 +1,2 @@\n', (1, 2), (1, 2))
  143. hunk.append(('-', 'hella\n'))
  144. hunk.append(('+', 'hello\n'))
  145. hunk.append((' ', 'common\n'))
  146. diff = cdiff.UnifiedDiff([], '--- old\n', '+++ new\n', [hunk])
  147. marker = cdiff.DiffMarker()
  148. out = list(marker._markup_traditional(diff))
  149. self.assertEqual(len(out), 6)
  150. self.assertEqual(out[0], '\x1b[33m--- old\n\x1b[0m')
  151. self.assertEqual(out[1], '\x1b[33m+++ new\n\x1b[0m')
  152. self.assertEqual(out[2], '\x1b[1;34m@@ -1,2 +1,2 @@\n\x1b[0m')
  153. self.assertEqual(
  154. out[3],
  155. '\x1b[1;31m-\x1b[0m\x1b[31mhell'
  156. '\x1b[4m\x1b[31ma\x1b[0m\x1b[31m\n\x1b[0m')
  157. self.assertEqual(
  158. out[4],
  159. '\x1b[1;32m+\x1b[0m\x1b[32mhell'
  160. '\x1b[4m\x1b[32mo\x1b[0m\x1b[32m\n\x1b[0m')
  161. self.assertEqual(out[5], '\x1b[0m common\n\x1b[0m')
  162. def test_markup_side_by_side_padded(self):
  163. diff = self._init_diff()
  164. marker = cdiff.DiffMarker()
  165. out = list(marker._markup_side_by_side(diff, 7))
  166. self.assertEqual(len(out), 10)
  167. sys.stdout.write('\n')
  168. for markup in out:
  169. sys.stdout.write(markup)
  170. self.assertEqual(out[0], '\x1b[36mheader\n\x1b[0m')
  171. self.assertEqual(out[1], '\x1b[33m--- old\n\x1b[0m')
  172. self.assertEqual(out[2], '\x1b[33m+++ new\n\x1b[0m')
  173. self.assertEqual(out[3], '\x1b[1;36mhunk header\n\x1b[0m')
  174. self.assertEqual(out[4], '\x1b[1;34m@@ -1,4 +1,4 @@\n\x1b[0m')
  175. self.assertEqual(
  176. out[5],
  177. '\x1b[33m1\x1b[0m '
  178. '\x1b[31m\x1b[7m\x1b[31mh\x1b[0m\x1b[31mhello\x1b[0m '
  179. '\x1b[0m\x1b[33m1\x1b[0m '
  180. '\x1b[32mhello\x1b[7m\x1b[32mo\x1b[0m\x1b[32m\x1b[0m\n')
  181. self.assertEqual(
  182. out[6],
  183. '\x1b[33m '
  184. '\x1b[0m '
  185. '\x1b[0m\x1b[33m2\x1b[0m '
  186. '\x1b[1;32mspammm\x1b[0m\n')
  187. self.assertEqual(
  188. out[7],
  189. '\x1b[33m2\x1b[0m '
  190. '\x1b[0mworld\x1b[0m '
  191. '\x1b[0m\x1b[33m3\x1b[0m '
  192. '\x1b[0mworld\x1b[0m\n')
  193. self.assertEqual(
  194. out[8],
  195. '\x1b[33m3\x1b[0m '
  196. '\x1b[1;31mgarb\x1b[0m '
  197. '\x1b[0m\x1b[33m '
  198. '\x1b[0m \n')
  199. self.assertEqual(
  200. out[9],
  201. '\x1b[33m4\x1b[0m '
  202. '\x1b[31m\x1b[4m\x1b[31mA\x1b[0m\x1b[31mgain\x1b[0m '
  203. '\x1b[0m\x1b[33m4\x1b[0m '
  204. '\x1b[32m\x1b[4m\x1b[32ma\x1b[0m\x1b[32mgain\x1b[0m\n')
  205. def test_markup_side_by_side_neg_width(self):
  206. diff = self._init_diff()
  207. marker = cdiff.DiffMarker()
  208. out = list(marker._markup_side_by_side(diff, -1))
  209. self.assertEqual(len(out), 10)
  210. self.assertEqual(out[0], '\x1b[36mheader\n\x1b[0m')
  211. self.assertEqual(out[1], '\x1b[33m--- old\n\x1b[0m')
  212. self.assertEqual(out[2], '\x1b[33m+++ new\n\x1b[0m')
  213. self.assertEqual(out[3], '\x1b[1;36mhunk header\n\x1b[0m')
  214. self.assertEqual(out[4], '\x1b[1;34m@@ -1,4 +1,4 @@\n\x1b[0m')
  215. self.assertEqual(
  216. out[5],
  217. '\x1b[33m1\x1b[0m '
  218. '\x1b[31m\x1b[7m\x1b[31mh\x1b[0m\x1b[31mhello\x1b[0m ' +
  219. (' ' * 74) +
  220. '\x1b[0m\x1b[33m1\x1b[0m '
  221. '\x1b[32mhello\x1b[7m\x1b[32mo\x1b[0m\x1b[32m\x1b[0m\n')
  222. self.assertEqual(
  223. out[6],
  224. '\x1b[33m '
  225. '\x1b[0m ' + (' ' * 80) +
  226. '\x1b[0m\x1b[33m2\x1b[0m '
  227. '\x1b[1;32mspammm\x1b[0m\n')
  228. self.assertEqual(
  229. out[7],
  230. '\x1b[33m2\x1b[0m '
  231. '\x1b[0mworld\x1b[0m ' + (' ' * 75) +
  232. '\x1b[0m\x1b[33m3\x1b[0m '
  233. '\x1b[0mworld\x1b[0m\n')
  234. self.assertEqual(
  235. out[8],
  236. '\x1b[33m3\x1b[0m '
  237. '\x1b[1;31mgarb\x1b[0m '
  238. '\x1b[0m\x1b[33m '
  239. '\x1b[0m \n')
  240. self.assertEqual(
  241. out[9],
  242. '\x1b[33m4\x1b[0m '
  243. '\x1b[31m\x1b[4m\x1b[31mA\x1b[0m\x1b[31mgain\x1b[0m ' +
  244. (' ' * 75) +
  245. '\x1b[0m\x1b[33m4\x1b[0m '
  246. '\x1b[32m\x1b[4m\x1b[32ma\x1b[0m\x1b[32mgain\x1b[0m\n')
  247. def test_markup_side_by_side_off_by_one(self):
  248. diff = self._init_diff()
  249. marker = cdiff.DiffMarker()
  250. out = list(marker._markup_side_by_side(diff, 6))
  251. self.assertEqual(len(out), 10)
  252. sys.stdout.write('\n')
  253. for markup in out:
  254. sys.stdout.write(markup)
  255. self.assertEqual(out[0], '\x1b[36mheader\n\x1b[0m')
  256. self.assertEqual(out[1], '\x1b[33m--- old\n\x1b[0m')
  257. self.assertEqual(out[2], '\x1b[33m+++ new\n\x1b[0m')
  258. self.assertEqual(out[3], '\x1b[1;36mhunk header\n\x1b[0m')
  259. self.assertEqual(out[4], '\x1b[1;34m@@ -1,4 +1,4 @@\n\x1b[0m')
  260. self.assertEqual(
  261. out[5],
  262. '\x1b[33m1\x1b[0m '
  263. '\x1b[31m\x1b[7m\x1b[31mh\x1b[0m\x1b[31mhello\x1b[0m '
  264. '\x1b[0m\x1b[33m1\x1b[0m '
  265. '\x1b[32mhello\x1b[7m\x1b[32mo\x1b[0m\n')
  266. self.assertEqual(
  267. out[6],
  268. '\x1b[33m \x1b[0m '
  269. '\x1b[0m\x1b[33m2\x1b[0m '
  270. '\x1b[1;32mspammm\x1b[0m\n')
  271. self.assertEqual(
  272. out[7],
  273. '\x1b[33m2\x1b[0m '
  274. '\x1b[0mworld\x1b[0m '
  275. '\x1b[0m\x1b[33m3\x1b[0m '
  276. '\x1b[0mworld\x1b[0m\n')
  277. self.assertEqual(
  278. out[8],
  279. '\x1b[33m3\x1b[0m '
  280. '\x1b[1;31mgarb\x1b[0m '
  281. '\x1b[0m\x1b[33m '
  282. '\x1b[0m \n')
  283. self.assertEqual(
  284. out[9],
  285. '\x1b[33m4\x1b[0m '
  286. '\x1b[31m\x1b[4m\x1b[31mA\x1b[0m\x1b[31mgain\x1b[0m '
  287. '\x1b[0m\x1b[33m4\x1b[0m '
  288. '\x1b[32m\x1b[4m\x1b[32ma\x1b[0m\x1b[32mgain\x1b[0m\n')
  289. def test_markup_side_by_side_wrapped(self):
  290. diff = self._init_diff()
  291. marker = cdiff.DiffMarker()
  292. out = list(marker._markup_side_by_side(diff, 5))
  293. self.assertEqual(len(out), 10)
  294. sys.stdout.write('\n')
  295. for markup in out:
  296. sys.stdout.write(markup)
  297. self.assertEqual(out[0], '\x1b[36mheader\n\x1b[0m')
  298. self.assertEqual(out[1], '\x1b[33m--- old\n\x1b[0m')
  299. self.assertEqual(out[2], '\x1b[33m+++ new\n\x1b[0m')
  300. self.assertEqual(out[3], '\x1b[1;36mhunk header\n\x1b[0m')
  301. self.assertEqual(out[4], '\x1b[1;34m@@ -1,4 +1,4 @@\n\x1b[0m')
  302. self.assertEqual(
  303. out[5],
  304. '\x1b[33m1\x1b[0m '
  305. '\x1b[31m\x1b[7m\x1b[31mh\x1b[0m\x1b[31mhel\x1b[0m\x1b[1;35m>\x1b[0m '
  306. '\x1b[0m\x1b[33m1\x1b[0m '
  307. '\x1b[32mhell\x1b[0m\x1b[1;35m>\x1b[0m\n')
  308. self.assertEqual(
  309. out[6],
  310. '\x1b[33m \x1b[0m '
  311. '\x1b[0m\x1b[33m2\x1b[0m '
  312. ''
  313. '\x1b[1;32mspam\x1b[0m\x1b[1;35m>\x1b[0m\n')
  314. self.assertEqual(
  315. out[7],
  316. '\x1b[33m2\x1b[0m '
  317. '\x1b[0mworld\x1b[0m '
  318. '\x1b[0m\x1b[33m3\x1b[0m '
  319. '\x1b[0mworld\x1b[0m\n')
  320. self.assertEqual(
  321. out[8],
  322. '\x1b[33m3\x1b[0m '
  323. '\x1b[1;31mgarb\x1b[0m '
  324. '\x1b[0m\x1b[33m '
  325. '\x1b[0m \n')
  326. self.assertEqual(
  327. out[9],
  328. '\x1b[33m4\x1b[0m '
  329. '\x1b[31m\x1b[4m\x1b[31mA\x1b[0m\x1b[31mgain\x1b[0m '
  330. '\x1b[0m\x1b[33m4\x1b[0m '
  331. '\x1b[32m\x1b[4m\x1b[32ma\x1b[0m\x1b[32mgain\x1b[0m\n')
  332. class UnifiedDiffTest(unittest.TestCase):
  333. diff = cdiff.UnifiedDiff(None, None, None, None)
  334. def test_is_hunk_meta_normal(self):
  335. self.assertTrue(self.diff.is_hunk_meta('@@ -1 +1 @@'))
  336. self.assertTrue(self.diff.is_hunk_meta('@@ -3,7 +3,6 @@'))
  337. self.assertTrue(self.diff.is_hunk_meta('@@ -3,7 +3,6 @@ class Foo'))
  338. self.assertTrue(self.diff.is_hunk_meta('@@ -3,7 +3,6 @@ class Foo\n'))
  339. self.assertTrue(
  340. self.diff.is_hunk_meta('@@ -3,7 +3,6 @@ class Foo\r\n'))
  341. def test_is_hunk_meta_svn_prop(self):
  342. self.assertTrue(self.diff.is_hunk_meta('## -0,0 +1 ##'))
  343. self.assertTrue(self.diff.is_hunk_meta('## -0,0 +1 ##\n'))
  344. self.assertTrue(self.diff.is_hunk_meta('## -0,0 +1 ##\r\n'))
  345. def test_is_hunk_meta_neg(self):
  346. self.assertFalse(self.diff.is_hunk_meta('@@ -1 + @@'))
  347. self.assertFalse(self.diff.is_hunk_meta('@@ -this is not a hunk meta'))
  348. self.assertFalse(self.diff.is_hunk_meta('## -this is not either'))
  349. def test_parse_hunk_meta_normal(self):
  350. self.assertEqual(self.diff.parse_hunk_meta('@@ -3,7 +3,6 @@'),
  351. ((3, 7), (3, 6)))
  352. def test_parse_hunk_meta_missing(self):
  353. self.assertEqual(self.diff.parse_hunk_meta('@@ -3 +3,6 @@'),
  354. ((3, 0), (3, 6)))
  355. self.assertEqual(self.diff.parse_hunk_meta('@@ -3,7 +3 @@'),
  356. ((3, 7), (3, 0)))
  357. self.assertEqual(self.diff.parse_hunk_meta('@@ -3 +3 @@'),
  358. ((3, 0), (3, 0)))
  359. def test_parse_hunk_meta_svn_prop(self):
  360. self.assertEqual(self.diff.parse_hunk_meta('## -0,0 +1 ##'),
  361. ((0, 0), (1, 0)))
  362. def test_is_old(self):
  363. self.assertTrue(self.diff.is_old('-hello world'))
  364. self.assertTrue(self.diff.is_old('----')) # yaml
  365. def test_is_old_neg(self):
  366. self.assertFalse(self.diff.is_old('--- considered as old path'))
  367. self.assertFalse(self.diff.is_old('-' * 72)) # svn log --diff
  368. def test_is_new(self):
  369. self.assertTrue(self.diff.is_new('+hello world'))
  370. self.assertTrue(self.diff.is_new('++++hello world'))
  371. def test_is_new_neg(self):
  372. self.assertFalse(self.diff.is_new('+++ considered as new path'))
  373. class DiffParserTest(unittest.TestCase):
  374. def test_type_detect_unified(self):
  375. patch = """\
  376. spam
  377. --- a
  378. +++ b
  379. @@ -1,2 +1,2 @@
  380. """
  381. items = patch.splitlines(True)
  382. stream = cdiff.PatchStream(Sequential(items))
  383. parser = cdiff.DiffParser(stream)
  384. self.assertEqual(parser._type, 'unified')
  385. def test_type_detect_context(self):
  386. patch = """\
  387. *** /path/to/original timestamp
  388. --- /path/to/new timestamp
  389. ***************
  390. *** 1,1 ****
  391. --- 1,2 ----
  392. + This is an important
  393. This part of the
  394. """
  395. items = patch.splitlines(True)
  396. stream = cdiff.PatchStream(Sequential(items))
  397. parser = cdiff.DiffParser(stream)
  398. self.assertEqual(parser._type, 'context')
  399. def test_type_detect_neg(self):
  400. patch = """\
  401. spam
  402. --- a
  403. spam
  404. +++ b
  405. """
  406. items = patch.splitlines(True)
  407. stream = cdiff.PatchStream(Sequential(items))
  408. parser = cdiff.DiffParser(stream)
  409. self.assertEqual(parser._type, 'unified')
  410. def test_parse_invalid_hunk_meta(self):
  411. patch = """\
  412. spam
  413. --- a
  414. +++ b
  415. spam
  416. @@ -a,a +0 @@
  417. """
  418. items = patch.splitlines(True)
  419. stream = cdiff.PatchStream(Sequential(items))
  420. parser = cdiff.DiffParser(stream)
  421. self.assertRaises(RuntimeError, list, parser.get_diff_generator())
  422. def test_parse_dangling_header(self):
  423. patch = """\
  424. --- a
  425. +++ b
  426. @@ -1,2 +1,2 @@
  427. -foo
  428. +bar
  429. common
  430. spam
  431. """
  432. items = patch.splitlines(True)
  433. stream = cdiff.PatchStream(Sequential(items))
  434. parser = cdiff.DiffParser(stream)
  435. out = list(parser.get_diff_generator())
  436. self.assertEqual(len(out), 2)
  437. self.assertEqual(len(out[1]._headers), 1)
  438. self.assertEqual(out[1]._headers[0], 'spam\n')
  439. self.assertEqual(out[1]._old_path, '')
  440. self.assertEqual(out[1]._new_path, '')
  441. self.assertEqual(len(out[1]._hunks), 0)
  442. def test_parse_missing_new_path(self):
  443. patch = """\
  444. --- a
  445. +++ b
  446. @@ -1,2 +1,2 @@
  447. -foo
  448. +bar
  449. common
  450. --- c
  451. """
  452. items = patch.splitlines(True)
  453. stream = cdiff.PatchStream(Sequential(items))
  454. parser = cdiff.DiffParser(stream)
  455. self.assertRaises(AssertionError, list, parser.get_diff_generator())
  456. def test_parse_missing_hunk_meta(self):
  457. patch = """\
  458. --- a
  459. +++ b
  460. @@ -1,2 +1,2 @@
  461. -foo
  462. +bar
  463. common
  464. --- c
  465. +++ d
  466. """
  467. items = patch.splitlines(True)
  468. stream = cdiff.PatchStream(Sequential(items))
  469. parser = cdiff.DiffParser(stream)
  470. out = list(parser.get_diff_generator())
  471. self.assertEqual(len(out), 2)
  472. self.assertEqual(len(out[1]._headers), 0)
  473. self.assertEqual(out[1]._old_path, '--- c\n')
  474. self.assertEqual(out[1]._new_path, '+++ d\n')
  475. self.assertEqual(len(out[1]._hunks), 0)
  476. def test_parse_missing_hunk_list(self):
  477. patch = """\
  478. --- a
  479. +++ b
  480. @@ -1,2 +1,2 @@
  481. -foo
  482. +bar
  483. common
  484. --- c
  485. +++ d
  486. @@ -1,2 +1,2 @@
  487. """
  488. items = patch.splitlines(True)
  489. stream = cdiff.PatchStream(Sequential(items))
  490. parser = cdiff.DiffParser(stream)
  491. self.assertRaises(AssertionError, list, parser.get_diff_generator())
  492. def test_parse_only_in_dir(self):
  493. patch = """\
  494. --- a
  495. +++ b
  496. @@ -1,2 +1,2 @@
  497. -foo
  498. +bar
  499. common
  500. Only in foo: foo
  501. --- c
  502. +++ d
  503. @@ -1,2 +1,2 @@
  504. -foo
  505. +bar
  506. common
  507. """
  508. items = patch.splitlines(True)
  509. stream = cdiff.PatchStream(Sequential(items))
  510. parser = cdiff.DiffParser(stream)
  511. out = list(parser.get_diff_generator())
  512. self.assertEqual(len(out), 3)
  513. self.assertEqual(len(out[1]._hunks), 0)
  514. self.assertEqual(out[1]._headers, ['Only in foo: foo\n'])
  515. self.assertEqual(len(out[2]._hunks), 1)
  516. self.assertEqual(len(out[2]._hunks[0]._hunk_list), 3)
  517. def test_parse_only_in_dir_at_last(self):
  518. patch = """\
  519. --- a
  520. +++ b
  521. @@ -1,2 +1,2 @@
  522. -foo
  523. +bar
  524. common
  525. Only in foo: foo
  526. """
  527. items = patch.splitlines(True)
  528. stream = cdiff.PatchStream(Sequential(items))
  529. parser = cdiff.DiffParser(stream)
  530. out = list(parser.get_diff_generator())
  531. self.assertEqual(len(out), 2)
  532. self.assertEqual(len(out[1]._hunks), 0)
  533. self.assertEqual(out[1]._headers, ['Only in foo: foo\n'])
  534. def test_parse_binary_differ_diff_ru(self):
  535. patch = """\
  536. --- a
  537. +++ b
  538. @@ -1,2 +1,2 @@
  539. -foo
  540. +bar
  541. common
  542. Binary files a/1.pdf and b/1.pdf differ
  543. --- c
  544. +++ d
  545. @@ -1,2 +1,2 @@
  546. -foo
  547. +bar
  548. common
  549. """
  550. items = patch.splitlines(True)
  551. stream = cdiff.PatchStream(Sequential(items))
  552. parser = cdiff.DiffParser(stream)
  553. out = list(parser.get_diff_generator())
  554. self.assertEqual(len(out), 3)
  555. self.assertEqual(len(out[1]._hunks), 0)
  556. self.assertEqual(out[1]._old_path, '')
  557. self.assertEqual(out[1]._new_path, '')
  558. self.assertEqual(len(out[1]._headers), 1)
  559. self.assertTrue(out[1]._headers[0].startswith('Binary files'))
  560. self.assertEqual(len(out[2]._hunks), 1)
  561. self.assertEqual(len(out[2]._hunks[0]._hunk_list), 3)
  562. def test_parse_binary_differ_git(self):
  563. patch = """\
  564. diff --git a/foo b/foo
  565. index 529d8a3..ad71911 100755
  566. --- a/foo
  567. +++ b/foo
  568. @@ -1,2 +1,2 @@
  569. -foo
  570. +bar
  571. common
  572. diff --git a/example.pdf b/example.pdf
  573. index 1eacfd8..3696851 100644
  574. Binary files a/example.pdf and b/example.pdf differ
  575. diff --git a/bar b/bar
  576. index 529e8a3..ad71921 100755
  577. --- a/bar
  578. +++ b/bar
  579. @@ -1,2 +1,2 @@
  580. -foo
  581. +bar
  582. common
  583. """
  584. items = patch.splitlines(True)
  585. stream = cdiff.PatchStream(Sequential(items))
  586. parser = cdiff.DiffParser(stream)
  587. out = list(parser.get_diff_generator())
  588. self.assertEqual(len(out), 3)
  589. self.assertEqual(len(out[1]._hunks), 0)
  590. self.assertEqual(out[1]._old_path, '')
  591. self.assertEqual(out[1]._new_path, '')
  592. self.assertEqual(len(out[1]._headers), 3)
  593. self.assertTrue(out[1]._headers[2].startswith('Binary files'))
  594. self.assertEqual(len(out[2]._hunks), 1)
  595. self.assertEqual(len(out[2]._hunks[0]._hunk_list), 3)
  596. def test_parse_svn_prop(self):
  597. patch = """\
  598. --- a
  599. +++ b
  600. Added: svn:executable
  601. ## -0,0 +1 ##
  602. +*
  603. \\ No newline at end of property
  604. Added: svn:keywords
  605. ## -0,0 +1 ##
  606. +Id
  607. """
  608. items = patch.splitlines(True)
  609. stream = cdiff.PatchStream(Sequential(items))
  610. parser = cdiff.DiffParser(stream)
  611. out = list(parser.get_diff_generator())
  612. self.assertEqual(len(out), 1)
  613. self.assertEqual(len(out[0]._hunks), 2)
  614. hunk = out[0]._hunks[1]
  615. self.assertEqual(hunk._hunk_headers, ['Added: svn:keywords\n'])
  616. self.assertEqual(hunk._hunk_list, [('+', 'Id\n')])
  617. class MainTest(unittest.TestCase):
  618. def setUp(self):
  619. self._cwd = os.getcwd()
  620. self._ws = tempfile.mkdtemp(prefix='test_cdiff')
  621. self._non_ws = tempfile.mkdtemp(prefix='test_cdiff')
  622. cmd = ('cd %s; git init; git config user.name me; '
  623. 'git config user.email me@example.org') % self._ws
  624. subprocess.call(cmd, shell=True, stdout=subprocess.PIPE)
  625. self._change_file('init')
  626. def tearDown(self):
  627. os.chdir(self._cwd)
  628. cmd = ['/bin/rm', '-rf', self._ws, self._non_ws]
  629. subprocess.call(cmd)
  630. def _change_file(self, text):
  631. cmd = ['/bin/sh', '-c',
  632. 'cd %s; echo "%s" > foo' % (self._ws, text)]
  633. subprocess.call(cmd)
  634. def _commit_file(self):
  635. cmd = ['/bin/sh', '-c',
  636. 'cd %s; git add foo; git commit foo -m update' % self._ws]
  637. subprocess.call(cmd, stdout=subprocess.PIPE)
  638. def test_read_diff(self):
  639. sys.argv = sys.argv[:1]
  640. self._change_file('read_diff')
  641. os.chdir(self._ws)
  642. ret = cdiff.main()
  643. os.chdir(self._cwd)
  644. self.assertEqual(ret, 0)
  645. def test_read_diff_neg(self):
  646. sys.argv = sys.argv[:1]
  647. os.chdir(self._non_ws)
  648. ret = cdiff.main()
  649. os.chdir(self._cwd)
  650. self.assertNotEqual(ret, 0)
  651. def test_read_log(self):
  652. sys.argv = [sys.argv[0], '--log']
  653. self._change_file('read_log')
  654. self._commit_file()
  655. os.chdir(self._ws)
  656. ret = cdiff.main()
  657. os.chdir(self._cwd)
  658. self.assertEqual(ret, 0)
  659. def test_read_log_neg(self):
  660. sys.argv = [sys.argv[0], '--log']
  661. os.chdir(self._non_ws)
  662. ret = cdiff.main()
  663. os.chdir(self._cwd)
  664. self.assertNotEqual(ret, 0)
  665. if __name__ == '__main__':
  666. unittest.main()
  667. # vim:set et sts=4 sw=4 tw=80: