Skip to content

Commit 4f57b69

Browse files
committed
[GR-10500] Python unittest runner enhancements
PullRequest: graalpython/89
2 parents b2866c6 + 23f2079 commit 4f57b69

File tree

1 file changed

+52
-20
lines changed

1 file changed

+52
-20
lines changed

graalpython/com.oracle.graal.python.test/src/python_unittests.py

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
# SOFTWARE.
3737

3838
import csv
39+
import gzip
3940
import os
4041
import re
4142
import subprocess
@@ -55,7 +56,7 @@
5556
PATH_UNITTESTS = "graalpython/lib-python/3/test/"
5657

5758
_BASE_NAME = "unittests"
58-
TXT_RESULTS_NAME = "{}.txt".format(_BASE_NAME)
59+
TXT_RESULTS_NAME = "{}.txt.gz".format(_BASE_NAME)
5960
CSV_RESULTS_NAME = "{}.csv".format(_BASE_NAME)
6061
HTML_RESULTS_NAME = "{}.html".format(_BASE_NAME)
6162

@@ -65,6 +66,8 @@
6566
PTRN_NUM_TESTS = re.compile(r'^Ran (?P<num_tests>\d+) test.*$')
6667
PTRN_NUM_ERRORS = re.compile(
6768
r'^FAILED \((failures=(?P<failures>\d+))?(, )?(errors=(?P<errors>\d+))?(, )?(skipped=(?P<skipped>\d+))?\)$')
69+
PTRN_JAVA_EXCEPTION = re.compile(r'^(?P<exception>com\.oracle\.[^:]*):(?P<message>.*)')
70+
PTRN_MODULE_NOT_FOUND = re.compile(r'.*ModuleNotFound: \'(?P<module>.*)\'\..*', re.DOTALL)
6871

6972

7073
# ----------------------------------------------------------------------------------------------------------------------
@@ -82,7 +85,7 @@ def debug(msg, *args, **kwargs):
8285

8386

8487
def file_name(name, current_date_time):
85-
idx = name.rindex('.')
88+
idx = name.index('.')
8689
if idx > 0:
8790
return '{}-{}{}'.format(name[:idx], current_date_time, name[idx:])
8891
return '{}-{}'.format(name, current_date_time)
@@ -131,14 +134,17 @@ def _run_unittest(test_path):
131134

132135
def run_unittests(unittests):
133136
assert isinstance(unittests, (list, tuple))
134-
log("[EXEC] running {} unittests ... ".format(len(unittests)))
137+
num_unittests = len(unittests)
138+
log("[EXEC] running {} unittests ... ", num_unittests)
135139
results = []
140+
136141
pool = Pool()
137142
for ut in unittests:
138-
results.append(pool.apply_async(_run_unittest, args=(ut,)))
143+
results.append(pool.apply_async(_run_unittest, args=(ut, )))
139144
pool.close()
140145
pool.join()
141-
return [r.get()[1] for r in results]
146+
147+
return [res.get()[1] for res in results]
142148

143149

144150
def get_unittests(base_tests_path, limit=None, sort=True):
@@ -180,6 +186,7 @@ def process_output(output_lines):
180186

181187
unittests = []
182188
error_messages = defaultdict(set)
189+
java_exceptions = defaultdict(set)
183190
stats = defaultdict(StatEntry)
184191

185192
for line in output_lines:
@@ -194,6 +201,12 @@ def process_output(output_lines):
194201
error_messages[unittests[-1]].add((match.group('error'), match.group('message')))
195202
continue
196203

204+
# extract java exceptions
205+
match = re.match(PTRN_JAVA_EXCEPTION, line)
206+
if match:
207+
java_exceptions[unittests[-1]].add((match.group('exception'), match.group('message')))
208+
continue
209+
197210
# stats
198211
if line.strip() == 'OK':
199212
stats[unittests[-1]].all_ok()
@@ -216,27 +229,34 @@ def process_output(output_lines):
216229
stats[unittests[-1]].num_errors = int(errs) if errs else 0
217230
stats[unittests[-1]].num_skipped = int(skipped) if skipped else 0
218231

219-
return unittests, error_messages, stats
232+
return unittests, error_messages, java_exceptions, stats
220233

221234

222235
# ----------------------------------------------------------------------------------------------------------------------
223236
#
224237
# python error processing
225238
#
226239
# ----------------------------------------------------------------------------------------------------------------------
227-
def process_errors(unittests, error_messages, error, msg_processor):
240+
def process_errors(unittests, error_messages, error=None, msg_processor=None):
241+
def _err_filter(item):
242+
if not error:
243+
return True
244+
return item[0] == error
245+
246+
def _processor(msg):
247+
if not msg_processor:
248+
return msg
249+
return msg_processor(msg)
250+
228251
missing_modules = defaultdict(lambda: 0)
229252
for ut in unittests:
230253
errors = error_messages[ut]
231-
for name in map(msg_processor, (msg for err, msg in errors if err == error)):
254+
for name in map(_processor, (msg for err, msg in filter(_err_filter, errors))):
232255
missing_modules[name] = missing_modules[name] + 1
233256

