test_ydiff.py 26KB

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