aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/run_tests/python_utils/report_utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/run_tests/python_utils/report_utils.py')
-rw-r--r--tools/run_tests/python_utils/report_utils.py131
1 files changed, 131 insertions, 0 deletions
diff --git a/tools/run_tests/python_utils/report_utils.py b/tools/run_tests/python_utils/report_utils.py
new file mode 100644
index 0000000000..352cf7abe7
--- /dev/null
+++ b/tools/run_tests/python_utils/report_utils.py
@@ -0,0 +1,131 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Generate XML and HTML test reports."""
+
+from __future__ import print_function
+
+try:
+ from mako.runtime import Context
+ from mako.template import Template
+ from mako import exceptions
+except (ImportError):
+ pass # Mako not installed but it is ok.
+import os
+import string
+import xml.etree.cElementTree as ET
+
+
+def _filter_msg(msg, output_format):
+ """Filters out nonprintable and illegal characters from the message."""
+ if output_format in ['XML', 'HTML']:
+ # keep whitespaces but remove formfeed and vertical tab characters
+ # that make XML report unparseable.
+ filtered_msg = filter(
+ lambda x: x in string.printable and x != '\f' and x != '\v',
+ msg.decode('UTF-8', 'ignore'))
+ if output_format == 'HTML':
+ filtered_msg = filtered_msg.replace('"', '"')
+ return filtered_msg
+ else:
+ return msg
+
+
+def render_junit_xml_report(resultset, xml_report, suite_package='grpc',
+ suite_name='tests'):
+ """Generate JUnit-like XML report."""
+ root = ET.Element('testsuites')
+ testsuite = ET.SubElement(root, 'testsuite', id='1', package=suite_package,
+ name=suite_name)
+ for shortname, results in resultset.iteritems():
+ for result in results:
+ xml_test = ET.SubElement(testsuite, 'testcase', name=shortname)
+ if result.elapsed_time:
+ xml_test.set('time', str(result.elapsed_time))
+ ET.SubElement(xml_test, 'system-out').text = _filter_msg(result.message,
+ 'XML')
+ if result.state == 'FAILED':
+ ET.SubElement(xml_test, 'failure', message='Failure')
+ elif result.state == 'TIMEOUT':
+ ET.SubElement(xml_test, 'error', message='Timeout')
+ elif result.state == 'SKIPPED':
+ ET.SubElement(xml_test, 'skipped', message='Skipped')
+ tree = ET.ElementTree(root)
+ tree.write(xml_report, encoding='UTF-8')
+
+
+def render_interop_html_report(
+ client_langs, server_langs, test_cases, auth_test_cases, http2_cases,
+ resultset, num_failures, cloud_to_prod, prod_servers, http2_interop):
+ """Generate HTML report for interop tests."""
+ template_file = 'tools/run_tests/interop/interop_html_report.template'
+ try:
+ mytemplate = Template(filename=template_file, format_exceptions=True)
+ except NameError:
+ print('Mako template is not installed. Skipping HTML report generation.')
+ return
+ except IOError as e:
+ print('Failed to find the template %s: %s' % (template_file, e))
+ return
+
+ sorted_test_cases = sorted(test_cases)
+ sorted_auth_test_cases = sorted(auth_test_cases)
+ sorted_http2_cases = sorted(http2_cases)
+ sorted_client_langs = sorted(client_langs)
+ sorted_server_langs = sorted(server_langs)
+ sorted_prod_servers = sorted(prod_servers)
+
+ args = {'client_langs': sorted_client_langs,
+ 'server_langs': sorted_server_langs,
+ 'test_cases': sorted_test_cases,
+ 'auth_test_cases': sorted_auth_test_cases,
+ 'http2_cases': sorted_http2_cases,
+ 'resultset': resultset,
+ 'num_failures': num_failures,
+ 'cloud_to_prod': cloud_to_prod,
+ 'prod_servers': sorted_prod_servers,
+ 'http2_interop': http2_interop}
+
+ html_report_out_dir = 'reports'
+ if not os.path.exists(html_report_out_dir):
+ os.mkdir(html_report_out_dir)
+ html_file_path = os.path.join(html_report_out_dir, 'index.html')
+ try:
+ with open(html_file_path, 'w') as output_file:
+ mytemplate.render_context(Context(output_file, **args))
+ except:
+ print(exceptions.text_error_template().render())
+ raise
+
+def render_perf_profiling_results(output_filepath, profile_names):
+ with open(output_filepath, 'w') as output_file:
+ output_file.write('<ul>\n')
+ for name in profile_names:
+ output_file.write('<li><a href=%s>%s</a></li>\n' % (name, name))
+ output_file.write('</ul>\n')