3
3
import os
4
4
import re
5
5
from junitparser import JUnitXml , Error , Failure , Skipped
6
+ from collections import defaultdict
6
7
7
8
parser = argparse .ArgumentParser (description = 'Test results analyzer' )
8
9
parser .add_argument ('input_files' , nargs = '+' , help = 'JUnit XML files or log files' )
9
10
args = parser .parse_args ()
10
11
11
12
failures = []
12
13
summaries = []
14
+ failures_by_category = defaultdict (list )
15
+ passed_cases = []
16
+ passed_by_category = defaultdict (list )
17
+ category_totals = defaultdict (lambda : {
18
+ 'Test cases' : 0 ,
19
+ 'Passed' : 0 ,
20
+ 'Skipped' : 0 ,
21
+ 'Failures' : 0 ,
22
+ 'Errors' : 0
23
+ })
13
24
14
25
error_types = [
15
26
"RuntimeError" ,
@@ -38,6 +49,14 @@ def get_name(case):
38
49
return case .get ('name' , '' )
39
50
return ' ' .join (case .name .split ())
40
51
52
+ def get_category_from_case (case ):
53
+ if isinstance (case , dict ):
54
+ return case .get ('category' , 'unknown' )
55
+ else :
56
+ if hasattr (case , '_file_category' ):
57
+ return case ._file_category
58
+ return 'unknown'
59
+
41
60
def get_result (case ):
42
61
if isinstance (case , dict ):
43
62
return case .get ('status' , 'failed' )
@@ -108,6 +127,7 @@ def print_failures(failure_list=None):
108
127
print_header = True
109
128
for case in failures :
110
129
print_md_row ({
130
+ 'Category' : get_category_from_case (case ),
111
131
'Class name' : get_classname (case ),
112
132
'Test name' : get_name (case ),
113
133
'Status' : get_result (case ),
@@ -116,13 +136,34 @@ def print_failures(failure_list=None):
116
136
}, print_header , failure_list = failure_list )
117
137
print_header = False
118
138
139
+ def generate_failures_log ():
140
+ if not failures :
141
+ print ("No failures found, skipping log file creation." )
142
+ return
143
+
144
+ for case in failures :
145
+ category = get_category_from_case (case )
146
+ failures_by_category [category ].append (case )
147
+
148
+ for category , category_failures in failures_by_category .items ():
149
+ if not category_failures :
150
+ continue
151
+
152
+ log_filename = f"failures_{ category } .log"
153
+ with open (log_filename , "w" , encoding = 'utf-8' ) as log_file :
154
+ for case in category_failures :
155
+ class_name = get_classname (case )
156
+ test_name = get_name (case )
157
+ log_file .write (f"{ category } ,{ class_name } ,{ test_name } \n " )
158
+
119
159
def parse_log_file (log_file ):
120
160
with open (log_file , encoding = 'utf-8' ) as f :
121
161
content = f .read ()
122
162
123
163
ut_name = os .path .splitext (os .path .basename (log_file ))[0 ]
164
+ category = determine_category (ut_name )
124
165
summary = {
125
- 'Category' : determine_category ( ut_name ) ,
166
+ 'Category' : category ,
126
167
'UT' : ut_name ,
127
168
'Test cases' : 0 ,
128
169
'Passed' : 0 ,
@@ -170,19 +211,29 @@ def parse_log_file(log_file):
170
211
for match in error_matches :
171
212
error_msg .append (match .group (0 ).strip ())
172
213
173
- failures . append ( {
214
+ failure_case = {
174
215
'classname' : ut_name ,
175
216
'name' : f"{ case_match .group (2 )} :{ test_name } " ,
176
217
'error' : " " .join (error_msg ),
177
218
'status' : 'failed' ,
178
- 'source' : 'Log'
179
- })
219
+ 'source' : 'Log' ,
220
+ 'category' : category
221
+ }
222
+ failures .append (failure_case )
223
+ failures_by_category [category ].append (failure_case )
180
224
failures_number += 1
181
225
182
226
if failures_number > summary ['Failures' ]:
183
227
summary ['Failures' ] = failures_number
184
228
summary ['Passed' ] = summary ['Test cases' ] - summary ['Failures' ] - summary ['Skipped' ]
185
229
230
+ # Update category totals
231
+ category_totals [category ]['Test cases' ] += summary ['Test cases' ]
232
+ category_totals [category ]['Passed' ] += summary ['Passed' ]
233
+ category_totals [category ]['Skipped' ] += summary ['Skipped' ]
234
+ category_totals [category ]['Failures' ] += summary ['Failures' ]
235
+ category_totals [category ]['Errors' ] += summary ['Errors' ]
236
+
186
237
return summary
187
238
188
239
def determine_category (ut ):
@@ -192,6 +243,8 @@ def determine_category(ut):
192
243
return 'op_regression_dev1'
193
244
elif ut == 'op_extended' :
194
245
return 'op_extended'
246
+ elif ut == 'op_transformers' :
247
+ return 'op_transformers'
195
248
elif 'op_ut' in ut :
196
249
return 'op_ut'
197
250
else :
@@ -223,12 +276,56 @@ def process_xml_file(xml_file):
223
276
}
224
277
summaries .append (suite_summary )
225
278
279
+ # Update category totals
280
+ category_totals [category ]['Test cases' ] += suite_summary ['Test cases' ]
281
+ category_totals [category ]['Passed' ] += suite_summary ['Passed' ]
282
+ category_totals [category ]['Skipped' ] += suite_summary ['Skipped' ]
283
+ category_totals [category ]['Failures' ] += suite_summary ['Failures' ]
284
+ category_totals [category ]['Errors' ] += suite_summary ['Errors' ]
285
+
226
286
for case in suite :
227
287
if get_result (case ) not in ["passed" , "skipped" ]:
288
+ case ._file_category = category
228
289
failures .append (case )
290
+ elif get_result (case ) == "passed" :
291
+ case ._file_category = category
292
+ passed_cases .append (case )
293
+ passed_by_category [category ].append (case )
229
294
except Exception as e :
230
295
print (f"Error processing { xml_file } : { e } " , file = sys .stderr )
231
296
297
+ def generate_passed_log ():
298
+ if not passed_cases :
299
+ print ("No passed cases found, skipping log file creation." )
300
+ return
301
+
302
+ for category , category_passed in passed_by_category .items ():
303
+ if not category_passed :
304
+ continue
305
+
306
+ log_filename = f"passed_{ category } .log"
307
+ with open (log_filename , "w" , encoding = 'utf-8' ) as log_file :
308
+ for case in category_passed :
309
+ class_name = get_classname (case )
310
+ test_name = get_name (case )
311
+ status = get_result (case )
312
+ log_file .write (f"{ category } ,{ class_name } ,{ test_name } \n " )
313
+
314
+ def generate_category_totals_log ():
315
+ """Generate log files with category totals"""
316
+ for category , totals in category_totals .items ():
317
+ if totals ['Test cases' ] == 0 :
318
+ continue
319
+
320
+ log_filename = f"category_{ category } .log"
321
+ with open (log_filename , "w" , encoding = 'utf-8' ) as log_file :
322
+ log_file .write (f"Category: { category } \n " )
323
+ log_file .write (f"Test cases: { totals ['Test cases' ]} \n " )
324
+ log_file .write (f"Passed: { totals ['Passed' ]} \n " )
325
+ log_file .write (f"Skipped: { totals ['Skipped' ]} \n " )
326
+ log_file .write (f"Failures: { totals ['Failures' ]} \n " )
327
+ log_file .write (f"Errors: { totals ['Errors' ]} \n " )
328
+
232
329
def print_summary ():
233
330
print ("### Results Summary" )
234
331
print_header = True
@@ -276,6 +373,10 @@ def main():
276
373
277
374
with open ("ut_failure_list.csv" , "w" ) as failure_list :
278
375
print_failures (failure_list = failure_list )
376
+
377
+ generate_failures_log ()
378
+ generate_passed_log ()
379
+ generate_category_totals_log ()
279
380
print_summary ()
280
381
281
382
0 commit comments