test_cdiff.py 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  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")
  7. import unittest
  8. sys.path.insert(0, '')
  9. import cdiff
  10. class Sequential(object):
  11. """A non-seekable iterator, mock of file object"""
  12. def __init__(self, items):
  13. self._items = items
  14. self._index = 0
  15. def __iter__(self):
  16. while True:
  17. try:
  18. item = self._items[self._index]
  19. except IndexError:
  20. raise StopIteration
  21. yield item
  22. self._index += 1
  23. def readline(self):
  24. try:
  25. item = self._items[self._index]
  26. except IndexError:
  27. return ''
  28. self._index += 1
  29. return item
  30. class TestPatchStream(unittest.TestCase):
  31. def test_is_empty(self):
  32. stream = cdiff.PatchStream(Sequential([]))
  33. self.assertTrue(stream.is_empty())
  34. stream = cdiff.PatchStream(Sequential(['hello', 'world']))
  35. self.assertFalse(stream.is_empty())
  36. def test_read_stream_header(self):
  37. stream = cdiff.PatchStream(Sequential([]))
  38. self.assertEqual(stream.read_stream_header(1), [])
  39. items = ['hello', 'world', 'again']
  40. stream = cdiff.PatchStream(Sequential(items))
  41. self.assertEqual(stream.read_stream_header(2), items[:2])
  42. stream = cdiff.PatchStream(Sequential(items))
  43. self.assertEqual(stream.read_stream_header(4), items[:])
  44. def test_iter_after_read_stream_header(self):
  45. items = ['hello', 'world', 'again', 'and', 'again']
  46. stream = cdiff.PatchStream(Sequential(items))
  47. out = []
  48. _ = stream.read_stream_header(2)
  49. for item in stream:
  50. out.append(item)
  51. self.assertEqual(out, items)
  52. class TestHunk(unittest.TestCase):
  53. def test_get_old_text(self):
  54. hunk = cdiff.Hunk([], '@@ -1,2 +1,2 @@', (1,2), (1,2))
  55. hunk.append(('-', 'foo\n'))
  56. hunk.append(('+', 'bar\n'))
  57. hunk.append((' ', 'common\n'))
  58. self.assertEqual(hunk._get_old_text(), ['foo\n', 'common\n'])
  59. def test_get_new_text(self):
  60. hunk = cdiff.Hunk([], '@@ -1,2 +1,2 @@', (1,2), (1,2))
  61. hunk.append(('-', 'foo\n'))
  62. hunk.append(('+', 'bar\n'))
  63. hunk.append((' ', 'common\n'))
  64. self.assertEqual(hunk._get_new_text(), ['bar\n', 'common\n'])
  65. class TestDiff(unittest.TestCase):
  66. def _init_diff(self):
  67. hunk = cdiff.Hunk([], '@@ -1,2 +1,2 @@\n', (1,2), (1,2))
  68. hunk.append(('-', 'hella\n'))
  69. hunk.append(('+', 'hello\n'))
  70. hunk.append((' ', 'world\n'))
  71. diff = cdiff.Diff(['header\n'], '--- old\n', '+++ new\n', [hunk])
  72. return diff
  73. def test_markup_mix(self):
  74. line = 'foo \x00-del\x01 \x00+add\x01 \x00^chg\x01 bar'
  75. base_color = 'red'
  76. diff = cdiff.Diff(None, None, None, None)
  77. self.assertEqual(diff._markup_mix(line, base_color),
  78. '\x1b[31mfoo \x1b[7m\x1b[31mdel\x1b[0m\x1b[31m '
  79. '\x1b[7m\x1b[31madd\x1b[0m\x1b[31m '
  80. '\x1b[4m\x1b[31mchg\x1b[0m\x1b[31m bar\x1b[0m')
  81. def test_markup_traditional(self):
  82. diff = self._init_diff()
  83. out = list(diff.markup_traditional())
  84. self.assertEqual(len(out), 7)
  85. sys.stdout.write('\n')
  86. for markup in out:
  87. sys.stdout.write(markup)
  88. self.assertEqual(out[0], '\x1b[36mheader\n\x1b[0m')
  89. self.assertEqual(out[1], '\x1b[33m--- old\n\x1b[0m')
  90. self.assertEqual(out[2], '\x1b[33m+++ new\n\x1b[0m')
  91. self.assertEqual(out[3], '\x1b[1;34m@@ -1,2 +1,2 @@\n\x1b[0m')
  92. self.assertEqual(out[4],
  93. '\x1b[1;31m-\x1b[0m\x1b[31mhell\x1b[4m'
  94. '\x1b[31ma\x1b[0m\x1b[31m\n\x1b[0m')
  95. self.assertEqual(out[5],
  96. '\x1b[1;32m+\x1b[0m\x1b[32mhell\x1b[4m'
  97. '\x1b[32mo\x1b[0m\x1b[32m\n\x1b[0m')
  98. self.assertEqual(out[6], '\x1b[0m world\n\x1b[0m')
  99. def test_markup_side_by_side_padded(self):
  100. diff = self._init_diff()
  101. out = list(diff.markup_side_by_side(6))
  102. self.assertEqual(len(out), 6)
  103. sys.stdout.write('\n')
  104. for markup in out:
  105. sys.stdout.write(markup)
  106. self.assertEqual(out[0], '\x1b[36mheader\n\x1b[0m')
  107. self.assertEqual(out[1], '\x1b[33m--- old\n\x1b[0m')
  108. self.assertEqual(out[2], '\x1b[33m+++ new\n\x1b[0m')
  109. self.assertEqual(out[3], '\x1b[1;34m@@ -1,2 +1,2 @@\n\x1b[0m')
  110. self.assertEqual(out[4],
  111. '\x1b[33m1\x1b[0m '
  112. '\x1b[31mhell\x1b[4m\x1b[31ma\x1b[0m\x1b[31m\x1b[0m '
  113. '\x1b[0m\x1b[33m1\x1b[0m '
  114. '\x1b[32mhell\x1b[4m\x1b[32mo\x1b[0m\x1b[32m\x1b[0m\n')
  115. self.assertEqual(out[5],
  116. '\x1b[33m2\x1b[0m '
  117. '\x1b[0mworld\x1b[0m '
  118. '\x1b[0m\x1b[33m2\x1b[0m '
  119. '\x1b[0mworld\x1b[0m\n')
  120. def test_markup_side_by_side_off_by_one(self):
  121. diff = self._init_diff()
  122. out = list(diff.markup_side_by_side(5))
  123. self.assertEqual(len(out), 6)
  124. sys.stdout.write('\n')
  125. for markup in out:
  126. sys.stdout.write(markup)
  127. self.assertEqual(out[0], '\x1b[36mheader\n\x1b[0m')
  128. self.assertEqual(out[1], '\x1b[33m--- old\n\x1b[0m')
  129. self.assertEqual(out[2], '\x1b[33m+++ new\n\x1b[0m')
  130. self.assertEqual(out[3], '\x1b[1;34m@@ -1,2 +1,2 @@\n\x1b[0m')
  131. self.assertEqual(out[4],
  132. '\x1b[33m1\x1b[0m '
  133. '\x1b[31mhell\x1b[4m\x1b[31ma\x1b[0m '
  134. '\x1b[0m\x1b[33m1\x1b[0m '
  135. '\x1b[32mhell\x1b[4m\x1b[32mo\x1b[0m\n')
  136. self.assertEqual(out[5],
  137. '\x1b[33m2\x1b[0m '
  138. '\x1b[0mworld\x1b[0m '
  139. '\x1b[0m\x1b[33m2\x1b[0m '
  140. '\x1b[0mworld\x1b[0m\n')
  141. def test_markup_side_by_side_wrapped(self):
  142. diff = self._init_diff()
  143. out = list(diff.markup_side_by_side(4))
  144. self.assertEqual(len(out), 6)
  145. sys.stdout.write('\n')
  146. for markup in out:
  147. sys.stdout.write(markup)
  148. self.assertEqual(out[0], '\x1b[36mheader\n\x1b[0m')
  149. self.assertEqual(out[1], '\x1b[33m--- old\n\x1b[0m')
  150. self.assertEqual(out[2], '\x1b[33m+++ new\n\x1b[0m')
  151. self.assertEqual(out[3], '\x1b[1;34m@@ -1,2 +1,2 @@\n\x1b[0m')
  152. self.assertEqual(out[4],
  153. '\x1b[33m1\x1b[0m '
  154. '\x1b[31mhel\x1b[0m\x1b[1;35m>\x1b[0m '
  155. '\x1b[0m\x1b[33m1\x1b[0m '
  156. '\x1b[32mhel\x1b[0m\x1b[1;35m>\x1b[0m\n')
  157. self.assertEqual(out[5],
  158. '\x1b[33m2\x1b[0m '
  159. '\x1b[0mwor\x1b[0m\x1b[1;35m>\x1b[0m '
  160. '\x1b[0m\x1b[33m2\x1b[0m '
  161. '\x1b[0mwor\x1b[0m\x1b[1;35m>\x1b[0m\n')
  162. class TestUdiff(unittest.TestCase):
  163. diff = cdiff.Udiff(None, None, None, None)
  164. def test_is_hunk_meta_normal(self):
  165. self.assertTrue(self.diff.is_hunk_meta('@@ -1 +1 @@'))
  166. self.assertTrue(self.diff.is_hunk_meta('@@ -3,7 +3,6 @@'))
  167. self.assertTrue(self.diff.is_hunk_meta('@@ -3,7 +3,6 @@ class Foo'))
  168. self.assertTrue(self.diff.is_hunk_meta('@@ -3,7 +3,6 @@ class Foo\n'))
  169. self.assertTrue(self.diff.is_hunk_meta('@@ -3,7 +3,6 @@ class Foo\r\n'))
  170. def test_is_hunk_meta_svn_prop(self):
  171. self.assertTrue(self.diff.is_hunk_meta('## -0,0 +1 ##'))
  172. self.assertTrue(self.diff.is_hunk_meta('## -0,0 +1 ##\n'))
  173. self.assertTrue(self.diff.is_hunk_meta('## -0,0 +1 ##\r\n'))
  174. def test_is_hunk_meta_neg(self):
  175. self.assertFalse(self.diff.is_hunk_meta('@@ -1 + @@'))
  176. self.assertFalse(self.diff.is_hunk_meta('@@ -this is not a hunk meta'))
  177. self.assertFalse(self.diff.is_hunk_meta('## -this is not either'))
  178. def test_parse_hunk_meta_normal(self):
  179. self.assertEqual(
  180. self.diff.parse_hunk_meta('@@ -3,7 +3,6 @@'),
  181. ((3, 7), (3, 6)))
  182. def test_parse_hunk_meta_missing(self):
  183. self.assertEqual(
  184. self.diff.parse_hunk_meta('@@ -3 +3,6 @@'),
  185. ((3, 0), (3, 6)))
  186. self.assertEqual(
  187. self.diff.parse_hunk_meta('@@ -3,7 +3 @@'),
  188. ((3, 7), (3, 0)))
  189. self.assertEqual(
  190. self.diff.parse_hunk_meta('@@ -3 +3 @@'),
  191. ((3, 0), (3, 0)))
  192. def test_parse_hunk_meta_svn_prop(self):
  193. self.assertEqual(
  194. self.diff.parse_hunk_meta('## -0,0 +1 ##'),
  195. ((0, 0), (1, 0)))
  196. def test_is_old(self):
  197. self.assertTrue(self.diff.is_old('-hello world'))
  198. self.assertTrue(self.diff.is_old('----')) # yaml
  199. def test_is_old_neg(self):
  200. self.assertFalse(self.diff.is_old('--- considered as old path'))
  201. self.assertFalse(self.diff.is_old('-------------')) # svn log --diff
  202. def test_is_new(self):
  203. self.assertTrue(self.diff.is_new('+hello world'))
  204. self.assertTrue(self.diff.is_new('++++hello world'))
  205. def test_is_new_neg(self):
  206. self.assertFalse(self.diff.is_new('+++ considered as new path'))
  207. class TestDiffParser(unittest.TestCase):
  208. def test_type_detect(self):
  209. patch = r"""\
  210. spam
  211. --- a
  212. +++ b
  213. @@ -1,2 +1,2 @@
  214. """
  215. items = patch.splitlines(True)
  216. stream = cdiff.PatchStream(Sequential(items))
  217. parser = cdiff.DiffParser(stream)
  218. self.assertEqual(parser._type, 'udiff')
  219. def test_type_detect_neg(self):
  220. patch = r"""\
  221. spam
  222. --- a
  223. +++ b
  224. spam
  225. """
  226. items = patch.splitlines(True)
  227. stream = cdiff.PatchStream(Sequential(items))
  228. self.assertRaises(RuntimeError, cdiff.DiffParser, stream)
  229. def test_parse_dangling_header(self):
  230. patch = r"""\
  231. --- a
  232. +++ b
  233. @@ -1,2 +1,2 @@
  234. -foo
  235. +bar
  236. common
  237. spam
  238. """
  239. items = patch.splitlines(True)
  240. stream = cdiff.PatchStream(Sequential(items))
  241. parser = cdiff.DiffParser(stream)
  242. self.assertRaises(RuntimeError, list, parser._parse())
  243. def test_parse_missing_new_path(self):
  244. patch = r"""\
  245. --- a
  246. +++ b
  247. @@ -1,2 +1,2 @@
  248. -foo
  249. +bar
  250. common
  251. --- c
  252. """
  253. items = patch.splitlines(True)
  254. stream = cdiff.PatchStream(Sequential(items))
  255. parser = cdiff.DiffParser(stream)
  256. self.assertRaises(AssertionError, list, parser._parse())
  257. def test_parse_missing_hunk(self):
  258. patch = r"""\
  259. --- a
  260. +++ b
  261. @@ -1,2 +1,2 @@
  262. -foo
  263. +bar
  264. common
  265. --- c
  266. +++ d
  267. """
  268. items = patch.splitlines(True)
  269. stream = cdiff.PatchStream(Sequential(items))
  270. parser = cdiff.DiffParser(stream)
  271. self.assertRaises(AssertionError, list, parser._parse())
  272. def test_parse_svn_prop(self):
  273. patch = r"""\
  274. --- a
  275. +++ b
  276. Added: svn:executable
  277. ## -0,0 +1 ##
  278. +*
  279. \ No newline at end of property
  280. Added: svn:keywords
  281. ## -0,0 +1 ##
  282. +Id
  283. """
  284. items = patch.splitlines(True)
  285. stream = cdiff.PatchStream(Sequential(items))
  286. parser = cdiff.DiffParser(stream)
  287. out = list(parser._parse())
  288. self.assertEqual(len(out), 1)
  289. self.assertEqual(len(out[0]._hunks), 2)
  290. hunk = out[0]._hunks[1]
  291. self.assertEqual(hunk._hunk_headers, ['Added: svn:keywords\n'])
  292. self.assertEqual(hunk._hunk_list, [('+', 'Id\n')])
  293. if __name__ == '__main__':
  294. unittest.main()
  295. # vim:set et sts=4 sw=4 tw=80: