@@ -68,6 +68,11 @@ def parse_stats_file(file_path):
68
68
if len (parts ) >= 3 :
69
69
time_str = parts [0 ]
70
70
test_path = " " .join (parts [2 :])
71
+
72
+ # Skip entries with "< 0.05 secs were omitted" or similar
73
+ if "secs were omitted" in test_path :
74
+ continue
75
+
71
76
try :
72
77
time_seconds = float (time_str .rstrip ("s" ))
73
78
slowest_tests .append ({"test" : test_path , "duration" : time_seconds })
@@ -94,26 +99,39 @@ def parse_durations_file(file_path):
94
99
if os .path .exists (durations_file ):
95
100
with open (durations_file , "r" ) as f :
96
101
content = f .read ()
97
-
102
+
98
103
# Skip the header line
99
- for line in content .split (' \n ' )[1 :]:
104
+ for line in content .split (" \n " )[1 :]:
100
105
if line .strip ():
101
106
# Format is typically: 10.37s call tests/path/to/test.py::TestClass::test_method
102
107
parts = line .strip ().split ()
103
108
if len (parts ) >= 3 :
104
109
time_str = parts [0 ]
105
- test_path = ' ' .join (parts [2 :])
110
+ test_path = " " .join (parts [2 :])
111
+
112
+ # Skip entries with "< 0.05 secs were omitted" or similar
113
+ if "secs were omitted" in test_path :
114
+ continue
115
+
106
116
try :
107
- time_seconds = float (time_str .rstrip ('s' ))
108
- slowest_tests .append ({
109
- "test" : test_path ,
110
- "duration" : time_seconds
111
- })
117
+ time_seconds = float (time_str .rstrip ("s" ))
118
+ slowest_tests .append ({"test" : test_path , "duration" : time_seconds })
112
119
except ValueError :
113
- pass
120
+ # If time_str is not a valid float, it might be a different format
121
+ # For example, some pytest formats show "< 0.05s" or similar
122
+ if test_path .startswith ("<" ) and "secs were omitted" in test_path :
123
+ # Extract the time value from test_path if it's in the format "< 0.05 secs were omitted"
124
+ try :
125
+ # This handles entries where the time is in the test_path itself
126
+ dur_match = re .search (r"(\d+(?:\.\d+)?)" , test_path )
127
+ if dur_match :
128
+ time_seconds = float (dur_match .group (1 ))
129
+ slowest_tests .append ({"test" : test_path , "duration" : time_seconds })
130
+ except ValueError :
131
+ pass
114
132
except Exception as e :
115
133
print (f"Error parsing durations file { file_path .replace ('_stats.txt' , '_durations.txt' )} : { e } " )
116
-
134
+
117
135
return slowest_tests
118
136
119
137
@@ -248,7 +266,7 @@ def consolidate_reports(reports_dir):
248
266
249
267
# Parse stats
250
268
stats = parse_stats_file (stats_file )
251
-
269
+
252
270
# If no slowest tests found in stats file, try the durations file directly
253
271
if not stats .get ("slowest_tests" ):
254
272
stats ["slowest_tests" ] = parse_durations_file (stats_file )
@@ -300,34 +318,34 @@ def consolidate_reports(reports_dir):
300
318
# Store results for this test suite
301
319
results [base_name ] = {"stats" : stats , "failures" : failures }
302
320
321
+ # Filter out entries with "secs were omitted"
322
+ filtered_slow_tests = [test for test in all_slow_tests if "secs were omitted" not in test ["test" ]]
323
+
303
324
# Sort all slow tests by duration (descending)
304
- all_slow_tests .sort (key = lambda x : x ["duration" ], reverse = True )
325
+ filtered_slow_tests .sort (key = lambda x : x ["duration" ], reverse = True )
305
326
306
327
# Get the number of slowest tests to show from environment variable or default to 10
307
328
num_slowest_tests = int (os .environ .get ("SHOW_SLOWEST_TESTS" , "10" ))
308
- top_slowest_tests = all_slow_tests [:num_slowest_tests ] if all_slow_tests else []
309
-
329
+ top_slowest_tests = filtered_slow_tests [:num_slowest_tests ] if filtered_slow_tests else []
330
+
310
331
# Calculate additional duration statistics
311
332
total_duration = sum (test ["duration" ] for test in all_slow_tests )
312
-
333
+
313
334
# Calculate duration per suite
314
335
suite_durations = {}
315
336
for test in all_slow_tests :
316
337
suite_name = test ["suite" ]
317
338
if suite_name not in suite_durations :
318
339
suite_durations [suite_name ] = 0
319
340
suite_durations [suite_name ] += test ["duration" ]
320
-
341
+
321
342
# Removed duration categories
322
343
323
344
return {
324
- "total_stats" : total_stats ,
325
- "test_suites" : results ,
345
+ "total_stats" : total_stats ,
346
+ "test_suites" : results ,
326
347
"slowest_tests" : top_slowest_tests ,
327
- "duration_stats" : {
328
- "total_duration" : total_duration ,
329
- "suite_durations" : suite_durations
330
- }
348
+ "duration_stats" : {"total_duration" : total_duration , "suite_durations" : suite_durations },
331
349
}
332
350
333
351
@@ -348,7 +366,7 @@ def generate_report(consolidated_data):
348
366
# Get duration stats if available
349
367
duration_stats = consolidated_data .get ("duration_stats" , {})
350
368
total_duration = duration_stats .get ("total_duration" , 0 )
351
-
369
+
352
370
summary_table = [
353
371
["Total Tests" , total ["tests" ]],
354
372
["Passed" , total ["passed" ]],
@@ -360,15 +378,15 @@ def generate_report(consolidated_data):
360
378
361
379
report .append (tabulate (summary_table , tablefmt = "pipe" ))
362
380
report .append ("" )
363
-
381
+
364
382
# Removed duration distribution section
365
383
366
384
# Add test suites summary
367
385
report .append ("## Test Suites" )
368
386
369
387
# Include duration in test suites table if available
370
388
suite_durations = consolidated_data .get ("duration_stats" , {}).get ("suite_durations" , {})
371
-
389
+
372
390
if suite_durations :
373
391
suites_table = [["Test Suite" , "Tests" , "Passed" , "Failed" , "Skipped" , "Success Rate" , "Duration (s)" ]]
374
392
else :
@@ -382,11 +400,19 @@ def generate_report(consolidated_data):
382
400
for suite_name , suite_data in sorted_suites :
383
401
stats = suite_data ["stats" ]
384
402
success_rate = f"{ (stats ['passed' ] / stats ['tests' ] * 100 ):.2f} %" if stats ["tests" ] > 0 else "N/A"
385
-
403
+
386
404
if suite_durations :
387
405
duration = suite_durations .get (suite_name , 0 )
388
406
suites_table .append (
389
- [suite_name , stats ["tests" ], stats ["passed" ], stats ["failed" ], stats ["skipped" ], success_rate , f"{ duration :.2f} " ]
407
+ [
408
+ suite_name ,
409
+ stats ["tests" ],
410
+ stats ["passed" ],
411
+ stats ["failed" ],
412
+ stats ["skipped" ],
413
+ success_rate ,
414
+ f"{ duration :.2f} " ,
415
+ ]
390
416
)
391
417
else :
392
418
suites_table .append (
@@ -403,6 +429,9 @@ def generate_report(consolidated_data):
403
429
404
430
slowest_table = [["Rank" , "Test" , "Duration (s)" , "Test Suite" ]]
405
431
for i , test in enumerate (slowest_tests , 1 ):
432
+ # Skip entries that don't contain actual test names
433
+ if "< 0.05 secs were omitted" in test ["test" ]:
434
+ continue
406
435
slowest_table .append ([i , test ["test" ], f"{ test ['duration' ]:.2f} " , test ["suite" ]])
407
436
408
437
report .append (tabulate (slowest_table , headers = "firstrow" , tablefmt = "pipe" ))
@@ -541,15 +570,19 @@ def create_slack_payload(consolidated_data):
541
570
# Add slowest tests summary
542
571
slowest_tests = consolidated_data .get ("slowest_tests" , [])
543
572
if slowest_tests :
573
+ # Filter out "< 0.05 secs were omitted" entries
574
+ filtered_tests = [test for test in slowest_tests if "secs were omitted" not in test ["test" ]]
575
+
544
576
# Take top 5 for Slack message to avoid clutter
545
- top5_slowest = slowest_tests [:5 ]
577
+ top5_slowest = filtered_tests [:5 ]
546
578
547
- slowest_message = "*Top 5 Slowest Tests:*\n "
548
- for i , test in enumerate (top5_slowest , 1 ):
549
- test_name = test ["test" ].split ("::" )[- 1 ] if "::" in test ["test" ] else test ["test" ]
550
- slowest_message += f"{ i } . `{ test_name } ` - { test ['duration' ]:.2f} s ({ test ['suite' ]} )\n "
579
+ if top5_slowest :
580
+ slowest_message = "*Top 5 Slowest Tests:*\n "
581
+ for i , test in enumerate (top5_slowest , 1 ):
582
+ test_name = test ["test" ].split ("::" )[- 1 ] if "::" in test ["test" ] else test ["test" ]
583
+ slowest_message += f"{ i } . `{ test_name } ` - { test ['duration' ]:.2f} s ({ test ['suite' ]} )\n "
551
584
552
- payload .append ({"type" : "section" , "text" : {"type" : "mrkdwn" , "text" : slowest_message }})
585
+ payload .append ({"type" : "section" , "text" : {"type" : "mrkdwn" , "text" : slowest_message }})
553
586
554
587
# Add action button
555
588
if os .environ .get ("GITHUB_RUN_ID" ):
0 commit comments