Skip to content

Commit 85924cc

Browse files
committed
improve reporting of failing tests and tests missing a result file
1 parent 883113b commit 85924cc

File tree

1 file changed

+76
-50
lines changed

1 file changed

+76
-50
lines changed

ament_cmake_test/cmake/run_test.py

+76-50
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import subprocess
2727
from xml.etree.ElementTree import ElementTree
2828
from xml.etree.ElementTree import ParseError
29+
from xml.sax.saxutils import quoteattr
2930

3031

3132
def main(argv=sys.argv[1:]):
@@ -74,66 +75,93 @@ def main(argv=sys.argv[1:]):
7475
args.result_file,
7576
'The test did not generate a result file.'
7677
)
77-
with open(args.result_file, 'w') as f:
78-
f.write(failure_result_file)
78+
with open(args.result_file, 'w') as h:
79+
h.write(failure_result_file)
7980

80-
print("-- run_test.py: invoke following command in '%s':\n - %s" %
81+
print("-- run_test.py: invoking following command in '%s':\n - %s" %
8182
(os.getcwd(), ' '.join(args.command)))
8283

84+
# collect output / exception to generate more detailed result file
85+
# if the command fails to generate it
86+
output = ''
8387
h = None
8488
if args.output_file:
8589
h = open(args.output_file, 'wb')
8690
try:
8791
proc = subprocess.Popen(args.command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
8892
for line in proc.stdout:
89-
print(line.decode(), end='')
93+
decoded_line = line.decode()
94+
print(decoded_line, end='')
95+
output += decoded_line
9096
if h:
9197
h.write(line)
98+
proc.wait()
9299
rc = proc.returncode
100+
print('-- run_test.py: return code', rc, file=sys.stderr if rc else sys.stdout)
101+
except Exception as e:
102+
print('-- run_test.py: invocation failed:', str(e), file=sys.stderr)
103+
output += str(e)
104+
rc = 1
93105
finally:
94106
if h:
95107
h.close()
96108

97-
print("-- run_test.py: verify result file '%s'" % args.result_file)
98-
99-
if os.path.exists(args.result_file):
100-
# if result file exists ensure that it contains valid xml
101-
# unit test suites are not good about screening out
102-
# illegal unicode characters
103-
tree = None
104-
try:
105-
tree = ElementTree(None, args.result_file)
106-
except ParseError as e:
107-
modified = _tidy_xml(args.result_file)
108-
if not modified:
109-
print("Invalid XML in result file '%s': %s" %
110-
(args.result_file, str(e)), file=sys.stderr)
111-
else:
112-
try:
113-
tree = ElementTree(None, args.result_file)
114-
except ParseError as e:
115-
print("Invalid XML in result file '%s' "
116-
"(even after trying to tidy it): %s" %
117-
(args.result_file, str(e)), file=sys.stderr)
118-
119-
if not tree:
120-
# set error code when result file is not parsable
121-
rc = 1
122-
else:
123-
# set error code when result file contains errors or failures
124-
root = tree.getroot()
125-
num_errors = int(root.attrib.get('errors', 0))
126-
num_failures = int(root.attrib.get('failures', 0))
127-
if num_errors or num_failures:
128-
rc = 1
129-
130-
elif not rc and args.generate_result_on_success:
109+
if not rc and args.generate_result_on_success:
131110
# generate result file with one passed test
132111
# if it was expected that no result file was generated
133112
# and the command returned with code zero
113+
print("-- run_test.py: generate result file '%s' with successful test" % args.result_file)
134114
success_result_file = _generate_result(args.result_file)
135-
with open(args.result_file, 'w') as f:
136-
f.write(success_result_file)
115+
with open(args.result_file, 'w') as h:
116+
h.write(success_result_file)
117+
118+
elif os.path.exists(args.result_file):
119+
# check if content of result file has actually changed
120+
with open(args.result_file, 'r') as h:
121+
not_changed = h.read() == failure_result_file
122+
123+
if not_changed:
124+
print("-- run_test.py: generate result file '%s' with failed test" % args.result_file,
125+
file=sys.stderr)
126+
# regenerate result file to include output / exception of the invoked command
127+
failure_result_file = _generate_result(
128+
args.result_file,
129+
'The test did not generate a result file:\n\n' + output
130+
)
131+
with open(args.result_file, 'w') as h:
132+
h.write(failure_result_file)
133+
134+
else:
135+
print("-- run_test.py: verify result file '%s'" % args.result_file)
136+
# if result file exists ensure that it contains valid xml
137+
# unit test suites are not good about screening out
138+
# illegal unicode characters
139+
tree = None
140+
try:
141+
tree = ElementTree(None, args.result_file)
142+
except ParseError as e:
143+
modified = _tidy_xml(args.result_file)
144+
if not modified:
145+
print("Invalid XML in result file '%s': %s" %
146+
(args.result_file, str(e)), file=sys.stderr)
147+
else:
148+
try:
149+
tree = ElementTree(None, args.result_file)
150+
except ParseError as e:
151+
print("Invalid XML in result file '%s' "
152+
"(even after trying to tidy it): %s" %
153+
(args.result_file, str(e)), file=sys.stderr)
154+
155+
if not tree:
156+
# set error code when result file is not parsable
157+
rc = 1
158+
else:
159+
# set error code when result file contains errors or failures
160+
root = tree.getroot()
161+
num_errors = int(root.attrib.get('errors', 0))
162+
num_failures = int(root.attrib.get('failures', 0))
163+
if num_errors or num_failures:
164+
rc = 1
137165

138166
# ensure that a result file exists at the end
139167
if not rc and not os.path.exists(args.result_file):
@@ -147,24 +175,22 @@ def main(argv=sys.argv[1:]):
147175
def _generate_result(result_file, failure_message=None):
148176
pkgname = os.path.basename(os.path.dirname(result_file))
149177
testname = os.path.splitext(os.path.basename(result_file))[0]
150-
name = '%s__%s' % (pkgname, testname)
151-
failure_message = '<failure message="%s" type=""/>' % failure_message \
178+
name = '%s.%s' % (pkgname, testname)
179+
failure_message = '<failure message=%s/>' % quoteattr(failure_message) \
152180
if failure_message else ''
153181
return '''<?xml version="1.0" encoding="UTF-8"?>
154182
<testsuite tests="1" failures="%d" time="1" errors="0" name="%s">
155-
<testcase name="%s" status="run" time="1" classname="Results">
183+
<testcase name="missing_result" status="run" time="1" classname="%s">
156184
%s
157185
</testcase>
158-
</testsuite>''' % (1 if failure_message else 0,
159-
name,
160-
name,
161-
failure_message)
186+
</testsuite>\n''' % \
187+
(1 if failure_message else 0, name, name, failure_message)
162188

163189

164190
def _tidy_xml(filename):
165191
assert os.path.isfile(filename)
166192

167-
# try reading utf-8 firth then iso
193+
# try reading utf-8 first then iso
168194
# this is ugly but the files in question do not declare a unicode type
169195
data = None
170196
for encoding in ['utf-8', 'iso8859-1']:
@@ -200,8 +226,8 @@ def _tidy_xml(filename):
200226
for match in SAFE_XML_REGEX.finditer(data):
201227
data = data[:match.start()] + '?' + data[match.end():]
202228

203-
with open(filename, 'w') as f:
204-
f.write(data)
229+
with open(filename, 'w') as h:
230+
h.write(data)
205231
return True
206232

207233

0 commit comments

Comments
 (0)