test_cdiff.py 27KB

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