Skip to content

Commit 1507efb

Browse files
authored
Remove illegal characters from xml (#17)
* add illegal unicode character * remove illegal unicode characters * add six to setup.py * use six for unichr * remove unnecessary compatibility code * remove illegal characters from attributes only
1 parent 3d480f6 commit 1507efb

File tree

4 files changed

+42
-10
lines changed

4 files changed

+42
-10
lines changed

python_testspace_xml/testspace_xml.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
from __future__ import print_function
2+
import six
23
import base64
34
import gzip
45
import os
56
import os.path
67
import io
78
from io import BytesIO
9+
import re
810
import sys
911
from xml.dom.minidom import parseString
1012

@@ -16,7 +18,7 @@ def __init__(self, name, value):
1618

1719
def write_xml(self, parent_element, dom):
1820
d_elem = dom.createElement('custom_data')
19-
d_elem.setAttribute('name', self.name)
21+
d_elem.setAttribute('name', XmlWriter.invalid_xml_remove(self.name))
2022
cdata = dom.createCDATASection(self.value)
2123
d_elem.appendChild(cdata)
2224
parent_element.appendChild(d_elem)
@@ -78,9 +80,9 @@ def set_link_annotation(self, path=None):
7880

7981
def write_xml(self, parent_element, dom):
8082
annotation = dom.createElement("annotation")
81-
annotation.setAttribute("description", self.description)
83+
annotation.setAttribute("description", XmlWriter.invalid_xml_remove(self.description))
8284
annotation.setAttribute("level", self.level)
83-
annotation.setAttribute("name", self.name)
85+
annotation.setAttribute("name", XmlWriter.invalid_xml_remove(self.name))
8486

8587
if self.file_path is not None:
8688
if self.link_file:
@@ -100,7 +102,7 @@ def write_xml(self, parent_element, dom):
100102
# add comments
101103
for comment in self.comments:
102104
c_elem = dom.createElement("comment")
103-
c_elem.setAttribute("label", comment.name)
105+
c_elem.setAttribute("label", XmlWriter.invalid_xml_remove(comment.name))
104106
cdata = dom.createCDATASection(comment.comment)
105107
c_elem.appendChild(cdata)
106108
annotation.appendChild(c_elem)
@@ -292,8 +294,8 @@ def _write_suite(self, parent_node, test_suite):
292294
suite_elem = parent_node
293295
if not test_suite.is_root_suite:
294296
suite_elem = self.dom.createElement('test_suite')
295-
suite_elem.setAttribute('name', test_suite.name)
296-
suite_elem.setAttribute('description', test_suite.description)
297+
suite_elem.setAttribute('name', XmlWriter.invalid_xml_remove(test_suite.name))
298+
suite_elem.setAttribute('description', XmlWriter.invalid_xml_remove(test_suite.description))
297299
suite_elem.setAttribute('start_time', str(test_suite.start_time))
298300
parent_node.appendChild(suite_elem)
299301
if test_suite.duration > 0:
@@ -315,8 +317,8 @@ def _write_suite(self, parent_node, test_suite):
315317
def _write_test_case(self, parent_node, test_case):
316318
elem_tc = self.dom.createElement('test_case')
317319

318-
elem_tc.setAttribute('name', test_case.name)
319-
elem_tc.setAttribute('description', test_case.description)
320+
elem_tc.setAttribute('name', XmlWriter.invalid_xml_remove(test_case.name))
321+
elem_tc.setAttribute('description', XmlWriter.invalid_xml_remove(test_case.description))
320322
elem_tc.setAttribute('status', test_case.status)
321323
elem_tc.setAttribute('start_time', test_case.start_time)
322324
elem_tc.setAttribute('duration', str(test_case.duration))
@@ -328,6 +330,26 @@ def _write_test_case(self, parent_node, test_case):
328330
for d in test_case.custom_data:
329331
d.write_xml(elem_tc, self.dom)
330332

333+
@staticmethod
334+
def invalid_xml_remove(string_to_clean):
335+
# http://stackoverflow.com/questions/1707890/fast-way-to-filter-illegal-xml-unicode-chars-in-python
336+
illegal_unichrs = [
337+
(0x00, 0x08), (0x0B, 0x1F), (0x7F, 0x84), (0x86, 0x9F),
338+
(0xD800, 0xDFFF), (0xFDD0, 0xFDDF), (0xFFFE, 0xFFFF),
339+
(0x1FFFE, 0x1FFFF), (0x2FFFE, 0x2FFFF), (0x3FFFE, 0x3FFFF),
340+
(0x4FFFE, 0x4FFFF), (0x5FFFE, 0x5FFFF), (0x6FFFE, 0x6FFFF),
341+
(0x7FFFE, 0x7FFFF), (0x8FFFE, 0x8FFFF), (0x9FFFE, 0x9FFFF),
342+
(0xAFFFE, 0xAFFFF), (0xBFFFE, 0xBFFFF), (0xCFFFE, 0xCFFFF),
343+
(0xDFFFE, 0xDFFFF), (0xEFFFE, 0xEFFFF), (0xFFFFE, 0xFFFFF),
344+
(0x10FFFE, 0x10FFFF)]
345+
346+
illegal_ranges = ["%s-%s" % (six.unichr(low), six.unichr(high))
347+
for (low, high) in illegal_unichrs
348+
if low < sys.maxunicode]
349+
350+
illegal_xml_re = re.compile(six.u('[%s]') % six.u('').join(illegal_ranges))
351+
return illegal_xml_re.sub('', string_to_clean)
352+
331353

332354
class TestspaceReport(TestSuite):
333355
def __init__(self):

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ pytest
22
lxml
33
flake8
44
pep8-naming
5-
pytest-cov
5+
pytest-cov
6+
six

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@
1515
'pytest-cov',
1616
'flake8',
1717
'pep8-naming',
18+
'six'
1819
]
1920
)

tests/test_testspace_report_xsd.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ def setup_class(cls):
7474
test_case.set_status('not_applicable')
7575
example_suite.add_test_case(test_case)
7676

77+
test_suite1 = testspace_report.get_or_add_test_suite('1 testsuite')
78+
test_case = testspace_xml.TestCase('Test case with illegel xml characters & and \0x10FFFF')
79+
test_suite1.add_test_case(test_case)
80+
7781
testspace_report.write_xml('testspace.xml', to_pretty=True)
7882

7983
xml_file = open('testspace.xml', 'r')
@@ -108,11 +112,15 @@ def test_testsuite_duration(self):
108112

109113
def test_number_testcases(self):
110114
test_cases = self.testspace_xml_root.xpath("//test_suite/test_case")
115+
assert len(test_cases) is 5
116+
117+
def test_number_testcases_example_suite(self):
118+
test_cases = self.testspace_xml_root.xpath("//test_suite[@name='Example Suite']/test_case")
111119
assert len(test_cases) is 4
112120

113121
def test_number_passed_testcases(self):
114122
test_cases = self.testspace_xml_root.xpath("//test_suite/test_case[@status='passed']")
115-
assert len(test_cases) is 2
123+
assert len(test_cases) is 3
116124

117125
def test_number_failed_testcases(self):
118126
test_cases = self.testspace_xml_root.xpath("//test_suite/test_case[@status='failed']")

0 commit comments

Comments
 (0)