|
|
|
# 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
|
|
|
|
import six
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
failure_count = 0
|
|
|
|
error_count = 0
|
|
|
|
for shortname, results in six.iteritems(resultset):
|
|
|
|
for result in results:
|
|
|
|
xml_test = ET.SubElement(testsuite, 'testcase', name=shortname)
|
|
|
|
if result.elapsed_time:
|
|
|
|
xml_test.set('time', str(result.elapsed_time))
|
|
|
|
filtered_msg = _filter_msg(result.message, 'XML')
|
|
|
|
if result.state == 'FAILED':
|
|
|
|
ET.SubElement(xml_test, 'failure', message='Failure').text = filtered_msg
|
|
|
|
failure_count += 1
|
|
|
|
elif result.state == 'TIMEOUT':
|
|
|
|
ET.SubElement(xml_test, 'error', message='Timeout').text = filtered_msg
|
|
|
|
error_count += 1
|
|
|
|
elif result.state == 'SKIPPED':
|
|
|
|
ET.SubElement(xml_test, 'skipped', message='Skipped')
|
|
|
|
testsuite.set('failures', str(failure_count))
|
|
|
|
testsuite.set('errors', str(error_count))
|
|
|
|
# ensure the report directory exists
|
|
|
|
report_dir = os.path.dirname(os.path.abspath(xml_report))
|
|
|
|
if not os.path.exists(report_dir):
|
|
|
|
os.makedirs(report_dir)
|
|
|
|
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,
|
|
|
|
http2_server_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_http2_server_cases = sorted(http2_server_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,
|
|
|
|
'http2_server_cases': sorted_http2_server_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')
|