report_utils.py
Go to the documentation of this file.
1 # Copyright 2015 gRPC authors.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 """Generate XML and HTML test reports."""
15 
16 try:
17  from mako import exceptions
18  from mako.runtime import Context
19  from mako.template import Template
20 except (ImportError):
21  pass # Mako not installed but it is ok.
22 import datetime
23 import os
24 import string
25 import xml.etree.cElementTree as ET
26 
27 import six
28 
29 
30 def _filter_msg(msg, output_format):
31  """Filters out nonprintable and illegal characters from the message."""
32  if output_format in ['XML', 'HTML']:
33  if isinstance(msg, bytes):
34  decoded_msg = msg.decode('UTF-8', 'ignore')
35  else:
36  decoded_msg = msg
37  # keep whitespaces but remove formfeed and vertical tab characters
38  # that make XML report unparsable.
39  filtered_msg = ''.join(
40  filter(lambda x: x in string.printable and x != '\f' and x != '\v',
41  decoded_msg))
42  if output_format == 'HTML':
43  filtered_msg = filtered_msg.replace('"', '"')
44  return filtered_msg
45  else:
46  return msg
47 
48 
50  return ET.ElementTree(ET.Element('testsuites'))
51 
52 
54  report_file,
55  suite_package='grpc',
56  suite_name='tests',
57  replace_dots=True,
58  multi_target=False):
59  """Generate JUnit-like XML report."""
60  if not multi_target:
61  tree = new_junit_xml_tree()
62  append_junit_xml_results(tree, resultset, suite_package, suite_name,
63  '1', replace_dots)
64  create_xml_report_file(tree, report_file)
65  else:
66  # To have each test result displayed as a separate target by the Resultstore/Sponge UI,
67  # we generate a separate XML report file for each test result
68  for shortname, results in six.iteritems(resultset):
69  one_result = {shortname: results}
70  tree = new_junit_xml_tree()
71  append_junit_xml_results(tree, one_result,
72  '%s_%s' % (suite_package, shortname),
73  '%s_%s' % (suite_name, shortname), '1',
74  replace_dots)
75  per_suite_report_file = os.path.join(os.path.dirname(report_file),
76  shortname,
77  os.path.basename(report_file))
78  create_xml_report_file(tree, per_suite_report_file)
79 
80 
81 def create_xml_report_file(tree, report_file):
82  """Generate JUnit-like report file from xml tree ."""
83  # env variable can be used to override the base location for the reports
84  base_dir = os.getenv('GRPC_TEST_REPORT_BASE_DIR', None)
85  if base_dir:
86  report_file = os.path.join(base_dir, report_file)
87  # ensure the report directory exists
88  report_dir = os.path.dirname(os.path.abspath(report_file))
89  if not os.path.exists(report_dir):
90  os.makedirs(report_dir)
91  tree.write(report_file, encoding='UTF-8')
92 
93 
95  resultset,
96  suite_package,
97  suite_name,
98  id,
99  replace_dots=True):
100  """Append a JUnit-like XML report tree with test results as a new suite."""
101  if replace_dots:
102  # ResultStore UI displays test suite names containing dots only as the component
103  # after the last dot, which results bad info being displayed in the UI.
104  # We replace dots by another character to avoid this problem.
105  suite_name = suite_name.replace('.', '_')
106  testsuite = ET.SubElement(tree.getroot(),
107  'testsuite',
108  id=id,
109  package=suite_package,
110  name=suite_name,
111  timestamp=datetime.datetime.now().isoformat())
112  failure_count = 0
113  error_count = 0
114  for shortname, results in six.iteritems(resultset):
115  for result in results:
116  xml_test = ET.SubElement(testsuite, 'testcase', name=shortname)
117  if result.elapsed_time:
118  xml_test.set('time', str(result.elapsed_time))
119  filtered_msg = _filter_msg(result.message, 'XML')
120  if result.state == 'FAILED':
121  ET.SubElement(xml_test, 'failure',
122  message='Failure').text = filtered_msg
123  failure_count += 1
124  elif result.state == 'TIMEOUT':
125  ET.SubElement(xml_test, 'error',
126  message='Timeout').text = filtered_msg
127  error_count += 1
128  elif result.state == 'SKIPPED':
129  ET.SubElement(xml_test, 'skipped', message='Skipped')
130  testsuite.set('failures', str(failure_count))
131  testsuite.set('errors', str(error_count))
132 
133 
134 def render_interop_html_report(client_langs, server_langs, test_cases,
135  auth_test_cases, http2_cases, http2_server_cases,
136  resultset, num_failures, cloud_to_prod,
137  prod_servers, http2_interop):
138  """Generate HTML report for interop tests."""
139  template_file = 'tools/run_tests/interop/interop_html_report.template'
140  try:
141  mytemplate = Template(filename=template_file, format_exceptions=True)
142  except NameError:
143  print(
144  'Mako template is not installed. Skipping HTML report generation.')
145  return
146  except IOError as e:
147  print(('Failed to find the template %s: %s' % (template_file, e)))
148  return
149 
150  sorted_test_cases = sorted(test_cases)
151  sorted_auth_test_cases = sorted(auth_test_cases)
152  sorted_http2_cases = sorted(http2_cases)
153  sorted_http2_server_cases = sorted(http2_server_cases)
154  sorted_client_langs = sorted(client_langs)
155  sorted_server_langs = sorted(server_langs)
156  sorted_prod_servers = sorted(prod_servers)
157 
158  args = {
159  'client_langs': sorted_client_langs,
160  'server_langs': sorted_server_langs,
161  'test_cases': sorted_test_cases,
162  'auth_test_cases': sorted_auth_test_cases,
163  'http2_cases': sorted_http2_cases,
164  'http2_server_cases': sorted_http2_server_cases,
165  'resultset': resultset,
166  'num_failures': num_failures,
167  'cloud_to_prod': cloud_to_prod,
168  'prod_servers': sorted_prod_servers,
169  'http2_interop': http2_interop
170  }
171 
172  html_report_out_dir = 'reports'
173  if not os.path.exists(html_report_out_dir):
174  os.mkdir(html_report_out_dir)
175  html_file_path = os.path.join(html_report_out_dir, 'index.html')
176  try:
177  with open(html_file_path, 'w') as output_file:
178  mytemplate.render_context(Context(output_file, **args))
179  except:
180  print((exceptions.text_error_template().render()))
181  raise
182 
183 
184 def render_perf_profiling_results(output_filepath, profile_names):
185  with open(output_filepath, 'w') as output_file:
186  output_file.write('<ul>\n')
187  for name in profile_names:
188  output_file.write('<li><a href=%s>%s</a></li>\n' % (name, name))
189  output_file.write('</ul>\n')
xds_interop_client.str
str
Definition: xds_interop_client.py:487
python_utils.report_utils.create_xml_report_file
def create_xml_report_file(tree, report_file)
Definition: report_utils.py:81
python_utils.report_utils._filter_msg
def _filter_msg(msg, output_format)
Definition: report_utils.py:30
python_utils.report_utils.render_junit_xml_report
def render_junit_xml_report(resultset, report_file, suite_package='grpc', suite_name='tests', replace_dots=True, multi_target=False)
Definition: report_utils.py:53
python_utils.report_utils.render_perf_profiling_results
def render_perf_profiling_results(output_filepath, profile_names)
Definition: report_utils.py:184
python_utils.report_utils.render_interop_html_report
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)
Definition: report_utils.py:134
render
Definition: render.py:1
open
#define open
Definition: test-fs.c:46
python_utils.report_utils.new_junit_xml_tree
def new_junit_xml_tree()
Definition: report_utils.py:49
python_utils.report_utils.append_junit_xml_results
def append_junit_xml_results(tree, resultset, suite_package, suite_name, id, replace_dots=True)
Definition: report_utils.py:94


grpc
Author(s):
autogenerated on Thu Mar 13 2025 03:01:10