test_incremental_xmlfile.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. from __future__ import absolute_import
  2. """
  3. Tests for the incremental XML serialisation API.
  4. Adapted from the tests from lxml.etree.xmlfile
  5. """
  6. try:
  7. import lxml
  8. except ImportError:
  9. raise ImportError("lxml is required to run the tests.")
  10. from io import BytesIO
  11. import unittest
  12. import tempfile, os, sys
  13. from .common_imports import HelperTestCase, skipIf
  14. from et_xmlfile import xmlfile
  15. from et_xmlfile.xmlfile import LxmlSyntaxError
  16. import pytest
  17. from .helper import compare_xml
  18. import xml.etree.ElementTree
  19. from xml.etree.ElementTree import Element, parse
  20. class _XmlFileTestCaseBase(HelperTestCase):
  21. _file = None # to be set by specific subtypes below
  22. def setUp(self):
  23. self._file = BytesIO()
  24. def test_element(self):
  25. with xmlfile(self._file) as xf:
  26. with xf.element('test'):
  27. pass
  28. self.assertXml('<test></test>')
  29. def test_element_write_text(self):
  30. with xmlfile(self._file) as xf:
  31. with xf.element('test'):
  32. xf.write('toast')
  33. self.assertXml('<test>toast</test>')
  34. def test_element_nested(self):
  35. with xmlfile(self._file) as xf:
  36. with xf.element('test'):
  37. with xf.element('toast'):
  38. with xf.element('taste'):
  39. xf.write('conTent')
  40. self.assertXml('<test><toast><taste>conTent</taste></toast></test>')
  41. def test_element_nested_with_text(self):
  42. with xmlfile(self._file) as xf:
  43. with xf.element('test'):
  44. xf.write('con')
  45. with xf.element('toast'):
  46. xf.write('tent')
  47. with xf.element('taste'):
  48. xf.write('inside')
  49. xf.write('tnet')
  50. xf.write('noc')
  51. self.assertXml('<test>con<toast>tent<taste>inside</taste>'
  52. 'tnet</toast>noc</test>')
  53. def test_write_Element(self):
  54. with xmlfile(self._file) as xf:
  55. xf.write(Element('test'))
  56. self.assertXml('<test/>')
  57. def test_write_Element_repeatedly(self):
  58. element = Element('test')
  59. with xmlfile(self._file) as xf:
  60. with xf.element('test'):
  61. for i in range(100):
  62. xf.write(element)
  63. tree = self._parse_file()
  64. self.assertTrue(tree is not None)
  65. self.assertEqual(100, len(tree.getroot()))
  66. self.assertEqual(set(['test']), set(el.tag for el in tree.getroot()))
  67. def test_namespace_nsmap(self):
  68. with xmlfile(self._file) as xf:
  69. with xf.element('{nsURI}test', nsmap={'x': 'nsURI'}):
  70. pass
  71. self.assertXml('<x:test xmlns:x="nsURI"></x:test>')
  72. def test_namespace_nested_nsmap(self):
  73. with xmlfile(self._file) as xf:
  74. with xf.element('test', nsmap={'x': 'nsURI'}):
  75. with xf.element('{nsURI}toast'):
  76. pass
  77. self.assertXml('<test xmlns:x="nsURI"><x:toast></x:toast></test>')
  78. def test_anonymous_namespace(self):
  79. with xmlfile(self._file) as xf:
  80. with xf.element('{nsURI}test'):
  81. pass
  82. self.assertXml('<ns0:test xmlns:ns0="nsURI"></ns0:test>')
  83. def test_namespace_nested_anonymous(self):
  84. with xmlfile(self._file) as xf:
  85. with xf.element('test'):
  86. with xf.element('{nsURI}toast'):
  87. pass
  88. self.assertXml('<test><ns0:toast xmlns:ns0="nsURI"></ns0:toast></test>')
  89. def test_default_namespace(self):
  90. with xmlfile(self._file) as xf:
  91. with xf.element('{nsURI}test', nsmap={None: 'nsURI'}):
  92. pass
  93. self.assertXml('<test xmlns="nsURI"></test>')
  94. def test_nested_default_namespace(self):
  95. with xmlfile(self._file) as xf:
  96. with xf.element('{nsURI}test', nsmap={None: 'nsURI'}):
  97. with xf.element('{nsURI}toast'):
  98. pass
  99. self.assertXml('<test xmlns="nsURI"><toast></toast></test>')
  100. @pytest.mark.xfail
  101. def test_pi(self):
  102. from et_xmlfile.xmlfile import ProcessingInstruction
  103. with xmlfile(self._file) as xf:
  104. xf.write(ProcessingInstruction('pypi'))
  105. with xf.element('test'):
  106. pass
  107. self.assertXml('<?pypi ?><test></test>')
  108. @pytest.mark.xfail
  109. def test_comment(self):
  110. with xmlfile(self._file) as xf:
  111. xf.write(etree.Comment('a comment'))
  112. with xf.element('test'):
  113. pass
  114. self.assertXml('<!--a comment--><test></test>')
  115. def test_attribute(self):
  116. with xmlfile(self._file) as xf:
  117. with xf.element('test', attrib={'k': 'v'}):
  118. pass
  119. self.assertXml('<test k="v"></test>')
  120. def test_escaping(self):
  121. with xmlfile(self._file) as xf:
  122. with xf.element('test'):
  123. xf.write('Comments: <!-- text -->\n')
  124. xf.write('Entities: &amp;')
  125. self.assertXml(
  126. '<test>Comments: &lt;!-- text --&gt;\nEntities: &amp;amp;</test>')
  127. @pytest.mark.xfail
  128. def test_encoding(self):
  129. with xmlfile(self._file, encoding='utf16') as xf:
  130. with xf.element('test'):
  131. xf.write('toast')
  132. self.assertXml('<test>toast</test>', encoding='utf16')
  133. @pytest.mark.xfail
  134. def test_buffering(self):
  135. with xmlfile(self._file, buffered=False) as xf:
  136. with xf.element('test'):
  137. self.assertXml("<test>")
  138. xf.write('toast')
  139. self.assertXml("<test>toast")
  140. with xf.element('taste'):
  141. self.assertXml("<test>toast<taste>")
  142. xf.write('some', etree.Element("more"), "toast")
  143. self.assertXml("<test>toast<taste>some<more/>toast")
  144. self.assertXml("<test>toast<taste>some<more/>toast</taste>")
  145. xf.write('end')
  146. self.assertXml("<test>toast<taste>some<more/>toast</taste>end")
  147. self.assertXml("<test>toast<taste>some<more/>toast</taste>end</test>")
  148. self.assertXml("<test>toast<taste>some<more/>toast</taste>end</test>")
  149. @pytest.mark.xfail
  150. def test_flush(self):
  151. with xmlfile(self._file, buffered=True) as xf:
  152. with xf.element('test'):
  153. self.assertXml("")
  154. xf.write('toast')
  155. self.assertXml("")
  156. with xf.element('taste'):
  157. self.assertXml("")
  158. xf.flush()
  159. self.assertXml("<test>toast<taste>")
  160. self.assertXml("<test>toast<taste>")
  161. self.assertXml("<test>toast<taste>")
  162. self.assertXml("<test>toast<taste></taste></test>")
  163. def test_failure_preceding_text(self):
  164. try:
  165. with xmlfile(self._file) as xf:
  166. xf.write('toast')
  167. except LxmlSyntaxError:
  168. self.assertTrue(True)
  169. else:
  170. self.assertTrue(False)
  171. def test_failure_trailing_text(self):
  172. with xmlfile(self._file) as xf:
  173. with xf.element('test'):
  174. pass
  175. try:
  176. xf.write('toast')
  177. except LxmlSyntaxError:
  178. self.assertTrue(True)
  179. else:
  180. self.assertTrue(False)
  181. def test_failure_trailing_Element(self):
  182. with xmlfile(self._file) as xf:
  183. with xf.element('test'):
  184. pass
  185. try:
  186. xf.write(Element('test'))
  187. except LxmlSyntaxError:
  188. self.assertTrue(True)
  189. else:
  190. self.assertTrue(False)
  191. @pytest.mark.xfail
  192. def test_closing_out_of_order_in_error_case(self):
  193. cm_exit = None
  194. try:
  195. with xmlfile(self._file) as xf:
  196. x = xf.element('test')
  197. cm_exit = x.__exit__
  198. x.__enter__()
  199. raise ValueError('123')
  200. except ValueError:
  201. self.assertTrue(cm_exit)
  202. try:
  203. cm_exit(ValueError, ValueError("huhu"), None)
  204. except LxmlSyntaxError:
  205. self.assertTrue(True)
  206. else:
  207. self.assertTrue(False)
  208. else:
  209. self.assertTrue(False)
  210. def _read_file(self):
  211. pos = self._file.tell()
  212. self._file.seek(0)
  213. try:
  214. return self._file.read()
  215. finally:
  216. self._file.seek(pos)
  217. def _parse_file(self):
  218. pos = self._file.tell()
  219. self._file.seek(0)
  220. try:
  221. return parse(self._file)
  222. finally:
  223. self._file.seek(pos)
  224. def tearDown(self):
  225. if self._file is not None:
  226. self._file.close()
  227. def assertXml(self, expected, encoding='utf8'):
  228. diff = compare_xml(self._read_file().decode(encoding), expected)
  229. assert diff is None, diff
  230. class BytesIOXmlFileTestCase(_XmlFileTestCaseBase):
  231. def setUp(self):
  232. self._file = BytesIO()
  233. def test_filelike_close(self):
  234. with xmlfile(self._file, close=True) as xf:
  235. with xf.element('test'):
  236. pass
  237. self.assertRaises(ValueError, self._file.getvalue)
  238. class TempXmlFileTestCase(_XmlFileTestCaseBase):
  239. def setUp(self):
  240. self._file = tempfile.TemporaryFile()
  241. class TempPathXmlFileTestCase(_XmlFileTestCaseBase):
  242. def setUp(self):
  243. self._tmpfile = tempfile.NamedTemporaryFile(delete=False)
  244. self._file = self._tmpfile.name
  245. def tearDown(self):
  246. try:
  247. self._tmpfile.close()
  248. finally:
  249. if os.path.exists(self._tmpfile.name):
  250. os.unlink(self._tmpfile.name)
  251. def _read_file(self):
  252. self._tmpfile.seek(0)
  253. return self._tmpfile.read()
  254. def _parse_file(self):
  255. self._tmpfile.seek(0)
  256. return parse(self._tmpfile)
  257. @skipIf(True, "temp file behaviour is too platform specific here")
  258. def test_buffering(self):
  259. pass
  260. @skipIf(True, "temp file behaviour is too platform specific here")
  261. def test_flush(self):
  262. pass
  263. class SimpleFileLikeXmlFileTestCase(_XmlFileTestCaseBase):
  264. class SimpleFileLike(object):
  265. def __init__(self, target):
  266. self._target = target
  267. self.write = target.write
  268. self.tell = target.tell
  269. self.seek = target.seek
  270. self.closed = False
  271. def close(self):
  272. assert not self.closed
  273. self.closed = True
  274. self._target.close()
  275. def setUp(self):
  276. self._target = BytesIO()
  277. self._file = self.SimpleFileLike(self._target)
  278. def _read_file(self):
  279. return self._target.getvalue()
  280. def _parse_file(self):
  281. pos = self._file.tell()
  282. self._target.seek(0)
  283. try:
  284. return parse(self._target)
  285. finally:
  286. self._target.seek(pos)
  287. def test_filelike_not_closing(self):
  288. with xmlfile(self._file) as xf:
  289. with xf.element('test'):
  290. pass
  291. self.assertFalse(self._file.closed)
  292. def test_filelike_close(self):
  293. with xmlfile(self._file, close=True) as xf:
  294. with xf.element('test'):
  295. pass
  296. self.assertTrue(self._file.closed)
  297. self._file = None # prevent closing in tearDown()