blob: a349510ab85946eaa0c0f57bc0ba59a8211f650c [file] [log] [blame]
Brad Bishop40320b12019-03-26 16:08:25 -04001# test result tool - report text based test results
2#
3# Copyright (c) 2019, Intel Corporation.
4# Copyright (c) 2019, Linux Foundation
5#
Brad Bishopc342db32019-05-15 21:57:59 -04006# SPDX-License-Identifier: GPL-2.0-only
Brad Bishop40320b12019-03-26 16:08:25 -04007#
Brad Bishopc342db32019-05-15 21:57:59 -04008
Brad Bishop40320b12019-03-26 16:08:25 -04009import os
10import glob
11import json
12import resulttool.resultutils as resultutils
13from oeqa.utils.git import GitRepo
14import oeqa.utils.gitarchive as gitarchive
15
16
17class ResultsTextReport(object):
18 def __init__(self):
19 self.ptests = {}
Brad Bishopc342db32019-05-15 21:57:59 -040020 self.ltptests = {}
21 self.ltpposixtests = {}
Brad Bishop79641f22019-09-10 07:20:22 -040022 self.result_types = {'passed': ['PASSED', 'passed', 'PASS', 'XFAIL'],
23 'failed': ['FAILED', 'failed', 'FAIL', 'ERROR', 'error', 'UNKNOWN', 'XPASS'],
24 'skipped': ['SKIPPED', 'skipped', 'UNSUPPORTED', 'UNTESTED', 'UNRESOLVED']}
Brad Bishop40320b12019-03-26 16:08:25 -040025
26
Brad Bishop15ae2502019-06-18 21:44:24 -040027 def handle_ptest_result(self, k, status, result, machine):
28 if machine not in self.ptests:
29 self.ptests[machine] = {}
30
Brad Bishop40320b12019-03-26 16:08:25 -040031 if k == 'ptestresult.sections':
32 # Ensure tests without any test results still show up on the report
33 for suite in result['ptestresult.sections']:
Brad Bishop15ae2502019-06-18 21:44:24 -040034 if suite not in self.ptests[machine]:
Brad Bishopa34c0302019-09-23 22:34:48 -040035 self.ptests[machine][suite] = {
36 'passed': 0, 'failed': 0, 'skipped': 0, 'duration' : '-',
37 'failed_testcases': [], "testcases": set(),
38 }
Brad Bishop40320b12019-03-26 16:08:25 -040039 if 'duration' in result['ptestresult.sections'][suite]:
Brad Bishop15ae2502019-06-18 21:44:24 -040040 self.ptests[machine][suite]['duration'] = result['ptestresult.sections'][suite]['duration']
Brad Bishop40320b12019-03-26 16:08:25 -040041 if 'timeout' in result['ptestresult.sections'][suite]:
Brad Bishop15ae2502019-06-18 21:44:24 -040042 self.ptests[machine][suite]['duration'] += " T"
Brad Bishopa34c0302019-09-23 22:34:48 -040043 return True
44
45 # process test result
Brad Bishop40320b12019-03-26 16:08:25 -040046 try:
47 _, suite, test = k.split(".", 2)
48 except ValueError:
Brad Bishopa34c0302019-09-23 22:34:48 -040049 return True
50
Brad Bishop40320b12019-03-26 16:08:25 -040051 # Handle 'glib-2.0'
52 if 'ptestresult.sections' in result and suite not in result['ptestresult.sections']:
53 try:
54 _, suite, suite1, test = k.split(".", 3)
55 if suite + "." + suite1 in result['ptestresult.sections']:
56 suite = suite + "." + suite1
57 except ValueError:
58 pass
Brad Bishopa34c0302019-09-23 22:34:48 -040059
Brad Bishop15ae2502019-06-18 21:44:24 -040060 if suite not in self.ptests[machine]:
Brad Bishopa34c0302019-09-23 22:34:48 -040061 self.ptests[machine][suite] = {
62 'passed': 0, 'failed': 0, 'skipped': 0, 'duration' : '-',
63 'failed_testcases': [], "testcases": set(),
64 }
65
66 # do not process duplicate results
67 if test in self.ptests[machine][suite]["testcases"]:
68 print("Warning duplicate ptest result '{}.{}' for {}".format(suite, test, machine))
69 return False
70
Brad Bishop40320b12019-03-26 16:08:25 -040071 for tk in self.result_types:
72 if status in self.result_types[tk]:
Brad Bishop15ae2502019-06-18 21:44:24 -040073 self.ptests[machine][suite][tk] += 1
Brad Bishopa34c0302019-09-23 22:34:48 -040074 self.ptests[machine][suite]["testcases"].add(test)
75 return True
Brad Bishop40320b12019-03-26 16:08:25 -040076
Brad Bishop15ae2502019-06-18 21:44:24 -040077 def handle_ltptest_result(self, k, status, result, machine):
78 if machine not in self.ltptests:
79 self.ltptests[machine] = {}
80
Brad Bishopc342db32019-05-15 21:57:59 -040081 if k == 'ltpresult.sections':
82 # Ensure tests without any test results still show up on the report
83 for suite in result['ltpresult.sections']:
Brad Bishop15ae2502019-06-18 21:44:24 -040084 if suite not in self.ltptests[machine]:
85 self.ltptests[machine][suite] = {'passed': 0, 'failed': 0, 'skipped': 0, 'duration' : '-', 'failed_testcases': []}
Brad Bishopc342db32019-05-15 21:57:59 -040086 if 'duration' in result['ltpresult.sections'][suite]:
Brad Bishop15ae2502019-06-18 21:44:24 -040087 self.ltptests[machine][suite]['duration'] = result['ltpresult.sections'][suite]['duration']
Brad Bishopc342db32019-05-15 21:57:59 -040088 if 'timeout' in result['ltpresult.sections'][suite]:
Brad Bishop15ae2502019-06-18 21:44:24 -040089 self.ltptests[machine][suite]['duration'] += " T"
Brad Bishopc342db32019-05-15 21:57:59 -040090 return
91 try:
92 _, suite, test = k.split(".", 2)
93 except ValueError:
94 return
95 # Handle 'glib-2.0'
96 if 'ltpresult.sections' in result and suite not in result['ltpresult.sections']:
97 try:
98 _, suite, suite1, test = k.split(".", 3)
Brad Bishopc342db32019-05-15 21:57:59 -040099 if suite + "." + suite1 in result['ltpresult.sections']:
100 suite = suite + "." + suite1
101 except ValueError:
102 pass
Brad Bishop15ae2502019-06-18 21:44:24 -0400103 if suite not in self.ltptests[machine]:
104 self.ltptests[machine][suite] = {'passed': 0, 'failed': 0, 'skipped': 0, 'duration' : '-', 'failed_testcases': []}
Brad Bishopc342db32019-05-15 21:57:59 -0400105 for tk in self.result_types:
106 if status in self.result_types[tk]:
Brad Bishop15ae2502019-06-18 21:44:24 -0400107 self.ltptests[machine][suite][tk] += 1
Brad Bishopc342db32019-05-15 21:57:59 -0400108
Brad Bishop15ae2502019-06-18 21:44:24 -0400109 def handle_ltpposixtest_result(self, k, status, result, machine):
110 if machine not in self.ltpposixtests:
111 self.ltpposixtests[machine] = {}
112
Brad Bishopc342db32019-05-15 21:57:59 -0400113 if k == 'ltpposixresult.sections':
114 # Ensure tests without any test results still show up on the report
115 for suite in result['ltpposixresult.sections']:
Brad Bishop15ae2502019-06-18 21:44:24 -0400116 if suite not in self.ltpposixtests[machine]:
117 self.ltpposixtests[machine][suite] = {'passed': 0, 'failed': 0, 'skipped': 0, 'duration' : '-', 'failed_testcases': []}
Brad Bishopc342db32019-05-15 21:57:59 -0400118 if 'duration' in result['ltpposixresult.sections'][suite]:
Brad Bishop15ae2502019-06-18 21:44:24 -0400119 self.ltpposixtests[machine][suite]['duration'] = result['ltpposixresult.sections'][suite]['duration']
Brad Bishopc342db32019-05-15 21:57:59 -0400120 return
121 try:
122 _, suite, test = k.split(".", 2)
123 except ValueError:
124 return
125 # Handle 'glib-2.0'
126 if 'ltpposixresult.sections' in result and suite not in result['ltpposixresult.sections']:
127 try:
128 _, suite, suite1, test = k.split(".", 3)
129 if suite + "." + suite1 in result['ltpposixresult.sections']:
130 suite = suite + "." + suite1
131 except ValueError:
132 pass
Brad Bishop15ae2502019-06-18 21:44:24 -0400133 if suite not in self.ltpposixtests[machine]:
134 self.ltpposixtests[machine][suite] = {'passed': 0, 'failed': 0, 'skipped': 0, 'duration' : '-', 'failed_testcases': []}
Brad Bishopc342db32019-05-15 21:57:59 -0400135 for tk in self.result_types:
136 if status in self.result_types[tk]:
Brad Bishop15ae2502019-06-18 21:44:24 -0400137 self.ltpposixtests[machine][suite][tk] += 1
Brad Bishopc342db32019-05-15 21:57:59 -0400138
Brad Bishop15ae2502019-06-18 21:44:24 -0400139 def get_aggregated_test_result(self, logger, testresult, machine):
Brad Bishop40320b12019-03-26 16:08:25 -0400140 test_count_report = {'passed': 0, 'failed': 0, 'skipped': 0, 'failed_testcases': []}
141 result = testresult.get('result', [])
142 for k in result:
143 test_status = result[k].get('status', [])
Brad Bishopa34c0302019-09-23 22:34:48 -0400144 if k.startswith("ptestresult."):
145 if not self.handle_ptest_result(k, test_status, result, machine):
146 continue
147 elif k.startswith("ltpresult."):
148 self.handle_ltptest_result(k, test_status, result, machine)
149 elif k.startswith("ltpposixresult."):
150 self.handle_ltpposixtest_result(k, test_status, result, machine)
151
152 # process result if it was not skipped by a handler
Brad Bishop40320b12019-03-26 16:08:25 -0400153 for tk in self.result_types:
154 if test_status in self.result_types[tk]:
155 test_count_report[tk] += 1
156 if test_status in self.result_types['failed']:
157 test_count_report['failed_testcases'].append(k)
Brad Bishop40320b12019-03-26 16:08:25 -0400158 return test_count_report
159
160 def print_test_report(self, template_file_name, test_count_reports):
161 from jinja2 import Environment, FileSystemLoader
162 script_path = os.path.dirname(os.path.realpath(__file__))
163 file_loader = FileSystemLoader(script_path + '/template')
164 env = Environment(loader=file_loader, trim_blocks=True)
165 template = env.get_template(template_file_name)
166 havefailed = False
Brad Bishop40320b12019-03-26 16:08:25 -0400167 reportvalues = []
Brad Bishop15ae2502019-06-18 21:44:24 -0400168 machines = []
Brad Bishop40320b12019-03-26 16:08:25 -0400169 cols = ['passed', 'failed', 'skipped']
Brad Bishopc342db32019-05-15 21:57:59 -0400170 maxlen = {'passed' : 0, 'failed' : 0, 'skipped' : 0, 'result_id': 0, 'testseries' : 0, 'ptest' : 0 ,'ltptest': 0, 'ltpposixtest': 0}
Brad Bishop40320b12019-03-26 16:08:25 -0400171 for line in test_count_reports:
172 total_tested = line['passed'] + line['failed'] + line['skipped']
173 vals = {}
174 vals['result_id'] = line['result_id']
175 vals['testseries'] = line['testseries']
176 vals['sort'] = line['testseries'] + "_" + line['result_id']
177 vals['failed_testcases'] = line['failed_testcases']
178 for k in cols:
Andrew Geissler5082cc72023-09-11 08:41:39 -0400179 if total_tested:
180 vals[k] = "%d (%s%%)" % (line[k], format(line[k] / total_tested * 100, '.0f'))
181 else:
182 vals[k] = "0 (0%)"
Brad Bishop40320b12019-03-26 16:08:25 -0400183 for k in maxlen:
184 if k in vals and len(vals[k]) > maxlen[k]:
185 maxlen[k] = len(vals[k])
186 reportvalues.append(vals)
187 if line['failed_testcases']:
188 havefailed = True
Brad Bishop15ae2502019-06-18 21:44:24 -0400189 if line['machine'] not in machines:
190 machines.append(line['machine'])
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500191 reporttotalvalues = {}
192 for k in cols:
193 reporttotalvalues[k] = '%s' % sum([line[k] for line in test_count_reports])
194 reporttotalvalues['count'] = '%s' % len(test_count_reports)
Brad Bishop15ae2502019-06-18 21:44:24 -0400195 for (machine, report) in self.ptests.items():
196 for ptest in self.ptests[machine]:
197 if len(ptest) > maxlen['ptest']:
198 maxlen['ptest'] = len(ptest)
199 for (machine, report) in self.ltptests.items():
200 for ltptest in self.ltptests[machine]:
201 if len(ltptest) > maxlen['ltptest']:
202 maxlen['ltptest'] = len(ltptest)
203 for (machine, report) in self.ltpposixtests.items():
204 for ltpposixtest in self.ltpposixtests[machine]:
205 if len(ltpposixtest) > maxlen['ltpposixtest']:
206 maxlen['ltpposixtest'] = len(ltpposixtest)
Brad Bishop40320b12019-03-26 16:08:25 -0400207 output = template.render(reportvalues=reportvalues,
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500208 reporttotalvalues=reporttotalvalues,
Brad Bishop40320b12019-03-26 16:08:25 -0400209 havefailed=havefailed,
Brad Bishop15ae2502019-06-18 21:44:24 -0400210 machines=machines,
Brad Bishop40320b12019-03-26 16:08:25 -0400211 ptests=self.ptests,
Brad Bishopc342db32019-05-15 21:57:59 -0400212 ltptests=self.ltptests,
213 ltpposixtests=self.ltpposixtests,
Brad Bishop40320b12019-03-26 16:08:25 -0400214 maxlen=maxlen)
215 print(output)
216
Andrew Geissler82c905d2020-04-13 13:39:40 -0500217 def view_test_report(self, logger, source_dir, branch, commit, tag, use_regression_map, raw_test, selected_test_case_only):
218 def print_selected_testcase_result(testresults, selected_test_case_only):
219 for testsuite in testresults:
220 for resultid in testresults[testsuite]:
221 result = testresults[testsuite][resultid]['result']
222 test_case_result = result.get(selected_test_case_only, {})
223 if test_case_result.get('status'):
224 print('Found selected test case result for %s from %s' % (selected_test_case_only,
225 resultid))
226 print(test_case_result['status'])
227 else:
228 print('Could not find selected test case result for %s from %s' % (selected_test_case_only,
229 resultid))
230 if test_case_result.get('log'):
231 print(test_case_result['log'])
Brad Bishop40320b12019-03-26 16:08:25 -0400232 test_count_reports = []
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500233 configmap = resultutils.store_map
234 if use_regression_map:
235 configmap = resultutils.regression_map
Brad Bishop40320b12019-03-26 16:08:25 -0400236 if commit:
237 if tag:
238 logger.warning("Ignoring --tag as --commit was specified")
239 tag_name = "{branch}/{commit_number}-g{commit}/{tag_number}"
240 repo = GitRepo(source_dir)
241 revs = gitarchive.get_test_revs(logger, repo, tag_name, branch=branch)
242 rev_index = gitarchive.rev_find(revs, 'commit', commit)
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500243 testresults = resultutils.git_get_result(repo, revs[rev_index][2], configmap=configmap)
Brad Bishop40320b12019-03-26 16:08:25 -0400244 elif tag:
245 repo = GitRepo(source_dir)
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500246 testresults = resultutils.git_get_result(repo, [tag], configmap=configmap)
Brad Bishop40320b12019-03-26 16:08:25 -0400247 else:
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500248 testresults = resultutils.load_resultsdata(source_dir, configmap=configmap)
249 if raw_test:
250 raw_results = {}
251 for testsuite in testresults:
252 result = testresults[testsuite].get(raw_test, {})
253 if result:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500254 raw_results[testsuite] = {raw_test: result}
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500255 if raw_results:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500256 if selected_test_case_only:
257 print_selected_testcase_result(raw_results, selected_test_case_only)
258 else:
259 print(json.dumps(raw_results, sort_keys=True, indent=4))
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500260 else:
261 print('Could not find raw test result for %s' % raw_test)
262 return 0
Andrew Geissler82c905d2020-04-13 13:39:40 -0500263 if selected_test_case_only:
264 print_selected_testcase_result(testresults, selected_test_case_only)
265 return 0
Brad Bishop40320b12019-03-26 16:08:25 -0400266 for testsuite in testresults:
267 for resultid in testresults[testsuite]:
Brad Bishopc68388fc2019-08-26 01:33:31 -0400268 skip = False
Brad Bishop40320b12019-03-26 16:08:25 -0400269 result = testresults[testsuite][resultid]
Brad Bishop15ae2502019-06-18 21:44:24 -0400270 machine = result['configuration']['MACHINE']
Brad Bishopc68388fc2019-08-26 01:33:31 -0400271
272 # Check to see if there is already results for these kinds of tests for the machine
273 for key in result['result'].keys():
274 testtype = str(key).split('.')[0]
Brad Bishopa34c0302019-09-23 22:34:48 -0400275 if ((machine in self.ltptests and testtype == "ltpiresult" and self.ltptests[machine]) or
Brad Bishopc68388fc2019-08-26 01:33:31 -0400276 (machine in self.ltpposixtests and testtype == "ltpposixresult" and self.ltpposixtests[machine])):
277 print("Already have test results for %s on %s, skipping %s" %(str(key).split('.')[0], machine, resultid))
278 skip = True
279 break
280 if skip:
281 break
282
Brad Bishop15ae2502019-06-18 21:44:24 -0400283 test_count_report = self.get_aggregated_test_result(logger, result, machine)
284 test_count_report['machine'] = machine
Brad Bishop40320b12019-03-26 16:08:25 -0400285 test_count_report['testseries'] = result['configuration']['TESTSERIES']
286 test_count_report['result_id'] = resultid
287 test_count_reports.append(test_count_report)
288 self.print_test_report('test_report_full_text.txt', test_count_reports)
289
290def report(args, logger):
291 report = ResultsTextReport()
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500292 report.view_test_report(logger, args.source_dir, args.branch, args.commit, args.tag, args.use_regression_map,
Andrew Geissler82c905d2020-04-13 13:39:40 -0500293 args.raw_test_only, args.selected_test_case_only)
Brad Bishop40320b12019-03-26 16:08:25 -0400294 return 0
295
296def register_commands(subparsers):
297 """Register subcommands from this plugin"""
298 parser_build = subparsers.add_parser('report', help='summarise test results',
299 description='print a text-based summary of the test results',
300 group='analysis')
301 parser_build.set_defaults(func=report)
302 parser_build.add_argument('source_dir',
Brad Bishopc342db32019-05-15 21:57:59 -0400303 help='source file/directory/URL that contain the test result files to summarise')
Brad Bishop40320b12019-03-26 16:08:25 -0400304 parser_build.add_argument('--branch', '-B', default='master', help="Branch to find commit in")
305 parser_build.add_argument('--commit', help="Revision to report")
306 parser_build.add_argument('-t', '--tag', default='',
307 help='source_dir is a git repository, report on the tag specified from that repository')
Brad Bishop1d80a2e2019-11-15 16:35:03 -0500308 parser_build.add_argument('-m', '--use_regression_map', action='store_true',
309 help='instead of the default "store_map", use the "regression_map" for report')
310 parser_build.add_argument('-r', '--raw_test_only', default='',
311 help='output raw test result only for the user provided test result id')
Andrew Geissler82c905d2020-04-13 13:39:40 -0500312 parser_build.add_argument('-s', '--selected_test_case_only', default='',
313 help='output selected test case result for the user provided test case id, if both test '
314 'result id and test case id are provided then output the selected test case result '
315 'from the provided test result id')