36
36
# SOFTWARE.
37
37
38
38
import csv
39
+ import gzip
39
40
import os
40
41
import re
41
42
import subprocess
43
+ import sys
42
44
from collections import defaultdict
43
45
from json import dumps
44
46
from multiprocessing import Pool
45
47
from pprint import pformat
48
+ from time import gmtime , strftime
46
49
47
50
import argparse
48
- import sys
49
- from time import gmtime , strftime
50
51
51
52
# global CLI flags
52
53
flags = None
53
54
54
55
# constants
55
56
PATH_UNITTESTS = "graalpython/lib-python/3/test/"
56
57
57
- CSV_RESULTS_NAME = "unittests.csv"
58
- HTML_RESULTS_NAME = "unittests.html"
58
+ _BASE_NAME = "unittests"
59
+ TXT_RESULTS_NAME = "{}.txt.gz" .format (_BASE_NAME )
60
+ CSV_RESULTS_NAME = "{}.csv" .format (_BASE_NAME )
61
+ HTML_RESULTS_NAME = "{}.html" .format (_BASE_NAME )
62
+
59
63
60
64
PTRN_ERROR = re .compile (r'^(?P<error>[A-Z][a-z][a-zA-Z]+):(?P<message>.*)$' )
61
65
PTRN_UNITTEST = re .compile (r'^#### running: graalpython/lib-python/3/test/(?P<unittest>.*)$' )
62
66
PTRN_NUM_TESTS = re .compile (r'^Ran (?P<num_tests>\d+) test.*$' )
63
67
PTRN_NUM_ERRORS = re .compile (
64
68
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 )
65
71
66
72
67
73
# ----------------------------------------------------------------------------------------------------------------------
@@ -79,7 +85,7 @@ def debug(msg, *args, **kwargs):
79
85
80
86
81
87
def file_name (name , current_date_time ):
82
- idx = name .rindex ('.' )
88
+ idx = name .index ('.' )
83
89
if idx > 0 :
84
90
return '{}-{}{}' .format (name [:idx ], current_date_time , name [idx :])
85
91
return '{}-{}' .format (name , current_date_time )
@@ -117,7 +123,7 @@ def scp(results_file_path, destination_path, destination_name=None):
117
123
118
124
119
125
def _run_unittest (test_path ):
120
- cmd = ["mx" , "python3" , "--python.CatchAllExceptions=true" , test_path ]
126
+ cmd = ["mx" , "python3" , "--python.CatchAllExceptions=true" , test_path , "-v" ]
121
127
success , output = _run_cmd (cmd )
122
128
output = '''
123
129
##############################################################
@@ -128,14 +134,17 @@ def _run_unittest(test_path):
128
134
129
135
def run_unittests (unittests ):
130
136
assert isinstance (unittests , (list , tuple ))
131
- log ("[EXEC] running {} unittests ... " .format (len (unittests )))
137
+ num_unittests = len (unittests )
138
+ log ("[EXEC] running {} unittests ... " , num_unittests )
132
139
results = []
140
+
133
141
pool = Pool ()
134
142
for ut in unittests :
135
- results .append (pool .apply_async (_run_unittest , args = (ut ,)))
143
+ results .append (pool .apply_async (_run_unittest , args = (ut , )))
136
144
pool .close ()
137
145
pool .join ()
138
- return [r .get ()[1 ] for r in results ]
146
+
147
+ return [res .get ()[1 ] for res in results ]
139
148
140
149
141
150
def get_unittests (base_tests_path , limit = None , sort = True ):
@@ -177,6 +186,7 @@ def process_output(output_lines):
177
186
178
187
unittests = []
179
188
error_messages = defaultdict (set )
189
+ java_exceptions = defaultdict (set )
180
190
stats = defaultdict (StatEntry )
181
191
182
192
for line in output_lines :
@@ -191,6 +201,12 @@ def process_output(output_lines):
191
201
error_messages [unittests [- 1 ]].add ((match .group ('error' ), match .group ('message' )))
192
202
continue
193
203
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
+
194
210
# stats
195
211
if line .strip () == 'OK' :
196
212
stats [unittests [- 1 ]].all_ok ()
@@ -213,27 +229,34 @@ def process_output(output_lines):
213
229
stats [unittests [- 1 ]].num_errors = int (errs ) if errs else 0
214
230
stats [unittests [- 1 ]].num_skipped = int (skipped ) if skipped else 0
215
231
216
- return unittests , error_messages , stats
232
+ return unittests , error_messages , java_exceptions , stats
217
233
218
234
219
235
# ----------------------------------------------------------------------------------------------------------------------
220
236
#
221
237
# python error processing
222
238
#
223
239
# ----------------------------------------------------------------------------------------------------------------------
224
- 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
+
225
251
missing_modules = defaultdict (lambda : 0 )
226
252
for ut in unittests :
227
253
errors = error_messages [ut ]
228
- 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 ) )):
229
255
missing_modules [name ] = missing_modules [name ] + 1
230
256
231
257
return missing_modules
232
258
233
259
234
- PTRN_MODULE_NOT_FOUND = re .compile (r'.*ModuleNotFound: \'(?P<module>.*)\'\..*' , re .DOTALL )
235
-
236
-
237
260
def get_missing_module (msg ):
238
261
match = re .match (PTRN_MODULE_NOT_FOUND , msg )
239
262
return match .group ('module' ) if match else None
@@ -278,7 +301,14 @@ class Stat(object):
278
301
TEST_PERCENT_PASS = "test_percent_pass" # percentage of tests which pass from all running tests (all unittests)
279
302
280
303
281
- def save_as_csv (report_path , unittests , error_messages , stats , current_date ):
304
+ def save_as_txt (report_path , results ):
305
+ with gzip .open (report_path , 'wb' ) as TXT :
306
+ output = '\n ' .join (results )
307
+ TXT .write (bytes (output , 'utf-8' ))
308
+ return output
309
+
310
+
311
+ def save_as_csv (report_path , unittests , error_messages , java_exceptions , stats ):
282
312
rows = []
283
313
with open (report_path , 'w' ) as CSV :
284
314
totals = {
@@ -294,6 +324,9 @@ def save_as_csv(report_path, unittests, error_messages, stats, current_date):
294
324
for unittest in unittests :
295
325
unittest_stats = stats [unittest ]
296
326
unittest_errmsg = error_messages [unittest ]
327
+ if not unittest_errmsg :
328
+ unittest_errmsg = java_exceptions [unittest ]
329
+
297
330
rows .append ({
298
331
Col .UNITTEST : unittest ,
299
332
Col .NUM_TESTS : unittest_stats .num_tests ,
@@ -424,7 +457,7 @@ def save_as_csv(report_path, unittests, error_messages, stats, current_date):
424
457
'''
425
458
426
459
427
- 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 ):
428
461
def grid (* components ):
429
462
def _fmt (cmp ):
430
463
if isinstance (cmp , tuple ):
@@ -522,10 +555,15 @@ def format_val(row, k):
522
555
'''
523
556
524
557
missing_modules_info = ul ('missing modules' , [
525
- '<b>{}</b> <span class="text-muted">count: {} </span>' .format (name , cnt )
558
+ '<b>{}</b> <span class="text-muted">imported by {} unittests </span>' .format (name , cnt )
526
559
for cnt , name in sorted (((cnt , name ) for name , cnt in missing_modules .items ()), reverse = True )
527
560
])
528
561
562
+ java_issues_info = ul ('Java issues' , [
563
+ '<b>{}</b> <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
+
529
567
total_stats_info = ul ("<b>Summary</b>" , [
530
568
grid ('<b># total</b> unittests: {}' .format (totals [Stat .UT_TOTAL ])),
531
569
grid ((progress_bar (totals [Stat .UT_PERCENT_RUNS ], color = "info" ), 3 ),
@@ -539,7 +577,7 @@ def format_val(row, k):
539
577
540
578
table_stats = table ('stats' , CSV_HEADER , rows )
541
579
542
- 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 ])
543
581
544
582
report = HTML_TEMPLATE .format (
545
583
title = 'GraalPython Unittests Stats' ,
@@ -595,19 +633,27 @@ def _fmt(t):
595
633
unittests = get_unittests (flags .tests_path , limit = flags .limit )
596
634
597
635
results = run_unittests (unittests )
598
- unittests , error_messages , stats = process_output ('\n ' .join (results ))
636
+ txt_report_path = file_name (TXT_RESULTS_NAME , current_date )
637
+ output = save_as_txt (txt_report_path , results )
638
+
639
+ unittests , error_messages , java_exceptions , stats = process_output (output )
599
640
600
641
csv_report_path = file_name (CSV_RESULTS_NAME , current_date )
601
- 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 )
602
643
603
- 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 )
604
646
log ("[MISSING MODULES] \n {}" , pformat (dict (missing_modules )))
605
647
648
+ java_issues = process_errors (unittests , java_exceptions )
649
+ log ("[JAVA ISSUES] \n {}" , pformat (dict (java_issues )))
650
+
606
651
html_report_path = file_name (HTML_RESULTS_NAME , current_date )
607
- 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 )
608
653
609
654
if flags .path :
610
655
log ("[SAVE] saving results to {} ... " , flags .path )
656
+ scp (txt_report_path , flags .path )
611
657
scp (csv_report_path , flags .path )
612
658
scp (html_report_path , flags .path )
613
659
0 commit comments