234257
return missing_modules
235258

236259

237-
PTRN_MODULE_NOT_FOUND = re.compile(r'.*ModuleNotFound: \'(?P<module>.*)\'\..*', re.DOTALL)
238-
239-
240260
def get_missing_module(msg):
241261
match = re.match(PTRN_MODULE_NOT_FOUND, msg)
242262
return match.group('module') if match else None
@@ -282,13 +302,13 @@ class Stat(object):
282302

283303

284304
def save_as_txt(report_path, results):
285-
with open(report_path, 'w') as TXT:
305+
with gzip.open(report_path, 'wb') as TXT:
286306
output = '\n'.join(results)
287-
TXT.write(output)
307+
TXT.write(bytes(output, 'utf-8'))
288308
return output
289309

290310

291-
def save_as_csv(report_path, unittests, error_messages, stats, current_date):
311+
def save_as_csv(report_path, unittests, error_messages, java_exceptions, stats):
292312
rows = []
293313
with open(report_path, 'w') as CSV:
294314
totals = {
@@ -304,6 +324,9 @@ def save_as_csv(report_path, unittests, error_messages, stats, current_date):
304324
for unittest in unittests:
305325
unittest_stats = stats[unittest]
306326
unittest_errmsg = error_messages[unittest]
327+
if not unittest_errmsg:
328+
unittest_errmsg = java_exceptions[unittest]
329+
307330
rows.append({
308331
Col.UNITTEST: unittest,
309332
Col.NUM_TESTS: unittest_stats.num_tests,
@@ -434,7 +457,7 @@ def save_as_csv(report_path, unittests, error_messages, stats, current_date):
434457
'''
435458

436459

437-
def save_as_html(report_name, rows, totals, missing_modules, current_date):
460+
def save_as_html(report_name, rows, totals, missing_modules, java_issues, current_date):
438461
def grid(*components):
439462
def _fmt(cmp):
440463
if isinstance(cmp, tuple):
@@ -536,6 +559,11 @@ def format_val(row, k):
536559
for cnt, name in sorted(((cnt, name) for name, cnt in missing_modules.items()), reverse=True)
537560
])
538561

562+
java_issues_info = ul('Java issues', [
563+
'<b>{}</b>&nbsp;<span class="text-muted">caused by {} unittests</span>'.format(name, cnt)
564+
for cnt, name in sorted(((cnt, name) for name, cnt in java_issues.items()), reverse=True)
565+
])
566+
539567
total_stats_info = ul("<b>Summary</b>", [
540568
grid('<b># total</b> unittests: {}'.format(totals[Stat.UT_TOTAL])),
541569
grid((progress_bar(totals[Stat.UT_PERCENT_RUNS], color="info"), 3),
@@ -549,7 +577,7 @@ def format_val(row, k):
549577

550578
table_stats = table('stats', CSV_HEADER, rows)
551579

552-
content = ' <br> '.join([total_stats_info, table_stats, missing_modules_info])
580+
content = ' <br> '.join([total_stats_info, table_stats, missing_modules_info, java_issues_info])
553581

554582
report = HTML_TEMPLATE.format(
555583
title='GraalPython Unittests Stats',
@@ -608,16 +636,20 @@ def _fmt(t):
608636
txt_report_path = file_name(TXT_RESULTS_NAME, current_date)
609637
output = save_as_txt(txt_report_path, results)
610638

611-
unittests, error_messages, stats = process_output(output)
639+
unittests, error_messages, java_exceptions, stats = process_output(output)
612640

613641
csv_report_path = file_name(CSV_RESULTS_NAME, current_date)
614-
rows, totals = save_as_csv(csv_report_path, unittests, error_messages, stats, current_date)
642+
rows, totals = save_as_csv(csv_report_path, unittests, error_messages, java_exceptions, stats)
615643

616-
missing_modules = process_errors(unittests, error_messages, 'ModuleNotFoundError', get_missing_module)
644+
missing_modules = process_errors(unittests, error_messages, error='ModuleNotFoundError',
645+
msg_processor=get_missing_module)
617646
log("[MISSING MODULES] \n{}", pformat(dict(missing_modules)))
618647

648+
java_issues = process_errors(unittests, java_exceptions)
649+
log("[JAVA ISSUES] \n{}", pformat(dict(java_issues)))
650+
619651
html_report_path = file_name(HTML_RESULTS_NAME, current_date)
620-
save_as_html(html_report_path, rows, totals, missing_modules, current_date)
652+
save_as_html(html_report_path, rows, totals, missing_modules, java_issues, current_date)
621653

622654
if flags.path:
623655
log("[SAVE] saving results to {} ... ", flags.path)

0 commit comments

Comments
 (0)