test_cdiff.py 24KB

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